@transitrix/diagrams 1.0.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 (541) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +47 -0
  3. package/dist/__tests__/edge-path.test.d.ts +1 -0
  4. package/dist/__tests__/edge-path.test.js +39 -0
  5. package/dist/__tests__/scope.test.d.ts +1 -0
  6. package/dist/__tests__/scope.test.js +23 -0
  7. package/dist/__tests__/yaml-normalize.test.d.ts +1 -0
  8. package/dist/__tests__/yaml-normalize.test.js +148 -0
  9. package/dist/activities/__tests__/cpm.test.d.ts +1 -0
  10. package/dist/activities/__tests__/cpm.test.js +146 -0
  11. package/dist/activities/__tests__/gantt.test.d.ts +1 -0
  12. package/dist/activities/__tests__/gantt.test.js +224 -0
  13. package/dist/activities/__tests__/layout.test.d.ts +1 -0
  14. package/dist/activities/__tests__/layout.test.js +98 -0
  15. package/dist/activities/__tests__/validate.test.d.ts +1 -0
  16. package/dist/activities/__tests__/validate.test.js +363 -0
  17. package/dist/activities/cpm.d.ts +2 -0
  18. package/dist/activities/cpm.js +86 -0
  19. package/dist/activities/gantt.d.ts +6 -0
  20. package/dist/activities/gantt.js +250 -0
  21. package/dist/activities/index.d.ts +5 -0
  22. package/dist/activities/index.js +4 -0
  23. package/dist/activities/layout.d.ts +2 -0
  24. package/dist/activities/layout.js +103 -0
  25. package/dist/activities/types.d.ts +125 -0
  26. package/dist/activities/types.js +1 -0
  27. package/dist/activities/validate.d.ts +2 -0
  28. package/dist/activities/validate.js +326 -0
  29. package/dist/activity-card/__tests__/example.test.d.ts +1 -0
  30. package/dist/activity-card/__tests__/example.test.js +56 -0
  31. package/dist/activity-card/__tests__/layout.test.d.ts +1 -0
  32. package/dist/activity-card/__tests__/layout.test.js +57 -0
  33. package/dist/activity-card/__tests__/resolver.test.d.ts +1 -0
  34. package/dist/activity-card/__tests__/resolver.test.js +146 -0
  35. package/dist/activity-card/__tests__/validate.test.d.ts +1 -0
  36. package/dist/activity-card/__tests__/validate.test.js +116 -0
  37. package/dist/activity-card/index.d.ts +5 -0
  38. package/dist/activity-card/index.js +3 -0
  39. package/dist/activity-card/layout.d.ts +7 -0
  40. package/dist/activity-card/layout.js +167 -0
  41. package/dist/activity-card/resolver.d.ts +7 -0
  42. package/dist/activity-card/resolver.js +310 -0
  43. package/dist/activity-card/types.d.ts +175 -0
  44. package/dist/activity-card/types.js +20 -0
  45. package/dist/activity-card/validate.d.ts +3 -0
  46. package/dist/activity-card/validate.js +145 -0
  47. package/dist/actor/__tests__/validate.test.d.ts +1 -0
  48. package/dist/actor/__tests__/validate.test.js +57 -0
  49. package/dist/actor/index.d.ts +3 -0
  50. package/dist/actor/index.js +2 -0
  51. package/dist/actor/types.d.ts +20 -0
  52. package/dist/actor/types.js +4 -0
  53. package/dist/actor/validate.d.ts +2 -0
  54. package/dist/actor/validate.js +34 -0
  55. package/dist/applications/__tests__/validate.test.d.ts +1 -0
  56. package/dist/applications/__tests__/validate.test.js +223 -0
  57. package/dist/applications/index.d.ts +2 -0
  58. package/dist/applications/index.js +1 -0
  59. package/dist/applications/types.d.ts +39 -0
  60. package/dist/applications/types.js +1 -0
  61. package/dist/applications/validate.d.ts +3 -0
  62. package/dist/applications/validate.js +109 -0
  63. package/dist/assertion/__tests__/example.test.d.ts +1 -0
  64. package/dist/assertion/__tests__/example.test.js +32 -0
  65. package/dist/assertion/__tests__/validate.test.d.ts +1 -0
  66. package/dist/assertion/__tests__/validate.test.js +130 -0
  67. package/dist/assertion/index.d.ts +4 -0
  68. package/dist/assertion/index.js +2 -0
  69. package/dist/assertion/types.d.ts +43 -0
  70. package/dist/assertion/types.js +7 -0
  71. package/dist/assertion/validate.d.ts +9 -0
  72. package/dist/assertion/validate.js +137 -0
  73. package/dist/blocks/__tests__/layout.test.d.ts +1 -0
  74. package/dist/blocks/__tests__/layout.test.js +153 -0
  75. package/dist/blocks/__tests__/validate.test.d.ts +1 -0
  76. package/dist/blocks/__tests__/validate.test.js +235 -0
  77. package/dist/blocks/index.d.ts +4 -0
  78. package/dist/blocks/index.js +2 -0
  79. package/dist/blocks/layout.d.ts +4 -0
  80. package/dist/blocks/layout.js +145 -0
  81. package/dist/blocks/types.d.ts +56 -0
  82. package/dist/blocks/types.js +1 -0
  83. package/dist/blocks/validate.d.ts +9 -0
  84. package/dist/blocks/validate.js +186 -0
  85. package/dist/capability-map/__tests__/validate.test.d.ts +1 -0
  86. package/dist/capability-map/__tests__/validate.test.js +212 -0
  87. package/dist/capability-map/index.d.ts +2 -0
  88. package/dist/capability-map/index.js +1 -0
  89. package/dist/capability-map/types.d.ts +26 -0
  90. package/dist/capability-map/types.js +1 -0
  91. package/dist/capability-map/validate.d.ts +3 -0
  92. package/dist/capability-map/validate.js +103 -0
  93. package/dist/change/__tests__/validate.test.d.ts +1 -0
  94. package/dist/change/__tests__/validate.test.js +73 -0
  95. package/dist/change/index.d.ts +2 -0
  96. package/dist/change/index.js +1 -0
  97. package/dist/change/types.d.ts +17 -0
  98. package/dist/change/types.js +3 -0
  99. package/dist/change/validate.d.ts +2 -0
  100. package/dist/change/validate.js +57 -0
  101. package/dist/compliance/__tests__/classify.test.d.ts +1 -0
  102. package/dist/compliance/__tests__/classify.test.js +24 -0
  103. package/dist/compliance/__tests__/confidence.test.d.ts +1 -0
  104. package/dist/compliance/__tests__/confidence.test.js +78 -0
  105. package/dist/compliance/__tests__/coverage-metric.test.d.ts +1 -0
  106. package/dist/compliance/__tests__/coverage-metric.test.js +164 -0
  107. package/dist/compliance/__tests__/demo-scenario.test.d.ts +1 -0
  108. package/dist/compliance/__tests__/demo-scenario.test.js +226 -0
  109. package/dist/compliance/__tests__/example.test.d.ts +1 -0
  110. package/dist/compliance/__tests__/example.test.js +51 -0
  111. package/dist/compliance/__tests__/gap-report.test.d.ts +1 -0
  112. package/dist/compliance/__tests__/gap-report.test.js +113 -0
  113. package/dist/compliance/__tests__/html.test.d.ts +1 -0
  114. package/dist/compliance/__tests__/html.test.js +122 -0
  115. package/dist/compliance/__tests__/impact.test.d.ts +1 -0
  116. package/dist/compliance/__tests__/impact.test.js +410 -0
  117. package/dist/compliance/__tests__/markdown.test.d.ts +1 -0
  118. package/dist/compliance/__tests__/markdown.test.js +85 -0
  119. package/dist/compliance/__tests__/views.test.d.ts +1 -0
  120. package/dist/compliance/__tests__/views.test.js +59 -0
  121. package/dist/compliance/classify.d.ts +27 -0
  122. package/dist/compliance/classify.js +60 -0
  123. package/dist/compliance/confidence.d.ts +27 -0
  124. package/dist/compliance/confidence.js +43 -0
  125. package/dist/compliance/coverage-metric.d.ts +74 -0
  126. package/dist/compliance/coverage-metric.js +181 -0
  127. package/dist/compliance/gap-report.d.ts +20 -0
  128. package/dist/compliance/gap-report.js +64 -0
  129. package/dist/compliance/html.d.ts +22 -0
  130. package/dist/compliance/html.js +256 -0
  131. package/dist/compliance/impact.d.ts +251 -0
  132. package/dist/compliance/impact.js +464 -0
  133. package/dist/compliance/index.d.ts +16 -0
  134. package/dist/compliance/index.js +9 -0
  135. package/dist/compliance/markdown.d.ts +18 -0
  136. package/dist/compliance/markdown.js +129 -0
  137. package/dist/compliance/reverse-index.d.ts +8 -0
  138. package/dist/compliance/reverse-index.js +30 -0
  139. package/dist/compliance/types.d.ts +98 -0
  140. package/dist/compliance/types.js +5 -0
  141. package/dist/compliance/views.d.ts +14 -0
  142. package/dist/compliance/views.js +35 -0
  143. package/dist/compliance-matrix/__tests__/build.test.d.ts +1 -0
  144. package/dist/compliance-matrix/__tests__/build.test.js +183 -0
  145. package/dist/compliance-matrix/__tests__/example.test.d.ts +1 -0
  146. package/dist/compliance-matrix/__tests__/example.test.js +49 -0
  147. package/dist/compliance-matrix/build.d.ts +18 -0
  148. package/dist/compliance-matrix/build.js +0 -0
  149. package/dist/compliance-matrix/index.d.ts +2 -0
  150. package/dist/compliance-matrix/index.js +1 -0
  151. package/dist/compliance-matrix/types.d.ts +86 -0
  152. package/dist/compliance-matrix/types.js +8 -0
  153. package/dist/confidence/__tests__/score.test.d.ts +1 -0
  154. package/dist/confidence/__tests__/score.test.js +188 -0
  155. package/dist/confidence/index.d.ts +2 -0
  156. package/dist/confidence/index.js +1 -0
  157. package/dist/confidence/score.d.ts +62 -0
  158. package/dist/confidence/score.js +215 -0
  159. package/dist/confidence/types.d.ts +79 -0
  160. package/dist/confidence/types.js +6 -0
  161. package/dist/edge-path.d.ts +18 -0
  162. package/dist/edge-path.js +35 -0
  163. package/dist/element-envelope.d.ts +15 -0
  164. package/dist/element-envelope.js +55 -0
  165. package/dist/factor/__tests__/validate.test.d.ts +1 -0
  166. package/dist/factor/__tests__/validate.test.js +83 -0
  167. package/dist/factor/index.d.ts +3 -0
  168. package/dist/factor/index.js +2 -0
  169. package/dist/factor/types.d.ts +23 -0
  170. package/dist/factor/types.js +6 -0
  171. package/dist/factor/validate.d.ts +2 -0
  172. package/dist/factor/validate.js +69 -0
  173. package/dist/fgca/__tests__/chain-table.test.d.ts +1 -0
  174. package/dist/fgca/__tests__/chain-table.test.js +158 -0
  175. package/dist/fgca/__tests__/layout.test.d.ts +1 -0
  176. package/dist/fgca/__tests__/layout.test.js +140 -0
  177. package/dist/fgca/__tests__/parse-canonical.test.d.ts +1 -0
  178. package/dist/fgca/__tests__/parse-canonical.test.js +224 -0
  179. package/dist/fgca/__tests__/preview-layout.test.d.ts +1 -0
  180. package/dist/fgca/__tests__/preview-layout.test.js +106 -0
  181. package/dist/fgca/__tests__/validate.test.d.ts +1 -0
  182. package/dist/fgca/__tests__/validate.test.js +125 -0
  183. package/dist/fgca/chain-table.d.ts +36 -0
  184. package/dist/fgca/chain-table.js +177 -0
  185. package/dist/fgca/index.d.ts +16 -0
  186. package/dist/fgca/index.js +11 -0
  187. package/dist/fgca/layout.d.ts +27 -0
  188. package/dist/fgca/layout.js +156 -0
  189. package/dist/fgca/nodeTypes.d.ts +6 -0
  190. package/dist/fgca/nodeTypes.js +10 -0
  191. package/dist/fgca/nodes/FGCAActivityNode.d.ts +13 -0
  192. package/dist/fgca/nodes/FGCAActivityNode.js +35 -0
  193. package/dist/fgca/nodes/FGCAChangeNode.d.ts +12 -0
  194. package/dist/fgca/nodes/FGCAChangeNode.js +35 -0
  195. package/dist/fgca/nodes/FGCAFactorNode.d.ts +12 -0
  196. package/dist/fgca/nodes/FGCAFactorNode.js +35 -0
  197. package/dist/fgca/nodes/FGCAGoalNode.d.ts +14 -0
  198. package/dist/fgca/nodes/FGCAGoalNode.js +40 -0
  199. package/dist/fgca/parse-canonical.d.ts +58 -0
  200. package/dist/fgca/parse-canonical.js +339 -0
  201. package/dist/fgca/preview-layout.d.ts +87 -0
  202. package/dist/fgca/preview-layout.js +122 -0
  203. package/dist/fgca/types.d.ts +39 -0
  204. package/dist/fgca/types.js +7 -0
  205. package/dist/fgca/validate.d.ts +14 -0
  206. package/dist/fgca/validate.js +108 -0
  207. package/dist/geometry.d.ts +7 -0
  208. package/dist/geometry.js +2 -0
  209. package/dist/goals/__tests__/layout.test.d.ts +1 -0
  210. package/dist/goals/__tests__/layout.test.js +153 -0
  211. package/dist/goals/__tests__/mutations.test.d.ts +1 -0
  212. package/dist/goals/__tests__/mutations.test.js +95 -0
  213. package/dist/goals/__tests__/parse-canonical.test.d.ts +1 -0
  214. package/dist/goals/__tests__/parse-canonical.test.js +129 -0
  215. package/dist/goals/__tests__/validate.test.d.ts +1 -0
  216. package/dist/goals/__tests__/validate.test.js +183 -0
  217. package/dist/goals/index.d.ts +6 -0
  218. package/dist/goals/index.js +4 -0
  219. package/dist/goals/layout.d.ts +11 -0
  220. package/dist/goals/layout.js +147 -0
  221. package/dist/goals/mutations.d.ts +6 -0
  222. package/dist/goals/mutations.js +116 -0
  223. package/dist/goals/parse-canonical.d.ts +29 -0
  224. package/dist/goals/parse-canonical.js +257 -0
  225. package/dist/goals/types.d.ts +71 -0
  226. package/dist/goals/types.js +1 -0
  227. package/dist/goals/validate.d.ts +2 -0
  228. package/dist/goals/validate.js +122 -0
  229. package/dist/index.d.ts +23 -0
  230. package/dist/index.js +23 -0
  231. package/dist/issues/__tests__/layout.test.d.ts +1 -0
  232. package/dist/issues/__tests__/layout.test.js +62 -0
  233. package/dist/issues/__tests__/validate.test.d.ts +1 -0
  234. package/dist/issues/__tests__/validate.test.js +82 -0
  235. package/dist/issues/index.d.ts +4 -0
  236. package/dist/issues/index.js +2 -0
  237. package/dist/issues/layout.d.ts +10 -0
  238. package/dist/issues/layout.js +82 -0
  239. package/dist/issues/types.d.ts +61 -0
  240. package/dist/issues/types.js +6 -0
  241. package/dist/issues/validate.d.ts +3 -0
  242. package/dist/issues/validate.js +143 -0
  243. package/dist/process-blueprint/__tests__/compliance-lane.test.d.ts +1 -0
  244. package/dist/process-blueprint/__tests__/compliance-lane.test.js +272 -0
  245. package/dist/process-blueprint/__tests__/layout.test.d.ts +1 -0
  246. package/dist/process-blueprint/__tests__/layout.test.js +159 -0
  247. package/dist/process-blueprint/__tests__/validate.test.d.ts +1 -0
  248. package/dist/process-blueprint/__tests__/validate.test.js +280 -0
  249. package/dist/process-blueprint/index.d.ts +4 -0
  250. package/dist/process-blueprint/index.js +2 -0
  251. package/dist/process-blueprint/layout.d.ts +2 -0
  252. package/dist/process-blueprint/layout.js +416 -0
  253. package/dist/process-blueprint/types.d.ts +220 -0
  254. package/dist/process-blueprint/types.js +1 -0
  255. package/dist/process-blueprint/validate.d.ts +3 -0
  256. package/dist/process-blueprint/validate.js +165 -0
  257. package/dist/process-map/__tests__/validate.test.d.ts +1 -0
  258. package/dist/process-map/__tests__/validate.test.js +199 -0
  259. package/dist/process-map/index.d.ts +2 -0
  260. package/dist/process-map/index.js +1 -0
  261. package/dist/process-map/types.d.ts +32 -0
  262. package/dist/process-map/types.js +1 -0
  263. package/dist/process-map/validate.d.ts +3 -0
  264. package/dist/process-map/validate.js +115 -0
  265. package/dist/products/__tests__/validate.test.d.ts +1 -0
  266. package/dist/products/__tests__/validate.test.js +210 -0
  267. package/dist/products/index.d.ts +2 -0
  268. package/dist/products/index.js +1 -0
  269. package/dist/products/types.d.ts +28 -0
  270. package/dist/products/types.js +1 -0
  271. package/dist/products/validate.d.ts +3 -0
  272. package/dist/products/validate.js +93 -0
  273. package/dist/repo-validate/__tests__/validate-repo.test.d.ts +1 -0
  274. package/dist/repo-validate/__tests__/validate-repo.test.js +132 -0
  275. package/dist/repo-validate/index.d.ts +2 -0
  276. package/dist/repo-validate/index.js +1 -0
  277. package/dist/repo-validate/types.d.ts +34 -0
  278. package/dist/repo-validate/types.js +14 -0
  279. package/dist/repo-validate/validate-repo.d.ts +8 -0
  280. package/dist/repo-validate/validate-repo.js +189 -0
  281. package/dist/requirement/__tests__/example.test.d.ts +1 -0
  282. package/dist/requirement/__tests__/example.test.js +21 -0
  283. package/dist/requirement/__tests__/validate.test.d.ts +1 -0
  284. package/dist/requirement/__tests__/validate.test.js +80 -0
  285. package/dist/requirement/index.d.ts +4 -0
  286. package/dist/requirement/index.js +2 -0
  287. package/dist/requirement/types.d.ts +25 -0
  288. package/dist/requirement/types.js +4 -0
  289. package/dist/requirement/validate.d.ts +7 -0
  290. package/dist/requirement/validate.js +81 -0
  291. package/dist/scenarios/__tests__/validate.test.d.ts +1 -0
  292. package/dist/scenarios/__tests__/validate.test.js +183 -0
  293. package/dist/scenarios/index.d.ts +2 -0
  294. package/dist/scenarios/index.js +1 -0
  295. package/dist/scenarios/types.d.ts +45 -0
  296. package/dist/scenarios/types.js +1 -0
  297. package/dist/scenarios/validate.d.ts +3 -0
  298. package/dist/scenarios/validate.js +110 -0
  299. package/dist/schema-version.d.ts +16 -0
  300. package/dist/schema-version.js +16 -0
  301. package/dist/scope.d.ts +23 -0
  302. package/dist/scope.js +38 -0
  303. package/dist/stakeholder/__tests__/validate.test.d.ts +1 -0
  304. package/dist/stakeholder/__tests__/validate.test.js +86 -0
  305. package/dist/stakeholder/index.d.ts +3 -0
  306. package/dist/stakeholder/index.js +2 -0
  307. package/dist/stakeholder/types.d.ts +24 -0
  308. package/dist/stakeholder/types.js +5 -0
  309. package/dist/stakeholder/validate.d.ts +2 -0
  310. package/dist/stakeholder/validate.js +58 -0
  311. package/dist/target-state/__tests__/validate.test.d.ts +1 -0
  312. package/dist/target-state/__tests__/validate.test.js +62 -0
  313. package/dist/target-state/index.d.ts +2 -0
  314. package/dist/target-state/index.js +1 -0
  315. package/dist/target-state/types.d.ts +18 -0
  316. package/dist/target-state/types.js +7 -0
  317. package/dist/target-state/validate.d.ts +2 -0
  318. package/dist/target-state/validate.js +61 -0
  319. package/dist/theme/index.d.ts +4 -0
  320. package/dist/theme/index.js +2 -0
  321. package/dist/theme/themes.d.ts +16 -0
  322. package/dist/theme/themes.js +213 -0
  323. package/dist/theme/tokens.d.ts +158 -0
  324. package/dist/theme/tokens.js +105 -0
  325. package/dist/typed-id.d.ts +20 -0
  326. package/dist/typed-id.js +38 -0
  327. package/dist/validation-types.d.ts +34 -0
  328. package/dist/validation-types.js +1 -0
  329. package/dist/webview/__tests__/entry-notations.test.d.ts +1 -0
  330. package/dist/webview/__tests__/entry-notations.test.js +62 -0
  331. package/dist/webview/__tests__/entry.test.d.ts +1 -0
  332. package/dist/webview/__tests__/entry.test.js +56 -0
  333. package/dist/webview/__tests__/render-goals.test.d.ts +1 -0
  334. package/dist/webview/__tests__/render-goals.test.js +65 -0
  335. package/dist/webview/entry.d.ts +38 -0
  336. package/dist/webview/entry.js +182 -0
  337. package/dist/webview/render-activities.d.ts +24 -0
  338. package/dist/webview/render-activities.js +127 -0
  339. package/dist/webview/render-activity-card.d.ts +5 -0
  340. package/dist/webview/render-activity-card.js +168 -0
  341. package/dist/webview/render-applications.d.ts +11 -0
  342. package/dist/webview/render-applications.js +105 -0
  343. package/dist/webview/render-blocks.d.ts +5 -0
  344. package/dist/webview/render-blocks.js +71 -0
  345. package/dist/webview/render-capability-map.d.ts +7 -0
  346. package/dist/webview/render-capability-map.js +88 -0
  347. package/dist/webview/render-fgca.d.ts +8 -0
  348. package/dist/webview/render-fgca.js +94 -0
  349. package/dist/webview/render-goals.d.ts +6 -0
  350. package/dist/webview/render-goals.js +92 -0
  351. package/dist/webview/render-process-blueprint.d.ts +5 -0
  352. package/dist/webview/render-process-blueprint.js +135 -0
  353. package/dist/webview/render-process-map.d.ts +7 -0
  354. package/dist/webview/render-process-map.js +82 -0
  355. package/dist/webview/render-products.d.ts +7 -0
  356. package/dist/webview/render-products.js +81 -0
  357. package/dist/webview/render-scenarios.d.ts +7 -0
  358. package/dist/webview/render-scenarios.js +75 -0
  359. package/dist/webview/render-util.d.ts +16 -0
  360. package/dist/webview/render-util.js +31 -0
  361. package/dist/webview/transitrix-render.css +323 -0
  362. package/dist/webview/transitrix-render.js +6452 -0
  363. package/dist/webview/transitrix-render.js.map +7 -0
  364. package/dist/yaml-normalize.d.ts +20 -0
  365. package/dist/yaml-normalize.js +57 -0
  366. package/package.json +40 -0
  367. package/src/__tests__/edge-path.test.ts +46 -0
  368. package/src/__tests__/scope.test.ts +27 -0
  369. package/src/__tests__/yaml-normalize.test.ts +158 -0
  370. package/src/activities/__tests__/cpm.test.ts +164 -0
  371. package/src/activities/__tests__/gantt.test.ts +245 -0
  372. package/src/activities/__tests__/layout.test.ts +112 -0
  373. package/src/activities/__tests__/validate.test.ts +409 -0
  374. package/src/activities/cpm.ts +95 -0
  375. package/src/activities/gantt.ts +276 -0
  376. package/src/activities/index.ts +28 -0
  377. package/src/activities/layout.ts +112 -0
  378. package/src/activities/types.ts +148 -0
  379. package/src/activities/validate.ts +357 -0
  380. package/src/activity-card/__tests__/example.test.ts +66 -0
  381. package/src/activity-card/__tests__/layout.test.ts +63 -0
  382. package/src/activity-card/__tests__/resolver.test.ts +161 -0
  383. package/src/activity-card/__tests__/validate.test.ts +131 -0
  384. package/src/activity-card/index.ts +32 -0
  385. package/src/activity-card/layout.ts +194 -0
  386. package/src/activity-card/resolver.ts +337 -0
  387. package/src/activity-card/types.ts +237 -0
  388. package/src/activity-card/validate.ts +158 -0
  389. package/src/actor/__tests__/validate.test.ts +67 -0
  390. package/src/actor/index.ts +3 -0
  391. package/src/actor/types.ts +29 -0
  392. package/src/actor/validate.ts +38 -0
  393. package/src/applications/__tests__/validate.test.ts +256 -0
  394. package/src/applications/index.ts +11 -0
  395. package/src/applications/types.ts +43 -0
  396. package/src/applications/validate.ts +131 -0
  397. package/src/assertion/__tests__/example.test.ts +37 -0
  398. package/src/assertion/__tests__/validate.test.ts +142 -0
  399. package/src/assertion/index.ts +12 -0
  400. package/src/assertion/types.ts +58 -0
  401. package/src/assertion/validate.ts +145 -0
  402. package/src/blocks/__tests__/layout.test.ts +169 -0
  403. package/src/blocks/__tests__/validate.test.ts +260 -0
  404. package/src/blocks/index.ts +17 -0
  405. package/src/blocks/layout.ts +186 -0
  406. package/src/blocks/types.ts +62 -0
  407. package/src/blocks/validate.ts +206 -0
  408. package/src/capability-map/__tests__/validate.test.ts +238 -0
  409. package/src/capability-map/index.ts +8 -0
  410. package/src/capability-map/types.ts +29 -0
  411. package/src/capability-map/validate.ts +127 -0
  412. package/src/change/__tests__/validate.test.ts +88 -0
  413. package/src/change/index.ts +2 -0
  414. package/src/change/types.ts +23 -0
  415. package/src/change/validate.ts +61 -0
  416. package/src/compliance/__tests__/classify.test.ts +27 -0
  417. package/src/compliance/__tests__/confidence.test.ts +98 -0
  418. package/src/compliance/__tests__/coverage-metric.test.ts +188 -0
  419. package/src/compliance/__tests__/demo-scenario.test.ts +273 -0
  420. package/src/compliance/__tests__/example.test.ts +57 -0
  421. package/src/compliance/__tests__/gap-report.test.ts +129 -0
  422. package/src/compliance/__tests__/html.test.ts +142 -0
  423. package/src/compliance/__tests__/impact.test.ts +482 -0
  424. package/src/compliance/__tests__/markdown.test.ts +95 -0
  425. package/src/compliance/__tests__/views.test.ts +64 -0
  426. package/src/compliance/classify.ts +84 -0
  427. package/src/compliance/confidence.ts +69 -0
  428. package/src/compliance/coverage-metric.ts +263 -0
  429. package/src/compliance/gap-report.ts +94 -0
  430. package/src/compliance/html.ts +295 -0
  431. package/src/compliance/impact.ts +677 -0
  432. package/src/compliance/index.ts +56 -0
  433. package/src/compliance/markdown.ts +135 -0
  434. package/src/compliance/reverse-index.ts +33 -0
  435. package/src/compliance/types.ts +120 -0
  436. package/src/compliance/views.ts +46 -0
  437. package/src/compliance-matrix/__tests__/build.test.ts +200 -0
  438. package/src/compliance-matrix/__tests__/example.test.ts +57 -0
  439. package/src/compliance-matrix/build.ts +0 -0
  440. package/src/compliance-matrix/index.ts +11 -0
  441. package/src/compliance-matrix/types.ts +102 -0
  442. package/src/confidence/__tests__/score.test.ts +254 -0
  443. package/src/confidence/index.ts +21 -0
  444. package/src/confidence/score.ts +243 -0
  445. package/src/confidence/types.ts +96 -0
  446. package/src/edge-path.ts +44 -0
  447. package/src/element-envelope.ts +76 -0
  448. package/src/factor/__tests__/validate.test.ts +98 -0
  449. package/src/factor/index.ts +8 -0
  450. package/src/factor/types.ts +41 -0
  451. package/src/factor/validate.ts +74 -0
  452. package/src/fgca/__tests__/chain-table.test.ts +172 -0
  453. package/src/fgca/__tests__/layout.test.ts +179 -0
  454. package/src/fgca/__tests__/parse-canonical.test.ts +248 -0
  455. package/src/fgca/__tests__/preview-layout.test.ts +124 -0
  456. package/src/fgca/__tests__/validate.test.ts +142 -0
  457. package/src/fgca/chain-table.ts +225 -0
  458. package/src/fgca/index.ts +45 -0
  459. package/src/fgca/layout.ts +204 -0
  460. package/src/fgca/nodeTypes.ts +11 -0
  461. package/src/fgca/nodes/FGCAActivityNode.tsx +65 -0
  462. package/src/fgca/nodes/FGCAChangeNode.tsx +64 -0
  463. package/src/fgca/nodes/FGCAFactorNode.tsx +64 -0
  464. package/src/fgca/nodes/FGCAGoalNode.tsx +72 -0
  465. package/src/fgca/parse-canonical.ts +400 -0
  466. package/src/fgca/preview-layout.ts +217 -0
  467. package/src/fgca/types.ts +50 -0
  468. package/src/fgca/validate.ts +126 -0
  469. package/src/geometry.ts +9 -0
  470. package/src/goals/__tests__/layout.test.ts +175 -0
  471. package/src/goals/__tests__/mutations.test.ts +111 -0
  472. package/src/goals/__tests__/parse-canonical.test.ts +145 -0
  473. package/src/goals/__tests__/validate.test.ts +202 -0
  474. package/src/goals/index.ts +21 -0
  475. package/src/goals/layout.ts +172 -0
  476. package/src/goals/mutations.ts +123 -0
  477. package/src/goals/parse-canonical.ts +286 -0
  478. package/src/goals/types.ts +85 -0
  479. package/src/goals/validate.ts +136 -0
  480. package/src/index.ts +23 -0
  481. package/src/process-blueprint/__tests__/compliance-lane.test.ts +320 -0
  482. package/src/process-blueprint/__tests__/layout.test.ts +195 -0
  483. package/src/process-blueprint/__tests__/validate.test.ts +310 -0
  484. package/src/process-blueprint/index.ts +31 -0
  485. package/src/process-blueprint/layout.ts +531 -0
  486. package/src/process-blueprint/types.ts +242 -0
  487. package/src/process-blueprint/validate.ts +181 -0
  488. package/src/process-map/__tests__/validate.test.ts +223 -0
  489. package/src/process-map/index.ts +10 -0
  490. package/src/process-map/types.ts +36 -0
  491. package/src/process-map/validate.ts +135 -0
  492. package/src/products/__tests__/validate.test.ts +239 -0
  493. package/src/products/index.ts +9 -0
  494. package/src/products/types.ts +31 -0
  495. package/src/products/validate.ts +112 -0
  496. package/src/repo-validate/__tests__/validate-repo.test.ts +149 -0
  497. package/src/repo-validate/index.ts +2 -0
  498. package/src/repo-validate/types.ts +50 -0
  499. package/src/repo-validate/validate-repo.ts +196 -0
  500. package/src/requirement/__tests__/example.test.ts +24 -0
  501. package/src/requirement/__tests__/validate.test.ts +96 -0
  502. package/src/requirement/index.ts +4 -0
  503. package/src/requirement/types.ts +35 -0
  504. package/src/requirement/validate.ts +92 -0
  505. package/src/scenarios/__tests__/validate.test.ts +209 -0
  506. package/src/scenarios/index.ts +15 -0
  507. package/src/scenarios/types.ts +37 -0
  508. package/src/scenarios/validate.ts +122 -0
  509. package/src/schema-version.ts +16 -0
  510. package/src/scope.ts +46 -0
  511. package/src/stakeholder/__tests__/validate.test.ts +101 -0
  512. package/src/stakeholder/index.ts +3 -0
  513. package/src/stakeholder/types.ts +33 -0
  514. package/src/stakeholder/validate.ts +63 -0
  515. package/src/target-state/__tests__/validate.test.ts +74 -0
  516. package/src/target-state/index.ts +2 -0
  517. package/src/target-state/types.ts +28 -0
  518. package/src/target-state/validate.ts +67 -0
  519. package/src/theme/index.ts +4 -0
  520. package/src/theme/themes.ts +237 -0
  521. package/src/theme/tokens.ts +133 -0
  522. package/src/typed-id.ts +56 -0
  523. package/src/validation-types.ts +36 -0
  524. package/src/webview/__tests__/entry-notations.test.ts +77 -0
  525. package/src/webview/__tests__/entry.test.ts +64 -0
  526. package/src/webview/__tests__/render-goals.test.ts +72 -0
  527. package/src/webview/entry.ts +292 -0
  528. package/src/webview/render-activities.ts +156 -0
  529. package/src/webview/render-activity-card.ts +234 -0
  530. package/src/webview/render-applications.ts +123 -0
  531. package/src/webview/render-blocks.ts +91 -0
  532. package/src/webview/render-capability-map.ts +98 -0
  533. package/src/webview/render-fgca.ts +122 -0
  534. package/src/webview/render-goals.ts +110 -0
  535. package/src/webview/render-process-blueprint.ts +207 -0
  536. package/src/webview/render-process-map.ts +93 -0
  537. package/src/webview/render-products.ts +91 -0
  538. package/src/webview/render-scenarios.ts +90 -0
  539. package/src/webview/render-util.ts +34 -0
  540. package/src/webview/styles.css +323 -0
  541. package/src/yaml-normalize.ts +56 -0
@@ -0,0 +1,55 @@
1
+ // Shared §3 envelope validator for standalone element primitives.
2
+ //
3
+ // Every standalone element file in the methodology canon carries the same
4
+ // envelope: a fixed `notation` tag, a canonical `id` whose TYPE prefix matches
5
+ // the element TYPE, `name`, an admission record (`zone`/`admitted_at`/
6
+ // `admitted_by`/`gate_checks`), and a primitive lifecycle
7
+ // (`valid_from`/`valid_to`). Per-TYPE fields (§7) sit between.
8
+ //
9
+ // Each per-TYPE validator delegates envelope checks here so the §3 rule lives
10
+ // in one place; per-TYPE fields (subtype enums, cross-references) are checked
11
+ // in the TYPE's own validator. The error `code` is parameterised so an
12
+ // envelope failure surfaces under the TYPE's own code (e.g. `FACTOR-001`,
13
+ // `CHANGE-001`), preserving per-TYPE attribution.
14
+ import { isCanonicalIdOfType } from './typed-id.js';
15
+ export function validateElementEnvelope(input, opts) {
16
+ const errors = [];
17
+ if (input === null || typeof input !== 'object' || Array.isArray(input)) {
18
+ errors.push({ code: opts.code, message: `${opts.idType} must be a YAML mapping.` });
19
+ return { record: null, errors };
20
+ }
21
+ const r = input;
22
+ if (r.notation !== opts.notation) {
23
+ errors.push({
24
+ code: opts.code,
25
+ message: `notation must be the fixed value "${opts.notation}".`,
26
+ path: 'notation',
27
+ });
28
+ }
29
+ if (!isCanonicalIdOfType(r.id, opts.idType)) {
30
+ errors.push({
31
+ code: opts.code,
32
+ message: `id "${String(r.id)}" must match ${opts.idType}-[<middle>-]<INTEGER>.`,
33
+ path: 'id',
34
+ });
35
+ }
36
+ for (const f of ['name', 'admitted_at', 'admitted_by', 'valid_from']) {
37
+ if (typeof r[f] !== 'string' || r[f].trim() === '') {
38
+ errors.push({ code: opts.code, message: `${f} is required.`, path: f });
39
+ }
40
+ }
41
+ if (r.zone !== 'canon') {
42
+ errors.push({ code: opts.code, message: 'zone is required and must be "canon".', path: 'zone' });
43
+ }
44
+ if (r.gate_checks === null || typeof r.gate_checks !== 'object' || Array.isArray(r.gate_checks)) {
45
+ errors.push({ code: opts.code, message: 'gate_checks is required and must be a mapping.', path: 'gate_checks' });
46
+ }
47
+ if (!('valid_to' in r) || !(typeof r.valid_to === 'string' || r.valid_to === null)) {
48
+ errors.push({
49
+ code: opts.code,
50
+ message: 'valid_to is required (an ISO date string or null).',
51
+ path: 'valid_to',
52
+ });
53
+ }
54
+ return { record: r, errors };
55
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,83 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { validateFactor } from '../validate.js';
3
+ function valid() {
4
+ return {
5
+ notation: 'factor',
6
+ id: 'FACTOR-EU-REG-1',
7
+ name: 'EU regulatory window for market entry',
8
+ type: 'external',
9
+ category: 'legal',
10
+ description: 'Standing external driver — the EU regulatory regime.',
11
+ references_constraint: ['CONSTRAINT-GDPR-RESIDENCY-1'],
12
+ zone: 'canon',
13
+ admitted_at: '2026-05-29',
14
+ admitted_by: 'v.korobeinikov',
15
+ gate_checks: { uniqueness: 'pass', consistency: 'pass', completeness: 'pass' },
16
+ valid_from: '2026-05-26',
17
+ valid_to: null,
18
+ };
19
+ }
20
+ const codes = (input) => validateFactor(input).errors.map(e => e.code);
21
+ const warns = (input) => validateFactor(input).warnings.map(w => w.code);
22
+ describe('validateFactor — positive', () => {
23
+ it('accepts the worked-example FACTOR-EU-REG-1', () => {
24
+ const r = validateFactor(valid());
25
+ expect(r.valid, JSON.stringify(r.errors)).toBe(true);
26
+ expect(r.errors).toHaveLength(0);
27
+ });
28
+ it('accepts a minimal factor (no type/category/references)', () => {
29
+ const r = valid();
30
+ delete r.type;
31
+ delete r.category;
32
+ delete r.references_constraint;
33
+ expect(validateFactor(r).valid).toBe(true);
34
+ });
35
+ });
36
+ describe('validateFactor — FACTOR-001 (envelope / shape)', () => {
37
+ it('flags a non-object', () => {
38
+ expect(codes(null)).toEqual(['FACTOR-001']);
39
+ expect(codes(['x'])).toEqual(['FACTOR-001']);
40
+ });
41
+ it('flags a wrong notation tag', () => {
42
+ expect(codes({ ...valid(), notation: 'driver' })).toContain('FACTOR-001');
43
+ });
44
+ it('flags an id with the wrong TYPE prefix', () => {
45
+ expect(codes({ ...valid(), id: 'GOAL-EU-1' })).toContain('FACTOR-001');
46
+ });
47
+ it('flags an id with a leading-zero terminal', () => {
48
+ expect(codes({ ...valid(), id: 'FACTOR-EU-REG-01' })).toContain('FACTOR-001');
49
+ });
50
+ it('flags a missing valid_to key (distinct from a null value)', () => {
51
+ const r = valid();
52
+ delete r.valid_to;
53
+ expect(codes(r)).toContain('FACTOR-001');
54
+ expect(validateFactor({ ...valid(), valid_to: null }).valid).toBe(true);
55
+ });
56
+ it('flags references_constraint entries that are not CONSTRAINT typed ids', () => {
57
+ expect(codes({ ...valid(), references_constraint: ['GOAL-EU-1'] })).toContain('FACTOR-001');
58
+ expect(codes({ ...valid(), references_constraint: 'CONSTRAINT-X-1' })).toContain('FACTOR-001');
59
+ });
60
+ });
61
+ describe('validateFactor — FACTOR-002 (type enum)', () => {
62
+ it('flags a type outside external/internal', () => {
63
+ expect(codes({ ...valid(), type: 'driver' })).toContain('FACTOR-002');
64
+ });
65
+ it('accepts each permitted type', () => {
66
+ for (const t of ['external', 'internal']) {
67
+ expect(codes({ ...valid(), type: t, category: undefined })).not.toContain('FACTOR-002');
68
+ }
69
+ });
70
+ });
71
+ describe('validateFactor — FACTOR-003 (PESTLE category)', () => {
72
+ it('flags a category outside the PESTLE classes', () => {
73
+ expect(codes({ ...valid(), category: 'cultural' })).toContain('FACTOR-003');
74
+ });
75
+ it('warns when category is set on an internal factor', () => {
76
+ expect(warns({ ...valid(), type: 'internal', category: 'economic' })).toContain('FACTOR-003');
77
+ });
78
+ it('accepts each permitted PESTLE class', () => {
79
+ for (const c of ['political', 'economic', 'social', 'technological', 'legal', 'environmental']) {
80
+ expect(codes({ ...valid(), category: c })).not.toContain('FACTOR-003');
81
+ }
82
+ });
83
+ });
@@ -0,0 +1,3 @@
1
+ export type { Factor as FactorPrimitive, FactorType, FactorCategory } from './types.js';
2
+ export { FACTOR_TYPES, FACTOR_CATEGORIES } from './types.js';
3
+ export { validateFactor } from './validate.js';
@@ -0,0 +1,2 @@
1
+ export { FACTOR_TYPES, FACTOR_CATEGORIES } from './types.js';
2
+ export { validateFactor } from './validate.js';
@@ -0,0 +1,23 @@
1
+ import type { GateChecks } from '../requirement/types.js';
2
+ /** Optional subtype — internal/external organisation force. */
3
+ export type FactorType = 'external' | 'internal';
4
+ export declare const FACTOR_TYPES: readonly ["external", "internal"];
5
+ /** PESTLE sub-classification — external factors only. Open-ended in practice
6
+ * but v1 fixes the six standard classes; an unknown category is flagged. */
7
+ export type FactorCategory = 'political' | 'economic' | 'social' | 'technological' | 'legal' | 'environmental';
8
+ export declare const FACTOR_CATEGORIES: readonly ["political", "economic", "social", "technological", "legal", "environmental"];
9
+ export interface Factor {
10
+ notation: 'factor';
11
+ id: string;
12
+ name: string;
13
+ type?: FactorType;
14
+ category?: FactorCategory;
15
+ description?: string;
16
+ references_constraint?: string[];
17
+ zone: 'canon';
18
+ admitted_at: string;
19
+ admitted_by: string;
20
+ gate_checks: GateChecks;
21
+ valid_from: string;
22
+ valid_to: string | null;
23
+ }
@@ -0,0 +1,6 @@
1
+ // FACTOR — motivation-layer driver primitive.
2
+ // Schema: methodology notations/ELEMENT_PRIMITIVES.md §7.1 + envelope §3.
3
+ export const FACTOR_TYPES = ['external', 'internal'];
4
+ export const FACTOR_CATEGORIES = [
5
+ 'political', 'economic', 'social', 'technological', 'legal', 'environmental',
6
+ ];
@@ -0,0 +1,2 @@
1
+ import type { ValidationResult } from '../validation-types.js';
2
+ export declare function validateFactor(input: unknown): ValidationResult;
@@ -0,0 +1,69 @@
1
+ // FACTOR validator — methodology notations/ELEMENT_PRIMITIVES.md §7.1.
2
+ //
3
+ // Implements FACTOR-001..003. Cross-cutting HDR-/LIFECYCLE-/policy rules
4
+ // (CONTRACT.md §8) are owned elsewhere and out of scope for this
5
+ // single-artefact validator.
6
+ import { typeOfId } from '../typed-id.js';
7
+ import { validateElementEnvelope } from '../element-envelope.js';
8
+ import { FACTOR_CATEGORIES, FACTOR_TYPES } from './types.js';
9
+ export function validateFactor(input) {
10
+ const errors = [];
11
+ const warnings = [];
12
+ const { record, errors: envErrors } = validateElementEnvelope(input, {
13
+ notation: 'factor',
14
+ idType: 'FACTOR',
15
+ code: 'FACTOR-001',
16
+ });
17
+ errors.push(...envErrors);
18
+ if (!record)
19
+ return { valid: false, errors, warnings };
20
+ // FACTOR-002 — type subtype enum (optional field).
21
+ if (record.type !== undefined && !FACTOR_TYPES.includes(record.type)) {
22
+ errors.push({
23
+ code: 'FACTOR-002',
24
+ message: `type "${String(record.type)}" must be one of ${FACTOR_TYPES.join(', ')}.`,
25
+ path: 'type',
26
+ });
27
+ }
28
+ // FACTOR-003 — PESTLE category enum on external factors; warn on internal.
29
+ if (record.category !== undefined) {
30
+ if (!FACTOR_CATEGORIES.includes(record.category)) {
31
+ errors.push({
32
+ code: 'FACTOR-003',
33
+ message: `category "${String(record.category)}" must be a PESTLE class (${FACTOR_CATEGORIES.join(', ')}).`,
34
+ path: 'category',
35
+ });
36
+ }
37
+ else if (record.type === 'internal') {
38
+ // §7.1: "Omit on internal factors — PESTLE does not apply."
39
+ warnings.push({
40
+ code: 'FACTOR-003',
41
+ message: 'category is set on an internal factor; PESTLE applies to external drivers only.',
42
+ path: 'category',
43
+ });
44
+ }
45
+ }
46
+ // FACTOR-001 — references_constraint shape + TYPE on each entry.
47
+ if (record.references_constraint !== undefined) {
48
+ if (!Array.isArray(record.references_constraint)) {
49
+ errors.push({
50
+ code: 'FACTOR-001',
51
+ message: 'references_constraint must be a list of typed CONSTRAINT IDs.',
52
+ path: 'references_constraint',
53
+ });
54
+ }
55
+ else {
56
+ record.references_constraint.forEach((ref, i) => {
57
+ const type = typeOfId(ref);
58
+ if (type !== 'CONSTRAINT') {
59
+ errors.push({
60
+ code: 'FACTOR-001',
61
+ message: `references_constraint[${i}] "${String(ref)}" must be a CONSTRAINT typed ID.`,
62
+ path: `references_constraint[${i}]`,
63
+ });
64
+ }
65
+ });
66
+ }
67
+ }
68
+ return { valid: errors.length === 0, errors, warnings };
69
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,158 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { buildChainTable } from '../chain-table.js';
3
+ // Renders a ChainTable to a compact text grid for readable assertions. Each
4
+ // cell shows "label×rowSpan" (×1 omitted); a position covered by a span above
5
+ // shows "·".
6
+ function asGrid(t) {
7
+ return t.rows.map(row => row.map(c => {
8
+ if (c === null)
9
+ return '·';
10
+ const label = c.cell ? c.cell.label : '∅';
11
+ return c.rowSpan > 1 ? `${label}×${c.rowSpan}` : label;
12
+ }));
13
+ }
14
+ describe('buildChainTable — worked example (FGCA)', () => {
15
+ // F1 → G1, G2 ; G1 → C1 → A1 ; G2 → C2 → A2, A3
16
+ const doc = {
17
+ factors: [{ id: 1, name: 'F1' }],
18
+ goals: [
19
+ { id: 10, name: 'G1', factor: [{ id: 1 }] },
20
+ { id: 11, name: 'G2', factor: [{ id: 1 }] },
21
+ ],
22
+ changes: [
23
+ { id: 20, name: 'C1', goal_id: 10, activity_ids: [30] },
24
+ { id: 21, name: 'C2', goal_id: 11, activity_ids: [31, 32] },
25
+ ],
26
+ activities: [
27
+ { id: 30, name: 'A1', goal_id: 10 },
28
+ { id: 31, name: 'A2', goal_id: 11 },
29
+ { id: 32, name: 'A3', goal_id: 11 },
30
+ ],
31
+ };
32
+ it('produces the spec grid with F1 spanning 3 and G2/C2 spanning 2', () => {
33
+ const t = buildChainTable(doc);
34
+ expect(t.columns).toEqual(['factor', 'goal', 'change', 'activity']);
35
+ expect(asGrid(t)).toEqual([
36
+ ['F1×3', 'G1', 'C1', 'A1'],
37
+ ['·', 'G2×2', 'C2×2', 'A2'],
38
+ ['·', '·', '·', 'A3'],
39
+ ]);
40
+ });
41
+ it('orders the merged cells with correct rowSpans on the model', () => {
42
+ const t = buildChainTable(doc);
43
+ // Factor cell spans all three rows.
44
+ expect(t.rows[0][0]).toMatchObject({ rowSpan: 3, cell: { label: 'F1' } });
45
+ // Covered positions are null (no <td> emitted).
46
+ expect(t.rows[1][0]).toBeNull();
47
+ expect(t.rows[2][1]).toBeNull();
48
+ });
49
+ });
50
+ describe('buildChainTable — FGA (no Change column)', () => {
51
+ // FGA links Goal → Activity directly via activity.goal_id; no changes.
52
+ const doc = {
53
+ factors: [{ id: 1, name: 'F1' }],
54
+ goals: [{ id: 10, name: 'G1', factor: [{ id: 1 }] }],
55
+ activities: [
56
+ { id: 30, name: 'A1', goal_id: 10 },
57
+ { id: 31, name: 'A2', goal_id: 10 },
58
+ ],
59
+ };
60
+ it('emits three columns and merges Factor/Goal across the two activities', () => {
61
+ const t = buildChainTable(doc, { hideChanges: true });
62
+ expect(t.columns).toEqual(['factor', 'goal', 'activity']);
63
+ expect(asGrid(t)).toEqual([
64
+ ['F1×2', 'G1×2', 'A1'],
65
+ ['·', '·', 'A2'],
66
+ ]);
67
+ });
68
+ });
69
+ describe('buildChainTable — missing downstream links (gaps stay visible)', () => {
70
+ it('shows a factor with no goal, a goal with no change, and a change with no activity', () => {
71
+ const doc = {
72
+ factors: [
73
+ { id: 1, name: 'F1' },
74
+ { id: 2, name: 'F2' }, // no goals
75
+ ],
76
+ goals: [
77
+ { id: 10, name: 'G1', factor: [{ id: 1 }] }, // no change, no activity
78
+ { id: 11, name: 'G2', factor: [{ id: 1 }] }, // has a change with no activity
79
+ ],
80
+ changes: [{ id: 20, name: 'C1', goal_id: 11, activity_ids: [] }],
81
+ activities: [],
82
+ };
83
+ const t = buildChainTable(doc);
84
+ expect(asGrid(t)).toEqual([
85
+ ['F1×2', 'G1', '∅', '∅'],
86
+ ['·', 'G2', 'C1', '∅'],
87
+ ['F2', '∅', '∅', '∅'],
88
+ ]);
89
+ });
90
+ it('renders a change-less direct goal→activity path with an empty Change cell', () => {
91
+ const doc = {
92
+ factors: [{ id: 1, name: 'F1' }],
93
+ goals: [{ id: 10, name: 'G1', factor: [{ id: 1 }] }],
94
+ // activity bound straight to the goal, not covered by any change
95
+ changes: [],
96
+ activities: [{ id: 30, name: 'A1', goal_id: 10 }],
97
+ };
98
+ const t = buildChainTable(doc);
99
+ expect(asGrid(t)).toEqual([['F1', 'G1', '∅', 'A1']]);
100
+ });
101
+ });
102
+ describe('buildChainTable — orphans (broken refs are warnings, not errors)', () => {
103
+ it('places a goal with a missing factor in a trailing empty-Factor row', () => {
104
+ const doc = {
105
+ factors: [{ id: 1, name: 'F1' }],
106
+ goals: [
107
+ { id: 10, name: 'G1', factor: [{ id: 1 }] },
108
+ { id: 11, name: 'Gx', factor: [{ id: 99 }] }, // factor 99 does not exist
109
+ { id: 12, name: 'Gy' }, // no factor at all
110
+ ],
111
+ changes: [{ id: 20, name: 'C1', goal_id: 10, activity_ids: [30] }],
112
+ activities: [{ id: 30, name: 'A1', goal_id: 10 }],
113
+ };
114
+ const t = buildChainTable(doc);
115
+ expect(asGrid(t)).toEqual([
116
+ ['F1', 'G1', 'C1', 'A1'],
117
+ ['∅×2', 'Gx', '∅', '∅'],
118
+ ['·', 'Gy', '∅', '∅'],
119
+ ]);
120
+ });
121
+ it('surfaces an unlinked activity as a trailing row with empty cells to its left', () => {
122
+ const doc = {
123
+ factors: [{ id: 1, name: 'F1' }],
124
+ goals: [{ id: 10, name: 'G1', factor: [{ id: 1 }] }],
125
+ changes: [{ id: 20, name: 'C1', goal_id: 10, activity_ids: [30] }],
126
+ activities: [
127
+ { id: 30, name: 'A1', goal_id: 10 },
128
+ { id: 99, name: 'Aorphan' }, // no goal, no change
129
+ ],
130
+ };
131
+ const t = buildChainTable(doc);
132
+ expect(asGrid(t)).toEqual([
133
+ ['F1', 'G1', 'C1', 'A1'],
134
+ ['∅', '∅', '∅', 'Aorphan'],
135
+ ]);
136
+ });
137
+ it('surfaces a change whose goal is missing as an empty Factor+Goal row', () => {
138
+ const doc = {
139
+ factors: [],
140
+ goals: [],
141
+ changes: [{ id: 20, name: 'Cx', goal_id: 999, activity_ids: [30] }],
142
+ activities: [{ id: 30, name: 'A1' }],
143
+ };
144
+ const t = buildChainTable(doc);
145
+ expect(asGrid(t)).toEqual([['∅', '∅', 'Cx', 'A1']]);
146
+ });
147
+ });
148
+ describe('buildChainTable — name fallback', () => {
149
+ it('falls back to the id when an element has an empty name', () => {
150
+ const doc = {
151
+ factors: [{ id: 7, name: '' }],
152
+ goals: [],
153
+ activities: [],
154
+ };
155
+ const t = buildChainTable(doc);
156
+ expect(t.rows[0][0]?.cell?.label).toBe('7');
157
+ });
158
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,140 @@
1
+ import { describe, it, expect, vi, beforeAll } from "vitest";
2
+ // reactflow uses browser APIs — stub the minimum required before importing layout
3
+ beforeAll(() => {
4
+ vi.mock("reactflow", () => ({
5
+ Position: { Left: "left", Right: "right" },
6
+ MarkerType: { ArrowClosed: "arrowclosed" },
7
+ }));
8
+ });
9
+ import { buildFGCALayout } from "../layout";
10
+ import { ALL_FGCA_COLUMNS } from "../types";
11
+ const ALL_COLS = new Set(ALL_FGCA_COLUMNS);
12
+ const factors = [
13
+ { id: 1, name: "Digital growth" },
14
+ { id: 2, name: "Talent shortage" },
15
+ ];
16
+ const goals = [
17
+ { id: 10, name: "Grow revenue", level: 0, factor: [{ id: 1 }] },
18
+ { id: 11, name: "Build talent pipeline", level: 1, factor: [{ id: 2 }] },
19
+ ];
20
+ const changes = [
21
+ { id: 100, name: "Launch digital channel", goal_id: 10, activity_ids: [200] },
22
+ { id: 101, name: "Partner with bootcamps", goal_id: 11, activity_ids: [201] },
23
+ ];
24
+ const activities = [
25
+ { id: 200, name: "Build e-commerce MVP", goal_id: 10, activity_type_id: 1 },
26
+ { id: 201, name: "Run hiring sprint", goal_id: 11, activity_type_id: 1 },
27
+ ];
28
+ describe("buildFGCALayout — all columns visible", () => {
29
+ const { nodes, edges } = buildFGCALayout({
30
+ factors,
31
+ goals,
32
+ changes,
33
+ activities,
34
+ visibleColumns: ALL_COLS,
35
+ activityTypeList: [{ id: 1, name: "Initiative" }],
36
+ });
37
+ it("produces the correct number of nodes", () => {
38
+ expect(nodes).toHaveLength(factors.length + goals.length + changes.length + activities.length);
39
+ });
40
+ it("assigns correct node types", () => {
41
+ const types = nodes.map((n) => n.type);
42
+ expect(types.filter((t) => t === "fgcaFactor")).toHaveLength(factors.length);
43
+ expect(types.filter((t) => t === "fgcaGoal")).toHaveLength(goals.length);
44
+ expect(types.filter((t) => t === "fgcaChange")).toHaveLength(changes.length);
45
+ expect(types.filter((t) => t === "fgcaActivity")).toHaveLength(activities.length);
46
+ });
47
+ it("creates Factor→Goal edges", () => {
48
+ const fgEdges = edges.filter((e) => e.source.startsWith("factor_") && e.target.startsWith("goal_"));
49
+ expect(fgEdges).toHaveLength(2);
50
+ expect(fgEdges.find((e) => e.source === "factor_1" && e.target === "goal_10")).toBeTruthy();
51
+ });
52
+ it("creates Goal→Change edges", () => {
53
+ const gcEdges = edges.filter((e) => e.source.startsWith("goal_") && e.target.startsWith("change_"));
54
+ expect(gcEdges).toHaveLength(2);
55
+ });
56
+ it("creates Change→Activity edges", () => {
57
+ const caEdges = edges.filter((e) => e.source.startsWith("change_") && e.target.startsWith("activity_"));
58
+ expect(caEdges).toHaveLength(2);
59
+ });
60
+ it("does NOT create direct Goal→Activity edges when Change column is visible and all activities are covered", () => {
61
+ const directEdges = edges.filter((e) => e.source.startsWith("goal_") && e.target.startsWith("activity_"));
62
+ expect(directEdges).toHaveLength(0);
63
+ });
64
+ it("uses the activity type name in activity node data", () => {
65
+ const actNode = nodes.find((n) => n.id === "activity_200");
66
+ expect(actNode?.data.activityTypeName).toBe("Initiative");
67
+ });
68
+ it("encodes F-XXXX label format via id in node data", () => {
69
+ const factorNode = nodes.find((n) => n.id === "factor_1");
70
+ expect(String(factorNode?.data.id).padStart(4, "0")).toBe("0001");
71
+ });
72
+ it("encodes C-XXX label format via id in node data", () => {
73
+ const changeNode = nodes.find((n) => n.id === "change_100");
74
+ expect(String(changeNode?.data.id).padStart(3, "0")).toBe("100");
75
+ });
76
+ });
77
+ describe("buildFGCALayout — Factor column hidden", () => {
78
+ const cols = new Set(ALL_FGCA_COLUMNS.filter((c) => c !== "factor"));
79
+ const { nodes, edges } = buildFGCALayout({ factors, goals, changes, activities, visibleColumns: cols });
80
+ it("produces no factor nodes", () => {
81
+ expect(nodes.filter((n) => n.type === "fgcaFactor")).toHaveLength(0);
82
+ });
83
+ it("produces no Factor→Goal edges", () => {
84
+ expect(edges.filter((e) => e.source.startsWith("factor_"))).toHaveLength(0);
85
+ });
86
+ it("still produces Goal and downstream nodes", () => {
87
+ expect(nodes.filter((n) => n.type === "fgcaGoal")).toHaveLength(goals.length);
88
+ });
89
+ });
90
+ describe("buildFGCALayout — Change column hidden", () => {
91
+ const cols = new Set(ALL_FGCA_COLUMNS.filter((c) => c !== "change"));
92
+ const { nodes, edges } = buildFGCALayout({ factors, goals, changes, activities, visibleColumns: cols });
93
+ it("produces no change nodes", () => {
94
+ expect(nodes.filter((n) => n.type === "fgcaChange")).toHaveLength(0);
95
+ });
96
+ it("creates direct Goal→Activity edges when Change column is hidden", () => {
97
+ const directEdges = edges.filter((e) => e.source.startsWith("goal_") && e.target.startsWith("activity_"));
98
+ expect(directEdges.length).toBeGreaterThan(0);
99
+ });
100
+ });
101
+ describe("buildFGCALayout — only one column visible", () => {
102
+ const { nodes, edges } = buildFGCALayout({
103
+ factors,
104
+ goals,
105
+ changes,
106
+ activities,
107
+ visibleColumns: new Set(["goal"]),
108
+ });
109
+ it("produces only goal nodes", () => {
110
+ expect(nodes.filter((n) => n.type !== "fgcaGoal")).toHaveLength(0);
111
+ });
112
+ it("produces no edges", () => {
113
+ expect(edges).toHaveLength(0);
114
+ });
115
+ });
116
+ describe("buildFGCALayout — columns are positioned left to right", () => {
117
+ const { nodes } = buildFGCALayout({ factors, goals, changes, activities, visibleColumns: ALL_COLS });
118
+ it("factor column x < goal column x", () => {
119
+ const f = nodes.find((n) => n.type === "fgcaFactor");
120
+ const g = nodes.find((n) => n.type === "fgcaGoal");
121
+ expect(f.position.x).toBeLessThan(g.position.x);
122
+ });
123
+ it("goal column x < change column x", () => {
124
+ const g = nodes.find((n) => n.type === "fgcaGoal");
125
+ const c = nodes.find((n) => n.type === "fgcaChange");
126
+ expect(g.position.x).toBeLessThan(c.position.x);
127
+ });
128
+ it("change column x < activity column x", () => {
129
+ const c = nodes.find((n) => n.type === "fgcaChange");
130
+ const a = nodes.find((n) => n.type === "fgcaActivity");
131
+ expect(c.position.x).toBeLessThan(a.position.x);
132
+ });
133
+ // Pre-release blocker regression (orchestrator review 2026-05-21).
134
+ it("[blocker] tolerates a change with no activity_ids (optional per validator)", () => {
135
+ const changesNoIds = [
136
+ { id: 100, name: "No activities", goal_id: 10 },
137
+ ];
138
+ expect(() => buildFGCALayout({ factors, goals, changes: changesNoIds, activities, visibleColumns: ALL_COLS })).not.toThrow();
139
+ });
140
+ });
@@ -0,0 +1 @@
1
+ export {};