@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,138 @@
1
+ import { ChartGraph } from './graph.js';
2
+ import { composeCharts } from './spec/compose.js';
3
+ import { invokesOf, walkStates } from './spec/types.js';
4
+ const STATE_KEYS = new Set(['id', 'initial', 'type', 'meta', 'states', 'on', 'invoke', 'description']);
5
+ const INVOKE_KEYS = new Set(['src', 'id', 'onDone']);
6
+ const REQUIRED_META = ['description'];
7
+ const FORBIDDEN = ['entry', 'exit', 'actions', 'assign', 'after', 'always', 'context', 'guard', 'cond', 'delay', 'output', 'onError'];
8
+ function checkShape(node, name, requiredMeta, out) {
9
+ for (const key of Object.keys(node)) {
10
+ if (FORBIDDEN.includes(key))
11
+ out.push({ rule: 'pure', message: `${name}: "${key}" is not allowed` });
12
+ else if (!STATE_KEYS.has(key))
13
+ out.push({ rule: 'pure', message: `${name}: unknown key "${key}"` });
14
+ }
15
+ if (name.includes('.'))
16
+ out.push({ rule: 'naming', message: `${name}: state names cannot contain dots` });
17
+ for (const key of requiredMeta) {
18
+ if (!(key in (node.meta ?? {})))
19
+ out.push({ rule: 'meta', message: `${name}: meta.${key} is missing` });
20
+ }
21
+ for (const [event, t] of Object.entries(node.on ?? {})) {
22
+ if (typeof t === 'string')
23
+ continue;
24
+ if (typeof t !== 'object' || t === null) {
25
+ out.push({ rule: 'pure', message: `${name} --${event}--> must be a target` });
26
+ continue;
27
+ }
28
+ for (const key of Object.keys(t)) {
29
+ if (key !== 'target')
30
+ out.push({ rule: 'pure', message: `${name} --${event}-->: "${key}" is not allowed (no guards, no actions)` });
31
+ }
32
+ }
33
+ for (const invoke of invokesOf(node)) {
34
+ for (const key of Object.keys(invoke)) {
35
+ if (!INVOKE_KEYS.has(key))
36
+ out.push({ rule: 'pure', message: `${name}: invoke "${key}" is not allowed; invoke only names another chart` });
37
+ }
38
+ if (typeof invoke.src !== 'string')
39
+ out.push({ rule: 'pure', message: `${name}: invoke src must name another chart` });
40
+ }
41
+ }
42
+ /** A copy with forbidden keys removed, so reachability can be analysed after a purity finding. */
43
+ function sanitized(bundle) {
44
+ const clean = (node) => {
45
+ const out = {};
46
+ for (const [key, value] of Object.entries(node))
47
+ if (STATE_KEYS.has(key))
48
+ out[key] = value;
49
+ if (node.on) {
50
+ out['on'] = Object.fromEntries(Object.entries(node.on).map(([event, t]) => [event, typeof t === 'string' ? t : { target: t.target }]));
51
+ }
52
+ if (node.states)
53
+ out['states'] = Object.fromEntries(Object.entries(node.states).map(([k, v]) => [k, clean(v)]));
54
+ return out;
55
+ };
56
+ return { ...bundle, charts: bundle.charts.map((c) => ({ ...c, machine: clean(c.machine) })) };
57
+ }
58
+ /**
59
+ * Checks a spec bundle is executable: pure (no guards, actions, context or delays), every state
60
+ * reachable, no dead ends without `meta.deadEnd`, every journey replays to its `endsIn` states, and
61
+ * (when implementations are given) every event and state in scope has one.
62
+ */
63
+ export function lintBundle(bundle, options = {}) {
64
+ const findings = [];
65
+ const requiredMeta = options.requiredMeta ?? REQUIRED_META;
66
+ for (const chart of bundle.charts) {
67
+ checkShape(chart.machine, chart.machine.id, [], findings);
68
+ for (const { name, node } of walkStates(chart.machine))
69
+ checkShape(node, name, requiredMeta, findings);
70
+ }
71
+ let composition;
72
+ let graph;
73
+ try {
74
+ composition = composeCharts(sanitized(bundle));
75
+ graph = new ChartGraph(composition.machine, composition.handOffs);
76
+ }
77
+ catch (error) {
78
+ const reason = error instanceof Error ? error.message : String(error);
79
+ const listed = findings.map((f) => `\n [${f.rule}] ${f.message}`).join('');
80
+ throw new Error(`The charts cannot be composed: ${reason}${listed}`);
81
+ }
82
+ const reached = new Set();
83
+ for (const regions of graph.regionSets()) {
84
+ for (const { config } of graph.explore(graph.initial(), (event) => graph.actsIn(event, regions)).seen.values()) {
85
+ for (const name of config.active)
86
+ reached.add(name);
87
+ }
88
+ }
89
+ for (const name of graph.stateNames()) {
90
+ if (!reached.has(name))
91
+ findings.push({ rule: 'reachable', message: `${name} is unreachable from the initial state` });
92
+ }
93
+ for (const { name, node, trail } of walkStates(composition.machine)) {
94
+ if (node.states || node.type === 'final')
95
+ continue;
96
+ const hasExit = [name, ...trail].some((n) => Object.keys(graph.node(n)?.on ?? {}).length > 0);
97
+ if (!hasExit && !node.meta?.deadEnd) {
98
+ findings.push({ rule: 'dead-end', message: `${name} has no way out and no meta.deadEnd reason` });
99
+ }
100
+ }
101
+ const journeys = bundle.journeys.map((journey) => {
102
+ const replay = graph.replay(graph.initial(), journey.events);
103
+ if (!replay.ok) {
104
+ findings.push({ rule: 'journey', message: `${journey.name}: "${replay.failedAt}" does not apply in ${JSON.stringify(replay.config.value)}` });
105
+ }
106
+ else {
107
+ const missing = journey.endsIn.filter((s) => !replay.config.active.includes(s));
108
+ if (missing.length) {
109
+ findings.push({ rule: 'journey', message: `${journey.name}: should end in ${missing.join(', ')}, ends in ${replay.config.active.join(' > ')}` });
110
+ }
111
+ }
112
+ return { journey, replay };
113
+ });
114
+ const unimplemented = { events: [], states: [] };
115
+ if (options.implementations) {
116
+ const { scope, implementations } = options;
117
+ const events = new Set(walkStates(composition.machine).flatMap(({ node }) => Object.keys(node.on ?? {})));
118
+ for (const event of events) {
119
+ if (scope && !scope.events.has(event))
120
+ continue;
121
+ if (!implementations.events.has(event))
122
+ unimplemented.events.push(event);
123
+ }
124
+ for (const name of graph.stateNames()) {
125
+ if (scope && !scope.states.has(name))
126
+ continue;
127
+ if (!graph.isLeaf(name))
128
+ continue;
129
+ if (!implementations.states.has(name))
130
+ unimplemented.states.push(name);
131
+ }
132
+ for (const e of unimplemented.events)
133
+ findings.push({ rule: 'implemented', message: `event "${e}" has no implementation` });
134
+ for (const s of unimplemented.states)
135
+ findings.push({ rule: 'implemented', message: `state "${s}" has no recognizer` });
136
+ }
137
+ return { findings, composition, graph, journeys, unimplemented };
138
+ }
@@ -0,0 +1,109 @@
1
+ import type { Config, StateValue } from './graph.js';
2
+ import type { ChartGraph } from './graph.js';
3
+ import type { Composition } from './spec/compose.js';
4
+ import type { SpecBundle } from './spec/types.js';
5
+ export type ChartScope = {
6
+ chart: string;
7
+ /** Set when the run covers one state of the chart and what is inside it. */
8
+ state?: string;
9
+ /** The configuration each generated path starts from. */
10
+ start: StateValue;
11
+ startLabel: string;
12
+ states: Set<string>;
13
+ transitions: Set<string>;
14
+ events: Set<string>;
15
+ /** Transitions that enter the chart from outside it. */
16
+ entries: Set<string>;
17
+ };
18
+ export type PlannedStep = {
19
+ event: string;
20
+ transitions: string[];
21
+ handOff: boolean;
22
+ from: string[];
23
+ to: string[];
24
+ };
25
+ export type PlannedPath = {
26
+ id: string;
27
+ /** The same start and events give the same key on any machine and in any run. */
28
+ key: string;
29
+ name: string;
30
+ kind: 'journey' | 'generated';
31
+ description: string;
32
+ steps: PlannedStep[];
33
+ /** Active states the path starts in, for the implementation's setup. */
34
+ start: string[];
35
+ /** The seed that puts the system in the start configuration; `setup` does it when absent. */
36
+ seed?: string;
37
+ /** The journeys this stretch belongs to: a stretch several journeys share is run once. */
38
+ journeys?: string[];
39
+ blockedBy: string[];
40
+ /** A blocked path whose runnable part other paths already cover: reported, not run. */
41
+ skipRun?: boolean;
42
+ };
43
+ export type ScopeOptions = {
44
+ /** The chart to run; the root chart (the whole product) when omitted. */
45
+ chart?: string;
46
+ /** Where generated paths start; the root's initial configuration when omitted. */
47
+ start?: StateValue;
48
+ startLabel?: string;
49
+ /** Only these events enter the chart from outside it; every entering transition when omitted. */
50
+ entryEvents?: string[];
51
+ /**
52
+ * Cover only this state and what is inside it, with the transitions into and out of it: one
53
+ * area of a chart that holds the whole product.
54
+ */
55
+ state?: string;
56
+ };
57
+ /**
58
+ * The part of the composed product a run covers: every state of `chart`, the transitions that
59
+ * enter it from outside, and the hand-offs back to its parent.
60
+ */
61
+ export declare function chartScope(bundle: SpecBundle, composition: Composition, graph: ChartGraph, options?: ScopeOptions): ChartScope;
62
+ /** A named seed: a configuration the implementation can put the system in directly. */
63
+ export type SeedPoint = {
64
+ name: string;
65
+ at: StateValue;
66
+ blocked?: string;
67
+ for?: string[];
68
+ shows?: string;
69
+ };
70
+ /** A journey as the paths it is cut into, one per seeded stretch, in order. */
71
+ export type PlannedJourney = {
72
+ name: string;
73
+ description: string;
74
+ paths: string[];
75
+ };
76
+ export type PlanOptions = {
77
+ /** Events that cannot run yet; paths stop before them. */
78
+ isBlocked?: (event: string) => boolean;
79
+ /** Whether the implementation can set a path up in this starting configuration. */
80
+ canStart?: (active: string[]) => true | string;
81
+ /**
82
+ * Configurations the implementation can seed directly. Paths start from the nearest one, and
83
+ * journeys are cut wherever they pass through one, so every stretch runs on its own.
84
+ */
85
+ seeds?: SeedPoint[];
86
+ /** Whether `setup` can start a path at the scope's start; true when omitted. */
87
+ hasSetup?: boolean;
88
+ };
89
+ /** The steps a path can actually run: everything before its first blocked event. */
90
+ export declare function runnableSteps(steps: PlannedStep[], isBlocked: (event: string) => boolean): PlannedStep[];
91
+ export declare function pathKey(seed: string | undefined, start: Config, events: string[]): string;
92
+ /**
93
+ * Paths for one chart. Every path starts at a start point: the scope's start (through `setup`)
94
+ * or a seed. Each curated journey is replayed from the chart's initial state and cut into
95
+ * stretches wherever it passes through a seeded configuration; identical stretches of different
96
+ * journeys become one path. Then the shortest extra path from the nearest start point is added
97
+ * for every transition still uncovered, and extra paths that are a prefix of another path are
98
+ * dropped. Everything is ordered by the chart and the journeys, so the plan is deterministic.
99
+ */
100
+ export declare function planChart(bundle: SpecBundle, graph: ChartGraph, scope: ChartScope, options?: PlanOptions): {
101
+ paths: PlannedPath[];
102
+ unreachable: string[];
103
+ journeys: PlannedJourney[];
104
+ seeds: {
105
+ blocked?: string | undefined;
106
+ name: string;
107
+ active: string[];
108
+ }[];
109
+ };
@@ -0,0 +1,324 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { configKey } from './graph.js';
3
+ import { splitTransitionId, transitionId, walkStates } from './spec/types.js';
4
+ /**
5
+ * The part of the composed product a run covers: every state of `chart`, the transitions that
6
+ * enter it from outside, and the hand-offs back to its parent.
7
+ */
8
+ export function chartScope(bundle, composition, graph, options = {}) {
9
+ const chart = options.chart ?? bundle.root;
10
+ const states = new Set();
11
+ const area = options.state;
12
+ if (area) {
13
+ if (!graph.stateNames().includes(area))
14
+ throw new Error(`There is no state "${area}" to scope the run to`);
15
+ const within = (name) => {
16
+ for (let n = name; n; n = graph.parent(n))
17
+ if (n === area)
18
+ return true;
19
+ return false;
20
+ };
21
+ for (const name of graph.stateNames())
22
+ if (within(name))
23
+ states.add(name);
24
+ }
25
+ else {
26
+ for (const [state, owner] of composition.chartOf)
27
+ if (owner === chart)
28
+ states.add(state);
29
+ if (chart === bundle.root)
30
+ for (const name of graph.stateNames())
31
+ states.add(name);
32
+ const host = composition.hosts.get(chart);
33
+ if (host)
34
+ states.add(host);
35
+ }
36
+ const transitions = new Set();
37
+ const entries = new Set();
38
+ for (const { name, node } of walkStates(composition.machine)) {
39
+ for (const event of Object.keys(node.on ?? {})) {
40
+ const id = transitionId(name, event);
41
+ const target = graph.target(name, event);
42
+ if (states.has(name))
43
+ transitions.add(id);
44
+ else if (target && states.has(target) && (!options.entryEvents || options.entryEvents.includes(event))) {
45
+ transitions.add(id);
46
+ entries.add(id);
47
+ }
48
+ }
49
+ }
50
+ for (const h of composition.handOffs) {
51
+ if (!states.has(h.finalState))
52
+ continue;
53
+ const target = graph.target(h.finalState, h.event);
54
+ if (target)
55
+ states.add(target);
56
+ }
57
+ if (area) {
58
+ for (const id of transitions) {
59
+ const { source, event } = splitTransitionId(id);
60
+ const target = graph.target(source, event);
61
+ if (target && !entries.has(id))
62
+ states.add(target);
63
+ }
64
+ }
65
+ for (const id of entries)
66
+ states.add(splitTransitionId(id).source);
67
+ const start = options.start ?? graph.initial().value;
68
+ return {
69
+ chart,
70
+ ...(area ? { state: area } : {}),
71
+ start,
72
+ startLabel: options.startLabel ?? 'The start of the chart',
73
+ states,
74
+ transitions,
75
+ events: new Set([...transitions].map((id) => splitTransitionId(id).event)),
76
+ entries
77
+ };
78
+ }
79
+ function inScope(scope, fired) {
80
+ return fired.length > 0 && fired.every((t) => scope.transitions.has(t));
81
+ }
82
+ function toSteps(replaySteps) {
83
+ return replaySteps.map((s) => ({ event: s.event, transitions: s.transitions, handOff: s.handOff, from: s.from.active, to: s.to.active }));
84
+ }
85
+ /**
86
+ * `explore` takes hand-off events as ordinary moves, but `replay` inserts them by itself as soon as
87
+ * a hand-off state is active. Drop the ones `replay` will insert, so it does not see them twice.
88
+ */
89
+ function withoutAutomaticHandOffs(graph, start, events) {
90
+ const out = [];
91
+ let config = start;
92
+ for (const event of events) {
93
+ const automatic = graph.pendingHandOffs(config).some((h) => h.event === event);
94
+ const next = graph.step(config, event);
95
+ if (!automatic)
96
+ out.push(event);
97
+ if (!next)
98
+ break;
99
+ config = next.config;
100
+ }
101
+ return out;
102
+ }
103
+ /** The steps a path can actually run: everything before its first blocked event. */
104
+ export function runnableSteps(steps, isBlocked) {
105
+ const stop = steps.findIndex((s) => isBlocked(s.event));
106
+ return stop < 0 ? steps : steps.slice(0, stop);
107
+ }
108
+ export function pathKey(seed, start, events) {
109
+ return createHash('sha256')
110
+ .update(JSON.stringify([seed ?? null, configKey(start.value), events]))
111
+ .digest('hex')
112
+ .slice(0, 16);
113
+ }
114
+ const leaves = (graph, active) => active.filter((n) => graph.isLeaf(n));
115
+ /**
116
+ * Paths for one chart. Every path starts at a start point: the scope's start (through `setup`)
117
+ * or a seed. Each curated journey is replayed from the chart's initial state and cut into
118
+ * stretches wherever it passes through a seeded configuration; identical stretches of different
119
+ * journeys become one path. Then the shortest extra path from the nearest start point is added
120
+ * for every transition still uncovered, and extra paths that are a prefix of another path are
121
+ * dropped. Everything is ordered by the chart and the journeys, so the plan is deterministic.
122
+ */
123
+ export function planChart(bundle, graph, scope, options = {}) {
124
+ const isBlocked = options.isBlocked ?? (() => false);
125
+ const canStart = options.canStart ?? (() => true);
126
+ const hasSetup = options.hasSetup !== false;
127
+ const blockedEvents = (steps) => [...new Set(steps.map((s) => s.event).filter(isBlocked))];
128
+ const isRoot = scope.chart === bundle.root && !scope.state;
129
+ const seeds = (options.seeds ?? []).map((seed) => ({ ...seed, config: graph.resolve(seed.at) }));
130
+ const startPoints = [
131
+ ...seeds.filter((s) => !s.blocked).map((s) => ({ seed: s.name, config: s.config, ...(s.for ? { for: s.for } : {}), ...(s.shows ? { shows: s.shows } : {}) })),
132
+ ...(hasSetup ? [{ config: graph.resolve(scope.start) }] : [])
133
+ ];
134
+ const variantEvents = new Set(startPoints.flatMap((p) => p.for ?? []));
135
+ /** A variant seed starts only paths that take one of its events; those events start only from it. */
136
+ const suits = (point, events) => {
137
+ const needed = events.filter((e) => variantEvents.has(e));
138
+ return point.for ? needed.length > 0 && needed.every((e) => point.for.includes(e)) : needed.length === 0;
139
+ };
140
+ /** The region of a parallel state a state sits in: the child of its nearest parallel ancestor. */
141
+ const regionOf = (state) => {
142
+ for (let n = state, p = graph.parent(state); p; n = p, p = graph.parent(p))
143
+ if (graph.node(p)?.type === 'parallel')
144
+ return n;
145
+ return null;
146
+ };
147
+ /** Whether the first event acts in the region whose screen the seed leaves open. */
148
+ const sameRegion = (config, event, shows) => {
149
+ const sources = graph.firedBy(config, event).map((t) => splitTransitionId(t).source);
150
+ return sources.some((source) => regionOf(source) === regionOf(shows));
151
+ };
152
+ const startsAt = (config) => startPoints.filter((p) => configKey(p.config.value) === configKey(config.value));
153
+ const startVerdict = (config, events) => {
154
+ const candidates = startsAt(config);
155
+ const point = candidates.find((p) => suits(p, events));
156
+ if (point?.seed)
157
+ return { point, supported: true };
158
+ const needed = events.filter((e) => variantEvents.has(e));
159
+ if (!point && needed.length && candidates.length)
160
+ return { point, supported: `no seed at ${leaves(graph, config.active).join(' + ')} is for ${needed.map((e) => `"${e}"`).join(', ')}` };
161
+ if (!hasSetup)
162
+ return { point, supported: `no seed puts the system in ${leaves(graph, config.active).join(' + ')}` };
163
+ return { point, supported: canStart(config.active) };
164
+ };
165
+ const journeyPaths = [];
166
+ const byKey = new Map();
167
+ const journeys = [];
168
+ for (const journey of bundle.journeys) {
169
+ const replay = graph.replay(graph.initial(), journey.events);
170
+ const ids = [];
171
+ let current = null;
172
+ const flush = () => {
173
+ if (!current?.length) {
174
+ current = null;
175
+ return;
176
+ }
177
+ const startConfig = current[0].from;
178
+ const steps = toSteps(current);
179
+ current = null;
180
+ const { point, supported } = startVerdict(startConfig, steps.map((s) => s.event));
181
+ const key = pathKey(point?.seed, startConfig, steps.map((s) => s.event));
182
+ const existing = byKey.get(key);
183
+ if (existing) {
184
+ if (!existing.journeys.includes(journey.name))
185
+ existing.journeys.push(journey.name);
186
+ if (!ids.includes(existing.id))
187
+ ids.push(existing.id);
188
+ return;
189
+ }
190
+ const path = {
191
+ id: `journey-${journeyPaths.length + 1}`,
192
+ key,
193
+ name: journey.name,
194
+ kind: 'journey',
195
+ description: journey.description,
196
+ steps,
197
+ start: startConfig.active,
198
+ ...(point?.seed ? { seed: point.seed } : {}),
199
+ journeys: [journey.name],
200
+ blockedBy: [...(supported === true ? [] : [`Start: ${supported}`]), ...blockedEvents(steps)]
201
+ };
202
+ byKey.set(key, path);
203
+ journeyPaths.push(path);
204
+ ids.push(path.id);
205
+ };
206
+ /** The events of the stretch that would start at step `from`: up to the next seeded configuration. */
207
+ const stretchFrom = (from) => {
208
+ const events = [];
209
+ for (let i = from; i < replay.steps.length; i++) {
210
+ const next = replay.steps[i];
211
+ if (!inScope(scope, next.transitions) || (i > from && startsAt(next.from).length > 0))
212
+ break;
213
+ events.push(next.event);
214
+ }
215
+ return events;
216
+ };
217
+ for (const [index, step] of replay.steps.entries()) {
218
+ if (!inScope(scope, step.transitions)) {
219
+ flush();
220
+ continue;
221
+ }
222
+ // Cut only where a start point suits what follows: a variant seed for other events is no place to cut.
223
+ const candidates = startsAt(step.from);
224
+ const seeded = candidates.length > 0 && candidates.some((p) => suits(p, stretchFrom(index)));
225
+ if (current && seeded)
226
+ flush();
227
+ if (!current) {
228
+ const entering = isRoot || seeded || step.transitions.some((t) => scope.entries.has(t));
229
+ if (!entering)
230
+ continue;
231
+ current = [];
232
+ }
233
+ current.push(step);
234
+ }
235
+ flush();
236
+ if (ids.length)
237
+ journeys.push({ name: journey.name, description: journey.description, paths: ids });
238
+ }
239
+ const startable = (p) => !p.blockedBy.some((b) => b.startsWith('Start:'));
240
+ const covered = new Set(journeyPaths.filter(startable).flatMap((p) => runnableSteps(p.steps, isBlocked).flatMap((s) => s.transitions)));
241
+ const planned = new Set();
242
+ let generated = [];
243
+ if (startPoints.length) {
244
+ for (const avoidBlocked of [true, false]) {
245
+ const explorations = new Map();
246
+ const exploredFor = (source) => {
247
+ const regions = graph.regionsOf(source);
248
+ const key = regions.join('\u0000');
249
+ if (!explorations.has(key)) {
250
+ explorations.set(key, startPoints.map((point) => graph.explore(point.config, (event) => scope.events.has(event) &&
251
+ (!avoidBlocked || !isBlocked(event)) &&
252
+ (!variantEvents.has(event) || Boolean(point.for?.includes(event))) &&
253
+ graph.actsIn(event, regions))));
254
+ }
255
+ return explorations.get(key);
256
+ };
257
+ for (const target of scope.transitions) {
258
+ if (covered.has(target) || planned.has(target))
259
+ continue;
260
+ const { source, event } = splitTransitionId(target);
261
+ if (avoidBlocked && isBlocked(event))
262
+ continue;
263
+ let best = null;
264
+ const explored = exploredFor(source);
265
+ startPoints.forEach((point, i) => {
266
+ const first = explored[i].firstFired.get(target);
267
+ if (!first)
268
+ return;
269
+ const path = [...first.from.path, first.event];
270
+ if (point.for && !path.some((e) => point.for.includes(e)))
271
+ return;
272
+ if (point.shows && !sameRegion(point.config, path[0], point.shows))
273
+ return;
274
+ if (!best || path.length < best.path.length)
275
+ best = { origin: point, path };
276
+ });
277
+ if (!best)
278
+ continue;
279
+ const { origin, path: events } = best;
280
+ const replay = graph.replay(origin.config, withoutAutomaticHandOffs(graph, origin.config, events));
281
+ const steps = toSteps(replay.steps).filter((s) => inScope(scope, s.transitions));
282
+ for (const s of runnableSteps(steps, isBlocked))
283
+ for (const t of s.transitions)
284
+ covered.add(t);
285
+ if (!avoidBlocked)
286
+ for (const s of steps)
287
+ for (const t of s.transitions)
288
+ planned.add(t);
289
+ generated.push({
290
+ id: '',
291
+ key: pathKey(origin.seed, origin.config, steps.map((s) => s.event)),
292
+ name: `Covers: ${source} → ${event}`,
293
+ kind: 'generated',
294
+ description: `Shortest path from ${origin.seed ? `the seed "${origin.seed}"` : 'the start'} that reaches "${source}" and takes "${event}".`,
295
+ steps,
296
+ start: origin.config.active,
297
+ ...(origin.seed ? { seed: origin.seed } : {}),
298
+ blockedBy: blockedEvents(steps)
299
+ });
300
+ }
301
+ }
302
+ }
303
+ const all = [...journeyPaths, ...generated];
304
+ const startKey = (p) => `${p.seed ?? ''}|${p.start.join('|')}`;
305
+ const isPrefix = (short, long) => short !== long && startable(long) && startKey(short) === startKey(long) && short.steps.length <= long.steps.length && short.steps.every((s, i) => s.event === long.steps[i].event);
306
+ generated = generated.filter((p, i) => !all.some((q) => isPrefix(p, q) && (q.steps.length > p.steps.length || q.kind === 'journey' || generated.indexOf(q) < i)));
307
+ generated.forEach((p, i) => (p.id = `generated-${i + 1}`));
308
+ const everPlanned = new Set([...journeyPaths, ...generated].filter(startable).flatMap((p) => p.steps.flatMap((s) => s.transitions)));
309
+ const unreachable = [...scope.transitions].filter((t) => !everPlanned.has(t));
310
+ const runCovered = new Set();
311
+ const paths = [...journeyPaths, ...generated].map((p) => {
312
+ const prefix = startable(p) ? runnableSteps(p.steps, isBlocked).flatMap((s) => s.transitions) : [];
313
+ const adds = prefix.some((t) => !runCovered.has(t));
314
+ for (const t of prefix)
315
+ runCovered.add(t);
316
+ return p.blockedBy.length && !adds ? { ...p, skipRun: true } : p;
317
+ });
318
+ return {
319
+ paths,
320
+ unreachable,
321
+ journeys,
322
+ seeds: seeds.map((s) => ({ name: s.name, active: s.config.active, ...(s.blocked ? { blocked: s.blocked } : {}) }))
323
+ };
324
+ }
@@ -0,0 +1,8 @@
1
+ import type { PrivacyRules } from './run/types.js';
2
+ /** Defaults that fit any product: no real-looking emails, SSNs or card numbers in media or manifests. */
3
+ export declare const DEFAULT_PRIVACY: Required<PrivacyRules>;
4
+ export declare function mergePrivacy(rules?: PrivacyRules): Required<PrivacyRules>;
5
+ /** What in `text` looks like real personal data, described without repeating it. */
6
+ export declare function privacyFindings(text: string, where: string, rules: Required<PrivacyRules>): string[];
7
+ /** Refuse to run against anything but the allowed hosts (loopback by default). */
8
+ export declare function assertAllowedTarget(urls: string[], rules: Required<PrivacyRules>): void;
@@ -0,0 +1,42 @@
1
+ const EMAIL = /[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}/gi;
2
+ /** Defaults that fit any product: no real-looking emails, SSNs or card numbers in media or manifests. */
3
+ export const DEFAULT_PRIVACY = {
4
+ deny: [
5
+ { name: 'a US Social Security number', pattern: /\b\d{3}-\d{2}-\d{4}\b/ },
6
+ {
7
+ name: 'a payment card number',
8
+ pattern: /\b(?!4242[ -]?4242[ -]?4242[ -]?4242\b|4000[ -]?0000[ -]?0000[ -]?0002\b|5555[ -]?5555[ -]?5555[ -]?4444\b)(?:4\d{3}|5[1-5]\d{2})[ -]?\d{4}[ -]?\d{4}[ -]?\d{4}\b/
9
+ }
10
+ ],
11
+ allowEmails: [/@example\.(com|org|net)$/i, /\.(test|example|invalid|localhost|local)$/i],
12
+ allowHosts: ['127.0.0.1', 'localhost', '::1', '[::1]']
13
+ };
14
+ export function mergePrivacy(rules = {}) {
15
+ return {
16
+ deny: [...DEFAULT_PRIVACY.deny, ...(rules.deny ?? [])],
17
+ allowEmails: [...DEFAULT_PRIVACY.allowEmails, ...(rules.allowEmails ?? [])],
18
+ allowHosts: rules.allowHosts ?? DEFAULT_PRIVACY.allowHosts
19
+ };
20
+ }
21
+ /** What in `text` looks like real personal data, described without repeating it. */
22
+ export function privacyFindings(text, where, rules) {
23
+ const findings = [];
24
+ for (const email of text.match(EMAIL) ?? []) {
25
+ const lower = email.toLowerCase();
26
+ if (rules.allowEmails.some((p) => p.test(lower)))
27
+ continue;
28
+ findings.push(`${where}: an email address outside the allowed domains (${lower.replace(/^(.).*@/, '$1…@')})`);
29
+ }
30
+ for (const { name, pattern } of rules.deny)
31
+ if (pattern.test(text))
32
+ findings.push(`${where}: something shaped like ${name}`);
33
+ return findings;
34
+ }
35
+ /** Refuse to run against anything but the allowed hosts (loopback by default). */
36
+ export function assertAllowedTarget(urls, rules) {
37
+ for (const url of urls) {
38
+ const host = new URL(url).hostname;
39
+ if (!rules.allowHosts.includes(host))
40
+ throw new Error(`Refusing to run against ${host}: allowed hosts are ${rules.allowHosts.join(', ')}`);
41
+ }
42
+ }