@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.
Files changed (235) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +18 -2
  3. package/bin/cage.js +6 -0
  4. package/bin/create-cage-app.js +6 -0
  5. package/dist/analyze.d.ts +18 -0
  6. package/dist/analyze.js +26 -0
  7. package/dist/analyze.js.map +1 -0
  8. package/dist/baseline.d.ts +5 -0
  9. package/dist/baseline.js +50 -0
  10. package/dist/baseline.js.map +1 -0
  11. package/dist/budget.d.ts +7 -0
  12. package/dist/budget.js +52 -0
  13. package/dist/budget.js.map +1 -0
  14. package/dist/config.d.ts +30 -0
  15. package/dist/config.js +47 -0
  16. package/dist/config.js.map +1 -0
  17. package/dist/create.d.ts +1 -0
  18. package/dist/create.js +47 -0
  19. package/dist/create.js.map +1 -0
  20. package/dist/database.d.ts +5 -0
  21. package/dist/database.js +70 -0
  22. package/dist/database.js.map +1 -0
  23. package/dist/derive.d.ts +14 -0
  24. package/dist/derive.js +163 -0
  25. package/dist/derive.js.map +1 -0
  26. package/dist/dev.d.ts +2 -0
  27. package/dist/dev.js +155 -0
  28. package/dist/dev.js.map +1 -0
  29. package/dist/env.d.ts +1 -0
  30. package/dist/env.js +15 -0
  31. package/dist/env.js.map +1 -0
  32. package/dist/findings.d.ts +11 -0
  33. package/dist/findings.js +13 -0
  34. package/dist/findings.js.map +1 -0
  35. package/dist/gen/contracts.d.ts +6 -0
  36. package/dist/gen/contracts.js +167 -0
  37. package/dist/gen/contracts.js.map +1 -0
  38. package/dist/gen/index.d.ts +19 -0
  39. package/dist/gen/index.js +137 -0
  40. package/dist/gen/index.js.map +1 -0
  41. package/dist/gen/zod-code.d.ts +7 -0
  42. package/dist/gen/zod-code.js +161 -0
  43. package/dist/gen/zod-code.js.map +1 -0
  44. package/dist/graph-snapshot.d.ts +13 -0
  45. package/dist/graph-snapshot.js +46 -0
  46. package/dist/graph-snapshot.js.map +1 -0
  47. package/dist/graph.d.ts +11 -0
  48. package/dist/graph.js +87 -0
  49. package/dist/graph.js.map +1 -0
  50. package/dist/impact.d.ts +28 -0
  51. package/dist/impact.js +223 -0
  52. package/dist/impact.js.map +1 -0
  53. package/dist/index.d.ts +16 -0
  54. package/dist/index.js +18 -0
  55. package/dist/index.js.map +1 -0
  56. package/dist/load.d.ts +6 -0
  57. package/dist/load.js +17 -0
  58. package/dist/load.js.map +1 -0
  59. package/dist/main.d.ts +1 -0
  60. package/dist/main.js +176 -0
  61. package/dist/main.js.map +1 -0
  62. package/dist/migrate.d.ts +7 -0
  63. package/dist/migrate.js +69 -0
  64. package/dist/migrate.js.map +1 -0
  65. package/dist/ontology.d.ts +41 -0
  66. package/dist/ontology.js +270 -0
  67. package/dist/ontology.js.map +1 -0
  68. package/dist/process.d.ts +6 -0
  69. package/dist/process.js +39 -0
  70. package/dist/process.js.map +1 -0
  71. package/dist/rules.d.ts +23 -0
  72. package/dist/rules.js +251 -0
  73. package/dist/rules.js.map +1 -0
  74. package/dist/static-check.d.ts +23 -0
  75. package/dist/static-check.js +225 -0
  76. package/dist/static-check.js.map +1 -0
  77. package/dist/sync.d.ts +5 -0
  78. package/dist/sync.js +46 -0
  79. package/dist/sync.js.map +1 -0
  80. package/dist/upgrade.d.ts +10 -0
  81. package/dist/upgrade.js +81 -0
  82. package/dist/upgrade.js.map +1 -0
  83. package/dist/verify.d.ts +7 -0
  84. package/dist/verify.js +88 -0
  85. package/dist/verify.js.map +1 -0
  86. package/dist/version.d.ts +4 -0
  87. package/dist/version.js +6 -0
  88. package/dist/version.js.map +1 -0
  89. package/graph-ui/app.js +1065 -0
  90. package/graph-ui/index.html +197 -0
  91. package/graph-ui/model.js +141 -0
  92. package/graph-ui/relations.js +125 -0
  93. package/graph-ui/structure.js +74 -0
  94. package/graph-ui/style.css +1698 -0
  95. package/managed/errors/CAGE-APPR-001.md +32 -0
  96. package/managed/errors/CAGE-APPR-002.md +32 -0
  97. package/managed/errors/CAGE-APPR-003.md +32 -0
  98. package/managed/errors/CAGE-APPR-004.md +32 -0
  99. package/managed/errors/CAGE-APPR-005.md +32 -0
  100. package/managed/errors/CAGE-APPR-006.md +32 -0
  101. package/managed/errors/CAGE-APPR-007.md +32 -0
  102. package/managed/errors/CAGE-APPR-010.md +32 -0
  103. package/managed/errors/CAGE-AUTH-001.md +32 -0
  104. package/managed/errors/CAGE-BUDGET-001.md +32 -0
  105. package/managed/errors/CAGE-BUDGET-002.md +32 -0
  106. package/managed/errors/CAGE-BUDGET-003.md +32 -0
  107. package/managed/errors/CAGE-CMD-002.md +32 -0
  108. package/managed/errors/CAGE-CMD-003.md +34 -0
  109. package/managed/errors/CAGE-CONN-001.md +32 -0
  110. package/managed/errors/CAGE-CONN-002.md +32 -0
  111. package/managed/errors/CAGE-CONN-003.md +32 -0
  112. package/managed/errors/CAGE-CONN-004.md +32 -0
  113. package/managed/errors/CAGE-CONN-005.md +32 -0
  114. package/managed/errors/CAGE-CONN-006.md +32 -0
  115. package/managed/errors/CAGE-CONN-007.md +32 -0
  116. package/managed/errors/CAGE-CONN-008.md +32 -0
  117. package/managed/errors/CAGE-CONN-009.md +32 -0
  118. package/managed/errors/CAGE-CONN-010.md +32 -0
  119. package/managed/errors/CAGE-CONN-011.md +32 -0
  120. package/managed/errors/CAGE-CONN-012.md +32 -0
  121. package/managed/errors/CAGE-CREATE-001.md +32 -0
  122. package/managed/errors/CAGE-DECL-001.md +34 -0
  123. package/managed/errors/CAGE-DECL-002.md +34 -0
  124. package/managed/errors/CAGE-DECL-003.md +34 -0
  125. package/managed/errors/CAGE-DECL-004.md +34 -0
  126. package/managed/errors/CAGE-DEP-001.md +34 -0
  127. package/managed/errors/CAGE-DEP-002.md +34 -0
  128. package/managed/errors/CAGE-DEP-003.md +34 -0
  129. package/managed/errors/CAGE-DEP-004.md +34 -0
  130. package/managed/errors/CAGE-DEP-005.md +34 -0
  131. package/managed/errors/CAGE-DLQ-001.md +32 -0
  132. package/managed/errors/CAGE-ENV-001.md +32 -0
  133. package/managed/errors/CAGE-EVENT-001.md +32 -0
  134. package/managed/errors/CAGE-EVENT-002.md +32 -0
  135. package/managed/errors/CAGE-FIELD-001.md +32 -0
  136. package/managed/errors/CAGE-FIELD-002.md +32 -0
  137. package/managed/errors/CAGE-FIELD-003.md +32 -0
  138. package/managed/errors/CAGE-FIELD-004.md +32 -0
  139. package/managed/errors/CAGE-GEN-001.md +33 -0
  140. package/managed/errors/CAGE-GEN-002.md +33 -0
  141. package/managed/errors/CAGE-GRANT-001.md +32 -0
  142. package/managed/errors/CAGE-GRANT-002.md +32 -0
  143. package/managed/errors/CAGE-GRANT-003.md +32 -0
  144. package/managed/errors/CAGE-IMPACT-001.md +32 -0
  145. package/managed/errors/CAGE-JOB-001.md +32 -0
  146. package/managed/errors/CAGE-JOB-002.md +32 -0
  147. package/managed/errors/CAGE-JOB-003.md +32 -0
  148. package/managed/errors/CAGE-LINT-001.md +32 -0
  149. package/managed/errors/CAGE-MIG-001.md +32 -0
  150. package/managed/errors/CAGE-MIG-002.md +32 -0
  151. package/managed/errors/CAGE-MIG-003.md +32 -0
  152. package/managed/errors/CAGE-MIG-004.md +32 -0
  153. package/managed/errors/CAGE-MIG-005.md +32 -0
  154. package/managed/errors/CAGE-NAME-001.md +32 -0
  155. package/managed/errors/CAGE-NAME-002.md +32 -0
  156. package/managed/errors/CAGE-NAME-003.md +32 -0
  157. package/managed/errors/CAGE-NAME-004.md +32 -0
  158. package/managed/errors/CAGE-NAME-005.md +32 -0
  159. package/managed/errors/CAGE-NAME-006.md +32 -0
  160. package/managed/errors/CAGE-ORG-001.md +32 -0
  161. package/managed/errors/CAGE-PAIR-002.md +33 -0
  162. package/managed/errors/CAGE-QUERY-001.md +32 -0
  163. package/managed/errors/CAGE-QUERY-002.md +32 -0
  164. package/managed/errors/CAGE-QUERY-003.md +32 -0
  165. package/managed/errors/CAGE-REACT-001.md +32 -0
  166. package/managed/errors/CAGE-REACT-002.md +32 -0
  167. package/managed/errors/CAGE-READS-001.md +32 -0
  168. package/managed/errors/CAGE-REG-001.md +32 -0
  169. package/managed/errors/CAGE-REG-002.md +32 -0
  170. package/managed/errors/CAGE-REG-003.md +32 -0
  171. package/managed/errors/CAGE-ROUTE-001.md +32 -0
  172. package/managed/errors/CAGE-SCOPE-001.md +32 -0
  173. package/managed/errors/CAGE-SCOPE-002.md +32 -0
  174. package/managed/errors/CAGE-SECRET-001.md +32 -0
  175. package/managed/errors/CAGE-SECRET-002.md +32 -0
  176. package/managed/errors/CAGE-SENS-001.md +32 -0
  177. package/managed/errors/CAGE-SQL-001.md +32 -0
  178. package/managed/errors/CAGE-SQL-002.md +32 -0
  179. package/managed/errors/CAGE-STATE-001.md +32 -0
  180. package/managed/errors/CAGE-STATE-002.md +32 -0
  181. package/managed/errors/CAGE-STATE-003.md +32 -0
  182. package/managed/errors/CAGE-STATE-004.md +32 -0
  183. package/managed/errors/CAGE-STATE-005.md +32 -0
  184. package/managed/errors/CAGE-STATE-010.md +32 -0
  185. package/managed/errors/CAGE-STATE-011.md +32 -0
  186. package/managed/errors/CAGE-SYNC-001.md +35 -0
  187. package/managed/errors/CAGE-TEST-001.md +33 -0
  188. package/managed/errors/CAGE-UNSAFE-001.md +32 -0
  189. package/managed/errors/CAGE-UPGRADE-001.md +32 -0
  190. package/managed/errors/README.md +99 -0
  191. package/package.json +60 -3
  192. package/templates/app/.dockerignore +10 -0
  193. package/templates/app/.env.example +17 -0
  194. package/templates/app/.github/workflows/cage.yml +44 -0
  195. package/templates/app/AGENTS.md +13 -0
  196. package/templates/app/Dockerfile +23 -0
  197. package/templates/app/README.md +58 -0
  198. package/templates/app/apps/api/package.json +16 -0
  199. package/templates/app/apps/api/src/main.ts +10 -0
  200. package/templates/app/apps/api/src/worker.ts +14 -0
  201. package/templates/app/apps/api/tsconfig.json +5 -0
  202. package/templates/app/apps/web/index.html +13 -0
  203. package/templates/app/apps/web/package.json +22 -0
  204. package/templates/app/apps/web/src/main.tsx +552 -0
  205. package/templates/app/apps/web/src/style.css +663 -0
  206. package/templates/app/apps/web/tsconfig.json +11 -0
  207. package/templates/app/apps/web/vite.config.ts +16 -0
  208. package/templates/app/cage.config.json +114 -0
  209. package/templates/app/compose.yaml +39 -0
  210. package/templates/app/db/migrations/0001_init.sql +112 -0
  211. package/templates/app/package.json +25 -0
  212. package/templates/app/packages/contracts/package.json +16 -0
  213. package/templates/app/packages/contracts/src/dept.ts +74 -0
  214. package/templates/app/packages/contracts/src/index.ts +81 -0
  215. package/templates/app/packages/contracts/src/inventory-lock.ts +52 -0
  216. package/templates/app/packages/contracts/src/inventory.ts +57 -0
  217. package/templates/app/packages/contracts/src/purchase-order.ts +72 -0
  218. package/templates/app/packages/contracts/src/queries.ts +17 -0
  219. package/templates/app/packages/contracts/src/role.ts +85 -0
  220. package/templates/app/packages/contracts/src/user.ts +89 -0
  221. package/templates/app/packages/contracts/tsconfig.json +5 -0
  222. package/templates/app/packages/domain/package.json +23 -0
  223. package/templates/app/packages/domain/src/approvals.ts +18 -0
  224. package/templates/app/packages/domain/src/erp.ts +36 -0
  225. package/templates/app/packages/domain/src/index.ts +9 -0
  226. package/templates/app/packages/domain/src/inventory.ts +43 -0
  227. package/templates/app/packages/domain/src/jobs.ts +13 -0
  228. package/templates/app/packages/domain/src/purchase-order.test.ts +137 -0
  229. package/templates/app/packages/domain/src/purchase-order.ts +122 -0
  230. package/templates/app/packages/domain/src/queries.ts +10 -0
  231. package/templates/app/packages/domain/src/reactions.ts +24 -0
  232. package/templates/app/packages/domain/tsconfig.json +5 -0
  233. package/templates/app/pnpm-workspace.yaml +10 -0
  234. package/templates/app/renovate.json +5 -0
  235. package/templates/app/tsconfig.base.json +8 -0
@@ -0,0 +1,32 @@
1
+ # CAGE-FIELD-001
2
+
3
+ ## 什么时候出现
4
+
5
+ 命中以下实现中的检查时触发。终端会附具体声明位置、原因和修复方法。
6
+
7
+ 来源:[packages/kernel/src/primitives/fields.ts:92](../../packages/kernel/src/primitives/fields.ts)
8
+
9
+ ```ts
10
+ sensitive(kind: SensitiveKind): Field<T, Opt> {
11
+ if (!SENSITIVE_KINDS.includes(kind)) throw declError('CAGE-FIELD-001', `未知的敏感种类 ${kind}`);
12
+ return this.with({ sensitiveKind: kind });
13
+ }
14
+ /** 唯一约束(租户内) */
15
+ unique(): Field<T, Opt> {
16
+ ```
17
+
18
+ ## 怎么修
19
+
20
+ 使用 field 工厂声明字段,避免覆盖 id/status/version 等系统列,引用指向存在的聚合。
21
+
22
+ ## 正例
23
+
24
+ ```text
25
+ fields: { deptId: ref('Dept'), amount: money() }
26
+ ```
27
+
28
+ ## 反例
29
+
30
+ ```text
31
+ fields: { version: text() } // 系统版本号不能改作业务字段
32
+ ```
@@ -0,0 +1,32 @@
1
+ # CAGE-FIELD-002
2
+
3
+ ## 什么时候出现
4
+
5
+ 命中以下实现中的检查时触发。终端会附具体声明位置、原因和修复方法。
6
+
7
+ 来源:[packages/kernel/src/primitives/fields.ts:149](../../packages/kernel/src/primitives/fields.ts)
8
+
9
+ ```ts
10
+ const target = typeof to === 'string' ? to : to?.name;
11
+ if (!target) throw declError('CAGE-FIELD-002', 'ref() 的目标为空,可能是循环导入;可改为 ref("聚合名")');
12
+ return mk(z.string().uuid(), { kind: 'ref', sql: 'uuid', ref: target });
13
+ }
14
+
15
+ type ShapeZod<S extends Fields> = {
16
+ ```
17
+
18
+ ## 怎么修
19
+
20
+ 使用 field 工厂声明字段,避免覆盖 id/status/version 等系统列,引用指向存在的聚合。
21
+
22
+ ## 正例
23
+
24
+ ```text
25
+ fields: { deptId: ref('Dept'), amount: money() }
26
+ ```
27
+
28
+ ## 反例
29
+
30
+ ```text
31
+ fields: { version: text() } // 系统版本号不能改作业务字段
32
+ ```
@@ -0,0 +1,32 @@
1
+ # CAGE-FIELD-003
2
+
3
+ ## 什么时候出现
4
+
5
+ 命中以下实现中的检查时触发。终端会附具体声明位置、原因和修复方法。
6
+
7
+ 来源:[packages/kernel/src/primitives/aggregate.ts:83](../../packages/kernel/src/primitives/aggregate.ts)
8
+
9
+ ```ts
10
+ }
11
+ if (!isField(v)) throw declError('CAGE-FIELD-003', `${name}.${k} 不是字段原语`);
12
+ if ((RESERVED_FIELDS as readonly string[]).includes(k)) {
13
+ throw declError('CAGE-FIELD-004', `${name}.${k} 是保留字段名,由内核维护`);
14
+ }
15
+ if (!/^[a-z][A-Za-z0-9]*$/.test(k)) throw declError('CAGE-NAME-002', `字段名 ${name}.${k} 必须是小驼峰`);
16
+ ```
17
+
18
+ ## 怎么修
19
+
20
+ 使用 field 工厂声明字段,避免覆盖 id/status/version 等系统列,引用指向存在的聚合。
21
+
22
+ ## 正例
23
+
24
+ ```text
25
+ fields: { deptId: ref('Dept'), amount: money() }
26
+ ```
27
+
28
+ ## 反例
29
+
30
+ ```text
31
+ fields: { version: text() } // 系统版本号不能改作业务字段
32
+ ```
@@ -0,0 +1,32 @@
1
+ # CAGE-FIELD-004
2
+
3
+ ## 什么时候出现
4
+
5
+ 命中以下实现中的检查时触发。终端会附具体声明位置、原因和修复方法。
6
+
7
+ 来源:[packages/kernel/src/primitives/aggregate.ts:85](../../packages/kernel/src/primitives/aggregate.ts)
8
+
9
+ ```ts
10
+ if ((RESERVED_FIELDS as readonly string[]).includes(k)) {
11
+ throw declError('CAGE-FIELD-004', `${name}.${k} 是保留字段名,由内核维护`);
12
+ }
13
+ if (!/^[a-z][A-Za-z0-9]*$/.test(k)) throw declError('CAGE-NAME-002', `字段名 ${name}.${k} 必须是小驼峰`);
14
+ fields[k] = v;
15
+ }
16
+ ```
17
+
18
+ ## 怎么修
19
+
20
+ 使用 field 工厂声明字段,避免覆盖 id/status/version 等系统列,引用指向存在的聚合。
21
+
22
+ ## 正例
23
+
24
+ ```text
25
+ fields: { deptId: ref('Dept'), amount: money() }
26
+ ```
27
+
28
+ ## 反例
29
+
30
+ ```text
31
+ fields: { version: text() } // 系统版本号不能改作业务字段
32
+ ```
@@ -0,0 +1,33 @@
1
+ # CAGE-GEN-001
2
+
3
+ ## 什么时候出现
4
+
5
+ 命中以下实现中的检查时触发。终端会附具体声明位置、原因和修复方法。
6
+
7
+ 来源:[packages/cli/src/gen/index.ts:48](../../packages/cli/src/gen/index.ts)
8
+
9
+ ```ts
10
+ findings: Finding[],
11
+ code = 'CAGE-GEN-001',
12
+ ) {
13
+ const cur = existsSync(path) ? readFileSync(path, 'utf8') : null;
14
+ if (cur === content) return;
15
+ if (check) {
16
+ ```
17
+
18
+ ## 怎么修
19
+
20
+ 运行 cage gen,检查生成契约与迁移草稿后提交;不要手改生成文件。
21
+
22
+ ## 正例
23
+
24
+ ```text
25
+ pnpm cage gen
26
+ pnpm cage gen --check
27
+ ```
28
+
29
+ ## 反例
30
+
31
+ ```text
32
+ // 修改了 domain,但提交旧 contracts
33
+ ```
@@ -0,0 +1,33 @@
1
+ # CAGE-GEN-002
2
+
3
+ ## 什么时候出现
4
+
5
+ 命中以下实现中的检查时触发。终端会附具体声明位置、原因和修复方法。
6
+
7
+ 来源:[packages/cli/src/gen/index.ts:122](../../packages/cli/src/gen/index.ts)
8
+
9
+ ```ts
10
+ findings.push({
11
+ code: 'CAGE-GEN-002',
12
+ at: cfg.migrations,
13
+ reason: `声明的表结构与迁移不一致(${stmts.length} 处变化)`,
14
+ fix: '运行 cage gen 生成迁移草稿,人审后提交',
15
+ });
16
+ ```
17
+
18
+ ## 怎么修
19
+
20
+ 运行 cage gen,检查生成契约与迁移草稿后提交;不要手改生成文件。
21
+
22
+ ## 正例
23
+
24
+ ```text
25
+ pnpm cage gen
26
+ pnpm cage gen --check
27
+ ```
28
+
29
+ ## 反例
30
+
31
+ ```text
32
+ // 修改了 domain,但提交旧 contracts
33
+ ```
@@ -0,0 +1,32 @@
1
+ # CAGE-GRANT-001
2
+
3
+ ## 什么时候出现
4
+
5
+ 命中以下实现中的检查时触发。终端会附具体声明位置、原因和修复方法。
6
+
7
+ 来源:[packages/admin/src/domain/constraints.ts:21](../../packages/admin/src/domain/constraints.ts)
8
+
9
+ ```ts
10
+ const mine = ctx.capabilities.get(input.capability);
11
+ if (!mine) return violation('[CAGE-GRANT-001] 不能授出自己没有的能力点');
12
+ for (const [k, v] of Object.entries(input.params ?? {})) {
13
+ if (typeof v === 'number' && typeof mine[k] === 'number' && v > (mine[k] as number)) {
14
+ return violation(`[CAGE-GRANT-001] 参数 ${k}=${v} 超过了你自己的 ${mine[k]}`);
15
+ }
16
+ ```
17
+
18
+ ## 怎么修
19
+
20
+ 先声明能力点,再由具有该能力和范围的管理员分配,不能通过界面发明能力。
21
+
22
+ ## 正例
23
+
24
+ ```text
25
+ capability: 'PurchaseOrder.read'
26
+ ```
27
+
28
+ ## 反例
29
+
30
+ ```text
31
+ capability: 'PurchaseOrder.nonexistent'
32
+ ```
@@ -0,0 +1,32 @@
1
+ # CAGE-GRANT-002
2
+
3
+ ## 什么时候出现
4
+
5
+ 命中以下实现中的检查时触发。终端会附具体声明位置、原因和修复方法。
6
+
7
+ 来源:[packages/admin/src/domain/constraints.ts:37](../../packages/admin/src/domain/constraints.ts)
8
+
9
+ ```ts
10
+ const want = COMBINATOR_WIDTH[input.combinator as Combinator] ?? 99;
11
+ return want <= ownWidth(ctx, input.aggregate) ? ok() : violation('[CAGE-GRANT-002] 不能授出比自己更宽的数据范围');
12
+ },
13
+ });
14
+
15
+ /** 分配角色也是授权:被分配角色里的每个能力点和范围,操作人自己都必须有 */
16
+ ```
17
+
18
+ ## 怎么修
19
+
20
+ 先声明能力点,再由具有该能力和范围的管理员分配,不能通过界面发明能力。
21
+
22
+ ## 正例
23
+
24
+ ```text
25
+ capability: 'PurchaseOrder.read'
26
+ ```
27
+
28
+ ## 反例
29
+
30
+ ```text
31
+ capability: 'PurchaseOrder.nonexistent'
32
+ ```
@@ -0,0 +1,32 @@
1
+ # CAGE-GRANT-003
2
+
3
+ ## 什么时候出现
4
+
5
+ 命中以下实现中的检查时触发。终端会附具体声明位置、原因和修复方法。
6
+
7
+ 来源:[packages/admin/src/domain/constraints.ts:13](../../packages/admin/src/domain/constraints.ts)
8
+
9
+ ```ts
10
+ check: (_role, _ctx, input, meta) =>
11
+ meta.catalog().includes(input.capability) ? ok() : violation(`[CAGE-GRANT-003] 能力点 ${input.capability} 不在能力目录里`),
12
+ });
13
+
14
+ /** 防提权:不能授出自己没有的能力点;数值参数(额度)不能超过自己的 */
15
+ export const noEscalation = (): Constraint => ({
16
+ ```
17
+
18
+ ## 怎么修
19
+
20
+ 先声明能力点,再由具有该能力和范围的管理员分配,不能通过界面发明能力。
21
+
22
+ ## 正例
23
+
24
+ ```text
25
+ capability: 'PurchaseOrder.read'
26
+ ```
27
+
28
+ ## 反例
29
+
30
+ ```text
31
+ capability: 'PurchaseOrder.nonexistent'
32
+ ```
@@ -0,0 +1,32 @@
1
+ # CAGE-IMPACT-001
2
+
3
+ ## 什么时候出现
4
+
5
+ 命中以下实现中的检查时触发。终端会附具体声明位置、原因和修复方法。
6
+
7
+ 来源:[packages/cli/src/baseline.ts:45](../../packages/cli/src/baseline.ts)
8
+
9
+ ```ts
10
+ );
11
+ if (code !== 0) throw new Error('[CAGE-IMPACT-001] 基线抽取失败,请修复基线声明或使用保存的 --base-file');
12
+ return readOntology(join(project, '.cage/ontology.json'));
13
+ } finally {
14
+ // 先断开本次建立的 junction,避免清理工作树时遍历共享依赖目录。
15
+ for (const link of links) unlinkSync(link);
16
+ ```
17
+
18
+ ## 怎么修
19
+
20
+ 指定存在的 Git 提交,或保存过的完整 ontology.json。基线错误不能当作无变化。
21
+
22
+ ## 正例
23
+
24
+ ```text
25
+ cage impact --base-file baseline.json
26
+ ```
27
+
28
+ ## 反例
29
+
30
+ ```text
31
+ cage impact // 没有指定基线
32
+ ```
@@ -0,0 +1,32 @@
1
+ # CAGE-JOB-001
2
+
3
+ ## 什么时候出现
4
+
5
+ 命中以下实现中的检查时触发。终端会附具体声明位置、原因和修复方法。
6
+
7
+ 来源:[packages/kernel/src/primitives/flow.ts:190](../../packages/kernel/src/primitives/flow.ts)
8
+
9
+ ```ts
10
+ if (!/^[a-z][A-Za-z0-9]*$/.test(name)) throw declError('CAGE-NAME-006', `任务名 ${name} 必须是小驼峰`);
11
+ if (def.cron.trim().split(/\s+/).length < 5) throw declError('CAGE-JOB-001', `任务 ${name} 的 cron 表达式不合法`);
12
+ return Object.freeze({
13
+ [JOB]: true as const,
14
+ name,
15
+ cron: def.cron,
16
+ ```
17
+
18
+ ## 怎么修
19
+
20
+ 校正 cron、reads 和 calls。任务只能读取和调用显式声明的依赖。
21
+
22
+ ## 正例
23
+
24
+ ```text
25
+ job('expireOrders', { cron: '0 2 * * *', reads: [Order], calls: [cancel], run })
26
+ ```
27
+
28
+ ## 反例
29
+
30
+ ```text
31
+ calls: [] // run 却调用 cancel
32
+ ```
@@ -0,0 +1,32 @@
1
+ # CAGE-JOB-002
2
+
3
+ ## 什么时候出现
4
+
5
+ 命中以下实现中的检查时触发。终端会附具体声明位置、原因和修复方法。
6
+
7
+ 来源:[packages/kernel/src/worker/scheduler.ts:38](../../packages/kernel/src/worker/scheduler.ts)
8
+
9
+ ```ts
10
+ async call(cmd: AnyCommand, input: unknown) {
11
+ if (!j.calls.includes(cmd)) throw cageError('FORBIDDEN', { message: `[CAGE-JOB-002] 任务 ${j.name} 调用了未声明的命令` });
12
+ return (await k.execute(cmd, input, ctx)) as any;
13
+ },
14
+ };
15
+ }
16
+ ```
17
+
18
+ ## 怎么修
19
+
20
+ 校正 cron、reads 和 calls。任务只能读取和调用显式声明的依赖。
21
+
22
+ ## 正例
23
+
24
+ ```text
25
+ job('expireOrders', { cron: '0 2 * * *', reads: [Order], calls: [cancel], run })
26
+ ```
27
+
28
+ ## 反例
29
+
30
+ ```text
31
+ calls: [] // run 却调用 cancel
32
+ ```
@@ -0,0 +1,32 @@
1
+ # CAGE-JOB-003
2
+
3
+ ## 什么时候出现
4
+
5
+ 命中以下实现中的检查时触发。终端会附具体声明位置、原因和修复方法。
6
+
7
+ 来源:[packages/kernel/src/worker/scheduler.ts:28](../../packages/kernel/src/worker/scheduler.ts)
8
+
9
+ ```ts
10
+ async list(agg, where = {}, opts = {}) {
11
+ if (!allowedReads.has(agg)) throw new Error(`[CAGE-JOB-003] 任务 ${j.name} 读取了未声明的聚合 ${agg.name}`);
12
+ const { createdBefore, createdAfter, updatedBefore, ...eq } = where as Record<string, any>;
13
+ let q = scopedFind(k.app, agg, ctx, eq, { limit: opts.limit ?? 1000 });
14
+ if (createdBefore) q = q.where('t.created_at', '<', new Date(createdBefore));
15
+ if (createdAfter) q = q.where('t.created_at', '>=', new Date(createdAfter));
16
+ ```
17
+
18
+ ## 怎么修
19
+
20
+ 校正 cron、reads 和 calls。任务只能读取和调用显式声明的依赖。
21
+
22
+ ## 正例
23
+
24
+ ```text
25
+ job('expireOrders', { cron: '0 2 * * *', reads: [Order], calls: [cancel], run })
26
+ ```
27
+
28
+ ## 反例
29
+
30
+ ```text
31
+ calls: [] // run 却调用 cancel
32
+ ```
@@ -0,0 +1,32 @@
1
+ # CAGE-LINT-001
2
+
3
+ ## 什么时候出现
4
+
5
+ 命中以下实现中的检查时触发。终端会附具体声明位置、原因和修复方法。
6
+
7
+ 来源:[packages/cli/src/verify.ts:95](../../packages/cli/src/verify.ts)
8
+
9
+ ```ts
10
+ f.messages.map((m): Finding => ({
11
+ code: /\[(CAGE-[\w-]+)\]/.exec(m.message)?.[1] ?? 'CAGE-LINT-001',
12
+ at: `${f.filePath}:${m.line}`,
13
+ reason: m.message,
14
+ severity: m.severity === 1 ? 'warning' : 'error',
15
+ })),
16
+ ```
17
+
18
+ ## 怎么修
19
+
20
+ 根据输出中的 ruleId 修复语法或规则问题,再重跑 verify。
21
+
22
+ ## 正例
23
+
24
+ ```text
25
+ pnpm cage verify
26
+ ```
27
+
28
+ ## 反例
29
+
30
+ ```text
31
+ // 用禁用 lint 代替修复业务边界
32
+ ```
@@ -0,0 +1,32 @@
1
+ # CAGE-MIG-001
2
+
3
+ ## 什么时候出现
4
+
5
+ 命中以下实现中的检查时触发。终端会附具体声明位置、原因和修复方法。
6
+
7
+ 来源:[packages/kernel/src/db/migrate.ts:84](../../packages/kernel/src/db/migrate.ts)
8
+
9
+ ```ts
10
+ await client.query('rollback');
11
+ throw new Error(`[CAGE-MIG-001] 迁移 ${m.name} 失败:${(e as Error).message}`);
12
+ }
13
+ applied.push(m.name);
14
+ }
15
+ // 安全层每次整体重建
16
+ ```
17
+
18
+ ## 怎么修
19
+
20
+ 核对迁移 SQL、账号权限和目标数据库。初始化只用于独立新集群,已发布迁移不可重写。
21
+
22
+ ## 正例
23
+
24
+ ```text
25
+ pnpm cage migrate
26
+ ```
27
+
28
+ ## 反例
29
+
30
+ ```text
31
+ // 用 cage_read 执行建表或覆盖已有集群角色密码
32
+ ```
@@ -0,0 +1,32 @@
1
+ # CAGE-MIG-002
2
+
3
+ ## 什么时候出现
4
+
5
+ 命中以下实现中的检查时触发。终端会附具体声明位置、原因和修复方法。
6
+
7
+ 来源:[packages/kernel/src/db/migrate.ts:96](../../packages/kernel/src/db/migrate.ts)
8
+
9
+ ```ts
10
+ await client.query('rollback');
11
+ throw new Error(`[CAGE-MIG-002] 重建安全层失败:${(e as Error).message}`);
12
+ }
13
+ log('安全层已重建');
14
+ return { applied, safeLayerRebuilt: true };
15
+ } finally {
16
+ ```
17
+
18
+ ## 怎么修
19
+
20
+ 核对迁移 SQL、账号权限和目标数据库。初始化只用于独立新集群,已发布迁移不可重写。
21
+
22
+ ## 正例
23
+
24
+ ```text
25
+ pnpm cage migrate
26
+ ```
27
+
28
+ ## 反例
29
+
30
+ ```text
31
+ // 用 cage_read 执行建表或覆盖已有集群角色密码
32
+ ```
@@ -0,0 +1,32 @@
1
+ # CAGE-MIG-003
2
+
3
+ ## 什么时候出现
4
+
5
+ 命中以下实现中的检查时触发。终端会附具体声明位置、原因和修复方法。
6
+
7
+ 来源:[packages/cli/src/migrate.ts:15](../../packages/cli/src/migrate.ts)
8
+
9
+ ```ts
10
+ if (!/^[a-z][a-z0-9_]{0,62}$/.test(name))
11
+ throw new Error('[CAGE-MIG-003] 数据库名只允许小写字母、数字和下划线,以字母开头');
12
+ const admin = new pg.Client({ connectionString: superUrl });
13
+ await admin.connect();
14
+ try {
15
+ if (!(await admin.query('select 1 from pg_database where datname = $1', [name])).rowCount)
16
+ ```
17
+
18
+ ## 怎么修
19
+
20
+ 核对迁移 SQL、账号权限和目标数据库。初始化只用于独立新集群,已发布迁移不可重写。
21
+
22
+ ## 正例
23
+
24
+ ```text
25
+ pnpm cage migrate
26
+ ```
27
+
28
+ ## 反例
29
+
30
+ ```text
31
+ // 用 cage_read 执行建表或覆盖已有集群角色密码
32
+ ```
@@ -0,0 +1,32 @@
1
+ # CAGE-MIG-004
2
+
3
+ ## 什么时候出现
4
+
5
+ 命中以下实现中的检查时触发。终端会附具体声明位置、原因和修复方法。
6
+
7
+ 来源:[packages/cli/src/migrate.ts:35](../../packages/cli/src/migrate.ts)
8
+
9
+ ```ts
10
+ throw new Error(
11
+ '[CAGE-MIG-004] 集群已有 cage 账号,拒绝覆盖密码;请复用已初始化的库或使用独立 PostgreSQL 集群',
12
+ );
13
+ }
14
+ await db.query('begin');
15
+ await db.query(bootstrapSql(passwords, name));
16
+ ```
17
+
18
+ ## 怎么修
19
+
20
+ 核对迁移 SQL、账号权限和目标数据库。初始化只用于独立新集群,已发布迁移不可重写。
21
+
22
+ ## 正例
23
+
24
+ ```text
25
+ pnpm cage migrate
26
+ ```
27
+
28
+ ## 反例
29
+
30
+ ```text
31
+ // 用 cage_read 执行建表或覆盖已有集群角色密码
32
+ ```
@@ -0,0 +1,32 @@
1
+ # CAGE-MIG-005
2
+
3
+ ## 什么时候出现
4
+
5
+ 命中以下实现中的检查时触发。终端会附具体声明位置、原因和修复方法。
6
+
7
+ 来源:[packages/kernel/src/db/migrate.ts:71](../../packages/kernel/src/db/migrate.ts)
8
+
9
+ ```ts
10
+ if (done.has(migration.name) && done.get(migration.name) !== checksum(migration.sql)) {
11
+ throw new Error(`[CAGE-MIG-005] 已执行迁移 ${migration.name} 的内容被修改,请恢复原文件并新增迁移`);
12
+ }
13
+ }
14
+ for (const m of all) {
15
+ if (done.has(m.name)) continue;
16
+ ```
17
+
18
+ ## 怎么修
19
+
20
+ 恢复已经执行的迁移文件,不要修改数据库中的校验和来绕过检查。需要变更结构时新增迁移;若仅受换行转换影响,恢复原始文件字节并使用仓库的 LF 约定。
21
+
22
+ ## 正例
23
+
24
+ ```text
25
+ 保留 0001_init.sql,新增 0002_add_field.sql,再运行 cage migrate
26
+ ```
27
+
28
+ ## 反例
29
+
30
+ ```text
31
+ 修改已执行的 0001_init.sql,或手工重写迁移历史的 checksum
32
+ ```
@@ -0,0 +1,32 @@
1
+ # CAGE-NAME-001
2
+
3
+ ## 什么时候出现
4
+
5
+ 命中以下实现中的检查时触发。终端会附具体声明位置、原因和修复方法。
6
+
7
+ 来源:[packages/kernel/src/primitives/aggregate.ts:72](../../packages/kernel/src/primitives/aggregate.ts)
8
+
9
+ ```ts
10
+ if (!/^[A-Z][A-Za-z0-9]*$/.test(name)) {
11
+ throw declError('CAGE-NAME-001', `聚合名 ${name} 必须是大驼峰`);
12
+ }
13
+ let st: StatesDef<any> | undefined = def.states;
14
+ const fields: Record<string, AnyField> = {};
15
+ for (const [k, v] of Object.entries(def.fields)) {
16
+ ```
17
+
18
+ ## 怎么修
19
+
20
+ 聚合、事件使用大驼峰;命令、任务和查询使用小驼峰,且同类名称唯一。
21
+
22
+ ## 正例
23
+
24
+ ```text
25
+ aggregate('PurchaseOrder', definition);
26
+ ```
27
+
28
+ ## 反例
29
+
30
+ ```text
31
+ aggregate('purchase-order', definition);
32
+ ```