@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
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023-2026 Valerii Korobeinikov
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,47 @@
1
+ # @transitrix/diagrams
2
+
3
+ Shared rendering, validation, and pure mutations for Transitrix custom diagram
4
+ formats (BPMN, Goals, FGCA, Capability Map, Process Blueprint, and the rest of
5
+ the Transitrix notation family).
6
+
7
+ This package is the runtime library used by the **Transitrix Studio** VS Code
8
+ extension, the `@transitrix/cli`, and downstream tools that need to read,
9
+ validate, or render Transitrix diagrams.
10
+
11
+ ## Install
12
+
13
+ ```bash
14
+ npm install @transitrix/diagrams
15
+ ```
16
+
17
+ `react` and `reactflow` are declared as peer dependencies — install them in the
18
+ host application if you use the React renderers; pure validation / mutation
19
+ APIs do not require them.
20
+
21
+ ## What's inside
22
+
23
+ The package re-exports the per-notation modules under one entry point:
24
+
25
+ - Activities, Activity Card, Applications, Assertion, Blocks, Capability Map,
26
+ Compliance + Compliance Matrix, Confidence, FGCA, Goals, Process Blueprint,
27
+ Process Map, Products, Requirement, Scenarios.
28
+ - Shared primitives: `geometry`, `typed-id`, `validation-types`,
29
+ `yaml-normalize`, `SCHEMA_VERSION`, theme tokens, repository-level validators.
30
+
31
+ See the source layout under [`src/`](./src) and the per-module `index.ts`
32
+ exports for the public surface.
33
+
34
+ ## Versioning
35
+
36
+ `@transitrix/diagrams` ships on its own version line, independent of the
37
+ Transitrix Studio extension version. The first published release is `1.0.0`.
38
+
39
+ ## License
40
+
41
+ MIT — see [`LICENSE`](./LICENSE).
42
+
43
+ ## Links
44
+
45
+ - Monorepo: <https://github.com/transitrix/transitrix-studio>
46
+ - Issues: <https://github.com/transitrix/transitrix-studio/issues>
47
+ - Methodology spec: <https://github.com/transitrix/methodology>
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,39 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { horizontalCubicEdgePath, EDGE_MIN_HANDLE } from '../edge-path.js';
3
+ describe('horizontalCubicEdgePath', () => {
4
+ // A short horizontal edge: |dx|=40 < EDGE_MIN_HANDLE*2, |dy|=0, so the
5
+ // base handle is the floor (64).
6
+ const sx = 0, sy = 0, tx = 40, ty = 0;
7
+ it('curvature 1 uses the floor handle (historical appearance)', () => {
8
+ const d = horizontalCubicEdgePath(sx, sy, tx, ty, 1);
9
+ // handle = max(64, 20, 0) = 64
10
+ expect(d).toBe(`M0,0 C${EDGE_MIN_HANDLE},0 ${40 - EDGE_MIN_HANDLE},0 40,0`);
11
+ });
12
+ it('default curvature equals explicit curvature 1', () => {
13
+ expect(horizontalCubicEdgePath(sx, sy, tx, ty)).toBe(horizontalCubicEdgePath(sx, sy, tx, ty, 1));
14
+ });
15
+ it('curvature 0 collapses control points onto the endpoints (straight line)', () => {
16
+ const d = horizontalCubicEdgePath(sx, sy, tx, ty, 0);
17
+ expect(d).toBe('M0,0 C0,0 40,0 40,0');
18
+ });
19
+ it('changing curvature changes the path output (AC#6)', () => {
20
+ const a = horizontalCubicEdgePath(sx, sy, tx, ty, 1);
21
+ const b = horizontalCubicEdgePath(sx, sy, tx, ty, 2);
22
+ expect(b).not.toBe(a);
23
+ });
24
+ it('larger curvature pushes the control handles further out', () => {
25
+ const handleOf = (curv) => {
26
+ // First control point x is `sx + handle`; parse it back out.
27
+ const m = horizontalCubicEdgePath(0, 0, 40, 0, curv).match(/^M0,0 C(-?\d+(?:\.\d+)?),0/);
28
+ return Number(m[1]);
29
+ };
30
+ expect(handleOf(2)).toBeGreaterThan(handleOf(1));
31
+ expect(handleOf(0.5)).toBeLessThan(handleOf(1));
32
+ expect(handleOf(2)).toBe(2 * EDGE_MIN_HANDLE);
33
+ });
34
+ it('handle scales with the vertical span for stacked nodes', () => {
35
+ // |dy|=200 ⇒ base handle = max(64, 0, 160) = 160; ×1 = 160.
36
+ const d = horizontalCubicEdgePath(0, 0, 0, 200, 1);
37
+ expect(d).toBe('M0,0 C160,0 -160,200 0,200');
38
+ });
39
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,23 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { checkScopeRoot, SCOPE_MISSING_ROOT_CODE } from '../scope.js';
3
+ describe('checkScopeRoot', () => {
4
+ it('returns null for non-root scopes', () => {
5
+ expect(checkScopeRoot({ mode: 'all' }, [1, 2])).toBeNull();
6
+ expect(checkScopeRoot({ mode: 'level', maxLevel: 1 }, [1, 2])).toBeNull();
7
+ });
8
+ it('returns null when the root id is present (numeric ids compared as strings)', () => {
9
+ const scope = { mode: 'root', rootGoalId: '2' };
10
+ expect(checkScopeRoot(scope, [1, 2, 3])).toBeNull();
11
+ });
12
+ it('returns a SCOPE-001 warning when the root id is absent', () => {
13
+ const scope = { mode: 'root', rootGoalId: '99' };
14
+ const w = checkScopeRoot(scope, [1, 2, 3]);
15
+ expect(w).not.toBeNull();
16
+ expect(w.code).toBe(SCOPE_MISSING_ROOT_CODE);
17
+ expect(w.message).toContain('99');
18
+ });
19
+ it('handles string goal ids', () => {
20
+ expect(checkScopeRoot({ mode: 'root', rootGoalId: 'g1' }, ['g1', 'g2'])).toBeNull();
21
+ expect(checkScopeRoot({ mode: 'root', rootGoalId: 'gX' }, ['g1', 'g2'])).not.toBeNull();
22
+ });
23
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,148 @@
1
+ import { describe, it, expect, beforeAll, afterAll } from 'vitest';
2
+ import yaml from 'js-yaml';
3
+ import { coerceDatesToIsoStrings } from '../yaml-normalize.js';
4
+ import { validateActivities } from '../activities/index.js';
5
+ describe('coerceDatesToIsoStrings', () => {
6
+ it('replaces top-level Date with ISO YYYY-MM-DD string', () => {
7
+ const out = coerceDatesToIsoStrings(new Date(Date.UTC(2026, 5, 1))); // 2026-06-01 UTC
8
+ expect(out).toBe('2026-06-01');
9
+ });
10
+ it('replaces Date values inside nested arrays of objects', () => {
11
+ const input = {
12
+ activities: [
13
+ { id: 'A-001', start_date: new Date(Date.UTC(2026, 5, 1)), end_date: new Date(Date.UTC(2026, 5, 5)) },
14
+ { id: 'A-002', start_date: '2026-06-06' },
15
+ ],
16
+ };
17
+ coerceDatesToIsoStrings(input);
18
+ expect(input.activities[0].start_date).toBe('2026-06-01');
19
+ expect(input.activities[0].end_date).toBe('2026-06-05');
20
+ expect(input.activities[1].start_date).toBe('2026-06-06');
21
+ });
22
+ it('walks objects recursively', () => {
23
+ const input = {
24
+ project: { start_date: new Date(Date.UTC(2026, 0, 15)) },
25
+ };
26
+ coerceDatesToIsoStrings(input);
27
+ expect(input.project.start_date).toBe('2026-01-15');
28
+ });
29
+ it('leaves non-Date values untouched', () => {
30
+ const input = {
31
+ notation: 'activities',
32
+ activities: [{ id: 'A-001', duration: 3, start_date: '2026-06-01' }],
33
+ };
34
+ const out = coerceDatesToIsoStrings(input);
35
+ expect(out).toBe(input);
36
+ expect(input.activities[0].start_date).toBe('2026-06-01');
37
+ expect(input.activities[0].duration).toBe(3);
38
+ });
39
+ it('handles null, undefined, and primitive scalars', () => {
40
+ expect(coerceDatesToIsoStrings(null)).toBe(null);
41
+ expect(coerceDatesToIsoStrings(undefined)).toBe(undefined);
42
+ expect(coerceDatesToIsoStrings('hello')).toBe('hello');
43
+ expect(coerceDatesToIsoStrings(42)).toBe(42);
44
+ expect(coerceDatesToIsoStrings(true)).toBe(true);
45
+ });
46
+ it('handles arrays that contain bare Date elements', () => {
47
+ const arr = [new Date(Date.UTC(2026, 5, 1)), 'x', { d: new Date(Date.UTC(2026, 5, 2)) }];
48
+ coerceDatesToIsoStrings(arr);
49
+ expect(arr[0]).toBe('2026-06-01');
50
+ expect(arr[1]).toBe('x');
51
+ expect(arr[2].d).toBe('2026-06-02');
52
+ });
53
+ });
54
+ describe('coerceDatesToIsoStrings — js-yaml integration', () => {
55
+ it('quoted ISO dates are preserved untouched', () => {
56
+ const text = [
57
+ 'notation: activities',
58
+ 'activities:',
59
+ ' - id: "A-001"',
60
+ ' name: "Quoted dates"',
61
+ ' duration: 3',
62
+ ' start_date: "2026-06-01"',
63
+ ' end_date: "2026-06-03"',
64
+ '',
65
+ ].join('\n');
66
+ const parsed = yaml.load(text);
67
+ coerceDatesToIsoStrings(parsed);
68
+ expect(parsed.activities[0].start_date).toBe('2026-06-01');
69
+ expect(parsed.activities[0].end_date).toBe('2026-06-03');
70
+ const v = validateActivities(parsed);
71
+ expect(v.valid).toBe(true);
72
+ });
73
+ it('bare ISO dates are coerced to ISO strings and pass validation', () => {
74
+ const text = [
75
+ 'notation: activities',
76
+ 'activities:',
77
+ ' - id: "A-001"',
78
+ ' name: "Bare dates"',
79
+ ' duration: 3',
80
+ ' start_date: 2026-06-01',
81
+ ' end_date: 2026-06-03',
82
+ '',
83
+ ].join('\n');
84
+ const parsed = yaml.load(text);
85
+ // Without coercion, js-yaml returns Date instances and the validator
86
+ // would emit ACT-008 because typeof !== 'string'. Sanity-check that
87
+ // assumption before normalisation.
88
+ expect(parsed.activities[0].start_date).toBeInstanceOf(Date);
89
+ coerceDatesToIsoStrings(parsed);
90
+ expect(parsed.activities[0].start_date).toBe('2026-06-01');
91
+ expect(parsed.activities[0].end_date).toBe('2026-06-03');
92
+ const v = validateActivities(parsed);
93
+ expect(v.valid).toBe(true);
94
+ });
95
+ });
96
+ // The issue calls out the timezone trap: a Date built from a bare ISO
97
+ // date is midnight UTC, but if we used `getFullYear / getMonth / getDate`
98
+ // instead of the UTC variants, a non-UTC process timezone would shift the
99
+ // day. Pin a non-UTC TZ for this block so the regression bites if anyone
100
+ // later swaps to local-time accessors.
101
+ describe('coerceDatesToIsoStrings — non-UTC timezone', () => {
102
+ const originalTz = process.env.TZ;
103
+ beforeAll(() => {
104
+ // Pacific/Auckland is UTC+12/+13 — the most aggressive eastward shift
105
+ // available without DST gymnastics. If local-time accessors are used,
106
+ // `2026-06-01` parsed as midnight UTC reads back as `2026-06-01 12:00
107
+ // local`, which is still the right day — so use a westward TZ for the
108
+ // real test below. Auckland is here as a sanity case.
109
+ process.env.TZ = 'Pacific/Auckland';
110
+ });
111
+ afterAll(() => {
112
+ if (originalTz === undefined) {
113
+ delete process.env.TZ;
114
+ }
115
+ else {
116
+ process.env.TZ = originalTz;
117
+ }
118
+ });
119
+ it('does not drift the day forward in an eastward timezone', () => {
120
+ const text = 'd: 2026-06-01\n';
121
+ const parsed = yaml.load(text);
122
+ coerceDatesToIsoStrings(parsed);
123
+ expect(parsed.d).toBe('2026-06-01');
124
+ });
125
+ });
126
+ describe('coerceDatesToIsoStrings — westward (negative offset) timezone', () => {
127
+ const originalTz = process.env.TZ;
128
+ beforeAll(() => {
129
+ // Pacific/Honolulu is UTC-10, no DST. Midnight UTC `2026-06-01` reads
130
+ // back as `2026-05-31 14:00 local`. Local-time accessors would yield
131
+ // `2026-05-31` — wrong by one day. UTC accessors yield `2026-06-01`.
132
+ process.env.TZ = 'Pacific/Honolulu';
133
+ });
134
+ afterAll(() => {
135
+ if (originalTz === undefined) {
136
+ delete process.env.TZ;
137
+ }
138
+ else {
139
+ process.env.TZ = originalTz;
140
+ }
141
+ });
142
+ it('does not drift the day backward in a westward timezone', () => {
143
+ const text = 'd: 2026-06-01\n';
144
+ const parsed = yaml.load(text);
145
+ coerceDatesToIsoStrings(parsed);
146
+ expect(parsed.d).toBe('2026-06-01');
147
+ });
148
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,146 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { computeCpm } from '../cpm.js';
3
+ // PMBoK textbook example (5-activity network):
4
+ // A(5) → B(8) → D(7)
5
+ // A(5) → C(3) → D(7)
6
+ // D(7) → E(4)
7
+ // Critical path: A→B→D→E = 5+8+7+4 = 24
8
+ // A: ES=0, EF=5, LS=0, LF=5, slack=0 (critical)
9
+ // B: ES=5, EF=13, LS=5, LF=13, slack=0 (critical)
10
+ // C: ES=5, EF=8, LS=10, LF=13, slack=5
11
+ // D: ES=13, EF=20, LS=13, LF=20, slack=0 (critical)
12
+ // E: ES=20, EF=24, LS=20, LF=24, slack=0 (critical)
13
+ const pmBokActivities = [
14
+ { id: 'A', name: 'A', duration: 5 },
15
+ { id: 'B', name: 'B', duration: 8, predecessors: ['A'] },
16
+ { id: 'C', name: 'C', duration: 3, predecessors: ['A'] },
17
+ { id: 'D', name: 'D', duration: 7, predecessors: ['B', 'C'] },
18
+ { id: 'E', name: 'E', duration: 4, predecessors: ['D'] },
19
+ ];
20
+ describe('computeCpm — PMBoK textbook example', () => {
21
+ const result = computeCpm(pmBokActivities);
22
+ it('computes ES for each activity', () => {
23
+ expect(result.get('A').es).toBe(0);
24
+ expect(result.get('B').es).toBe(5);
25
+ expect(result.get('C').es).toBe(5);
26
+ expect(result.get('D').es).toBe(13);
27
+ expect(result.get('E').es).toBe(20);
28
+ });
29
+ it('computes EF for each activity', () => {
30
+ expect(result.get('A').ef).toBe(5);
31
+ expect(result.get('B').ef).toBe(13);
32
+ expect(result.get('C').ef).toBe(8);
33
+ expect(result.get('D').ef).toBe(20);
34
+ expect(result.get('E').ef).toBe(24);
35
+ });
36
+ it('computes LS for each activity', () => {
37
+ expect(result.get('A').ls).toBe(0);
38
+ expect(result.get('B').ls).toBe(5);
39
+ expect(result.get('C').ls).toBe(10);
40
+ expect(result.get('D').ls).toBe(13);
41
+ expect(result.get('E').ls).toBe(20);
42
+ });
43
+ it('computes LF for each activity', () => {
44
+ expect(result.get('A').lf).toBe(5);
45
+ expect(result.get('B').lf).toBe(13);
46
+ expect(result.get('C').lf).toBe(13);
47
+ expect(result.get('D').lf).toBe(20);
48
+ expect(result.get('E').lf).toBe(24);
49
+ });
50
+ it('computes slack', () => {
51
+ expect(result.get('A').slack).toBe(0);
52
+ expect(result.get('B').slack).toBe(0);
53
+ expect(result.get('C').slack).toBe(5);
54
+ expect(result.get('D').slack).toBe(0);
55
+ expect(result.get('E').slack).toBe(0);
56
+ });
57
+ it('marks critical path activities', () => {
58
+ expect(result.get('A').isCritical).toBe(true);
59
+ expect(result.get('B').isCritical).toBe(true);
60
+ expect(result.get('C').isCritical).toBe(false);
61
+ expect(result.get('D').isCritical).toBe(true);
62
+ expect(result.get('E').isCritical).toBe(true);
63
+ });
64
+ });
65
+ describe('computeCpm — edge cases', () => {
66
+ it('returns empty map for empty input', () => {
67
+ const result = computeCpm([]);
68
+ expect(result.size).toBe(0);
69
+ });
70
+ it('single node with no predecessors: ES=0, EF=duration, slack=0', () => {
71
+ const result = computeCpm([{ id: 'A', name: 'A', duration: 5 }]);
72
+ expect(result.get('A').es).toBe(0);
73
+ expect(result.get('A').ef).toBe(5);
74
+ expect(result.get('A').slack).toBe(0);
75
+ expect(result.get('A').isCritical).toBe(true);
76
+ });
77
+ it('activity with no duration treated as zero-duration', () => {
78
+ const result = computeCpm([{ id: 'A', name: 'Milestone' }]);
79
+ expect(result.get('A').ef).toBe(0);
80
+ });
81
+ it('linear chain A(3)→B(5)→C(2): total=10, all critical', () => {
82
+ const activities = [
83
+ { id: 'A', name: 'A', duration: 3 },
84
+ { id: 'B', name: 'B', duration: 5, predecessors: ['A'] },
85
+ { id: 'C', name: 'C', duration: 2, predecessors: ['B'] },
86
+ ];
87
+ const result = computeCpm(activities);
88
+ expect(result.get('A').ef).toBe(3);
89
+ expect(result.get('B').ef).toBe(8);
90
+ expect(result.get('C').ef).toBe(10);
91
+ expect(result.get('A').isCritical).toBe(true);
92
+ expect(result.get('B').isCritical).toBe(true);
93
+ expect(result.get('C').isCritical).toBe(true);
94
+ });
95
+ it('parallel paths: longer path is critical, shorter has positive slack', () => {
96
+ // A(1) → C(10)
97
+ // B(5) → C(10)
98
+ // merge at D(2): predecessors [C]
99
+ // path via A: 1+10+2=13; path via B: 5+10+2=17 → critical
100
+ const activities = [
101
+ { id: 'A', name: 'A', duration: 1 },
102
+ { id: 'B', name: 'B', duration: 5 },
103
+ { id: 'C', name: 'C', duration: 10, predecessors: ['A', 'B'] },
104
+ { id: 'D', name: 'D', duration: 2, predecessors: ['C'] },
105
+ ];
106
+ const result = computeCpm(activities);
107
+ // C.es = max(EF[A], EF[B]) = max(1,5) = 5
108
+ expect(result.get('C').es).toBe(5);
109
+ expect(result.get('A').isCritical).toBe(false);
110
+ expect(result.get('B').isCritical).toBe(true);
111
+ expect(result.get('C').isCritical).toBe(true);
112
+ expect(result.get('D').isCritical).toBe(true);
113
+ expect(result.get('A').slack).toBe(4);
114
+ expect(result.get('B').slack).toBe(0);
115
+ });
116
+ });
117
+ describe('computeCpm — cycle defence', () => {
118
+ // Pre-release should-fix: Kahn's topo-order silently omits cyclic nodes.
119
+ // The validator's ACT-006 is the authoritative cycle error, but
120
+ // layoutActivities is reachable without validation, so computeCpm must
121
+ // still produce an entry for every activity (filled with neutral values
122
+ // for omitted cyclic ones) — otherwise consumers see `undefined` and
123
+ // bars render without CPM data.
124
+ it('emits entries for every activity on a 2-node mutual cycle', () => {
125
+ const result = computeCpm([
126
+ { id: 'A', name: 'A', duration: 3, predecessors: ['B'] },
127
+ { id: 'B', name: 'B', duration: 2, predecessors: ['A'] },
128
+ ]);
129
+ expect(result.size).toBe(2);
130
+ expect(result.get('A')).toMatchObject({ es: 0, ef: 0, slack: 0, isCritical: false });
131
+ expect(result.get('B')).toMatchObject({ es: 0, ef: 0, slack: 0, isCritical: false });
132
+ });
133
+ it('keeps real CPM data for the acyclic prefix when a cycle exists downstream', () => {
134
+ // A → B is acyclic; C ↔ D is a mutual cycle disconnected from A/B.
135
+ const result = computeCpm([
136
+ { id: 'A', name: 'A', duration: 5 },
137
+ { id: 'B', name: 'B', duration: 3, predecessors: ['A'] },
138
+ { id: 'C', name: 'C', duration: 2, predecessors: ['D'] },
139
+ { id: 'D', name: 'D', duration: 2, predecessors: ['C'] },
140
+ ]);
141
+ expect(result.get('A')?.ef).toBe(5);
142
+ expect(result.get('B')?.ef).toBe(8);
143
+ expect(result.get('C')).toMatchObject({ es: 0, ef: 0, isCritical: false });
144
+ expect(result.get('D')).toMatchObject({ es: 0, ef: 0, isCritical: false });
145
+ });
146
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,224 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { computeGanttLayout, isGanttUnavailable } from '../gantt.js';
3
+ function assertRenders(result) {
4
+ if (isGanttUnavailable(result)) {
5
+ throw new Error(`Expected Gantt to render but got unavailable: ${result.reason}`);
6
+ }
7
+ return result;
8
+ }
9
+ describe('computeGanttLayout — mode detection', () => {
10
+ it('returns unavailable when neither project.start_date nor pinned dates exist', () => {
11
+ const doc = {
12
+ notation: 'activities',
13
+ activities: [
14
+ { id: 'A', name: 'A', duration: 3 },
15
+ { id: 'B', name: 'B', duration: 2, predecessors: ['A'] },
16
+ ],
17
+ };
18
+ const r = computeGanttLayout(doc);
19
+ expect(isGanttUnavailable(r)).toBe(true);
20
+ });
21
+ it('selects computed mode when project.start_date + every leaf has duration', () => {
22
+ const doc = {
23
+ notation: 'activities',
24
+ project: { start_date: '2026-06-01' },
25
+ activities: [
26
+ { id: 'A', name: 'A', duration: 3 },
27
+ { id: 'B', name: 'B', duration: 2, predecessors: ['A'] },
28
+ ],
29
+ };
30
+ const r = assertRenders(computeGanttLayout(doc));
31
+ expect(r.mode).toBe('computed');
32
+ });
33
+ it('selects pinned mode when every leaf has start_date + end_date', () => {
34
+ const doc = {
35
+ notation: 'activities',
36
+ activities: [
37
+ { id: 'A', name: 'A', start_date: '2026-06-01', end_date: '2026-06-03' },
38
+ { id: 'B', name: 'B', start_date: '2026-06-04', end_date: '2026-06-06' },
39
+ ],
40
+ };
41
+ const r = assertRenders(computeGanttLayout(doc));
42
+ expect(r.mode).toBe('pinned');
43
+ });
44
+ it('unavailable when only some leaves have pinned dates and no project.start_date', () => {
45
+ const doc = {
46
+ notation: 'activities',
47
+ activities: [
48
+ { id: 'A', name: 'A', start_date: '2026-06-01', end_date: '2026-06-03' },
49
+ { id: 'B', name: 'B', duration: 3 }, // no pinned dates, no project.start_date
50
+ ],
51
+ };
52
+ const r = computeGanttLayout(doc);
53
+ expect(isGanttUnavailable(r)).toBe(true);
54
+ });
55
+ it('unavailable when the document has no activities', () => {
56
+ const r = computeGanttLayout({ notation: 'activities', activities: [] });
57
+ expect(isGanttUnavailable(r)).toBe(true);
58
+ });
59
+ });
60
+ describe('computeGanttLayout — computed mode (CPM + calendar projection)', () => {
61
+ const doc = {
62
+ notation: 'activities',
63
+ project: { start_date: '2026-06-01' }, // Monday
64
+ activities: [
65
+ { id: 'A', name: 'A', duration: 3 },
66
+ { id: 'B', name: 'B', duration: 2, predecessors: ['A'] },
67
+ ],
68
+ };
69
+ it('places the first bar at project.start_date and spans its duration', () => {
70
+ const r = assertRenders(computeGanttLayout(doc));
71
+ const a = r.bars.find(b => b.id === 'A');
72
+ expect(a.startDate).toBe('2026-06-01');
73
+ // 7-day default calendar: day 0 = start_date, last day = 2026-06-03 (start + 2)
74
+ expect(a.endDate).toBe('2026-06-03');
75
+ });
76
+ it('places successor bars after their predecessor finishes', () => {
77
+ const r = assertRenders(computeGanttLayout(doc));
78
+ const b = r.bars.find(b => b.id === 'B');
79
+ // B has ES = EF(A) = 3 working days, so it starts at 2026-06-04
80
+ expect(b.startDate).toBe('2026-06-04');
81
+ expect(b.endDate).toBe('2026-06-05');
82
+ });
83
+ it('marks critical-path bars on a linear chain', () => {
84
+ const r = assertRenders(computeGanttLayout(doc));
85
+ const a = r.bars.find(b => b.id === 'A');
86
+ const bbar = r.bars.find(b => b.id === 'B');
87
+ expect(a.isCritical).toBe(true);
88
+ expect(bbar.isCritical).toBe(true);
89
+ });
90
+ it('emits predecessor → successor links with critical flag carried over', () => {
91
+ const r = assertRenders(computeGanttLayout(doc));
92
+ expect(r.links).toHaveLength(1);
93
+ expect(r.links[0]).toMatchObject({ sourceId: 'A', targetId: 'B', isCritical: true });
94
+ });
95
+ it('timeline bounds span min(start) to max(end)', () => {
96
+ const r = assertRenders(computeGanttLayout(doc));
97
+ expect(r.timelineStart).toBe('2026-06-01');
98
+ expect(r.timelineEnd).toBe('2026-06-05');
99
+ });
100
+ });
101
+ describe('computeGanttLayout — calendar (working_days, holidays)', () => {
102
+ it('skips weekend days when working_days excludes them', () => {
103
+ // 2026-06-01 is a Monday. Duration 7 days, only Mon–Fri working.
104
+ // Day 0 = Mon 06-01, days 1..4 = Tue-Fri, day 5 = next Mon 06-08, day 6 = Tue 06-09.
105
+ const doc = {
106
+ notation: 'activities',
107
+ project: {
108
+ start_date: '2026-06-01',
109
+ calendar: { working_days: ['mon', 'tue', 'wed', 'thu', 'fri'] },
110
+ },
111
+ activities: [{ id: 'A', name: 'A', duration: 7 }],
112
+ };
113
+ const r = assertRenders(computeGanttLayout(doc));
114
+ const a = r.bars.find(b => b.id === 'A');
115
+ expect(a.startDate).toBe('2026-06-01');
116
+ expect(a.endDate).toBe('2026-06-09');
117
+ });
118
+ it('skips dates listed under holidays', () => {
119
+ // 7-day week, but 2026-06-02 is a holiday → 3-day activity ends on 2026-06-04.
120
+ const doc = {
121
+ notation: 'activities',
122
+ project: {
123
+ start_date: '2026-06-01',
124
+ calendar: { holidays: ['2026-06-02'] },
125
+ },
126
+ activities: [{ id: 'A', name: 'A', duration: 3 }],
127
+ };
128
+ const r = assertRenders(computeGanttLayout(doc));
129
+ const a = r.bars.find(b => b.id === 'A');
130
+ expect(a.startDate).toBe('2026-06-01');
131
+ expect(a.endDate).toBe('2026-06-04');
132
+ });
133
+ });
134
+ describe('computeGanttLayout — milestones', () => {
135
+ it('renders zero-duration leaves as milestones with start == end', () => {
136
+ const doc = {
137
+ notation: 'activities',
138
+ project: { start_date: '2026-06-01' },
139
+ activities: [
140
+ { id: 'A', name: 'A', duration: 3 },
141
+ { id: 'M', name: 'Launch', duration: 0, predecessors: ['A'] },
142
+ ],
143
+ };
144
+ const r = assertRenders(computeGanttLayout(doc));
145
+ const m = r.bars.find(b => b.id === 'M');
146
+ expect(m.kind).toBe('milestone');
147
+ expect(m.startDate).toBe(m.endDate);
148
+ // M lives at the working day immediately after A finishes (ES = 3).
149
+ expect(m.startDate).toBe('2026-06-04');
150
+ });
151
+ it('pinned mode treats start==end activities as milestones', () => {
152
+ const doc = {
153
+ notation: 'activities',
154
+ activities: [
155
+ { id: 'M', name: 'Launch', duration: 0, start_date: '2026-06-15', end_date: '2026-06-15' },
156
+ ],
157
+ };
158
+ const r = assertRenders(computeGanttLayout(doc));
159
+ const m = r.bars.find(b => b.id === 'M');
160
+ expect(m.kind).toBe('milestone');
161
+ });
162
+ });
163
+ describe('computeGanttLayout — phases (parent activities)', () => {
164
+ it('rolls up phase span from children', () => {
165
+ const doc = {
166
+ notation: 'activities',
167
+ project: { start_date: '2026-06-01' },
168
+ activities: [
169
+ { id: 'PHASE-1', name: 'Design' },
170
+ { id: 'A', name: 'A', duration: 3, parent: 'PHASE-1' },
171
+ { id: 'B', name: 'B', duration: 5, parent: 'PHASE-1', predecessors: ['A'] },
172
+ ],
173
+ };
174
+ const r = assertRenders(computeGanttLayout(doc));
175
+ const phase = r.bars.find(b => b.id === 'PHASE-1');
176
+ expect(phase.kind).toBe('phase');
177
+ expect(phase.startDate).toBe('2026-06-01'); // earliest child = A start
178
+ expect(phase.endDate).toBe('2026-06-08'); // latest child = B end (ES=3 + 5 days)
179
+ });
180
+ it('marks the phase critical if any child is on the critical path', () => {
181
+ const doc = {
182
+ notation: 'activities',
183
+ project: { start_date: '2026-06-01' },
184
+ activities: [
185
+ { id: 'PHASE-1', name: 'Design' },
186
+ { id: 'A', name: 'A', duration: 3, parent: 'PHASE-1' },
187
+ ],
188
+ };
189
+ const r = assertRenders(computeGanttLayout(doc));
190
+ const phase = r.bars.find(b => b.id === 'PHASE-1');
191
+ expect(phase.isCritical).toBe(true);
192
+ });
193
+ });
194
+ describe('computeGanttLayout — pinned mode', () => {
195
+ it('places bars at pinned dates without computing CPM', () => {
196
+ const doc = {
197
+ notation: 'activities',
198
+ activities: [
199
+ { id: 'A', name: 'A', start_date: '2026-08-01', end_date: '2026-08-10' },
200
+ { id: 'B', name: 'B', start_date: '2026-08-12', end_date: '2026-08-20', predecessors: ['A'] },
201
+ ],
202
+ };
203
+ const r = assertRenders(computeGanttLayout(doc));
204
+ expect(r.mode).toBe('pinned');
205
+ expect(r.bars.find(b => b.id === 'A')?.startDate).toBe('2026-08-01');
206
+ expect(r.bars.find(b => b.id === 'A')?.endDate).toBe('2026-08-10');
207
+ expect(r.bars.find(b => b.id === 'B')?.startDate).toBe('2026-08-12');
208
+ // Pinned mode does not compute CPM → critical flag is false.
209
+ expect(r.bars.find(b => b.id === 'A')?.isCritical).toBe(false);
210
+ });
211
+ it('still emits links between pinned leaves', () => {
212
+ const doc = {
213
+ notation: 'activities',
214
+ activities: [
215
+ { id: 'A', name: 'A', start_date: '2026-08-01', end_date: '2026-08-10' },
216
+ { id: 'B', name: 'B', start_date: '2026-08-12', end_date: '2026-08-20', predecessors: ['A'] },
217
+ ],
218
+ };
219
+ const r = assertRenders(computeGanttLayout(doc));
220
+ expect(r.links).toHaveLength(1);
221
+ expect(r.links[0].sourceId).toBe('A');
222
+ expect(r.links[0].targetId).toBe('B');
223
+ });
224
+ });