@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,137 @@
1
+ // cage gen:从声明生成 contracts、路由清单、安全视图与策略 DDL、迁移草稿、状态图、本体图。
2
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
3
+ import { dirname, join } from 'node:path';
4
+ import { kebab } from '@yss123/cage-kernel';
5
+ import { createTableSql, diffSchemas, embedSnapshot, latestSnapshot, readMigrations, safeLayerSql, schemaModel, } from '@yss123/cage-kernel/db';
6
+ import { abs } from '../config.js';
7
+ import { statesHoldingResources } from '../rules.js';
8
+ import { contractsFiles } from './contracts.js';
9
+ export function stateDiagram(g, agg) {
10
+ const lines = ['stateDiagram-v2'];
11
+ const states = g.nodes.filter((n) => n.kind === 'state' && n.id.startsWith(`${agg}.`));
12
+ const short = (id) => id.slice(agg.length + 1);
13
+ for (const t of g.edges.filter((e) => e.kind === 'transition' && e.to.startsWith(`${agg}.`))) {
14
+ const from = t.from.endsWith('.[new]') ? '[*]' : short(t.from);
15
+ lines.push(` ${from} --> ${short(t.to)} : ${t.via.slice(agg.length + 1)}`);
16
+ }
17
+ for (const s of states)
18
+ if (s.attrs.terminal)
19
+ lines.push(` ${short(s.id)} --> [*]`);
20
+ for (const h of statesHoldingResources(g).filter((x) => x.state.startsWith(`${agg}.`))) {
21
+ lines.push(` note right of ${short(h.state)} : 持有 ${h.resource}`);
22
+ }
23
+ return lines.join('\n') + '\n';
24
+ }
25
+ function writeIf(path, content, check, written, findings, code = 'CAGE-GEN-001') {
26
+ const cur = existsSync(path) ? readFileSync(path, 'utf8') : null;
27
+ if (cur === content)
28
+ return;
29
+ if (check) {
30
+ findings.push({
31
+ code,
32
+ at: path,
33
+ reason: '生成物和声明不一致(有人手改了生成物,或声明改了没有重新生成)',
34
+ fix: '运行 cage gen,并提交生成结果',
35
+ });
36
+ return;
37
+ }
38
+ mkdirSync(dirname(path), { recursive: true });
39
+ writeFileSync(path, content);
40
+ written.push(path);
41
+ }
42
+ export function generate(cfg, reg, g, sites, opts = {}) {
43
+ const check = !!opts.check;
44
+ const written = [];
45
+ const findings = [];
46
+ const gen = abs(cfg, cfg.generated);
47
+ // 不入库的生成物:直接覆盖(check 模式不比较)
48
+ if (!check) {
49
+ const put = (rel, content) => {
50
+ const p = join(gen, rel);
51
+ mkdirSync(dirname(p), { recursive: true });
52
+ writeFileSync(p, content);
53
+ written.push(p);
54
+ };
55
+ mkdirSync(abs(cfg, '.cage'), { recursive: true });
56
+ writeFileSync(abs(cfg, '.cage/ontology.json'), JSON.stringify(g, null, 2));
57
+ const model = schemaModel(reg);
58
+ put('sql/schema.sql', `-- 生成物:期望的表结构(不要手改)\n\n${model.tables.map(createTableSql).join('\n\n')}\n`);
59
+ put('sql/safe-layer.sql', `-- 生成物:安全层(函数、行级策略、安全视图),cage migrate 每次整体重建\n${safeLayerSql(reg)}\n`);
60
+ put('capabilities.json', JSON.stringify(reg.capabilityCatalog(), null, 2));
61
+ put('routes.json', JSON.stringify(routes(reg), null, 2));
62
+ const md = ['# 状态图(生成物)', ''];
63
+ for (const a of reg.aggregates) {
64
+ const d = stateDiagram(g, a.name);
65
+ put(`diagrams/${a.name}.mmd`, d);
66
+ md.push(`## ${a.description ?? a.name}(${a.name})`, '', '```mermaid', d.trimEnd(), '```', '');
67
+ }
68
+ put('diagrams/README.md', md.join('\n'));
69
+ }
70
+ // 入库的生成物:前端契约
71
+ const files = contractsFiles(reg, sites);
72
+ for (const [name, content] of Object.entries(files))
73
+ writeIf(join(abs(cfg, cfg.contracts), name), content, check, written, findings);
74
+ // 迁移草稿:声明的期望结构 vs 最后一个迁移里内嵌的结构快照
75
+ const dir = abs(cfg, cfg.migrations);
76
+ const migrations = readMigrations(dir);
77
+ const model = schemaModel(reg);
78
+ const stmts = diffSchemas(latestSnapshot(migrations), model);
79
+ let migrationDraft;
80
+ if (stmts.length > 0) {
81
+ if (check) {
82
+ findings.push({
83
+ code: 'CAGE-GEN-002',
84
+ at: cfg.migrations,
85
+ reason: `声明的表结构与迁移不一致(${stmts.length} 处变化)`,
86
+ fix: '运行 cage gen 生成迁移草稿,人审后提交',
87
+ });
88
+ }
89
+ else {
90
+ const n = String(migrations.length + 1).padStart(4, '0');
91
+ const name = `${n}_${migrations.length === 0 ? 'init' : 'draft'}.sql`;
92
+ const body = [
93
+ `-- 迁移草稿:由 cage gen 生成,人审后提交;已发布的迁移不要修改`,
94
+ `-- 破坏性变更按「先扩展再收缩」分多次发布`,
95
+ '',
96
+ stmts.join('\n\n'),
97
+ ].join('\n');
98
+ mkdirSync(dir, { recursive: true });
99
+ migrationDraft = join(dir, name);
100
+ writeFileSync(migrationDraft, embedSnapshot(body, model));
101
+ written.push(migrationDraft);
102
+ }
103
+ }
104
+ return { written, findings, migrationDraft };
105
+ }
106
+ export function routes(reg) {
107
+ const out = [];
108
+ for (const c of reg.commands) {
109
+ if (!c.cmd.invokedBy.includes('user'))
110
+ continue;
111
+ const base = `/api/${kebab(c.cmd.agg.name)}`;
112
+ out.push({
113
+ method: 'POST',
114
+ url: c.cmd.from === 'new' ? `${base}/${kebab(c.name)}` : `${base}/:id/${kebab(c.name)}`,
115
+ capability: c.capability,
116
+ });
117
+ }
118
+ for (const a of reg.aggregates) {
119
+ const base = `/api/${kebab(a.name)}`;
120
+ out.push({ method: 'GET', url: base, capability: `${a.name}.read` }, { method: 'GET', url: `${base}/:id`, capability: `${a.name}.read` }, { method: 'POST', url: `${base}/export`, capability: `${a.name}.export` });
121
+ }
122
+ for (const q of reg.queries)
123
+ out.push({
124
+ method: 'GET',
125
+ url: `/api/queries/${kebab(q.name)}`,
126
+ capability: q.reads.map((r) => `${r.name}.read`).join(' + '),
127
+ });
128
+ for (const e of reg.extensions)
129
+ if (e.kind === 'INBOUND')
130
+ out.push({
131
+ method: 'POST',
132
+ url: `/hooks/${e.value.connector.name}/${kebab(e.value.event)}`,
133
+ capability: `验签 ${e.value.verify.secret}`,
134
+ });
135
+ return out;
136
+ }
137
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/gen/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import type { AnyField, Fields } from '@yss123/cage-kernel';
2
+ import { z } from 'zod';
3
+ export declare function zodCodeOf(schema: z.ZodType): string;
4
+ export declare function jsonToZod(s: any, root: any): string;
5
+ /** 读侧快照的字段 schema:敏感字段返回的是掩码值,所以只校验为字符串 */
6
+ export declare function fieldReadCode(f: AnyField): string;
7
+ export declare function snapshotCode(fields: Fields, states: readonly string[]): string;
@@ -0,0 +1,161 @@
1
+ import { z } from 'zod';
2
+ const str = (s) => JSON.stringify(s);
3
+ export function zodCodeOf(schema) {
4
+ let js;
5
+ try {
6
+ js = z.toJSONSchema(schema, { unrepresentable: 'any', io: 'input' });
7
+ }
8
+ catch {
9
+ return 'z.unknown()';
10
+ }
11
+ return jsonToZod(js, js);
12
+ }
13
+ function withChecks(base, s) {
14
+ let out = base;
15
+ if (s.type === 'string' || base.startsWith('z.string')) {
16
+ if (typeof s.minLength === 'number')
17
+ out += `.min(${s.minLength})`;
18
+ if (typeof s.maxLength === 'number')
19
+ out += `.max(${s.maxLength})`;
20
+ if (typeof s.pattern === 'string' && !s.format)
21
+ out += `.regex(new RegExp(${str(s.pattern)}))`;
22
+ }
23
+ if (s.type === 'number' || s.type === 'integer') {
24
+ if (typeof s.minimum === 'number')
25
+ out += `.min(${s.minimum})`;
26
+ if (typeof s.maximum === 'number')
27
+ out += `.max(${s.maximum})`;
28
+ if (typeof s.exclusiveMinimum === 'number')
29
+ out += `.gt(${s.exclusiveMinimum})`;
30
+ if (typeof s.exclusiveMaximum === 'number')
31
+ out += `.lt(${s.exclusiveMaximum})`;
32
+ }
33
+ if (s.type === 'array') {
34
+ if (typeof s.minItems === 'number')
35
+ out += `.min(${s.minItems})`;
36
+ if (typeof s.maxItems === 'number')
37
+ out += `.max(${s.maxItems})`;
38
+ }
39
+ return out;
40
+ }
41
+ export function jsonToZod(s, root) {
42
+ if (!s || typeof s !== 'object' || Object.keys(s).length === 0)
43
+ return 'z.unknown()';
44
+ if (s.$ref) {
45
+ const name = String(s.$ref).split('/').pop();
46
+ const def = root.$defs?.[name] ?? root.definitions?.[name];
47
+ return def ? jsonToZod(def, root) : 'z.unknown()';
48
+ }
49
+ if (s.const !== undefined)
50
+ return `z.literal(${JSON.stringify(s.const)})`;
51
+ if (Array.isArray(s.enum)) {
52
+ if (s.enum.every((v) => typeof v === 'string'))
53
+ return `z.enum([${s.enum.map(str).join(', ')}])`;
54
+ return `z.union([${s.enum.map((v) => `z.literal(${JSON.stringify(v)})`).join(', ')}])`;
55
+ }
56
+ const variants = s.anyOf ?? s.oneOf;
57
+ if (Array.isArray(variants)) {
58
+ const nonNull = variants.filter((v) => v.type !== 'null');
59
+ const nullable = nonNull.length !== variants.length;
60
+ const inner = nonNull.length === 1
61
+ ? jsonToZod(nonNull[0], root)
62
+ : `z.union([${nonNull.map((v) => jsonToZod(v, root)).join(', ')}])`;
63
+ return nullable ? `${inner}.nullable()` : inner;
64
+ }
65
+ if (Array.isArray(s.type)) {
66
+ const nonNull = s.type.filter((t) => t !== 'null');
67
+ const inner = nonNull.length === 1 ? jsonToZod({ ...s, type: nonNull[0] }, root) : 'z.unknown()';
68
+ return s.type.includes('null') ? `${inner}.nullable()` : inner;
69
+ }
70
+ switch (s.type) {
71
+ case 'string': {
72
+ if (s.format === 'uuid')
73
+ return 'z.string().uuid()';
74
+ if (s.format === 'email')
75
+ return withChecks('z.string().email()', { ...s, pattern: undefined });
76
+ if (s.format === 'date-time')
77
+ return 'z.string().datetime({ offset: true })';
78
+ return withChecks('z.string()', s);
79
+ }
80
+ case 'integer':
81
+ return withChecks('z.number().int()', s);
82
+ case 'number':
83
+ return withChecks('z.number()', s);
84
+ case 'boolean':
85
+ return 'z.boolean()';
86
+ case 'null':
87
+ return 'z.null()';
88
+ case 'array':
89
+ return withChecks(`z.array(${jsonToZod(s.items ?? {}, root)})`, s);
90
+ case 'object': {
91
+ const props = Object.entries(s.properties ?? {});
92
+ if (props.length === 0 && s.additionalProperties && typeof s.additionalProperties === 'object') {
93
+ return `z.record(z.string(), ${jsonToZod(s.additionalProperties, root)})`;
94
+ }
95
+ if (props.length === 0)
96
+ return 'z.record(z.string(), z.unknown())';
97
+ const req = new Set(s.required ?? []);
98
+ const body = props
99
+ .map(([k, v]) => `${/^[A-Za-z_$][\w$]*$/.test(k) ? k : str(k)}: ${jsonToZod(v, root)}${req.has(k) ? '' : '.optional()'}`)
100
+ .join(', ');
101
+ return `z.object({ ${body} })`;
102
+ }
103
+ default:
104
+ return 'z.unknown()';
105
+ }
106
+ }
107
+ /** 读侧快照的字段 schema:敏感字段返回的是掩码值,所以只校验为字符串 */
108
+ export function fieldReadCode(f) {
109
+ let base;
110
+ if (f.sensitiveKind)
111
+ base = 'z.string()';
112
+ else {
113
+ switch (f.kind) {
114
+ case 'int':
115
+ base = 'z.number().int()';
116
+ break;
117
+ case 'number':
118
+ base = 'z.number()';
119
+ break;
120
+ case 'bool':
121
+ base = 'z.boolean()';
122
+ break;
123
+ case 'id':
124
+ case 'ref':
125
+ base = 'z.string().uuid()';
126
+ break;
127
+ case 'enum':
128
+ base = `z.enum([${(f.values ?? []).map(str).join(', ')}])`;
129
+ break;
130
+ case 'list': {
131
+ const shape = f.shape ?? {};
132
+ if ('item' in shape && Object.keys(shape).length === 1)
133
+ base = `z.array(${fieldReadCode(shape.item)})`;
134
+ else if (Object.keys(shape).length === 0)
135
+ base = 'z.record(z.string(), z.unknown())';
136
+ else
137
+ base = `z.array(z.object({ ${Object.entries(shape)
138
+ .map(([k, sf]) => `${k}: ${fieldReadCode(sf)}`)
139
+ .join(', ')} }))`;
140
+ break;
141
+ }
142
+ default:
143
+ base = 'z.string()';
144
+ }
145
+ }
146
+ return f.isOptional ? `${base}.nullable().optional()` : base;
147
+ }
148
+ export function snapshotCode(fields, states) {
149
+ const lines = [
150
+ 'id: z.string().uuid()',
151
+ ...Object.entries(fields)
152
+ .filter(([, f]) => !f.isHidden)
153
+ .map(([k, f]) => `${k}: ${fieldReadCode(f)}`),
154
+ `status: z.enum([${states.map(str).join(', ')}])`,
155
+ 'version: z.number().int()',
156
+ 'createdAt: z.string()',
157
+ 'updatedAt: z.string()',
158
+ ];
159
+ return `z.object({\n${lines.map((l) => ` ${l},`).join('\n')}\n})`;
160
+ }
161
+ //# sourceMappingURL=zod-code.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"zod-code.js","sourceRoot":"","sources":["../../src/gen/zod-code.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import type { Finding } from './findings.js';
2
+ import type { Ontology } from './ontology.js';
3
+ export interface GraphSnapshot {
4
+ project: string;
5
+ domain: string;
6
+ generatedAt: string;
7
+ graph: Ontology;
8
+ findings: Finding[];
9
+ presentation: {
10
+ title: string;
11
+ labels: Record<string, string>;
12
+ };
13
+ }
@@ -0,0 +1,46 @@
1
+ // 图谱每次刷新都在独立进程抽取。ESM 会缓存 domain 及其依赖;只给入口添加
2
+ // 时间戳无法刷新所有间接导入,因此不在常驻 Web 服务里重复 import domain。
3
+ import { analyze } from './analyze.js';
4
+ import { loadConfig } from './config.js';
5
+ async function collect() {
6
+ const cfg = loadConfig(process.argv[2]);
7
+ const result = await analyze(cfg, { write: true });
8
+ if (!result.graph) {
9
+ throw new Error(result.findings.map((f) => `[${f.code}] ${f.reason}`).join('\n'));
10
+ }
11
+ // 优先复用声明已有的描述。图谱文案单独传递,不给本体节点改名或改哈希,
12
+ // 避免一次中文标题调整被误认为命令、能力点或数据库结构发生变化。
13
+ const labels = {};
14
+ const remember = (kind, id, description) => {
15
+ if (description)
16
+ labels[`${kind}:${id}`] = description;
17
+ };
18
+ const registry = result.registry;
19
+ for (const a of registry.aggregates)
20
+ remember('aggregate', a.name, a.description);
21
+ for (const c of registry.commands)
22
+ remember('command', c.capability, c.cmd.description);
23
+ for (const e of registry.events)
24
+ remember('event', e.name, e.description);
25
+ for (const q of registry.queries)
26
+ remember('query', q.name, q.description);
27
+ for (const j of registry.jobs)
28
+ remember('job', j.name, j.description);
29
+ // 有图规则问题时仍展示图和问题列表,开发者可以借助关系图定位错误。
30
+ const snapshot = {
31
+ project: cfg.name,
32
+ domain: cfg.domain,
33
+ generatedAt: new Date().toISOString(),
34
+ graph: result.graph,
35
+ findings: result.findings,
36
+ presentation: { title: cfg.graph?.title ?? cfg.name, labels: { ...labels, ...cfg.graph?.labels } },
37
+ };
38
+ process.send({ snapshot });
39
+ }
40
+ collect()
41
+ .catch((error) => {
42
+ process.send({ error: error.message });
43
+ process.exitCode = 1;
44
+ })
45
+ .finally(() => process.disconnect());
46
+ //# sourceMappingURL=graph-snapshot.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"graph-snapshot.js","sourceRoot":"","sources":["../src/graph-snapshot.ts"],"names":[],"mappings":"AAAA,4CAA4C;AAC5C,gDAAgD;AAChD,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAazC,KAAK,UAAU,OAAO;IACpB,MAAM,GAAG,GAAG,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;IACxC,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACnD,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;QAClB,MAAM,IAAI,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IACpF,CAAC;IACD,qCAAqC;IACrC,kCAAkC;IAClC,MAAM,MAAM,GAA2B,EAAE,CAAC;IAC1C,MAAM,QAAQ,GAAG,CAAC,IAAY,EAAE,EAAU,EAAE,WAAoB,EAAE,EAAE;QAClE,IAAI,WAAW;YAAE,MAAM,CAAC,GAAG,IAAI,IAAI,EAAE,EAAE,CAAC,GAAG,WAAW,CAAC;IACzD,CAAC,CAAC;IACF,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAS,CAAC;IAClC,KAAK,MAAM,CAAC,IAAI,QAAQ,CAAC,UAAU;QAAE,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,WAAW,CAAC,CAAC;IAClF,KAAK,MAAM,CAAC,IAAI,QAAQ,CAAC,QAAQ;QAAE,QAAQ,CAAC,SAAS,EAAE,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;IACxF,KAAK,MAAM,CAAC,IAAI,QAAQ,CAAC,MAAM;QAAE,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,WAAW,CAAC,CAAC;IAC1E,KAAK,MAAM,CAAC,IAAI,QAAQ,CAAC,OAAO;QAAE,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,WAAW,CAAC,CAAC;IAC3E,KAAK,MAAM,CAAC,IAAI,QAAQ,CAAC,IAAI;QAAE,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,WAAW,CAAC,CAAC;IACtE,mCAAmC;IACnC,MAAM,QAAQ,GAAkB;QAC9B,OAAO,EAAE,GAAG,CAAC,IAAI;QACjB,MAAM,EAAE,GAAG,CAAC,MAAM;QAClB,WAAW,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACrC,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,YAAY,EAAE,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,KAAK,IAAI,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,MAAM,EAAE,GAAG,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE;KACnG,CAAC;IACF,OAAO,CAAC,IAAK,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC;AAC9B,CAAC;AAED,OAAO,EAAE;KACN,KAAK,CAAC,CAAC,KAAY,EAAE,EAAE;IACtB,OAAO,CAAC,IAAK,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;IACxC,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;AACvB,CAAC,CAAC;KACD,OAAO,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC"}
@@ -0,0 +1,11 @@
1
+ import type { CageConfig } from './config.js';
2
+ import type { GraphSnapshot } from './graph-snapshot.js';
3
+ /** 只采集声明元数据,不连接业务数据库。子进程的 IPC 把日志和 JSON 分开,
4
+ * domain 的普通日志不会污染响应,也不会把进程环境变量发送给浏览器。 */
5
+ export declare function collectGraph(root: string): Promise<GraphSnapshot>;
6
+ /** 本地开发工具只监听回环地址;固定路由表不接受文件路径,不能拿来读取
7
+ * 项目的 .env、源码或任意本机文件。它不挂载到业务 API 或生产 /admin。 */
8
+ export declare function serveGraph(cfg: CageConfig, port?: number): Promise<{
9
+ server: import("http").Server<typeof import("http").IncomingMessage, typeof import("http").ServerResponse>;
10
+ url: string;
11
+ }>;
package/dist/graph.js ADDED
@@ -0,0 +1,87 @@
1
+ import { fork } from 'node:child_process';
2
+ import { readFile } from 'node:fs/promises';
3
+ import { createServer } from 'node:http';
4
+ import { fileURLToPath } from 'node:url';
5
+ /** 只采集声明元数据,不连接业务数据库。子进程的 IPC 把日志和 JSON 分开,
6
+ * domain 的普通日志不会污染响应,也不会把进程环境变量发送给浏览器。 */
7
+ export function collectGraph(root) {
8
+ return new Promise((resolve, reject) => {
9
+ const child = fork(fileURLToPath(new URL('./graph-snapshot.js', import.meta.url)), [root], {
10
+ windowsHide: true,
11
+ stdio: ['ignore', 'ignore', 'pipe', 'ipc'],
12
+ });
13
+ let failure = '';
14
+ child.stderr.on('data', (chunk) => {
15
+ failure += String(chunk);
16
+ });
17
+ child.once('message', (message) => {
18
+ if (message.snapshot)
19
+ resolve(message.snapshot);
20
+ else
21
+ reject(new Error(message.error));
22
+ });
23
+ child.once('error', reject);
24
+ child.once('exit', (code) => reject(new Error(failure || `本体抽取进程退出(${code})`)));
25
+ });
26
+ }
27
+ /** 本地开发工具只监听回环地址;固定路由表不接受文件路径,不能拿来读取
28
+ * 项目的 .env、源码或任意本机文件。它不挂载到业务 API 或生产 /admin。 */
29
+ export async function serveGraph(cfg, port = 15418) {
30
+ let snapshot = await collectGraph(cfg.root);
31
+ let refreshing;
32
+ const assets = new Map([
33
+ ['/', ['index.html', 'text/html; charset=utf-8']],
34
+ ['/app.js', ['app.js', 'text/javascript; charset=utf-8']],
35
+ ['/model.js', ['model.js', 'text/javascript; charset=utf-8']],
36
+ ['/relations.js', ['relations.js', 'text/javascript; charset=utf-8']],
37
+ ['/structure.js', ['structure.js', 'text/javascript; charset=utf-8']],
38
+ ['/style.css', ['style.css', 'text/css; charset=utf-8']],
39
+ ]);
40
+ let origin = '';
41
+ const server = createServer(async (req, res) => {
42
+ res.setHeader('Cache-Control', 'no-store');
43
+ res.setHeader('X-Content-Type-Options', 'nosniff');
44
+ res.setHeader('Content-Security-Policy', "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' blob: data:; object-src 'none'; base-uri 'none'; frame-ancestors 'none'");
45
+ const json = (status, data) => {
46
+ res.writeHead(status, { 'Content-Type': 'application/json; charset=utf-8' });
47
+ res.end(JSON.stringify(data));
48
+ };
49
+ // 限制 Host 和刷新来源,避免其他网站借用户浏览器触发本地模块求值。
50
+ if (req.headers.host !== new URL(origin).host)
51
+ return json(403, { error: '请使用终端输出的本地地址访问' });
52
+ const path = new URL(req.url, origin).pathname;
53
+ try {
54
+ if (req.method === 'GET' && path === '/api/graph')
55
+ return json(200, snapshot);
56
+ if (req.method === 'POST' && path === '/api/refresh') {
57
+ if (req.headers.origin !== origin)
58
+ return json(403, { error: '刷新必须由本地图谱页面发起' });
59
+ // 同时点击刷新共享一次抽取;失败向页面报告,不伪装成新图。
60
+ refreshing ??= collectGraph(cfg.root).finally(() => {
61
+ refreshing = undefined;
62
+ });
63
+ snapshot = await refreshing;
64
+ return json(200, snapshot);
65
+ }
66
+ const asset = req.method === 'GET' ? assets.get(path) : undefined;
67
+ if (!asset)
68
+ return json(404, { error: '页面不存在' });
69
+ const content = await readFile(new URL(`../graph-ui/${asset[0]}`, import.meta.url));
70
+ res.writeHead(200, { 'Content-Type': asset[1] });
71
+ res.end(content);
72
+ }
73
+ catch (error) {
74
+ json(500, { error: error.message });
75
+ }
76
+ });
77
+ await new Promise((resolve, reject) => {
78
+ server.once('error', reject);
79
+ server.listen(port, '127.0.0.1', () => {
80
+ const address = server.address();
81
+ origin = `http://127.0.0.1:${address.port}`;
82
+ resolve();
83
+ });
84
+ });
85
+ return { server, url: origin };
86
+ }
87
+ //# sourceMappingURL=graph.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"graph.js","sourceRoot":"","sources":["../src/graph.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ import type { DerivedTest } from './derive.js';
2
+ import { type NodeDiff, type Ontology } from './ontology.js';
3
+ import type { Finding } from './findings.js';
4
+ export interface ImpactResult {
5
+ diff: NodeDiff;
6
+ start: string[];
7
+ affected: string[];
8
+ /** 受影响节点 → 通过哪条边、以什么身份受影响 */
9
+ reasons: Map<string, string>;
10
+ tests: DerivedTest[];
11
+ projectTests: string[];
12
+ confirm: string[];
13
+ unsafe: {
14
+ base: number;
15
+ head: number;
16
+ };
17
+ findings: Finding[];
18
+ }
19
+ export declare function isGitRepo(root: string): boolean;
20
+ export declare function changedFiles(root: string, base: string): string[];
21
+ export declare function readOntology(path: string): Ontology;
22
+ export declare function impact(base: Ontology, head: Ontology, files: string[], tests?: DerivedTest[], projectTestFiles?: string[]): ImpactResult;
23
+ export declare function formatImpact(r: ImpactResult, head: Ontology, opts: {
24
+ base: string;
25
+ format: 'text' | 'md';
26
+ title?: string;
27
+ gates?: string;
28
+ }): string;