@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,214 @@
1
+ /**
2
+ * Read side of the shared reports store: what a reports API serves. Pure over
3
+ * an injected {@link ObjectStore}, {@link Clock} and {@link Timers}; nothing
4
+ * here imports a runtime or an HTTP framework.
5
+ */
6
+ import { readUtf8 } from './object-io.js';
7
+ import { inCompletionOrder } from './completion-order.js';
8
+ import { COMMITS_INDEX_PREFIX, commitKey, frameCssKey, isFramePath, shaOfIndexKey, sourceOfKey, sourcesPrefix, } from './keys.js';
9
+ import { reportStore } from './publication.js';
10
+ import { readBundledSnapshot, readBundledSnapshots } from './snapshot-bundle.js';
11
+ import { readStoredSource, sourceTestPrefix } from './source-content.js';
12
+ import { sourceReader } from './source-reader.js';
13
+ /** Parallel object reads per stream. */
14
+ const READ_WIDTH = 16;
15
+ const TEXT_MEDIA = new Set(['html', 'json', 'text']);
16
+ async function readJson(store, key) {
17
+ const text = await readUtf8(store, key);
18
+ if (text === null)
19
+ return null;
20
+ try {
21
+ return JSON.parse(text);
22
+ }
23
+ catch {
24
+ return null;
25
+ }
26
+ }
27
+ export async function readCommit(store, sha) {
28
+ return readJson(await reportStore(store, sha), commitKey(sha));
29
+ }
30
+ export async function readFrameCss(store, sha) {
31
+ return (await reportStore(store, sha)).get(frameCssKey(sha));
32
+ }
33
+ /**
34
+ * Newest commits first, `limit` per page. A commit published twice (a rerun)
35
+ * has two index entries; only the newer one is ever listed, on any page.
36
+ */
37
+ export async function listCommits(store, options) {
38
+ const seen = new Set();
39
+ const newest = [];
40
+ for (const key of await store.listKeys(COMMITS_INDEX_PREFIX)) {
41
+ const sha = shaOfIndexKey(key);
42
+ if (sha === null || seen.has(sha))
43
+ continue;
44
+ seen.add(sha);
45
+ newest.push({ key, sha });
46
+ }
47
+ const after = options.cursor === null
48
+ ? newest
49
+ : newest.filter((entry) => entry.key > (options.cursor ?? ''));
50
+ const page = after.slice(0, Math.max(1, options.limit));
51
+ const records = await Promise.all(page.map((e) => readCommit(store, e.sha)));
52
+ return {
53
+ commits: records.filter((r) => r !== null),
54
+ cursor: after.length > page.length ? (page.at(-1)?.key ?? null) : null,
55
+ };
56
+ }
57
+ function sleep(timers, ms) {
58
+ return new Promise((resolve) => {
59
+ timers.setTimeout(resolve, ms);
60
+ });
61
+ }
62
+ async function* newSources(store, sha, sent) {
63
+ store = await reportStore(store, sha);
64
+ const fresh = (await store.listKeys(sourcesPrefix(sha)))
65
+ .map((key) => sourceOfKey(sha, key))
66
+ .filter((s) => s !== null && !sent.has(s));
67
+ for (const s of fresh)
68
+ sent.add(s);
69
+ const reads = fresh.map((s) => async () => (await readStoredSource(store, sha, s))?.record ?? null);
70
+ for await (const record of inCompletionOrder(reads, READ_WIDTH)) {
71
+ if (record !== null)
72
+ yield record;
73
+ }
74
+ }
75
+ /**
76
+ * The commit, then every source in the order its read finishes. While the
77
+ * commit is running, its sources are re-listed every `pollMs` and each new
78
+ * one is sent as it lands, until the commit completes or `tailMs` passes.
79
+ * The state is read before each listing, so a listing after `complete` is
80
+ * the final one.
81
+ */
82
+ export async function* commitEvents(store, sha, options) {
83
+ const startedAt = options.clock.now();
84
+ const sent = new Set(options.have ?? []);
85
+ let commit = await readCommit(store, sha);
86
+ yield { type: 'commit', commit };
87
+ for (;;) {
88
+ for await (const source of newSources(store, sha, sent)) {
89
+ yield { type: 'source', source };
90
+ }
91
+ if (commit === null && sent.size === 0) {
92
+ yield { type: 'end', state: 'missing' };
93
+ return;
94
+ }
95
+ if (commit?.state === 'complete') {
96
+ yield { type: 'end', state: 'complete' };
97
+ return;
98
+ }
99
+ if (options.clock.now() - startedAt >= options.tailMs) {
100
+ yield { type: 'end', state: 'running' };
101
+ return;
102
+ }
103
+ await sleep(options.timers, options.pollMs);
104
+ const latest = await readCommit(store, sha);
105
+ if (latest !== null && latest.state !== commit?.state) {
106
+ yield { type: 'commit', commit: latest };
107
+ }
108
+ commit = latest ?? commit;
109
+ }
110
+ }
111
+ function collectFrameFiles(steps, out) {
112
+ for (const step of steps) {
113
+ for (const frame of step.frames) {
114
+ for (const blob of frame.snapshots)
115
+ out.set(blob.file, blob.media);
116
+ }
117
+ collectFrameFiles(step.steps, out);
118
+ }
119
+ }
120
+ function bytesToBase64(bytes) {
121
+ let binary = '';
122
+ const CHUNK = 0x8000;
123
+ for (let i = 0; i < bytes.length; i += CHUNK) {
124
+ binary += String.fromCharCode(...bytes.subarray(i, i + CHUNK));
125
+ }
126
+ return btoa(binary);
127
+ }
128
+ function frameBlob(obj, media) {
129
+ return TEXT_MEDIA.has(media)
130
+ ? {
131
+ contentType: obj.contentType,
132
+ text: new TextDecoder().decode(obj.bytes),
133
+ }
134
+ : { contentType: obj.contentType, base64: bytesToBase64(obj.bytes) };
135
+ }
136
+ /** One test's timeline, without its frame files. */
137
+ export async function readTestReport(store, args) {
138
+ store = await reportStore(store, args.sha);
139
+ const prefix = await sourceTestPrefix(store, args);
140
+ if (prefix === null)
141
+ return null;
142
+ if (prefix.startsWith('content/'))
143
+ return readJson(store, `${prefix}report.json`);
144
+ const read = await sourceReader(store, args);
145
+ const obj = await read(`${prefix}report.json`);
146
+ return obj === null
147
+ ? null
148
+ : JSON.parse(new TextDecoder().decode(obj.bytes));
149
+ }
150
+ /** One frame file's bytes, or null when the test never wrote it. */
151
+ export async function readFrameBody(store, args) {
152
+ store = await reportStore(store, args.sha);
153
+ if (!isFramePath(args.file))
154
+ throw new Error('Invalid report frame');
155
+ const prefix = await sourceTestPrefix(store, args);
156
+ if (prefix === null)
157
+ return null;
158
+ const key = `${prefix}${args.file}`;
159
+ if (prefix.startsWith('content/'))
160
+ return readBundledSnapshot(store, prefix, args.file);
161
+ const read = await sourceReader(store, args);
162
+ const obj = await read(key);
163
+ return obj === null
164
+ ? null
165
+ : { bytes: obj.bytes, contentType: obj.contentType };
166
+ }
167
+ /** Every source record of a commit, in the order each read finishes. */
168
+ export async function listSourceRecords(store, sha) {
169
+ const out = [];
170
+ for await (const source of newSources(store, sha, new Set())) {
171
+ out.push(source);
172
+ }
173
+ return out;
174
+ }
175
+ /** A test's report, then each of its frame files as its read finishes. */
176
+ export async function* testEvents(store, args) {
177
+ store = await reportStore(store, args.sha);
178
+ const prefix = await sourceTestPrefix(store, args);
179
+ const report = await readTestReport(store, args);
180
+ if (report === null) {
181
+ yield { type: 'end', found: false };
182
+ return;
183
+ }
184
+ yield { type: 'report', report };
185
+ const files = new Map();
186
+ collectFrameFiles(report.steps, files);
187
+ if (prefix?.startsWith('content/')) {
188
+ for await (const frame of readBundledSnapshots(store, prefix, new Set(files.keys()))) {
189
+ const blob = TEXT_MEDIA.has(files.get(frame.file) ?? '')
190
+ ? {
191
+ contentType: frame.contentType,
192
+ text: new TextDecoder().decode(frame.bytes),
193
+ }
194
+ : {
195
+ contentType: frame.contentType,
196
+ base64: bytesToBase64(frame.bytes),
197
+ };
198
+ yield { type: 'blob', file: frame.file, blob };
199
+ }
200
+ yield { type: 'end', found: true };
201
+ return;
202
+ }
203
+ const readFile = await sourceReader(store, args);
204
+ const reads = [...files].map(([file, media]) => async () => {
205
+ const obj = await readFile(`${prefix}${file}`);
206
+ return { file, blob: obj === null ? null : frameBlob(obj, media) };
207
+ });
208
+ for await (const read of inCompletionOrder(reads, READ_WIDTH)) {
209
+ if (read.blob !== null) {
210
+ yield { type: 'blob', file: read.file, blob: read.blob };
211
+ }
212
+ }
213
+ yield { type: 'end', found: true };
214
+ }
@@ -0,0 +1,22 @@
1
+ /**
2
+ * A report test with its frame files in memory: what a viewer renders. Text
3
+ * media are inline, binary media base64.
4
+ */
5
+ import type { ReportTestStatus, RunReport } from './report-types.js';
6
+ /** One embedded frame file: text media inline, binary media as base64. */
7
+ export type HtmlReportBlob = {
8
+ readonly contentType: string;
9
+ readonly text?: string;
10
+ readonly base64?: string;
11
+ };
12
+ export type HtmlReportTest = {
13
+ readonly report: RunReport;
14
+ /** Keyed by the blob's `file` (relative to the test dir). */
15
+ readonly blobs: Readonly<Record<string, HtmlReportBlob>>;
16
+ };
17
+ export type HtmlReportBundle = {
18
+ readonly runId: string;
19
+ readonly status: ReportTestStatus;
20
+ readonly updatedAtMs: number;
21
+ readonly tests: readonly HtmlReportTest[];
22
+ };
@@ -0,0 +1,4 @@
1
+ /**
2
+ * A report test with its frame files in memory: what a viewer renders. Text
3
+ * media are inline, binary media base64.
4
+ */
@@ -0,0 +1,9 @@
1
+ /** Object-store key layout for one report bucket. */
2
+ export declare const RUNS_INDEX_KEY = "runs/index.json";
3
+ export declare function runPrefix(runId: string): string;
4
+ export declare function runManifestKey(runId: string): string;
5
+ export declare function testPrefix(runId: string, testId: string): string;
6
+ export declare function testReportKey(runId: string, testId: string): string;
7
+ export declare function testFrameKey(runId: string, testId: string, frameFile: string): string;
8
+ export declare function mediaExtension(media: string): string;
9
+ export declare function contentTypeForMedia(media: string): string;
@@ -0,0 +1,50 @@
1
+ /** Object-store key layout for one report bucket. */
2
+ export const RUNS_INDEX_KEY = 'runs/index.json';
3
+ export function runPrefix(runId) {
4
+ return `runs/${runId}`;
5
+ }
6
+ export function runManifestKey(runId) {
7
+ return `${runPrefix(runId)}/manifest.json`;
8
+ }
9
+ export function testPrefix(runId, testId) {
10
+ return `${runPrefix(runId)}/tests/${testId}`;
11
+ }
12
+ export function testReportKey(runId, testId) {
13
+ return `${testPrefix(runId, testId)}/report.json`;
14
+ }
15
+ export function testFrameKey(runId, testId, frameFile) {
16
+ const rel = frameFile.replace(/^\/+/, '');
17
+ return `${testPrefix(runId, testId)}/${rel}`;
18
+ }
19
+ export function mediaExtension(media) {
20
+ switch (media) {
21
+ case 'html':
22
+ return 'html';
23
+ case 'png':
24
+ return 'png';
25
+ case 'json':
26
+ return 'json';
27
+ case 'text':
28
+ return 'txt';
29
+ case 'bytes':
30
+ return 'bin';
31
+ default:
32
+ return 'bin';
33
+ }
34
+ }
35
+ export function contentTypeForMedia(media) {
36
+ switch (media) {
37
+ case 'html':
38
+ return 'text/html; charset=utf-8';
39
+ case 'png':
40
+ return 'image/png';
41
+ case 'json':
42
+ return 'application/json; charset=utf-8';
43
+ case 'text':
44
+ return 'text/plain; charset=utf-8';
45
+ case 'bytes':
46
+ return 'application/octet-stream';
47
+ default:
48
+ return 'application/octet-stream';
49
+ }
50
+ }
@@ -0,0 +1,39 @@
1
+ /**
2
+ * A commit's report, streamed from a reports API into a Solid store the
3
+ * viewer renders as it grows: the test list fills in source by source, and
4
+ * a test's steps and frame files stream in the first time it is opened.
5
+ * Transport and time are injected; nothing here touches a global.
6
+ */
7
+ import type { Timers } from './clock.js';
8
+ import type { CommitRecord, SourceRecord } from './protocol.js';
9
+ import type { ReportData } from './types.js';
10
+ import type { HttpClient } from './http.js';
11
+ import { type Accessor } from 'solid-js';
12
+ import type { ReportFetcher } from './fetcher.js';
13
+ import type { ReportTestLoader } from './report-loader.js';
14
+ export type RemoteReportArgs = {
15
+ readonly http: HttpClient;
16
+ readonly timers: Timers;
17
+ /** The reports API root (`https://api.example.com/api/reports`). */
18
+ readonly apiBase: string;
19
+ readonly sha: string;
20
+ /** Cached reads (sources, stylesheet, one test, one frame file). */
21
+ readonly fetcher: ReportFetcher;
22
+ };
23
+ export type RemoteReportPhase = 'connecting' | 'live' | 'complete' | 'missing' | 'error';
24
+ export type RemoteReport = {
25
+ readonly data: ReportData;
26
+ readonly commit: Accessor<CommitRecord | null>;
27
+ readonly phase: Accessor<RemoteReportPhase>;
28
+ readonly sources: Accessor<readonly SourceRecord[]>;
29
+ readonly frameCss: Accessor<string | undefined>;
30
+ readonly loader: ReportTestLoader;
31
+ readonly dispose: () => void;
32
+ };
33
+ /** Read an SSE response body, handing each message to `onEvent`. */
34
+ export declare function readEventStream<T>(response: Response, onEvent: (event: T) => void): Promise<void>;
35
+ /**
36
+ * Open a commit's report. The commit stream reconnects on its own (sending
37
+ * the sources it already holds) until the commit is complete or missing.
38
+ */
39
+ export declare function createRemoteReport(args: RemoteReportArgs): RemoteReport;
@@ -0,0 +1,304 @@
1
+ /**
2
+ * A commit's report, streamed from a reports API into a Solid store the
3
+ * viewer renders as it grows: the test list fills in source by source, and
4
+ * a test's steps and frame files stream in the first time it is opened.
5
+ * Transport and time are injected; nothing here touches a global.
6
+ */
7
+ import { splitViewerTestId, viewerTestId, } from './keys.js';
8
+ import { SseDecoder } from './protocol.js';
9
+ import { batch, createSignal } from 'solid-js';
10
+ import { createStore, produce } from 'solid-js/store';
11
+ /** Wait before reconnecting a stream that failed. */
12
+ const RETRY_MS = 3_000;
13
+ /** Read an SSE response body, handing each message to `onEvent`. */
14
+ export async function readEventStream(response, onEvent) {
15
+ if (!response.ok || response.body === null) {
16
+ throw new Error(`reports API answered ${String(response.status)}`);
17
+ }
18
+ const reader = response.body.getReader();
19
+ const text = new TextDecoder();
20
+ const decoder = new SseDecoder();
21
+ for (;;) {
22
+ const { value, done } = await reader.read();
23
+ if (done)
24
+ return;
25
+ for (const event of decoder.push(text.decode(value, { stream: true }))) {
26
+ onEvent(event);
27
+ }
28
+ }
29
+ }
30
+ function worstStatus(tests) {
31
+ const statuses = tests.map((t) => t.report.meta.status);
32
+ if (statuses.includes('failed'))
33
+ return 'failed';
34
+ if (statuses.length > 0 && statuses.every((s) => s === 'skipped')) {
35
+ return 'skipped';
36
+ }
37
+ return statuses.length === 0 ? 'skipped' : 'passed';
38
+ }
39
+ function stubTest(source, meta) {
40
+ return {
41
+ report: {
42
+ meta: { ...meta, testId: viewerTestId(source, meta.testId) },
43
+ steps: [],
44
+ },
45
+ blobs: {},
46
+ };
47
+ }
48
+ function reportLabel(sha, commit) {
49
+ const short = sha.slice(0, 9);
50
+ return commit === null ? short : `${short} · ${commit.branch}`;
51
+ }
52
+ function loading() {
53
+ return { kind: 'loading', frames: 0, framesTotal: null };
54
+ }
55
+ function reportUrl(ctx, path) {
56
+ return `${ctx.args.apiBase}/${ctx.args.sha}${path}`;
57
+ }
58
+ function disposed(ctx) {
59
+ return ctx.controller.signal.aborted;
60
+ }
61
+ function addSource(ctx, record, authoritative = true) {
62
+ if (!authoritative && ctx.sources.has(record.source))
63
+ return;
64
+ if (authoritative)
65
+ ctx.sources.add(record.source);
66
+ if (JSON.stringify(ctx.records.get(record.source)) === JSON.stringify(record))
67
+ return;
68
+ ctx.records.set(record.source, record);
69
+ ctx.addRecord(record);
70
+ for (const test of ctx.store.data.tests) {
71
+ if (splitViewerTestId(test.report.meta.testId)?.source === record.source)
72
+ ctx.setStore('loads', test.report.meta.testId, undefined);
73
+ }
74
+ ctx.setStore('data', produce((data) => {
75
+ data.tests = data.tests.filter((test) => splitViewerTestId(test.report.meta.testId)?.source !== record.source);
76
+ for (const meta of record.tests) {
77
+ data.tests.push(stubTest(record.source, meta));
78
+ }
79
+ data.status = streamedStatus(ctx, data.tests);
80
+ data.updatedAtMs = Math.max(data.updatedAtMs, record.updatedAtMs);
81
+ }));
82
+ }
83
+ function streamedStatus(ctx, tests) {
84
+ if (ctx.commit()?.status === 'failed' ||
85
+ [...ctx.records.values()].some((record) => record.status === 'failed'))
86
+ return 'failed';
87
+ return worstStatus(tests);
88
+ }
89
+ function onCommitEvent(ctx, event) {
90
+ if (event.type === 'commit') {
91
+ ctx.setCommit(event.commit);
92
+ ctx.setStore('data', 'status', streamedStatus(ctx, ctx.store.data.tests));
93
+ ctx.setStore('data', 'runId', reportLabel(ctx.args.sha, event.commit));
94
+ if (event.commit?.endedAtMs != null) {
95
+ ctx.setStore('data', 'updatedAtMs', event.commit.endedAtMs);
96
+ }
97
+ return;
98
+ }
99
+ if (event.type === 'source') {
100
+ ctx.setPhase('live');
101
+ addSource(ctx, event.source);
102
+ return;
103
+ }
104
+ ctx.setPhase(event.state === 'running' ? 'live' : event.state);
105
+ }
106
+ /** Follow the commit stream, reconnecting until it is complete or missing. */
107
+ async function followCommit(ctx) {
108
+ while (!disposed(ctx)) {
109
+ const have = [...ctx.sources].join(',');
110
+ const query = have === '' ? '' : `?have=${encodeURIComponent(have)}`;
111
+ let ended = false;
112
+ try {
113
+ const response = await ctx.args.http.fetch(reportUrl(ctx, `/events${query}`), { signal: ctx.controller.signal });
114
+ await readEventStream(response, (event) => {
115
+ if (event.type === 'end')
116
+ ended = true;
117
+ onCommitEvent(ctx, event);
118
+ });
119
+ }
120
+ catch {
121
+ ended = false;
122
+ }
123
+ if (disposed(ctx))
124
+ return;
125
+ if (ctx.phase() === 'complete' || ctx.phase() === 'missing')
126
+ return;
127
+ // A stream cut short (no `end`) is a failure: back off, then resume.
128
+ if (!ended) {
129
+ ctx.setPhase('error');
130
+ await waitForReconnect(ctx);
131
+ }
132
+ }
133
+ }
134
+ function waitForReconnect(ctx) {
135
+ return new Promise((resolve) => {
136
+ const signal = ctx.controller.signal;
137
+ const finish = () => {
138
+ ctx.args.timers.clearTimeout(timer);
139
+ signal.removeEventListener('abort', finish);
140
+ resolve();
141
+ };
142
+ const timer = ctx.args.timers.setTimeout(finish, RETRY_MS);
143
+ signal.addEventListener('abort', finish, { once: true });
144
+ if (signal.aborted)
145
+ finish();
146
+ });
147
+ }
148
+ function testIndex(ctx, id) {
149
+ return ctx.store.data.tests.findIndex((t) => t.report.meta.testId === id);
150
+ }
151
+ /** The timeline only. Frame files arrive later, one at a time, as they paint. */
152
+ async function loadTest(ctx, id) {
153
+ const parts = splitViewerTestId(id);
154
+ if (parts === null) {
155
+ ctx.setStore('loads', id, { kind: 'missing' });
156
+ return;
157
+ }
158
+ try {
159
+ const report = await ctx.args.fetcher.test(ctx.args.sha, parts.source, parts.testId);
160
+ if (disposed(ctx))
161
+ return;
162
+ if (report === null) {
163
+ ctx.setStore('loads', id, { kind: 'missing' });
164
+ return;
165
+ }
166
+ batch(() => {
167
+ const index = testIndex(ctx, id);
168
+ if (index >= 0) {
169
+ ctx.setStore('data', 'tests', index, 'report', 'steps', report.steps);
170
+ }
171
+ ctx.setStore('loads', id, { kind: 'ready' });
172
+ });
173
+ }
174
+ catch (error) {
175
+ if (disposed(ctx))
176
+ return;
177
+ ctx.setStore('loads', id, {
178
+ kind: 'error',
179
+ message: error instanceof Error ? error.message : String(error),
180
+ });
181
+ }
182
+ }
183
+ async function loadBlob(ctx, id, file) {
184
+ const key = `${id}\n${file}`;
185
+ if (ctx.inflight.has(key))
186
+ return;
187
+ const index = testIndex(ctx, id);
188
+ if (index < 0 || ctx.store.data.tests[index]?.blobs[file] !== undefined) {
189
+ return;
190
+ }
191
+ const parts = splitViewerTestId(id);
192
+ if (parts === null)
193
+ return;
194
+ ctx.inflight.add(key);
195
+ try {
196
+ const blob = await ctx.args.fetcher.blob(ctx.args.sha, parts.source, parts.testId, file);
197
+ if (!disposed(ctx) && blob !== null) {
198
+ const at = testIndex(ctx, id);
199
+ if (at >= 0)
200
+ ctx.setStore('data', 'tests', at, 'blobs', file, blob);
201
+ }
202
+ }
203
+ catch {
204
+ // The file can be asked for again. A 404 is not remembered.
205
+ }
206
+ finally {
207
+ ctx.inflight.delete(key);
208
+ }
209
+ }
210
+ function testLoader(ctx) {
211
+ return {
212
+ load(id) {
213
+ const current = ctx.store.loads[id];
214
+ if (current !== undefined && current.kind !== 'error')
215
+ return;
216
+ ctx.setStore('loads', id, loading());
217
+ void loadTest(ctx, id);
218
+ },
219
+ ensureBlob(id, file) {
220
+ void loadBlob(ctx, id, file);
221
+ },
222
+ state(id) {
223
+ const known = ctx.store.data.tests.some((t) => t.report.meta.testId === id);
224
+ if (!known) {
225
+ const settled = ctx.phase() === 'complete' || ctx.phase() === 'missing';
226
+ return settled ? { kind: 'missing' } : loading();
227
+ }
228
+ return ctx.store.loads[id] ?? loading();
229
+ },
230
+ };
231
+ }
232
+ /** Sources and the snapshot stylesheet, from the query cache when it has them. */
233
+ async function loadCached(ctx) {
234
+ try {
235
+ const [sources, css] = await Promise.allSettled([
236
+ ctx.args.fetcher.sources(ctx.args.sha),
237
+ ctx.args.fetcher.frameCss(ctx.args.sha),
238
+ ]);
239
+ if (disposed(ctx))
240
+ return;
241
+ if (sources.status === 'fulfilled')
242
+ for (const source of sources.value)
243
+ addSource(ctx, source, false);
244
+ ctx.setFrameCss(css.status === 'fulfilled' ? css.value : '');
245
+ if (sources.status === 'fulfilled' &&
246
+ sources.value.length > 0 &&
247
+ ctx.phase() === 'connecting')
248
+ ctx.setPhase('live');
249
+ }
250
+ catch {
251
+ if (!disposed(ctx) && ctx.frameCss() === undefined)
252
+ ctx.setFrameCss('');
253
+ }
254
+ }
255
+ /**
256
+ * Open a commit's report. The commit stream reconnects on its own (sending
257
+ * the sources it already holds) until the commit is complete or missing.
258
+ */
259
+ export function createRemoteReport(args) {
260
+ const [store, setStore] = createStore({
261
+ data: {
262
+ title: 'Spec report',
263
+ runId: reportLabel(args.sha, null),
264
+ status: 'skipped',
265
+ updatedAtMs: 0,
266
+ tests: [],
267
+ },
268
+ loads: {},
269
+ });
270
+ const [commit, setCommit] = createSignal(null);
271
+ const [records, setRecords] = createSignal([]);
272
+ const [phase, setPhase] = createSignal('connecting');
273
+ const [frameCss, setFrameCss] = createSignal();
274
+ const ctx = {
275
+ args,
276
+ store,
277
+ setStore,
278
+ setCommit,
279
+ commit,
280
+ phase,
281
+ setPhase,
282
+ frameCss,
283
+ setFrameCss,
284
+ sources: new Set(),
285
+ records: new Map(),
286
+ addRecord: (record) => setRecords((previous) => [
287
+ ...previous.filter((source) => source.source !== record.source),
288
+ record,
289
+ ]),
290
+ inflight: new Set(),
291
+ controller: new AbortController(),
292
+ };
293
+ void followCommit(ctx);
294
+ void loadCached(ctx);
295
+ return {
296
+ data: store.data,
297
+ commit,
298
+ phase,
299
+ sources: records,
300
+ frameCss,
301
+ loader: testLoader(ctx),
302
+ dispose: () => ctx.controller.abort(),
303
+ };
304
+ }
@@ -0,0 +1,22 @@
1
+ export type TestLoad = {
2
+ readonly kind: 'ready';
3
+ } | {
4
+ readonly kind: 'loading';
5
+ readonly frames: number;
6
+ readonly framesTotal: number | null;
7
+ } | {
8
+ readonly kind: 'missing';
9
+ } | {
10
+ readonly kind: 'error';
11
+ readonly message: string;
12
+ };
13
+ /**
14
+ * Loads a test's steps on first view, then individual frame files as the
15
+ * scrubber reaches them. Absent when every test arrives with its frames
16
+ * (a standalone HTML file).
17
+ */
18
+ export type ReportTestLoader = {
19
+ readonly load: (testId: string) => void;
20
+ readonly ensureBlob?: (testId: string, file: string) => void;
21
+ readonly state: (testId: string) => TestLoad;
22
+ };
File without changes
@@ -0,0 +1,10 @@
1
+ import type { ObjectStore } from './object-store.js';
2
+ import type { EvidenceStatus, ReportPart } from './explorer-types.js';
3
+ export declare function partStatus(part: ReportPart): EvidenceStatus;
4
+ export declare function validatePart(part: ReportPart): void;
5
+ /** Each producer owns a separate key, independent of the spec-run attempt. */
6
+ export declare function publishPart(store: ObjectStore, part: ReportPart): Promise<void>;
7
+ /** Latest result per producer, sorted stably; corrupt evidence fails visibly. */
8
+ export declare function readParts(store: ObjectStore, sha: string): Promise<readonly ReportPart[]>;
9
+ /** Ties are conservative and independent of write/list order. */
10
+ export declare function latestParts(parts: readonly ReportPart[]): readonly ReportPart[];