@crvouga/atlas 0.0.0-stage → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (606) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/LICENSE +21 -0
  3. package/README.md +649 -2
  4. package/dist/bun/src/driver.d.ts +67 -0
  5. package/dist/bun/src/driver.js +90 -0
  6. package/dist/bun/src/index.d.ts +2 -0
  7. package/dist/bun/src/index.js +2 -0
  8. package/dist/core/src/cli.d.ts +1 -0
  9. package/dist/core/src/cli.js +177 -0
  10. package/dist/core/src/config.d.ts +477 -0
  11. package/dist/core/src/config.js +100 -0
  12. package/dist/core/src/events/cloudevents.d.ts +56 -0
  13. package/dist/core/src/events/cloudevents.js +152 -0
  14. package/dist/core/src/graph.d.ts +113 -0
  15. package/dist/core/src/graph.js +221 -0
  16. package/dist/core/src/index.d.ts +17 -0
  17. package/dist/core/src/index.js +17 -0
  18. package/dist/core/src/lint.d.ts +53 -0
  19. package/dist/core/src/lint.js +138 -0
  20. package/dist/core/src/plan.d.ts +109 -0
  21. package/dist/core/src/plan.js +324 -0
  22. package/dist/core/src/privacy.d.ts +8 -0
  23. package/dist/core/src/privacy.js +42 -0
  24. package/dist/core/src/report/formats.d.ts +69 -0
  25. package/dist/core/src/report/formats.js +109 -0
  26. package/dist/core/src/report/markdown.d.ts +8 -0
  27. package/dist/core/src/report/markdown.js +140 -0
  28. package/dist/core/src/run/clients.d.ts +42 -0
  29. package/dist/core/src/run/clients.js +250 -0
  30. package/dist/core/src/run/execute.d.ts +854 -0
  31. package/dist/core/src/run/execute.js +877 -0
  32. package/dist/core/src/run/frames.d.ts +16 -0
  33. package/dist/core/src/run/frames.js +52 -0
  34. package/dist/core/src/run/http.d.ts +41 -0
  35. package/dist/core/src/run/http.js +71 -0
  36. package/dist/core/src/run/types.d.ts +248 -0
  37. package/dist/core/src/run/types.js +39 -0
  38. package/dist/core/src/signals.d.ts +110 -0
  39. package/dist/core/src/signals.js +121 -0
  40. package/dist/core/src/spec/compose.d.ts +28 -0
  41. package/dist/core/src/spec/compose.js +71 -0
  42. package/dist/core/src/spec/index.d.ts +5 -0
  43. package/dist/core/src/spec/index.js +5 -0
  44. package/dist/core/src/spec/load.d.ts +8 -0
  45. package/dist/core/src/spec/load.js +46 -0
  46. package/dist/core/src/spec/parse.d.ts +5 -0
  47. package/dist/core/src/spec/parse.js +32 -0
  48. package/dist/core/src/spec/scxml.d.ts +19 -0
  49. package/dist/core/src/spec/scxml.js +239 -0
  50. package/dist/core/src/spec/types.d.ts +96 -0
  51. package/dist/core/src/spec/types.js +25 -0
  52. package/dist/detox/src/index.d.ts +75 -0
  53. package/dist/detox/src/index.js +110 -0
  54. package/dist/playwright/src/driver.d.ts +44 -0
  55. package/dist/playwright/src/driver.js +82 -0
  56. package/dist/playwright/src/index.d.ts +3 -0
  57. package/dist/playwright/src/index.js +3 -0
  58. package/dist/playwright/src/media.d.ts +68 -0
  59. package/dist/playwright/src/media.js +283 -0
  60. package/dist/playwright/src/signals.d.ts +34 -0
  61. package/dist/playwright/src/signals.js +56 -0
  62. package/dist/reports/src/browser-navigation.d.ts +2 -0
  63. package/dist/reports/src/browser-navigation.js +28 -0
  64. package/dist/reports/src/bundle.d.ts +24 -0
  65. package/dist/reports/src/bundle.js +114 -0
  66. package/dist/reports/src/catalog.d.ts +37 -0
  67. package/dist/reports/src/catalog.js +127 -0
  68. package/dist/reports/src/clock.d.ts +14 -0
  69. package/dist/reports/src/clock.js +4 -0
  70. package/dist/reports/src/completion-order.d.ts +10 -0
  71. package/dist/reports/src/completion-order.js +76 -0
  72. package/dist/reports/src/evidence-live.d.ts +40 -0
  73. package/dist/reports/src/evidence-live.js +118 -0
  74. package/dist/reports/src/evidence-types.d.ts +90 -0
  75. package/dist/reports/src/evidence-types.js +15 -0
  76. package/dist/reports/src/evidence-view.d.ts +68 -0
  77. package/dist/reports/src/evidence-view.js +113 -0
  78. package/dist/reports/src/explorer-types.d.ts +56 -0
  79. package/dist/reports/src/explorer-types.js +0 -0
  80. package/dist/reports/src/explorer-versions.d.ts +4 -0
  81. package/dist/reports/src/explorer-versions.js +21 -0
  82. package/dist/reports/src/export-evidence.d.ts +11 -0
  83. package/dist/reports/src/export-evidence.js +94 -0
  84. package/dist/reports/src/fetcher.d.ts +50 -0
  85. package/dist/reports/src/fetcher.js +205 -0
  86. package/dist/reports/src/format-date.d.ts +1 -0
  87. package/dist/reports/src/format-date.js +10 -0
  88. package/dist/reports/src/format.d.ts +16 -0
  89. package/dist/reports/src/format.js +71 -0
  90. package/dist/reports/src/frames.d.ts +46 -0
  91. package/dist/reports/src/frames.js +245 -0
  92. package/dist/reports/src/hash.d.ts +17 -0
  93. package/dist/reports/src/hash.js +43 -0
  94. package/dist/reports/src/html-frame.d.ts +13 -0
  95. package/dist/reports/src/html-frame.js +63 -0
  96. package/dist/reports/src/http.d.ts +3 -0
  97. package/dist/reports/src/http.js +0 -0
  98. package/dist/reports/src/junit.d.ts +23 -0
  99. package/dist/reports/src/junit.js +119 -0
  100. package/dist/reports/src/keyboard.d.ts +16 -0
  101. package/dist/reports/src/keyboard.js +135 -0
  102. package/dist/reports/src/keys.d.ts +53 -0
  103. package/dist/reports/src/keys.js +122 -0
  104. package/dist/reports/src/layer-catalog.d.ts +5 -0
  105. package/dist/reports/src/layer-catalog.js +39 -0
  106. package/dist/reports/src/layer-merge.d.ts +43 -0
  107. package/dist/reports/src/layer-merge.js +154 -0
  108. package/dist/reports/src/layer-source.d.ts +6 -0
  109. package/dist/reports/src/layer-source.js +32 -0
  110. package/dist/reports/src/layer-store.d.ts +10 -0
  111. package/dist/reports/src/layer-store.js +36 -0
  112. package/dist/reports/src/layer-types.d.ts +32 -0
  113. package/dist/reports/src/layer-types.js +0 -0
  114. package/dist/reports/src/layers-route.d.ts +20 -0
  115. package/dist/reports/src/layers-route.js +259 -0
  116. package/dist/reports/src/namespace-guard.d.ts +11 -0
  117. package/dist/reports/src/namespace-guard.js +40 -0
  118. package/dist/reports/src/navigation.d.ts +8 -0
  119. package/dist/reports/src/navigation.js +0 -0
  120. package/dist/reports/src/object-io.d.ts +9 -0
  121. package/dist/reports/src/object-io.js +41 -0
  122. package/dist/reports/src/object-store.d.ts +67 -0
  123. package/dist/reports/src/object-store.js +32 -0
  124. package/dist/reports/src/performance-part.d.ts +9 -0
  125. package/dist/reports/src/performance-part.js +42 -0
  126. package/dist/reports/src/performance.d.ts +13 -0
  127. package/dist/reports/src/performance.js +45 -0
  128. package/dist/reports/src/persist.d.ts +10 -0
  129. package/dist/reports/src/persist.js +38 -0
  130. package/dist/reports/src/phone.d.ts +5 -0
  131. package/dist/reports/src/phone.js +18 -0
  132. package/dist/reports/src/protocol.d.ts +78 -0
  133. package/dist/reports/src/protocol.js +36 -0
  134. package/dist/reports/src/publication.d.ts +4 -0
  135. package/dist/reports/src/publication.js +35 -0
  136. package/dist/reports/src/publish.d.ts +52 -0
  137. package/dist/reports/src/publish.js +189 -0
  138. package/dist/reports/src/read.d.ts +62 -0
  139. package/dist/reports/src/read.js +214 -0
  140. package/dist/reports/src/recording-bundle.d.ts +22 -0
  141. package/dist/reports/src/recording-bundle.js +4 -0
  142. package/dist/reports/src/recording-keys.d.ts +9 -0
  143. package/dist/reports/src/recording-keys.js +50 -0
  144. package/dist/reports/src/remote-report.d.ts +39 -0
  145. package/dist/reports/src/remote-report.js +304 -0
  146. package/dist/reports/src/report-loader.d.ts +22 -0
  147. package/dist/reports/src/report-loader.js +0 -0
  148. package/dist/reports/src/report-parts.d.ts +10 -0
  149. package/dist/reports/src/report-parts.js +112 -0
  150. package/dist/reports/src/report-types.d.ts +194 -0
  151. package/dist/reports/src/report-types.js +29 -0
  152. package/dist/reports/src/reports-explorer-route.d.ts +90 -0
  153. package/dist/reports/src/reports-explorer-route.js +65 -0
  154. package/dist/reports/src/reports-list-route.d.ts +23 -0
  155. package/dist/reports/src/reports-list-route.js +55 -0
  156. package/dist/reports/src/reports-route.d.ts +267 -0
  157. package/dist/reports/src/reports-route.js +204 -0
  158. package/dist/reports/src/reports-store.d.ts +11 -0
  159. package/dist/reports/src/reports-store.js +28 -0
  160. package/dist/reports/src/resilient-cache.d.ts +3 -0
  161. package/dist/reports/src/resilient-cache.js +33 -0
  162. package/dist/reports/src/retention.d.ts +9 -0
  163. package/dist/reports/src/retention.js +16 -0
  164. package/dist/reports/src/scenario.d.ts +15 -0
  165. package/dist/reports/src/scenario.js +35 -0
  166. package/dist/reports/src/snapshot-bundle.d.ts +18 -0
  167. package/dist/reports/src/snapshot-bundle.js +94 -0
  168. package/dist/reports/src/snapshot-surface.d.ts +29 -0
  169. package/dist/reports/src/snapshot-surface.js +339 -0
  170. package/dist/reports/src/source-content.d.ts +30 -0
  171. package/dist/reports/src/source-content.js +63 -0
  172. package/dist/reports/src/source-entries.d.ts +4 -0
  173. package/dist/reports/src/source-entries.js +34 -0
  174. package/dist/reports/src/source-part.d.ts +4 -0
  175. package/dist/reports/src/source-part.js +66 -0
  176. package/dist/reports/src/source-reader.d.ts +6 -0
  177. package/dist/reports/src/source-reader.js +17 -0
  178. package/dist/reports/src/spec-evidence.d.ts +6 -0
  179. package/dist/reports/src/spec-evidence.js +20 -0
  180. package/dist/reports/src/spec-inventory.d.ts +14 -0
  181. package/dist/reports/src/spec-inventory.js +75 -0
  182. package/dist/reports/src/status.d.ts +4 -0
  183. package/dist/reports/src/status.js +8 -0
  184. package/dist/reports/src/store-prefix.d.ts +21 -0
  185. package/dist/reports/src/store-prefix.js +53 -0
  186. package/dist/reports/src/test-catalog-entity.d.ts +3 -0
  187. package/dist/reports/src/test-catalog-entity.js +34 -0
  188. package/dist/reports/src/types.d.ts +5 -0
  189. package/dist/reports/src/types.js +0 -0
  190. package/dist/reports/src/use-report-nav.d.ts +25 -0
  191. package/dist/reports/src/use-report-nav.js +181 -0
  192. package/dist/schema/src/common.d.ts +57 -0
  193. package/dist/schema/src/common.js +32 -0
  194. package/dist/schema/src/index.d.ts +5 -0
  195. package/dist/schema/src/index.js +5 -0
  196. package/dist/schema/src/manifest.d.ts +457 -0
  197. package/dist/schema/src/manifest.js +247 -0
  198. package/dist/schema/src/report-sources.d.ts +68 -0
  199. package/dist/schema/src/report-sources.js +53 -0
  200. package/dist/schema/src/runs-index.d.ts +52 -0
  201. package/dist/schema/src/runs-index.js +24 -0
  202. package/dist/schema/src/spec.d.ts +245 -0
  203. package/dist/schema/src/spec.js +133 -0
  204. package/dist/vitest/src/index.d.ts +22 -0
  205. package/dist/vitest/src/index.js +33 -0
  206. package/dist/webdriver/src/driver.d.ts +69 -0
  207. package/dist/webdriver/src/driver.js +230 -0
  208. package/dist/webdriver/src/index.d.ts +11 -0
  209. package/dist/webdriver/src/index.js +10 -0
  210. package/dist/webdriver/src/protocol.d.ts +60 -0
  211. package/dist/webdriver/src/protocol.js +146 -0
  212. package/package.json +181 -4
  213. package/packages/bun/README.md +69 -0
  214. package/packages/core/README.md +78 -0
  215. package/packages/core/bin/atlas.mjs +8 -0
  216. package/packages/detox/README.md +73 -0
  217. package/packages/playwright/README.md +77 -0
  218. package/packages/reports/README.md +47 -0
  219. package/packages/reports/STORAGE.md +73 -0
  220. package/packages/schema/README.md +23 -0
  221. package/packages/schema/schemas/atlas-change.schema.json +34 -0
  222. package/packages/schema/schemas/atlas.schema.json +32 -0
  223. package/packages/schema/schemas/cloud-event.schema.json +40 -0
  224. package/packages/schema/schemas/journeys.schema.json +17 -0
  225. package/packages/schema/schemas/manifest.schema.json +160 -0
  226. package/packages/schema/schemas/path-record.schema.json +151 -0
  227. package/packages/schema/schemas/report-sources.schema.json +95 -0
  228. package/packages/schema/schemas/run-info.schema.json +176 -0
  229. package/packages/schema/schemas/run-summary.schema.json +206 -0
  230. package/packages/schema/schemas/runs-index.schema.json +23 -0
  231. package/packages/schema/schemas/state-node.schema.json +238 -0
  232. package/packages/schema/schemas/state-record.schema.json +354 -0
  233. package/packages/schema/schemas/timeline-entry.schema.json +124 -0
  234. package/packages/schema/schemas/transition-record.schema.json +125 -0
  235. package/packages/visualizer/README.md +154 -0
  236. package/packages/visualizer/bin/atlas-visualizer.mjs +176 -0
  237. package/packages/visualizer/example/specs/coffee/ordering/checkout.scxml +82 -0
  238. package/packages/visualizer/example/specs/coffee/ordering/journeys.yaml +129 -0
  239. package/packages/visualizer/example/specs/coffee/ordering/known-issues.md +14 -0
  240. package/packages/visualizer/example/specs/coffee/ordering/machine.yaml +158 -0
  241. package/packages/visualizer/example/specs/coffee/ordering/open-questions.md +25 -0
  242. package/packages/visualizer/fixtures/failures/runs/2026-10-06T14-40-00-000Z-showcase/chart.mmd +67 -0
  243. package/packages/visualizer/fixtures/failures/runs/2026-10-06T14-40-00-000Z-showcase/ctrf.json +245 -0
  244. package/packages/visualizer/fixtures/failures/runs/2026-10-06T14-40-00-000Z-showcase/junit.xml +12 -0
  245. package/packages/visualizer/fixtures/failures/runs/2026-10-06T14-40-00-000Z-showcase/manifest.json +2003 -0
  246. package/packages/visualizer/fixtures/failures/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/001.vtt +9 -0
  247. package/packages/visualizer/fixtures/failures/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/002.vtt +9 -0
  248. package/packages/visualizer/fixtures/failures/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/003.vtt +9 -0
  249. package/packages/visualizer/fixtures/failures/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/004.vtt +9 -0
  250. package/packages/visualizer/fixtures/failures/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/005.vtt +17 -0
  251. package/packages/visualizer/fixtures/failures/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/006.vtt +9 -0
  252. package/packages/visualizer/fixtures/failures/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/007.vtt +17 -0
  253. package/packages/visualizer/fixtures/failures/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/008.vtt +17 -0
  254. package/packages/visualizer/fixtures/failures/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/009.vtt +13 -0
  255. package/packages/visualizer/fixtures/failures/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/010.vtt +9 -0
  256. package/packages/visualizer/fixtures/failures/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/011.vtt +9 -0
  257. package/packages/visualizer/fixtures/failures/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/012.vtt +9 -0
  258. package/packages/visualizer/fixtures/failures/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/013.vtt +9 -0
  259. package/packages/visualizer/fixtures/failures/runs/2026-10-06T14-40-00-000Z-showcase/media/clips/journey-1/01.mp4 +0 -0
  260. package/packages/visualizer/fixtures/failures/runs/2026-10-06T14-40-00-000Z-showcase/media/clips/journey-1/01.poster.webp +0 -0
  261. package/packages/visualizer/fixtures/failures/runs/2026-10-06T14-40-00-000Z-showcase/media/clips/journey-1/02.mp4 +0 -0
  262. package/packages/visualizer/fixtures/failures/runs/2026-10-06T14-40-00-000Z-showcase/media/clips/journey-1/02.poster.webp +0 -0
  263. package/packages/visualizer/fixtures/failures/runs/2026-10-06T14-40-00-000Z-showcase/media/clips/journey-1/03.mp4 +0 -0
  264. package/packages/visualizer/fixtures/failures/runs/2026-10-06T14-40-00-000Z-showcase/media/clips/journey-1/03.poster.webp +0 -0
  265. package/packages/visualizer/fixtures/failures/runs/2026-10-06T14-40-00-000Z-showcase/media/clips/journey-1/04.mp4 +0 -0
  266. package/packages/visualizer/fixtures/failures/runs/2026-10-06T14-40-00-000Z-showcase/media/clips/journey-1/04.poster.webp +0 -0
  267. package/packages/visualizer/fixtures/failures/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/01.png +0 -0
  268. package/packages/visualizer/fixtures/failures/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/01.webp +0 -0
  269. package/packages/visualizer/fixtures/failures/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/02.png +0 -0
  270. package/packages/visualizer/fixtures/failures/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/02.webp +0 -0
  271. package/packages/visualizer/fixtures/failures/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/03.png +0 -0
  272. package/packages/visualizer/fixtures/failures/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/03.webp +0 -0
  273. package/packages/visualizer/fixtures/failures/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/04.png +0 -0
  274. package/packages/visualizer/fixtures/failures/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/04.webp +0 -0
  275. package/packages/visualizer/fixtures/failures/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/05.png +0 -0
  276. package/packages/visualizer/fixtures/failures/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/05.webp +0 -0
  277. package/packages/visualizer/fixtures/failures/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-2/01.png +0 -0
  278. package/packages/visualizer/fixtures/failures/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-2/01.webp +0 -0
  279. package/packages/visualizer/fixtures/failures/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-3/failed-01.png +0 -0
  280. package/packages/visualizer/fixtures/failures/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-3/failed-01.webp +0 -0
  281. package/packages/visualizer/fixtures/failures/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-4/01.png +0 -0
  282. package/packages/visualizer/fixtures/failures/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-4/01.webp +0 -0
  283. package/packages/visualizer/fixtures/failures/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-5/01.png +0 -0
  284. package/packages/visualizer/fixtures/failures/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-5/01.webp +0 -0
  285. package/packages/visualizer/fixtures/failures/specs/coffee/ordering/checkout.scxml +82 -0
  286. package/packages/visualizer/fixtures/failures/specs/coffee/ordering/journeys.yaml +129 -0
  287. package/packages/visualizer/fixtures/failures/specs/coffee/ordering/known-issues.md +14 -0
  288. package/packages/visualizer/fixtures/failures/specs/coffee/ordering/machine.yaml +158 -0
  289. package/packages/visualizer/fixtures/failures/specs/coffee/ordering/open-questions.md +25 -0
  290. package/packages/visualizer/fixtures/full/runs/2026-10-02T09-10-00-000Z-fast/chart.mmd +67 -0
  291. package/packages/visualizer/fixtures/full/runs/2026-10-02T09-10-00-000Z-fast/ctrf.json +247 -0
  292. package/packages/visualizer/fixtures/full/runs/2026-10-02T09-10-00-000Z-fast/junit.xml +12 -0
  293. package/packages/visualizer/fixtures/full/runs/2026-10-02T09-10-00-000Z-fast/manifest.json +1875 -0
  294. package/packages/visualizer/fixtures/full/runs/2026-10-04T16-20-00-000Z-showcase/chart.mmd +67 -0
  295. package/packages/visualizer/fixtures/full/runs/2026-10-04T16-20-00-000Z-showcase/ctrf.json +244 -0
  296. package/packages/visualizer/fixtures/full/runs/2026-10-04T16-20-00-000Z-showcase/junit.xml +12 -0
  297. package/packages/visualizer/fixtures/full/runs/2026-10-04T16-20-00-000Z-showcase/manifest.json +1977 -0
  298. package/packages/visualizer/fixtures/full/runs/2026-10-04T16-20-00-000Z-showcase/media/captions/001.vtt +9 -0
  299. package/packages/visualizer/fixtures/full/runs/2026-10-04T16-20-00-000Z-showcase/media/captions/002.vtt +9 -0
  300. package/packages/visualizer/fixtures/full/runs/2026-10-04T16-20-00-000Z-showcase/media/captions/003.vtt +9 -0
  301. package/packages/visualizer/fixtures/full/runs/2026-10-04T16-20-00-000Z-showcase/media/captions/004.vtt +9 -0
  302. package/packages/visualizer/fixtures/full/runs/2026-10-04T16-20-00-000Z-showcase/media/captions/005.vtt +17 -0
  303. package/packages/visualizer/fixtures/full/runs/2026-10-04T16-20-00-000Z-showcase/media/captions/006.vtt +9 -0
  304. package/packages/visualizer/fixtures/full/runs/2026-10-04T16-20-00-000Z-showcase/media/clips/journey-1/01.mp4 +0 -0
  305. package/packages/visualizer/fixtures/full/runs/2026-10-04T16-20-00-000Z-showcase/media/clips/journey-1/01.poster.webp +0 -0
  306. package/packages/visualizer/fixtures/full/runs/2026-10-04T16-20-00-000Z-showcase/media/clips/journey-1/02.mp4 +0 -0
  307. package/packages/visualizer/fixtures/full/runs/2026-10-04T16-20-00-000Z-showcase/media/clips/journey-1/02.poster.webp +0 -0
  308. package/packages/visualizer/fixtures/full/runs/2026-10-04T16-20-00-000Z-showcase/media/paths/journey-1/01.png +0 -0
  309. package/packages/visualizer/fixtures/full/runs/2026-10-04T16-20-00-000Z-showcase/media/paths/journey-1/01.webp +0 -0
  310. package/packages/visualizer/fixtures/full/runs/2026-10-04T16-20-00-000Z-showcase/media/paths/journey-1/02.png +0 -0
  311. package/packages/visualizer/fixtures/full/runs/2026-10-04T16-20-00-000Z-showcase/media/paths/journey-1/02.webp +0 -0
  312. package/packages/visualizer/fixtures/full/runs/2026-10-04T16-20-00-000Z-showcase/media/paths/journey-1/03.png +0 -0
  313. package/packages/visualizer/fixtures/full/runs/2026-10-04T16-20-00-000Z-showcase/media/paths/journey-1/03.webp +0 -0
  314. package/packages/visualizer/fixtures/full/runs/2026-10-04T16-20-00-000Z-showcase/media/paths/journey-2/01.png +0 -0
  315. package/packages/visualizer/fixtures/full/runs/2026-10-04T16-20-00-000Z-showcase/media/paths/journey-2/01.webp +0 -0
  316. package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/chart.mmd +67 -0
  317. package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/ctrf.json +472 -0
  318. package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/junit.xml +16 -0
  319. package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/manifest.json +3701 -0
  320. package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/001.vtt +9 -0
  321. package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/002.vtt +9 -0
  322. package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/003.vtt +9 -0
  323. package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/004.vtt +9 -0
  324. package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/005.vtt +9 -0
  325. package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/006.vtt +13 -0
  326. package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/007.vtt +9 -0
  327. package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/008.vtt +13 -0
  328. package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/009.vtt +17 -0
  329. package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/010.vtt +9 -0
  330. package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/011.vtt +13 -0
  331. package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/012.vtt +9 -0
  332. package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/013.vtt +9 -0
  333. package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/014.vtt +9 -0
  334. package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/015.vtt +9 -0
  335. package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/016.vtt +9 -0
  336. package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/017.vtt +9 -0
  337. package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/018.vtt +17 -0
  338. package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/019.vtt +9 -0
  339. package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/020.vtt +9 -0
  340. package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/021.vtt +9 -0
  341. package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/022.vtt +17 -0
  342. package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/023.vtt +17 -0
  343. package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/024.vtt +13 -0
  344. package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/025.vtt +9 -0
  345. package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/026.vtt +9 -0
  346. package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/027.vtt +9 -0
  347. package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/028.vtt +9 -0
  348. package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/clips/journey-1/01.mp4 +0 -0
  349. package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/clips/journey-1/01.poster.webp +0 -0
  350. package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/clips/journey-1/02.mp4 +0 -0
  351. package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/clips/journey-1/02.poster.webp +0 -0
  352. package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/clips/journey-1/03.mp4 +0 -0
  353. package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/clips/journey-1/03.poster.webp +0 -0
  354. package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/clips/journey-1/04.mp4 +0 -0
  355. package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/clips/journey-1/04.poster.webp +0 -0
  356. package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/01.png +0 -0
  357. package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/01.webp +0 -0
  358. package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/02.png +0 -0
  359. package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/02.webp +0 -0
  360. package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/03.png +0 -0
  361. package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/03.webp +0 -0
  362. package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/04.png +0 -0
  363. package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/04.webp +0 -0
  364. package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/05.png +0 -0
  365. package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/05.webp +0 -0
  366. package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/06.png +0 -0
  367. package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/06.webp +0 -0
  368. package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/07.png +0 -0
  369. package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/07.webp +0 -0
  370. package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/08.png +0 -0
  371. package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/08.webp +0 -0
  372. package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/09.png +0 -0
  373. package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/09.webp +0 -0
  374. package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/10.png +0 -0
  375. package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/10.webp +0 -0
  376. package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/11.png +0 -0
  377. package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/11.webp +0 -0
  378. package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/12.png +0 -0
  379. package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/12.webp +0 -0
  380. package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/13.png +0 -0
  381. package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/13.webp +0 -0
  382. package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/14.png +0 -0
  383. package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/14.webp +0 -0
  384. package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-2/01.png +0 -0
  385. package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-2/01.webp +0 -0
  386. package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-3/01.png +0 -0
  387. package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-3/01.webp +0 -0
  388. package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-4/01.png +0 -0
  389. package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-4/01.webp +0 -0
  390. package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-5/01.png +0 -0
  391. package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-5/01.webp +0 -0
  392. package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-6/01.png +0 -0
  393. package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-6/01.webp +0 -0
  394. package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-7/01.png +0 -0
  395. package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-7/01.webp +0 -0
  396. package/packages/visualizer/fixtures/full/specs/coffee/ordering/checkout.scxml +82 -0
  397. package/packages/visualizer/fixtures/full/specs/coffee/ordering/journeys.yaml +129 -0
  398. package/packages/visualizer/fixtures/full/specs/coffee/ordering/known-issues.md +14 -0
  399. package/packages/visualizer/fixtures/full/specs/coffee/ordering/machine.yaml +158 -0
  400. package/packages/visualizer/fixtures/full/specs/coffee/ordering/open-questions.md +25 -0
  401. package/packages/visualizer/fixtures/large/runs/2026-10-06T14-40-00-000Z-fast/ctrf.json +1280 -0
  402. package/packages/visualizer/fixtures/large/runs/2026-10-06T14-40-00-000Z-fast/junit.xml +26 -0
  403. package/packages/visualizer/fixtures/large/runs/2026-10-06T14-40-00-000Z-fast/manifest.json +28393 -0
  404. package/packages/visualizer/fixtures/large/specs/accounts/sign-up/identity-check.machine.yaml +397 -0
  405. package/packages/visualizer/fixtures/large/specs/accounts/sign-up/journeys.yaml +129 -0
  406. package/packages/visualizer/fixtures/large/specs/accounts/sign-up/machine.yaml +830 -0
  407. package/packages/visualizer/fixtures/large/specs/atlas.yaml +65 -0
  408. package/packages/visualizer/fixtures/large/specs/billing/invoices/journeys.yaml +132 -0
  409. package/packages/visualizer/fixtures/large/specs/billing/invoices/machine.yaml +830 -0
  410. package/packages/visualizer/fixtures/large/specs/billing/invoices/refund-handling.scxml +288 -0
  411. package/packages/visualizer/fixtures/large/specs/catalog/browsing/journeys.yaml +126 -0
  412. package/packages/visualizer/fixtures/large/specs/catalog/browsing/machine.yaml +831 -0
  413. package/packages/visualizer/fixtures/large/specs/catalog/browsing/product-comparison.machine.yaml +395 -0
  414. package/packages/visualizer/fixtures/large/specs/orders/checkout/journeys.yaml +120 -0
  415. package/packages/visualizer/fixtures/large/specs/orders/checkout/machine.yaml +724 -0
  416. package/packages/visualizer/fixtures/large/specs/orders/checkout/open-questions.md +16 -0
  417. package/packages/visualizer/fixtures/large/specs/orders/checkout/payment.scxml +288 -0
  418. package/packages/visualizer/fixtures/large/specs/shipping/deliveries/delivery-scheduling.machine.yaml +397 -0
  419. package/packages/visualizer/fixtures/large/specs/shipping/deliveries/journeys.yaml +132 -0
  420. package/packages/visualizer/fixtures/large/specs/shipping/deliveries/known-issues.md +13 -0
  421. package/packages/visualizer/fixtures/large/specs/shipping/deliveries/machine.yaml +830 -0
  422. package/packages/visualizer/fixtures/large/specs/support/tickets/escalation.machine.yaml +395 -0
  423. package/packages/visualizer/fixtures/large/specs/support/tickets/journeys.yaml +123 -0
  424. package/packages/visualizer/fixtures/large/specs/support/tickets/machine.yaml +720 -0
  425. package/packages/visualizer/fixtures/malformed/runs/2026-10-06T10-00-00-000Z-showcase/manifest.json +68 -0
  426. package/packages/visualizer/fixtures/malformed/runs/2026-10-06T11-00-00-000Z-showcase/ctrf.json +50 -0
  427. package/packages/visualizer/fixtures/malformed/runs/2026-10-06T11-00-00-000Z-showcase/junit.xml +6 -0
  428. package/packages/visualizer/fixtures/malformed/runs/2026-10-06T11-00-00-000Z-showcase/manifest.json +1281 -0
  429. package/packages/visualizer/fixtures/malformed/runs/2026-10-06T12-00-00-000Z-showcase/ctrf.json +50 -0
  430. package/packages/visualizer/fixtures/malformed/runs/2026-10-06T12-00-00-000Z-showcase/junit.xml +6 -0
  431. package/packages/visualizer/fixtures/malformed/runs/2026-10-06T12-00-00-000Z-showcase/manifest.json +1232 -0
  432. package/packages/visualizer/fixtures/malformed/runs/2026-10-06T13-00-00-000Z-showcase/manifest.json +1244 -0
  433. package/packages/visualizer/fixtures/malformed/specs/coffee/ordering/checkout.scxml +82 -0
  434. package/packages/visualizer/fixtures/malformed/specs/coffee/ordering/journeys.yaml +134 -0
  435. package/packages/visualizer/fixtures/malformed/specs/coffee/ordering/known-issues.md +14 -0
  436. package/packages/visualizer/fixtures/malformed/specs/coffee/ordering/loyalty.scxml +4 -0
  437. package/packages/visualizer/fixtures/malformed/specs/coffee/ordering/machine.yaml +156 -0
  438. package/packages/visualizer/fixtures/malformed/specs/coffee/ordering/open-questions.md +25 -0
  439. package/packages/visualizer/fixtures/partial/runs/2026-10-06T14-40-00-000Z-showcase/chart.mmd +67 -0
  440. package/packages/visualizer/fixtures/partial/runs/2026-10-06T14-40-00-000Z-showcase/ctrf.json +149 -0
  441. package/packages/visualizer/fixtures/partial/runs/2026-10-06T14-40-00-000Z-showcase/junit.xml +9 -0
  442. package/packages/visualizer/fixtures/partial/runs/2026-10-06T14-40-00-000Z-showcase/manifest.json +1718 -0
  443. package/packages/visualizer/fixtures/partial/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/001.vtt +9 -0
  444. package/packages/visualizer/fixtures/partial/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/002.vtt +17 -0
  445. package/packages/visualizer/fixtures/partial/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/003.vtt +17 -0
  446. package/packages/visualizer/fixtures/partial/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/004.vtt +9 -0
  447. package/packages/visualizer/fixtures/partial/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/005.vtt +9 -0
  448. package/packages/visualizer/fixtures/partial/runs/2026-10-06T14-40-00-000Z-showcase/media/clips/journey-1/01.mp4 +0 -0
  449. package/packages/visualizer/fixtures/partial/runs/2026-10-06T14-40-00-000Z-showcase/media/clips/journey-1/01.poster.webp +0 -0
  450. package/packages/visualizer/fixtures/partial/runs/2026-10-06T14-40-00-000Z-showcase/media/clips/journey-1/02.mp4 +0 -0
  451. package/packages/visualizer/fixtures/partial/runs/2026-10-06T14-40-00-000Z-showcase/media/clips/journey-1/02.poster.webp +0 -0
  452. package/packages/visualizer/fixtures/partial/runs/2026-10-06T14-40-00-000Z-showcase/media/clips/journey-1/03.mp4 +0 -0
  453. package/packages/visualizer/fixtures/partial/runs/2026-10-06T14-40-00-000Z-showcase/media/clips/journey-1/03.poster.webp +0 -0
  454. package/packages/visualizer/fixtures/partial/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/01.png +0 -0
  455. package/packages/visualizer/fixtures/partial/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/01.webp +0 -0
  456. package/packages/visualizer/fixtures/partial/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/02.png +0 -0
  457. package/packages/visualizer/fixtures/partial/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/02.webp +0 -0
  458. package/packages/visualizer/fixtures/partial/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/03.png +0 -0
  459. package/packages/visualizer/fixtures/partial/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/03.webp +0 -0
  460. package/packages/visualizer/fixtures/partial/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/05.png +0 -0
  461. package/packages/visualizer/fixtures/partial/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/05.webp +0 -0
  462. package/packages/visualizer/fixtures/partial/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-3/01.png +0 -0
  463. package/packages/visualizer/fixtures/partial/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-3/01.webp +0 -0
  464. package/packages/visualizer/fixtures/partial/runs/2026-10-06T15-05-00-000Z-showcase/media/paths/journey-1/00-start-01.png +0 -0
  465. package/packages/visualizer/fixtures/partial/runs/2026-10-06T15-05-00-000Z-showcase/media/paths/journey-1/00-start-01.webp +0 -0
  466. package/packages/visualizer/fixtures/partial/runs/2026-10-06T15-05-00-000Z-showcase/media/paths/journey-1/02.png +0 -0
  467. package/packages/visualizer/fixtures/partial/runs/2026-10-06T15-05-00-000Z-showcase/media/paths/journey-1/02.webp +0 -0
  468. package/packages/visualizer/fixtures/partial/specs/coffee/ordering/checkout.scxml +82 -0
  469. package/packages/visualizer/fixtures/partial/specs/coffee/ordering/journeys.yaml +129 -0
  470. package/packages/visualizer/fixtures/partial/specs/coffee/ordering/known-issues.md +14 -0
  471. package/packages/visualizer/fixtures/partial/specs/coffee/ordering/machine.yaml +158 -0
  472. package/packages/visualizer/fixtures/partial/specs/coffee/ordering/open-questions.md +25 -0
  473. package/packages/visualizer/fixtures/removed/runs/2026-10-06T14-40-00-000Z-showcase/ctrf.json +79 -0
  474. package/packages/visualizer/fixtures/removed/runs/2026-10-06T14-40-00-000Z-showcase/junit.xml +7 -0
  475. package/packages/visualizer/fixtures/removed/runs/2026-10-06T14-40-00-000Z-showcase/manifest.json +1504 -0
  476. package/packages/visualizer/fixtures/removed/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/001.vtt +9 -0
  477. package/packages/visualizer/fixtures/removed/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/002.vtt +9 -0
  478. package/packages/visualizer/fixtures/removed/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/003.vtt +9 -0
  479. package/packages/visualizer/fixtures/removed/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/004.vtt +17 -0
  480. package/packages/visualizer/fixtures/removed/runs/2026-10-06T14-40-00-000Z-showcase/media/clips/journey-1/01.mp4 +0 -0
  481. package/packages/visualizer/fixtures/removed/runs/2026-10-06T14-40-00-000Z-showcase/media/clips/journey-1/01.poster.webp +0 -0
  482. package/packages/visualizer/fixtures/removed/runs/2026-10-06T14-40-00-000Z-showcase/media/clips/journey-1/02.mp4 +0 -0
  483. package/packages/visualizer/fixtures/removed/runs/2026-10-06T14-40-00-000Z-showcase/media/clips/journey-1/02.poster.webp +0 -0
  484. package/packages/visualizer/fixtures/removed/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/01.png +0 -0
  485. package/packages/visualizer/fixtures/removed/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/01.webp +0 -0
  486. package/packages/visualizer/fixtures/removed/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/02.png +0 -0
  487. package/packages/visualizer/fixtures/removed/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/02.webp +0 -0
  488. package/packages/visualizer/fixtures/removed/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/03.png +0 -0
  489. package/packages/visualizer/fixtures/removed/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/03.webp +0 -0
  490. package/packages/visualizer/fixtures/removed/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/04.png +0 -0
  491. package/packages/visualizer/fixtures/removed/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/04.webp +0 -0
  492. package/packages/visualizer/fixtures/removed/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-2/01.png +0 -0
  493. package/packages/visualizer/fixtures/removed/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-2/01.webp +0 -0
  494. package/packages/visualizer/fixtures/removed/specs/coffee/ordering/checkout.scxml +82 -0
  495. package/packages/visualizer/fixtures/removed/specs/coffee/ordering/journeys.yaml +129 -0
  496. package/packages/visualizer/fixtures/removed/specs/coffee/ordering/known-issues.md +14 -0
  497. package/packages/visualizer/fixtures/removed/specs/coffee/ordering/machine.yaml +158 -0
  498. package/packages/visualizer/fixtures/removed/specs/coffee/ordering/open-questions.md +25 -0
  499. package/packages/visualizer/fixtures/spec-only/runs/.gitkeep +0 -0
  500. package/packages/visualizer/fixtures/spec-only/specs/coffee/ordering/checkout.scxml +82 -0
  501. package/packages/visualizer/fixtures/spec-only/specs/coffee/ordering/journeys.yaml +129 -0
  502. package/packages/visualizer/fixtures/spec-only/specs/coffee/ordering/known-issues.md +14 -0
  503. package/packages/visualizer/fixtures/spec-only/specs/coffee/ordering/machine.yaml +158 -0
  504. package/packages/visualizer/fixtures/spec-only/specs/coffee/ordering/open-questions.md +25 -0
  505. package/packages/visualizer/index.html +13 -0
  506. package/packages/visualizer/scripts/make-fixtures.ts +1490 -0
  507. package/packages/visualizer/scripts/publish-data.ts +22 -0
  508. package/packages/visualizer/src/app/AppShell.tsx +56 -0
  509. package/packages/visualizer/src/app/atlas-context.tsx +23 -0
  510. package/packages/visualizer/src/app/gazetteer.module.css +97 -0
  511. package/packages/visualizer/src/app/gazetteer.tsx +105 -0
  512. package/packages/visualizer/src/app/report-sources.tsx +122 -0
  513. package/packages/visualizer/src/app/router.tsx +69 -0
  514. package/packages/visualizer/src/app/search-query.tsx +14 -0
  515. package/packages/visualizer/src/components/DataIssues.module.css +136 -0
  516. package/packages/visualizer/src/components/DataIssues.tsx +70 -0
  517. package/packages/visualizer/src/components/Header.module.css +189 -0
  518. package/packages/visualizer/src/components/Header.tsx +127 -0
  519. package/packages/visualizer/src/components/Legend.module.css +84 -0
  520. package/packages/visualizer/src/components/Legend.tsx +50 -0
  521. package/packages/visualizer/src/components/Screen.module.css +312 -0
  522. package/packages/visualizer/src/components/Screen.tsx +141 -0
  523. package/packages/visualizer/src/components/Status.module.css +116 -0
  524. package/packages/visualizer/src/components/Status.tsx +60 -0
  525. package/packages/visualizer/src/components/icons.tsx +156 -0
  526. package/packages/visualizer/src/components/labels.tsx +22 -0
  527. package/packages/visualizer/src/data/adapters/adapter.ts +112 -0
  528. package/packages/visualizer/src/data/adapters/context.tsx +11 -0
  529. package/packages/visualizer/src/data/adapters/index.ts +29 -0
  530. package/packages/visualizer/src/data/adapters/report-backend.test.ts +96 -0
  531. package/packages/visualizer/src/data/adapters/report-backend.ts +42 -0
  532. package/packages/visualizer/src/data/fixtures.test.ts +407 -0
  533. package/packages/visualizer/src/data/model/build.ts +418 -0
  534. package/packages/visualizer/src/data/model/event-kind.test.ts +59 -0
  535. package/packages/visualizer/src/data/model/event-kind.ts +54 -0
  536. package/packages/visualizer/src/data/model/index.ts +3 -0
  537. package/packages/visualizer/src/data/model/journeys.test.ts +159 -0
  538. package/packages/visualizer/src/data/model/journeys.ts +29 -0
  539. package/packages/visualizer/src/data/model/simulation.test.ts +115 -0
  540. package/packages/visualizer/src/data/model/simulation.ts +187 -0
  541. package/packages/visualizer/src/data/model/status.test.ts +34 -0
  542. package/packages/visualizer/src/data/model/status.ts +43 -0
  543. package/packages/visualizer/src/data/model/types.ts +280 -0
  544. package/packages/visualizer/src/data/parse/issues.test.ts +82 -0
  545. package/packages/visualizer/src/data/parse/issues.ts +114 -0
  546. package/packages/visualizer/src/data/parse/manifest.ts +173 -0
  547. package/packages/visualizer/src/data/parse/markdown.test.ts +119 -0
  548. package/packages/visualizer/src/data/parse/markdown.ts +133 -0
  549. package/packages/visualizer/src/data/parse/spec.test.ts +106 -0
  550. package/packages/visualizer/src/data/parse/spec.ts +557 -0
  551. package/packages/visualizer/src/data/parse/structured.ts +44 -0
  552. package/packages/visualizer/src/data/queries.ts +369 -0
  553. package/packages/visualizer/src/data/testing/load-fixture.ts +63 -0
  554. package/packages/visualizer/src/evidence/api.ts +41 -0
  555. package/packages/visualizer/src/evidence/check-detail.tsx +257 -0
  556. package/packages/visualizer/src/evidence/decision-table.tsx +68 -0
  557. package/packages/visualizer/src/evidence/document-page.tsx +40 -0
  558. package/packages/visualizer/src/evidence/evidence-context.tsx +159 -0
  559. package/packages/visualizer/src/evidence/evidence.module.css +877 -0
  560. package/packages/visualizer/src/evidence/feature-page.tsx +88 -0
  561. package/packages/visualizer/src/evidence/format.ts +56 -0
  562. package/packages/visualizer/src/evidence/gherkin.ts +98 -0
  563. package/packages/visualizer/src/evidence/marks.tsx +118 -0
  564. package/packages/visualizer/src/evidence/model-page.tsx +60 -0
  565. package/packages/visualizer/src/evidence/model-trace.tsx +57 -0
  566. package/packages/visualizer/src/evidence/overview.tsx +134 -0
  567. package/packages/visualizer/src/evidence/quint.ts +45 -0
  568. package/packages/visualizer/src/evidence/recording-page.tsx +69 -0
  569. package/packages/visualizer/src/evidence/recording.module.css +464 -0
  570. package/packages/visualizer/src/evidence/recording.tsx +197 -0
  571. package/packages/visualizer/src/evidence/snapshot.tsx +108 -0
  572. package/packages/visualizer/src/evidence/sources-panel.tsx +134 -0
  573. package/packages/visualizer/src/evidence/sources.module.css +168 -0
  574. package/packages/visualizer/src/evidence/spec-header.tsx +64 -0
  575. package/packages/visualizer/src/evidence/spec-index.tsx +82 -0
  576. package/packages/visualizer/src/evidence/spec-page.tsx +92 -0
  577. package/packages/visualizer/src/evidence/spec-snapshot.tsx +26 -0
  578. package/packages/visualizer/src/layout/graph.test.ts +113 -0
  579. package/packages/visualizer/src/layout/graph.ts +324 -0
  580. package/packages/visualizer/src/layout/layout.ts +237 -0
  581. package/packages/visualizer/src/layout/use-chart-layout.ts +18 -0
  582. package/packages/visualizer/src/main.tsx +31 -0
  583. package/packages/visualizer/src/map/ChartMap.tsx +252 -0
  584. package/packages/visualizer/src/map/EventEdge.tsx +138 -0
  585. package/packages/visualizer/src/map/map.module.css +546 -0
  586. package/packages/visualizer/src/map/nodes.tsx +145 -0
  587. package/packages/visualizer/src/map/path.ts +24 -0
  588. package/packages/visualizer/src/reachable.test.ts +54 -0
  589. package/packages/visualizer/src/spec-kind.test.ts +27 -0
  590. package/packages/visualizer/src/spec-kind.ts +56 -0
  591. package/packages/visualizer/src/state/ui-store.ts +44 -0
  592. package/packages/visualizer/src/styles/global.css +105 -0
  593. package/packages/visualizer/src/styles/tokens.css +80 -0
  594. package/packages/visualizer/src/views/ChartPage.tsx +52 -0
  595. package/packages/visualizer/src/views/ProductPage.tsx +35 -0
  596. package/packages/visualizer/src/views/SidePanel.tsx +339 -0
  597. package/packages/visualizer/src/views/views.module.css +546 -0
  598. package/packages/visualizer/vite/atlas-dev-plugin.ts +169 -0
  599. package/packages/visualizer/vite/atlas-files.test.ts +119 -0
  600. package/packages/visualizer/vite/atlas-files.ts +199 -0
  601. package/packages/visualizer/vite/publish.test.ts +52 -0
  602. package/packages/visualizer/vite/publish.ts +99 -0
  603. package/packages/visualizer/vite/reports-dev-plugin.ts +82 -0
  604. package/packages/visualizer/vite.config.ts +13 -0
  605. package/packages/vitest/README.md +58 -0
  606. package/packages/webdriver/README.md +77 -0
@@ -0,0 +1,557 @@
1
+ import {
2
+ AtlasContextSchema,
3
+ AtlasFileSchema,
4
+ AtlasHandOffSchema,
5
+ JourneySchema,
6
+ LayoutFileSchema,
7
+ StateMetaShape,
8
+ type LayoutFile,
9
+ type StateMeta,
10
+ type StateType,
11
+ transitionId
12
+ } from '@crvouga/atlas/schema';
13
+ import { parseChart } from '@crvouga/atlas/spec';
14
+ import { z } from 'zod/v4';
15
+
16
+ import { createIssueSink, isRecord, parseFields, parseOne, type IssueSink } from './issues';
17
+ import { parseKnownIssues, parseOpenQuestions, type ParsedNote, type ParsedQuestion } from './markdown';
18
+ import { readStructured } from './structured';
19
+
20
+ export type SpecFile = { path: string; text: string | null; error?: string };
21
+
22
+ export type SpecSource = { ref: string | null; files: SpecFile[] };
23
+
24
+ export type ParsedTransition = {
25
+ id: string;
26
+ chartId: string;
27
+ source: string;
28
+ event: string;
29
+ rawTarget: string;
30
+ target: string | null;
31
+ /** A child chart's final state passing its event up to the parent. */
32
+ handOff: boolean;
33
+ /** For a collapsed child chart's own transition: the hand-off transition that carries it. */
34
+ carriedBy: string | null;
35
+ };
36
+
37
+ export type ParsedState = {
38
+ name: string;
39
+ chartId: string;
40
+ file: string;
41
+ parent: string | null;
42
+ depth: number;
43
+ type: StateType;
44
+ initial: string | null;
45
+ meta: StateMeta;
46
+ children: string[];
47
+ transitions: string[];
48
+ /** The chart this state runs: `invoke: { src }` (SCXML `<invoke>`), or the legacy `meta.childMachine`. */
49
+ childChartId: string | null;
50
+ /** The invoke that names the child chart, when it is declared the standard way. */
51
+ invoke: ParsedInvoke | null;
52
+ /** On a child chart's final state: the event its parent receives (`meta.doneEvent`, SCXML `atlas:done-event`). */
53
+ doneEvent: string | null;
54
+ };
55
+
56
+ export type ParsedInvoke = { src: string; id: string | null; onDone: string | null };
57
+
58
+ export type ParsedChart = {
59
+ id: string;
60
+ name: string;
61
+ file: string;
62
+ format: 'scxml' | 'xstate';
63
+ dir: string;
64
+ contextId: string;
65
+ meta: StateMeta;
66
+ initial: string | null;
67
+ rootStates: string[];
68
+ states: string[];
69
+ parent: { chartId: string; state: string } | null;
70
+ layout: LayoutFile | null;
71
+ type?: 'parallel';
72
+ explorationError?: string;
73
+ };
74
+
75
+ export type ParsedJourney = {
76
+ id: string;
77
+ name: string;
78
+ dir: string;
79
+ file: string;
80
+ description: string;
81
+ events: string[];
82
+ endsIn: string[];
83
+ owner: string | null;
84
+ tour: boolean;
85
+ };
86
+
87
+ export type ParsedContext = {
88
+ id: string;
89
+ name: string;
90
+ description: string;
91
+ owner: string | null;
92
+ directories: string[];
93
+ declared: boolean;
94
+ };
95
+
96
+ export type ParsedHandOff = { from: string; to: string; event: string; description: string };
97
+
98
+ export type SpecDocument = {
99
+ ref: string | null;
100
+ title: string;
101
+ description: string;
102
+ contexts: ParsedContext[];
103
+ handOffs: ParsedHandOff[];
104
+ charts: ParsedChart[];
105
+ states: Map<string, ParsedState>;
106
+ transitions: Map<string, ParsedTransition>;
107
+ journeys: ParsedJourney[];
108
+ questions: ParsedQuestion[];
109
+ knownIssues: ParsedNote[];
110
+ excluded: string[];
111
+ /** Directories holding charts, each with the charts it composes (top chart first). */
112
+ groups: { dir: string; chartIds: string[]; journeyIds: string[] }[];
113
+ };
114
+
115
+ const StateFieldsShape = {
116
+ id: z.string().min(1),
117
+ initial: z.string().min(1),
118
+ type: z.enum(['parallel', 'final'])
119
+ };
120
+
121
+ const MachineFieldsShape = { ...StateFieldsShape, id: z.string().min(1) };
122
+
123
+ function dirOf(path: string) {
124
+ const i = path.lastIndexOf('/');
125
+ return i < 0 ? '' : path.slice(0, i);
126
+ }
127
+
128
+ function baseOf(path: string) {
129
+ return path.slice(path.lastIndexOf('/') + 1);
130
+ }
131
+
132
+ export function slug(text: string) {
133
+ return text
134
+ .toLowerCase()
135
+ .replace(/[^a-z0-9]+/g, '-')
136
+ .replace(/^-|-$/g, '');
137
+ }
138
+
139
+ function titleCase(id: string) {
140
+ return id
141
+ .split(/[-_\s]+/)
142
+ .filter(Boolean)
143
+ .map((w) => w[0]!.toUpperCase() + w.slice(1))
144
+ .join(' ');
145
+ }
146
+
147
+ function layoutPathFor(chartFile: string) {
148
+ return chartFile.replace(/\.(ya?ml|json|scxml)$/i, '.layout.json');
149
+ }
150
+
151
+ function isScxmlFile(path: string) {
152
+ return /\.scxml$/i.test(path);
153
+ }
154
+
155
+ /** A chart in either open format: W3C SCXML, or XState machine config in YAML or JSON. */
156
+ export function isMachineFile(path: string) {
157
+ return isScxmlFile(path) || /(^|\/)([\w.-]+\.)?machine\.(ya?ml|json)$/.test(path);
158
+ }
159
+
160
+ /** `meta` fields that hold lists or maps; SCXML carries them as text in the `atlas:` namespace. */
161
+ const STRUCTURED_META = new Set(['events', 'source', 'checks', 'hints', 'gherkin', 'contracts', 'design', 'eventKinds', 'timeEvents', 'childFinalEvents']);
162
+ const LIST_META = new Set(['events', 'source', 'checks', 'hints', 'gherkin', 'contracts']);
163
+
164
+ /** SCXML metadata arrives as text: JSON for lists and maps, or one plain item of a list. */
165
+ function normalizeScxmlMeta(meta: unknown) {
166
+ if (!isRecord(meta)) return meta;
167
+ return Object.fromEntries(
168
+ Object.entries(meta).map(([key, value]) => {
169
+ if (typeof value !== 'string' || !STRUCTURED_META.has(key)) return [key, value];
170
+ const text = value.trim();
171
+ if (/^[[{]/.test(text)) {
172
+ try {
173
+ return [key, JSON.parse(text) as unknown];
174
+ } catch {
175
+ return [key, value];
176
+ }
177
+ }
178
+ return [key, LIST_META.has(key) ? [value] : value];
179
+ })
180
+ );
181
+ }
182
+
183
+ function normalizeScxmlStates(node: Record<string, unknown>) {
184
+ if (node.meta !== undefined) node.meta = normalizeScxmlMeta(node.meta);
185
+ if (isRecord(node.states)) for (const child of Object.values(node.states)) if (isRecord(child)) normalizeScxmlStates(child);
186
+ return node;
187
+ }
188
+
189
+ /** A chart file to plain data: SCXML through the Atlas reader, YAML and JSON as they are. */
190
+ function readChart(file: string, text: string, sink: IssueSink): unknown {
191
+ if (!isScxmlFile(file)) return readStructured(file, text, sink);
192
+ try {
193
+ return normalizeScxmlStates(structuredClone(parseChart(file, text).machine) as Record<string, unknown>);
194
+ } catch (error) {
195
+ sink.add('error', file, [], `This SCXML file could not be read, so it was skipped: ${error instanceof Error ? error.message : 'unknown problem'}.`);
196
+ return undefined;
197
+ }
198
+ }
199
+
200
+ function targetText(raw: unknown) {
201
+ if (typeof raw === 'string' && raw.trim()) return raw;
202
+ if (isRecord(raw) && typeof raw.target === 'string' && raw.target.trim()) return raw.target;
203
+ return null;
204
+ }
205
+
206
+ /** `invoke: { src, id, onDone }` or a list of them; the map follows the first. */
207
+ function readInvoke(raw: unknown, sink: IssueSink, file: string, path: (string | number)[]): ParsedInvoke | null {
208
+ if (raw === undefined) return null;
209
+ const list = Array.isArray(raw) ? raw : [raw];
210
+ if (list.length > 1) sink.add('warning', file, path, 'runs several charts at once; the map shows the first one.');
211
+ const first: unknown = list[0];
212
+ if (!isRecord(first) || typeof first.src !== 'string' || !first.src.trim()) {
213
+ sink.add('warning', file, Array.isArray(raw) ? [...path, 0] : path, 'should name the chart it runs in "src", so it is left off the map.');
214
+ return null;
215
+ }
216
+ return { src: first.src, id: typeof first.id === 'string' && first.id ? first.id : null, onDone: targetText(first.onDone) };
217
+ }
218
+
219
+ /** `machine.json` beside a `machine.yaml` is the same chart exported for Stately Studio. */
220
+ function shadowedJson(path: string, all: ReadonlySet<string>) {
221
+ return path.endsWith('.json') && (all.has(path.replace(/\.json$/, '.yaml')) || all.has(path.replace(/\.json$/, '.yml')));
222
+ }
223
+
224
+ type RawTarget = { target: string } | null;
225
+
226
+ function readTarget(raw: unknown, sink: IssueSink, file: string, path: (string | number)[]): RawTarget {
227
+ if (typeof raw === 'string' && raw.trim()) return { target: raw };
228
+ if (Array.isArray(raw)) {
229
+ sink.add('warning', file, path, 'has several possible destinations; the map shows the first one.');
230
+ return readTarget(raw[0], sink, file, [...path, 0]);
231
+ }
232
+ if (isRecord(raw) && typeof raw.target === 'string' && raw.target.trim()) return { target: raw.target };
233
+ sink.add('warning', file, path, 'has no destination screen, so it is left off the map.');
234
+ return null;
235
+ }
236
+
237
+ /** The map remains tolerant, but exploration must never silently erase executable conditions. */
238
+ function explorationIssue(raw: Record<string, unknown>): string | undefined {
239
+ const allowed = new Set(['id', 'initial', 'type', 'meta', 'states', 'on', 'invoke', 'description']);
240
+ const target = (value: unknown) => value === null || value === undefined || typeof value === 'string' || (isRecord(value) && typeof value.target === 'string' && Object.keys(value).every((key) => key === 'target'));
241
+ const visit = (node: Record<string, unknown>): string | undefined => {
242
+ const key = Object.keys(node).find((key) => !allowed.has(key));
243
+ if (key) return `“${key}” needs executable code and is outside Atlas’s pure exploration model.`;
244
+ if (node.type !== undefined && node.type !== 'parallel' && node.type !== 'final') return 'This state type is outside Atlas’s pure exploration model.';
245
+ if (node.on !== undefined && (!isRecord(node.on) || Object.values(node.on).some((value) => !target(value)))) return 'Conditional, multiple or invalid transition destinations cannot be explored safely.';
246
+ if (node.invoke !== undefined) {
247
+ const invokes = Array.isArray(node.invoke) ? node.invoke : [node.invoke];
248
+ if (invokes.length !== 1 || invokes.some((invoke) => !isRecord(invoke) || typeof invoke.src !== 'string' || Object.keys(invoke).some((key) => !['src', 'id', 'onDone'].includes(key)) || (invoke.onDone !== undefined && !target(invoke.onDone)))) return 'Only one statically named child chart per state can be explored safely.';
249
+ }
250
+ if (isRecord(node.states)) for (const child of Object.values(node.states)) {
251
+ if (!isRecord(child)) return 'A state with invalid details cannot be explored safely.';
252
+ const issue = visit(child);
253
+ if (issue) return issue;
254
+ }
255
+ return undefined;
256
+ };
257
+ return visit(raw);
258
+ }
259
+
260
+ /**
261
+ * The statechart files under the specs root, parsed one piece at a time: a broken state, journey
262
+ * or file is reported and skipped, and everything else still renders.
263
+ */
264
+ export function parseSpec(source: SpecSource, sink: IssueSink = createIssueSink()): SpecDocument {
265
+ const paths = new Set(source.files.map((f) => f.path));
266
+ const texts = new Map<string, string>();
267
+ for (const file of source.files) {
268
+ if (file.text === null) {
269
+ sink.add('error', file.path, [], `This file could not be loaded${file.error ? ` (${file.error})` : ''}. Its part of the map is missing until it loads.`);
270
+ } else {
271
+ texts.set(file.path, file.text);
272
+ }
273
+ }
274
+
275
+ const atlasPath = [...texts.keys()].find((p) => /^atlas\.ya?ml$/.test(p));
276
+ const atlasRaw = atlasPath ? readStructured(atlasPath, texts.get(atlasPath)!, sink) : undefined;
277
+ const atlas = atlasPath && atlasRaw !== undefined ? parseOne(AtlasFileSchema, atlasRaw, sink, atlasPath, []) : null;
278
+ const declaredContexts: ParsedContext[] = (atlas?.contexts ?? []).flatMap((raw, i) => {
279
+ const ctx = parseOne(AtlasContextSchema, raw, sink, atlasPath!, ['contexts', i]);
280
+ return ctx
281
+ ? [{ id: ctx.id, name: ctx.name ?? titleCase(ctx.id), description: ctx.description ?? '', owner: ctx.owner ?? null, directories: ctx.directories ?? [ctx.id], declared: true }]
282
+ : [];
283
+ });
284
+ const handOffs: ParsedHandOff[] = (atlas?.handOffs ?? []).flatMap((raw, i) => {
285
+ const h = parseOne(AtlasHandOffSchema, raw, sink, atlasPath!, ['handOffs', i]);
286
+ return h ? [{ from: h.from, to: h.to, event: h.event, description: h.description ?? '' }] : [];
287
+ });
288
+
289
+ const contexts = [...declaredContexts];
290
+ const contextFor = (dir: string) => {
291
+ const declared = declaredContexts
292
+ .filter((c) => c.directories.some((d) => dir === d || dir.startsWith(`${d}/`)))
293
+ .sort((a, b) => Math.max(...b.directories.map((d) => d.length)) - Math.max(...a.directories.map((d) => d.length)))[0];
294
+ if (declared) return declared.id;
295
+ const id = dir.split('/')[0] || 'product';
296
+ if (!contexts.some((c) => c.id === id)) {
297
+ contexts.push({ id, name: titleCase(id), description: '', owner: null, directories: [id], declared: false });
298
+ }
299
+ return id;
300
+ };
301
+
302
+ const charts: ParsedChart[] = [];
303
+ const states = new Map<string, ParsedState>();
304
+ const transitions = new Map<string, ParsedTransition>();
305
+ const idToName = new Map<string, Map<string, string>>();
306
+
307
+ const machineFiles = [...texts.keys()].filter((p) => isMachineFile(p) && !shadowedJson(p, paths)).sort((a, b) => {
308
+ const depth = (p: string) => (baseOf(p).startsWith('machine.') ? 0 : 1);
309
+ return dirOf(a).localeCompare(dirOf(b)) || depth(a) - depth(b) || a.localeCompare(b);
310
+ });
311
+
312
+ for (const file of machineFiles) {
313
+ const raw = readChart(file, texts.get(file)!, sink);
314
+ if (raw === undefined) continue;
315
+ if (!isRecord(raw)) {
316
+ sink.add('error', file, [], 'This file should describe a chart with an id and states, so it was skipped.');
317
+ continue;
318
+ }
319
+ const fields = parseFields(MachineFieldsShape, raw, sink, file, []);
320
+ let id = fields.id ?? '';
321
+ if (!id) {
322
+ id = titleCase(baseOf(file).replace(/\.(machine\.)?(ya?ml|json)$/, '').replace(/\.scxml$/i, '').replace(/^machine$/, dirOf(file).split('/').pop() ?? 'chart'));
323
+ sink.add('warning', file, ['id'], `The chart has no name; it is shown as "${id}".`);
324
+ }
325
+ if (charts.some((c) => c.id === id)) {
326
+ const renamed = `${id} (${dirOf(file)})`;
327
+ sink.add('warning', file, ['id'], `Another chart is already called "${id}"; this one is shown as "${renamed}".`);
328
+ id = renamed;
329
+ }
330
+ const dir = dirOf(file);
331
+ const layoutPath = layoutPathFor(file);
332
+ let layout: LayoutFile | null = null;
333
+ if (texts.has(layoutPath)) {
334
+ const layoutRaw = readStructured(layoutPath, texts.get(layoutPath)!, sink);
335
+ layout = layoutRaw === undefined ? null : parseOne(LayoutFileSchema, layoutRaw, sink, layoutPath, [], 'warning');
336
+ }
337
+ const chart: ParsedChart = {
338
+ id,
339
+ name: id,
340
+ file,
341
+ format: isScxmlFile(file) ? 'scxml' : 'xstate',
342
+ dir,
343
+ contextId: contextFor(dir),
344
+ meta: parseFields(StateMetaShape, raw.meta, sink, file, ['meta']),
345
+ initial: fields.initial ?? null,
346
+ rootStates: [],
347
+ states: [],
348
+ parent: null,
349
+ layout
350
+ };
351
+ if (fields.type === 'parallel') chart.type = 'parallel';
352
+ chart.explorationError = explorationIssue(raw);
353
+ if (chart.explorationError) sink.add('warning', file, [], `Free exploration is disabled: ${chart.explorationError}`);
354
+ charts.push(chart);
355
+ const ids = new Map<string, string>();
356
+ idToName.set(id, ids);
357
+
358
+ const walk = (statesRaw: unknown, parent: string | null, depth: number, yamlPath: (string | number)[]): string[] => {
359
+ if (statesRaw === undefined) return [];
360
+ if (!isRecord(statesRaw)) {
361
+ sink.add('warning', file, yamlPath, 'should list the states inside it by name.');
362
+ return [];
363
+ }
364
+ const names: string[] = [];
365
+ for (const [rawName, node] of Object.entries(statesRaw)) {
366
+ const path = [...yamlPath, rawName];
367
+ if (!isRecord(node)) {
368
+ sink.add('warning', file, path, 'This state has no details, so it is left off the map.');
369
+ continue;
370
+ }
371
+ let name = rawName;
372
+ if (states.has(name)) {
373
+ name = `${rawName} (${id})`;
374
+ sink.add('warning', file, path, `Another state is already called "${rawName}"; this one is shown as "${name}".`);
375
+ }
376
+ const f = parseFields(StateFieldsShape, node, sink, file, path);
377
+ const meta = parseFields(StateMetaShape, node.meta, sink, file, [...path, 'meta']);
378
+ if (node.meta === undefined) sink.add('info', file, path, 'This state has no description yet.');
379
+ const invoke = readInvoke(node.invoke, sink, file, [...path, 'invoke']);
380
+ const rawDone = isRecord(node.meta) ? node.meta.doneEvent : undefined;
381
+ const state: ParsedState = {
382
+ name,
383
+ chartId: id,
384
+ file,
385
+ parent,
386
+ depth,
387
+ type: 'atomic',
388
+ initial: f.initial ?? null,
389
+ meta,
390
+ children: [],
391
+ transitions: [],
392
+ childChartId: invoke?.src ?? meta.childMachine ?? null,
393
+ invoke,
394
+ doneEvent: typeof rawDone === 'string' && rawDone.trim() ? rawDone : null
395
+ };
396
+ states.set(name, state);
397
+ chart.states.push(name);
398
+ ids.set(f.id ?? rawName, name);
399
+ if (f.id && f.id !== rawName) ids.set(rawName, name);
400
+ names.push(name);
401
+ state.children = walk(node.states, name, depth + 1, [...path, 'states']);
402
+ state.type = f.type === 'final' ? 'final' : f.type === 'parallel' ? 'parallel' : state.children.length ? 'compound' : 'atomic';
403
+ if (state.type === 'compound' && !state.initial) {
404
+ state.initial = state.children[0] ?? null;
405
+ sink.add('info', file, path, `No starting state is named; "${state.initial}" is assumed.`);
406
+ }
407
+ if (node.on !== undefined && !isRecord(node.on)) {
408
+ sink.add('warning', file, [...path, 'on'], 'should list events by name.');
409
+ }
410
+ for (const [event, rawTarget] of Object.entries(isRecord(node.on) ? node.on : {})) {
411
+ const target = readTarget(rawTarget, sink, file, [...path, 'on', event]);
412
+ if (!target) continue;
413
+ const tid = transitionId(name, event);
414
+ transitions.set(tid, { id: tid, chartId: id, source: name, event, rawTarget: target.target, target: null, handOff: false, carriedBy: null });
415
+ state.transitions.push(tid);
416
+ }
417
+ }
418
+ return names;
419
+ };
420
+ chart.rootStates = walk(raw.states, null, 0, ['states']);
421
+ if (chart.rootStates.length === 0) sink.add('warning', file, ['states'], 'This chart has no states yet.');
422
+ if (!chart.initial) chart.initial = chart.rootStates[0] ?? null;
423
+ }
424
+
425
+ const chartById = new Map(charts.map((c) => [c.id, c]));
426
+ const findByName = (chartId: string, target: string) => {
427
+ const clean = target.replace(/^#/, '').split('.').pop()!;
428
+ return idToName.get(chartId)?.get(clean) ?? (states.has(clean) ? clean : null) ?? [...idToName.values()].map((m) => m.get(clean)).find(Boolean) ?? null;
429
+ };
430
+ for (const t of transitions.values()) {
431
+ t.target = findByName(t.chartId, t.rawTarget);
432
+ if (!t.target) {
433
+ sink.add('warning', chartById.get(t.chartId)!.file, ['states', t.source, 'on', t.event], `goes to "${t.rawTarget.replace(/^#/, '')}", which isn't in the spec, so it is left off the map.`);
434
+ }
435
+ }
436
+
437
+ /**
438
+ * Child charts, composed as the runner composes them: each final state at the top of the child
439
+ * hands an event to the parent state that runs it. The event is, in order, the legacy
440
+ * `meta.childFinalEvents` entry, the final's own `doneEvent`, or `done.invoke.<id>`; the parent
441
+ * handles it with a transition of that name or, failing that, the invoke's `onDone`.
442
+ */
443
+ for (const state of states.values()) {
444
+ if (!state.childChartId) continue;
445
+ const child = chartById.get(state.childChartId);
446
+ const parentChart = chartById.get(state.chartId)!;
447
+ const declaredAt = state.invoke ? ['states', state.name, 'invoke'] : ['states', state.name, 'meta', 'childMachine'];
448
+ if (!child) {
449
+ sink.add('warning', state.file, declaredAt, `names the chart "${state.childChartId}", which isn't in the spec.`);
450
+ state.childChartId = null;
451
+ continue;
452
+ }
453
+ if (child.id === parentChart.id) {
454
+ state.childChartId = null;
455
+ continue;
456
+ }
457
+ child.parent = { chartId: parentChart.id, state: state.name };
458
+ const legacy = state.meta.childFinalEvents ?? {};
459
+ for (const finalName of Object.keys(legacy)) {
460
+ const final = states.get(finalName);
461
+ if (!final || final.chartId !== child.id) {
462
+ sink.add('warning', state.file, ['states', state.name, 'meta', 'childFinalEvents', finalName], `names "${finalName}" as an end of "${child.id}", but that chart has no such state.`);
463
+ }
464
+ }
465
+ const defaultEvent = `done.invoke.${state.invoke?.id ?? child.id}`;
466
+ for (const finalName of child.rootStates) {
467
+ const final = states.get(finalName);
468
+ if (!final || final.type !== 'final') continue;
469
+ const event = legacy[finalName] ?? final.doneEvent ?? defaultEvent;
470
+ let parentTransition = transitions.get(transitionId(state.name, event));
471
+ if (!parentTransition && state.invoke?.onDone) {
472
+ const id = transitionId(state.name, event);
473
+ parentTransition = { id, chartId: parentChart.id, source: state.name, event, rawTarget: state.invoke.onDone, target: findByName(parentChart.id, state.invoke.onDone), handOff: false, carriedBy: null };
474
+ if (!parentTransition.target) {
475
+ sink.add('warning', state.file, [...declaredAt, 'onDone'], `goes to "${state.invoke.onDone.replace(/^#/, '')}", which isn't in the spec, so it is left off the map.`);
476
+ }
477
+ transitions.set(id, parentTransition);
478
+ state.transitions.push(id);
479
+ }
480
+ if (!parentTransition) {
481
+ const where = finalName in legacy ? ['states', state.name, 'meta', 'childFinalEvents', finalName] : declaredAt;
482
+ sink.add('warning', state.file, where, `"${finalName}" ends "${child.id}" and passes "${event}" up, but "${state.name}" doesn't respond to it.`);
483
+ continue;
484
+ }
485
+ const tid = transitionId(finalName, event);
486
+ transitions.set(tid, { id: tid, chartId: child.id, source: finalName, event, rawTarget: parentTransition.rawTarget, target: parentTransition.target, handOff: true, carriedBy: null });
487
+ final.transitions.push(tid);
488
+ parentTransition.carriedBy ??= tid;
489
+ }
490
+ }
491
+
492
+ const groups: SpecDocument['groups'] = [];
493
+ for (const chart of charts) {
494
+ let group = groups.find((g) => g.dir === chart.dir);
495
+ if (!group) {
496
+ group = { dir: chart.dir, chartIds: [], journeyIds: [] };
497
+ groups.push(group);
498
+ }
499
+ group.chartIds.push(chart.id);
500
+ }
501
+ for (const group of groups) {
502
+ group.chartIds.sort((a, b) => Number(Boolean(chartById.get(a)!.parent)) - Number(Boolean(chartById.get(b)!.parent)));
503
+ }
504
+
505
+ const journeys: ParsedJourney[] = [];
506
+ const journeyIds = new Set<string>();
507
+ for (const file of [...texts.keys()].filter((p) => /(^|\/)journeys\.ya?ml$/.test(p)).sort()) {
508
+ const raw = readStructured(file, texts.get(file)!, sink);
509
+ if (raw === undefined) continue;
510
+ if (!isRecord(raw) || !isRecord(raw.journeys)) {
511
+ sink.add('warning', file, ['journeys'], 'should list journeys by name.');
512
+ continue;
513
+ }
514
+ const dir = dirOf(file);
515
+ for (const [name, entry] of Object.entries(raw.journeys)) {
516
+ const j = parseOne(JourneySchema, entry, sink, file, ['journeys', name]);
517
+ if (!j) continue;
518
+ let id = slug(name) || `journey-${journeys.length + 1}`;
519
+ while (journeyIds.has(id)) id = `${id}-2`;
520
+ journeyIds.add(id);
521
+ journeys.push({ id, name, dir, file, description: j.description ?? '', events: j.events, endsIn: j.endsIn ?? [], owner: j.owner ?? null, tour: j.tour ?? false });
522
+ const group = groups.find((g) => g.dir === dir);
523
+ if (group) group.journeyIds.push(id);
524
+ else sink.add('info', file, ['journeys', name], 'There is no chart in this folder for the journey to follow.');
525
+ }
526
+ }
527
+
528
+ const questions: ParsedQuestion[] = [];
529
+ const excluded = [...(atlas?.excluded ?? [])];
530
+ const stateNames = new Set(states.keys());
531
+ for (const file of [...texts.keys()].filter((p) => /(^|\/)open-questions\.md$/.test(p)).sort()) {
532
+ const parsed = parseOpenQuestions(file, texts.get(file)!, stateNames);
533
+ questions.push(...parsed.questions);
534
+ for (const item of parsed.excluded) if (!excluded.includes(item)) excluded.push(item);
535
+ }
536
+ for (const q of questions) q.states = q.states.filter((s) => stateNames.has(s));
537
+ const knownIssues = [...texts.keys()]
538
+ .filter((p) => /(^|\/)known-issues\.md$/.test(p))
539
+ .sort()
540
+ .flatMap((file) => parseKnownIssues(file, texts.get(file)!, stateNames));
541
+
542
+ return {
543
+ ref: source.ref,
544
+ title: atlas?.title ?? 'Atlas',
545
+ description: atlas?.description ?? '',
546
+ contexts: contexts.filter((c) => c.declared || charts.some((ch) => ch.contextId === c.id)),
547
+ handOffs,
548
+ charts,
549
+ states,
550
+ transitions,
551
+ journeys,
552
+ questions,
553
+ knownIssues,
554
+ excluded,
555
+ groups
556
+ };
557
+ }
@@ -0,0 +1,44 @@
1
+ import YAML from 'yaml';
2
+
3
+ import type { IssueSink } from './issues';
4
+
5
+ function plainYamlMessage(message: string) {
6
+ const first = message.split('\n')[0] ?? message;
7
+ return first
8
+ .replace(/^[A-Z_]+:\s*/, '')
9
+ .replace(/ at line \d+, column \d+:?$/, '')
10
+ .replace(/^Nested mappings are not allowed in compact mappings/, 'A line has a colon where the file expected plain text')
11
+ .replace(/^Implicit keys need to be on a single line/, 'A name runs over more than one line')
12
+ .replace(/^Map keys must be unique/, 'The same name appears twice at this level');
13
+ }
14
+
15
+ /**
16
+ * YAML or JSON text to plain data. YAML syntax errors are reported with their line, and whatever
17
+ * the parser could still read is returned, so one bad line doesn't hide a whole chart.
18
+ */
19
+ export function readStructured(file: string, text: string, sink: IssueSink): unknown {
20
+ if (file.endsWith('.json')) {
21
+ try {
22
+ return JSON.parse(text) as unknown;
23
+ } catch (error) {
24
+ const detail = error instanceof Error ? error.message.replace(/^JSON\.parse: /, '') : '';
25
+ sink.add('error', file, [], `This file is not valid JSON, so it was skipped. ${detail}`.trim());
26
+ return undefined;
27
+ }
28
+ }
29
+ const doc = YAML.parseDocument(text, { uniqueKeys: true, prettyErrors: true });
30
+ for (const error of doc.errors) {
31
+ const line = error.linePos?.[0]?.line;
32
+ sink.add('error', file, line ? [`line ${line}`] : [], `${plainYamlMessage(error.message)}. The rest of the file was still read.`);
33
+ }
34
+ for (const warning of doc.warnings) {
35
+ const line = warning.linePos?.[0]?.line;
36
+ sink.add('info', file, line ? [`line ${line}`] : [], plainYamlMessage(warning.message));
37
+ }
38
+ try {
39
+ return doc.toJS({ maxAliasCount: 10_000 }) as unknown;
40
+ } catch (error) {
41
+ sink.add('error', file, [], `This file could not be read: ${error instanceof Error ? error.message : 'unknown problem'}.`);
42
+ return undefined;
43
+ }
44
+ }