@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,121 @@
1
+ export const testId = (id) => ({ testId: id });
2
+ export const text = (value, options = {}) => ({ text: value, ...options });
3
+ export const role = (r, name, options = {}) => ({ role: r, ...(name ? { name } : {}), ...options });
4
+ export const label = (value) => ({ label: value });
5
+ export const css = (selector) => ({ css: selector });
6
+ export function describeSignal(signal) {
7
+ if ('testId' in signal)
8
+ return `test ID "${signal.testId}"`;
9
+ if ('role' in signal)
10
+ return `${signal.role}${signal.name ? ` "${String(signal.name)}"` : ''}`;
11
+ if ('label' in signal)
12
+ return `label ${String(signal.label)}`;
13
+ if ('css' in signal)
14
+ return `selector ${signal.css}`;
15
+ return `text ${String(signal.text)}`;
16
+ }
17
+ /**
18
+ * Build `screen()` for a driver from one question: is this signal visible right now? A state is
19
+ * recognised when every `all` is visible, one `any` is visible and no `none` is. Drivers with
20
+ * their own locators (WebDriver's) pass their signal type and how to describe it.
21
+ */
22
+ export function signatureScreen(isVisible, describe) {
23
+ const visible = (ctx, signal) => isVisible(ctx, signal).catch(() => false);
24
+ const describeSignal = describe;
25
+ return function screen(spec) {
26
+ return {
27
+ async recognize(ctx) {
28
+ const signals = [];
29
+ for (const s of spec.all ?? [])
30
+ signals.push({ signal: describeSignal(s), expected: 'visible', visible: await visible(ctx, s) });
31
+ if (spec.any?.length) {
32
+ let seen = false;
33
+ for (const s of spec.any)
34
+ if (await visible(ctx, s))
35
+ seen = true;
36
+ signals.push({ signal: `one of: ${spec.any.map(describeSignal).join(' | ')}`, expected: 'visible', visible: seen });
37
+ }
38
+ for (const s of spec.none ?? [])
39
+ signals.push({ signal: describeSignal(s), expected: 'hidden', visible: await visible(ctx, s) });
40
+ return { matched: signals.every((x) => (x.expected === 'visible' ? x.visible : !x.visible)), signals };
41
+ },
42
+ ...(spec.checks
43
+ ? {
44
+ async checks(ctx) {
45
+ const results = [];
46
+ for (const { check, signal } of spec.checks) {
47
+ const seen = await visible(ctx, signal);
48
+ results.push({ check, passed: seen, expected: `${describeSignal(signal)} visible`, actual: seen ? 'visible' : 'not on screen' });
49
+ }
50
+ return results;
51
+ }
52
+ }
53
+ : {}),
54
+ ...(spec.transient ? { transient: true } : {}),
55
+ ...(spec.sameAs ? { sameAs: spec.sameAs } : {}),
56
+ ...(spec.lookIn ? { lookIn: spec.lookIn } : {}),
57
+ ...(spec.settle ? { settle: spec.settle } : {})
58
+ };
59
+ };
60
+ }
61
+ /** A state recognised from signals on any driver that provides a `SignalContext`. */
62
+ export const screen = signatureScreen((ctx, signal) => ctx.isVisible(signal), describeSignal);
63
+ const wire = (value, exact = false) => typeof value === 'string' ? { text: value, exact } : { regex: value.source, flags: value.flags };
64
+ /** A signal as JSON, for a probe that runs inside the page. */
65
+ export function toWire(signal) {
66
+ if ('testId' in signal)
67
+ return { kind: 'testId', id: signal.testId };
68
+ if ('role' in signal)
69
+ return { kind: 'role', role: signal.role, name: signal.name ? wire(signal.name, signal.exact) : null };
70
+ if ('label' in signal)
71
+ return { kind: 'label', match: wire(signal.label) };
72
+ if ('css' in signal)
73
+ return { kind: 'css', selector: signal.css };
74
+ return { kind: 'text', match: wire(signal.text, signal.exact) };
75
+ }
76
+ /**
77
+ * A function expression, as source, that finds a wire signal in a DOM: `(signal, scroll,
78
+ * withElement) => DomProbeResult`, plus the element itself when `withElement` is set (WebDriver
79
+ * returns it as an element reference). It is plain JavaScript so any driver can run it in a page (Bun WebView's
80
+ * `evaluate`, WebDriver's Execute Script). Roles follow the HTML-AAM implicit role mapping for the
81
+ * common elements; names follow aria-label, the associated label, then text content.
82
+ */
83
+ export const DOM_PROBE = `(signal, scroll, withElement) => {
84
+ const norm = (s) => String(s || '').replace(/\\s+/g, ' ').trim();
85
+ const matches = (value, m) => (m.regex !== undefined ? new RegExp(m.regex, m.flags).test(value) : m.exact ? value === m.text : value.toLowerCase().includes(m.text.toLowerCase()));
86
+ const shown = (el) => {
87
+ const r = el.getBoundingClientRect();
88
+ const st = getComputedStyle(el);
89
+ return r.width > 0 && r.height > 0 && st.visibility !== 'hidden' && st.display !== 'none' && Number(st.opacity) !== 0;
90
+ };
91
+ const ROLES = {
92
+ button: 'button,[role=button],input[type=button],input[type=submit],input[type=reset]',
93
+ heading: 'h1,h2,h3,h4,h5,h6,[role=heading]',
94
+ link: 'a[href],[role=link]',
95
+ textbox: 'input:not([type]),input[type=text],input[type=email],input[type=tel],input[type=url],input[type=search],input[type=password],textarea,[role=textbox]',
96
+ checkbox: 'input[type=checkbox],[role=checkbox]',
97
+ radio: 'input[type=radio],[role=radio]',
98
+ combobox: 'select,[role=combobox]',
99
+ listitem: 'li,[role=listitem]',
100
+ list: 'ul,ol,[role=list]',
101
+ status: 'output,[role=status]',
102
+ alert: '[role=alert]',
103
+ dialog: 'dialog,[role=dialog]',
104
+ img: 'img[alt],[role=img]'
105
+ };
106
+ const labelOf = (el) => norm(el.getAttribute('aria-label') || (el.labels && el.labels[0] ? el.labels[0].textContent : ''));
107
+ const nameOf = (el) => labelOf(el) || norm(el.textContent) || norm(el.getAttribute('alt') || el.getAttribute('title') || el.getAttribute('placeholder') || (el.type === 'submit' || el.type === 'button' ? el.value : ''));
108
+ const ownText = (el) => Array.from(el.childNodes).some((n) => n.nodeType === 3 && n.textContent.trim());
109
+ let found = [];
110
+ if (signal.kind === 'css') found = Array.from(document.querySelectorAll(signal.selector));
111
+ else if (signal.kind === 'testId') found = Array.from(document.querySelectorAll('[data-testid="' + CSS.escape(signal.id) + '"]'));
112
+ else if (signal.kind === 'role') found = Array.from(document.querySelectorAll(ROLES[signal.role] || '[role="' + signal.role + '"]')).filter((el) => !signal.name || matches(nameOf(el), signal.name));
113
+ else if (signal.kind === 'label') found = Array.from(document.querySelectorAll('input,textarea,select,[aria-label]')).filter((el) => matches(labelOf(el), signal.match));
114
+ else found = Array.from(document.querySelectorAll('body *')).filter((el) => ownText(el) && matches(norm(el.textContent), signal.match));
115
+ const el = found.find(shown);
116
+ if (!el) return { visible: false, count: found.length };
117
+ if (scroll) el.scrollIntoView({ block: 'center', inline: 'center' });
118
+ const r = el.getBoundingClientRect();
119
+ const at = { visible: true, count: found.length, x: r.x + r.width / 2, y: r.y + r.height / 2 };
120
+ return withElement ? Object.assign(at, { element: el }) : at;
121
+ }`;
@@ -0,0 +1,28 @@
1
+ import type { MachineConfig, SpecBundle } from './types.js';
2
+ export type HandOff = {
3
+ finalState: string;
4
+ event: string;
5
+ parentState: string;
6
+ childChart: string;
7
+ };
8
+ export type Composition = {
9
+ machine: MachineConfig;
10
+ /** Which chart each state came from. */
11
+ chartOf: Map<string, string>;
12
+ /** A child's former final states and the event each hands to its parent. */
13
+ handOffs: HandOff[];
14
+ /** The state each child chart is inlined into. */
15
+ hosts: Map<string, string>;
16
+ };
17
+ /**
18
+ * Inline every child chart where its parent invokes it, so XState's graph traversal (which cannot
19
+ * enter an invoked actor) walks straight through. Supports:
20
+ *
21
+ * - XState `invoke: { src, id, onDone }` and SCXML `<invoke>` + `done.invoke.<id>`;
22
+ * - a final state's `meta.doneEvent`, naming the event the parent receives from that final;
23
+ * - the legacy `meta.childMachine` + `meta.childFinalEvents` on the parent state.
24
+ *
25
+ * A child's final states stay as states (they keep their screenshots and checks) but stop being
26
+ * final: each takes the parent's transition for its hand-off event.
27
+ */
28
+ export declare function composeCharts(bundle: SpecBundle): Composition;
@@ -0,0 +1,71 @@
1
+ import { invokesOf, targetOf, walkStates } from './types.js';
2
+ function asTransition(t) {
3
+ const target = targetOf(t);
4
+ return target ? { target: `#${target}` } : undefined;
5
+ }
6
+ /**
7
+ * Inline every child chart where its parent invokes it, so XState's graph traversal (which cannot
8
+ * enter an invoked actor) walks straight through. Supports:
9
+ *
10
+ * - XState `invoke: { src, id, onDone }` and SCXML `<invoke>` + `done.invoke.<id>`;
11
+ * - a final state's `meta.doneEvent`, naming the event the parent receives from that final;
12
+ * - the legacy `meta.childMachine` + `meta.childFinalEvents` on the parent state.
13
+ *
14
+ * A child's final states stay as states (they keep their screenshots and checks) but stop being
15
+ * final: each takes the parent's transition for its hand-off event.
16
+ */
17
+ export function composeCharts(bundle) {
18
+ const byId = new Map(bundle.charts.map((c) => [c.machine.id, c.machine]));
19
+ const chartOf = new Map();
20
+ const handOffs = [];
21
+ const hosts = new Map();
22
+ const root = byId.get(bundle.root);
23
+ if (!root)
24
+ throw new Error(`Root chart "${bundle.root}" is not loaded`);
25
+ const inline = (name, node, chart, stack) => {
26
+ chartOf.set(name, chart);
27
+ const invoke = invokesOf(node)[0] ?? (typeof node.meta?.childMachine === 'string' ? { src: node.meta.childMachine } : undefined);
28
+ if (invoke) {
29
+ const child = byId.get(invoke.src);
30
+ if (!child)
31
+ throw new Error(`"${name}" invokes chart "${invoke.src}", which is not loaded`);
32
+ hosts.set(invoke.src, name);
33
+ if (stack.includes(invoke.src))
34
+ throw new Error(`Charts invoke each other in a cycle: ${[...stack, invoke.src].join(' → ')}`);
35
+ const legacy = node.meta?.childFinalEvents ?? {};
36
+ const doneEvent = `done.invoke.${invoke.id ?? invoke.src}`;
37
+ const consumed = new Set();
38
+ const retarget = (states) => Object.fromEntries(Object.entries(states).map(([k, s]) => {
39
+ if (s.type === 'final') {
40
+ const event = legacy[k] ?? (typeof s.meta?.doneEvent === 'string' ? s.meta.doneEvent : doneEvent);
41
+ const transition = asTransition(node.on?.[event]) ?? asTransition(invoke.onDone);
42
+ if (!transition)
43
+ throw new Error(`Child final "${k}" has nowhere to go in "${name}" (no "${event}" or onDone)`);
44
+ consumed.add(event);
45
+ handOffs.push({ finalState: k, event, parentState: name, childChart: invoke.src });
46
+ const { type: _final, ...rest } = s;
47
+ chartOf.set(k, invoke.src);
48
+ return [k, { ...rest, on: { ...rest.on, [event]: transition } }];
49
+ }
50
+ return [k, inline(k, s, invoke.src, [...stack, invoke.src])];
51
+ }));
52
+ const states = retarget(child.states ?? {});
53
+ const { invoke: _invoke, initial: _initial, on: _on, ...withoutInvoke } = node;
54
+ const on = Object.fromEntries(Object.entries(node.on ?? {}).filter(([event]) => !consumed.has(event)));
55
+ return { ...withoutInvoke, ...(child.initial === undefined ? {} : { initial: child.initial }), states, ...(Object.keys(on).length ? { on } : {}) };
56
+ }
57
+ if (!node.states)
58
+ return node;
59
+ return {
60
+ ...node,
61
+ states: Object.fromEntries(Object.entries(node.states).map(([k, v]) => [k, inline(k, v, chart, stack)]))
62
+ };
63
+ };
64
+ const states = Object.fromEntries(Object.entries(root.states ?? {}).map(([k, v]) => [k, inline(k, v, root.id, [root.id])]));
65
+ const machine = JSON.parse(JSON.stringify({ ...root, states }));
66
+ for (const { name, node } of walkStates(machine)) {
67
+ if (!node.id)
68
+ node.id = name;
69
+ }
70
+ return { machine, chartOf, handOffs, hosts };
71
+ }
@@ -0,0 +1,5 @@
1
+ /** Browser-safe: parsing, SCXML conversion and composition, with no Node built-ins. */
2
+ export * from './types.js';
3
+ export { parseChart, parseJourneys } from './parse.js';
4
+ export { scxmlToMachine, machineToScxml, scxmlToken, ATLAS_NS } from './scxml.js';
5
+ export { composeCharts, type Composition, type HandOff } from './compose.js';
@@ -0,0 +1,5 @@
1
+ /** Browser-safe: parsing, SCXML conversion and composition, with no Node built-ins. */
2
+ export * from './types.js';
3
+ export { parseChart, parseJourneys } from './parse.js';
4
+ export { scxmlToMachine, machineToScxml, scxmlToken, ATLAS_NS } from './scxml.js';
5
+ export { composeCharts } from './compose.js';
@@ -0,0 +1,8 @@
1
+ import type { Chart, Journey, MachineConfig, SpecBundle } from './types.js';
2
+ import { parseChart, parseJourneys } from './parse.js';
3
+ export { parseChart, parseJourneys };
4
+ /** The ids of the charts a machine composes, by `invoke` or the legacy `meta.childMachine`. */
5
+ export declare function childChartIds(machine: MachineConfig): Set<string>;
6
+ export declare function bundleOf(directory: string, charts: Chart[], journeys: Journey[]): SpecBundle;
7
+ /** Load every chart and journeys file directly inside `directory`. */
8
+ export declare function loadSpecDirectory(directory: string): SpecBundle;
@@ -0,0 +1,46 @@
1
+ import { existsSync, readdirSync, readFileSync } from 'node:fs';
2
+ import path from 'node:path';
3
+ import { parseChart, parseJourneys } from './parse.js';
4
+ import { invokesOf, walkStates } from './types.js';
5
+ export { parseChart, parseJourneys };
6
+ const CHART_FILE = /(\.scxml|(^|\.)machine\.(ya?ml|json))$/;
7
+ const JOURNEY_FILE = /^journeys\.(ya?ml|json)$/;
8
+ /** The ids of the charts a machine composes, by `invoke` or the legacy `meta.childMachine`. */
9
+ export function childChartIds(machine) {
10
+ const ids = new Set();
11
+ for (const { node } of walkStates(machine)) {
12
+ for (const invoke of invokesOf(node))
13
+ ids.add(invoke.src);
14
+ if (typeof node.meta?.childMachine === 'string')
15
+ ids.add(node.meta.childMachine);
16
+ }
17
+ return ids;
18
+ }
19
+ export function bundleOf(directory, charts, journeys) {
20
+ const invoked = new Set(charts.flatMap((c) => [...childChartIds(c.machine)]));
21
+ const roots = charts.filter((c) => !invoked.has(c.machine.id));
22
+ const root = roots[0]?.machine.id ?? charts[0]?.machine.id;
23
+ if (!root)
24
+ throw new Error(`No statechart found in ${directory}`);
25
+ return { directory, charts, journeys, root };
26
+ }
27
+ /** Load every chart and journeys file directly inside `directory`. */
28
+ export function loadSpecDirectory(directory) {
29
+ if (!existsSync(directory))
30
+ throw new Error(`Spec directory not found: ${directory}`);
31
+ const files = readdirSync(directory).sort();
32
+ const rank = (f) => (f.endsWith('.scxml') ? 0 : /\.ya?ml$/.test(f) ? 1 : 2);
33
+ const parsed = files
34
+ .filter((f) => CHART_FILE.test(f))
35
+ .sort((a, b) => rank(a) - rank(b) || a.localeCompare(b))
36
+ .map((f) => parseChart(f, readFileSync(path.join(directory, f), 'utf8')));
37
+ const byId = new Map();
38
+ for (const chart of parsed)
39
+ if (!byId.has(chart.machine.id))
40
+ byId.set(chart.machine.id, chart);
41
+ const charts = [...byId.values()];
42
+ const journeys = files
43
+ .filter((f) => JOURNEY_FILE.test(f))
44
+ .flatMap((f) => parseJourneys(f, readFileSync(path.join(directory, f), 'utf8')));
45
+ return bundleOf(directory, charts, journeys);
46
+ }
@@ -0,0 +1,5 @@
1
+ import type { Chart, Journey } from './types.js';
2
+ /** Parse one chart from its text, by file name: SCXML or XState config in JSON or YAML. */
3
+ export declare function parseChart(file: string, text: string): Chart;
4
+ /** Parse a journeys file: `{ journeys: { <name>: { description, events, endsIn } } }`. */
5
+ export declare function parseJourneys(file: string, text: string): Journey[];
@@ -0,0 +1,32 @@
1
+ import YAML from 'yaml';
2
+ import { scxmlToMachine } from './scxml.js';
3
+ function isRecord(value) {
4
+ return value !== null && typeof value === 'object' && !Array.isArray(value);
5
+ }
6
+ /** Parse one chart from its text, by file name: SCXML or XState config in JSON or YAML. */
7
+ export function parseChart(file, text) {
8
+ if (file.endsWith('.scxml')) {
9
+ return { file, format: 'scxml', machine: scxmlToMachine(text, file.replace(/^.*[\\/]/, '').replace(/\.scxml$/, '')) };
10
+ }
11
+ const value = file.endsWith('.json') ? JSON.parse(text) : YAML.parse(text);
12
+ if (!isRecord(value) || typeof value['id'] !== 'string' || !isRecord(value['states'])) {
13
+ throw new Error(`${file} is not an XState machine config with an id and states`);
14
+ }
15
+ return { file, format: 'xstate', machine: value };
16
+ }
17
+ /** Parse a journeys file: `{ journeys: { <name>: { description, events, endsIn } } }`. */
18
+ export function parseJourneys(file, text) {
19
+ const value = file.endsWith('.json') ? JSON.parse(text) : YAML.parse(text);
20
+ if (!isRecord(value) || !isRecord(value['journeys']))
21
+ throw new Error(`${file} has no journeys map`);
22
+ return Object.entries(value['journeys']).map(([name, raw]) => {
23
+ if (!isRecord(raw) || !Array.isArray(raw['events']))
24
+ throw new Error(`${file}: journey "${name}" needs an events list`);
25
+ return {
26
+ name,
27
+ description: typeof raw['description'] === 'string' ? raw['description'] : '',
28
+ events: raw['events'].map(String),
29
+ endsIn: Array.isArray(raw['endsIn']) ? raw['endsIn'].map(String) : []
30
+ };
31
+ });
32
+ }
@@ -0,0 +1,19 @@
1
+ import type { MachineConfig } from './types.js';
2
+ /**
3
+ * W3C SCXML ↔ XState config, for the pure subset Atlas executes.
4
+ *
5
+ * - `<state>`, `<parallel>`, `<final>`, `initial`, `<transition event target>`.
6
+ * - `<invoke type="scxml" src="other.scxml" id>` composes another chart; a transition on
7
+ * `done.invoke.<id>` (or `done.invoke`) becomes the invoke's `onDone`.
8
+ * - Business metadata lives in a foreign namespace, as SCXML allows:
9
+ * `<atlas:meta><atlas:description>…</atlas:description><atlas:event>…</atlas:event></atlas:meta>`.
10
+ * A final state's `atlas:done-event` attribute names the event its parent receives. Meta values
11
+ * that are not strings are written as JSON in an element marked `type="json"`.
12
+ */
13
+ export declare const ATLAS_NS = "https://github.com/crvouga/atlas/ns/1";
14
+ /** SCXML ids are XML IDs and event attributes are token lists, so names travel in `atlas:name`. */
15
+ export declare function scxmlToken(name: string): string;
16
+ /** Read an SCXML document into an XState machine config. */
17
+ export declare function scxmlToMachine(xml: string, fallbackId?: string): MachineConfig;
18
+ /** Write an XState machine config as an SCXML document. */
19
+ export declare function machineToScxml(machine: MachineConfig): string;
@@ -0,0 +1,239 @@
1
+ import { XMLBuilder, XMLParser } from 'fast-xml-parser';
2
+ import { invokesOf, targetOf } from './types.js';
3
+ /**
4
+ * W3C SCXML ↔ XState config, for the pure subset Atlas executes.
5
+ *
6
+ * - `<state>`, `<parallel>`, `<final>`, `initial`, `<transition event target>`.
7
+ * - `<invoke type="scxml" src="other.scxml" id>` composes another chart; a transition on
8
+ * `done.invoke.<id>` (or `done.invoke`) becomes the invoke's `onDone`.
9
+ * - Business metadata lives in a foreign namespace, as SCXML allows:
10
+ * `<atlas:meta><atlas:description>…</atlas:description><atlas:event>…</atlas:event></atlas:meta>`.
11
+ * A final state's `atlas:done-event` attribute names the event its parent receives. Meta values
12
+ * that are not strings are written as JSON in an element marked `type="json"`.
13
+ */
14
+ export const ATLAS_NS = 'https://github.com/crvouga/atlas/ns/1';
15
+ const SCXML_NS = 'http://www.w3.org/2005/07/scxml';
16
+ const parser = new XMLParser({
17
+ ignoreAttributes: false,
18
+ attributeNamePrefix: '',
19
+ preserveOrder: true,
20
+ trimValues: true
21
+ });
22
+ function tagOf(node) {
23
+ return Object.keys(node).find((k) => k !== ':@') ?? '';
24
+ }
25
+ function local(tag) {
26
+ return tag.includes(':') ? tag.slice(tag.indexOf(':') + 1) : tag;
27
+ }
28
+ function children(node) {
29
+ const value = node[tagOf(node)];
30
+ return Array.isArray(value) ? value : [];
31
+ }
32
+ function text(node) {
33
+ return children(node)
34
+ .map((c) => (typeof c['#text'] === 'string' || typeof c['#text'] === 'number' ? String(c['#text']) : text(c)))
35
+ .join('')
36
+ .trim();
37
+ }
38
+ function attrs(node) {
39
+ return node[':@'] ?? {};
40
+ }
41
+ const META_LISTS = new Set(['event', 'source', 'check', 'hint']);
42
+ const META_LIST_KEYS = { event: 'events', source: 'source', check: 'checks', hint: 'hints' };
43
+ function readMeta(node) {
44
+ const meta = {};
45
+ for (const child of children(node)) {
46
+ const key = local(tagOf(child));
47
+ if (META_LISTS.has(key)) {
48
+ const listKey = META_LIST_KEYS[key];
49
+ meta[listKey] = [...(meta[listKey] ?? []), text(child)];
50
+ }
51
+ else if (key === 'deadEnd' || key === 'dead-end') {
52
+ meta.deadEnd = text(child);
53
+ }
54
+ else if (attrs(child)['type'] === 'json') {
55
+ try {
56
+ meta[key] = JSON.parse(text(child));
57
+ }
58
+ catch {
59
+ meta[key] = text(child);
60
+ }
61
+ }
62
+ else {
63
+ meta[key] = text(child);
64
+ }
65
+ }
66
+ return meta;
67
+ }
68
+ /** SCXML ids are XML IDs and event attributes are token lists, so names travel in `atlas:name`. */
69
+ export function scxmlToken(name) {
70
+ const token = name
71
+ .normalize('NFKD')
72
+ .replace(/[^A-Za-z0-9]+/g, '-')
73
+ .replace(/^-+|-+$/g, '')
74
+ .toLowerCase();
75
+ return /^[a-z_]/.test(token) ? token : `s-${token}`;
76
+ }
77
+ function collectNames(nodes, names) {
78
+ for (const node of nodes) {
79
+ const tag = local(tagOf(node));
80
+ if (tag === 'state' || tag === 'parallel' || tag === 'final') {
81
+ const a = attrs(node);
82
+ if (a['id'])
83
+ names.set(a['id'], a['atlas:name'] ?? a['id']);
84
+ }
85
+ collectNames(children(node), names);
86
+ }
87
+ }
88
+ let currentNames = new Map();
89
+ const nameOf = (id) => currentNames.get(id) ?? id;
90
+ function readState(node, invokeIds) {
91
+ const tag = local(tagOf(node));
92
+ const a = attrs(node);
93
+ const name = nameOf(a['id'] ?? '');
94
+ const state = { id: name };
95
+ if (tag === 'parallel')
96
+ state.type = 'parallel';
97
+ if (tag === 'final')
98
+ state.type = 'final';
99
+ const states = {};
100
+ const invokes = [];
101
+ for (const child of children(node)) {
102
+ if (local(tagOf(child)) !== 'invoke')
103
+ continue;
104
+ const ca = attrs(child);
105
+ const src = (ca['src'] ?? '').replace(/\.scxml$/, '').replace(/^.*\//, '');
106
+ const invoke = { src: ca['atlas:name'] ?? src, ...(ca['id'] ? { id: ca['id'] } : {}) };
107
+ invokes.push(invoke);
108
+ if (ca['id'])
109
+ invokeIds.set(ca['id'], invoke);
110
+ }
111
+ for (const child of children(node)) {
112
+ const childTag = local(tagOf(child));
113
+ const ca = attrs(child);
114
+ if (childTag === 'state' || childTag === 'parallel' || childTag === 'final') {
115
+ const [k, v] = readState(child, invokeIds);
116
+ states[k] = v;
117
+ }
118
+ else if (childTag === 'transition') {
119
+ const event = ca['atlas:name'] ?? ca['event'];
120
+ if (!event || !ca['target'])
121
+ continue;
122
+ const target = nameOf(ca['target']);
123
+ const raw = ca['atlas:name'] ? '' : event;
124
+ if (raw === 'done.invoke' || raw.startsWith('done.invoke.')) {
125
+ const id = raw.slice('done.invoke.'.length);
126
+ const invoke = invokes.find((i) => !id || i.id === id) ?? invokeIds.get(id);
127
+ if (invoke) {
128
+ invoke.onDone = { target: `#${target}` };
129
+ continue;
130
+ }
131
+ }
132
+ state.on = { ...state.on, [event]: { target: `#${target}` } };
133
+ }
134
+ else if (childTag === 'meta' || tagOf(child).startsWith('atlas:')) {
135
+ state.meta = { ...state.meta, ...readMeta(child) };
136
+ }
137
+ }
138
+ const doneEvent = a['atlas:done-event'];
139
+ if (doneEvent)
140
+ state.meta = { ...state.meta, doneEvent };
141
+ if (a['initial'])
142
+ state.initial = nameOf(a['initial']);
143
+ else if (Object.keys(states).length && tag !== 'parallel')
144
+ state.initial = Object.keys(states)[0];
145
+ if (Object.keys(states).length)
146
+ state.states = states;
147
+ if (invokes.length)
148
+ state.invoke = invokes.length === 1 ? invokes[0] : invokes;
149
+ return [name, state];
150
+ }
151
+ /** Read an SCXML document into an XState machine config. */
152
+ export function scxmlToMachine(xml, fallbackId = 'machine') {
153
+ const doc = parser.parse(xml);
154
+ const root = doc.find((n) => local(tagOf(n)) === 'scxml');
155
+ if (!root)
156
+ throw new Error('Not an SCXML document: no <scxml> root element');
157
+ const a = attrs(root);
158
+ currentNames = new Map();
159
+ collectNames(children(root), currentNames);
160
+ const invokeIds = new Map();
161
+ const machine = { id: a['name'] ?? fallbackId };
162
+ const states = {};
163
+ for (const child of children(root)) {
164
+ const tag = local(tagOf(child));
165
+ if (tag === 'state' || tag === 'parallel' || tag === 'final') {
166
+ const [k, v] = readState(child, invokeIds);
167
+ states[k] = v;
168
+ }
169
+ else if (tag === 'meta' || tagOf(child).startsWith('atlas:')) {
170
+ machine.meta = { ...machine.meta, ...readMeta(child) };
171
+ }
172
+ }
173
+ const initial = a['initial'] ? nameOf(a['initial']) : Object.keys(states)[0];
174
+ if (initial !== undefined)
175
+ machine.initial = initial;
176
+ for (const [name, state] of Object.entries(states))
177
+ state.id = name;
178
+ machine.states = states;
179
+ return machine;
180
+ }
181
+ function metaToXml(meta) {
182
+ if (!meta)
183
+ return [];
184
+ const items = [];
185
+ for (const [key, value] of Object.entries(meta)) {
186
+ if (key === 'doneEvent' || value === undefined)
187
+ continue;
188
+ const listTag = Object.entries(META_LIST_KEYS).find(([, k]) => k === key)?.[0];
189
+ if (listTag && Array.isArray(value)) {
190
+ for (const v of value)
191
+ items.push({ [`atlas:${listTag}`]: [{ '#text': String(v) }] });
192
+ }
193
+ else if (typeof value === 'string') {
194
+ items.push({ [`atlas:${key}`]: [{ '#text': value }] });
195
+ }
196
+ else {
197
+ items.push({ [`atlas:${key}`]: [{ '#text': JSON.stringify(value) }], ':@': { type: 'json' } });
198
+ }
199
+ }
200
+ return items.length ? [{ 'atlas:meta': items }] : [];
201
+ }
202
+ function stateToXml(name, node) {
203
+ const tag = node.type === 'parallel' ? 'parallel' : node.type === 'final' ? 'final' : 'state';
204
+ const kids = [...metaToXml(node.meta)];
205
+ for (const [event, t] of Object.entries(node.on ?? {})) {
206
+ kids.push({ transition: [], ':@': { event: scxmlToken(event), target: scxmlToken(targetOf(t)), 'atlas:name': event } });
207
+ }
208
+ for (const [i, invoke] of invokesOf(node).entries()) {
209
+ const id = scxmlToken(invoke.id ?? `${name} invoke ${i}`);
210
+ kids.push({ invoke: [], ':@': { type: 'scxml', src: `${scxmlToken(invoke.src)}.scxml`, id, 'atlas:name': invoke.src } });
211
+ if (invoke.onDone)
212
+ kids.push({ transition: [], ':@': { event: `done.invoke.${id}`, target: scxmlToken(targetOf(invoke.onDone)) } });
213
+ }
214
+ for (const [k, v] of Object.entries(node.states ?? {}))
215
+ kids.push(stateToXml(k, v));
216
+ const at = { id: scxmlToken(name), 'atlas:name': name };
217
+ if (node.initial && node.type !== 'parallel')
218
+ at['initial'] = scxmlToken(node.initial);
219
+ if (typeof node.meta?.doneEvent === 'string')
220
+ at['atlas:done-event'] = node.meta.doneEvent;
221
+ return { [tag]: kids, ':@': at };
222
+ }
223
+ /** Write an XState machine config as an SCXML document. */
224
+ export function machineToScxml(machine) {
225
+ const kids = [...metaToXml(machine.meta)];
226
+ for (const [k, v] of Object.entries(machine.states ?? {}))
227
+ kids.push(stateToXml(k, v));
228
+ const at = {
229
+ xmlns: SCXML_NS,
230
+ 'xmlns:atlas': ATLAS_NS,
231
+ version: '1.0',
232
+ name: machine.id,
233
+ datamodel: 'null'
234
+ };
235
+ if (machine.initial)
236
+ at['initial'] = scxmlToken(machine.initial);
237
+ const builder = new XMLBuilder({ ignoreAttributes: false, attributeNamePrefix: '', preserveOrder: true, format: true, suppressEmptyNode: true });
238
+ return `<?xml version="1.0" encoding="UTF-8"?>\n${String(builder.build([{ scxml: kids, ':@': at }])).trim()}\n`;
239
+ }