@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
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 shunshunyue and Cage contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,19 @@
1
- # Temporary Holding Version
1
+ # @yss123/cage-cli
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ cage 命令行:gen、check、impact、test、verify、budget、sync、upgrade、dev、migrate
4
+
5
+ Cage 是通过 TypeScript 声明业务、由框架统一执行权限和数据规则的中后台框架。此包是框架的一部分。
6
+
7
+ ## 使用
8
+
9
+ ```powershell
10
+ pnpm add -D @yss123/cage-cli
11
+ ```
12
+
13
+ 首次使用建议通过脚手架创建完整项目,所需依赖由模板统一配置。需要 Node.js 24 或更新版本。
14
+
15
+ - [完整使用说明](https://github.com/shunshunyue/cage/blob/main/docs/使用说明.md)
16
+ - [框架源码与范例](https://github.com/shunshunyue/cage)
17
+ - [问题反馈](https://github.com/shunshunyue/cage/issues)
18
+
19
+ 许可证:[MIT](https://github.com/shunshunyue/cage/blob/main/LICENSE)。
package/bin/cage.js ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env node
2
+ import { main } from '../dist/main.js';
3
+ main().then((code) => { process.exitCode = code; }).catch((error) => {
4
+ console.error(error.message);
5
+ process.exitCode = 1;
6
+ });
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env node
2
+ import { main } from '../dist/main.js';
3
+ main(['create', ...process.argv.slice(2)]).then((code) => { process.exitCode = code; }).catch((error) => {
4
+ console.error(error.message);
5
+ process.exitCode = 1;
6
+ });
@@ -0,0 +1,18 @@
1
+ import { type CageConfig } from './config.js';
2
+ import { type Finding } from './findings.js';
3
+ import { type Ontology } from './ontology.js';
4
+ /** 静态检查先于模块求值;发现动态声明或 IO 时不执行模块。 */
5
+ export declare function analyze(cfg: CageConfig, opts?: {
6
+ base?: Ontology;
7
+ write?: boolean;
8
+ }): Promise<{
9
+ source: import("./static-check.js").StaticResult;
10
+ findings: Finding[];
11
+ registry?: undefined;
12
+ graph?: undefined;
13
+ } | {
14
+ source: import("./static-check.js").StaticResult;
15
+ registry: import("@yss123/cage-kernel").Registry;
16
+ graph: Ontology;
17
+ findings: Finding[];
18
+ }>;
@@ -0,0 +1,26 @@
1
+ import { mkdirSync, writeFileSync } from 'node:fs';
2
+ import { abs } from './config.js';
3
+ import { errorsOnly } from './findings.js';
4
+ import { loadRegistry } from './load.js';
5
+ import { extract } from './ontology.js';
6
+ import { runRules } from './rules.js';
7
+ import { staticCheck, unexportedFindings } from './static-check.js';
8
+ /** 静态检查先于模块求值;发现动态声明或 IO 时不执行模块。 */
9
+ export async function analyze(cfg, opts = {}) {
10
+ const source = staticCheck(cfg);
11
+ if (errorsOnly(source.findings).length)
12
+ return { source, findings: source.findings };
13
+ const { mod, registry } = await loadRegistry(abs(cfg, cfg.domain));
14
+ const graph = extract(registry, source.sites, source.unsafeCount);
15
+ const findings = [
16
+ ...source.findings,
17
+ ...unexportedFindings(source.sites, new Set(Object.keys(mod))),
18
+ ...runRules(graph, opts.base),
19
+ ];
20
+ if (opts.write) {
21
+ mkdirSync(abs(cfg, '.cage'), { recursive: true });
22
+ writeFileSync(abs(cfg, '.cage/ontology.json'), JSON.stringify(graph, null, 2) + '\n');
23
+ }
24
+ return { source, registry, graph, findings };
25
+ }
26
+ //# sourceMappingURL=analyze.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analyze.js","sourceRoot":"","sources":["../src/analyze.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,GAAG,EAAmB,MAAM,aAAa,CAAC;AACnD,OAAO,EAAE,UAAU,EAAgB,MAAM,eAAe,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,EAAE,OAAO,EAAiB,MAAM,eAAe,CAAC;AACvD,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAEpE,oCAAoC;AACpC,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,GAAe,EAAE,OAA6C,EAAE;IAC5F,MAAM,MAAM,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC;IAChC,IAAI,UAAU,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,MAAM;QAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,CAAC;IACrF,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,MAAM,YAAY,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;IACnE,MAAM,KAAK,GAAG,OAAO,CAAC,QAAQ,EAAE,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC;IAClE,MAAM,QAAQ,GAAc;QAC1B,GAAG,MAAM,CAAC,QAAQ;QAClB,GAAG,kBAAkB,CAAC,MAAM,CAAC,KAAK,EAAE,IAAI,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;QAC9D,GAAG,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAC,IAAI,CAAC;KAC9B,CAAC;IACF,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;QACf,SAAS,CAAC,GAAG,CAAC,GAAG,EAAE,OAAO,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAClD,aAAa,CAAC,GAAG,CAAC,GAAG,EAAE,qBAAqB,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;IACxF,CAAC;IACD,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC;AAC/C,CAAC"}
@@ -0,0 +1,5 @@
1
+ import { type CageConfig } from './config.js';
2
+ export declare function loadBaseline(cfg: CageConfig, opts: {
3
+ base?: string;
4
+ baseFile?: string;
5
+ }): Promise<import("./ontology.js").Ontology | undefined>;
@@ -0,0 +1,50 @@
1
+ // 基线来自显式 JSON 文件,或 Git 提交里的业务声明。
2
+ // Git 比较在独立临时工作树中抽取,不切换用户当前分支,也不安装/执行基线脚本。
3
+ import { execFileSync } from 'node:child_process';
4
+ import { existsSync, mkdirSync, mkdtempSync, rmSync, symlinkSync, unlinkSync } from 'node:fs';
5
+ import { dirname, join, relative } from 'node:path';
6
+ import { fileURLToPath } from 'node:url';
7
+ import { abs } from './config.js';
8
+ import { readOntology } from './impact.js';
9
+ import { run } from './process.js';
10
+ import { projectManifests } from './upgrade.js';
11
+ export async function loadBaseline(cfg, opts) {
12
+ if (opts.baseFile)
13
+ return readOntology(abs(cfg, opts.baseFile));
14
+ if (!opts.base)
15
+ return undefined;
16
+ const git = (args) => execFileSync('git', args, { cwd: cfg.root, encoding: 'utf8', windowsHide: true }).trim();
17
+ const repository = git(['rev-parse', '--show-toplevel']);
18
+ // 先解析 ref,错误基线必须报错,不能把“基线不存在”解释为“没有变化”。
19
+ const commit = git(['rev-parse', '--verify', `${opts.base}^{commit}`]);
20
+ mkdirSync(abs(cfg, '.cage'), { recursive: true });
21
+ const temporary = mkdtempSync(abs(cfg, '.cage/baseline-'));
22
+ const checkout = join(temporary, 'checkout');
23
+ git(['worktree', 'add', '--detach', checkout, commit]);
24
+ const links = [];
25
+ try {
26
+ const project = join(checkout, relative(repository, cfg.root));
27
+ // 使用当前已经安装的框架版本;基线与当前声明由同一抽取器解释。
28
+ // pnpm 把依赖链接放在各 workspace 包内,单独链接根目录不足以解析 domain。
29
+ for (const original of new Set([repository, cfg.root, ...projectManifests(repository).map(dirname)])) {
30
+ const destination = join(checkout, relative(repository, original));
31
+ if (existsSync(destination) && existsSync(join(original, 'node_modules')) && !existsSync(join(destination, 'node_modules'))) {
32
+ symlinkSync(join(original, 'node_modules'), join(destination, 'node_modules'), process.platform === 'win32' ? 'junction' : 'dir');
33
+ links.push(join(destination, 'node_modules'));
34
+ }
35
+ }
36
+ const code = await run(fileURLToPath(new URL('../bin/cage.js', import.meta.url)), ['analyze', '--root', project], project);
37
+ if (code !== 0)
38
+ throw new Error('[CAGE-IMPACT-001] 基线抽取失败,请修复基线声明或使用保存的 --base-file');
39
+ return readOntology(join(project, '.cage/ontology.json'));
40
+ }
41
+ finally {
42
+ // 先断开本次建立的 junction,避免清理工作树时遍历共享依赖目录。
43
+ for (const link of links)
44
+ unlinkSync(link);
45
+ git(['worktree', 'remove', '--force', checkout]);
46
+ // temporary 是本次 mkdtemp 创建的绝对目录,只移除自己的临时目录。
47
+ rmSync(temporary, { recursive: true, force: true });
48
+ }
49
+ }
50
+ //# sourceMappingURL=baseline.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"baseline.js","sourceRoot":"","sources":["../src/baseline.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import { type CageConfig } from './config.js';
2
+ import type { Ontology } from './ontology.js';
3
+ import type { Finding } from './findings.js';
4
+ export declare function budget(cfg: CageConfig, graph: Ontology, base?: Ontology): {
5
+ metrics: Record<string, number | null>;
6
+ findings: Finding[];
7
+ };
package/dist/budget.js ADDED
@@ -0,0 +1,52 @@
1
+ import { existsSync, readFileSync } from 'node:fs';
2
+ import { abs } from './config.js';
3
+ import { listTs } from './static-check.js';
4
+ export function budget(cfg, graph, base) {
5
+ const kernel = abs(cfg, 'packages/kernel/src');
6
+ const lines = existsSync(kernel)
7
+ ? listTs(kernel).reduce((n, f) => n +
8
+ readFileSync(f, 'utf8')
9
+ .split('\n')
10
+ .filter((l) => l.trim() && !l.trim().startsWith('//')).length, 0)
11
+ : null;
12
+ const primitivesFile = abs(cfg, 'packages/kernel/src/brands.ts');
13
+ // 使用公共词汇表计数;品牌还含内部扩展标记,不能把两者混算。
14
+ const families = {
15
+ CONNECTOR: 'external',
16
+ OUTBOUND: 'external',
17
+ INBOUND: 'external',
18
+ ROUTE: 'approval',
19
+ APPROVAL: 'approval',
20
+ };
21
+ const vocabulary = new Set(['field', 'capability', 'scope', 'constraint', 'invariant']);
22
+ if (existsSync(primitivesFile)) {
23
+ for (const match of readFileSync(primitivesFile, 'utf8').matchAll(/export const (\w+) = Symbol\.for\(/g)) {
24
+ if (['CONSUMERS', 'SYSTEM_DDL'].includes(match[1]))
25
+ continue;
26
+ vocabulary.add(families[match[1]] ?? match[1].toLowerCase());
27
+ }
28
+ }
29
+ const primitives = existsSync(primitivesFile) ? vocabulary.size : null;
30
+ const findings = [];
31
+ if (lines !== null && lines > 5000)
32
+ findings.push({
33
+ code: 'CAGE-BUDGET-001',
34
+ reason: `内核 ${lines} 行,超过 5000 行预算`,
35
+ fix: '拆出扩展职责,或提交架构评审调整预算',
36
+ });
37
+ if (primitives !== null && primitives > 16)
38
+ findings.push({
39
+ code: 'CAGE-BUDGET-002',
40
+ reason: `品牌原语 ${primitives} 个,超过 16 个预算`,
41
+ fix: '优先使用组合子,避免新增原语',
42
+ });
43
+ const allowance = base?.unsafeCount ?? 0;
44
+ if (graph.unsafeCount > allowance)
45
+ findings.push({
46
+ code: 'CAGE-BUDGET-003',
47
+ reason: `unsafe 从 ${allowance} 增加到 ${graph.unsafeCount}`,
48
+ fix: '移除新增逃生口,或在评审完成后更新受审基线',
49
+ });
50
+ return { metrics: { kernelLines: lines, primitives, unsafe: graph.unsafeCount }, findings };
51
+ }
52
+ //# sourceMappingURL=budget.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"budget.js","sourceRoot":"","sources":["../src/budget.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ export interface CageConfig {
2
+ root: string;
3
+ name: string;
4
+ /** domain 包入口:抽取器和内核都从这里读 */
5
+ domain: string;
6
+ /** 前端 SDK 生成目录 */
7
+ contracts: string;
8
+ /** contracts 包名(前端从这里引入) */
9
+ contractsPackage: string;
10
+ migrations: string;
11
+ generated: string;
12
+ apiEntry: string;
13
+ workerEntry: string;
14
+ web: string;
15
+ /** 需要类型检查的目录(各自有 tsconfig.json) */
16
+ typecheck: string[];
17
+ /** lint 与依赖规则检查的目录 */
18
+ lint: string[];
19
+ /** 本地开发数据库端口;用于 Docker 初始配置及未配置外部连接时的内嵌实例 */
20
+ devDbPort: number;
21
+ /** 图谱的显示文案独立于业务标识;键为“节点类型:节点 ID”,不影响路由、
22
+ * 数据表、能力点和影响分析。可用短标题覆盖较长的声明 description。 */
23
+ graph?: {
24
+ title?: string;
25
+ labels?: Record<string, string>;
26
+ };
27
+ }
28
+ export declare function findRoot(start?: string): string;
29
+ export declare function loadConfig(start?: string): CageConfig;
30
+ export declare const abs: (cfg: CageConfig, p: string) => string;
package/dist/config.js ADDED
@@ -0,0 +1,47 @@
1
+ // 项目配置:默认值对应模板生成的目录结构,cage.config.json 可以覆盖。
2
+ import { existsSync, readFileSync } from 'node:fs';
3
+ import { dirname, join, resolve } from 'node:path';
4
+ export function findRoot(start = process.cwd()) {
5
+ let dir = resolve(start);
6
+ for (;;) {
7
+ if (existsSync(join(dir, 'cage.config.json')))
8
+ return dir;
9
+ const pkg = join(dir, 'package.json');
10
+ if (existsSync(pkg)) {
11
+ const j = JSON.parse(readFileSync(pkg, 'utf8'));
12
+ if (j.devDependencies?.['@yss123/cage-cli'] || j.dependencies?.['@yss123/cage-cli'] || j.cage)
13
+ return dir;
14
+ }
15
+ const up = dirname(dir);
16
+ if (up === dir)
17
+ return resolve(start);
18
+ dir = up;
19
+ }
20
+ }
21
+ export function loadConfig(start) {
22
+ const root = findRoot(start);
23
+ const file = join(root, 'cage.config.json');
24
+ const user = existsSync(file) ? JSON.parse(readFileSync(file, 'utf8')) : {};
25
+ const pkg = existsSync(join(root, 'package.json'))
26
+ ? JSON.parse(readFileSync(join(root, 'package.json'), 'utf8'))
27
+ : {};
28
+ const c = {
29
+ name: pkg.name ?? 'cage-app',
30
+ domain: 'packages/domain/src/index.ts',
31
+ contracts: 'packages/contracts/src',
32
+ contractsPackage: '@app/contracts',
33
+ migrations: 'db/migrations',
34
+ generated: 'generated',
35
+ apiEntry: 'apps/api/src/main.ts',
36
+ workerEntry: 'apps/api/src/worker.ts',
37
+ web: 'apps/web',
38
+ typecheck: ['packages/domain', 'packages/contracts', 'apps/api', 'apps/web'],
39
+ lint: ['packages/domain/src', 'apps/api/src', 'apps/web/src'],
40
+ devDbPort: 54329,
41
+ ...pkg.cage,
42
+ ...user,
43
+ };
44
+ return { ...c, root };
45
+ }
46
+ export const abs = (cfg, p) => resolve(cfg.root, p);
47
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,6CAA6C;AAC7C,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA2BnD,MAAM,UAAU,QAAQ,CAAC,KAAK,GAAG,OAAO,CAAC,GAAG,EAAE;IAC5C,IAAI,GAAG,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;IACzB,SAAS,CAAC;QACR,IAAI,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE,kBAAkB,CAAC,CAAC;YAAE,OAAO,GAAG,CAAC;QAC1D,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC;QACtC,IAAI,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YACpB,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,CAAC;YAChD,IAAI,CAAC,CAAC,eAAe,EAAE,CAAC,kBAAkB,CAAC,IAAI,CAAC,CAAC,YAAY,EAAE,CAAC,kBAAkB,CAAC,IAAI,CAAC,CAAC,IAAI;gBAAE,OAAO,GAAG,CAAC;QAC5G,CAAC;QACD,MAAM,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;QACxB,IAAI,EAAE,KAAK,GAAG;YAAE,OAAO,OAAO,CAAC,KAAK,CAAC,CAAC;QACtC,GAAG,GAAG,EAAE,CAAC;IACX,CAAC;AACH,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,KAAc;IACvC,MAAM,IAAI,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC7B,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE,kBAAkB,CAAC,CAAC;IAC5C,MAAM,IAAI,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAC5E,MAAM,GAAG,GAAG,UAAU,CAAC,IAAI,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC;QAChD,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE,cAAc,CAAC,EAAE,MAAM,CAAC,CAAC;QAC9D,CAAC,CAAC,EAAE,CAAC;IACP,MAAM,CAAC,GAAG;QACR,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,UAAU;QAC5B,MAAM,EAAE,8BAA8B;QACtC,SAAS,EAAE,wBAAwB;QACnC,gBAAgB,EAAE,gBAAgB;QAClC,UAAU,EAAE,eAAe;QAC3B,SAAS,EAAE,WAAW;QACtB,QAAQ,EAAE,sBAAsB;QAChC,WAAW,EAAE,wBAAwB;QACrC,GAAG,EAAE,UAAU;QACf,SAAS,EAAE,CAAC,iBAAiB,EAAE,oBAAoB,EAAE,UAAU,EAAE,UAAU,CAAC;QAC5E,IAAI,EAAE,CAAC,qBAAqB,EAAE,cAAc,EAAE,cAAc,CAAC;QAC7D,SAAS,EAAE,KAAK;QAChB,GAAG,GAAG,CAAC,IAAI;QACX,GAAG,IAAI;KACR,CAAC;IACF,OAAO,EAAE,GAAG,CAAC,EAAE,IAAI,EAAE,CAAC;AACxB,CAAC;AAED,MAAM,CAAC,MAAM,GAAG,GAAG,CAAC,GAAe,EAAE,CAAS,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC"}
@@ -0,0 +1 @@
1
+ export declare function createApp(target: string, frameworkPath?: string): string;
package/dist/create.js ADDED
@@ -0,0 +1,47 @@
1
+ import { cpSync, existsSync, readFileSync, writeFileSync } from 'node:fs';
2
+ import { basename, dirname, join, relative, resolve } from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+ import { VERSION, projectManifests } from './upgrade.js';
5
+ export function createApp(target, frameworkPath) {
6
+ const root = resolve(target);
7
+ if (existsSync(root))
8
+ throw new Error('[CAGE-CREATE-001] 目标路径已存在,请选择新目录');
9
+ const here = dirname(fileURLToPath(import.meta.url));
10
+ const template = [resolve(here, '../templates/app'), resolve(here, '../../../templates/app')].find(existsSync);
11
+ if (!template)
12
+ throw new Error('[CAGE-CREATE-001] CLI 发布包缺少模板');
13
+ cpSync(template, root, {
14
+ recursive: true,
15
+ filter: (p) => {
16
+ // 安装后的模板本身就在 node_modules 中,只排除模板内部的运行目录。
17
+ // 若检查绝对路径,npm 安装场景下连模板根目录都会被过滤,产生空项目。
18
+ const local = relative(template, p);
19
+ return !/(^|[\\/])(node_modules|dist|\.cage|generated)([\\/]|$)/.test(local) &&
20
+ (!/^\.env(?:\..+)?$/.test(basename(p)) || basename(p) === '.env.example');
21
+ },
22
+ });
23
+ // 报错链接在新项目中也能直接打开,不要求用户另行克隆框架文档。
24
+ const errors = [resolve(here, '../managed/errors'), resolve(here, '../../../docs/errors')].find(existsSync);
25
+ if (errors)
26
+ cpSync(errors, join(root, 'docs/errors'), { recursive: true });
27
+ for (const path of projectManifests(root)) {
28
+ const p = JSON.parse(readFileSync(path, 'utf8'));
29
+ // 发布后使用统一版本号;源码试用用 link 指向已构建的本地框架,不要求先发 npm。
30
+ for (const group of ['dependencies', 'devDependencies'])
31
+ for (const name of Object.keys(p[group] ?? {}))
32
+ if (name.startsWith('@yss123/cage-')) {
33
+ p[group][name] = frameworkPath
34
+ ? `link:${resolve(frameworkPath, 'packages', name.slice('@yss123/cage-'.length)).replace(/\\/g, '/')}`
35
+ : VERSION;
36
+ }
37
+ if (path === join(root, 'package.json'))
38
+ p.name = root
39
+ .split(/[\\/]/)
40
+ .pop()
41
+ .toLowerCase()
42
+ .replace(/[^a-z0-9-]/g, '-');
43
+ writeFileSync(path, JSON.stringify(p, null, 2) + '\n');
44
+ }
45
+ return root;
46
+ }
47
+ //# sourceMappingURL=create.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create.js","sourceRoot":"","sources":["../src/create.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ import { type CageConfig } from './config.js';
2
+ /** 首次为项目生成独立凭证;已有配置保持原样,避免与持久卷中的角色密码失配。 */
3
+ export declare function setupDatabase(cfg: CageConfig): boolean;
4
+ /** Docker 仅负责基础设施,建库、账号权限和迁移仍复用框架现有 migrate 流程。 */
5
+ export declare function database(cfg: CageConfig, action?: string): Promise<number>;
@@ -0,0 +1,70 @@
1
+ // 开发数据库的环境配置与 Docker 生命周期属于框架工具链。
2
+ // 业务仓库只保留 compose.yaml/.env 等部署参数,不维护连接、建库或凭证生成脚本。
3
+ import { existsSync, writeFileSync } from 'node:fs';
4
+ import { randomBytes } from 'node:crypto';
5
+ import { spawn } from 'node:child_process';
6
+ import { abs } from './config.js';
7
+ import { migrate } from './migrate.js';
8
+ /** 首次为项目生成独立凭证;已有配置保持原样,避免与持久卷中的角色密码失配。 */
9
+ export function setupDatabase(cfg) {
10
+ const target = abs(cfg, '.env');
11
+ if (existsSync(target))
12
+ return false;
13
+ const secret = () => randomBytes(24).toString('hex');
14
+ const passwords = { postgres: secret(), test: secret(), app: secret(), read: secret(), migrate: secret() };
15
+ const port = cfg.devDbPort;
16
+ const url = (user, password, port, database) => `postgresql://${user}:${password}@127.0.0.1:${port}/${database}`;
17
+ const content = [
18
+ '# 仅供本地开发,由 cage db setup 生成;不要提交或复制进镜像。',
19
+ `CAGE_POSTGRES_PORT=${port}`,
20
+ `CAGE_TEST_POSTGRES_PORT=${port + 1}`,
21
+ `POSTGRES_PASSWORD=${passwords.postgres}`,
22
+ `CAGE_TEST_POSTGRES_PASSWORD=${passwords.test}`,
23
+ `CAGE_SUPER_DATABASE_URL=${url('postgres', passwords.postgres, port, 'postgres')}`,
24
+ `CAGE_APP_DATABASE_URL=${url('cage_app', passwords.app, port, 'cage_dev')}`,
25
+ `CAGE_READ_DATABASE_URL=${url('cage_read', passwords.read, port, 'cage_dev')}`,
26
+ `CAGE_MIGRATE_DATABASE_URL=${url('cage_migrate', passwords.migrate, port, 'cage_dev')}`,
27
+ '# 测试会初始化集群角色,必须使用独立的可丢弃测试集群。',
28
+ `CAGE_TEST_DATABASE_URL=${url('postgres', passwords.test, port + 1, 'postgres')}`,
29
+ `CAGE_COOKIE_SECRET=${secret()}`,
30
+ 'CAGE_STAGE=fake',
31
+ 'CAGE_WEB_PORT=5173',
32
+ 'PORT=3000',
33
+ 'HOST=127.0.0.1',
34
+ '',
35
+ ].join('\n');
36
+ writeFileSync(target, content, { mode: 0o600, flag: 'wx' });
37
+ return true;
38
+ }
39
+ /** Docker 仅负责基础设施,建库、账号权限和迁移仍复用框架现有 migrate 流程。 */
40
+ export async function database(cfg, action) {
41
+ if (action === 'setup') {
42
+ console.log(setupDatabase(cfg)
43
+ ? '已生成 .env(不输出密码)。随后运行 cage db up、cage db init、cage dev。'
44
+ : '已有 .env,保留当前数据库配置。');
45
+ return 0;
46
+ }
47
+ if (action === 'init') {
48
+ await migrate(cfg, true);
49
+ return 0;
50
+ }
51
+ // 参数列表由命令固定,不接受任意 shell 文本;down 不删除持久卷。
52
+ const actions = {
53
+ up: ['up', '-d', '--wait'],
54
+ down: ['down'],
55
+ status: ['ps'],
56
+ };
57
+ if (!action || !actions[action])
58
+ throw new Error('用法:cage db setup|up|init|status|down');
59
+ const child = spawn('docker', ['compose', '--profile', 'test', ...actions[action]], {
60
+ cwd: cfg.root,
61
+ env: process.env,
62
+ stdio: 'inherit',
63
+ windowsHide: true,
64
+ });
65
+ return new Promise((resolve, reject) => {
66
+ child.once('error', reject);
67
+ child.once('exit', (code) => resolve(code ?? 1));
68
+ });
69
+ }
70
+ //# sourceMappingURL=database.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"database.js","sourceRoot":"","sources":["../src/database.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import { type CageConfig } from './config.js';
2
+ import type { Ontology } from './ontology.js';
3
+ export interface DerivedTest {
4
+ file: string;
5
+ name: string;
6
+ /** 这个测试守护的本体节点(影响分析据此挑选要运行的测试) */
7
+ subject: string;
8
+ }
9
+ export declare function deriveTests(cfg: CageConfig, g: Ontology): {
10
+ files: Record<string, string>;
11
+ tests: DerivedTest[];
12
+ };
13
+ export declare function writeDerived(cfg: CageConfig, files: Record<string, string>): string;
14
+ export declare function vitestConfig(cfg: CageConfig, only?: 'derived' | 'project'): string;