@yss123/cage-cli 0.1.0 → 0.2.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 (540) hide show
  1. package/README.md +23 -1
  2. package/dist/analyze.js +2 -0
  3. package/dist/analyze.js.map +1 -1
  4. package/dist/config.d.ts +2 -2
  5. package/dist/config.js +0 -1
  6. package/dist/config.js.map +1 -1
  7. package/dist/context.d.ts +72 -0
  8. package/dist/context.js +202 -0
  9. package/dist/context.js.map +1 -0
  10. package/dist/create-prompt.d.ts +5 -0
  11. package/dist/create-prompt.js +50 -0
  12. package/dist/create-prompt.js.map +1 -0
  13. package/dist/create.d.ts +18 -1
  14. package/dist/create.js +30 -13
  15. package/dist/create.js.map +1 -1
  16. package/dist/dev.js +12 -51
  17. package/dist/dev.js.map +1 -1
  18. package/dist/gen/contracts.js +34 -13
  19. package/dist/gen/contracts.js.map +1 -1
  20. package/dist/gen/index.js +43 -3
  21. package/dist/gen/index.js.map +1 -1
  22. package/dist/gen/zod-code.d.ts +1 -1
  23. package/dist/gen/zod-code.js +3 -1
  24. package/dist/gen/zod-code.js.map +1 -1
  25. package/dist/graph-command.d.ts +11 -0
  26. package/dist/graph-command.js +67 -0
  27. package/dist/graph-command.js.map +1 -0
  28. package/dist/graph-model.d.ts +21 -0
  29. package/dist/graph-model.js +101 -0
  30. package/dist/graph-model.js.map +1 -0
  31. package/dist/graph-presentation.d.ts +11 -0
  32. package/dist/graph-presentation.js +25 -0
  33. package/dist/graph-presentation.js.map +1 -0
  34. package/dist/graph-query.d.ts +171 -0
  35. package/dist/graph-query.js +242 -0
  36. package/dist/graph-query.js.map +1 -0
  37. package/dist/graph-relations.d.ts +14 -0
  38. package/dist/graph-relations.js +142 -0
  39. package/dist/graph-relations.js.map +1 -0
  40. package/dist/graph-search.d.ts +21 -0
  41. package/dist/graph-search.js +63 -0
  42. package/dist/graph-search.js.map +1 -0
  43. package/dist/graph-snapshot.d.ts +2 -4
  44. package/dist/graph-snapshot.js +2 -19
  45. package/dist/graph-snapshot.js.map +1 -1
  46. package/dist/graph.js +19 -1
  47. package/dist/graph.js.map +1 -1
  48. package/dist/impact.d.ts +6 -0
  49. package/dist/impact.js +28 -16
  50. package/dist/impact.js.map +1 -1
  51. package/dist/index.d.ts +5 -1
  52. package/dist/index.js +5 -1
  53. package/dist/index.js.map +1 -1
  54. package/dist/init.d.ts +6 -0
  55. package/dist/init.js +131 -0
  56. package/dist/init.js.map +1 -0
  57. package/dist/main.js +77 -32
  58. package/dist/main.js.map +1 -1
  59. package/dist/migrate.d.ts +3 -6
  60. package/dist/migrate.js +14 -53
  61. package/dist/migrate.js.map +1 -1
  62. package/dist/ontology.d.ts +3 -1
  63. package/dist/ontology.js +13 -3
  64. package/dist/ontology.js.map +1 -1
  65. package/dist/project-structure.d.ts +8 -0
  66. package/dist/project-structure.js +94 -0
  67. package/dist/project-structure.js.map +1 -0
  68. package/dist/static-check.js +1 -4
  69. package/dist/static-check.js.map +1 -1
  70. package/dist/sync.js +82 -10
  71. package/dist/sync.js.map +1 -1
  72. package/graph-ui/app.js +46 -8
  73. package/graph-ui/graph-model.js +100 -0
  74. package/graph-ui/graph-relations.js +141 -0
  75. package/graph-ui/graph-search.js +62 -0
  76. package/graph-ui/model.js +19 -77
  77. package/graph-ui/relations.js +2 -125
  78. package/graph-ui/structure.js +4 -1
  79. package/graph-ui/style.css +21 -0
  80. package/package.json +8 -8
  81. package/templates/app/.env.example +58 -14
  82. package/templates/app/AGENTS.md +95 -12
  83. package/templates/app/Dockerfile +2 -0
  84. package/templates/app/README.md +46 -16
  85. package/templates/app/_gitignore +8 -0
  86. package/templates/app/apps/api/package.json +2 -2
  87. package/templates/app/apps/web/src/app/app.tsx +18 -0
  88. package/templates/app/apps/web/src/app/workspace-layout.tsx +70 -0
  89. package/templates/app/apps/web/src/main.tsx +4 -543
  90. package/templates/app/apps/web/src/modules/approval/hooks/use-approval-tasks.ts +20 -0
  91. package/templates/app/apps/web/src/modules/approval/index.ts +2 -0
  92. package/templates/app/apps/web/src/modules/approval/pages/approval-tasks-page.tsx +54 -0
  93. package/templates/app/apps/web/src/modules/auth/index.ts +1 -0
  94. package/templates/app/apps/web/src/modules/auth/pages/login-page.tsx +59 -0
  95. package/templates/app/apps/web/src/modules/inventory/index.ts +1 -0
  96. package/templates/app/apps/web/src/modules/inventory/pages/inventory-page.tsx +74 -0
  97. package/templates/app/apps/web/src/modules/purchase/components/purchase-order-form.tsx +60 -0
  98. package/templates/app/apps/web/src/modules/purchase/index.ts +2 -0
  99. package/templates/app/apps/web/src/modules/purchase/pages/purchase-orders-page.tsx +210 -0
  100. package/templates/app/apps/web/src/shared/hooks/use-command.ts +29 -0
  101. package/templates/app/apps/web/src/shared/lib/error-message.ts +15 -0
  102. package/templates/app/apps/web/src/shared/ui/notice.tsx +13 -0
  103. package/templates/app/apps/web/src/shared/ui/page-heading.tsx +28 -0
  104. package/templates/app/apps/web/vite.config.ts +3 -0
  105. package/templates/app/cage.config.json +1 -1
  106. package/templates/app/docs/ai/README.md +77 -0
  107. package/templates/app/docs/ai/architecture.md +122 -0
  108. package/templates/app/docs/ai/ontology-context.md +117 -0
  109. package/templates/app/docs/ai/primitives.md +249 -0
  110. package/templates/app/docs/ai/project-structure.md +123 -0
  111. package/templates/app/docs/ai/purchase-example.md +97 -0
  112. package/templates/app/docs/ai/recipes.md +81 -0
  113. package/templates/app/docs/ai/relations.md +160 -0
  114. package/templates/app/docs/ai/transfers.md +92 -0
  115. package/templates/app/docs/ai/troubleshooting.md +60 -0
  116. package/templates/app/docs/ai/workflow.md +139 -0
  117. package/templates/app/docs/errors/CAGE-APPR-001.md +47 -0
  118. package/templates/app/docs/errors/CAGE-APPR-002.md +47 -0
  119. package/templates/app/docs/errors/CAGE-APPR-003.md +47 -0
  120. package/templates/app/docs/errors/CAGE-APPR-004.md +47 -0
  121. package/templates/app/docs/errors/CAGE-APPR-005.md +47 -0
  122. package/templates/app/docs/errors/CAGE-APPR-006.md +47 -0
  123. package/templates/app/docs/errors/CAGE-APPR-007.md +50 -0
  124. package/templates/app/docs/errors/CAGE-APPR-010.md +48 -0
  125. package/templates/app/docs/errors/CAGE-AUTH-001.md +47 -0
  126. package/templates/app/docs/errors/CAGE-BUDGET-001.md +47 -0
  127. package/templates/app/docs/errors/CAGE-BUDGET-002.md +47 -0
  128. package/templates/app/docs/errors/CAGE-BUDGET-003.md +47 -0
  129. package/templates/app/docs/errors/CAGE-CMD-002.md +47 -0
  130. package/templates/app/docs/errors/CAGE-CMD-003.md +47 -0
  131. package/templates/app/docs/errors/CAGE-CONN-001.md +47 -0
  132. package/templates/app/docs/errors/CAGE-CONN-002.md +47 -0
  133. package/templates/app/docs/errors/CAGE-CONN-003.md +47 -0
  134. package/templates/app/docs/errors/CAGE-CONN-004.md +47 -0
  135. package/templates/app/docs/errors/CAGE-CONN-005.md +47 -0
  136. package/templates/app/docs/errors/CAGE-CONN-006.md +47 -0
  137. package/templates/app/docs/errors/CAGE-CONN-007.md +47 -0
  138. package/templates/app/docs/errors/CAGE-CONN-008.md +47 -0
  139. package/templates/app/docs/errors/CAGE-CONN-009.md +47 -0
  140. package/templates/app/docs/errors/CAGE-CONN-010.md +47 -0
  141. package/templates/app/docs/errors/CAGE-CONN-011.md +47 -0
  142. package/templates/app/docs/errors/CAGE-CONN-012.md +47 -0
  143. package/templates/app/docs/errors/CAGE-CREATE-001.md +52 -0
  144. package/templates/app/docs/errors/CAGE-DECL-001.md +48 -0
  145. package/templates/app/docs/errors/CAGE-DECL-002.md +47 -0
  146. package/templates/app/docs/errors/CAGE-DECL-003.md +47 -0
  147. package/templates/app/docs/errors/CAGE-DECL-004.md +47 -0
  148. package/templates/app/docs/errors/CAGE-DEP-001.md +48 -0
  149. package/templates/app/docs/errors/CAGE-DEP-002.md +47 -0
  150. package/templates/app/docs/errors/CAGE-DEP-003.md +47 -0
  151. package/templates/app/docs/errors/CAGE-DEP-004.md +47 -0
  152. package/templates/app/docs/errors/CAGE-DEP-005.md +47 -0
  153. package/templates/app/docs/errors/CAGE-DLQ-001.md +48 -0
  154. package/templates/app/docs/errors/CAGE-ENV-001.md +52 -0
  155. package/templates/app/docs/errors/CAGE-EVENT-001.md +47 -0
  156. package/templates/app/docs/errors/CAGE-EVENT-002.md +50 -0
  157. package/templates/app/docs/errors/CAGE-FIELD-001.md +47 -0
  158. package/templates/app/docs/errors/CAGE-FIELD-002.md +47 -0
  159. package/templates/app/docs/errors/CAGE-FIELD-003.md +47 -0
  160. package/templates/app/docs/errors/CAGE-FIELD-004.md +47 -0
  161. package/templates/app/docs/errors/CAGE-GEN-001.md +48 -0
  162. package/templates/app/docs/errors/CAGE-GEN-002.md +47 -0
  163. package/templates/app/docs/errors/CAGE-GRANT-001.md +49 -0
  164. package/templates/app/docs/errors/CAGE-GRANT-002.md +48 -0
  165. package/templates/app/docs/errors/CAGE-GRANT-003.md +47 -0
  166. package/templates/app/docs/errors/CAGE-IMPACT-001.md +48 -0
  167. package/templates/app/docs/errors/CAGE-INIT-001.md +47 -0
  168. package/templates/app/docs/errors/CAGE-INIT-002.md +49 -0
  169. package/templates/app/docs/errors/CAGE-INIT-003.md +48 -0
  170. package/templates/app/docs/errors/CAGE-INIT-004.md +48 -0
  171. package/templates/app/docs/errors/CAGE-JOB-001.md +47 -0
  172. package/templates/app/docs/errors/CAGE-JOB-002.md +47 -0
  173. package/templates/app/docs/errors/CAGE-JOB-003.md +47 -0
  174. package/templates/app/docs/errors/CAGE-LINT-001.md +47 -0
  175. package/templates/app/docs/errors/CAGE-MIG-001.md +47 -0
  176. package/templates/app/docs/errors/CAGE-MIG-002.md +47 -0
  177. package/templates/app/docs/errors/CAGE-MIG-003.md +5 -0
  178. package/templates/app/docs/errors/CAGE-MIG-004.md +5 -0
  179. package/templates/app/docs/errors/CAGE-MIG-005.md +47 -0
  180. package/templates/app/docs/errors/CAGE-NAME-001.md +47 -0
  181. package/templates/app/docs/errors/CAGE-NAME-002.md +47 -0
  182. package/templates/app/docs/errors/CAGE-NAME-003.md +47 -0
  183. package/templates/app/docs/errors/CAGE-NAME-004.md +47 -0
  184. package/templates/app/docs/errors/CAGE-NAME-005.md +47 -0
  185. package/templates/app/docs/errors/CAGE-NAME-006.md +47 -0
  186. package/templates/app/docs/errors/CAGE-ORG-001.md +49 -0
  187. package/templates/app/docs/errors/CAGE-PAIR-002.md +50 -0
  188. package/templates/app/docs/errors/CAGE-QUERY-001.md +47 -0
  189. package/templates/app/docs/errors/CAGE-QUERY-002.md +47 -0
  190. package/templates/app/docs/errors/CAGE-QUERY-003.md +49 -0
  191. package/templates/app/docs/errors/CAGE-REACT-001.md +47 -0
  192. package/templates/app/docs/errors/CAGE-REACT-002.md +47 -0
  193. package/templates/app/docs/errors/CAGE-READS-001.md +47 -0
  194. package/templates/app/docs/errors/CAGE-REG-001.md +53 -0
  195. package/templates/app/docs/errors/CAGE-REG-002.md +48 -0
  196. package/templates/app/docs/errors/CAGE-REG-003.md +47 -0
  197. package/templates/app/docs/errors/CAGE-REL-001.md +64 -0
  198. package/templates/app/docs/errors/CAGE-ROUTE-001.md +47 -0
  199. package/templates/app/docs/errors/CAGE-SCOPE-001.md +47 -0
  200. package/templates/app/docs/errors/CAGE-SCOPE-002.md +47 -0
  201. package/templates/app/docs/errors/CAGE-SECRET-001.md +48 -0
  202. package/templates/app/docs/errors/CAGE-SECRET-002.md +47 -0
  203. package/templates/app/docs/errors/CAGE-SENS-001.md +48 -0
  204. package/templates/app/docs/errors/CAGE-SQL-001.md +47 -0
  205. package/templates/app/docs/errors/CAGE-SQL-002.md +47 -0
  206. package/templates/app/docs/errors/CAGE-STATE-001.md +49 -0
  207. package/templates/app/docs/errors/CAGE-STATE-002.md +47 -0
  208. package/templates/app/docs/errors/CAGE-STATE-003.md +47 -0
  209. package/templates/app/docs/errors/CAGE-STATE-004.md +47 -0
  210. package/templates/app/docs/errors/CAGE-STATE-005.md +48 -0
  211. package/templates/app/docs/errors/CAGE-STATE-010.md +48 -0
  212. package/templates/app/docs/errors/CAGE-STATE-011.md +48 -0
  213. package/templates/app/docs/errors/CAGE-STRUCT-001.md +51 -0
  214. package/templates/app/docs/errors/CAGE-STRUCT-002.md +51 -0
  215. package/templates/app/docs/errors/CAGE-SYNC-001.md +48 -0
  216. package/templates/app/docs/errors/CAGE-TEST-001.md +47 -0
  217. package/templates/app/docs/errors/CAGE-UNSAFE-001.md +48 -0
  218. package/templates/app/docs/errors/CAGE-UPGRADE-001.md +48 -0
  219. package/templates/app/docs/errors/README.md +307 -0
  220. package/templates/app/docs/errors/runtime.md +50 -0
  221. package/templates/app/package.json +4 -7
  222. package/templates/app/packages/contracts/package.json +1 -1
  223. package/templates/app/packages/contracts/src/{dept.ts → aggregates/dept/index.ts} +3 -3
  224. package/templates/app/packages/contracts/src/{inventory.ts → aggregates/inventory/index.ts} +3 -3
  225. package/templates/app/packages/contracts/src/{inventory-lock.ts → aggregates/inventory-lock/index.ts} +3 -3
  226. package/templates/app/packages/contracts/src/{purchase-order.ts → aggregates/purchase-order/index.ts} +3 -3
  227. package/templates/app/packages/contracts/src/{role.ts → aggregates/role/index.ts} +3 -3
  228. package/templates/app/packages/contracts/src/{user.ts → aggregates/user/index.ts} +3 -3
  229. package/templates/app/packages/contracts/src/index.ts +14 -14
  230. package/templates/app/packages/domain/package.json +4 -4
  231. package/templates/app/packages/domain/src/index.ts +4 -7
  232. package/templates/app/packages/domain/src/{erp.ts → integrations/erp.ts} +1 -1
  233. package/templates/app/packages/domain/src/modules/inventory/index.ts +2 -0
  234. package/templates/app/packages/domain/src/modules/purchase/index.ts +6 -0
  235. package/templates/app/packages/domain/src/{purchase-order.test.ts → modules/purchase/purchase-order.test.ts} +2 -2
  236. package/templates/app/packages/domain/src/{purchase-order.ts → modules/purchase/purchase-order.ts} +1 -1
  237. package/templates/app/packages/domain/src/{reactions.ts → workflows/purchase-inventory.ts} +2 -2
  238. package/templates/blank/.dockerignore +10 -0
  239. package/templates/blank/.env.example +61 -0
  240. package/templates/blank/.github/workflows/cage.yml +44 -0
  241. package/templates/blank/AGENTS.md +96 -0
  242. package/templates/blank/Dockerfile +25 -0
  243. package/templates/blank/README.md +37 -0
  244. package/templates/blank/_gitignore +8 -0
  245. package/templates/blank/apps/api/package.json +15 -0
  246. package/templates/blank/apps/api/src/main.ts +11 -0
  247. package/templates/blank/apps/api/src/worker.ts +8 -0
  248. package/templates/blank/apps/api/tsconfig.json +5 -0
  249. package/templates/blank/apps/web/index.html +13 -0
  250. package/templates/blank/apps/web/package.json +22 -0
  251. package/templates/blank/apps/web/src/app/app.tsx +44 -0
  252. package/templates/blank/apps/web/src/app/styles/global.css +516 -0
  253. package/templates/blank/apps/web/src/app/workspace-layout.tsx +70 -0
  254. package/templates/blank/apps/web/src/main.tsx +13 -0
  255. package/templates/blank/apps/web/src/modules/auth/index.ts +1 -0
  256. package/templates/blank/apps/web/src/modules/auth/pages/login-page.tsx +87 -0
  257. package/templates/blank/apps/web/src/modules/workspace/index.ts +1 -0
  258. package/templates/blank/apps/web/src/modules/workspace/pages/home-page.tsx +45 -0
  259. package/templates/blank/apps/web/src/shared/lib/error-message.ts +11 -0
  260. package/templates/blank/apps/web/src/shared/ui/brand.tsx +11 -0
  261. package/templates/blank/apps/web/tsconfig.json +11 -0
  262. package/templates/blank/apps/web/vite.config.ts +19 -0
  263. package/templates/blank/cage.config.json +14 -0
  264. package/templates/blank/db/migrations/0001_init.sql +63 -0
  265. package/templates/blank/docs/ai/README.md +77 -0
  266. package/templates/blank/docs/ai/architecture.md +122 -0
  267. package/templates/blank/docs/ai/ontology-context.md +117 -0
  268. package/templates/blank/docs/ai/primitives.md +249 -0
  269. package/templates/blank/docs/ai/project-structure.md +123 -0
  270. package/templates/blank/docs/ai/purchase-example.md +97 -0
  271. package/templates/blank/docs/ai/recipes.md +81 -0
  272. package/templates/blank/docs/ai/relations.md +160 -0
  273. package/templates/blank/docs/ai/transfers.md +92 -0
  274. package/templates/blank/docs/ai/troubleshooting.md +60 -0
  275. package/templates/blank/docs/ai/workflow.md +139 -0
  276. package/templates/blank/docs/errors/CAGE-APPR-001.md +47 -0
  277. package/templates/blank/docs/errors/CAGE-APPR-002.md +47 -0
  278. package/templates/blank/docs/errors/CAGE-APPR-003.md +47 -0
  279. package/templates/blank/docs/errors/CAGE-APPR-004.md +47 -0
  280. package/templates/blank/docs/errors/CAGE-APPR-005.md +47 -0
  281. package/templates/blank/docs/errors/CAGE-APPR-006.md +47 -0
  282. package/templates/blank/docs/errors/CAGE-APPR-007.md +50 -0
  283. package/templates/blank/docs/errors/CAGE-APPR-010.md +48 -0
  284. package/templates/blank/docs/errors/CAGE-AUTH-001.md +47 -0
  285. package/templates/blank/docs/errors/CAGE-BUDGET-001.md +47 -0
  286. package/templates/blank/docs/errors/CAGE-BUDGET-002.md +47 -0
  287. package/templates/blank/docs/errors/CAGE-BUDGET-003.md +47 -0
  288. package/templates/blank/docs/errors/CAGE-CMD-002.md +47 -0
  289. package/templates/blank/docs/errors/CAGE-CMD-003.md +47 -0
  290. package/templates/blank/docs/errors/CAGE-CONN-001.md +47 -0
  291. package/templates/blank/docs/errors/CAGE-CONN-002.md +47 -0
  292. package/templates/blank/docs/errors/CAGE-CONN-003.md +47 -0
  293. package/templates/blank/docs/errors/CAGE-CONN-004.md +47 -0
  294. package/templates/blank/docs/errors/CAGE-CONN-005.md +47 -0
  295. package/templates/blank/docs/errors/CAGE-CONN-006.md +47 -0
  296. package/templates/blank/docs/errors/CAGE-CONN-007.md +47 -0
  297. package/templates/blank/docs/errors/CAGE-CONN-008.md +47 -0
  298. package/templates/blank/docs/errors/CAGE-CONN-009.md +47 -0
  299. package/templates/blank/docs/errors/CAGE-CONN-010.md +47 -0
  300. package/templates/blank/docs/errors/CAGE-CONN-011.md +47 -0
  301. package/templates/blank/docs/errors/CAGE-CONN-012.md +47 -0
  302. package/templates/blank/docs/errors/CAGE-CREATE-001.md +52 -0
  303. package/templates/blank/docs/errors/CAGE-DECL-001.md +48 -0
  304. package/templates/blank/docs/errors/CAGE-DECL-002.md +47 -0
  305. package/templates/blank/docs/errors/CAGE-DECL-003.md +47 -0
  306. package/templates/blank/docs/errors/CAGE-DECL-004.md +47 -0
  307. package/templates/blank/docs/errors/CAGE-DEP-001.md +48 -0
  308. package/templates/blank/docs/errors/CAGE-DEP-002.md +47 -0
  309. package/templates/blank/docs/errors/CAGE-DEP-003.md +47 -0
  310. package/templates/blank/docs/errors/CAGE-DEP-004.md +47 -0
  311. package/templates/blank/docs/errors/CAGE-DEP-005.md +47 -0
  312. package/templates/blank/docs/errors/CAGE-DLQ-001.md +48 -0
  313. package/templates/blank/docs/errors/CAGE-ENV-001.md +52 -0
  314. package/templates/blank/docs/errors/CAGE-EVENT-001.md +47 -0
  315. package/templates/blank/docs/errors/CAGE-EVENT-002.md +50 -0
  316. package/templates/blank/docs/errors/CAGE-FIELD-001.md +47 -0
  317. package/templates/blank/docs/errors/CAGE-FIELD-002.md +47 -0
  318. package/templates/blank/docs/errors/CAGE-FIELD-003.md +47 -0
  319. package/templates/blank/docs/errors/CAGE-FIELD-004.md +47 -0
  320. package/templates/blank/docs/errors/CAGE-GEN-001.md +48 -0
  321. package/templates/blank/docs/errors/CAGE-GEN-002.md +47 -0
  322. package/templates/blank/docs/errors/CAGE-GRANT-001.md +49 -0
  323. package/templates/blank/docs/errors/CAGE-GRANT-002.md +48 -0
  324. package/templates/blank/docs/errors/CAGE-GRANT-003.md +47 -0
  325. package/templates/blank/docs/errors/CAGE-IMPACT-001.md +48 -0
  326. package/templates/blank/docs/errors/CAGE-INIT-001.md +47 -0
  327. package/templates/blank/docs/errors/CAGE-INIT-002.md +49 -0
  328. package/templates/blank/docs/errors/CAGE-INIT-003.md +48 -0
  329. package/templates/blank/docs/errors/CAGE-INIT-004.md +48 -0
  330. package/templates/blank/docs/errors/CAGE-JOB-001.md +47 -0
  331. package/templates/blank/docs/errors/CAGE-JOB-002.md +47 -0
  332. package/templates/blank/docs/errors/CAGE-JOB-003.md +47 -0
  333. package/templates/blank/docs/errors/CAGE-LINT-001.md +47 -0
  334. package/templates/blank/docs/errors/CAGE-MIG-001.md +47 -0
  335. package/templates/blank/docs/errors/CAGE-MIG-002.md +47 -0
  336. package/templates/blank/docs/errors/CAGE-MIG-003.md +5 -0
  337. package/templates/blank/docs/errors/CAGE-MIG-004.md +5 -0
  338. package/templates/blank/docs/errors/CAGE-MIG-005.md +47 -0
  339. package/templates/blank/docs/errors/CAGE-NAME-001.md +47 -0
  340. package/templates/blank/docs/errors/CAGE-NAME-002.md +47 -0
  341. package/templates/blank/docs/errors/CAGE-NAME-003.md +47 -0
  342. package/templates/blank/docs/errors/CAGE-NAME-004.md +47 -0
  343. package/templates/blank/docs/errors/CAGE-NAME-005.md +47 -0
  344. package/templates/blank/docs/errors/CAGE-NAME-006.md +47 -0
  345. package/templates/blank/docs/errors/CAGE-ORG-001.md +49 -0
  346. package/templates/blank/docs/errors/CAGE-PAIR-002.md +50 -0
  347. package/templates/blank/docs/errors/CAGE-QUERY-001.md +47 -0
  348. package/templates/blank/docs/errors/CAGE-QUERY-002.md +47 -0
  349. package/templates/blank/docs/errors/CAGE-QUERY-003.md +49 -0
  350. package/templates/blank/docs/errors/CAGE-REACT-001.md +47 -0
  351. package/templates/blank/docs/errors/CAGE-REACT-002.md +47 -0
  352. package/templates/blank/docs/errors/CAGE-READS-001.md +47 -0
  353. package/templates/blank/docs/errors/CAGE-REG-001.md +53 -0
  354. package/templates/blank/docs/errors/CAGE-REG-002.md +48 -0
  355. package/templates/blank/docs/errors/CAGE-REG-003.md +47 -0
  356. package/templates/blank/docs/errors/CAGE-REL-001.md +64 -0
  357. package/templates/blank/docs/errors/CAGE-ROUTE-001.md +47 -0
  358. package/templates/blank/docs/errors/CAGE-SCOPE-001.md +47 -0
  359. package/templates/blank/docs/errors/CAGE-SCOPE-002.md +47 -0
  360. package/templates/blank/docs/errors/CAGE-SECRET-001.md +48 -0
  361. package/templates/blank/docs/errors/CAGE-SECRET-002.md +47 -0
  362. package/templates/blank/docs/errors/CAGE-SENS-001.md +48 -0
  363. package/templates/blank/docs/errors/CAGE-SQL-001.md +47 -0
  364. package/templates/blank/docs/errors/CAGE-SQL-002.md +47 -0
  365. package/templates/blank/docs/errors/CAGE-STATE-001.md +49 -0
  366. package/templates/blank/docs/errors/CAGE-STATE-002.md +47 -0
  367. package/templates/blank/docs/errors/CAGE-STATE-003.md +47 -0
  368. package/templates/blank/docs/errors/CAGE-STATE-004.md +47 -0
  369. package/templates/blank/docs/errors/CAGE-STATE-005.md +48 -0
  370. package/templates/blank/docs/errors/CAGE-STATE-010.md +48 -0
  371. package/templates/blank/docs/errors/CAGE-STATE-011.md +48 -0
  372. package/templates/blank/docs/errors/CAGE-STRUCT-001.md +51 -0
  373. package/templates/blank/docs/errors/CAGE-STRUCT-002.md +51 -0
  374. package/templates/blank/docs/errors/CAGE-SYNC-001.md +48 -0
  375. package/templates/blank/docs/errors/CAGE-TEST-001.md +47 -0
  376. package/templates/blank/docs/errors/CAGE-UNSAFE-001.md +48 -0
  377. package/templates/blank/docs/errors/CAGE-UPGRADE-001.md +48 -0
  378. package/templates/blank/docs/errors/README.md +307 -0
  379. package/templates/blank/docs/errors/runtime.md +50 -0
  380. package/templates/blank/docs/examples/purchase-admin/README.md +5 -0
  381. package/templates/blank/docs/examples/purchase-admin/apps/api/src/main.ts +10 -0
  382. package/templates/blank/docs/examples/purchase-admin/apps/api/src/worker.ts +14 -0
  383. package/templates/blank/docs/examples/purchase-admin/apps/web/src/app/app.tsx +18 -0
  384. package/templates/blank/docs/examples/purchase-admin/apps/web/src/app/styles/global.css +663 -0
  385. package/templates/blank/docs/examples/purchase-admin/apps/web/src/app/workspace-layout.tsx +70 -0
  386. package/templates/blank/docs/examples/purchase-admin/apps/web/src/main.tsx +13 -0
  387. package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/approval/hooks/use-approval-tasks.ts +20 -0
  388. package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/approval/index.ts +2 -0
  389. package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/approval/pages/approval-tasks-page.tsx +54 -0
  390. package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/auth/index.ts +1 -0
  391. package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/auth/pages/login-page.tsx +59 -0
  392. package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/inventory/index.ts +1 -0
  393. package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/inventory/pages/inventory-page.tsx +74 -0
  394. package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/purchase/components/purchase-order-form.tsx +60 -0
  395. package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/purchase/index.ts +2 -0
  396. package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/purchase/pages/purchase-orders-page.tsx +210 -0
  397. package/templates/blank/docs/examples/purchase-admin/apps/web/src/shared/hooks/use-command.ts +29 -0
  398. package/templates/blank/docs/examples/purchase-admin/apps/web/src/shared/lib/error-message.ts +15 -0
  399. package/templates/blank/docs/examples/purchase-admin/apps/web/src/shared/ui/notice.tsx +13 -0
  400. package/templates/blank/docs/examples/purchase-admin/apps/web/src/shared/ui/page-heading.tsx +28 -0
  401. package/templates/blank/docs/examples/purchase-admin/cage.config.json +114 -0
  402. package/templates/blank/docs/examples/purchase-admin/packages/contracts/src/aggregates/dept/index.ts +74 -0
  403. package/templates/blank/docs/examples/purchase-admin/packages/contracts/src/aggregates/inventory/index.ts +57 -0
  404. package/templates/blank/docs/examples/purchase-admin/packages/contracts/src/aggregates/inventory-lock/index.ts +52 -0
  405. package/templates/blank/docs/examples/purchase-admin/packages/contracts/src/aggregates/purchase-order/index.ts +72 -0
  406. package/templates/blank/docs/examples/purchase-admin/packages/contracts/src/aggregates/role/index.ts +85 -0
  407. package/templates/blank/docs/examples/purchase-admin/packages/contracts/src/aggregates/user/index.ts +89 -0
  408. package/templates/blank/docs/examples/purchase-admin/packages/contracts/src/index.ts +81 -0
  409. package/templates/blank/docs/examples/purchase-admin/packages/contracts/src/queries/index.ts +17 -0
  410. package/templates/blank/docs/examples/purchase-admin/packages/domain/src/index.ts +6 -0
  411. package/templates/blank/docs/examples/purchase-admin/packages/domain/src/integrations/erp.ts +36 -0
  412. package/templates/blank/docs/examples/purchase-admin/packages/domain/src/modules/inventory/index.ts +2 -0
  413. package/templates/blank/docs/examples/purchase-admin/packages/domain/src/modules/inventory/inventory.ts +43 -0
  414. package/templates/blank/docs/examples/purchase-admin/packages/domain/src/modules/purchase/approvals.ts +18 -0
  415. package/templates/blank/docs/examples/purchase-admin/packages/domain/src/modules/purchase/index.ts +6 -0
  416. package/templates/blank/docs/examples/purchase-admin/packages/domain/src/modules/purchase/jobs.ts +13 -0
  417. package/templates/blank/docs/examples/purchase-admin/packages/domain/src/modules/purchase/purchase-order.test.ts +137 -0
  418. package/templates/blank/docs/examples/purchase-admin/packages/domain/src/modules/purchase/purchase-order.ts +122 -0
  419. package/templates/blank/docs/examples/purchase-admin/packages/domain/src/modules/purchase/queries.ts +10 -0
  420. package/templates/blank/docs/examples/purchase-admin/packages/domain/src/workflows/purchase-inventory.ts +24 -0
  421. package/templates/blank/package.json +22 -0
  422. package/templates/blank/packages/contracts/package.json +16 -0
  423. package/templates/blank/packages/contracts/src/aggregates/dept/index.ts +74 -0
  424. package/templates/blank/packages/contracts/src/aggregates/role/index.ts +85 -0
  425. package/templates/blank/packages/contracts/src/aggregates/user/index.ts +89 -0
  426. package/templates/blank/packages/contracts/src/index.ts +55 -0
  427. package/templates/blank/packages/contracts/src/queries/index.ts +13 -0
  428. package/templates/blank/packages/contracts/tsconfig.json +5 -0
  429. package/templates/blank/packages/domain/package.json +23 -0
  430. package/templates/blank/packages/domain/src/index.ts +4 -0
  431. package/templates/blank/packages/domain/tsconfig.json +5 -0
  432. package/templates/blank/pnpm-workspace.yaml +10 -0
  433. package/templates/blank/renovate.json +5 -0
  434. package/templates/blank/tsconfig.base.json +8 -0
  435. package/dist/database.d.ts +0 -5
  436. package/dist/database.js +0 -70
  437. package/dist/database.js.map +0 -1
  438. package/managed/errors/CAGE-APPR-001.md +0 -32
  439. package/managed/errors/CAGE-APPR-002.md +0 -32
  440. package/managed/errors/CAGE-APPR-003.md +0 -32
  441. package/managed/errors/CAGE-APPR-004.md +0 -32
  442. package/managed/errors/CAGE-APPR-005.md +0 -32
  443. package/managed/errors/CAGE-APPR-006.md +0 -32
  444. package/managed/errors/CAGE-APPR-007.md +0 -32
  445. package/managed/errors/CAGE-APPR-010.md +0 -32
  446. package/managed/errors/CAGE-AUTH-001.md +0 -32
  447. package/managed/errors/CAGE-BUDGET-001.md +0 -32
  448. package/managed/errors/CAGE-BUDGET-002.md +0 -32
  449. package/managed/errors/CAGE-BUDGET-003.md +0 -32
  450. package/managed/errors/CAGE-CMD-002.md +0 -32
  451. package/managed/errors/CAGE-CMD-003.md +0 -34
  452. package/managed/errors/CAGE-CONN-001.md +0 -32
  453. package/managed/errors/CAGE-CONN-002.md +0 -32
  454. package/managed/errors/CAGE-CONN-003.md +0 -32
  455. package/managed/errors/CAGE-CONN-004.md +0 -32
  456. package/managed/errors/CAGE-CONN-005.md +0 -32
  457. package/managed/errors/CAGE-CONN-006.md +0 -32
  458. package/managed/errors/CAGE-CONN-007.md +0 -32
  459. package/managed/errors/CAGE-CONN-008.md +0 -32
  460. package/managed/errors/CAGE-CONN-009.md +0 -32
  461. package/managed/errors/CAGE-CONN-010.md +0 -32
  462. package/managed/errors/CAGE-CONN-011.md +0 -32
  463. package/managed/errors/CAGE-CONN-012.md +0 -32
  464. package/managed/errors/CAGE-CREATE-001.md +0 -32
  465. package/managed/errors/CAGE-DECL-001.md +0 -34
  466. package/managed/errors/CAGE-DECL-002.md +0 -34
  467. package/managed/errors/CAGE-DECL-003.md +0 -34
  468. package/managed/errors/CAGE-DECL-004.md +0 -34
  469. package/managed/errors/CAGE-DEP-001.md +0 -34
  470. package/managed/errors/CAGE-DEP-002.md +0 -34
  471. package/managed/errors/CAGE-DEP-003.md +0 -34
  472. package/managed/errors/CAGE-DEP-004.md +0 -34
  473. package/managed/errors/CAGE-DEP-005.md +0 -34
  474. package/managed/errors/CAGE-DLQ-001.md +0 -32
  475. package/managed/errors/CAGE-ENV-001.md +0 -32
  476. package/managed/errors/CAGE-EVENT-001.md +0 -32
  477. package/managed/errors/CAGE-EVENT-002.md +0 -32
  478. package/managed/errors/CAGE-FIELD-001.md +0 -32
  479. package/managed/errors/CAGE-FIELD-002.md +0 -32
  480. package/managed/errors/CAGE-FIELD-003.md +0 -32
  481. package/managed/errors/CAGE-FIELD-004.md +0 -32
  482. package/managed/errors/CAGE-GEN-001.md +0 -33
  483. package/managed/errors/CAGE-GEN-002.md +0 -33
  484. package/managed/errors/CAGE-GRANT-001.md +0 -32
  485. package/managed/errors/CAGE-GRANT-002.md +0 -32
  486. package/managed/errors/CAGE-GRANT-003.md +0 -32
  487. package/managed/errors/CAGE-IMPACT-001.md +0 -32
  488. package/managed/errors/CAGE-JOB-001.md +0 -32
  489. package/managed/errors/CAGE-JOB-002.md +0 -32
  490. package/managed/errors/CAGE-JOB-003.md +0 -32
  491. package/managed/errors/CAGE-LINT-001.md +0 -32
  492. package/managed/errors/CAGE-MIG-001.md +0 -32
  493. package/managed/errors/CAGE-MIG-002.md +0 -32
  494. package/managed/errors/CAGE-MIG-003.md +0 -32
  495. package/managed/errors/CAGE-MIG-004.md +0 -32
  496. package/managed/errors/CAGE-MIG-005.md +0 -32
  497. package/managed/errors/CAGE-NAME-001.md +0 -32
  498. package/managed/errors/CAGE-NAME-002.md +0 -32
  499. package/managed/errors/CAGE-NAME-003.md +0 -32
  500. package/managed/errors/CAGE-NAME-004.md +0 -32
  501. package/managed/errors/CAGE-NAME-005.md +0 -32
  502. package/managed/errors/CAGE-NAME-006.md +0 -32
  503. package/managed/errors/CAGE-ORG-001.md +0 -32
  504. package/managed/errors/CAGE-PAIR-002.md +0 -33
  505. package/managed/errors/CAGE-QUERY-001.md +0 -32
  506. package/managed/errors/CAGE-QUERY-002.md +0 -32
  507. package/managed/errors/CAGE-QUERY-003.md +0 -32
  508. package/managed/errors/CAGE-REACT-001.md +0 -32
  509. package/managed/errors/CAGE-REACT-002.md +0 -32
  510. package/managed/errors/CAGE-READS-001.md +0 -32
  511. package/managed/errors/CAGE-REG-001.md +0 -32
  512. package/managed/errors/CAGE-REG-002.md +0 -32
  513. package/managed/errors/CAGE-REG-003.md +0 -32
  514. package/managed/errors/CAGE-ROUTE-001.md +0 -32
  515. package/managed/errors/CAGE-SCOPE-001.md +0 -32
  516. package/managed/errors/CAGE-SCOPE-002.md +0 -32
  517. package/managed/errors/CAGE-SECRET-001.md +0 -32
  518. package/managed/errors/CAGE-SECRET-002.md +0 -32
  519. package/managed/errors/CAGE-SENS-001.md +0 -32
  520. package/managed/errors/CAGE-SQL-001.md +0 -32
  521. package/managed/errors/CAGE-SQL-002.md +0 -32
  522. package/managed/errors/CAGE-STATE-001.md +0 -32
  523. package/managed/errors/CAGE-STATE-002.md +0 -32
  524. package/managed/errors/CAGE-STATE-003.md +0 -32
  525. package/managed/errors/CAGE-STATE-004.md +0 -32
  526. package/managed/errors/CAGE-STATE-005.md +0 -32
  527. package/managed/errors/CAGE-STATE-010.md +0 -32
  528. package/managed/errors/CAGE-STATE-011.md +0 -32
  529. package/managed/errors/CAGE-SYNC-001.md +0 -35
  530. package/managed/errors/CAGE-TEST-001.md +0 -33
  531. package/managed/errors/CAGE-UNSAFE-001.md +0 -32
  532. package/managed/errors/CAGE-UPGRADE-001.md +0 -32
  533. package/managed/errors/README.md +0 -99
  534. package/templates/app/compose.yaml +0 -39
  535. /package/templates/app/apps/web/src/{style.css → app/styles/global.css} +0 -0
  536. /package/templates/app/packages/contracts/src/{queries.ts → queries/index.ts} +0 -0
  537. /package/templates/app/packages/domain/src/{inventory.ts → modules/inventory/inventory.ts} +0 -0
  538. /package/templates/app/packages/domain/src/{approvals.ts → modules/purchase/approvals.ts} +0 -0
  539. /package/templates/app/packages/domain/src/{jobs.ts → modules/purchase/jobs.ts} +0 -0
  540. /package/templates/app/packages/domain/src/{queries.ts → modules/purchase/queries.ts} +0 -0
@@ -0,0 +1,141 @@
1
+ import { kindLabels, nodeLabel } from './graph-model.js';
2
+ export function explainEdge(edge, model, labels = {}) {
3
+ const from = model.nodes.find((n) => n.key === edge.from);
4
+ const to = model.nodes.find((n) => n.key === edge.to);
5
+ const a = nodeLabel(from, labels), b = nodeLabel(to, labels);
6
+ const field = to.id.slice(to.id.indexOf('.') + 1);
7
+ const resource = (id) => labels[`resource:${id}`] ?? id;
8
+ const result = (label, category, why, purpose, evidence, source = from) => ({
9
+ label,
10
+ category,
11
+ why,
12
+ purpose,
13
+ evidence,
14
+ source,
15
+ basis: edge.derived || (edge.kind === 'uses' && ['事件载荷', '出站映射'].includes(edge.as ?? ''))
16
+ ? 'inferred'
17
+ : edge.kind === 'uses' && edge.as === '查询'
18
+ ? 'text-match'
19
+ : 'declared',
20
+ direction: `“${a}” → “${b}”:${label}。`,
21
+ });
22
+ switch (edge.kind) {
23
+ case 'field':
24
+ return result(to.attrs.virtual ? '声明关联字段' : '包含字段', '数据结构', `“${a}”的字段声明中包含“${b}”。`, to.attrs.virtual
25
+ ? '这是 fields 中的集合关系,不占主表列;点击它指向目标的连线,查看关系基数、存储和读写方式。'
26
+ : '说明业务对象由哪些数据组成;调整字段时,可以继续沿线查看受影响的查询、审批和事件。', [`${from.id}.fields.${field}`]);
27
+ case 'refs': {
28
+ if (edge.cardinality === 'one-to-many')
29
+ return result('一对多 1:N', '反向关联 · 只读', `“${a}”使用 hasMany 反查“${b}”,对应外键是 ${to.id}.${edge.mappedBy}。`, '按需分页列出属于当前对象的子对象,不新建列或中间表。改变归属要执行子对象的命令;读取同时受两侧权限和范围约束。', [`${from.id}: hasMany(${JSON.stringify(to.id)}, { mappedBy: ${JSON.stringify(edge.mappedBy)} })`]);
30
+ if (edge.cardinality === 'many-to-many')
31
+ return result('多对多 N:M', '集合关联 · 自动中间表', `“${a}”声明 manyToMany:一个所属对象可以关联多个“${b}”,同一个目标也可被多个所属对象关联。`, '框架生成中间表、同租户外键和组合唯一约束;所属对象的命令用 ID 数组替换关联,与主记录共同提交。解绑不删除目标,读取通过独立分页接口。', [`${from.id}: manyToMany(${JSON.stringify(to.id)})`, `自动中间表:${from.attrs.through}`]);
32
+ return result(edge.cardinality === 'one-to-one' ? '一对一 1:1' : '多对一 N:1', '数据关联', `“${a}”声明为引用“${b}”的字段,保存所关联对象的标识。`, edge.cardinality === 'one-to-one'
33
+ ? '租户内的唯一约束保证同一目标最多被这一字段引用一次;可选字段允许没有关联,并不要求双方一定存在。'
34
+ : `多个所属对象可以引用同一${kindLabels[to.kind]}。字段保存外键;具体查询仍受数据范围约束,这条线本身不执行查询。`, [`${from.id}: ref(${JSON.stringify(to.id)})${edge.cardinality === 'one-to-one' ? '.unique()' : ''}`]);
35
+ }
36
+ case 'ownership':
37
+ return result(`拥有${kindLabels[to.kind]}`, '标识归属 · 推导', `“${b}”的标识位于“${from.id}.”之下,投影据此将它归入“${a}”。`, '帮助整理同一业务对象的命令、状态和能力点。这是标识推导的归属,不是抽取器声明的依赖,也不会触发命令。', [`${to.id} → 所属聚合 ${from.id}`]);
38
+ case 'transition': {
39
+ const command = model.nodes.find((n) => n.kind === 'command' && n.id === edge.via);
40
+ const name = nodeLabel(command, labels);
41
+ const stays = edge.from === edge.to;
42
+ return result(stays ? '保持状态' : '迁移状态', '状态变化', `“${name}”声明${stays ? `在“${a}”状态执行后保持原状态` : `从“${a}”进入“${b}”`}。`, `用于检查业务生命周期是否完整。只有命令通过权限、约束并成功提交,${stays ? '操作才生效' : '状态才改变'}。`, [
43
+ `命令:${command.id}`,
44
+ `from: ${JSON.stringify(command.attrs.from)}`,
45
+ `to: ${JSON.stringify(command.attrs.to)}`,
46
+ ], command);
47
+ }
48
+ case 'reads':
49
+ case 'queries':
50
+ return result('读取数据', '读取依赖', `“${a}”在 reads 中声明读取“${b}”${edge.alias ? `,使用别名 ${edge.alias}` : ''}。`, '明确可读取的业务对象,并让影响分析找到依赖方;实际读取通过安全视图,受权限和数据范围约束。箭头指向数据来源,不表示先执行它。', [`${from.id}.reads${edge.alias ? `.${edge.alias}` : ''} → ${to.id}`]);
51
+ case 'emits':
52
+ return result('产生事件', '事件协作', `“${a}”的 emits 中声明了“${b}”事件。`, '命令成功执行时按声明生成事件,连接后续反应、审批或外部同步;事务失败时不能当作已经发生。', [`${from.id}.emits → ${to.id}`]);
53
+ case 'uses': {
54
+ const as = edge.as ?? '';
55
+ if (as === '可路由字段')
56
+ return result('允许审批读取', '审批可用字段', `“${a}”把“${b}”列入 routable,允许该字段作为审批流程变量。`, '明确审批可以拿到哪些业务数据。这里只表示允许使用;是否真的参与分支或审批选人,要查看审批路由的字段关系。', [`${from.id}.routable 包含 ${field}`]);
57
+ if (as === '路由条件') {
58
+ // fieldsUsed 同时包含 when 条件和 deptLeader 选人字段,不能一律解释成条件判断。
59
+ const nodes = from.attrs.nodes;
60
+ const conditions = nodes.filter((n) => n.kind === 'when' && n.cond.field === field);
61
+ const steps = nodes
62
+ .flatMap((n) => (n.kind === 'step' ? [n] : n.steps))
63
+ .filter((s) => s.approver.kind === 'deptLeader' && s.approver.field === field);
64
+ const operators = {
65
+ gt: '大于',
66
+ gte: '大于等于',
67
+ lt: '小于',
68
+ lte: '小于等于',
69
+ eq: '等于',
70
+ ne: '不等于',
71
+ in: '属于',
72
+ };
73
+ const reasons = [
74
+ ...steps.map((s) => `“${s.title}”步骤根据“${b}”找到对应部门的负责人`),
75
+ ...conditions.map((n) => `当“${b}”${operators[n.cond.op]} ${JSON.stringify(n.cond.value)} 时,加入${n.steps.map((s) => `“${s.title}”`).join('、')}步骤`),
76
+ ];
77
+ return result(steps.length && conditions.length ? '选人和分支依据' : steps.length ? '确定审批人' : '判断审批分支', '审批路由依赖', `${reasons.join(';')}。`, '字段值会影响审批人或需要经过的审批步骤;修改字段含义时,应一起检查这些路由配置。', [
78
+ ...steps.map((s) => `${s.title}: approver.deptLeader(${JSON.stringify(field)})`),
79
+ ...conditions.map((n) => `when(${n.cond.op}(${JSON.stringify(field)}, ${JSON.stringify(n.cond.value)}))`),
80
+ ]);
81
+ }
82
+ if (as === '事件载荷')
83
+ return result('携带字段', '事件数据依赖', `“${a}”的默认事件载荷按同名字段从业务对象取“${b}”。`, '订阅方可以用事件中的这份数据处理后续业务;字段变更需要检查事件及订阅方的数据约定。箭头指向载荷的数据来源。', [`事件 ${from.id} 的同名字段 ${field} ← ${to.id}`]);
84
+ if (as === '查询')
85
+ return result('查询使用字段', '查询字段线索', `抽取器在“${a}”的 SQL 或查询构造函数文本中匹配到了“${b}”的字段名。`, '提供检查查询依赖的线索。这里基于字段名文本匹配,可能匹配到注释或同名内容;实际筛选、分组或计算用途请核对查询声明。', [`${from.id}: SQL / build 文本匹配 ${field}`]);
86
+ if (as === '出站映射')
87
+ return result('映射外发字段', '外部同步数据', `“${a}”的 fields 声明包含“${b}”对应的字段名;抽取器将它关联到发出事件的聚合中同名字段。`, '用于核对外发字段范围和字段变更的同步影响;具体取值及转换由事件载荷与 map 函数决定。', [`${from.id}.fields 包含 ${field}`, `同名聚合字段:${to.id}`]);
88
+ if (as.startsWith('约束 ')) {
89
+ const rule = as.slice(3);
90
+ const purpose = rule.startsWith('ceiling(')
91
+ ? `用于校验“${b}”是否超出调用人的审批额度。`
92
+ : rule.startsWith('sod.notSameAs(')
93
+ ? `用于校验调用人与“${b}”记录的人不同,避免同一人完成需要分离的职责。`
94
+ : `该约束读取“${b}”执行命令前的业务检查;字段变更时应一起复核约束。`;
95
+ return result('执行约束检查', '命令校验依赖', `“${a}”声明的约束 ${rule} 使用“${b}”。`, purpose, [
96
+ `${from.id}.constraints → ${rule}`,
97
+ `约束 fields 包含 ${field}`,
98
+ ]);
99
+ }
100
+ return result('使用字段', '字段依赖', `“${a}”声明使用“${b}”。`, '用于追踪字段变更的影响范围。', [
101
+ as || edge.kind,
102
+ ]);
103
+ }
104
+ case 'cancels':
105
+ return result('取消审批', '流程取消', `“${b}”在 cancelOn 中声明了“${a}”事件。`, '事件发生后取消该单据仍在进行的审批;不会启动新流程,也不替代释放资源等业务反应。', [`${to.id}.cancelOn 包含 ${from.id}`], to);
106
+ case 'triggers': {
107
+ const label = to.kind === 'approval' ? '启动审批' : to.kind === 'outbound' ? '触发外部同步' : '触发反应';
108
+ return result(label, '事件触发', `“${b}”订阅“${a}”作为${to.kind === 'approval' ? '启动' : '触发'}事件。`, to.kind === 'approval'
109
+ ? '事件发生后启动对应审批流程,生成流程中的审批任务。'
110
+ : to.kind === 'outbound'
111
+ ? '事件发生后由 worker 处理外部推送,失败按投递策略重试。'
112
+ : `事件发生后执行订阅的反应${to.attrs.tx === 'same' ? ',该反应与原命令处于同一事务' : ';执行方式由反应的事务声明决定'}。`, [`${to.id}.${to.kind === 'approval' ? 'start' : 'event'} → ${from.id}`], to);
113
+ }
114
+ case 'targets': {
115
+ if (to.kind === 'connector')
116
+ return result('调用外部系统', '连接器调用', `“${a}”声明通过“${b}”执行操作 ${labels[`operation:${from.attrs.operation}`] ?? from.attrs.operation}。`, '由连接器统一处理地址与字段白名单、密钥、限流和超时,让外部同步保持可管控。', [`${from.id}.connector → ${to.id}`, `operation: ${from.attrs.operation}`]);
117
+ if (from.kind === 'outbound')
118
+ return result('成功后回写', '同步成功回调', `“${a}”的 onSuccess 绑定“${b}”。`, '外部操作成功后调用该命令,将返回结果按映射写回业务;失败时不执行成功回写。', [`${from.id}.onSuccess → ${to.id}`]);
119
+ if (from.kind === 'inbound')
120
+ return result('回调转为命令', '外部回调', `“${a}”将接收的外部回调映射到“${b}”。`, '外部回调通过验签和数据校验后,使用业务命令处理,统一遵守状态和业务约束。', [`${from.id}.command → ${to.id}`]);
121
+ const resources = [
122
+ edge.acquires ? `获取“${resource(edge.acquires)}”` : '',
123
+ edge.releases ? `释放“${resource(edge.releases)}”` : '',
124
+ ].filter(Boolean);
125
+ return result('调用业务命令', '反应调用', `“${a}”把事件映射后交给“${b}”处理。`, `将后续业务操作交给受约束的命令执行。${resources.length ? `此声明还标记${resources.join('、')},用于检查资源获取与释放是否成对。` : ''}`, [`${from.id}.command → ${to.id}`, ...resources]);
126
+ }
127
+ case 'binds': {
128
+ if (from.kind === 'route')
129
+ return result('定义审批步骤', '流程配置', `“${a}”与“${b}”使用同一流程标识 ${from.attrs.process}。`, '这份路由定义流程里的审批步骤、选人方式和条件分支,供代码内审批引擎使用。', [`process: ${from.attrs.process}`]);
130
+ const approved = from.attrs.onApproved === to.id;
131
+ const rejected = from.attrs.onRejected === to.id;
132
+ const outcome = approved && rejected ? '结束' : approved ? '通过' : '驳回';
133
+ return result(`${outcome}后执行`, '审批结论回写', `“${a}”${outcome}时绑定执行“${b}”。`, '将审批结论交给业务命令推进单据状态,并统一执行权限和业务约束。', [
134
+ approved ? `${from.id}.onApproved → ${to.id}` : '',
135
+ rejected ? `${from.id}.onRejected → ${to.id}` : '',
136
+ ].filter(Boolean));
137
+ }
138
+ case 'calls':
139
+ return result('允许调用命令', '任务调用范围', `“${a}”在 calls 中声明可以调用“${b}”。`, '明确任务能执行哪些业务写入,并支持影响分析。每次任务是否实际调用该命令,取决于 run 中的业务逻辑。', [`${from.id}.calls → ${to.id}`]);
140
+ }
141
+ }
@@ -0,0 +1,62 @@
1
+ // 轻量、可解释的名称检索;不调用模型,不把关键词匹配描述成“理解任意需求”。
2
+ // 中文用相邻双字作为线索,让“采购单取消”能匹配“取消采购单”,同时返回命中依据。
3
+ import { kindLabels, nodeLabel } from './graph-model.js';
4
+ const normalize = (value) => value.normalize('NFKC').toLowerCase().trim();
5
+ function searchTerms(query) {
6
+ const terms = query.match(/[a-z0-9_$.:/-]+/g) ?? [];
7
+ for (const segment of query.match(/[\p{Script=Han}]+/gu) ?? []) {
8
+ const chars = [...segment];
9
+ if (chars.length === 1)
10
+ terms.push(segment);
11
+ else
12
+ for (let i = 0; i < chars.length - 1; i++)
13
+ terms.push(chars[i] + chars[i + 1]);
14
+ }
15
+ return [...new Set(terms)];
16
+ }
17
+ function matchNode(node, query, labels, descriptions) {
18
+ const normalized = normalize(query);
19
+ if (!normalized)
20
+ return;
21
+ const terms = searchTerms(normalized);
22
+ const ownerId = ['field', 'state', 'command', 'capability', 'entry'].includes(node.kind)
23
+ ? node.id.split('.')[0]
24
+ : undefined;
25
+ const fields = [
26
+ ['key', node.key, 18],
27
+ ['id', node.id, 16],
28
+ ['label', nodeLabel(node, labels), 12],
29
+ ['description', descriptions[node.key] ?? String(node.attrs.description ?? ''), 10],
30
+ ['owner', ownerId ? (labels[`aggregate:${ownerId}`] ?? ownerId) : '', 2],
31
+ ];
32
+ const matches = [];
33
+ let score = 0;
34
+ for (const [field, value, weight] of fields) {
35
+ const text = normalize(value);
36
+ if (!text)
37
+ continue;
38
+ const exact = text === normalized;
39
+ const whole = text.includes(normalized);
40
+ const found = terms.filter((term) => text.includes(term));
41
+ if (!exact && !whole && found.length === 0)
42
+ continue;
43
+ const points = exact ? weight * 100 : whole ? weight * 30 : weight * found.length;
44
+ score = Math.max(score, points);
45
+ matches.push({ field, value, terms: exact || whole ? [query.trim()] : found });
46
+ }
47
+ if (!matches.length)
48
+ return;
49
+ return { key: node.key, id: node.id, kind: node.kind, label: nodeLabel(node, labels), score, matches };
50
+ }
51
+ /** 排序稳定,结果携带命中字段;多个候选不静默合成一个“正确需求”。 */
52
+ export function searchGraphNodes(model, query, labels = {}, options = {}) {
53
+ return model.nodes
54
+ .filter((node) => !options.kind || node.kind === options.kind)
55
+ .map((node) => matchNode(node, query, labels, options.descriptions ?? {}))
56
+ .filter((hit) => !!hit)
57
+ .sort((a, b) => b.score - a.score || a.key.localeCompare(b.key));
58
+ }
59
+ /** 画布的筛选与搜索下拉共用同一匹配规则,保留按节点类型查找的用法。 */
60
+ export function matchesNode(node, query, labels) {
61
+ return !!matchNode(node, query, labels, {}) || normalize(kindLabels[node.kind]).includes(normalize(query));
62
+ }
package/graph-ui/model.js CHANGED
@@ -1,82 +1,24 @@
1
+ import { kindLabels } from './graph-model.js';
2
+ export { nodeLabel, projectGraph, neighborhood, edgeLabels } from './graph-model.js';
3
+ export { matchesNode } from './graph-search.js';
1
4
  export const kinds = {
2
- aggregate: { label: '业务对象', color: '#197c72', short: '表', column: 0 },
3
- command: { label: '命令', color: '#547db2', short: '令', column: 1 },
4
- event: { label: '事件', color: '#c08c36', short: '事', column: 2 },
5
- reaction: { label: '反应', color: '#aa7288', short: '应', column: 3 },
6
- query: { label: '查询', color: '#648c5b', short: '查', column: 1 },
7
- approval: { label: '审批', color: '#9c739f', short: '审', column: 3 },
8
- route: { label: '审批路由', color: '#9c739f', short: '路', column: 3 },
9
- job: { label: '定时任务', color: '#7f82a0', short: '时', column: 0 },
10
- connector: { label: '连接器', color: '#b57452', short: '连', column: 4 },
11
- outbound: { label: '出站映射', color: '#b57452', short: '出', column: 3 },
12
- inbound: { label: '入站回调', color: '#b57452', short: '入', column: 4 },
13
- field: { label: '字段', color: '#67998f', short: '字', column: 1 },
14
- state: { label: '状态', color: '#898b69', short: '态', column: 2 },
15
- capability: { label: '能力点', color: '#81949f', short: '权', column: 1 },
16
- entry: { label: '新建入口', color: '#89938e', short: '+', column: 0 },
5
+ aggregate: { label: kindLabels.aggregate, color: '#197c72', short: '表', column: 0 },
6
+ command: { label: kindLabels.command, color: '#547db2', short: '令', column: 1 },
7
+ event: { label: kindLabels.event, color: '#c08c36', short: '事', column: 2 },
8
+ reaction: { label: kindLabels.reaction, color: '#aa7288', short: '应', column: 3 },
9
+ query: { label: kindLabels.query, color: '#648c5b', short: '查', column: 1 },
10
+ approval: { label: kindLabels.approval, color: '#9c739f', short: '审', column: 3 },
11
+ route: { label: kindLabels.route, color: '#9c739f', short: '路', column: 3 },
12
+ job: { label: kindLabels.job, color: '#7f82a0', short: '时', column: 0 },
13
+ connector: { label: kindLabels.connector, color: '#b57452', short: '连', column: 4 },
14
+ outbound: { label: kindLabels.outbound, color: '#b57452', short: '出', column: 3 },
15
+ inbound: { label: kindLabels.inbound, color: '#b57452', short: '入', column: 4 },
16
+ field: { label: kindLabels.field, color: '#67998f', short: '字', column: 1 },
17
+ state: { label: kindLabels.state, color: '#898b69', short: '态', column: 2 },
18
+ capability: { label: kindLabels.capability, color: '#81949f', short: '权', column: 1 },
19
+ entry: { label: kindLabels.entry, color: '#89938e', short: '+', column: 0 },
17
20
  };
18
- export const edgeLabels = {
19
- transition: '状态迁移',
20
- reads: '读取',
21
- emits: '发出',
22
- uses: '使用字段',
23
- triggers: '触发',
24
- targets: '调用目标',
25
- queries: '查询',
26
- refs: '引用',
27
- calls: '调用',
28
- binds: '审批绑定',
29
- field: '声明字段',
30
- ownership: '归属(视图推导)',
31
- };
32
- /** 中文用于展示,英文仍作为复制、搜索和查源码的稳定标识。
33
- * 没有配置业务名称的未知节点保留原标识,不能猜测业务含义后悄悄改名。 */
34
- export function nodeLabel(node, labels = {}) {
35
- if (node.kind === 'entry')
36
- return '新建入口';
37
- return labels[node.key] || String(node.attrs.description || node.id);
38
- }
39
- export function matchesNode(node, query, labels) {
40
- return `${nodeLabel(node, labels)} ${node.id} ${kinds[node.kind].label}`
41
- .toLowerCase()
42
- .includes(query.toLowerCase());
43
- }
44
- export function projectGraph(graph) {
45
- const nodes = graph.nodes.map((n) => ({ ...n, key: `${n.kind}:${n.id}` }));
46
- const edges = [];
47
- // 节点身份包含 kind。能力点和命令可能同名,不能单用显示名作为 SVG 的主键。
48
- const find = (id) => nodes.find((n) => n.id === id && n.kind !== 'capability') ?? nodes.find((n) => n.id === id);
49
- for (const e of graph.edges) {
50
- let from = find(e.from);
51
- const to = find(e.to);
52
- // [new] 是迁移的起点,并不属于声明状态;在视图中显式标为虚拟入口。
53
- if (!from && e.kind === 'transition' && e.from.endsWith('.[new]')) {
54
- from = { key: `entry:${e.from}`, id: e.from, kind: 'entry', hash: '', attrs: {} };
55
- nodes.push(from);
56
- }
57
- if (from && to)
58
- edges.push({ ...e, from: from.key, to: to.key, key: `edge:${edges.length}`, derived: false });
59
- }
60
- for (const n of nodes) {
61
- if (!['command', 'state', 'capability', 'entry'].includes(n.kind))
62
- continue;
63
- const owner = nodes.find((a) => a.kind === 'aggregate' && n.id.startsWith(`${a.id}.`));
64
- if (owner)
65
- edges.push({ key: `owner:${n.key}`, kind: 'ownership', from: owner.key, to: n.key, derived: true });
66
- }
67
- return { nodes, edges };
68
- }
69
- export const isSystem = (n) => /^(User|Role|Dept)(\.|$)/.test(n.id);
70
- export function neighborhood(model, key) {
71
- const connected = new Set([key]);
72
- for (const e of model.edges) {
73
- if (e.from === key)
74
- connected.add(e.to);
75
- if (e.to === key)
76
- connected.add(e.from);
77
- }
78
- return connected;
79
- }
21
+ export const isSystem = (node) => /^(User|Role|Dept)(\.|$)/.test(node.id);
80
22
  export const NODE_WIDTH = 190;
81
23
  export const NODE_HEIGHT = 56;
82
24
  /** 按类型分组的平衡列 + 邻接排序。相同声明每次打开位置相同,没有持续运动的力导向
@@ -1,125 +1,2 @@
1
- import { kinds, nodeLabel } from './model.js';
2
- export function explainEdge(edge, model, labels = {}) {
3
- const from = model.nodes.find((n) => n.key === edge.from);
4
- const to = model.nodes.find((n) => n.key === edge.to);
5
- const a = nodeLabel(from, labels), b = nodeLabel(to, labels);
6
- const field = to.id.slice(to.id.indexOf('.') + 1);
7
- const resource = (id) => labels[`resource:${id}`] ?? id;
8
- const result = (label, category, why, purpose, evidence, source = from) => ({
9
- label,
10
- category,
11
- why,
12
- purpose,
13
- evidence,
14
- source,
15
- direction: `“${a}” → “${b}”:${label}。`,
16
- });
17
- switch (edge.kind) {
18
- case 'field':
19
- return result('包含字段', '数据结构', `“${a}”的字段声明中包含“${b}”。`, '说明业务对象由哪些数据组成;调整字段时,可以继续沿线查看受影响的查询、审批和事件。', [`${from.id}.fields.${field}`]);
20
- case 'refs':
21
- return result('引用对象', '数据关联', `“${a}”声明为引用“${b}”的字段,保存所关联对象的标识。`, `用于理解这条数据关联到哪个${kinds[to.kind].label}。例如部门字段关联部门对象;具体查询仍受数据范围约束。这条线本身不执行查询。`, [`${from.id}: ref(${JSON.stringify(to.id)})`]);
22
- case 'ownership':
23
- return result(`拥有${kinds[to.kind].label}`, '视图归属 · 虚线', `“${b}”的标识位于“${from.id}.”之下,页面据此将它归入“${a}”。`, '帮助整理同一业务对象的命令、状态和能力点。这是页面推导的归属,不是抽取器声明的依赖,也不会触发命令。', [`${to.id} → 所属聚合 ${from.id}`]);
24
- case 'transition': {
25
- const command = model.nodes.find((n) => n.kind === 'command' && n.id === edge.via);
26
- const name = nodeLabel(command, labels);
27
- const stays = edge.from === edge.to;
28
- return result(stays ? '保持状态' : '迁移状态', '状态变化', `“${name}”声明${stays ? `在“${a}”状态执行后保持原状态` : `从“${a}”进入“${b}”`}。`, `用于检查业务生命周期是否完整。只有命令通过权限、约束并成功提交,${stays ? '操作才生效' : '状态才改变'}。`, [
29
- `命令:${command.id}`,
30
- `from: ${JSON.stringify(command.attrs.from)}`,
31
- `to: ${JSON.stringify(command.attrs.to)}`,
32
- ], command);
33
- }
34
- case 'reads':
35
- case 'queries':
36
- return result('读取数据', '读取依赖', `“${a}”在 reads 中声明读取“${b}”${edge.alias ? `,使用别名 ${edge.alias}` : ''}。`, '明确可读取的业务对象,并让影响分析找到依赖方;实际读取通过安全视图,受权限和数据范围约束。箭头指向数据来源,不表示先执行它。', [`${from.id}.reads${edge.alias ? `.${edge.alias}` : ''} → ${to.id}`]);
37
- case 'emits':
38
- return result('产生事件', '事件协作', `“${a}”的 emits 中声明了“${b}”事件。`, '命令成功执行时按声明生成事件,连接后续反应、审批或外部同步;事务失败时不能当作已经发生。', [`${from.id}.emits → ${to.id}`]);
39
- case 'uses': {
40
- const as = edge.as ?? '';
41
- if (as === '可路由字段')
42
- return result('允许审批读取', '审批可用字段', `“${a}”把“${b}”列入 routable,允许该字段作为审批流程变量。`, '明确审批可以拿到哪些业务数据。这里只表示允许使用;是否真的参与分支或审批选人,要查看审批路由的字段关系。', [`${from.id}.routable 包含 ${field}`]);
43
- if (as === '路由条件') {
44
- // fieldsUsed 同时包含 when 条件和 deptLeader 选人字段,不能一律解释成条件判断。
45
- const nodes = from.attrs.nodes;
46
- const conditions = nodes.filter((n) => n.kind === 'when' && n.cond.field === field);
47
- const steps = nodes
48
- .flatMap((n) => (n.kind === 'step' ? [n] : n.steps))
49
- .filter((s) => s.approver.kind === 'deptLeader' && s.approver.field === field);
50
- const operators = {
51
- gt: '大于',
52
- gte: '大于等于',
53
- lt: '小于',
54
- lte: '小于等于',
55
- eq: '等于',
56
- ne: '不等于',
57
- in: '属于',
58
- };
59
- const reasons = [
60
- ...steps.map((s) => `“${s.title}”步骤根据“${b}”找到对应部门的负责人`),
61
- ...conditions.map((n) => `当“${b}”${operators[n.cond.op]} ${JSON.stringify(n.cond.value)} 时,加入${n.steps.map((s) => `“${s.title}”`).join('、')}步骤`),
62
- ];
63
- return result(steps.length && conditions.length ? '选人和分支依据' : steps.length ? '确定审批人' : '判断审批分支', '审批路由依赖', `${reasons.join(';')}。`, '字段值会影响审批人或需要经过的审批步骤;修改字段含义时,应一起检查这些路由配置。', [
64
- ...steps.map((s) => `${s.title}: approver.deptLeader(${JSON.stringify(field)})`),
65
- ...conditions.map((n) => `when(${n.cond.op}(${JSON.stringify(field)}, ${JSON.stringify(n.cond.value)}))`),
66
- ]);
67
- }
68
- if (as === '事件载荷')
69
- return result('携带字段', '事件数据依赖', `“${a}”的默认事件载荷按同名字段从业务对象取“${b}”。`, '订阅方可以用事件中的这份数据处理后续业务;字段变更需要检查事件及订阅方的数据约定。箭头指向载荷的数据来源。', [`事件 ${from.id} 的同名字段 ${field} ← ${to.id}`]);
70
- if (as === '查询')
71
- return result('查询使用字段', '查询字段线索', `抽取器在“${a}”的 SQL 或查询构造函数文本中匹配到了“${b}”的字段名。`, '提供检查查询依赖的线索。这里基于字段名文本匹配,可能匹配到注释或同名内容;实际筛选、分组或计算用途请核对查询声明。', [`${from.id}: SQL / build 文本匹配 ${field}`]);
72
- if (as === '出站映射')
73
- return result('映射外发字段', '外部同步数据', `“${a}”的 fields 声明包含“${b}”对应的字段名;抽取器将它关联到发出事件的聚合中同名字段。`, '用于核对外发字段范围和字段变更的同步影响;具体取值及转换由事件载荷与 map 函数决定。', [`${from.id}.fields 包含 ${field}`, `同名聚合字段:${to.id}`]);
74
- if (as.startsWith('约束 ')) {
75
- const rule = as.slice(3);
76
- const purpose = rule.startsWith('ceiling(')
77
- ? `用于校验“${b}”是否超出调用人的审批额度。`
78
- : rule.startsWith('sod.notSameAs(')
79
- ? `用于校验调用人与“${b}”记录的人不同,避免同一人完成需要分离的职责。`
80
- : `该约束读取“${b}”执行命令前的业务检查;字段变更时应一起复核约束。`;
81
- return result('执行约束检查', '命令校验依赖', `“${a}”声明的约束 ${rule} 使用“${b}”。`, purpose, [
82
- `${from.id}.constraints → ${rule}`,
83
- `约束 fields 包含 ${field}`,
84
- ]);
85
- }
86
- return result('使用字段', '字段依赖', `“${a}”声明使用“${b}”。`, '用于追踪字段变更的影响范围。', [
87
- as || edge.kind,
88
- ]);
89
- }
90
- case 'triggers': {
91
- const label = to.kind === 'approval' ? '启动审批' : to.kind === 'outbound' ? '触发外部同步' : '触发反应';
92
- return result(label, '事件触发', `“${b}”订阅“${a}”作为${to.kind === 'approval' ? '启动' : '触发'}事件。`, to.kind === 'approval'
93
- ? '事件发生后启动对应审批流程,生成流程中的审批任务。'
94
- : to.kind === 'outbound'
95
- ? '事件发生后由 worker 处理外部推送,失败按投递策略重试。'
96
- : `事件发生后执行订阅的反应${to.attrs.tx === 'same' ? ',该反应与原命令处于同一事务' : ';执行方式由反应的事务声明决定'}。`, [`${to.id}.${to.kind === 'approval' ? 'start' : 'event'} → ${from.id}`], to);
97
- }
98
- case 'targets': {
99
- if (to.kind === 'connector')
100
- return result('调用外部系统', '连接器调用', `“${a}”声明通过“${b}”执行操作 ${labels[`operation:${from.attrs.operation}`] ?? from.attrs.operation}。`, '由连接器统一处理地址与字段白名单、密钥、限流和超时,让外部同步保持可管控。', [`${from.id}.connector → ${to.id}`, `operation: ${from.attrs.operation}`]);
101
- if (from.kind === 'outbound')
102
- return result('成功后回写', '同步成功回调', `“${a}”的 onSuccess 绑定“${b}”。`, '外部操作成功后调用该命令,将返回结果按映射写回业务;失败时不执行成功回写。', [`${from.id}.onSuccess → ${to.id}`]);
103
- if (from.kind === 'inbound')
104
- return result('回调转为命令', '外部回调', `“${a}”将接收的外部回调映射到“${b}”。`, '外部回调通过验签和数据校验后,使用业务命令处理,统一遵守状态和业务约束。', [`${from.id}.command → ${to.id}`]);
105
- const resources = [
106
- edge.acquires ? `获取“${resource(edge.acquires)}”` : '',
107
- edge.releases ? `释放“${resource(edge.releases)}”` : '',
108
- ].filter(Boolean);
109
- return result('调用业务命令', '反应调用', `“${a}”把事件映射后交给“${b}”处理。`, `将后续业务操作交给受约束的命令执行。${resources.length ? `此声明还标记${resources.join('、')},用于检查资源获取与释放是否成对。` : ''}`, [`${from.id}.command → ${to.id}`, ...resources]);
110
- }
111
- case 'binds': {
112
- if (from.kind === 'route')
113
- return result('定义审批步骤', '流程配置', `“${a}”与“${b}”使用同一流程标识 ${from.attrs.process}。`, '这份路由定义流程里的审批步骤、选人方式和条件分支,供代码内审批引擎使用。', [`process: ${from.attrs.process}`]);
114
- const approved = from.attrs.onApproved === to.id;
115
- const rejected = from.attrs.onRejected === to.id;
116
- const outcome = approved && rejected ? '结束' : approved ? '通过' : '驳回';
117
- return result(`${outcome}后执行`, '审批结论回写', `“${a}”${outcome}时绑定执行“${b}”。`, '将审批结论交给业务命令推进单据状态,并统一执行权限和业务约束。', [
118
- approved ? `${from.id}.onApproved → ${to.id}` : '',
119
- rejected ? `${from.id}.onRejected → ${to.id}` : '',
120
- ].filter(Boolean));
121
- }
122
- case 'calls':
123
- return result('允许调用命令', '任务调用范围', `“${a}”在 calls 中声明可以调用“${b}”。`, '明确任务能执行哪些业务写入,并支持影响分析。每次任务是否实际调用该命令,取决于 run 中的业务逻辑。', [`${from.id}.calls → ${to.id}`]);
124
- }
125
- }
1
+ // Web 与 AI 上下文共用关系解释,保持证据和业务含义一致。
2
+ export { explainEdge } from './graph-relations.js';
@@ -3,7 +3,8 @@ export const TABLE_HEADER = 58;
3
3
  export const TABLE_ROW = 38;
4
4
  export const TABLE_FOOTER = 34;
5
5
  /** 对象卡片展示声明字段,而不是猜测完整数据库结构。系统自动添加的列不在
6
- * 抽取结果中,因此这里不补造 id、主键、外键约束或一对多基数。
6
+ * 抽取结果中,因此这里不补造 id 或主键。集合关系也留在 fields 中展示,
7
+ * 用类型和虚拟关系标记区分物理列,基数来自抽取器明确记录的声明。
7
8
  * 字段仍保留原节点身份,搜索、点击和关系解释都能回到同一份声明。 */
8
9
  export function tableCards(model) {
9
10
  return model.nodes
@@ -64,6 +65,8 @@ export function fieldType(field) {
64
65
  boolean: '布尔',
65
66
  id: '标识',
66
67
  ref: '引用',
68
+ hasMany: '一对多',
69
+ manyToMany: '多对多',
67
70
  list: '列表',
68
71
  json: 'JSON',
69
72
  date: '日期',
@@ -904,6 +904,27 @@ h1 {
904
904
  font-size: 10px;
905
905
  padding: 8px 9px;
906
906
  }
907
+ /* 交给 AI 的入口沿用纸面绿,以独立阅读块说明包含什么,避免挤进节点操作行。 */
908
+ .context-handoff {
909
+ padding: 12px;
910
+ margin: 0 0 20px;
911
+ background: #f1f6ed;
912
+ border: 1px solid #dde7d5;
913
+ border-radius: 7px;
914
+ }
915
+ .context-handoff .button {
916
+ width: 100%;
917
+ font-size: 11px;
918
+ color: #365f45;
919
+ border-color: #ceddc3;
920
+ background: #fbfdf8;
921
+ }
922
+ .context-handoff p {
923
+ margin: 9px 0 0;
924
+ font-size: 10px;
925
+ line-height: 1.8;
926
+ color: #7b8d72;
927
+ }
907
928
  .detail-section {
908
929
  border-top: 1px solid #edf0e7;
909
930
  padding-top: 16px;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@yss123/cage-cli",
3
- "version": "0.1.0",
4
- "description": "cage 命令行:gen、check、impact、test、verify、budget、sync、upgrade、dev、migrate",
3
+ "version": "0.2.0",
4
+ "description": "cage 命令行:init、gen、check、graph、context、impact、test、verify、budget、sync、upgrade、dev、migrate",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "cage": "./bin/cage.js",
@@ -19,10 +19,10 @@
19
19
  "dist",
20
20
  "bin",
21
21
  "graph-ui",
22
- "templates",
23
- "managed"
22
+ "templates"
24
23
  ],
25
24
  "dependencies": {
25
+ "@clack/prompts": "^1.8.1",
26
26
  "dependency-cruiser": "^17.0.1",
27
27
  "eslint": "^9.35.0",
28
28
  "pg": "^8.16.3",
@@ -31,9 +31,9 @@
31
31
  "typescript": "~5.9.3",
32
32
  "vitest": "^4.1.12",
33
33
  "zod": "^4.1.5",
34
- "@yss123/cage-testkit": "0.1.0",
35
- "@yss123/cage-kernel": "0.1.0",
36
- "@yss123/cage-lint": "0.1.0"
34
+ "@yss123/cage-kernel": "0.2.0",
35
+ "@yss123/cage-lint": "0.2.0",
36
+ "@yss123/cage-testkit": "0.2.0"
37
37
  },
38
38
  "devDependencies": {
39
39
  "@types/pg": "^8.15.5"
@@ -56,7 +56,7 @@
56
56
  "node": ">=24"
57
57
  },
58
58
  "scripts": {
59
- "build": "tsc -p tsconfig.json && tsc -p ui/tsconfig.json && node scripts/build-graph.mjs && node scripts/copy-assets.mjs",
59
+ "build": "node scripts/clean-dist.ts && tsc -p tsconfig.json && tsc -p ui/tsconfig.json && node scripts/build-graph.ts && node scripts/copy-assets.ts",
60
60
  "typecheck": "tsc -p tsconfig.json --noEmit && tsc -p ui/tsconfig.json",
61
61
  "test": "vitest run"
62
62
  }
@@ -1,17 +1,61 @@
1
- # 推荐运行 pnpm db:setup 自动生成 .env 和随机密码;不要直接使用占位符连接。
2
- CAGE_POSTGRES_PORT=54329
3
- CAGE_TEST_POSTGRES_PORT=54330
4
- POSTGRES_PASSWORD=<开发集群超级用户密码>
5
- CAGE_TEST_POSTGRES_PASSWORD=<测试集群独立密码>
6
- CAGE_SUPER_DATABASE_URL=postgresql://postgres:<开发集群超级用户密码>@127.0.0.1:54329/postgres
7
- CAGE_APP_DATABASE_URL=postgresql://cage_app:<业务写入密码>@127.0.0.1:54329/cage_dev
8
- CAGE_READ_DATABASE_URL=postgresql://cage_read:<只读密码>@127.0.0.1:54329/cage_dev
9
- CAGE_MIGRATE_DATABASE_URL=postgresql://cage_migrate:<迁移密码>@127.0.0.1:54329/cage_dev
10
- # 只允许指向可丢弃的独立测试集群。
11
- CAGE_TEST_DATABASE_URL=postgresql://postgres:<测试集群独立密码>@127.0.0.1:54330/postgres
12
- CAGE_COOKIE_SECRET=<随机会话签名密钥>
1
+ # Cage 业务项目配置:复制此文件为同目录的 .env,首次只填写管理员数据库连接。
2
+ # .env 位于业务项目根目录,与 package.json、cage.config.json 同级。
3
+ # pnpm exec cage init / pnpm dev / pnpm migrate 自动读取它;同名外部环境变量优先。
4
+ # 修改后重启 dev;migrate 在下次执行时读取新值。不要把 .env 提交到 Git。
5
+ # 数据库需已经存在并能连接;框架不安装 PostgreSQL、不创建数据库、不管理 Docker。
6
+
7
+ # ===== 第一步:提供已有数据库的管理员连接,执行 pnpm exec cage init =====
8
+ # 格式:postgresql://用户名:密码@主机:端口/数据库名
9
+ # 将 127.0.0.1、5432、business_db 换成实际地址、端口和已有数据库名。
10
+ # 密码中的特殊字符必须 URL 编码,例如 @ 写成 %40、# 写成 %23、% 写成 %25。
11
+ # [首次初始化必填] 管理员通常叫 postgres;若你的管理员叫 root,使用实际账号名。
12
+ # 管理员需要创建角色、schema 和授权的权限。连接的数据库必须已经存在。
13
+ # 将下面的管理员密码、主机、端口和库名改为实际值,再执行 pnpm exec cage init。
14
+ # init 会创建框架账号、生成独立随机密码和 Cookie 密钥,并自动填好下面的配置。
15
+ # 成功后管理员连接会从 .env 移除;日常开发不用再提供管理员密码。
16
+ CAGE_ADMIN_DATABASE_URL=postgresql://postgres:REPLACE_WITH_ADMIN_PASSWORD@127.0.0.1:5432/business_db
17
+
18
+ # ===== 第二步:以下配置由 init 自动填充,无需手动创建账号或编写授权 SQL =====
19
+ # 三个连接使用同一个数据库,主机、端口、库名一致,账号权限不同。
20
+ # 重复 init 会验证并复用已保存的账号,不重置密码。
21
+ # 若集群已有 Cage 账号且本项目未保存连接,需复用原凭证,不能仅凭管理员重置其他项目的密码。
22
+
23
+ # [必填] 业务写入账号 cage_app:API 和 worker 执行命令、审计和后台任务使用。
24
+ # 它有业务所需读写权限,不应使用 postgres 超级用户替代。
25
+ # CAGE_APP_DATABASE_URL=
26
+
27
+ # [必填] 安全查询账号 cage_read:仅通过安全视图读取,经数据范围过滤和敏感字段掩码。
28
+ # 不要与写入账号混用,否则会破坏读写权限隔离。
29
+ # CAGE_READ_DATABASE_URL=
30
+
31
+ # [执行迁移时必填] 迁移账号 cage_migrate:pnpm migrate 用它更新表、索引、视图和权限。
32
+ # 它连接上述同一个数据库;迁移只在手动执行命令时发生,dev 不自动执行迁移。
33
+ # 生产环境只给迁移任务配置此项,API / worker 无需获得此账号。
34
+ # CAGE_MIGRATE_DATABASE_URL=
35
+
36
+ # ===== 应用配置 =====
37
+ # [init 自动生成] Cookie 签名密钥,不是数据库密码,也不是网页登录密码。
38
+ # 自动生成 32 字节随机值;已有值保留。同一应用的多个实例使用同一值。
39
+ # CAGE_COOKIE_SECRET=
40
+
41
+ # [可选,开发默认 fake] 第三方连接器阶段:fake 使用模拟实现;test / prod 使用真实适配器。
42
+ # 本项只影响第三方外部服务,fake 阶段仍连接真实 PostgreSQL。
43
+ # test / prod 需另行配置业务连接器要求的地址与密钥,不是修改此值就完成对接。
13
44
  CAGE_STAGE=fake
14
- # 5173 被 Windows 保留时,可改成 15173。端口不会自动漂移,避免访问到错误的服务。
15
- CAGE_WEB_PORT=5173
45
+
46
+ # [可选,Vite 默认 5173] 本地 Web 页面端口。这里选 15173,减少 Windows 保留端口冲突。
47
+ # 多个应用同时运行时,各选一个空闲端口;它与数据库端口无关。
48
+ CAGE_WEB_PORT=15173
49
+
50
+ # [可选,默认 3000] API 服务端口;本地 Web 代理自动转发到这个端口。
16
51
  PORT=3000
52
+
53
+ # [可选,范例默认 0.0.0.0] API 监听地址;这里使用 127.0.0.1,仅允许本机访问。
54
+ # 需要容器内或局域网访问时改为 0.0.0.0,并按部署环境配置网络访问权限。
17
55
  HOST=127.0.0.1
56
+
57
+ # ===== 仅执行数据库测试时使用;正常启动不需要 =====
58
+ # [可选] 独立、可丢弃 PostgreSQL 测试集群的管理连接,需有建库和管理角色权限。
59
+ # 测试会创建/删除临时库、初始化集群级角色;不能指向业务或生产数据库所在的集群。
60
+ # 不填写时测试工具使用内嵌真实 PostgreSQL;这仅是测试行为,dev 不会自动启动数据库。
61
+ # CAGE_TEST_DATABASE_URL=postgresql://test_admin:REPLACE_WITH_TEST_PASSWORD@127.0.0.1:5433/postgres