@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,259 @@
1
+ import { Hono } from 'hono';
2
+ import { createBundleReader } from './bundle.js';
3
+ import { eachLimited } from './completion-order.js';
4
+ import { createLiveEvidence } from './evidence-live.js';
5
+ import { isFramePath, isKeySegment } from './keys.js';
6
+ import { catalogEntries, catalogLayers } from './layer-catalog.js';
7
+ import { layerEvidence } from './layer-merge.js';
8
+ import { readLayer } from './layer-store.js';
9
+ import { readBytes, readUtf8 } from './object-io.js';
10
+ import { SPEC_RUN_ID } from './publish.js';
11
+ import { testFrameKey, testReportKey } from './recording-keys.js';
12
+ const TTL = 30_000;
13
+ const LOCAL_TTL = 1_500;
14
+ const PAGE = 40;
15
+ const CACHED_ENTRIES = 4_096;
16
+ export const LOCAL_BACKEND = 'local';
17
+ export const SHARED_BACKEND = 'shared';
18
+ /** The stores that contribute layers, in the order they answer for a recording. */
19
+ export function reportBackends(deps) {
20
+ const backends = [];
21
+ if (deps.localStore)
22
+ backends.push({ id: LOCAL_BACKEND, label: 'This machine', store: deps.localStore, live: true });
23
+ if (deps.store && deps.store !== deps.localStore)
24
+ backends.push({ id: SHARED_BACKEND, label: 'Shared reports', store: deps.store, live: false });
25
+ for (const backend of deps.backends ?? []) {
26
+ if (!/^[a-z0-9-]+$/.test(backend.id) || backends.some(known => known.id === backend.id))
27
+ throw new Error(`Invalid report backend id: "${backend.id}"`);
28
+ backends.push({ ...backend, live: false });
29
+ }
30
+ return backends;
31
+ }
32
+ function archived(id) {
33
+ return /^[0-9a-f]{64}$/.test(id);
34
+ }
35
+ /** Bounded pages of layer origins, each layer's checks read once, and the runs still writing. */
36
+ export function createLayersApp(deps) {
37
+ const backends = reportBackends(deps);
38
+ const listings = backends.map(() => undefined);
39
+ let pending = null;
40
+ const loaded = new Map();
41
+ const readers = new Map();
42
+ const live = createLiveEvidence({ live: deps.live, clock: deps.clock, backend: LOCAL_BACKEND });
43
+ function freshFor(backend) {
44
+ return backend.live ? LOCAL_TTL : TTL;
45
+ }
46
+ async function discover() {
47
+ pending ??= (async () => {
48
+ const now = deps.clock.now();
49
+ await Promise.all(backends.map(async (backend, i) => {
50
+ const prior = listings[i];
51
+ if (prior && now - prior.at < freshFor(backend))
52
+ return;
53
+ try {
54
+ listings[i] = { at: now, keys: await catalogEntries(backend.store), available: true };
55
+ }
56
+ catch {
57
+ // Keep what was listed before; the next refresh after the wait tries again.
58
+ listings[i] = { at: now, keys: prior?.keys ?? [], available: false };
59
+ }
60
+ }));
61
+ const entries = listings.flatMap((listing, backend) => (listing?.keys ?? []).map(key => ({ backend, key })));
62
+ // Immutable layers first, newest first: their index keys start with an inverted time.
63
+ return entries.sort((a, b) => Number(b.key.startsWith('layer:')) - Number(a.key.startsWith('layer:')) || (a.key < b.key ? -1 : a.key > b.key ? 1 : a.backend - b.backend));
64
+ })();
65
+ try {
66
+ return await pending;
67
+ }
68
+ finally {
69
+ pending = null;
70
+ }
71
+ }
72
+ /** An immutable layer is read once. A commit's mutable parts are re-read after a short wait. */
73
+ function entryEvidence(entry) {
74
+ const backend = backends[entry.backend];
75
+ const cacheKey = `${backend.id}|${entry.key}`;
76
+ const cached = loaded.get(cacheKey);
77
+ if (cached && (entry.key.startsWith('layer:') || deps.clock.now() - cached.at < freshFor(backend)))
78
+ return cached.value;
79
+ const value = catalogLayers(backend.store, entry.key).then(layers => layers.map(layer => layerEvidence(layer, backend.id)));
80
+ value.catch(() => {
81
+ if (loaded.get(cacheKey)?.value === value)
82
+ loaded.delete(cacheKey);
83
+ });
84
+ if (!cached && loaded.size >= CACHED_ENTRIES)
85
+ loaded.delete(loaded.keys().next().value);
86
+ loaded.set(cacheKey, { at: deps.clock.now(), value });
87
+ return value;
88
+ }
89
+ async function findEvidence(backend, id) {
90
+ const own = backends[backend].id === LOCAL_BACKEND ? live.find(id) : null;
91
+ if (own)
92
+ return own;
93
+ const sha = /^(?:source|part)-([0-9a-f]{40})-/.exec(id)?.[1];
94
+ const key = archived(id)
95
+ ? (await discover()).find(entry => entry.backend === backend && entry.key.endsWith(`-${id}.json`))?.key
96
+ : sha && `commit:${sha}`;
97
+ if (!key)
98
+ return null;
99
+ try {
100
+ return (await entryEvidence({ backend, key })).find(evidence => evidence.id === id) ?? null;
101
+ }
102
+ catch {
103
+ return null;
104
+ }
105
+ }
106
+ async function file(id, path) {
107
+ if (!archived(id))
108
+ return null;
109
+ let reader = readers.get(id);
110
+ if (!reader) {
111
+ for (const backend of backends) {
112
+ const layer = await readLayer(backend.store, id);
113
+ if (!layer)
114
+ continue;
115
+ reader = createBundleReader(backend.store, layer.bundle);
116
+ if (readers.size >= 8)
117
+ readers.delete(readers.keys().next().value);
118
+ readers.set(id, reader);
119
+ break;
120
+ }
121
+ }
122
+ return reader?.(path) ?? null;
123
+ }
124
+ async function activeRecords() {
125
+ const local = deps.localStore;
126
+ if (!local)
127
+ return [];
128
+ const runs = await Promise.all((await local.listKeys('active/')).map(async (key) => {
129
+ try {
130
+ const raw = await readUtf8(local, key);
131
+ if (raw === null)
132
+ return null;
133
+ const run = JSON.parse(raw);
134
+ if (!run.packageDir || !run.bucket || typeof run.startedAtMs !== 'number')
135
+ return null;
136
+ return { packageDir: run.packageDir, bucket: run.bucket, startedAtMs: run.startedAtMs, ...(typeof run.plannedChecks === 'number' ? { plannedChecks: run.plannedChecks } : {}) };
137
+ }
138
+ catch {
139
+ return null;
140
+ }
141
+ }));
142
+ return runs.filter(run => run !== null);
143
+ }
144
+ let outlineAt = -Infinity;
145
+ let outlinePending;
146
+ function currentOutline() {
147
+ if (!outlinePending || deps.clock.now() - outlineAt >= TTL) {
148
+ outlineAt = deps.clock.now();
149
+ outlinePending = deps.outline?.().catch(error => { outlinePending = undefined; throw error; });
150
+ }
151
+ return outlinePending;
152
+ }
153
+ const app = new Hono();
154
+ app.get('/outline', async (c) => {
155
+ const outline = await currentOutline();
156
+ if (!outline)
157
+ return c.json({ error: 'Current spec outline is unavailable' }, 503);
158
+ return c.json({ specs: outline.specs, sha: outline.sha, changedFiles: outline.changedFiles }, 200, { 'cache-control': 'no-cache' });
159
+ });
160
+ app.get('/outline/spec', async (c) => {
161
+ const outline = await currentOutline();
162
+ const path = c.req.query('path') ?? '';
163
+ if (!outline?.specs.some(entry => entry.path === path))
164
+ return c.notFound();
165
+ const text = await outline.readSpec(path);
166
+ return text === null ? c.notFound() : c.text(text);
167
+ });
168
+ app.get('/evidence', async (c) => {
169
+ const cursor = c.req.query('cursor') ?? '';
170
+ // Asking again reaches a store that was unavailable without waiting out its listing.
171
+ if (c.req.query('retry'))
172
+ for (const [i, listing] of listings.entries())
173
+ if (listing && !listing.available)
174
+ listings[i] = { ...listing, at: -Infinity };
175
+ const entries = await discover();
176
+ const ref = (entry) => `${backends[entry.backend].id}:${entry.key}`;
177
+ const after = cursor ? entries.findIndex(entry => ref(entry) === cursor) + 1 : 0;
178
+ if (cursor && after === 0)
179
+ return c.json({ error: 'Layer catalog changed; refresh the report.' }, 409);
180
+ const selected = entries.slice(after, after + PAGE);
181
+ const layers = [];
182
+ const notices = [];
183
+ if (!backends.length)
184
+ notices.push('No report store is configured.');
185
+ for (const [i, listing] of listings.entries())
186
+ if (listing && !listing.available)
187
+ notices.push(`${backends[i].label} is unavailable. Layers already loaded remain usable.`);
188
+ await eachLimited(selected, 8, async (entry) => {
189
+ try {
190
+ for (const evidence of await entryEvidence(entry))
191
+ layers.push(evidence.layer);
192
+ }
193
+ catch {
194
+ notices.push('A report layer could not be loaded. Its outcome is unknown.');
195
+ }
196
+ });
197
+ let active = [];
198
+ if (!cursor) {
199
+ const running = await live.refresh(await activeRecords(), layers);
200
+ layers.push(...running.layers.map(evidence => evidence.layer));
201
+ active = running.active;
202
+ }
203
+ layers.sort((a, b) => b.recordedAtMs - a.recordedAtMs || (a.id < b.id ? -1 : a.id > b.id ? 1 : 0));
204
+ const last = selected.at(-1);
205
+ const info = backends.map((backend, i) => ({ id: backend.id, label: backend.label, live: backend.live, available: listings[i]?.available ?? false }));
206
+ const page = { layers, notices: [...new Set(notices)], backends: info, active, cursor: after + PAGE < entries.length && last ? ref(last) : null };
207
+ return c.json(page, 200, { 'cache-control': 'no-cache' });
208
+ });
209
+ app.get('/evidence/rows/:backend/:id', async (c) => {
210
+ const backend = backends.findIndex(known => known.id === c.req.param('backend'));
211
+ const id = c.req.param('id');
212
+ const evidence = backend < 0 ? null : await findEvidence(backend, id);
213
+ if (!evidence)
214
+ return c.notFound();
215
+ const rows = { id: evidence.id, rows: evidence.rows, fingerprints: evidence.fingerprints };
216
+ return c.json(rows, 200, { 'cache-control': archived(id) ? 'public, max-age=31536000, immutable' : 'no-cache' });
217
+ });
218
+ app.get('/layers/:id/*', async (c) => {
219
+ const id = c.req.param('id');
220
+ let path;
221
+ try {
222
+ path = decodeURIComponent(new URL(c.req.url).pathname.split(`/layers/${id}/`)[1] ?? '');
223
+ }
224
+ catch {
225
+ return c.json({ error: 'Invalid recording path' }, 400);
226
+ }
227
+ const valid = path === 'frame.css' || /^tests\/[a-zA-Z0-9._-]+\/report\.json$/.test(path) || (path.startsWith('tests/') && isKeySegment(path.split('/')[1] ?? '') && path.split('/')[2] === 'files' && isFramePath(path.split('/').slice(3).join('/')));
228
+ if (!valid)
229
+ return c.notFound();
230
+ const body = await file(id, path);
231
+ return body ? new Response(new Uint8Array(body.bytes), { headers: { 'content-type': body.contentType, 'cache-control': 'public, max-age=31536000, immutable' } }) : c.notFound();
232
+ });
233
+ app.get('/live/:source/frame.css', async (c) => {
234
+ const css = live.store(c.req.param('source')) ? await deps.live?.frameCss?.() : null;
235
+ return css == null ? c.notFound() : c.body(css, 200, { 'content-type': 'text/css; charset=utf-8', 'cache-control': 'no-store' });
236
+ });
237
+ app.get('/live/:source/tests/:testId/*', async (c) => {
238
+ const source = c.req.param('source');
239
+ const testId = c.req.param('testId');
240
+ const store = isKeySegment(testId) ? live.store(source) : null;
241
+ if (!store)
242
+ return c.notFound();
243
+ let path;
244
+ try {
245
+ path = decodeURIComponent(new URL(c.req.url).pathname.split(`/tests/${testId}/`)[1] ?? '');
246
+ }
247
+ catch {
248
+ return c.json({ error: 'Invalid recording path' }, 400);
249
+ }
250
+ const frame = path.startsWith('files/') ? path.slice('files/'.length) : null;
251
+ if (path !== 'report.json' && !(frame && isFramePath(frame)))
252
+ return c.notFound();
253
+ const stored = await store.get(frame ? testFrameKey(SPEC_RUN_ID, testId, frame) : testReportKey(SPEC_RUN_ID, testId));
254
+ if (stored === null)
255
+ return c.notFound();
256
+ return new Response(new Uint8Array(await readBytes(stored.body)), { headers: { 'content-type': stored.contentType, 'cache-control': 'no-store' } });
257
+ });
258
+ return app;
259
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Validates a wire key before any remote object-store I/O. Every S3 command
3
+ * for the shared bucket must pass this gate so projects cannot clobber
4
+ * each other's prefixes.
5
+ */
6
+ export declare function assertRemoteObjectKey(key: string, namespace: string): void;
7
+ /**
8
+ * Validates a logical key before {@link ObjectStoreWithPrefix} scopes it under
9
+ * a namespace or feature prefix.
10
+ */
11
+ export declare function assertLogicalObjectKey(key: string): void;
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Validates a wire key before any remote object-store I/O. Every S3 command
3
+ * for the shared bucket must pass this gate so projects cannot clobber
4
+ * each other's prefixes.
5
+ */
6
+ export function assertRemoteObjectKey(key, namespace) {
7
+ if (key.length === 0) {
8
+ throw new Error('Remote object key must be non-empty');
9
+ }
10
+ if (key.startsWith('/')) {
11
+ throw new Error(`Remote object key must not start with "/": ${key}`);
12
+ }
13
+ if (key.includes('..')) {
14
+ throw new Error(`Remote object key must not contain "..": ${key}`);
15
+ }
16
+ const bareNamespace = namespace.endsWith('/')
17
+ ? namespace.slice(0, -1)
18
+ : namespace;
19
+ if (key === namespace || key === bareNamespace) {
20
+ throw new Error(`Remote object key must include a path after "${namespace}": ${key}`);
21
+ }
22
+ if (!key.startsWith(namespace)) {
23
+ throw new Error(`Remote object key must start with "${namespace}": ${key}`);
24
+ }
25
+ }
26
+ /**
27
+ * Validates a logical key before {@link ObjectStoreWithPrefix} scopes it under
28
+ * a namespace or feature prefix.
29
+ */
30
+ export function assertLogicalObjectKey(key) {
31
+ if (key.length === 0) {
32
+ throw new Error('Object store key must be non-empty');
33
+ }
34
+ if (key.startsWith('/')) {
35
+ throw new Error(`Object store key must not start with "/": ${key}`);
36
+ }
37
+ if (key.includes('..')) {
38
+ throw new Error(`Object store key must not contain "..": ${key}`);
39
+ }
40
+ }
@@ -0,0 +1,8 @@
1
+ /** Host navigation and keyboard events, scoped to one reports explorer. */
2
+ export type ReportNavigation = {
3
+ readonly readHash: () => string;
4
+ readonly writeHash: (hash: string, replace: boolean) => void;
5
+ readonly subscribeHash: (listener: () => void) => () => void;
6
+ readonly subscribeKeys: (listener: (event: KeyboardEvent) => void) => () => void;
7
+ readonly setTitle: (title: string) => void;
8
+ };
File without changes
@@ -0,0 +1,9 @@
1
+ import type { ObjectStore } from './object-store.js';
2
+ /** Drain a {@link ReadableStream} into a single byte array. */
3
+ export declare function readBytes(stream: ReadableStream<Uint8Array>): Promise<Uint8Array>;
4
+ /** Read object bytes as UTF-8 text, or `null` when the key is missing. */
5
+ export declare function readUtf8(store: ObjectStore, key: string): Promise<string | null>;
6
+ /** Write UTF-8 text under `key` with the given content type (default text/plain). */
7
+ export declare function writeUtf8(store: ObjectStore, key: string, text: string, contentType?: string): Promise<void>;
8
+ /** Read object bytes, or `null` when the key is missing. */
9
+ export declare function readObjectBytes(store: ObjectStore, key: string): Promise<Uint8Array | null>;
@@ -0,0 +1,41 @@
1
+ /** Drain a {@link ReadableStream} into a single byte array. */
2
+ export async function readBytes(stream) {
3
+ const reader = stream.getReader();
4
+ const chunks = [];
5
+ let total = 0;
6
+ for (;;) {
7
+ const { value, done } = await reader.read();
8
+ if (done)
9
+ break;
10
+ if (value !== undefined) {
11
+ chunks.push(value);
12
+ total += value.byteLength;
13
+ }
14
+ }
15
+ const out = new Uint8Array(total);
16
+ let offset = 0;
17
+ for (const chunk of chunks) {
18
+ out.set(chunk, offset);
19
+ offset += chunk.byteLength;
20
+ }
21
+ return out;
22
+ }
23
+ /** Read object bytes as UTF-8 text, or `null` when the key is missing. */
24
+ export async function readUtf8(store, key) {
25
+ const obj = await store.get(key);
26
+ if (obj === null)
27
+ return null;
28
+ const bytes = await readBytes(obj.body);
29
+ return new TextDecoder().decode(bytes);
30
+ }
31
+ /** Write UTF-8 text under `key` with the given content type (default text/plain). */
32
+ export async function writeUtf8(store, key, text, contentType = 'text/plain; charset=utf-8') {
33
+ await store.put(key, new TextEncoder().encode(text), contentType);
34
+ }
35
+ /** Read object bytes, or `null` when the key is missing. */
36
+ export async function readObjectBytes(store, key) {
37
+ const obj = await store.get(key);
38
+ if (obj === null)
39
+ return null;
40
+ return readBytes(obj.body);
41
+ }
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Content-addressed binary object storage.
3
+ *
4
+ * Tiny, async, transport-agnostic surface — exactly what a "blob keyed by id"
5
+ * cache needs (TTS audio, exported files, derived avatars, etc.). Implementations
6
+ * live in sibling `impl-*.ts` files (S3/R2, in-memory, file-system).
7
+ *
8
+ * Mirrors the {@link ../sql-client/interface.ts SqlClient} shape: one interface,
9
+ * multiple `impl-*.ts` siblings, plus a conformance suite in `interface.test.ts`.
10
+ *
11
+ * Read semantics:
12
+ * - {@link ObjectStore.get} returns `null` when the key is missing. Transport /
13
+ * parse errors throw.
14
+ * - {@link ObjectStore.head} returns `false` for a missing key (never throws on
15
+ * "not found"). Transport errors throw.
16
+ *
17
+ * Write semantics:
18
+ * - {@link ObjectStore.put} MUST be idempotent: writing the same key twice
19
+ * overwrites without error. Stored `contentType` MUST round-trip through
20
+ * {@link ObjectStore.get}.
21
+ * - {@link ObjectStore.delete} on a missing key is a no-op (does not throw).
22
+ *
23
+ * URI semantics:
24
+ * - {@link ObjectStore.getUri} returns a URL the host runtime can load
25
+ * directly (`file://` on native, `blob:` on web). Stores that don't expose
26
+ * direct URLs (e.g. remote R2 — bytes must be streamed through HTTP)
27
+ * return `null`. Returns `null` when the key is missing.
28
+ *
29
+ * Listing:
30
+ * - {@link ObjectStore.listKeys} returns keys in sorted code-unit order
31
+ * (deterministic). Optional `prefix` filters to keys that start with it.
32
+ */
33
+ export interface StoredObject {
34
+ /** Stream of stored bytes. Single-use; consume or discard before issuing more I/O on the same store. */
35
+ readonly body: ReadableStream<Uint8Array>;
36
+ readonly contentType: string;
37
+ readonly size: number;
38
+ }
39
+ export interface ObjectStore {
40
+ /** Returns the stored object, or `null` if no value exists for `key`. */
41
+ get(key: string): Promise<StoredObject | null>;
42
+ /** Idempotently writes `bytes` under `key` with the given `contentType`. */
43
+ put(key: string, bytes: Uint8Array, contentType: string): Promise<void>;
44
+ /** `true` iff a value exists for `key`. Never throws on "not found". */
45
+ head(key: string): Promise<boolean>;
46
+ /** Removes the value at `key` if present. No-op when missing. */
47
+ delete(key: string): Promise<void>;
48
+ /** Remove every key, idempotently. Errors (including partial failure) throw. */
49
+ deleteMany(keys: readonly string[]): Promise<void>;
50
+ /**
51
+ * Returns a URL that the host runtime can load directly without reading bytes
52
+ * through {@link ObjectStore.get} again. Returns `null` for missing keys, and
53
+ * `null` for stores whose backend has no addressable URL (e.g. R2).
54
+ */
55
+ getUri(key: string): Promise<string | null>;
56
+ /**
57
+ * Returns keys present in the store, sorted by code-unit order.
58
+ * When `prefix` is set, only keys that start with it are returned.
59
+ */
60
+ listKeys(prefix?: string): Promise<readonly string[]>;
61
+ /**
62
+ * Returns a new store scoped to `prefix`. Caller keys are relative (must not
63
+ * include the prefix). All I/O operates on `prefix + key`. Nested calls compose
64
+ * prefixes (`a/` + `b/` → `a/b/`).
65
+ */
66
+ withPrefix(prefix: string): ObjectStore;
67
+ }
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Content-addressed binary object storage.
3
+ *
4
+ * Tiny, async, transport-agnostic surface — exactly what a "blob keyed by id"
5
+ * cache needs (TTS audio, exported files, derived avatars, etc.). Implementations
6
+ * live in sibling `impl-*.ts` files (S3/R2, in-memory, file-system).
7
+ *
8
+ * Mirrors the {@link ../sql-client/interface.ts SqlClient} shape: one interface,
9
+ * multiple `impl-*.ts` siblings, plus a conformance suite in `interface.test.ts`.
10
+ *
11
+ * Read semantics:
12
+ * - {@link ObjectStore.get} returns `null` when the key is missing. Transport /
13
+ * parse errors throw.
14
+ * - {@link ObjectStore.head} returns `false` for a missing key (never throws on
15
+ * "not found"). Transport errors throw.
16
+ *
17
+ * Write semantics:
18
+ * - {@link ObjectStore.put} MUST be idempotent: writing the same key twice
19
+ * overwrites without error. Stored `contentType` MUST round-trip through
20
+ * {@link ObjectStore.get}.
21
+ * - {@link ObjectStore.delete} on a missing key is a no-op (does not throw).
22
+ *
23
+ * URI semantics:
24
+ * - {@link ObjectStore.getUri} returns a URL the host runtime can load
25
+ * directly (`file://` on native, `blob:` on web). Stores that don't expose
26
+ * direct URLs (e.g. remote R2 — bytes must be streamed through HTTP)
27
+ * return `null`. Returns `null` when the key is missing.
28
+ *
29
+ * Listing:
30
+ * - {@link ObjectStore.listKeys} returns keys in sorted code-unit order
31
+ * (deterministic). Optional `prefix` filters to keys that start with it.
32
+ */
@@ -0,0 +1,9 @@
1
+ import type { RunReport } from './report-types.js';
2
+ import type { ReportPart } from './explorer-types.js';
3
+ /** Diagnostic budgets over recorded self time; no missing measurement becomes a pass. */
4
+ export declare function performancePart(args: {
5
+ readonly sha: string;
6
+ readonly recordedAtMs: number;
7
+ readonly allowanceMs: number;
8
+ readonly reports: readonly RunReport[];
9
+ }): ReportPart;
@@ -0,0 +1,42 @@
1
+ import { performanceRows } from './performance.js';
2
+ /** Diagnostic budgets over recorded self time; no missing measurement becomes a pass. */
3
+ export function performancePart(args) {
4
+ if (!Number.isFinite(args.allowanceMs) || args.allowanceMs <= 0)
5
+ throw new Error('Invalid performance allowance');
6
+ return {
7
+ sha: args.sha,
8
+ id: 'recorded-performance',
9
+ kind: 'performance',
10
+ title: 'Recorded action performance',
11
+ recordedAtMs: args.recordedAtMs,
12
+ checks: args.reports.map((report) => {
13
+ const rows = performanceRows(report.steps, args.allowanceMs);
14
+ const measured = rows.filter((row) => row.band !== 'unmeasured');
15
+ const slow = rows.filter((row) => row.band === 'slow').length;
16
+ const maximum = measured.reduce((max, row) => Math.max(max, row.step.performance.selfMs), 0);
17
+ return {
18
+ id: report.meta.testId,
19
+ title: report.meta.title,
20
+ status: slow > 0
21
+ ? 'failed'
22
+ : rows.length === 0 || rows.length !== measured.length
23
+ ? 'not-run'
24
+ : 'passed',
25
+ detail: `${measured.length} measured steps; ${slow} over the diagnostic allowance. Includes waits and harness work.`,
26
+ ...(report.meta.sourceFile === undefined
27
+ ? {}
28
+ : { specPath: report.meta.sourceFile }),
29
+ measurements: measured.length === 0
30
+ ? []
31
+ : [
32
+ {
33
+ name: 'Longest own action',
34
+ value: maximum,
35
+ unit: 'ms',
36
+ limit: args.allowanceMs,
37
+ },
38
+ ],
39
+ };
40
+ }),
41
+ };
42
+ }
@@ -0,0 +1,13 @@
1
+ import type { ReportStep } from './report-types.js';
2
+ import { type FlatFrame, type FlatStep } from './frames.js';
3
+ export type HeatBand = 'unmeasured' | 'cool' | 'warm' | 'slow';
4
+ export type PerformanceRow = FlatStep & {
5
+ readonly band: HeatBand;
6
+ };
7
+ export declare function heatBand(ms: number | undefined, allowanceMs: number): HeatBand;
8
+ export declare function performanceRows(steps: readonly ReportStep[], allowanceMs: number): PerformanceRow[];
9
+ /** Self time attributes nested work once, instead of ranking every ancestor as slow. */
10
+ export declare function performanceHotspots(rows: readonly PerformanceRow[]): PerformanceRow[];
11
+ export declare function measuredMs(ms: number): string;
12
+ /** Linear lookup for a large heatmap; includes a parent's first descendant frame. */
13
+ export declare function performanceFrameIndex(rows: readonly PerformanceRow[], frames: readonly FlatFrame[]): ReadonlyMap<ReportStep, FlatFrame>;
@@ -0,0 +1,45 @@
1
+ import { flattenSteps } from './frames.js';
2
+ export function heatBand(ms, allowanceMs) {
3
+ if (ms === undefined || !Number.isFinite(ms) || ms < 0)
4
+ return 'unmeasured';
5
+ if (ms > allowanceMs)
6
+ return 'slow';
7
+ return ms > allowanceMs / 4 ? 'warm' : 'cool';
8
+ }
9
+ export function performanceRows(steps, allowanceMs) {
10
+ return flattenSteps(steps).map((row) => ({
11
+ ...row,
12
+ band: heatBand(row.step.performance?.selfMs, allowanceMs),
13
+ }));
14
+ }
15
+ /** Self time attributes nested work once, instead of ranking every ancestor as slow. */
16
+ export function performanceHotspots(rows) {
17
+ return rows
18
+ .filter((row) => row.band !== 'unmeasured')
19
+ .sort((a, b) => (b.step.performance?.selfMs ?? 0) - (a.step.performance?.selfMs ?? 0) ||
20
+ a.index - b.index);
21
+ }
22
+ export function measuredMs(ms) {
23
+ return `${ms.toFixed(2)} ms`;
24
+ }
25
+ /** Linear lookup for a large heatmap; includes a parent's first descendant frame. */
26
+ export function performanceFrameIndex(rows, frames) {
27
+ const byStep = new Map();
28
+ for (const frame of frames) {
29
+ if (!byStep.has(frame.step))
30
+ byStep.set(frame.step, frame);
31
+ }
32
+ for (let i = rows.length - 1; i >= 0; i -= 1) {
33
+ const step = rows[i].step;
34
+ if (byStep.has(step))
35
+ continue;
36
+ for (const child of step.steps) {
37
+ const frame = byStep.get(child);
38
+ if (frame === undefined)
39
+ continue;
40
+ byStep.set(step, frame);
41
+ break;
42
+ }
43
+ }
44
+ return byStep;
45
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Persist the TanStack query cache. Frame-file queries stay out of this
3
+ * document — those bytes live in {@link ReportCache} on their own keys.
4
+ */
5
+ import { type Persister } from '@tanstack/query-persist-client-core';
6
+ import type { QueryClient } from '@tanstack/solid-query';
7
+ import type { ReportCache } from './fetcher.js';
8
+ export declare function reportPersister(cache: ReportCache): Persister;
9
+ /** Restore the cache, then keep writing it. Returns unsubscribe. */
10
+ export declare function attachReportCache(client: QueryClient, cache: ReportCache): [() => void, Promise<void>];
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Persist the TanStack query cache. Frame-file queries stay out of this
3
+ * document — those bytes live in {@link ReportCache} on their own keys.
4
+ */
5
+ import { persistQueryClient, } from '@tanstack/query-persist-client-core';
6
+ const CACHE_KEY = 'reports-query-cache';
7
+ const WEEK = 7 * 24 * 60 * 60 * 1000;
8
+ export function reportPersister(cache) {
9
+ return {
10
+ persistClient: (client) => cache.set(CACHE_KEY, JSON.stringify(client)),
11
+ restoreClient: async () => {
12
+ const raw = await cache.get(CACHE_KEY);
13
+ if (raw === null)
14
+ return undefined;
15
+ try {
16
+ return JSON.parse(raw);
17
+ }
18
+ catch {
19
+ await cache.remove(CACHE_KEY);
20
+ return undefined;
21
+ }
22
+ },
23
+ removeClient: () => cache.remove(CACHE_KEY),
24
+ };
25
+ }
26
+ function keepQuery(query) {
27
+ return query.state.status === 'success' && query.queryKey[1] !== 'blob';
28
+ }
29
+ /** Restore the cache, then keep writing it. Returns unsubscribe. */
30
+ export function attachReportCache(client, cache) {
31
+ return persistQueryClient({
32
+ queryClient: client,
33
+ persister: reportPersister(cache),
34
+ maxAge: WEEK,
35
+ buster: 'reports-v2',
36
+ dehydrateOptions: { shouldDehydrateQuery: keepQuery },
37
+ });
38
+ }
@@ -0,0 +1,5 @@
1
+ export declare const PHONE_W = 390;
2
+ export declare const PHONE_H = 844;
3
+ export declare function zoomScale(mode: string, wrapW: number, wrapH: number): number;
4
+ /** Drop scripts and the external shell stylesheet (frameCss replaces it). */
5
+ export declare function cleanSnapshotHtml(html: string): string;