@yss123/cage-cli 0.2.0 → 0.3.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 (607) hide show
  1. package/README.md +47 -3
  2. package/bin/cage.js +5 -0
  3. package/bin/create-cage-app.js +5 -0
  4. package/dist/analyze.d.ts +16 -0
  5. package/dist/analyze.js +18 -2
  6. package/dist/analyze.js.map +1 -1
  7. package/dist/baseline.d.ts +8 -0
  8. package/dist/baseline.js +13 -0
  9. package/dist/baseline.js.map +1 -1
  10. package/dist/bootstrap.d.ts +20 -0
  11. package/dist/bootstrap.js +75 -0
  12. package/dist/bootstrap.js.map +1 -0
  13. package/dist/budget.d.ts +7 -0
  14. package/dist/budget.js +49 -5
  15. package/dist/budget.js.map +1 -1
  16. package/dist/capabilities.d.ts +38 -0
  17. package/dist/capabilities.js +133 -0
  18. package/dist/capabilities.js.map +1 -0
  19. package/dist/config.d.ts +31 -2
  20. package/dist/config.js +22 -0
  21. package/dist/config.js.map +1 -1
  22. package/dist/context.d.ts +14 -0
  23. package/dist/context.js +35 -2
  24. package/dist/context.js.map +1 -1
  25. package/dist/create-prompt.d.ts +7 -0
  26. package/dist/create-prompt.js +12 -0
  27. package/dist/create-prompt.js.map +1 -1
  28. package/dist/create.d.ts +21 -1
  29. package/dist/create.js +23 -1
  30. package/dist/create.js.map +1 -1
  31. package/dist/derive.d.ts +23 -0
  32. package/dist/derive.js +40 -0
  33. package/dist/derive.js.map +1 -1
  34. package/dist/dev.d.ts +6 -0
  35. package/dist/dev.js +11 -0
  36. package/dist/dev.js.map +1 -1
  37. package/dist/doctor.d.ts +98 -0
  38. package/dist/doctor.js +93 -0
  39. package/dist/doctor.js.map +1 -0
  40. package/dist/env.d.ts +6 -0
  41. package/dist/env.js +11 -0
  42. package/dist/env.js.map +1 -1
  43. package/dist/findings.d.ts +27 -0
  44. package/dist/findings.js +20 -0
  45. package/dist/findings.js.map +1 -1
  46. package/dist/gen/contracts.d.ts +28 -0
  47. package/dist/gen/contracts.js +52 -12
  48. package/dist/gen/contracts.js.map +1 -1
  49. package/dist/gen/index.d.ts +26 -0
  50. package/dist/gen/index.js +69 -1
  51. package/dist/gen/index.js.map +1 -1
  52. package/dist/gen/zod-code.d.ts +42 -0
  53. package/dist/gen/zod-code.js +178 -16
  54. package/dist/gen/zod-code.js.map +1 -1
  55. package/dist/graph-approvals.d.ts +60 -0
  56. package/dist/graph-approvals.js +80 -0
  57. package/dist/graph-approvals.js.map +1 -0
  58. package/dist/graph-business.d.ts +135 -0
  59. package/dist/graph-business.js +404 -0
  60. package/dist/graph-business.js.map +1 -0
  61. package/dist/graph-command.d.ts +17 -0
  62. package/dist/graph-command.js +21 -0
  63. package/dist/graph-command.js.map +1 -1
  64. package/dist/graph-model.d.ts +44 -0
  65. package/dist/graph-model.js +36 -3
  66. package/dist/graph-model.js.map +1 -1
  67. package/dist/graph-presentation.d.ts +16 -0
  68. package/dist/graph-presentation.js +10 -1
  69. package/dist/graph-presentation.js.map +1 -1
  70. package/dist/graph-query.d.ts +95 -0
  71. package/dist/graph-query.js +62 -0
  72. package/dist/graph-query.js.map +1 -1
  73. package/dist/graph-relations.d.ts +20 -0
  74. package/dist/graph-relations.js +34 -4
  75. package/dist/graph-relations.js.map +1 -1
  76. package/dist/graph-search.d.ts +35 -0
  77. package/dist/graph-search.js +36 -0
  78. package/dist/graph-search.js.map +1 -1
  79. package/dist/graph-snapshot.d.ts +26 -0
  80. package/dist/graph-snapshot.js +30 -1
  81. package/dist/graph-snapshot.js.map +1 -1
  82. package/dist/graph.d.ts +13 -2
  83. package/dist/graph.js +59 -4
  84. package/dist/graph.js.map +1 -1
  85. package/dist/impact.d.ts +50 -0
  86. package/dist/impact.js +62 -2
  87. package/dist/impact.js.map +1 -1
  88. package/dist/index.d.ts +10 -0
  89. package/dist/index.js +10 -0
  90. package/dist/index.js.map +1 -1
  91. package/dist/init.d.ts +12 -0
  92. package/dist/init.js +15 -0
  93. package/dist/init.js.map +1 -1
  94. package/dist/load.d.ts +10 -0
  95. package/dist/load.js +15 -0
  96. package/dist/load.js.map +1 -1
  97. package/dist/main.d.ts +6 -0
  98. package/dist/main.js +93 -5
  99. package/dist/main.js.map +1 -1
  100. package/dist/migrate.d.ts +12 -0
  101. package/dist/migrate.js +17 -0
  102. package/dist/migrate.js.map +1 -1
  103. package/dist/ontology.d.ts +67 -2
  104. package/dist/ontology.js +89 -2
  105. package/dist/ontology.js.map +1 -1
  106. package/dist/process-rules.d.ts +21 -0
  107. package/dist/process-rules.js +100 -0
  108. package/dist/process-rules.js.map +1 -0
  109. package/dist/process.d.ts +32 -0
  110. package/dist/process.js +32 -0
  111. package/dist/process.js.map +1 -1
  112. package/dist/project-structure.d.ts +5 -0
  113. package/dist/project-structure.js +10 -0
  114. package/dist/project-structure.js.map +1 -1
  115. package/dist/registration.d.ts +16 -0
  116. package/dist/registration.js +56 -0
  117. package/dist/registration.js.map +1 -0
  118. package/dist/rules.d.ts +52 -0
  119. package/dist/rules.js +58 -0
  120. package/dist/rules.js.map +1 -1
  121. package/dist/runtime-diagnostics.d.ts +27 -0
  122. package/dist/runtime-diagnostics.js +60 -0
  123. package/dist/runtime-diagnostics.js.map +1 -0
  124. package/dist/snapshots.d.ts +40 -0
  125. package/dist/snapshots.js +62 -0
  126. package/dist/snapshots.js.map +1 -0
  127. package/dist/static-check.d.ts +33 -0
  128. package/dist/static-check.js +37 -1
  129. package/dist/static-check.js.map +1 -1
  130. package/dist/sync.d.ts +11 -0
  131. package/dist/sync.js +65 -8
  132. package/dist/sync.js.map +1 -1
  133. package/dist/upgrade.d.ts +16 -0
  134. package/dist/upgrade.js +21 -0
  135. package/dist/upgrade.js.map +1 -1
  136. package/dist/verify.d.ts +14 -0
  137. package/dist/verify.js +19 -0
  138. package/dist/verify.js.map +1 -1
  139. package/dist/version.js +5 -0
  140. package/dist/version.js.map +1 -1
  141. package/graph-ui/app.js +321 -27
  142. package/graph-ui/approval-flow.js +165 -0
  143. package/graph-ui/business.css +985 -0
  144. package/graph-ui/business.js +549 -0
  145. package/graph-ui/flowchart-interaction.js +177 -0
  146. package/graph-ui/flowchart.js +1059 -0
  147. package/graph-ui/graph-approvals.js +79 -0
  148. package/graph-ui/graph-business.js +403 -0
  149. package/graph-ui/graph-model.js +36 -3
  150. package/graph-ui/graph-relations.js +34 -4
  151. package/graph-ui/graph-search.js +36 -0
  152. package/graph-ui/index.html +26 -12
  153. package/graph-ui/model.js +28 -0
  154. package/graph-ui/relations.js +4 -0
  155. package/graph-ui/structure.js +44 -0
  156. package/graph-ui/style.css +12 -0
  157. package/package.json +6 -4
  158. package/templates/app/.dockerignore +2 -0
  159. package/templates/app/.env.example +10 -0
  160. package/templates/app/AGENTS.md +49 -9
  161. package/templates/app/README.md +4 -1
  162. package/templates/app/_gitignore +1 -0
  163. package/templates/app/apps/api/package.json +2 -2
  164. package/templates/app/apps/api/src/main.ts +5 -1
  165. package/templates/app/apps/api/src/worker.ts +3 -0
  166. package/templates/app/apps/web/index.html +1 -0
  167. package/templates/app/apps/web/package.json +7 -3
  168. package/templates/app/apps/web/src/app/app.tsx +122 -12
  169. package/templates/app/apps/web/src/app/styles/global.css +88 -8
  170. package/templates/app/apps/web/src/app/use-session.ts +43 -0
  171. package/templates/app/apps/web/src/app/workspace-layout.tsx +58 -19
  172. package/templates/app/apps/web/src/main.tsx +5 -0
  173. package/templates/app/apps/web/src/modules/approval/hooks/use-approval-tasks.ts +11 -0
  174. package/templates/app/apps/web/src/modules/approval/index.ts +4 -0
  175. package/templates/app/apps/web/src/modules/approval/pages/approval-tasks-page.tsx +20 -1
  176. package/templates/app/apps/web/src/modules/auth/index.ts +4 -0
  177. package/templates/app/apps/web/src/modules/auth/pages/login-page.tsx +12 -0
  178. package/templates/app/apps/web/src/modules/inventory/index.ts +4 -0
  179. package/templates/app/apps/web/src/modules/inventory/pages/inventory-page.tsx +29 -1
  180. package/templates/app/apps/web/src/modules/purchase/components/approval-history.tsx +32 -0
  181. package/templates/app/apps/web/src/modules/purchase/components/order-attachments.tsx +29 -0
  182. package/templates/app/apps/web/src/modules/purchase/components/purchase-assistant-context.tsx +17 -0
  183. package/templates/app/apps/web/src/modules/purchase/components/purchase-order-form.css +6 -0
  184. package/templates/app/apps/web/src/modules/purchase/components/purchase-order-form.tsx +25 -3
  185. package/templates/app/apps/web/src/modules/purchase/index.ts +5 -0
  186. package/templates/app/apps/web/src/modules/purchase/lib/presentation.ts +20 -0
  187. package/templates/app/apps/web/src/modules/purchase/pages/purchase-intelligence.css +15 -0
  188. package/templates/app/apps/web/src/modules/purchase/pages/purchase-order-detail-page.tsx +154 -0
  189. package/templates/app/apps/web/src/modules/purchase/pages/purchase-orders-page.tsx +52 -22
  190. package/templates/app/apps/web/src/modules/workspace/index.ts +5 -0
  191. package/templates/app/apps/web/src/modules/workspace/pages/home-page.tsx +44 -0
  192. package/templates/app/apps/web/src/shared/hooks/use-command.ts +13 -0
  193. package/templates/app/apps/web/src/shared/lib/error-message.ts +9 -0
  194. package/templates/app/apps/web/src/shared/lib/navigation.ts +21 -0
  195. package/templates/app/apps/web/src/shared/ui/assistant-markdown.tsx +19 -0
  196. package/templates/app/apps/web/src/shared/ui/assistant-panel.css +134 -0
  197. package/templates/app/apps/web/src/shared/ui/assistant-panel.tsx +200 -0
  198. package/templates/app/apps/web/src/shared/ui/assistant-scope.tsx +72 -0
  199. package/templates/app/apps/web/src/shared/ui/attachment-upload.css +28 -0
  200. package/templates/app/apps/web/src/shared/ui/attachment-upload.tsx +163 -0
  201. package/templates/app/apps/web/src/shared/ui/notice.tsx +7 -0
  202. package/templates/app/apps/web/src/shared/ui/page-heading.tsx +7 -0
  203. package/templates/app/apps/web/src/shared/ui/page-state.css +47 -0
  204. package/templates/app/apps/web/src/shared/ui/page-state.tsx +76 -0
  205. package/templates/app/apps/web/src/shared/ui/require-capability.tsx +34 -0
  206. package/templates/app/apps/web/vite.config.ts +5 -0
  207. package/templates/app/cage.config.json +9 -0
  208. package/templates/app/db/migrations/0002_draft.sql +12 -0
  209. package/templates/app/db/migrations/0003_draft.sql +8 -0
  210. package/templates/app/deploy/nginx.conf +52 -0
  211. package/templates/app/docs/ai/README.md +21 -0
  212. package/templates/app/docs/ai/architecture.md +26 -0
  213. package/templates/app/docs/ai/commenting.md +203 -0
  214. package/templates/app/docs/ai/diagnostics.md +80 -0
  215. package/templates/app/docs/ai/files.md +93 -0
  216. package/templates/app/docs/ai/intelligence.md +348 -0
  217. package/templates/app/docs/ai/ontology-context.md +59 -4
  218. package/templates/app/docs/ai/primitives.md +33 -0
  219. package/templates/app/docs/ai/project-structure.md +8 -3
  220. package/templates/app/docs/ai/purchase-example.md +16 -2
  221. package/templates/app/docs/ai/recipes.md +9 -1
  222. package/templates/app/docs/ai/routing.md +86 -0
  223. package/templates/app/docs/ai/troubleshooting.md +5 -0
  224. package/templates/app/docs/ai/workflow.md +24 -11
  225. package/templates/app/docs/errors/CAGE-AI-001.md +61 -0
  226. package/templates/app/docs/errors/CAGE-APPR-001.md +7 -6
  227. package/templates/app/docs/errors/CAGE-APPR-002.md +2 -2
  228. package/templates/app/docs/errors/CAGE-APPR-003.md +1 -1
  229. package/templates/app/docs/errors/CAGE-APPR-004.md +4 -4
  230. package/templates/app/docs/errors/CAGE-APPR-005.md +1 -1
  231. package/templates/app/docs/errors/CAGE-APPR-006.md +12 -6
  232. package/templates/app/docs/errors/CAGE-APPR-007.md +3 -3
  233. package/templates/app/docs/errors/CAGE-APPR-010.md +5 -5
  234. package/templates/app/docs/errors/CAGE-AUDIT-001.md +53 -0
  235. package/templates/app/docs/errors/CAGE-AUTH-001.md +1 -1
  236. package/templates/app/docs/errors/CAGE-BIZ-001.md +51 -0
  237. package/templates/app/docs/errors/CAGE-BIZ-002.md +49 -0
  238. package/templates/app/docs/errors/CAGE-BUDGET-001.md +1 -1
  239. package/templates/app/docs/errors/CAGE-BUDGET-002.md +1 -1
  240. package/templates/app/docs/errors/CAGE-BUDGET-003.md +1 -1
  241. package/templates/app/docs/errors/CAGE-CMD-002.md +1 -1
  242. package/templates/app/docs/errors/CAGE-CMD-003.md +1 -1
  243. package/templates/app/docs/errors/CAGE-CONN-001.md +3 -3
  244. package/templates/app/docs/errors/CAGE-CONN-002.md +1 -1
  245. package/templates/app/docs/errors/CAGE-CONN-003.md +1 -1
  246. package/templates/app/docs/errors/CAGE-CONN-004.md +1 -1
  247. package/templates/app/docs/errors/CAGE-CONN-005.md +1 -1
  248. package/templates/app/docs/errors/CAGE-CONN-006.md +1 -1
  249. package/templates/app/docs/errors/CAGE-CONN-007.md +6 -6
  250. package/templates/app/docs/errors/CAGE-CONN-008.md +5 -5
  251. package/templates/app/docs/errors/CAGE-CONN-009.md +6 -6
  252. package/templates/app/docs/errors/CAGE-CONN-010.md +6 -6
  253. package/templates/app/docs/errors/CAGE-CONN-011.md +1 -1
  254. package/templates/app/docs/errors/CAGE-CONN-012.md +1 -1
  255. package/templates/app/docs/errors/CAGE-CONN-013.md +49 -0
  256. package/templates/app/docs/errors/CAGE-CONN-014.md +48 -0
  257. package/templates/app/docs/errors/CAGE-CONN-015.md +49 -0
  258. package/templates/app/docs/errors/CAGE-CREATE-001.md +10 -9
  259. package/templates/app/docs/errors/CAGE-DECL-001.md +2 -2
  260. package/templates/app/docs/errors/CAGE-DECL-002.md +7 -6
  261. package/templates/app/docs/errors/CAGE-DECL-003.md +1 -1
  262. package/templates/app/docs/errors/CAGE-DECL-004.md +1 -1
  263. package/templates/app/docs/errors/CAGE-DEP-001.md +2 -2
  264. package/templates/app/docs/errors/CAGE-DEP-002.md +1 -1
  265. package/templates/app/docs/errors/CAGE-DEP-003.md +2 -2
  266. package/templates/app/docs/errors/CAGE-DEP-004.md +1 -1
  267. package/templates/app/docs/errors/CAGE-DEP-005.md +1 -1
  268. package/templates/app/docs/errors/CAGE-DIAG-001.md +53 -0
  269. package/templates/app/docs/errors/CAGE-DLQ-001.md +2 -2
  270. package/templates/app/docs/errors/CAGE-ENV-001.md +4 -4
  271. package/templates/app/docs/errors/CAGE-ENV-002.md +51 -0
  272. package/templates/app/docs/errors/CAGE-ENV-003.md +48 -0
  273. package/templates/app/docs/errors/CAGE-EVENT-001.md +1 -1
  274. package/templates/app/docs/errors/CAGE-EVENT-002.md +7 -7
  275. package/templates/app/docs/errors/CAGE-FIELD-001.md +2 -2
  276. package/templates/app/docs/errors/CAGE-FIELD-002.md +1 -1
  277. package/templates/app/docs/errors/CAGE-FIELD-003.md +1 -1
  278. package/templates/app/docs/errors/CAGE-FIELD-004.md +1 -1
  279. package/templates/app/docs/errors/CAGE-FILE-001.md +56 -0
  280. package/templates/app/docs/errors/CAGE-GEN-001.md +2 -2
  281. package/templates/app/docs/errors/CAGE-GEN-002.md +1 -1
  282. package/templates/app/docs/errors/CAGE-GEN-003.md +53 -0
  283. package/templates/app/docs/errors/CAGE-GRANT-001.md +3 -3
  284. package/templates/app/docs/errors/CAGE-GRANT-002.md +2 -2
  285. package/templates/app/docs/errors/CAGE-GRANT-003.md +7 -6
  286. package/templates/app/docs/errors/CAGE-IMPACT-001.md +2 -2
  287. package/templates/app/docs/errors/CAGE-IMPACT-002.md +52 -0
  288. package/templates/app/docs/errors/CAGE-INIT-001.md +1 -1
  289. package/templates/app/docs/errors/CAGE-INIT-002.md +1 -1
  290. package/templates/app/docs/errors/CAGE-INIT-003.md +2 -2
  291. package/templates/app/docs/errors/CAGE-INIT-004.md +1 -1
  292. package/templates/app/docs/errors/CAGE-INIT-005.md +52 -0
  293. package/templates/app/docs/errors/CAGE-JOB-001.md +1 -1
  294. package/templates/app/docs/errors/CAGE-JOB-002.md +1 -1
  295. package/templates/app/docs/errors/CAGE-JOB-003.md +1 -1
  296. package/templates/app/docs/errors/CAGE-LINT-001.md +1 -1
  297. package/templates/app/docs/errors/CAGE-MIG-001.md +1 -1
  298. package/templates/app/docs/errors/CAGE-MIG-002.md +1 -1
  299. package/templates/app/docs/errors/CAGE-MIG-005.md +1 -1
  300. package/templates/app/docs/errors/CAGE-NAME-001.md +1 -1
  301. package/templates/app/docs/errors/CAGE-NAME-002.md +1 -1
  302. package/templates/app/docs/errors/CAGE-NAME-003.md +3 -3
  303. package/templates/app/docs/errors/CAGE-NAME-004.md +1 -1
  304. package/templates/app/docs/errors/CAGE-NAME-005.md +1 -1
  305. package/templates/app/docs/errors/CAGE-NAME-006.md +1 -1
  306. package/templates/app/docs/errors/CAGE-ORG-001.md +7 -6
  307. package/templates/app/docs/errors/CAGE-PAIR-002.md +6 -5
  308. package/templates/app/docs/errors/CAGE-QUERY-001.md +1 -1
  309. package/templates/app/docs/errors/CAGE-QUERY-002.md +2 -2
  310. package/templates/app/docs/errors/CAGE-QUERY-003.md +7 -6
  311. package/templates/app/docs/errors/CAGE-REACT-001.md +3 -3
  312. package/templates/app/docs/errors/CAGE-REACT-002.md +1 -1
  313. package/templates/app/docs/errors/CAGE-REACT-003.md +50 -0
  314. package/templates/app/docs/errors/CAGE-READS-001.md +9 -6
  315. package/templates/app/docs/errors/CAGE-REG-001.md +13 -12
  316. package/templates/app/docs/errors/CAGE-REG-002.md +2 -1
  317. package/templates/app/docs/errors/CAGE-REG-003.md +1 -1
  318. package/templates/app/docs/errors/CAGE-REL-001.md +14 -14
  319. package/templates/app/docs/errors/CAGE-ROUTE-001.md +7 -6
  320. package/templates/app/docs/errors/CAGE-SCOPE-001.md +1 -1
  321. package/templates/app/docs/errors/CAGE-SCOPE-002.md +2 -2
  322. package/templates/app/docs/errors/CAGE-SECRET-001.md +2 -2
  323. package/templates/app/docs/errors/CAGE-SECRET-002.md +1 -1
  324. package/templates/app/docs/errors/CAGE-SENS-001.md +5 -5
  325. package/templates/app/docs/errors/CAGE-SQL-001.md +1 -1
  326. package/templates/app/docs/errors/CAGE-SQL-002.md +1 -1
  327. package/templates/app/docs/errors/CAGE-STATE-001.md +3 -3
  328. package/templates/app/docs/errors/CAGE-STATE-002.md +1 -1
  329. package/templates/app/docs/errors/CAGE-STATE-003.md +1 -1
  330. package/templates/app/docs/errors/CAGE-STATE-004.md +1 -1
  331. package/templates/app/docs/errors/CAGE-STATE-005.md +3 -2
  332. package/templates/app/docs/errors/CAGE-STATE-010.md +5 -5
  333. package/templates/app/docs/errors/CAGE-STATE-011.md +5 -5
  334. package/templates/app/docs/errors/CAGE-STRUCT-001.md +1 -1
  335. package/templates/app/docs/errors/CAGE-STRUCT-002.md +1 -1
  336. package/templates/app/docs/errors/CAGE-SYNC-001.md +3 -2
  337. package/templates/app/docs/errors/CAGE-TEST-001.md +1 -1
  338. package/templates/app/docs/errors/CAGE-TEST-002.md +50 -0
  339. package/templates/app/docs/errors/CAGE-TEST-003.md +52 -0
  340. package/templates/app/docs/errors/CAGE-UNSAFE-001.md +3 -2
  341. package/templates/app/docs/errors/CAGE-UPGRADE-001.md +2 -2
  342. package/templates/app/docs/errors/CAGE-WORKER-001.md +48 -0
  343. package/templates/app/docs/errors/CAGE-WORKER-002.md +50 -0
  344. package/templates/app/docs/errors/CAGE-WORKER-003.md +48 -0
  345. package/templates/app/docs/errors/README.md +50 -0
  346. package/templates/app/package.json +3 -3
  347. package/templates/app/packages/contracts/package.json +1 -1
  348. package/templates/app/packages/contracts/src/aggregates/dept/index.ts +10 -10
  349. package/templates/app/packages/contracts/src/aggregates/inventory/index.ts +5 -5
  350. package/templates/app/packages/contracts/src/aggregates/inventory-lock/index.ts +4 -4
  351. package/templates/app/packages/contracts/src/aggregates/purchase-order/index.ts +26 -12
  352. package/templates/app/packages/contracts/src/aggregates/role/index.ts +15 -15
  353. package/templates/app/packages/contracts/src/aggregates/user/index.ts +16 -16
  354. package/templates/app/packages/contracts/src/index.ts +7 -1
  355. package/templates/app/packages/contracts/src/queries/index.ts +2 -2
  356. package/templates/app/packages/domain/package.json +5 -4
  357. package/templates/app/packages/domain/src/index.ts +4 -0
  358. package/templates/app/packages/domain/src/integrations/erp.ts +15 -0
  359. package/templates/app/packages/domain/src/integrations/intelligence.ts +20 -0
  360. package/templates/app/packages/domain/src/modules/inventory/index.ts +1 -0
  361. package/templates/app/packages/domain/src/modules/inventory/inventory.ts +12 -0
  362. package/templates/app/packages/domain/src/modules/purchase/approvals.ts +22 -1
  363. package/templates/app/packages/domain/src/modules/purchase/index.ts +1 -0
  364. package/templates/app/packages/domain/src/modules/purchase/intelligence.test.ts +62 -0
  365. package/templates/app/packages/domain/src/modules/purchase/jobs.ts +5 -0
  366. package/templates/app/packages/domain/src/modules/purchase/purchase-order.test.ts +9 -0
  367. package/templates/app/packages/domain/src/modules/purchase/purchase-order.ts +70 -0
  368. package/templates/app/packages/domain/src/modules/purchase/queries.ts +5 -0
  369. package/templates/app/packages/domain/src/workflows/purchase-inventory.ts +6 -0
  370. package/templates/app/packages/domain/src/workflows/purchase-process.ts +42 -0
  371. package/templates/blank/.dockerignore +2 -0
  372. package/templates/blank/.env.example +10 -0
  373. package/templates/blank/AGENTS.md +49 -9
  374. package/templates/blank/README.md +4 -1
  375. package/templates/blank/_gitignore +1 -0
  376. package/templates/blank/apps/api/package.json +1 -1
  377. package/templates/blank/apps/api/src/main.ts +4 -1
  378. package/templates/blank/apps/api/src/worker.ts +2 -0
  379. package/templates/blank/apps/web/index.html +1 -0
  380. package/templates/blank/apps/web/package.json +7 -3
  381. package/templates/blank/apps/web/src/app/app.tsx +80 -18
  382. package/templates/blank/apps/web/src/app/styles/global.css +90 -0
  383. package/templates/blank/apps/web/src/app/use-session.ts +43 -0
  384. package/templates/blank/apps/web/src/app/workspace-layout.tsx +30 -5
  385. package/templates/blank/apps/web/src/main.tsx +5 -0
  386. package/templates/blank/apps/web/src/modules/auth/index.ts +4 -0
  387. package/templates/blank/apps/web/src/modules/auth/pages/login-page.tsx +18 -1
  388. package/templates/blank/apps/web/src/modules/directory/index.ts +7 -0
  389. package/templates/blank/apps/web/src/modules/directory/pages/user-detail-page.tsx +78 -0
  390. package/templates/blank/apps/web/src/modules/directory/pages/users-page.tsx +93 -0
  391. package/templates/blank/apps/web/src/modules/workspace/index.ts +4 -0
  392. package/templates/blank/apps/web/src/modules/workspace/pages/home-page.tsx +17 -0
  393. package/templates/blank/apps/web/src/shared/lib/error-message.ts +11 -0
  394. package/templates/blank/apps/web/src/shared/lib/navigation.ts +21 -0
  395. package/templates/blank/apps/web/src/shared/ui/brand.tsx +10 -2
  396. package/templates/blank/apps/web/src/shared/ui/page-state.css +47 -0
  397. package/templates/blank/apps/web/src/shared/ui/page-state.tsx +76 -0
  398. package/templates/blank/apps/web/src/shared/ui/require-capability.tsx +34 -0
  399. package/templates/blank/apps/web/vite.config.ts +5 -0
  400. package/templates/blank/deploy/nginx.conf +52 -0
  401. package/templates/blank/docs/ai/README.md +21 -0
  402. package/templates/blank/docs/ai/architecture.md +26 -0
  403. package/templates/blank/docs/ai/commenting.md +203 -0
  404. package/templates/blank/docs/ai/diagnostics.md +80 -0
  405. package/templates/blank/docs/ai/files.md +93 -0
  406. package/templates/blank/docs/ai/intelligence.md +348 -0
  407. package/templates/blank/docs/ai/ontology-context.md +59 -4
  408. package/templates/blank/docs/ai/primitives.md +33 -0
  409. package/templates/blank/docs/ai/project-structure.md +8 -3
  410. package/templates/blank/docs/ai/purchase-example.md +16 -2
  411. package/templates/blank/docs/ai/recipes.md +9 -1
  412. package/templates/blank/docs/ai/routing.md +86 -0
  413. package/templates/blank/docs/ai/troubleshooting.md +5 -0
  414. package/templates/blank/docs/ai/workflow.md +24 -11
  415. package/templates/blank/docs/errors/CAGE-AI-001.md +61 -0
  416. package/templates/blank/docs/errors/CAGE-APPR-001.md +7 -6
  417. package/templates/blank/docs/errors/CAGE-APPR-002.md +2 -2
  418. package/templates/blank/docs/errors/CAGE-APPR-003.md +1 -1
  419. package/templates/blank/docs/errors/CAGE-APPR-004.md +4 -4
  420. package/templates/blank/docs/errors/CAGE-APPR-005.md +1 -1
  421. package/templates/blank/docs/errors/CAGE-APPR-006.md +12 -6
  422. package/templates/blank/docs/errors/CAGE-APPR-007.md +3 -3
  423. package/templates/blank/docs/errors/CAGE-APPR-010.md +5 -5
  424. package/templates/blank/docs/errors/CAGE-AUDIT-001.md +53 -0
  425. package/templates/blank/docs/errors/CAGE-AUTH-001.md +1 -1
  426. package/templates/blank/docs/errors/CAGE-BIZ-001.md +51 -0
  427. package/templates/blank/docs/errors/CAGE-BIZ-002.md +49 -0
  428. package/templates/blank/docs/errors/CAGE-BUDGET-001.md +1 -1
  429. package/templates/blank/docs/errors/CAGE-BUDGET-002.md +1 -1
  430. package/templates/blank/docs/errors/CAGE-BUDGET-003.md +1 -1
  431. package/templates/blank/docs/errors/CAGE-CMD-002.md +1 -1
  432. package/templates/blank/docs/errors/CAGE-CMD-003.md +1 -1
  433. package/templates/blank/docs/errors/CAGE-CONN-001.md +3 -3
  434. package/templates/blank/docs/errors/CAGE-CONN-002.md +1 -1
  435. package/templates/blank/docs/errors/CAGE-CONN-003.md +1 -1
  436. package/templates/blank/docs/errors/CAGE-CONN-004.md +1 -1
  437. package/templates/blank/docs/errors/CAGE-CONN-005.md +1 -1
  438. package/templates/blank/docs/errors/CAGE-CONN-006.md +1 -1
  439. package/templates/blank/docs/errors/CAGE-CONN-007.md +6 -6
  440. package/templates/blank/docs/errors/CAGE-CONN-008.md +5 -5
  441. package/templates/blank/docs/errors/CAGE-CONN-009.md +6 -6
  442. package/templates/blank/docs/errors/CAGE-CONN-010.md +6 -6
  443. package/templates/blank/docs/errors/CAGE-CONN-011.md +1 -1
  444. package/templates/blank/docs/errors/CAGE-CONN-012.md +1 -1
  445. package/templates/blank/docs/errors/CAGE-CONN-013.md +49 -0
  446. package/templates/blank/docs/errors/CAGE-CONN-014.md +48 -0
  447. package/templates/blank/docs/errors/CAGE-CONN-015.md +49 -0
  448. package/templates/blank/docs/errors/CAGE-CREATE-001.md +10 -9
  449. package/templates/blank/docs/errors/CAGE-DECL-001.md +2 -2
  450. package/templates/blank/docs/errors/CAGE-DECL-002.md +7 -6
  451. package/templates/blank/docs/errors/CAGE-DECL-003.md +1 -1
  452. package/templates/blank/docs/errors/CAGE-DECL-004.md +1 -1
  453. package/templates/blank/docs/errors/CAGE-DEP-001.md +2 -2
  454. package/templates/blank/docs/errors/CAGE-DEP-002.md +1 -1
  455. package/templates/blank/docs/errors/CAGE-DEP-003.md +2 -2
  456. package/templates/blank/docs/errors/CAGE-DEP-004.md +1 -1
  457. package/templates/blank/docs/errors/CAGE-DEP-005.md +1 -1
  458. package/templates/blank/docs/errors/CAGE-DIAG-001.md +53 -0
  459. package/templates/blank/docs/errors/CAGE-DLQ-001.md +2 -2
  460. package/templates/blank/docs/errors/CAGE-ENV-001.md +4 -4
  461. package/templates/blank/docs/errors/CAGE-ENV-002.md +51 -0
  462. package/templates/blank/docs/errors/CAGE-ENV-003.md +48 -0
  463. package/templates/blank/docs/errors/CAGE-EVENT-001.md +1 -1
  464. package/templates/blank/docs/errors/CAGE-EVENT-002.md +7 -7
  465. package/templates/blank/docs/errors/CAGE-FIELD-001.md +2 -2
  466. package/templates/blank/docs/errors/CAGE-FIELD-002.md +1 -1
  467. package/templates/blank/docs/errors/CAGE-FIELD-003.md +1 -1
  468. package/templates/blank/docs/errors/CAGE-FIELD-004.md +1 -1
  469. package/templates/blank/docs/errors/CAGE-FILE-001.md +56 -0
  470. package/templates/blank/docs/errors/CAGE-GEN-001.md +2 -2
  471. package/templates/blank/docs/errors/CAGE-GEN-002.md +1 -1
  472. package/templates/blank/docs/errors/CAGE-GEN-003.md +53 -0
  473. package/templates/blank/docs/errors/CAGE-GRANT-001.md +3 -3
  474. package/templates/blank/docs/errors/CAGE-GRANT-002.md +2 -2
  475. package/templates/blank/docs/errors/CAGE-GRANT-003.md +7 -6
  476. package/templates/blank/docs/errors/CAGE-IMPACT-001.md +2 -2
  477. package/templates/blank/docs/errors/CAGE-IMPACT-002.md +52 -0
  478. package/templates/blank/docs/errors/CAGE-INIT-001.md +1 -1
  479. package/templates/blank/docs/errors/CAGE-INIT-002.md +1 -1
  480. package/templates/blank/docs/errors/CAGE-INIT-003.md +2 -2
  481. package/templates/blank/docs/errors/CAGE-INIT-004.md +1 -1
  482. package/templates/blank/docs/errors/CAGE-INIT-005.md +52 -0
  483. package/templates/blank/docs/errors/CAGE-JOB-001.md +1 -1
  484. package/templates/blank/docs/errors/CAGE-JOB-002.md +1 -1
  485. package/templates/blank/docs/errors/CAGE-JOB-003.md +1 -1
  486. package/templates/blank/docs/errors/CAGE-LINT-001.md +1 -1
  487. package/templates/blank/docs/errors/CAGE-MIG-001.md +1 -1
  488. package/templates/blank/docs/errors/CAGE-MIG-002.md +1 -1
  489. package/templates/blank/docs/errors/CAGE-MIG-005.md +1 -1
  490. package/templates/blank/docs/errors/CAGE-NAME-001.md +1 -1
  491. package/templates/blank/docs/errors/CAGE-NAME-002.md +1 -1
  492. package/templates/blank/docs/errors/CAGE-NAME-003.md +3 -3
  493. package/templates/blank/docs/errors/CAGE-NAME-004.md +1 -1
  494. package/templates/blank/docs/errors/CAGE-NAME-005.md +1 -1
  495. package/templates/blank/docs/errors/CAGE-NAME-006.md +1 -1
  496. package/templates/blank/docs/errors/CAGE-ORG-001.md +7 -6
  497. package/templates/blank/docs/errors/CAGE-PAIR-002.md +6 -5
  498. package/templates/blank/docs/errors/CAGE-QUERY-001.md +1 -1
  499. package/templates/blank/docs/errors/CAGE-QUERY-002.md +2 -2
  500. package/templates/blank/docs/errors/CAGE-QUERY-003.md +7 -6
  501. package/templates/blank/docs/errors/CAGE-REACT-001.md +3 -3
  502. package/templates/blank/docs/errors/CAGE-REACT-002.md +1 -1
  503. package/templates/blank/docs/errors/CAGE-REACT-003.md +50 -0
  504. package/templates/blank/docs/errors/CAGE-READS-001.md +9 -6
  505. package/templates/blank/docs/errors/CAGE-REG-001.md +13 -12
  506. package/templates/blank/docs/errors/CAGE-REG-002.md +2 -1
  507. package/templates/blank/docs/errors/CAGE-REG-003.md +1 -1
  508. package/templates/blank/docs/errors/CAGE-REL-001.md +14 -14
  509. package/templates/blank/docs/errors/CAGE-ROUTE-001.md +7 -6
  510. package/templates/blank/docs/errors/CAGE-SCOPE-001.md +1 -1
  511. package/templates/blank/docs/errors/CAGE-SCOPE-002.md +2 -2
  512. package/templates/blank/docs/errors/CAGE-SECRET-001.md +2 -2
  513. package/templates/blank/docs/errors/CAGE-SECRET-002.md +1 -1
  514. package/templates/blank/docs/errors/CAGE-SENS-001.md +5 -5
  515. package/templates/blank/docs/errors/CAGE-SQL-001.md +1 -1
  516. package/templates/blank/docs/errors/CAGE-SQL-002.md +1 -1
  517. package/templates/blank/docs/errors/CAGE-STATE-001.md +3 -3
  518. package/templates/blank/docs/errors/CAGE-STATE-002.md +1 -1
  519. package/templates/blank/docs/errors/CAGE-STATE-003.md +1 -1
  520. package/templates/blank/docs/errors/CAGE-STATE-004.md +1 -1
  521. package/templates/blank/docs/errors/CAGE-STATE-005.md +3 -2
  522. package/templates/blank/docs/errors/CAGE-STATE-010.md +5 -5
  523. package/templates/blank/docs/errors/CAGE-STATE-011.md +5 -5
  524. package/templates/blank/docs/errors/CAGE-STRUCT-001.md +1 -1
  525. package/templates/blank/docs/errors/CAGE-STRUCT-002.md +1 -1
  526. package/templates/blank/docs/errors/CAGE-SYNC-001.md +3 -2
  527. package/templates/blank/docs/errors/CAGE-TEST-001.md +1 -1
  528. package/templates/blank/docs/errors/CAGE-TEST-002.md +50 -0
  529. package/templates/blank/docs/errors/CAGE-TEST-003.md +52 -0
  530. package/templates/blank/docs/errors/CAGE-UNSAFE-001.md +3 -2
  531. package/templates/blank/docs/errors/CAGE-UPGRADE-001.md +2 -2
  532. package/templates/blank/docs/errors/CAGE-WORKER-001.md +48 -0
  533. package/templates/blank/docs/errors/CAGE-WORKER-002.md +50 -0
  534. package/templates/blank/docs/errors/CAGE-WORKER-003.md +48 -0
  535. package/templates/blank/docs/errors/README.md +50 -0
  536. package/templates/blank/docs/examples/purchase-admin/apps/api/src/main.ts +5 -1
  537. package/templates/blank/docs/examples/purchase-admin/apps/api/src/worker.ts +3 -0
  538. package/templates/blank/docs/examples/purchase-admin/apps/web/src/app/app.tsx +122 -12
  539. package/templates/blank/docs/examples/purchase-admin/apps/web/src/app/styles/global.css +88 -8
  540. package/templates/blank/docs/examples/purchase-admin/apps/web/src/app/use-session.ts +43 -0
  541. package/templates/blank/docs/examples/purchase-admin/apps/web/src/app/workspace-layout.tsx +58 -19
  542. package/templates/blank/docs/examples/purchase-admin/apps/web/src/main.tsx +5 -0
  543. package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/approval/hooks/use-approval-tasks.ts +11 -0
  544. package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/approval/index.ts +4 -0
  545. package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/approval/pages/approval-tasks-page.tsx +20 -1
  546. package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/auth/index.ts +4 -0
  547. package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/auth/pages/login-page.tsx +12 -0
  548. package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/inventory/index.ts +4 -0
  549. package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/inventory/pages/inventory-page.tsx +29 -1
  550. package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/purchase/components/approval-history.tsx +32 -0
  551. package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/purchase/components/order-attachments.tsx +29 -0
  552. package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/purchase/components/purchase-assistant-context.tsx +17 -0
  553. package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/purchase/components/purchase-order-form.css +6 -0
  554. package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/purchase/components/purchase-order-form.tsx +25 -3
  555. package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/purchase/index.ts +5 -0
  556. package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/purchase/lib/presentation.ts +20 -0
  557. package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/purchase/pages/purchase-intelligence.css +15 -0
  558. package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/purchase/pages/purchase-order-detail-page.tsx +154 -0
  559. package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/purchase/pages/purchase-orders-page.tsx +52 -22
  560. package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/workspace/index.ts +5 -0
  561. package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/workspace/pages/home-page.tsx +44 -0
  562. package/templates/blank/docs/examples/purchase-admin/apps/web/src/shared/hooks/use-command.ts +13 -0
  563. package/templates/blank/docs/examples/purchase-admin/apps/web/src/shared/lib/error-message.ts +9 -0
  564. package/templates/blank/docs/examples/purchase-admin/apps/web/src/shared/lib/navigation.ts +21 -0
  565. package/templates/blank/docs/examples/purchase-admin/apps/web/src/shared/ui/assistant-markdown.tsx +19 -0
  566. package/templates/blank/docs/examples/purchase-admin/apps/web/src/shared/ui/assistant-panel.css +134 -0
  567. package/templates/blank/docs/examples/purchase-admin/apps/web/src/shared/ui/assistant-panel.tsx +200 -0
  568. package/templates/blank/docs/examples/purchase-admin/apps/web/src/shared/ui/assistant-scope.tsx +72 -0
  569. package/templates/blank/docs/examples/purchase-admin/apps/web/src/shared/ui/attachment-upload.css +28 -0
  570. package/templates/blank/docs/examples/purchase-admin/apps/web/src/shared/ui/attachment-upload.tsx +163 -0
  571. package/templates/blank/docs/examples/purchase-admin/apps/web/src/shared/ui/notice.tsx +7 -0
  572. package/templates/blank/docs/examples/purchase-admin/apps/web/src/shared/ui/page-heading.tsx +7 -0
  573. package/templates/blank/docs/examples/purchase-admin/apps/web/src/shared/ui/page-state.css +47 -0
  574. package/templates/blank/docs/examples/purchase-admin/apps/web/src/shared/ui/page-state.tsx +76 -0
  575. package/templates/blank/docs/examples/purchase-admin/apps/web/src/shared/ui/require-capability.tsx +34 -0
  576. package/templates/blank/docs/examples/purchase-admin/cage.config.json +9 -0
  577. package/templates/blank/docs/examples/purchase-admin/packages/contracts/src/aggregates/dept/index.ts +10 -10
  578. package/templates/blank/docs/examples/purchase-admin/packages/contracts/src/aggregates/inventory/index.ts +5 -5
  579. package/templates/blank/docs/examples/purchase-admin/packages/contracts/src/aggregates/inventory-lock/index.ts +4 -4
  580. package/templates/blank/docs/examples/purchase-admin/packages/contracts/src/aggregates/purchase-order/index.ts +26 -12
  581. package/templates/blank/docs/examples/purchase-admin/packages/contracts/src/aggregates/role/index.ts +15 -15
  582. package/templates/blank/docs/examples/purchase-admin/packages/contracts/src/aggregates/user/index.ts +16 -16
  583. package/templates/blank/docs/examples/purchase-admin/packages/contracts/src/index.ts +7 -1
  584. package/templates/blank/docs/examples/purchase-admin/packages/contracts/src/queries/index.ts +2 -2
  585. package/templates/blank/docs/examples/purchase-admin/packages/domain/src/index.ts +4 -0
  586. package/templates/blank/docs/examples/purchase-admin/packages/domain/src/integrations/erp.ts +15 -0
  587. package/templates/blank/docs/examples/purchase-admin/packages/domain/src/integrations/intelligence.ts +20 -0
  588. package/templates/blank/docs/examples/purchase-admin/packages/domain/src/modules/inventory/index.ts +1 -0
  589. package/templates/blank/docs/examples/purchase-admin/packages/domain/src/modules/inventory/inventory.ts +12 -0
  590. package/templates/blank/docs/examples/purchase-admin/packages/domain/src/modules/purchase/approvals.ts +22 -1
  591. package/templates/blank/docs/examples/purchase-admin/packages/domain/src/modules/purchase/index.ts +1 -0
  592. package/templates/blank/docs/examples/purchase-admin/packages/domain/src/modules/purchase/intelligence.test.ts +62 -0
  593. package/templates/blank/docs/examples/purchase-admin/packages/domain/src/modules/purchase/jobs.ts +5 -0
  594. package/templates/blank/docs/examples/purchase-admin/packages/domain/src/modules/purchase/purchase-order.test.ts +9 -0
  595. package/templates/blank/docs/examples/purchase-admin/packages/domain/src/modules/purchase/purchase-order.ts +70 -0
  596. package/templates/blank/docs/examples/purchase-admin/packages/domain/src/modules/purchase/queries.ts +5 -0
  597. package/templates/blank/docs/examples/purchase-admin/packages/domain/src/workflows/purchase-inventory.ts +6 -0
  598. package/templates/blank/docs/examples/purchase-admin/packages/domain/src/workflows/purchase-process.ts +42 -0
  599. package/templates/blank/package.json +3 -3
  600. package/templates/blank/packages/contracts/package.json +1 -1
  601. package/templates/blank/packages/contracts/src/aggregates/dept/index.ts +10 -10
  602. package/templates/blank/packages/contracts/src/aggregates/role/index.ts +15 -15
  603. package/templates/blank/packages/contracts/src/aggregates/user/index.ts +16 -16
  604. package/templates/blank/packages/contracts/src/index.ts +1 -1
  605. package/templates/blank/packages/contracts/src/queries/index.ts +1 -1
  606. package/templates/blank/packages/domain/package.json +5 -4
  607. package/templates/blank/packages/domain/src/index.ts +2 -1
@@ -0,0 +1,79 @@
1
+ /** 只展开阶段的主要审批。details 中作为实现依据出现的审批不代表此处执行审批;
2
+ * 取消动作也不能被画成重新走一轮审批。步骤顺序完全来自绑定的 route。 */
3
+ /**
4
+ * 为业务步骤提取主要审批的路线,不展开仅作为 details 的旁证审批。
5
+ * @param model 语义图。
6
+ * @param step 当前阅读步骤,取消和对象关联返回 undefined。
7
+ * @param labels 字段、状态与角色中文覆盖。
8
+ * @returns 审批与按顺序分组的任务;不运行条件、不选人、不检查授权。
9
+ */
10
+ export function approvalFlowForStep(model, step, labels = {}) {
11
+ if (step.label === '取消审批' || step.mode === 'association')
12
+ return undefined;
13
+ const approval = step.node.kind === 'approval'
14
+ ? step.node
15
+ : step.node.kind === 'stage'
16
+ ? model.nodes.find((n) => n.kind === 'approval' && n.key === step.node.attrs.primary)
17
+ : undefined;
18
+ if (!approval)
19
+ return undefined;
20
+ const route = model.nodes.find((n) => n.kind === 'route' &&
21
+ model.edges.some((e) => e.kind === 'binds' && e.from === n.key && e.to === approval.key));
22
+ if (!route)
23
+ return { approval, groups: [] };
24
+ const owner = String(approval.attrs.aggregate);
25
+ const fieldName = (field) => labels[`field:${owner}.${field}`] ?? '待命名字段';
26
+ const valueName = (value) => typeof value === 'number'
27
+ ? value.toLocaleString('zh-CN', { maximumFractionDigits: 20 })
28
+ : typeof value === 'boolean'
29
+ ? value
30
+ ? '是'
31
+ : '否'
32
+ : (labels[`state:${owner}.${value}`] ?? value);
33
+ const conditionName = (condition) => {
34
+ const field = fieldName(condition.field);
35
+ if (condition.op === 'eq' && condition.value === true)
36
+ return field;
37
+ const op = { gt: '大于', gte: '大于等于', lt: '小于', lte: '小于等于', eq: '为', ne: '不为', in: '属于' };
38
+ const value = Array.isArray(condition.value)
39
+ ? condition.value.map(valueName).join('、')
40
+ : valueName(condition.value);
41
+ return `${field}${op[condition.op]}${value}`;
42
+ };
43
+ let number = 0;
44
+ const task = (item) => item.kind === 'ai' ? {
45
+ number: ++number, title: item.title, actor: 'AI 审核', rule: '通过继续;退回修改结束本轮;转人工由本节点接管人处理', mode: 'any',
46
+ } : ({
47
+ number: ++number,
48
+ title: item.title,
49
+ actor: item.approver.kind === 'deptLeader'
50
+ ? `${fieldName(item.approver.field)}的负责人`
51
+ : item.approver.kind === 'role'
52
+ ? (labels[`role:${item.approver.code}`] ?? '待命名审批角色')
53
+ : `指定用户 ${item.approver.userId}`,
54
+ rule: item.mode === 'all' ? '所有审批人同意后继续' : '任一审批人同意即可继续',
55
+ mode: item.mode,
56
+ });
57
+ return {
58
+ approval,
59
+ route,
60
+ groups: route.attrs.nodes.map((item) => item.kind !== 'when'
61
+ ? { tasks: [task(item)] }
62
+ : { condition: conditionName(item.cond), tasks: item.steps.map(task) }),
63
+ };
64
+ }
65
+ /** 从真实驳回结论绑定辨认出口,不依赖阶段标题里是否含有“驳回”二字。 */
66
+ /**
67
+ * 用真实 onRejected 绑定辨认驳回出口,不从标题猜测。
68
+ * @param flow 当前审批投影。
69
+ * @param target 待检查的命令或业务阶段。
70
+ * @returns 目标主要命令是否等于驳回结论,纯计算无副作用。
71
+ */
72
+ export function isApprovalRejection(flow, target) {
73
+ const command = target.node.kind === 'command'
74
+ ? target.node.key
75
+ : target.node.kind === 'stage'
76
+ ? target.node.attrs.primary
77
+ : undefined;
78
+ return command === `command:${flow.approval.attrs.onRejected}`;
79
+ }
@@ -0,0 +1,403 @@
1
+ /**
2
+ * 把语义图整理为业务板块、显式流程、对象生命周期和操作触发树。
3
+ * 供图谱浏览器使用,优先采用 businessProcess 声明,旧项目按模块或聚合归属组织。
4
+ * 所有投影只辅助阅读,不调度操作;状态迁移、读取依赖、声明调用能力与真实自动触发分别处理。
5
+ */
6
+ import { nodeLabel } from './graph-model.js';
7
+ /**
8
+ * 识别 User、Role、Dept 及其成员的系统组织前缀,供默认隐藏开关使用;不表示权限检查。
9
+ */
10
+ export const isSystemObject = (node) => /^(User|Role|Dept)(\.|$)/.test(node.id);
11
+ /**
12
+ * 只对有明确标识归属的节点寻找聚合,取最长匹配前缀。
13
+ * @param model 语义图。
14
+ * @param node 待定位节点。
15
+ * @returns 聚合或 undefined;事件/查询等任意名称不会按前缀误归属。
16
+ */
17
+ export function businessOwner(model, node) {
18
+ if (node.kind === 'aggregate')
19
+ return node;
20
+ // 查询、事件等可任意命名,不能凭同名前缀把它们归入某个对象。
21
+ if (!['command', 'state', 'field', 'capability', 'entry'].includes(node.kind))
22
+ return undefined;
23
+ return model.nodes
24
+ .filter((n) => n.kind === 'aggregate' && node.id.startsWith(`${n.id}.`))
25
+ .sort((a, b) => b.id.length - a.id.length)[0];
26
+ }
27
+ /** 板块只组织展示,不新增本体关系。模块目录提供归属,旧的平铺项目按对象分组;
28
+ * 查询与任务允许出现在多个相关板块,但不会沿读取关系扩散出一整张业务图。 */
29
+ /**
30
+ * 按显式业务区、模块路径或聚合整理板块,并附声明相关查询与任务。
31
+ * @param model 语义图,不被修改。
32
+ * @param labels 显示覆盖。
33
+ * @param showSystem 是否保留系统组织对象。
34
+ * @returns 稳定排序的板块;同一操作可出现在多个相关板块,不表示重复执行。
35
+ */
36
+ export function businessBlocks(model, labels = {}, showSystem = false) {
37
+ const blocks = new Map();
38
+ const owners = new Map();
39
+ const commands = model.nodes.filter((n) => n.kind === 'command');
40
+ const count = (n) => commands.filter((c) => businessOwner(model, c)?.key === n.key).length;
41
+ const objects = model.nodes
42
+ .filter((n) => n.kind === 'aggregate')
43
+ .sort((a, b) => count(b) - count(a) || a.id.localeCompare(b.id));
44
+ const processes = model.nodes.filter((n) => n.kind === 'process');
45
+ for (const node of objects) {
46
+ const system = isSystemObject(node);
47
+ if (system && !showSystem)
48
+ continue;
49
+ const module = node.file?.replaceAll('\\', '/').match(/(?:^|\/)modules\/([^/]+)\//)?.[1];
50
+ const process = processes.find((p) => p.attrs.subject === node.id);
51
+ const area = process?.attrs.area;
52
+ const id = system
53
+ ? 'system:organization'
54
+ : area
55
+ ? `module:${area.id}`
56
+ : module
57
+ ? `module:${module}`
58
+ : node.key;
59
+ let block = blocks.get(id);
60
+ if (!block) {
61
+ block = {
62
+ id,
63
+ title: system ? '组织与权限' : (area?.title ?? businessLabel(node, labels)),
64
+ description: area?.description,
65
+ objects: [],
66
+ flows: [],
67
+ processes: [],
68
+ system,
69
+ };
70
+ blocks.set(id, block);
71
+ }
72
+ block.objects.push(node);
73
+ owners.set(node.id, block);
74
+ }
75
+ for (const block of blocks.values()) {
76
+ block.title = labels[block.id] || block.title;
77
+ }
78
+ for (const process of processes) {
79
+ const subject = model.nodes.find((n) => n.kind === 'aggregate' && n.id === process.attrs.subject);
80
+ if (subject && isSystemObject(subject) && !showSystem)
81
+ continue;
82
+ const area = process.attrs.area;
83
+ const id = `module:${area.id}`;
84
+ let block = blocks.get(id);
85
+ if (!block) {
86
+ block = {
87
+ id,
88
+ title: labels[id] || area.title,
89
+ description: area.description,
90
+ objects: [],
91
+ flows: [],
92
+ processes: [],
93
+ system: false,
94
+ };
95
+ blocks.set(id, block);
96
+ }
97
+ block.title = labels[id] || area.title;
98
+ block.description = area.description;
99
+ block.processes.push(process);
100
+ }
101
+ const byKey = new Map(model.nodes.map((n) => [n.key, n]));
102
+ for (const node of model.nodes) {
103
+ if (!['command', 'query', 'job', 'inbound'].includes(node.kind))
104
+ continue;
105
+ const related = new Set();
106
+ const addOwner = (target) => {
107
+ const owner = target && businessOwner(model, target);
108
+ const block = owner && owners.get(owner.id);
109
+ if (block)
110
+ related.add(block);
111
+ };
112
+ addOwner(node);
113
+ for (const edge of model.edges) {
114
+ if (edge.from !== node.key || !['queries', 'calls', 'targets'].includes(edge.kind))
115
+ continue;
116
+ addOwner(byKey.get(edge.to));
117
+ }
118
+ // 组织模型在开关关闭时不借“其他业务”重新出现。
119
+ if (!related.size && businessOwner(model, node) && !showSystem)
120
+ continue;
121
+ if (!related.size) {
122
+ const belongsToHiddenSystem = model.edges.some((e) => e.from === node.key &&
123
+ ['queries', 'calls', 'targets'].includes(e.kind) &&
124
+ isSystemObject(byKey.get(e.to)));
125
+ if (belongsToHiddenSystem && !showSystem)
126
+ continue;
127
+ const id = 'other:collaboration';
128
+ if (!blocks.has(id))
129
+ blocks.set(id, {
130
+ id,
131
+ title: '其他业务协作',
132
+ objects: [],
133
+ flows: [],
134
+ processes: [],
135
+ system: false,
136
+ });
137
+ related.add(blocks.get(id));
138
+ }
139
+ for (const block of related)
140
+ block.flows.push(node);
141
+ }
142
+ for (const block of blocks.values())
143
+ block.flows.sort((a, b) => {
144
+ const rank = (n) => n.kind !== 'command'
145
+ ? 3
146
+ : n.attrs.from === 'new'
147
+ ? 0
148
+ : n.attrs.invokedBy?.includes('user')
149
+ ? 1
150
+ : 2;
151
+ return (rank(a) - rank(b) ||
152
+ (a.file ?? '').localeCompare(b.file ?? '') ||
153
+ (a.line ?? 0) - (b.line ?? 0) ||
154
+ a.id.localeCompare(b.id));
155
+ });
156
+ return [...blocks.values()].sort((a, b) => Number(a.system) - Number(b.system) || b.flows.length - a.flows.length || a.id.localeCompare(b.id));
157
+ }
158
+ /** 业务页面缺少文案时明确提示补充,不用程序标识替代业务名称。完整本体仍保留标识。 */
159
+ /**
160
+ * 优先使用中文覆盖与声明文案,缺失时明确显示待命名。
161
+ * @param node 展示节点。
162
+ * @param labels 中文标签映射。
163
+ * @returns 阅读标题,不改实际标识。
164
+ */
165
+ export function businessLabel(node, labels = {}) {
166
+ if (node.kind === 'entry')
167
+ return '新建业务';
168
+ if (labels[node.key] || node.attrs.title || node.attrs.description)
169
+ return nodeLabel(node, labels);
170
+ return `待命名${{ aggregate: '业务对象', command: '操作', state: '状态', process: '流程', stage: '阶段', event: '业务结果', reaction: '自动处理', approval: '审批', inbound: '外部通知', outbound: '外部同步', query: '查询', job: '定时处理' }[node.kind] ?? '声明'}`;
171
+ }
172
+ /**
173
+ * 优先返回板块显式流程并保持源码顺序;没有流程规格时返回有状态迁移的对象供生命周期阅读。
174
+ */
175
+ export const businessJourneys = (block, model) => block.processes.length
176
+ ? [...block.processes].sort((a, b) => (a.file ?? '').localeCompare(b.file ?? '') ||
177
+ (a.line ?? 0) - (b.line ?? 0) ||
178
+ a.id.localeCompare(b.id))
179
+ : block.objects.filter((object) => model.edges.some((e) => e.kind === 'transition' && e.to.startsWith(`state:${object.id}.`)));
180
+ /** 显式流程的阶段是唯一身份。汇合与循环回连已有阶段,避免复制成多份业务。 */
181
+ /**
182
+ * 把显式流程阶段组织成阅读树,汇合/循环只保留回连标记。
183
+ * @param model 语义图。
184
+ * @param process 已声明业务流程。
185
+ * @param options branches 控制次要分支,related 控制对象关联。
186
+ * @returns 流程入口树;关联不充当下一步,遍历不执行命令。
187
+ */
188
+ export function traceBusinessProcess(model, process, options) {
189
+ const nodes = new Map(model.nodes.map((n) => [n.key, n]));
190
+ const seen = new Set();
191
+ const walk = (key, incoming) => {
192
+ const node = nodes.get(key);
193
+ const repeated = seen.has(key);
194
+ const mode = incoming?.mode;
195
+ const step = {
196
+ id: repeated ? `${incoming.from}>${key}` : key,
197
+ node,
198
+ via: model.edges.filter((e) => e.from === key && e.kind === 'implements').map((e) => nodes.get(e.to)),
199
+ label: incoming
200
+ ? `${{ manual: '人工', automatic: '自动', wait: '等待' }[mode]} · ${incoming.label}`
201
+ : '流程起点',
202
+ note: mode === 'automatic'
203
+ ? '由实际声明触发;执行条件、事务与映射以实现为准。'
204
+ : mode === 'wait'
205
+ ? '等待外部通知或定时处理,流程图本身不发起调用。'
206
+ : '由参与人发起,仍需通过权限和业务规则检查。',
207
+ mode,
208
+ branch: incoming?.branch,
209
+ children: [],
210
+ repeated,
211
+ boundary: false,
212
+ };
213
+ if (repeated)
214
+ return step;
215
+ seen.add(key);
216
+ step.children = model.edges
217
+ .filter((e) => e.kind === 'follows' && e.from === key && (options.branches || !e.branch))
218
+ .map((e) => walk(e.to, e));
219
+ if (options.related)
220
+ for (const relation of model.edges.filter((e) => e.kind === 'relates' && e.from === key)) {
221
+ const object = nodes.get(relation.to);
222
+ step.children.push({
223
+ id: `${key}>${object.key}`,
224
+ node: object,
225
+ via: relation.via && nodes.has(relation.via) ? [nodes.get(relation.via)] : [],
226
+ label: relation.label ?? '关联对象',
227
+ note: '业务对象关联;不表示下一步操作或额外触发。',
228
+ mode: 'association',
229
+ children: [],
230
+ repeated: false,
231
+ boundary: false,
232
+ });
233
+ }
234
+ return step;
235
+ };
236
+ return walk(`stage:${process.attrs.entry}`);
237
+ }
238
+ /** 旧项目按实际状态迁移阅读;只表达允许的操作,绝不推断成自动调用链。 */
239
+ /**
240
+ * 对没有业务流程规格的对象沿允许状态迁移生成阅读树。
241
+ * @param model 语义图。
242
+ * @param object 目标聚合。
243
+ * @param labels 中文文案。
244
+ * @returns 从新建入口或初始状态开始的树;只表示允许操作,不推断自动触发。
245
+ */
246
+ export function traceBusinessLifecycle(model, object, labels) {
247
+ const nodes = new Map(model.nodes.map((n) => [n.key, n]));
248
+ const seen = new Set();
249
+ const entry = nodes.get(`entry:${object.id}.[new]`) ??
250
+ model.nodes.find((n) => n.kind === 'state' && n.id.startsWith(`${object.id}.`) && n.attrs.initial);
251
+ const walk = (node, via = [], parent = '') => {
252
+ const repeated = seen.has(node.key);
253
+ const action = via[0];
254
+ const actor = (action?.attrs.invokedBy ?? []);
255
+ const label = action
256
+ ? `${actor.includes('user') ? '人工' : actor.includes('external') ? '外部通知' : actor.includes('process') ? '审批结论' : '业务操作'} · ${businessLabel(action, labels)}`
257
+ : '生命周期起点';
258
+ const step = {
259
+ id: repeated ? `${parent}>${node.key}` : node.key,
260
+ node,
261
+ via,
262
+ label,
263
+ note: '根据已声明状态迁移整理,表示允许执行的操作;条件与触发方式请查看实现。',
264
+ mode: 'lifecycle',
265
+ repeated,
266
+ boundary: false,
267
+ children: [],
268
+ };
269
+ if (repeated)
270
+ return step;
271
+ seen.add(node.key);
272
+ step.children = model.edges
273
+ .filter((e) => e.kind === 'transition' && e.from === node.key)
274
+ .map((e) => walk(nodes.get(e.to), [nodes.get(`command:${e.via}`)], node.key));
275
+ return step;
276
+ };
277
+ return walk(entry);
278
+ }
279
+ /** 只沿会驱动后续操作的声明追踪。状态可达性和 reads 均不当作调用顺序。
280
+ * 事件与反应收进步骤的实现依据,但保留事务、条件映射、取消及审批结论语义。 */
281
+ /**
282
+ * 沿事件、反应、审批结论及外部回写构建有界操作树。
283
+ * @param model 语义图。
284
+ * @param root 完整节点 key。
285
+ * @param maxDepth 默认 6,另以累计步骤计数限制膨胀。
286
+ * @returns 入口树或 undefined;取消审批停止展开,循环/上限通过 repeated/boundary 显示。
287
+ */
288
+ export function traceBusinessFlow(model, root, maxDepth = 6) {
289
+ const nodes = new Map(model.nodes.map((n) => [n.key, n]));
290
+ const outgoing = (key) => model.edges.filter((e) => e.from === key && !e.derived);
291
+ const next = (node) => {
292
+ const result = [];
293
+ const add = (key, via, label, note = '') => {
294
+ const target = nodes.get(key);
295
+ if (target)
296
+ result.push({ node: target, via, label, note });
297
+ };
298
+ for (const edge of outgoing(node.key)) {
299
+ const target = nodes.get(edge.to);
300
+ if (node.kind === 'command' && edge.kind === 'emits') {
301
+ const effects = outgoing(target.key).filter((e) => ['triggers', 'cancels'].includes(e.kind));
302
+ if (!effects.length)
303
+ add(target.key, [], '成功后产生的结果');
304
+ for (const effect of effects) {
305
+ const handler = nodes.get(effect.to);
306
+ if (effect.kind === 'cancels') {
307
+ add(handler.key, [target], '取消审批', '该分支终止审批,不继续执行审批结论。');
308
+ }
309
+ else if (handler.kind === 'reaction') {
310
+ for (const call of outgoing(handler.key).filter((e) => e.kind === 'targets')) {
311
+ add(call.to, [target, handler], handler.attrs.tx === 'same' ? '成功后 · 同一事务' : '成功后 · 异步处理', '是否调用及输入由映射函数决定;返回 null 时跳过。');
312
+ }
313
+ }
314
+ else {
315
+ add(handler.key, [target], handler.kind === 'approval' ? '成功后 · 异步启动审批' : '成功后 · 异步外部同步');
316
+ }
317
+ }
318
+ }
319
+ else if (node.kind === 'approval' && edge.kind === 'binds' && target.kind === 'command') {
320
+ add(target.key, [], node.attrs.onApproved === target.id ? '审批通过时' : '审批驳回时', '审批结论仍须通过目标业务操作的权限与规则检查。');
321
+ }
322
+ else if (node.kind === 'outbound' && edge.kind === 'targets' && target.kind === 'command') {
323
+ add(target.key, [], '外部调用成功后回写');
324
+ }
325
+ else if (node.kind === 'inbound' && edge.kind === 'targets' && target.kind === 'command') {
326
+ add(target.key, [], '验签与映射后');
327
+ }
328
+ else if (node.kind === 'job' && edge.kind === 'calls') {
329
+ add(target.key, [], '任务声明可调用', '实际调用条件和次数由任务实现决定。');
330
+ }
331
+ else if (node.kind === 'reaction' && edge.kind === 'targets') {
332
+ add(target.key, [], node.attrs.tx === 'same' ? '同一事务' : '异步处理', '是否调用由映射函数决定。');
333
+ }
334
+ else if (node.kind === 'event' && edge.kind === 'triggers') {
335
+ add(target.key, [], '结果触发的后续处理');
336
+ }
337
+ }
338
+ return result;
339
+ };
340
+ let count = 0;
341
+ const walk = (item, path, seen, depth) => {
342
+ const nextPath = [...path, ...item.via.map((n) => n.key), item.node.key];
343
+ const id = nextPath.join('>');
344
+ const repeated = seen.has(item.node.key);
345
+ // 取消审批是终止动作,不能再展示“通过/驳回”的后续调用。
346
+ const children = item.label === '取消审批' ? [] : next(item.node);
347
+ const boundary = !repeated && children.length > 0 && (depth >= maxDepth || count >= 60);
348
+ count++;
349
+ const step = { ...item, id, children: [], repeated, boundary };
350
+ if (!repeated && !boundary) {
351
+ const visited = new Set([...seen, item.node.key]);
352
+ step.children = children.map((child) => walk(child, nextPath, visited, depth + 1));
353
+ }
354
+ return step;
355
+ };
356
+ const node = nodes.get(root);
357
+ return node && walk({ node, via: [], label: '业务入口', note: '' }, [], new Set(), 0);
358
+ }
359
+ /**
360
+ * 按先序把已经有界的阅读树展开成列表。
361
+ * @param root 由追踪函数生成的根。
362
+ * @returns 包含重复引用占位节点的数组,不去重、不执行关系。
363
+ */
364
+ export function flattenBusinessSteps(root) {
365
+ return [root, ...root.children.flatMap(flattenBusinessSteps)];
366
+ }
367
+ /**
368
+ * 把命令 from/to 转成中文状态摘要。
369
+ * @param node 非命令返回空字符串。
370
+ * @param labels 状态名称覆盖。
371
+ * @returns 允许起始状态与目标,to=null 描述保持当前状态。
372
+ */
373
+ export function businessStateSummary(node, labels) {
374
+ if (node.kind !== 'command')
375
+ return '';
376
+ const owner = node.id.slice(0, node.id.lastIndexOf('.'));
377
+ const name = (s) => (s === 'new' ? '新建' : (labels[`state:${owner}.${s}`] ?? '待命名状态'));
378
+ const from = (Array.isArray(node.attrs.from) ? node.attrs.from : [node.attrs.from]);
379
+ return `${from.filter(Boolean).map(name).join(' / ')} → ${node.attrs.to == null ? '保持当前状态' : name(String(node.attrs.to))}`;
380
+ }
381
+ /**
382
+ * 把已知约束名翻译为业务提示,并解析额度与职责分离字段。
383
+ * @param name 内核约束标识。
384
+ * @param owner 所属聚合 ID。
385
+ * @param labels 字段中文标签。
386
+ * @returns 展示文案,未知约束保留原名,不求值或授予能力。
387
+ */
388
+ export function businessConstraintLabel(name, owner, labels) {
389
+ const field = (id) => labels[`field:${owner}.${id}`] ?? id;
390
+ const ceiling = /^ceiling\((.+)\)$/.exec(name);
391
+ if (ceiling)
392
+ return `${field(ceiling[1])}不超过审批额度`;
393
+ const sod = /^sod\.notSameAs\((.+)\)$/.exec(name);
394
+ if (sod)
395
+ return `审批人与${field(sod[1])}不同`;
396
+ return ({
397
+ stepUp: '需要二次认证',
398
+ knownCapability: '能力点必须已声明',
399
+ noEscalation: '不能授予超出自身的能力',
400
+ noWiderScope: '不能授予超出自身的数据范围',
401
+ noEscalationViaRoles: '不能通过分配角色提权',
402
+ }[name] ?? name);
403
+ }
@@ -1,3 +1,6 @@
1
+ /**
2
+ * 节点类型的固定中文词汇,CLI 报告和浏览器共用;只影响呈现,不改变本体 kind。
3
+ */
1
4
  export const kindLabels = {
2
5
  aggregate: '业务对象',
3
6
  command: '命令',
@@ -14,7 +17,12 @@ export const kindLabels = {
14
17
  state: '状态',
15
18
  capability: '能力点',
16
19
  entry: '新建入口',
20
+ process: '业务流程',
21
+ stage: '业务阶段',
17
22
  };
23
+ /**
24
+ * 本体及展示归属边的中文短名,未知扩展关系由具体解释层处理。
25
+ */
18
26
  export const edgeLabels = {
19
27
  transition: '状态迁移',
20
28
  reads: '读取',
@@ -29,14 +37,29 @@ export const edgeLabels = {
29
37
  binds: '审批绑定',
30
38
  field: '声明字段',
31
39
  ownership: '归属(标识推导)',
40
+ contains: '包含业务阶段',
41
+ implements: '阶段实现',
42
+ follows: '业务下一步',
43
+ relates: '关联业务对象',
32
44
  };
33
45
  /** graph.labels 的显示覆盖不参与本体哈希;声明内的 description 仍由抽取器
34
46
  * 按该声明的含义处理。中文展示不会替换路由、权限或数据表使用的稳定标识。 */
47
+ /**
48
+ * 按显示覆盖、标题、description、原 ID 的顺序选择文案。
49
+ * @param node 展示节点,虚拟 entry 固定使用新建入口文案。
50
+ * @param labels 仅用于显示的中文映射。
51
+ * @returns 展示名称,不修改节点或业务标识。
52
+ */
35
53
  export function nodeLabel(node, labels = {}) {
36
54
  if (node.kind === 'entry')
37
55
  return '新建入口';
38
- return labels[node.key] || String(node.attrs.description || node.id);
56
+ return labels[node.key] || String(node.attrs.title || node.attrs.description || node.id);
39
57
  }
58
+ /**
59
+ * 将本体 ID 按关系端点类型转成唯一 key,并补新建入口及归属边。
60
+ * @param graph 原始本体,不会被修改。
61
+ * @returns 新展示模型;无法匹配两端的关系不输出,派生 ownership 不代表调用。
62
+ */
40
63
  export function projectGraph(graph) {
41
64
  const nodes = graph.nodes.map((n) => ({ ...n, key: `${n.kind}:${n.id}` }));
42
65
  const edges = [];
@@ -61,11 +84,15 @@ export function projectGraph(graph) {
61
84
  ['approval', 'command'],
62
85
  ],
63
86
  uses: [['command', 'event', 'query', 'approval', 'route', 'outbound'], ['field']],
87
+ contains: [['process'], ['stage']],
88
+ implements: [['stage'], ['command', 'event', 'reaction', 'query', 'approval', 'job', 'inbound', 'outbound']],
89
+ follows: [['stage'], ['stage']],
90
+ relates: [['process', 'stage'], ['aggregate']],
64
91
  };
65
92
  for (const edge of graph.edges) {
66
93
  const [fromKinds, toKinds] = endpoints[edge.kind];
67
- let from = find(edge.from, fromKinds);
68
- const to = find(edge.to, toKinds);
94
+ let from = find(edge.from, edge.fromKind ? [edge.fromKind] : fromKinds);
95
+ const to = find(edge.to, edge.toKind ? [edge.toKind] : toKinds);
69
96
  if (!from && edge.kind === 'transition' && edge.from.endsWith('.[new]')) {
70
97
  from = { key: `entry:${edge.from}`, id: edge.from, kind: 'entry', hash: '', attrs: {} };
71
98
  nodes.push(from);
@@ -88,6 +115,12 @@ export function projectGraph(graph) {
88
115
  }
89
116
  return { nodes, edges };
90
117
  }
118
+ /**
119
+ * 计算节点的一跳无向邻域,供画布高亮。
120
+ * @param model 展示图。
121
+ * @param key 完整节点身份。
122
+ * @returns 包含自身和直接邻居的集合;不继续传递,不区分执行方向。
123
+ */
91
124
  export function neighborhood(model, key) {
92
125
  const connected = new Set([key]);
93
126
  for (const edge of model.edges) {
@@ -1,4 +1,16 @@
1
+ /**
2
+ * 将每种本体连线翻译为中文原因、用途、方向与证据,供 CLI 和浏览器详情复用。
3
+ * 解释基于投影端点及已抽取属性,分别标注显式声明、规则推导与文本匹配。
4
+ * 不求值 handler、映射或审批条件;存在连线只代表对应声明关系,不证明某次操作已发生。
5
+ */
1
6
  import { kindLabels, nodeLabel } from './graph-model.js';
7
+ /**
8
+ * 按真实关系种类解释连线,补充声明来源和证据强度。
9
+ * @param edge 已投影且两端有效的边。
10
+ * @param model 展示图,提供端点和辅助声明。
11
+ * @param labels 中文名称覆盖。
12
+ * @returns 中文解释;只读元数据,不执行审批、映射、查询或业务命令。
13
+ */
2
14
  export function explainEdge(edge, model, labels = {}) {
3
15
  const from = model.nodes.find((n) => n.key === edge.from);
4
16
  const to = model.nodes.find((n) => n.key === edge.to);
@@ -20,6 +32,16 @@ export function explainEdge(edge, model, labels = {}) {
20
32
  direction: `“${a}” → “${b}”:${label}。`,
21
33
  });
22
34
  switch (edge.kind) {
35
+ case 'contains':
36
+ return result('包含业务阶段', '业务流程', `“${a}”声明了“${b}”阶段。`, '组织完整业务过程;阶段本身不执行命令。', [`${from.id}.stages → ${to.id}`]);
37
+ case 'implements':
38
+ return result('阶段实现', '实现依据', `“${a}”引用“${b}”作为实现。`, '从业务阶段追到实际声明;引用顺序不表示执行顺序。', [`${from.id}.action / details → ${to.id}`]);
39
+ case 'follows': {
40
+ const mode = { manual: '人工下一步', automatic: '自动触发', wait: '等待后继续' }[edge.mode ?? 'manual'];
41
+ return result(edge.label ?? mode, mode, `“${a}” → “${b}”:${edge.label}(${mode})。`, edge.mode === 'automatic' ? '检查实际触发链是否存在;映射条件、事务和权限仍以引用的实现为准。' : '这是业务规格中的后续步骤,不会自动调度目标操作。', [`${from.attrs.process}.transitions`, `mode: ${edge.mode}`]);
42
+ }
43
+ case 'relates':
44
+ return result(edge.label ?? '关联业务对象', '业务关联', `“${a}”关联“${b}”:${edge.label}。`, '展示这个环节涉及的业务对象;数据引用、读取和写入由对应实现决定。', [edge.via ?? `${from.id}.subject`]);
23
45
  case 'field':
24
46
  return result(to.attrs.virtual ? '声明关联字段' : '包含字段', '数据结构', `“${a}”的字段声明中包含“${b}”。`, to.attrs.virtual
25
47
  ? '这是 fields 中的集合关系,不占主表列;点击它指向目标的连线,查看关系基数、存储和读写方式。'
@@ -59,8 +81,8 @@ export function explainEdge(edge, model, labels = {}) {
59
81
  const nodes = from.attrs.nodes;
60
82
  const conditions = nodes.filter((n) => n.kind === 'when' && n.cond.field === field);
61
83
  const steps = nodes
62
- .flatMap((n) => (n.kind === 'step' ? [n] : n.steps))
63
- .filter((s) => s.approver.kind === 'deptLeader' && s.approver.field === field);
84
+ .flatMap((n) => (n.kind !== 'when' ? [n] : n.steps))
85
+ .filter((s) => (s.approver ?? s.handoff)?.kind === 'deptLeader' && (s.approver ?? s.handoff)?.field === field);
64
86
  const operators = {
65
87
  gt: '大于',
66
88
  gte: '大于等于',
@@ -122,17 +144,25 @@ export function explainEdge(edge, model, labels = {}) {
122
144
  edge.acquires ? `获取“${resource(edge.acquires)}”` : '',
123
145
  edge.releases ? `释放“${resource(edge.releases)}”` : '',
124
146
  ].filter(Boolean);
125
- return result('调用业务命令', '反应调用', `“${a}”把事件映射后交给“${b}”处理。`, `将后续业务操作交给受约束的命令执行。${resources.length ? `此声明还标记${resources.join('、')},用于检查资源获取与释放是否成对。` : ''}`, [`${from.id}.command → ${to.id}`, ...resources]);
147
+ const collection = from.attrs.collection;
148
+ const execution = [
149
+ from.attrs.tx === 'same' ? '同一事务,任意一项失败共同回滚' : '异步投递',
150
+ from.attrs.order !== null && from.attrs.order !== undefined ? `步骤顺序 ${from.attrs.order}` : '',
151
+ collection ? `最多 ${collection.maxItems} 项,按 ${collection.orderBy} 排序,串行调用` : '',
152
+ ].filter(Boolean);
153
+ return result('调用业务命令', '反应调用', `“${a}”把事件映射后交给“${b}”处理。`, `将后续业务操作交给受约束的命令执行。${execution.join(';')}。${resources.length ? `此声明还标记${resources.join('、')},用于检查资源获取与释放是否成对。` : ''}`, [`${from.id}.command → ${to.id}`, ...execution, ...resources]);
126
154
  }
127
155
  case 'binds': {
128
156
  if (from.kind === 'route')
129
157
  return result('定义审批步骤', '流程配置', `“${a}”与“${b}”使用同一流程标识 ${from.attrs.process}。`, '这份路由定义流程里的审批步骤、选人方式和条件分支,供代码内审批引擎使用。', [`process: ${from.attrs.process}`]);
130
158
  const approved = from.attrs.onApproved === to.id;
131
159
  const rejected = from.attrs.onRejected === to.id;
132
- const outcome = approved && rejected ? '结束' : approved ? '通过' : '驳回';
160
+ const returned = from.attrs.onReturned === to.id;
161
+ const outcome = approved && rejected ? '结束' : approved ? '通过' : returned ? '退回修改' : '驳回';
133
162
  return result(`${outcome}后执行`, '审批结论回写', `“${a}”${outcome}时绑定执行“${b}”。`, '将审批结论交给业务命令推进单据状态,并统一执行权限和业务约束。', [
134
163
  approved ? `${from.id}.onApproved → ${to.id}` : '',
135
164
  rejected ? `${from.id}.onRejected → ${to.id}` : '',
165
+ returned ? `${from.id}.onReturned → ${to.id}` : '',
136
166
  ].filter(Boolean));
137
167
  }
138
168
  case 'calls':