@crvouga/atlas 0.3.0 → 0.5.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 (429) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +178 -604
  3. package/dist/bun/src/driver.d.ts +41 -7
  4. package/dist/bun/src/driver.js +47 -6
  5. package/dist/bun/src/index.d.ts +8 -0
  6. package/dist/bun/src/index.js +8 -0
  7. package/dist/core/src/cli.d.ts +14 -1
  8. package/dist/core/src/cli.js +30 -1
  9. package/dist/core/src/config.d.ts +70 -4
  10. package/dist/core/src/config.js +62 -3
  11. package/dist/core/src/events/cloudevents.d.ts +54 -9
  12. package/dist/core/src/events/cloudevents.js +60 -9
  13. package/dist/core/src/graph.d.ts +21 -0
  14. package/dist/core/src/graph.js +26 -0
  15. package/dist/core/src/index.d.ts +11 -0
  16. package/dist/core/src/index.js +11 -0
  17. package/dist/core/src/lint.d.ts +30 -0
  18. package/dist/core/src/lint.js +13 -0
  19. package/dist/core/src/plan.d.ts +39 -1
  20. package/dist/core/src/plan.js +28 -1
  21. package/dist/core/src/privacy.d.ts +29 -2
  22. package/dist/core/src/privacy.js +19 -2
  23. package/dist/core/src/report/formats.d.ts +34 -4
  24. package/dist/core/src/report/formats.js +24 -4
  25. package/dist/core/src/report/markdown.d.ts +15 -0
  26. package/dist/core/src/report/markdown.js +8 -0
  27. package/dist/core/src/run/clients.d.ts +44 -5
  28. package/dist/core/src/run/clients.js +46 -2
  29. package/dist/core/src/run/execute.d.ts +69 -16
  30. package/dist/core/src/run/execute.js +67 -16
  31. package/dist/core/src/run/frames.d.ts +13 -4
  32. package/dist/core/src/run/frames.js +19 -4
  33. package/dist/core/src/run/http.d.ts +34 -1
  34. package/dist/core/src/run/http.js +14 -1
  35. package/dist/core/src/run/types.d.ts +76 -3
  36. package/dist/core/src/run/types.js +19 -3
  37. package/dist/core/src/signals.d.ts +49 -5
  38. package/dist/core/src/signals.js +22 -5
  39. package/dist/core/src/spec/compose.d.ts +23 -1
  40. package/dist/core/src/spec/compose.js +10 -1
  41. package/dist/core/src/spec/index.d.ts +9 -1
  42. package/dist/core/src/spec/index.js +9 -1
  43. package/dist/core/src/spec/load.d.ts +22 -1
  44. package/dist/core/src/spec/load.js +31 -1
  45. package/dist/core/src/spec/parse.d.ts +16 -2
  46. package/dist/core/src/spec/parse.js +25 -2
  47. package/dist/core/src/spec/scxml.d.ts +25 -4
  48. package/dist/core/src/spec/scxml.js +36 -4
  49. package/dist/core/src/spec/types.d.ts +52 -1
  50. package/dist/core/src/spec/types.js +24 -0
  51. package/dist/detox/src/index.d.ts +54 -11
  52. package/dist/detox/src/index.js +45 -7
  53. package/dist/playwright/src/driver.d.ts +39 -3
  54. package/dist/playwright/src/driver.js +27 -3
  55. package/dist/playwright/src/index.d.ts +10 -0
  56. package/dist/playwright/src/index.js +10 -0
  57. package/dist/playwright/src/media.d.ts +50 -4
  58. package/dist/playwright/src/media.js +55 -4
  59. package/dist/playwright/src/signals.d.ts +44 -5
  60. package/dist/playwright/src/signals.js +29 -5
  61. package/dist/reports/src/attachment-files.d.ts +44 -0
  62. package/dist/reports/src/attachment-files.js +121 -0
  63. package/dist/reports/src/attachment-response.d.ts +31 -0
  64. package/dist/reports/src/attachment-response.js +55 -0
  65. package/dist/reports/src/aws-credentials.d.ts +48 -0
  66. package/dist/reports/src/aws-credentials.js +221 -0
  67. package/dist/reports/src/browser-navigation.d.ts +14 -0
  68. package/dist/reports/src/browser-navigation.js +7 -0
  69. package/dist/reports/src/bundle.d.ts +34 -2
  70. package/dist/reports/src/bundle.js +38 -2
  71. package/dist/reports/src/catalog.d.ts +34 -3
  72. package/dist/reports/src/catalog.js +17 -3
  73. package/dist/reports/src/cli-stores.d.ts +36 -0
  74. package/dist/reports/src/cli-stores.js +73 -0
  75. package/dist/reports/src/clock.d.ts +7 -2
  76. package/dist/reports/src/clock.js +5 -2
  77. package/dist/reports/src/completion-order.d.ts +18 -5
  78. package/dist/reports/src/completion-order.js +18 -5
  79. package/dist/reports/src/compose.d.ts +80 -0
  80. package/dist/reports/src/compose.js +117 -0
  81. package/dist/reports/src/copy-store.d.ts +29 -0
  82. package/dist/reports/src/copy-store.js +52 -0
  83. package/dist/reports/src/directory-outline.d.ts +38 -0
  84. package/dist/reports/src/directory-outline.js +103 -0
  85. package/dist/reports/src/directory-store.d.ts +20 -0
  86. package/dist/reports/src/directory-store.js +166 -0
  87. package/dist/reports/src/doctor.d.ts +29 -0
  88. package/dist/reports/src/doctor.js +83 -0
  89. package/dist/reports/src/effective-delta.d.ts +34 -0
  90. package/dist/reports/src/effective-delta.js +25 -0
  91. package/dist/reports/src/evidence-detail.d.ts +64 -0
  92. package/dist/reports/src/evidence-detail.js +92 -0
  93. package/dist/reports/src/evidence-live.d.ts +40 -4
  94. package/dist/reports/src/evidence-live.js +52 -4
  95. package/dist/reports/src/evidence-types.d.ts +70 -1
  96. package/dist/reports/src/evidence-types.js +5 -1
  97. package/dist/reports/src/evidence-view.d.ts +58 -6
  98. package/dist/reports/src/evidence-view.js +44 -5
  99. package/dist/reports/src/explorer-types.d.ts +40 -3
  100. package/dist/reports/src/explorer-types.js +8 -0
  101. package/dist/reports/src/explorer-versions.d.ts +9 -1
  102. package/dist/reports/src/explorer-versions.js +18 -1
  103. package/dist/reports/src/export-evidence.d.ts +16 -1
  104. package/dist/reports/src/export-evidence.js +52 -1
  105. package/dist/reports/src/fetcher.d.ts +27 -3
  106. package/dist/reports/src/fetcher.js +11 -3
  107. package/dist/reports/src/format-date.d.ts +6 -0
  108. package/dist/reports/src/format-date.js +6 -0
  109. package/dist/reports/src/format.d.ts +16 -0
  110. package/dist/reports/src/format.js +10 -0
  111. package/dist/reports/src/frames.d.ts +36 -3
  112. package/dist/reports/src/frames.js +22 -3
  113. package/dist/reports/src/hash.d.ts +18 -0
  114. package/dist/reports/src/hash.js +13 -0
  115. package/dist/reports/src/host.d.ts +29 -0
  116. package/dist/reports/src/host.js +64 -0
  117. package/dist/reports/src/html-frame.d.ts +5 -6
  118. package/dist/reports/src/html-frame.js +12 -1
  119. package/dist/reports/src/http-store.d.ts +52 -0
  120. package/dist/reports/src/http-store.js +145 -0
  121. package/dist/reports/src/http.d.ts +7 -0
  122. package/dist/reports/src/http.js +6 -0
  123. package/dist/reports/src/ingest.d.ts +54 -0
  124. package/dist/reports/src/ingest.js +210 -0
  125. package/dist/reports/src/init.d.ts +40 -0
  126. package/dist/reports/src/init.js +364 -0
  127. package/dist/reports/src/junit.d.ts +33 -6
  128. package/dist/reports/src/junit.js +87 -21
  129. package/dist/reports/src/keyboard.d.ts +20 -0
  130. package/dist/reports/src/keyboard.js +11 -0
  131. package/dist/reports/src/keys.d.ts +21 -3
  132. package/dist/reports/src/keys.js +21 -3
  133. package/dist/reports/src/layer-axes.d.ts +48 -0
  134. package/dist/reports/src/layer-axes.js +71 -0
  135. package/dist/reports/src/layer-catalog.d.ts +28 -1
  136. package/dist/reports/src/layer-catalog.js +19 -2
  137. package/dist/reports/src/layer-merge.d.ts +98 -9
  138. package/dist/reports/src/layer-merge.js +154 -33
  139. package/dist/reports/src/layer-source.d.ts +18 -0
  140. package/dist/reports/src/layer-source.js +14 -0
  141. package/dist/reports/src/layer-store.d.ts +42 -0
  142. package/dist/reports/src/layer-store.js +63 -5
  143. package/dist/reports/src/layer-types.d.ts +34 -1
  144. package/dist/reports/src/layers-route.d.ts +47 -4
  145. package/dist/reports/src/layers-route.js +88 -12
  146. package/dist/reports/src/media-types.d.ts +11 -0
  147. package/dist/reports/src/media-types.js +22 -0
  148. package/dist/reports/src/namespace-guard.d.ts +10 -1
  149. package/dist/reports/src/namespace-guard.js +10 -1
  150. package/dist/reports/src/navigation.d.ts +12 -0
  151. package/dist/reports/src/navigation.js +7 -0
  152. package/dist/reports/src/object-io.d.ts +11 -4
  153. package/dist/reports/src/object-io.js +4 -4
  154. package/dist/reports/src/object-store-conformance.d.ts +27 -0
  155. package/dist/reports/src/object-store-conformance.js +87 -0
  156. package/dist/reports/src/object-store.d.ts +33 -24
  157. package/dist/reports/src/object-store.js +17 -24
  158. package/dist/reports/src/parse-with.d.ts +16 -0
  159. package/dist/reports/src/parse-with.js +14 -0
  160. package/dist/reports/src/performance-part.d.ts +25 -1
  161. package/dist/reports/src/performance-part.js +15 -1
  162. package/dist/reports/src/performance.d.ts +22 -2
  163. package/dist/reports/src/performance.js +12 -2
  164. package/dist/reports/src/persist.d.ts +12 -3
  165. package/dist/reports/src/persist.js +12 -3
  166. package/dist/reports/src/phone.d.ts +18 -1
  167. package/dist/reports/src/phone.js +18 -1
  168. package/dist/reports/src/protocol.d.ts +30 -4
  169. package/dist/reports/src/protocol.js +15 -4
  170. package/dist/reports/src/publication.d.ts +21 -1
  171. package/dist/reports/src/publication.js +21 -1
  172. package/dist/reports/src/publish-checks.d.ts +35 -0
  173. package/dist/reports/src/publish-checks.js +122 -0
  174. package/dist/reports/src/publish.d.ts +44 -9
  175. package/dist/reports/src/publish.js +40 -7
  176. package/dist/reports/src/read.d.ts +46 -16
  177. package/dist/reports/src/read.js +38 -14
  178. package/dist/reports/src/recording-bundle.d.ts +13 -2
  179. package/dist/reports/src/recording-bundle.js +4 -2
  180. package/dist/reports/src/recording-keys.d.ts +15 -1
  181. package/dist/reports/src/recording-keys.js +15 -1
  182. package/dist/reports/src/remote-report.d.ts +28 -7
  183. package/dist/reports/src/remote-report.js +15 -7
  184. package/dist/reports/src/report-cli.d.ts +51 -0
  185. package/dist/reports/src/report-cli.js +485 -0
  186. package/dist/reports/src/report-index.d.ts +99 -0
  187. package/dist/reports/src/report-index.js +337 -0
  188. package/dist/reports/src/report-loader.d.ts +10 -0
  189. package/dist/reports/src/report-loader.js +6 -0
  190. package/dist/reports/src/report-parts.d.ts +29 -3
  191. package/dist/reports/src/report-parts.js +40 -3
  192. package/dist/reports/src/report-status.d.ts +48 -0
  193. package/dist/reports/src/report-status.js +156 -0
  194. package/dist/reports/src/report-types.d.ts +73 -0
  195. package/dist/reports/src/report-types.js +3 -0
  196. package/dist/reports/src/reporter.d.ts +67 -0
  197. package/dist/reports/src/reporter.js +128 -0
  198. package/dist/reports/src/reports-explorer-route.d.ts +541 -0
  199. package/dist/reports/src/reports-explorer-route.js +11 -0
  200. package/dist/reports/src/reports-list-route.d.ts +9 -0
  201. package/dist/reports/src/reports-list-route.js +9 -0
  202. package/dist/reports/src/reports-route.d.ts +561 -22
  203. package/dist/reports/src/reports-route.js +11 -3
  204. package/dist/reports/src/reports-store.d.ts +19 -2
  205. package/dist/reports/src/reports-store.js +10 -2
  206. package/dist/reports/src/resilient-cache.d.ts +14 -1
  207. package/dist/reports/src/resilient-cache.js +8 -1
  208. package/dist/reports/src/retention.d.ts +17 -1
  209. package/dist/reports/src/retention.js +5 -1
  210. package/dist/reports/src/run-bundle.d.ts +63 -0
  211. package/dist/reports/src/run-bundle.js +159 -0
  212. package/dist/reports/src/s3-store.d.ts +79 -0
  213. package/dist/reports/src/s3-store.js +295 -0
  214. package/dist/reports/src/scenario.d.ts +14 -4
  215. package/dist/reports/src/scenario.js +5 -4
  216. package/dist/reports/src/sigv4.d.ts +72 -0
  217. package/dist/reports/src/sigv4.js +96 -0
  218. package/dist/reports/src/snapshot-bundle.d.ts +24 -1
  219. package/dist/reports/src/snapshot-bundle.js +27 -2
  220. package/dist/reports/src/snapshot-surface.d.ts +20 -9
  221. package/dist/reports/src/snapshot-surface.js +32 -19
  222. package/dist/reports/src/source-content.d.ts +23 -0
  223. package/dist/reports/src/source-content.js +30 -1
  224. package/dist/reports/src/source-entries.d.ts +13 -0
  225. package/dist/reports/src/source-entries.js +6 -0
  226. package/dist/reports/src/source-part.d.ts +15 -0
  227. package/dist/reports/src/source-part.js +8 -0
  228. package/dist/reports/src/source-reader.d.ts +11 -0
  229. package/dist/reports/src/source-reader.js +5 -0
  230. package/dist/reports/src/spec-evidence.d.ts +12 -2
  231. package/dist/reports/src/spec-evidence.js +6 -2
  232. package/dist/reports/src/spec-inventory.d.ts +15 -1
  233. package/dist/reports/src/spec-inventory.js +22 -1
  234. package/dist/reports/src/spec-link.d.ts +28 -0
  235. package/dist/reports/src/spec-link.js +70 -0
  236. package/dist/reports/src/spec-set.d.ts +63 -0
  237. package/dist/reports/src/spec-set.js +177 -0
  238. package/dist/reports/src/status.d.ts +10 -0
  239. package/dist/reports/src/status.js +5 -0
  240. package/dist/reports/src/store-prefix.d.ts +28 -0
  241. package/dist/reports/src/store-prefix.js +22 -0
  242. package/dist/reports/src/store-url.d.ts +89 -0
  243. package/dist/reports/src/store-url.js +160 -0
  244. package/dist/reports/src/test-catalog-entity.d.ts +11 -0
  245. package/dist/reports/src/test-catalog-entity.js +6 -0
  246. package/dist/reports/src/types.d.ts +6 -1
  247. package/dist/reports/src/use-report-nav.d.ts +35 -0
  248. package/dist/reports/src/use-report-nav.js +12 -0
  249. package/dist/schema/src/api.d.ts +799 -0
  250. package/dist/schema/src/api.js +201 -0
  251. package/dist/schema/src/cli-results.d.ts +210 -0
  252. package/dist/schema/src/cli-results.js +124 -0
  253. package/dist/schema/src/common.d.ts +53 -0
  254. package/dist/schema/src/common.js +42 -0
  255. package/dist/schema/src/index.d.ts +14 -0
  256. package/dist/schema/src/index.js +14 -0
  257. package/dist/schema/src/layer.d.ts +2273 -0
  258. package/dist/schema/src/layer.js +462 -0
  259. package/dist/schema/src/manifest.d.ts +95 -0
  260. package/dist/schema/src/manifest.js +84 -0
  261. package/dist/schema/src/report-sources.d.ts +28 -0
  262. package/dist/schema/src/report-sources.js +25 -0
  263. package/dist/schema/src/runs-index.d.ts +16 -0
  264. package/dist/schema/src/runs-index.js +15 -0
  265. package/dist/schema/src/server-config.d.ts +93 -0
  266. package/dist/schema/src/server-config.js +73 -0
  267. package/dist/schema/src/spec.d.ts +69 -0
  268. package/dist/schema/src/spec.js +58 -0
  269. package/dist/server/src/app.d.ts +62 -0
  270. package/dist/server/src/app.js +419 -0
  271. package/dist/server/src/auth.d.ts +41 -0
  272. package/dist/server/src/auth.js +59 -0
  273. package/dist/server/src/config.d.ts +83 -0
  274. package/dist/server/src/config.js +187 -0
  275. package/dist/server/src/dev.d.ts +12 -0
  276. package/dist/server/src/dev.js +80 -0
  277. package/dist/server/src/events.d.ts +29 -0
  278. package/dist/server/src/events.js +101 -0
  279. package/dist/server/src/host.d.ts +30 -0
  280. package/dist/server/src/host.js +115 -0
  281. package/dist/server/src/index.d.ts +16 -0
  282. package/dist/server/src/index.js +16 -0
  283. package/dist/server/src/llms.d.ts +19 -0
  284. package/dist/server/src/llms.js +63 -0
  285. package/dist/server/src/main.d.ts +30 -0
  286. package/dist/server/src/main.js +112 -0
  287. package/dist/server/src/openapi.d.ts +59 -0
  288. package/dist/server/src/openapi.js +374 -0
  289. package/dist/server/src/problem.d.ts +8 -0
  290. package/dist/server/src/problem.js +39 -0
  291. package/dist/server/src/site.d.ts +13 -0
  292. package/dist/server/src/site.js +43 -0
  293. package/dist/server/src/store-api.d.ts +28 -0
  294. package/dist/server/src/store-api.js +159 -0
  295. package/dist/server/src/validate.d.ts +71 -0
  296. package/dist/server/src/validate.js +197 -0
  297. package/dist/vitest/src/index.d.ts +27 -5
  298. package/dist/vitest/src/index.js +18 -5
  299. package/dist/webdriver/src/driver.d.ts +63 -11
  300. package/dist/webdriver/src/driver.js +57 -6
  301. package/dist/webdriver/src/index.d.ts +8 -0
  302. package/dist/webdriver/src/index.js +1 -0
  303. package/dist/webdriver/src/protocol.d.ts +75 -6
  304. package/dist/webdriver/src/protocol.js +79 -6
  305. package/docs/agents.md +137 -0
  306. package/docs/deploying.md +345 -0
  307. package/docs/dogfooding.md +175 -0
  308. package/docs/extending.md +242 -0
  309. package/docs/integrating.md +485 -0
  310. package/docs/reference/bun.md +141 -0
  311. package/docs/reference/cli.md +146 -0
  312. package/docs/reference/core.md +2426 -0
  313. package/docs/reference/detox.md +150 -0
  314. package/docs/reference/http-api.md +303 -0
  315. package/docs/reference/playwright.md +303 -0
  316. package/docs/reference/reports.md +7098 -0
  317. package/docs/reference/schema.md +1651 -0
  318. package/docs/reference/server.md +685 -0
  319. package/docs/reference/vitest.md +45 -0
  320. package/docs/reference/webdriver.md +330 -0
  321. package/docs/report-backends.md +144 -0
  322. package/docs/report-contract.md +400 -0
  323. package/docs/reporting-issues.md +160 -0
  324. package/docs/server.md +285 -0
  325. package/docs/statecharts.md +641 -0
  326. package/llms-full.txt +3506 -0
  327. package/llms.txt +47 -0
  328. package/package.json +33 -5
  329. package/packages/core/README.md +2 -1
  330. package/packages/reports/README.md +69 -2
  331. package/packages/reports/STORAGE.md +5 -1
  332. package/packages/schema/README.md +14 -0
  333. package/packages/schema/schemas/check-detail.schema.json +1032 -0
  334. package/packages/schema/schemas/checks-result.schema.json +26 -0
  335. package/packages/schema/schemas/command-error.schema.json +35 -0
  336. package/packages/schema/schemas/doctor-result.schema.json +60 -0
  337. package/packages/schema/schemas/effective-delta.schema.json +490 -0
  338. package/packages/schema/schemas/effective-evidence.schema.json +471 -0
  339. package/packages/schema/schemas/evidence-page.schema.json +271 -0
  340. package/packages/schema/schemas/hello-event.schema.json +22 -0
  341. package/packages/schema/schemas/init-plan.schema.json +347 -0
  342. package/packages/schema/schemas/layer-rows.schema.json +199 -0
  343. package/packages/schema/schemas/layer.schema.json +2888 -0
  344. package/packages/schema/schemas/merge-result.schema.json +40 -0
  345. package/packages/schema/schemas/outline.schema.json +62 -0
  346. package/packages/schema/schemas/problem.schema.json +37 -0
  347. package/packages/schema/schemas/publish-checks.schema.json +1413 -0
  348. package/packages/schema/schemas/publish-result.schema.json +168 -0
  349. package/packages/schema/schemas/ready.schema.json +68 -0
  350. package/packages/schema/schemas/report-event.schema.json +100 -0
  351. package/packages/schema/schemas/report-status.schema.json +370 -0
  352. package/packages/schema/schemas/server-config.schema.json +194 -0
  353. package/packages/schema/schemas/server-info.schema.json +114 -0
  354. package/packages/schema/schemas/spec-set.schema.json +103 -0
  355. package/packages/schema/schemas/store-listing.schema.json +29 -0
  356. package/packages/schema/schemas/store-write-result.schema.json +28 -0
  357. package/packages/schema/schemas/stream-result.schema.json +59 -0
  358. package/packages/server/README.md +43 -0
  359. package/packages/server/openapi.json +6699 -0
  360. package/packages/visualizer/README.md +27 -5
  361. package/packages/visualizer/example/specs/coffee/ordering/cart.qnt +7 -0
  362. package/packages/visualizer/example/specs/coffee/ordering/checkout.openslo.yaml +10 -0
  363. package/packages/visualizer/example/specs/coffee/ordering/drink-price.dmn +24 -0
  364. package/packages/visualizer/example/specs/coffee/ordering/loyalty.feature +7 -0
  365. package/packages/visualizer/example/specs/coffee/ordering/ordering.feature +36 -0
  366. package/packages/visualizer/example/specs/coffee/ordering/payment.als +4 -0
  367. package/packages/visualizer/fixtures/failures/specs/coffee/ordering/cart.qnt +7 -0
  368. package/packages/visualizer/fixtures/failures/specs/coffee/ordering/checkout.openslo.yaml +10 -0
  369. package/packages/visualizer/fixtures/failures/specs/coffee/ordering/drink-price.dmn +24 -0
  370. package/packages/visualizer/fixtures/failures/specs/coffee/ordering/loyalty.feature +7 -0
  371. package/packages/visualizer/fixtures/failures/specs/coffee/ordering/ordering.feature +36 -0
  372. package/packages/visualizer/fixtures/failures/specs/coffee/ordering/payment.als +4 -0
  373. package/packages/visualizer/fixtures/full/specs/coffee/ordering/cart.qnt +7 -0
  374. package/packages/visualizer/fixtures/full/specs/coffee/ordering/checkout.openslo.yaml +10 -0
  375. package/packages/visualizer/fixtures/full/specs/coffee/ordering/drink-price.dmn +24 -0
  376. package/packages/visualizer/fixtures/full/specs/coffee/ordering/loyalty.feature +7 -0
  377. package/packages/visualizer/fixtures/full/specs/coffee/ordering/ordering.feature +36 -0
  378. package/packages/visualizer/fixtures/full/specs/coffee/ordering/payment.als +4 -0
  379. package/packages/visualizer/fixtures/malformed/specs/coffee/ordering/cart.qnt +7 -0
  380. package/packages/visualizer/fixtures/malformed/specs/coffee/ordering/checkout.openslo.yaml +10 -0
  381. package/packages/visualizer/fixtures/malformed/specs/coffee/ordering/drink-price.dmn +24 -0
  382. package/packages/visualizer/fixtures/malformed/specs/coffee/ordering/loyalty.feature +7 -0
  383. package/packages/visualizer/fixtures/malformed/specs/coffee/ordering/ordering.feature +36 -0
  384. package/packages/visualizer/fixtures/malformed/specs/coffee/ordering/payment.als +4 -0
  385. package/packages/visualizer/fixtures/partial/specs/coffee/ordering/cart.qnt +7 -0
  386. package/packages/visualizer/fixtures/partial/specs/coffee/ordering/checkout.openslo.yaml +10 -0
  387. package/packages/visualizer/fixtures/partial/specs/coffee/ordering/drink-price.dmn +24 -0
  388. package/packages/visualizer/fixtures/partial/specs/coffee/ordering/loyalty.feature +7 -0
  389. package/packages/visualizer/fixtures/partial/specs/coffee/ordering/ordering.feature +36 -0
  390. package/packages/visualizer/fixtures/partial/specs/coffee/ordering/payment.als +4 -0
  391. package/packages/visualizer/fixtures/removed/specs/coffee/ordering/cart.qnt +7 -0
  392. package/packages/visualizer/fixtures/removed/specs/coffee/ordering/checkout.openslo.yaml +10 -0
  393. package/packages/visualizer/fixtures/removed/specs/coffee/ordering/drink-price.dmn +24 -0
  394. package/packages/visualizer/fixtures/removed/specs/coffee/ordering/loyalty.feature +7 -0
  395. package/packages/visualizer/fixtures/removed/specs/coffee/ordering/ordering.feature +36 -0
  396. package/packages/visualizer/fixtures/removed/specs/coffee/ordering/payment.als +4 -0
  397. package/packages/visualizer/fixtures/spec-only/specs/coffee/ordering/cart.qnt +7 -0
  398. package/packages/visualizer/fixtures/spec-only/specs/coffee/ordering/checkout.openslo.yaml +10 -0
  399. package/packages/visualizer/fixtures/spec-only/specs/coffee/ordering/drink-price.dmn +24 -0
  400. package/packages/visualizer/fixtures/spec-only/specs/coffee/ordering/loyalty.feature +7 -0
  401. package/packages/visualizer/fixtures/spec-only/specs/coffee/ordering/ordering.feature +36 -0
  402. package/packages/visualizer/fixtures/spec-only/specs/coffee/ordering/payment.als +4 -0
  403. package/packages/visualizer/scripts/make-fixtures.ts +2 -0
  404. package/packages/visualizer/scripts/publish-data.ts +1 -2
  405. package/packages/visualizer/src/app/AppShell.tsx +9 -2
  406. package/packages/visualizer/src/app/report-sources.tsx +6 -4
  407. package/packages/visualizer/src/app/router.tsx +3 -3
  408. package/packages/visualizer/src/components/Header.module.css +35 -0
  409. package/packages/visualizer/src/components/Header.tsx +41 -4
  410. package/packages/visualizer/src/data/adapters/adapter.ts +5 -17
  411. package/packages/visualizer/src/data/adapters/index.ts +10 -1
  412. package/packages/visualizer/src/evidence/api.ts +14 -2
  413. package/packages/visualizer/src/evidence/artifacts.module.css +213 -0
  414. package/packages/visualizer/src/evidence/artifacts.tsx +272 -0
  415. package/packages/visualizer/src/evidence/check-detail.tsx +38 -4
  416. package/packages/visualizer/src/evidence/evidence-context.tsx +74 -12
  417. package/packages/visualizer/src/evidence/evidence.module.css +20 -0
  418. package/packages/visualizer/src/evidence/marks.tsx +2 -0
  419. package/packages/visualizer/src/evidence/overview.tsx +3 -3
  420. package/packages/visualizer/src/evidence/sources-panel.tsx +88 -34
  421. package/packages/visualizer/src/evidence/spec-page.tsx +23 -16
  422. package/packages/visualizer/src/find/Finder.tsx +1 -7
  423. package/packages/visualizer/src/find/open.ts +4 -0
  424. package/packages/visualizer/src/live/live-stream.ts +204 -0
  425. package/packages/visualizer/src/runtime.ts +19 -0
  426. package/packages/visualizer/src/state/ui-store.ts +6 -2
  427. package/packages/visualizer/vite/atlas-server-proxy.ts +36 -0
  428. package/packages/visualizer/vite/example-reports.ts +475 -0
  429. package/packages/visualizer/vite.config.ts +18 -2
@@ -0,0 +1,92 @@
1
+ import { contentTypeOf } from './media-types.js';
2
+ function count(holder) {
3
+ return (holder.attachments?.length ?? 0) + (holder.steps?.length ?? 0) + (holder.annotations?.length ?? 0) + (holder.attempts?.length ?? 0) + (holder.tags?.length ?? 0) + (holder.links?.length ?? 0) + Object.keys(holder.labels ?? {}).length + Object.keys(holder.extensions ?? {}).length;
4
+ }
5
+ /**
6
+ * What a result carries beyond its outcome, as counts and short facts. It rides in every row of the
7
+ * report so a list can show "3 attachments, 12 steps" without opening anything; the evidence itself is
8
+ * read when someone asks for it. Undefined when there is none.
9
+ *
10
+ * Several holders (for example a check and its layer) are summed: `attachments` also counts those on steps and
11
+ * attempts, `steps` counts only each holder's own top-level steps, tags are de-duplicated, and for a repeated
12
+ * label the later holder wins. `roles` are the attachments' roles (or, without one, what their media type or file
13
+ * name implies), sorted and distinct.
14
+ */
15
+ export function summarizeEvidence(...holders) {
16
+ if (!holders.some(holder => count(holder) > 0))
17
+ return undefined;
18
+ const roles = new Set();
19
+ const tags = new Set();
20
+ const labels = {};
21
+ let attachments = 0, steps = 0, annotations = 0, attempts = 0, links = 0;
22
+ const attachmentsOf = (list) => { for (const item of list ?? []) {
23
+ attachments += 1;
24
+ roles.add(item.role ?? roleOfType(item.contentType ?? contentTypeOf(item.name)));
25
+ } };
26
+ for (const holder of holders) {
27
+ attachmentsOf(holder.attachments);
28
+ // Attachments on steps and attempts are part of the check too.
29
+ for (const step of holder.steps ?? [])
30
+ attachmentsOf(step.attachments);
31
+ for (const attempt of holder.attempts ?? []) {
32
+ attachmentsOf(attempt.attachments);
33
+ for (const step of attempt.steps ?? [])
34
+ attachmentsOf(step.attachments);
35
+ }
36
+ steps += holder.steps?.length ?? 0;
37
+ annotations += holder.annotations?.length ?? 0;
38
+ attempts += holder.attempts?.length ?? 0;
39
+ links += holder.links?.length ?? 0;
40
+ for (const tag of holder.tags ?? [])
41
+ tags.add(tag);
42
+ Object.assign(labels, holder.labels);
43
+ }
44
+ return { attachments, steps, annotations, attempts, links, tags: [...tags], labels, roles: [...roles].sort() };
45
+ }
46
+ /** A role to show when a producer gave none, from the media type: `image`, `video`, `audio`, `snapshot` (text/html), `text` (text, JSON, XML) or `file`. Roles are an open vocabulary; a producer's own role always wins. */
47
+ export function roleOfType(contentType) {
48
+ const type = (contentType ?? '').toLowerCase();
49
+ if (type.startsWith('image/'))
50
+ return 'image';
51
+ if (type.startsWith('video/'))
52
+ return 'video';
53
+ if (type.startsWith('audio/'))
54
+ return 'audio';
55
+ if (type === 'text/html')
56
+ return 'snapshot';
57
+ if (type.startsWith('text/') || type.includes('json') || type.includes('xml'))
58
+ return 'text';
59
+ return 'file';
60
+ }
61
+ /** One result's evidence, from a layer: the first check with id `checkId` (in a part of kind `kind`, when given) plus the layer's own evidence. Null when the layer holds no such check. */
62
+ export function checkDetailOf(layer, checkId, kind) {
63
+ for (const part of layer.parts) {
64
+ if (kind !== undefined && part.kind !== kind)
65
+ continue;
66
+ const check = part.checks.find(item => item.id === checkId);
67
+ if (check)
68
+ return { layer: layer.id, kind: part.kind, check, run: runEvidence(layer) };
69
+ }
70
+ return null;
71
+ }
72
+ /** What a layer carries as a whole (its tags, labels, links, attachments, annotations and extensions; not its parts' checks). Absent fields stay absent. */
73
+ export function runEvidence(layer) {
74
+ const { tags, labels, links, attachments, annotations, extensions } = layer;
75
+ return { ...(tags ? { tags } : {}), ...(labels ? { labels } : {}), ...(links ? { links } : {}), ...(attachments ? { attachments } : {}), ...(annotations ? { annotations } : {}), ...(extensions ? { extensions } : {}) };
76
+ }
77
+ /** Every distinct bundle key (`bundleKey`) that the attachments of a holder, its steps and its attempts point to, so a layer's files can be copied. Attachments held by `uri` or inline `text` have none. */
78
+ export function bundleKeysOf(holder) {
79
+ const keys = new Set();
80
+ const take = (list) => { for (const item of list ?? [])
81
+ if (item.bundleKey)
82
+ keys.add(item.bundleKey); };
83
+ take(holder.attachments);
84
+ for (const step of holder.steps ?? [])
85
+ take(step.attachments);
86
+ for (const attempt of holder.attempts ?? []) {
87
+ take(attempt.attachments);
88
+ for (const step of attempt.steps ?? [])
89
+ take(step.attachments);
90
+ }
91
+ return [...keys];
92
+ }
@@ -1,29 +1,65 @@
1
+ /**
2
+ * @module Live evidence from running suites
3
+ *
4
+ * Shows results the moment each test finishes, before its package has been collected into an archived layer. A
5
+ * running suite announces itself with a record under `active/` in the local store (see {@link LiveRunRecord});
6
+ * this module reads the test reports that suite has written so far into a temporary layer (id `live-<source>`,
7
+ * `live: true`) and drops it once the archived layer arrives or after a short linger. Live layers are a viewing
8
+ * aid only (this module writes nothing), and an unreadable or half-written report is skipped, not guessed at.
9
+ */
1
10
  import type { Clock } from './clock.js';
2
11
  import type { ActiveRun, LayerEvidence, LayerHeader } from './evidence-types.js';
12
+ import type { LayerParameters } from './layer-types.js';
3
13
  import type { ObjectStore } from './object-store.js';
4
14
  /** What a running spec suite announces under `active/` in the local store. */
5
15
  export type LiveRunRecord = {
16
+ /** The package directory the suite runs in, relative to the repository (`libs/chess`); with `bucket`, it names the source. */
6
17
  readonly packageDir: string;
18
+ /** The suite's bucket within the package (`part-2`); where the run writes each finished test. */
7
19
  readonly bucket: string;
20
+ /** When the run started, epoch milliseconds. A new value for the same source means a new run: tracking restarts. */
8
21
  readonly startedAtMs: number;
22
+ /** How many checks the run expects to execute, when known. */
9
23
  readonly plannedChecks?: number;
24
+ /** The client this suite is checking. A different client does not replace it. */
25
+ readonly environment?: string;
26
+ /** Other string choices that make the results their own slot; non-string or empty values are dropped on reading. */
27
+ readonly parameters?: LayerParameters;
10
28
  };
29
+ /**
30
+ * Validates a parsed `active/` announcement and keeps only the fields the live layer can trust (the environment is
31
+ * normalised to its slug, parameters are string-to-string). Null when `packageDir`, `bucket` or a numeric
32
+ * `startedAtMs` is missing, so garbage in `active/` is ignored rather than shown.
33
+ */
34
+ export declare function liveRunRecord(value: unknown): LiveRunRecord | null;
11
35
  /** How the host reaches the results a spec run has written so far. */
12
36
  export type LiveSource = {
13
- /** The bucket the run writes each finished test into, or null when it cannot be read. */
37
+ /** The bucket the run writes each finished test into, or null when it cannot be read (the run is then not tracked). */
14
38
  readonly bucket: (run: LiveRunRecord) => ObjectStore | null;
15
- /** The checkout the runs execute. */
39
+ /** The checkout the runs execute: its commit and branch, stamped on every live layer. Asked once per refresh pass. */
16
40
  readonly origin?: () => Promise<{
17
41
  readonly sha: string | null;
18
42
  readonly branch: string | null;
19
43
  }>;
20
- /** The stylesheet the runs' snapshots render with. */
44
+ /** The stylesheet the runs' snapshots render with (served as `frame.css`). */
21
45
  readonly frameCss?: () => Promise<string | null>;
22
46
  };
47
+ /** The id of the temporary layer for a source: `live-<source>`. Not a content hash, unlike an archived layer's id. */
23
48
  export declare function liveLayerId(source: string): string;
24
49
  /**
25
- * Finished tests of runs that have not been collected into a layer yet, so a
50
+ * Creates the tracker of finished tests of runs that have not been collected into a layer yet, so a
26
51
  * result appears the moment its test ends instead of when its package does.
52
+ *
53
+ * The tracker is stateful and meant to live as long as the server: call `refresh` on each poll with the current
54
+ * `active/` records and the newest archived layers. It returns the live layers (only those with at least one result)
55
+ * and the runs still in progress with their completed and failed counts. A run that disappears is read one last
56
+ * time and kept for 30 seconds, or until an archived (non-live) layer of the same producer and environment recorded
57
+ * at or after its start appears. `find(id)` returns a live layer by `live-<source>` id, and `store(source)` the
58
+ * bucket its files are read from. A bucket that is being replaced or a report still being written is skipped until
59
+ * the next pass; `refresh` does not throw for those.
60
+ *
61
+ * @param args.live How the host reaches the buckets; without it nothing is tracked.
62
+ * @param args.backend The backend id stamped on live layers.
27
63
  */
28
64
  export declare function createLiveEvidence(args: {
29
65
  readonly live: LiveSource | undefined;
@@ -1,14 +1,48 @@
1
1
  import { eachLimited } from './completion-order.js';
2
+ import { environmentOf } from './evidence-view.js';
2
3
  import { sourceId } from './keys.js';
4
+ import { normalizeEnvironment } from './layer-axes.js';
3
5
  import { layerEvidence } from './layer-merge.js';
4
6
  import { readUtf8 } from './object-io.js';
5
7
  import { SPEC_RUN_ID } from './publish.js';
6
8
  import { testPrefix } from './recording-keys.js';
7
9
  import { sourcePart } from './source-part.js';
8
10
  import { suiteStatus } from './status.js';
11
+ function readParameters(value) {
12
+ if (!value || typeof value !== 'object' || Array.isArray(value))
13
+ return {};
14
+ const parameters = {};
15
+ for (const [key, item] of Object.entries(value))
16
+ if (typeof item === 'string' && item)
17
+ parameters[key] = item;
18
+ return parameters;
19
+ }
20
+ /**
21
+ * Validates a parsed `active/` announcement and keeps only the fields the live layer can trust (the environment is
22
+ * normalised to its slug, parameters are string-to-string). Null when `packageDir`, `bucket` or a numeric
23
+ * `startedAtMs` is missing, so garbage in `active/` is ignored rather than shown.
24
+ */
25
+ export function liveRunRecord(value) {
26
+ if (!value || typeof value !== 'object')
27
+ return null;
28
+ const run = value;
29
+ if (!run.packageDir || !run.bucket || typeof run.startedAtMs !== 'number')
30
+ return null;
31
+ const parameters = readParameters(run.parameters);
32
+ const environment = typeof run.environment === 'string' ? normalizeEnvironment(run.environment) : null;
33
+ return {
34
+ packageDir: run.packageDir,
35
+ bucket: run.bucket,
36
+ startedAtMs: run.startedAtMs,
37
+ ...(typeof run.plannedChecks === 'number' ? { plannedChecks: run.plannedChecks } : {}),
38
+ ...(environment ? { environment } : {}),
39
+ ...(Object.keys(parameters).length > 0 ? { parameters } : {}),
40
+ };
41
+ }
9
42
  /** A finished run stays visible this long while its layer is being collected. */
10
43
  const LINGER_MS = 30_000;
11
44
  const REPORT = '/report.json';
45
+ /** The id of the temporary layer for a source: `live-<source>`. Not a content hash, unlike an archived layer's id. */
12
46
  export function liveLayerId(source) {
13
47
  return `live-${source}`;
14
48
  }
@@ -21,8 +55,19 @@ function sourceOf(run) {
21
55
  }
22
56
  }
23
57
  /**
24
- * Finished tests of runs that have not been collected into a layer yet, so a
58
+ * Creates the tracker of finished tests of runs that have not been collected into a layer yet, so a
25
59
  * result appears the moment its test ends instead of when its package does.
60
+ *
61
+ * The tracker is stateful and meant to live as long as the server: call `refresh` on each poll with the current
62
+ * `active/` records and the newest archived layers. It returns the live layers (only those with at least one result)
63
+ * and the runs still in progress with their completed and failed counts. A run that disappears is read one last
64
+ * time and kept for 30 seconds, or until an archived (non-live) layer of the same producer and environment recorded
65
+ * at or after its start appears. `find(id)` returns a live layer by `live-<source>` id, and `store(source)` the
66
+ * bucket its files are read from. A bucket that is being replaced or a report still being written is skipped until
67
+ * the next pass; `refresh` does not throw for those.
68
+ *
69
+ * @param args.live How the host reaches the buckets; without it nothing is tracked.
70
+ * @param args.backend The backend id stamped on live layers.
26
71
  */
27
72
  export function createLiveEvidence(args) {
28
73
  const tracked = new Map();
@@ -46,8 +91,9 @@ export function createLiveEvidence(args) {
46
91
  }
47
92
  });
48
93
  const tests = [...state.metas.values()].sort((a, b) => (a.testId < b.testId ? -1 : a.testId > b.testId ? 1 : 0));
49
- const record = { source, packageDir: state.run.packageDir, bucket: state.run.bucket, status: suiteStatus(tests), updatedAtMs: args.clock.now(), tests };
50
- const summary = { id: liveLayerId(source), producer: source, recordedAtMs: record.updatedAtMs, sha: origin.sha, branch: origin.branch, runUrl: null, parameters: {}, specs: [], parts: [sourcePart(origin.sha ?? '', record)], source: record, recording: null };
94
+ const environment = normalizeEnvironment(state.run.environment ?? state.run.parameters?.environment);
95
+ const record = { source, packageDir: state.run.packageDir, bucket: state.run.bucket, status: suiteStatus(tests), updatedAtMs: args.clock.now(), ...(environment ? { environment } : {}), ...(state.run.parameters ? { parameters: state.run.parameters } : {}), tests };
96
+ const summary = { id: liveLayerId(source), producer: source, recordedAtMs: record.updatedAtMs, sha: origin.sha, branch: origin.branch, runUrl: null, ...(environment ? { environment } : {}), parameters: state.run.parameters ?? {}, specs: [], parts: [sourcePart(origin.sha ?? '', record)], source: record, recording: null };
51
97
  const evidence = layerEvidence(summary, args.backend);
52
98
  const layer = { ...evidence.layer, live: true, recording: { type: 'live', source }, execution: `live:${state.run.startedAtMs}:${tests.length}` };
53
99
  state.evidence = { ...evidence, layer };
@@ -98,7 +144,9 @@ export function createLiveEvidence(args) {
98
144
  /* keep what was read while it ran */
99
145
  }
100
146
  }
101
- const archived = collected.some(layer => layer.producer === source && !layer.live && layer.recordedAtMs >= state.run.startedAtMs);
147
+ const declared = normalizeEnvironment(state.run.environment ?? state.run.parameters?.environment) ?? 'unspecified';
148
+ // A collected layer of another client is a different result, so this suite stays up.
149
+ const archived = collected.some(layer => layer.producer === source && !layer.live && layer.recordedAtMs >= state.run.startedAtMs && environmentOf(layer) === declared);
102
150
  if (archived || now - state.goneAt > LINGER_MS)
103
151
  tracked.delete(source);
104
152
  }
@@ -1,3 +1,13 @@
1
+ /**
2
+ * @module Report evidence types
3
+ *
4
+ * The shapes the report API and the viewer exchange about layers: a layer's header (origin and totals), its rows
5
+ * (one per check), the backends that contributed, runs in progress, and the address of a recorded test's
6
+ * timeline. These are the wire shapes of `/api/reports/*`, so within a version they only gain optional fields.
7
+ * Also holds {@link recordingPaths}, the one place the URL layout of a recording is defined.
8
+ *
9
+ * @see docs/report-contract.md
10
+ */
1
11
  import type { EvidenceMeasurement, EvidenceStatus } from './explorer-types.js';
2
12
  import type { LayerParameters } from './layer-types.js';
3
13
  /** Where a recorded test's timeline and captured files are read from. */
@@ -14,53 +24,101 @@ export type RecordingAddress = {
14
24
  };
15
25
  /** One store that contributes report layers. */
16
26
  export type BackendInfo = {
27
+ /** Stable backend id (lowercase letters, digits, hyphens); names where a result came from. */
17
28
  readonly id: string;
29
+ /** What a person sees for the backend. */
18
30
  readonly label: string;
19
31
  /** Spec runs on this machine write here while they are still running. */
20
32
  readonly live: boolean;
33
+ /** False when the backend could not be read; a notice says why, and its results are unknown, not passed. */
21
34
  readonly available: boolean;
22
35
  };
36
+ /** How many results have each status (`passed`, `failed`, `running`, `not-run`). */
23
37
  export type StatusCounts = Readonly<Record<EvidenceStatus, number>>;
38
+ /**
39
+ * What a result (or a run) carries beyond its outcome, as counts and short facts: attachments, steps,
40
+ * annotations, retries, tags, labels, links. It is only there when there is something. The evidence itself
41
+ * is read when someone opens the result.
42
+ */
43
+ export type EvidenceSummary = {
44
+ /** Attachments on the result, including those on its steps and attempts. */
45
+ readonly attachments: number;
46
+ readonly steps: number;
47
+ readonly annotations: number;
48
+ /** How many tries were recorded when the check was retried. */
49
+ readonly attempts: number;
50
+ readonly links: number;
51
+ readonly tags: readonly string[];
52
+ readonly labels: Readonly<Record<string, string>>;
53
+ /** The distinct roles of its attachments (or what their media types say), to show what kind of evidence there is. */
54
+ readonly roles: readonly string[];
55
+ };
24
56
  /** A report layer's origin and totals. Its checks are read separately, once. */
25
57
  export type LayerHeader = {
58
+ /** The layer's id: 64 hex digits for an archived layer, `live-<source>` for a live one. */
26
59
  readonly id: string;
60
+ /** The id of the backend it was read from (the one whose id sorts first when several hold it). */
27
61
  readonly backend: string;
62
+ /** The suite or tool that made it. Part of the slot. */
28
63
  readonly producer: string;
64
+ /** The package directory of a spec run, when the layer came from one. */
29
65
  readonly packageDir: string | null;
66
+ /** When the checks ran, epoch milliseconds; the newest completed result wins its slot. */
30
67
  readonly recordedAtMs: number;
68
+ /** Provenance: the commit it was recorded at. Never part of the slot. */
31
69
  readonly sha: string | null;
70
+ /** Provenance: the branch it was recorded on. Never part of the slot. */
32
71
  readonly branch: string | null;
72
+ /** The client this layer checked. Null when the layer did not say. */
73
+ readonly environment: string | null;
74
+ /** Provenance: the CI run that made it. */
33
75
  readonly runUrl: string | null;
76
+ /** Other string choices that make a result its own slot. */
34
77
  readonly parameters: LayerParameters;
78
+ /** `modified`: recorded from a checkout with uncommitted changes; `unknown`: origin not captured; absent: clean. Advisory; never changes a status. */
35
79
  readonly freshness?: 'unknown' | 'modified';
80
+ /** Where this layer's recorded timelines and captured files are read from, or null when it has none. */
36
81
  readonly recording: RecordingAddress | null;
37
82
  /** From a spec run that has not been collected into a layer yet. */
38
83
  readonly live: boolean;
84
+ /** Its checks by status. Parts of kind `performance` are not counted when anything else was recorded. */
39
85
  readonly counts: StatusCounts;
40
86
  /** Equal for the mutable and the archived address of one execution. */
41
87
  readonly execution: string;
88
+ /** What the run as a whole carries: its logs, links and labels. */
89
+ readonly evidence?: EvidenceSummary;
42
90
  };
43
91
  /** One check of one report part. */
44
92
  export type LayerRow = {
93
+ /** The kind of the part holding the check (`behavior`, `performance`, ...). Part of the slot. */
45
94
  readonly kind: string;
95
+ /** The check's stable id. Part of the slot. */
46
96
  readonly id: string;
47
97
  readonly title: string;
48
98
  readonly status: EvidenceStatus;
99
+ /** The spec the check belongs to (`specs/<path>`), or null. */
49
100
  readonly specPath: string | null;
101
+ /** Why it has this status, for a person; empty when there is nothing to add. */
50
102
  readonly detail: string;
51
103
  readonly measurements: readonly EvidenceMeasurement[];
52
104
  /** The recorded test behind this check, when it has a timeline to open. */
53
105
  readonly testId: string | null;
54
106
  /** What distinguishes this recorded test from another run of the same check. */
55
107
  readonly variant: LayerParameters;
108
+ /** How long the check took, milliseconds, or null when unknown. */
56
109
  readonly durationMs: number | null;
110
+ /** What this result carries beyond its outcome. Absent when nothing. */
111
+ readonly evidence?: EvidenceSummary;
57
112
  };
113
+ /** The checks of one layer, read separately from its header. */
58
114
  export type LayerRows = {
115
+ /** The layer's id. */
59
116
  readonly id: string;
60
117
  readonly rows: readonly LayerRow[];
61
118
  /** Spec path to the fingerprint the layer recorded for it. */
62
119
  readonly fingerprints: Readonly<Record<string, string>>;
63
120
  };
121
+ /** A layer's rows together with its header: everything composition needs to lay the layer over the specs. */
64
122
  export type LayerEvidence = LayerRows & {
65
123
  readonly layer: LayerHeader;
66
124
  };
@@ -72,17 +130,28 @@ export type ActiveRun = {
72
130
  readonly startedAtMs: number;
73
131
  /** Spec files the run planned, when the producer recorded it. */
74
132
  readonly plannedChecks?: number;
133
+ /** Checks finished so far: passed, failed or not-run. */
75
134
  readonly completed: number;
135
+ /** Of those, how many failed. */
76
136
  readonly failed: number;
77
137
  };
138
+ /** One page of the layer listing the viewer reads. */
78
139
  export type EvidencePage = {
140
+ /** The layers on this page, newest first. */
79
141
  readonly layers: readonly LayerHeader[];
142
+ /** Opaque token for the next page, or null at the end. */
80
143
  readonly cursor: string | null;
144
+ /** What could not be read; a gate treats any as unknown, never as a pass. */
81
145
  readonly notices: readonly string[];
82
146
  readonly backends: readonly BackendInfo[];
147
+ /** Runs writing results right now. */
83
148
  readonly active: readonly ActiveRun[];
84
149
  };
85
- /** The report API paths of one recorded test. */
150
+ /**
151
+ * The report API paths (relative to the reports API root) of one recorded test: its `report` JSON, a `file(path)`
152
+ * builder for its captured files (each path segment is URL-encoded), and the `css` its snapshots render with.
153
+ * The layout depends on where the recording lives: an archived layer, a commit (the older layout) or a live run.
154
+ */
86
155
  export declare function recordingPaths(address: RecordingAddress, testId: string): {
87
156
  report: string;
88
157
  file: (path: string) => string;
@@ -1,4 +1,8 @@
1
- /** The report API paths of one recorded test. */
1
+ /**
2
+ * The report API paths (relative to the reports API root) of one recorded test: its `report` JSON, a `file(path)`
3
+ * builder for its captured files (each path segment is URL-encoded), and the `css` its snapshots render with.
4
+ * The layout depends on where the recording lives: an archived layer, a commit (the older layout) or a live run.
5
+ */
2
6
  export function recordingPaths(address, testId) {
3
7
  const test = encodeURIComponent(testId);
4
8
  const file = (base) => (path) => `${base}/files/${path.split('/').map(encodeURIComponent).join('/')}`;
@@ -1,65 +1,117 @@
1
+ /**
2
+ * @module The viewer's model of a combined report
3
+ *
4
+ * Pure functions that reshape a combined report for display: results grouped per check (a {@link CheckSlot}), rolled
5
+ * up per spec and overall, the facets by which layers are included or left out, and the history of one check across
6
+ * layers. They never change a result: statuses come from the composition (`layer-merge.ts`), and a spec with no
7
+ * recorded check is `none`, never a pass.
8
+ *
9
+ * @see docs/report-contract.md
10
+ */
1
11
  import type { EvidenceStatus } from './explorer-types.js';
2
12
  import type { LayerEvidence, LayerHeader, LayerRow, StatusCounts } from './evidence-types.js';
3
13
  import { type CombinedEvidence, type EvidenceResult } from './layer-merge.js';
4
14
  /** Everything the combined report holds for one check. */
5
15
  export type CheckSlot = {
16
+ /** The check's key (its spec path and id), shared by all of its results. */
6
17
  readonly key: string;
7
18
  readonly id: string;
8
19
  readonly title: string;
9
20
  readonly specPath: string | null;
10
- /** The worst outcome across the check's parameter sets. */
21
+ /** The worst outcome across the check's parameter sets (failed, then running, then not-run, then passed). */
11
22
  readonly status: EvidenceStatus;
12
- /** What happened, newest first. One entry per producer and parameter set. */
23
+ /** What happened, newest first. One entry per producer, environment and parameter set. */
13
24
  readonly outcomes: readonly EvidenceResult[];
14
25
  /** Recorded timings of the same check. They never change its outcome. */
15
26
  readonly diagnostics: readonly EvidenceResult[];
27
+ /** The newest `recordedAtMs` among all of its results, diagnostics included. */
16
28
  readonly latestAtMs: number;
29
+ /** True when any result comes from a run that is still in progress. */
17
30
  readonly live: boolean;
18
31
  };
19
- /** Group the combined results by check, in the order the checks were first seen. */
32
+ /**
33
+ * Groups the combined results (the winners of the slots) by check, in the order the checks were first seen.
34
+ * A check's status is the worst of its non-diagnostic results; if it has only diagnostics (timings), those are its
35
+ * outcomes. The title and id shown are the newest result's.
36
+ */
20
37
  export declare function checkSlots(results: readonly EvidenceResult[]): CheckSlot[];
38
+ /** A spec's rolled-up status: the worst of its checks, or `none` when nothing was recorded for it. */
21
39
  export type SpecStatus = EvidenceStatus | 'none';
40
+ /** The rollup of a set of checks (one spec, or the whole report). */
22
41
  export type SpecSummary = {
42
+ /** `failed` if any check failed, else `running`, else `passed` if any passed, else `not-run`; `none` with no checks. */
23
43
  readonly status: SpecStatus;
44
+ /** Checks by their own status. */
24
45
  readonly counts: StatusCounts;
25
46
  readonly total: number;
47
+ /** The newest `recordedAtMs` among them, or null with no checks. */
26
48
  readonly latestAtMs: number | null;
27
49
  readonly live: boolean;
28
50
  };
51
+ /** The summary of a spec with no recorded check: status `none`, which is not a pass. */
29
52
  export declare const NO_EVIDENCE: SpecSummary;
30
- /** No recorded check is `none`, never a pass. */
53
+ /** Rolls the checks of one spec up into a {@link SpecSummary}. No recorded check is `none`, never a pass. */
31
54
  export declare function summarizeSlots(slots: readonly CheckSlot[]): SpecSummary;
55
+ /** Adds several summaries (for example every spec's) into one, with the same status rule; `none` when they hold no checks at all. */
32
56
  export declare function addSummaries(summaries: readonly SpecSummary[]): SpecSummary;
57
+ /** One spec's checks and their rollup. */
33
58
  export type SpecEvidence = {
34
59
  readonly slots: readonly CheckSlot[];
35
60
  readonly summary: SpecSummary;
36
61
  };
62
+ /** The combined report as the viewer draws it. */
37
63
  export type EvidenceView = {
38
64
  /** Keyed by the spec path the outline uses (`specs/...`). */
39
65
  readonly specs: ReadonlyMap<string, SpecEvidence>;
40
66
  /** Results whose spec is no longer in the outline, or that name no spec. */
41
67
  readonly unassigned: readonly CheckSlot[];
68
+ /** The rollup of the specs in the outline only; `unassigned` results are not counted in it. */
42
69
  readonly total: SpecSummary;
43
70
  };
71
+ /** Builds the {@link EvidenceView} of a combined report: every spec of the outline gets an entry, with an empty one when it has no results. */
44
72
  export declare function evidenceView(combined: CombinedEvidence): EvidenceView;
45
- /** Sources left out of the combined report. Everything else, including layers that arrive later, contributes. */
73
+ /**
74
+ * Sources left out of the combined report. An empty list means every layer
75
+ * on that axis contributes, including layers that arrive later.
76
+ */
46
77
  export type SourceSelection = {
78
+ /** Backend ids to leave out. */
47
79
  readonly backends: readonly string[];
80
+ /** Branch names to leave out; `Unknown branch` stands for layers with no branch. */
48
81
  readonly branches: readonly string[];
82
+ /** Commit shas to leave out; `unknown` stands for layers with no sha. */
83
+ readonly versions: readonly string[];
84
+ /** Environment slugs to leave out; `unspecified` stands for layers with none. */
85
+ readonly environments: readonly string[];
86
+ /** Layer ids to leave out. */
49
87
  readonly layers: readonly string[];
50
88
  };
89
+ /** The selection that leaves nothing out: every layer contributes. */
51
90
  export declare const ALL_SOURCES: SourceSelection;
91
+ /** A saved selection from before versions and environments were axes still means "exclude nothing new". */
92
+ export declare function sourceSelection(excluded: Partial<SourceSelection> | null | undefined): SourceSelection;
93
+ /** The branch a layer was recorded on, or `Unknown branch` when it did not say. */
52
94
  export declare function branchOf(layer: LayerHeader): string;
53
- export declare function contributes(layer: LayerHeader, excluded: SourceSelection): boolean;
95
+ /** The commit a layer checked, or `unknown` when the layer did not record one. */
96
+ export declare function versionOf(layer: Pick<LayerHeader, 'sha'>): string;
97
+ /** The client a layer checked, or `unspecified` when it did not say. */
98
+ export declare function environmentOf(layer: Pick<LayerHeader, 'environment'>): string;
99
+ /** Whether a layer takes part given what is left out: false when its backend, branch, commit, environment or id is excluded. Missing axes in `excluded` exclude nothing. */
100
+ export declare function contributes(layer: LayerHeader, excluded: Partial<SourceSelection>): boolean;
101
+ /** One choice on an axis of the sources panel: its value, how many layers have it, and the newest `recordedAtMs` among them. */
54
102
  export type SourceFacet = {
55
103
  readonly id: string;
56
104
  readonly layers: number;
57
105
  readonly latestAtMs: number;
58
106
  };
107
+ /** Counts the layers per backend, branch, commit and environment, each list newest first (ties by name). */
59
108
  export declare function sourceFacets(layers: readonly LayerHeader[]): {
60
109
  backends: SourceFacet[];
61
110
  branches: SourceFacet[];
111
+ versions: SourceFacet[];
112
+ environments: SourceFacet[];
62
113
  };
114
+ /** One layer's record of a check. */
63
115
  export type HistoryEntry = {
64
116
  readonly layer: LayerHeader;
65
117
  readonly row: LayerRow;
@@ -10,7 +10,11 @@ function worst(statuses) {
10
10
  function newestFirst(a, b) {
11
11
  return b.layer.recordedAtMs - a.layer.recordedAtMs || (a.key < b.key ? -1 : a.key > b.key ? 1 : 0);
12
12
  }
13
- /** Group the combined results by check, in the order the checks were first seen. */
13
+ /**
14
+ * Groups the combined results (the winners of the slots) by check, in the order the checks were first seen.
15
+ * A check's status is the worst of its non-diagnostic results; if it has only diagnostics (timings), those are its
16
+ * outcomes. The title and id shown are the newest result's.
17
+ */
14
18
  export function checkSlots(results) {
15
19
  const groups = new Map();
16
20
  for (const result of results) {
@@ -38,8 +42,9 @@ export function checkSlots(results) {
38
42
  };
39
43
  });
40
44
  }
45
+ /** The summary of a spec with no recorded check: status `none`, which is not a pass. */
41
46
  export const NO_EVIDENCE = { status: 'none', counts: emptyCounts(), total: 0, latestAtMs: null, live: false };
42
- /** No recorded check is `none`, never a pass. */
47
+ /** Rolls the checks of one spec up into a {@link SpecSummary}. No recorded check is `none`, never a pass. */
43
48
  export function summarizeSlots(slots) {
44
49
  if (!slots.length)
45
50
  return NO_EVIDENCE;
@@ -55,6 +60,7 @@ export function summarizeSlots(slots) {
55
60
  live: slots.some(slot => slot.live),
56
61
  };
57
62
  }
63
+ /** Adds several summaries (for example every spec's) into one, with the same status rule; `none` when they hold no checks at all. */
58
64
  export function addSummaries(summaries) {
59
65
  const counts = emptyCounts();
60
66
  let total = 0, latestAtMs = null, live = false;
@@ -71,6 +77,7 @@ export function addSummaries(summaries) {
71
77
  const status = counts.failed ? 'failed' : counts.running ? 'running' : counts.passed ? 'passed' : 'not-run';
72
78
  return { status, counts, total, latestAtMs, live };
73
79
  }
80
+ /** Builds the {@link EvidenceView} of a combined report: every spec of the outline gets an entry, with an empty one when it has no results. */
74
81
  export function evidenceView(combined) {
75
82
  const specs = new Map();
76
83
  for (const { spec, results } of combined.specs) {
@@ -79,12 +86,38 @@ export function evidenceView(combined) {
79
86
  }
80
87
  return { specs, unassigned: checkSlots(combined.unassigned), total: addSummaries([...specs.values()].map(entry => entry.summary)) };
81
88
  }
82
- export const ALL_SOURCES = { backends: [], branches: [], layers: [] };
89
+ /** The selection that leaves nothing out: every layer contributes. */
90
+ export const ALL_SOURCES = { backends: [], branches: [], versions: [], environments: [], layers: [] };
91
+ /** A saved selection from before versions and environments were axes still means "exclude nothing new". */
92
+ export function sourceSelection(excluded) {
93
+ return {
94
+ backends: excluded?.backends ?? [],
95
+ branches: excluded?.branches ?? [],
96
+ versions: excluded?.versions ?? [],
97
+ environments: excluded?.environments ?? [],
98
+ layers: excluded?.layers ?? [],
99
+ };
100
+ }
101
+ /** The branch a layer was recorded on, or `Unknown branch` when it did not say. */
83
102
  export function branchOf(layer) {
84
103
  return layer.branch ?? 'Unknown branch';
85
104
  }
105
+ /** The commit a layer checked, or `unknown` when the layer did not record one. */
106
+ export function versionOf(layer) {
107
+ return layer.sha ?? 'unknown';
108
+ }
109
+ /** The client a layer checked, or `unspecified` when it did not say. */
110
+ export function environmentOf(layer) {
111
+ return layer.environment || 'unspecified';
112
+ }
113
+ /** Whether a layer takes part given what is left out: false when its backend, branch, commit, environment or id is excluded. Missing axes in `excluded` exclude nothing. */
86
114
  export function contributes(layer, excluded) {
87
- return !excluded.backends.includes(layer.backend) && !excluded.branches.includes(branchOf(layer)) && !excluded.layers.includes(layer.id);
115
+ const selection = sourceSelection(excluded);
116
+ return !selection.backends.includes(layer.backend)
117
+ && !selection.branches.includes(branchOf(layer))
118
+ && !selection.versions.includes(versionOf(layer))
119
+ && !selection.environments.includes(environmentOf(layer))
120
+ && !selection.layers.includes(layer.id);
88
121
  }
89
122
  function facets(layers, id) {
90
123
  const found = new Map();
@@ -96,8 +129,14 @@ function facets(layers, id) {
96
129
  }
97
130
  return [...found].map(([key, facet]) => ({ id: key, ...facet })).sort((a, b) => b.latestAtMs - a.latestAtMs || (a.id < b.id ? -1 : 1));
98
131
  }
132
+ /** Counts the layers per backend, branch, commit and environment, each list newest first (ties by name). */
99
133
  export function sourceFacets(layers) {
100
- return { backends: facets(layers, layer => layer.backend), branches: facets(layers, branchOf) };
134
+ return {
135
+ backends: facets(layers, layer => layer.backend),
136
+ branches: facets(layers, branchOf),
137
+ versions: facets(layers, versionOf),
138
+ environments: facets(layers, environmentOf),
139
+ };
101
140
  }
102
141
  /** Every layer's own record of one check, newest first. A superseded result stays readable here. */
103
142
  export function checkHistory(layers, path, id) {