@crvouga/atlas 0.4.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 (379) hide show
  1. package/CHANGELOG.md +7 -1
  2. package/README.md +178 -617
  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 +35 -5
  94. package/dist/reports/src/evidence-live.js +18 -2
  95. package/dist/reports/src/evidence-types.d.ts +68 -1
  96. package/dist/reports/src/evidence-types.js +5 -1
  97. package/dist/reports/src/evidence-view.d.ts +43 -4
  98. package/dist/reports/src/evidence-view.js +13 -2
  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 +35 -5
  134. package/dist/reports/src/layer-axes.js +25 -5
  135. package/dist/reports/src/layer-catalog.d.ts +28 -1
  136. package/dist/reports/src/layer-catalog.js +18 -1
  137. package/dist/reports/src/layer-merge.d.ts +98 -9
  138. package/dist/reports/src/layer-merge.js +147 -31
  139. package/dist/reports/src/layer-source.d.ts +18 -0
  140. package/dist/reports/src/layer-source.js +11 -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 +32 -1
  144. package/dist/reports/src/layers-route.d.ts +47 -4
  145. package/dist/reports/src/layers-route.js +86 -7
  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 +26 -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 +42 -9
  175. package/dist/reports/src/publish.js +36 -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 +37 -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 +32 -4
  329. package/packages/core/README.md +2 -1
  330. package/packages/reports/README.md +65 -0
  331. package/packages/reports/STORAGE.md +4 -0
  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 +21 -0
  361. package/packages/visualizer/src/app/AppShell.tsx +9 -2
  362. package/packages/visualizer/src/app/report-sources.tsx +6 -4
  363. package/packages/visualizer/src/app/router.tsx +3 -3
  364. package/packages/visualizer/src/components/Header.module.css +35 -0
  365. package/packages/visualizer/src/components/Header.tsx +40 -3
  366. package/packages/visualizer/src/data/adapters/adapter.ts +5 -17
  367. package/packages/visualizer/src/data/adapters/index.ts +10 -1
  368. package/packages/visualizer/src/evidence/api.ts +14 -2
  369. package/packages/visualizer/src/evidence/artifacts.module.css +213 -0
  370. package/packages/visualizer/src/evidence/artifacts.tsx +272 -0
  371. package/packages/visualizer/src/evidence/check-detail.tsx +3 -1
  372. package/packages/visualizer/src/evidence/evidence-context.tsx +74 -12
  373. package/packages/visualizer/src/evidence/spec-page.tsx +23 -16
  374. package/packages/visualizer/src/find/Finder.tsx +1 -7
  375. package/packages/visualizer/src/find/open.ts +4 -0
  376. package/packages/visualizer/src/live/live-stream.ts +204 -0
  377. package/packages/visualizer/src/runtime.ts +19 -0
  378. package/packages/visualizer/vite/atlas-server-proxy.ts +36 -0
  379. package/packages/visualizer/vite.config.ts +11 -3
@@ -45,6 +45,27 @@ manager sets it to where you ran the command, or the current directory). With no
45
45
  Editing a spec file or a run publishing a snapshot updates the open page in about a second, without a reload,
46
46
  keeping selection, zoom and pan.
47
47
 
48
+ ### Layered reports for any spec
49
+
50
+ Besides charts and runs, the viewer shows the combined result of every spec (Gherkin, Quint, DMN,
51
+ Alloy, OpenSLO, …) from the layers your suites published. Point `ATLAS_REPORTS_MODULE` at a module in
52
+ your project that exports `reportsDevFetch`, built with `reportsHost` from
53
+ `@crvouga/atlas/reports/host`:
54
+
55
+ ```ts
56
+ // atlas.reports.ts
57
+ import { reportsHost } from '@crvouga/atlas/reports/host';
58
+
59
+ export const reportsDevFetch = reportsHost({ specs: './specs', stores: ['./atlas-reports'] });
60
+ ```
61
+
62
+ ```sh
63
+ ATLAS_REPORTS_MODULE="$(pwd)/atlas.reports.ts" atlas-visualizer dev --specs ./specs
64
+ ```
65
+
66
+ See [integrating Atlas](../../docs/integrating.md#6-browse-it) for publishing layers and the routes
67
+ this serves.
68
+
48
69
  ### Commands
49
70
 
50
71
  ```sh
@@ -1,4 +1,5 @@
1
1
  import { Outlet, useLocation, useSearch } from '@tanstack/react-router';
2
+ import { lazy, Suspense } from 'react';
2
3
 
3
4
  import { Header } from '../components/Header';
4
5
  import { EvidenceProvider } from '../evidence/evidence-context';
@@ -6,10 +7,14 @@ import { SourcesPanel } from '../evidence/sources-panel';
6
7
  import { SpecIndex } from '../evidence/spec-index';
7
8
  import { kindOfAddress } from '../spec-kind';
8
9
  import styles from '../views/views.module.css';
9
- import { Finder } from '../find/Finder';
10
10
  import { AtlasProvider, useAtlas } from './atlas-context';
11
11
  import { Gazetteer } from './gazetteer';
12
12
 
13
+ // The finder brings every spec parser with it. It loads as soon as the page has drawn, long before anyone presses ⌘K.
14
+ const loadFinder = () => import('../find/Finder').then((m) => ({ default: m.Finder }));
15
+ const Finder = lazy(loadFinder);
16
+ if (typeof window !== 'undefined') setTimeout(() => void loadFinder(), 0);
17
+
13
18
  function Index() {
14
19
  const { pathname } = useLocation();
15
20
  const kind = kindOfAddress(pathname);
@@ -47,7 +52,9 @@ export function AppShell() {
47
52
  </main>
48
53
  </div>
49
54
  <SourcesPanel />
50
- <Finder />
55
+ <Suspense fallback={null}>
56
+ <Finder />
57
+ </Suspense>
51
58
  </div>
52
59
  </EvidenceProvider>
53
60
  </AtlasProvider>
@@ -9,6 +9,7 @@ import type { ReportBackend } from '../data/adapters/report-backend';
9
9
  import { getJson, LoadError } from '../data/adapters';
10
10
  import { useAdapter } from '../data/adapters/context';
11
11
  import { createReportBackend } from '../data/adapters/report-backend';
12
+ import { runtimeConfig, runtimeUrl } from '../runtime';
12
13
 
13
14
  const SourceListSchema = z
14
15
  .object({ schemaVersion: z.literal(1), sources: z.array(RemoteReportSourceSchema) })
@@ -35,15 +36,16 @@ export function useReportSources() {
35
36
 
36
37
  export function ReportSourcesProvider({ children }: { children: ReactNode }) {
37
38
  const adapter = useAdapter();
39
+ const server = runtimeUrl(runtimeConfig()?.dataUrl);
38
40
  const defaultConfig = useMemo<RemoteReportSource>(
39
41
  () => ({
40
42
  id: 'workspace',
41
- label: adapter.kind === 'dev' ? 'Workspace runs' : 'Published reports',
43
+ label: server ? 'Atlas server' : adapter.kind === 'dev' ? 'Workspace runs' : 'Published reports',
42
44
  type: 'http',
43
- baseUrl: adapter.kind === 'dev' ? '/__atlas/' : (import.meta.env.VITE_ATLAS_BASE_URL ?? './data/'),
44
- live: adapter.kind === 'dev'
45
+ baseUrl: server ?? (adapter.kind === 'dev' ? '/__atlas/' : (import.meta.env.VITE_ATLAS_BASE_URL ?? './data/')),
46
+ live: server !== undefined || adapter.kind === 'dev'
45
47
  }),
46
- [adapter]
48
+ [adapter, server]
47
49
  );
48
50
  const configBase = defaultConfig.baseUrl.endsWith('/') ? defaultConfig.baseUrl : `${defaultConfig.baseUrl}/`;
49
51
  const configUrl = new URL(import.meta.env.VITE_ATLAS_SOURCES_URL ?? 'sources.json', new URL(configBase, window.location.href)).href;
@@ -1,8 +1,7 @@
1
- import { createHashHistory, createRootRoute, createRoute, createRouter, type RouterHistory } from '@tanstack/react-router';
1
+ import { createHashHistory, createRootRoute, createRoute, createRouter, lazyRouteComponent, type RouterHistory } from '@tanstack/react-router';
2
2
  import { z } from 'zod/v4';
3
3
 
4
4
  import { SpecPage } from '../evidence/spec-page';
5
- import { ChartPage } from '../views/ChartPage';
6
5
  import { ProductPage } from '../views/ProductPage';
7
6
  import { AppShell } from './AppShell';
8
7
 
@@ -44,7 +43,8 @@ function routeTree() {
44
43
  getParentRoute: () => rootRoute,
45
44
  path: '/chart/$chartId',
46
45
  validateSearch: (search) => ChartSearch.parse(search),
47
- component: ChartPage
46
+ // The map (the graph, its layout and its renderer) is the largest part of the page and only this route needs it.
47
+ component: lazyRouteComponent(() => import('../views/ChartPage'), 'ChartPage')
48
48
  });
49
49
  const specRoute = createRoute({
50
50
  getParentRoute: () => rootRoute,
@@ -87,6 +87,40 @@
87
87
  animation: pulse 1.4s var(--ease) infinite;
88
88
  }
89
89
 
90
+ .connection {
91
+ display: inline-flex;
92
+ align-items: center;
93
+ gap: 8px;
94
+ color: var(--ink-3);
95
+ font-size: 0.8125rem;
96
+ font-weight: 600;
97
+ white-space: nowrap;
98
+ }
99
+
100
+ .connectionDot {
101
+ width: 8px;
102
+ height: 8px;
103
+ border-radius: 50%;
104
+ background: var(--ink-3);
105
+ }
106
+
107
+ .connection[data-status='live'] {
108
+ color: var(--place);
109
+ }
110
+
111
+ .connection[data-status='live'] .connectionDot {
112
+ background: var(--place);
113
+ }
114
+
115
+ .connection[data-status='reconnecting'] {
116
+ color: var(--failed-ink);
117
+ }
118
+
119
+ .connection[data-status='reconnecting'] .connectionDot {
120
+ background: currentColor;
121
+ animation: pulse 1.4s var(--ease) infinite;
122
+ }
123
+
90
124
  @keyframes pulse {
91
125
  50% {
92
126
  opacity: 0.35;
@@ -195,6 +229,7 @@
195
229
  }
196
230
 
197
231
  .live,
232
+ .connection,
198
233
  .picker {
199
234
  display: none;
200
235
  }
@@ -1,14 +1,14 @@
1
1
  import { Link, useLocation, useNavigate, useSearch } from '@tanstack/react-router';
2
- import { useEffect, useMemo } from 'react';
2
+ import { useEffect, useMemo, useState } from 'react';
3
3
 
4
4
  import { useAtlas } from '../app/atlas-context';
5
- import { commandKey } from '../find/Finder';
6
- import { setFinderOpen } from '../find/open';
5
+ import { commandKey, setFinderOpen } from '../find/open';
7
6
  import type { AtlasView } from '../data/model';
8
7
  import { LATEST } from '../data/queries';
9
8
  import { useEvidence } from '../evidence/evidence-context';
10
9
  import { plural } from '../evidence/format';
11
10
  import { excludedCount } from '../evidence/sources-panel';
11
+ import { eventsAddress, listenLive, useLiveStatus, type ReportAnnouncement } from '../live/live-stream';
12
12
  import { kindOfAddress, outlinePath, SPEC_KINDS } from '../spec-kind';
13
13
  import { useUiStore } from '../state/ui-store';
14
14
  import { DataIssues } from './DataIssues';
@@ -69,6 +69,42 @@ function LiveRuns() {
69
69
  );
70
70
  }
71
71
 
72
+ /** Is this page being kept up to date by its server, and what arrived last. Nothing at all for a static site. */
73
+ function ServerConnection() {
74
+ const status = useLiveStatus();
75
+ const [arrival, setArrival] = useState<ReportAnnouncement | null>(null);
76
+ useEffect(() => {
77
+ const url = eventsAddress();
78
+ if (!url) return;
79
+ let timer: ReturnType<typeof setTimeout> | undefined;
80
+ const stop = listenLive(url, (event) => {
81
+ if (event.type !== 'report' || !event.report.added.length) return;
82
+ setArrival(event.report);
83
+ clearTimeout(timer);
84
+ timer = setTimeout(() => setArrival(null), 8_000);
85
+ });
86
+ return () => {
87
+ clearTimeout(timer);
88
+ stop();
89
+ };
90
+ }, []);
91
+ if (status === 'off') return null;
92
+ const first = arrival?.added[0];
93
+ const failed = arrival?.added.reduce((sum, layer) => sum + (layer.counts?.failed ?? 0), 0) ?? 0;
94
+ return (
95
+ <span className={styles.connection} data-status={status} role="status" title={status === 'live' ? 'This page updates as results reach the server.' : status === 'connecting' ? 'Connecting to the server.' : 'The connection to the server was lost. Trying again; the report on screen is the last one received.'}>
96
+ <span className={styles.connectionDot} aria-hidden="true" />
97
+ {arrival && first
98
+ ? `${first.producer}${first.environment ? ` · ${first.environment}` : ''}${arrival.addedTotal > 1 ? ` +${arrival.addedTotal - 1}` : ''}${failed ? ` · ${plural(failed, 'failure', 'failures')}` : ' · new results'}`
99
+ : status === 'live'
100
+ ? 'Live'
101
+ : status === 'connecting'
102
+ ? 'Connecting'
103
+ : 'Reconnecting'}
104
+ </span>
105
+ );
106
+ }
107
+
72
108
  function SourcesButton() {
73
109
  const { layers, ready, unavailable, excluded } = useEvidence();
74
110
  const setOpen = useUiStore((s) => s.setSourcesOpen);
@@ -116,6 +152,7 @@ export function Header() {
116
152
  ))}
117
153
  </nav>
118
154
  <div className={styles.tools}>
155
+ <ServerConnection />
119
156
  <LiveRuns />
120
157
  {!kind && view && <RunPicker view={view} />}
121
158
  {!kind && view && <DataIssues issues={view.issues} />}
@@ -1,4 +1,6 @@
1
- import { AtlasChangeSchema, type AtlasChange } from '@crvouga/atlas/schema';
1
+ import type { AtlasChange } from '@crvouga/atlas/schema';
2
+
3
+ import { listenChanges } from '../../live/live-stream';
2
4
 
3
5
  export type { AtlasChange } from '@crvouga/atlas/schema';
4
6
 
@@ -93,20 +95,6 @@ export function createHttpAdapter(baseUrl: string, kind: AtlasAdapter['kind'] =
93
95
  function subscribeToEvents(url: string): NonNullable<AtlasAdapter['subscribe']> {
94
96
  const location = new URL(url);
95
97
  if (!['http:', 'https:'].includes(location.protocol) || location.username || location.password) throw new Error('Use an HTTP event URL without credentials.');
96
- return (onChange) => {
97
- if (typeof EventSource === 'undefined') return () => undefined;
98
- const stream = new EventSource(url);
99
- const receive = (event: MessageEvent<string>) => {
100
- try {
101
- const change = AtlasChangeSchema.safeParse(JSON.parse(event.data));
102
- if (change.success) onChange(change.data);
103
- } catch {
104
- return;
105
- }
106
- };
107
- stream.addEventListener('message', receive);
108
- stream.addEventListener('atlas:change', receive);
109
- stream.addEventListener('open', () => onChange({ scope: 'runs', files: [], runIds: [] }));
110
- return () => stream.close();
111
- };
98
+ // One connection per address, however many parts of the page listen to it.
99
+ return (onChange) => listenChanges(url, onChange);
112
100
  }
@@ -1,3 +1,4 @@
1
+ import { runtimeConfig, runtimeUrl } from '../../runtime';
1
2
  import { createHttpAdapter, type AtlasAdapter, type AtlasChange } from './adapter';
2
3
 
3
4
  export * from './adapter';
@@ -22,8 +23,16 @@ export function createDevAdapter(base = DEV_PREFIX, label = 'Live from disk', so
22
23
  };
23
24
  }
24
25
 
25
- /** The dev adapter under `vite dev`, the static adapter everywhere else. */
26
+ /** The server's own files, with its event stream: what a page served by `atlas serve` or `atlas dev` reads. */
27
+ export function createServerAdapter(dataUrl: string, eventsUrl: string | undefined): AtlasAdapter {
28
+ return { ...createHttpAdapter(dataUrl, 'static', eventsUrl), label: 'Atlas server' };
29
+ }
30
+
31
+ /** The server's adapter when the page was served by one, the dev adapter under a bare `vite dev`, the static adapter everywhere else. */
26
32
  export function createAdapter(): AtlasAdapter {
33
+ const config = runtimeConfig();
34
+ const dataUrl = runtimeUrl(config?.dataUrl);
35
+ if (dataUrl) return createServerAdapter(dataUrl, runtimeUrl(config?.eventsUrl));
27
36
  if (import.meta.env.DEV && import.meta.hot) return createDevAdapter();
28
37
  return createHttpAdapter(import.meta.env.VITE_ATLAS_BASE_URL ?? './data/');
29
38
  }
@@ -1,8 +1,20 @@
1
1
  import { evidenceFile } from '@crvouga/atlas/reports/export-evidence';
2
2
 
3
+ import { runtimeConfig } from '../runtime';
4
+
5
+ /**
6
+ * An Atlas server writes its address book into the page it serves (`atlas serve`, and `atlas dev` through
7
+ * Vite), so one build of the viewer works for every project. A build can instead be pointed at a reports
8
+ * API when it is made.
9
+ */
10
+ const runtime = runtimeConfig;
11
+
3
12
  /** A built site without a reports API reads the evidence exported beside it. */
4
- export const staticEvidence = !import.meta.env.DEV && !import.meta.env.VITE_ATLAS_REPORTS_URL;
5
- export const reportsBase = (import.meta.env.VITE_ATLAS_REPORTS_URL as string | undefined)?.replace(/\/$/, '') ?? (staticEvidence ? './data/evidence' : '/api/reports');
13
+ export const staticEvidence = !import.meta.env.DEV && !import.meta.env.VITE_ATLAS_REPORTS_URL && !runtime()?.reportsUrl;
14
+ export const reportsBase = (import.meta.env.VITE_ATLAS_REPORTS_URL as string | undefined)?.replace(/\/$/, '') ?? runtime()?.reportsUrl?.replace(/\/$/, '') ?? (staticEvidence ? './data/evidence' : '/api/reports');
15
+
16
+ /** Does the server answer `/evidence/effective`: the whole report in one request, without per-layer history? */
17
+ export const effectiveEvidence = () => runtime()?.effective === true;
6
18
 
7
19
  export class ReportsError extends Error {
8
20
  constructor(
@@ -0,0 +1,213 @@
1
+ .panel {
2
+ display: grid;
3
+ gap: 14px;
4
+ margin: 12px 0;
5
+ }
6
+
7
+ .panel h4 {
8
+ margin: 0 0 6px;
9
+ font-size: 0.78rem;
10
+ font-weight: 600;
11
+ letter-spacing: 0.04em;
12
+ text-transform: uppercase;
13
+ color: var(--faint);
14
+ }
15
+
16
+ .chips {
17
+ display: flex;
18
+ flex-wrap: wrap;
19
+ gap: 6px;
20
+ margin: 0;
21
+ padding: 0;
22
+ list-style: none;
23
+ }
24
+
25
+ .chip {
26
+ display: inline-flex;
27
+ gap: 4px;
28
+ padding: 2px 8px;
29
+ border-radius: var(--pill);
30
+ background: var(--unrun);
31
+ color: var(--ink-3);
32
+ font-size: 0.78rem;
33
+ }
34
+
35
+ .chip b {
36
+ font-weight: 600;
37
+ color: var(--ink-2, inherit);
38
+ }
39
+
40
+ .links a {
41
+ font-size: 0.85rem;
42
+ }
43
+
44
+ .gallery {
45
+ display: grid;
46
+ grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
47
+ gap: 12px;
48
+ margin: 0;
49
+ padding: 0;
50
+ list-style: none;
51
+ }
52
+
53
+ .figure {
54
+ display: grid;
55
+ gap: 4px;
56
+ margin: 0;
57
+ min-width: 0;
58
+ }
59
+
60
+ .figure[data-wide] {
61
+ grid-column: 1 / -1;
62
+ }
63
+
64
+ .figure img,
65
+ .figure video {
66
+ width: 100%;
67
+ max-height: 420px;
68
+ object-fit: contain;
69
+ border: 1px solid var(--line, #dfe3e8);
70
+ border-radius: 6px;
71
+ background: #f6f7f9;
72
+ }
73
+
74
+ .figure iframe {
75
+ width: 100%;
76
+ height: 360px;
77
+ border: 1px solid var(--line, #dfe3e8);
78
+ border-radius: 6px;
79
+ background: #fff;
80
+ }
81
+
82
+ .figure audio {
83
+ width: 100%;
84
+ }
85
+
86
+ .figure figcaption {
87
+ display: flex;
88
+ flex-wrap: wrap;
89
+ gap: 6px;
90
+ align-items: baseline;
91
+ font-size: 0.8rem;
92
+ color: var(--ink-3);
93
+ }
94
+
95
+ .figure figcaption strong {
96
+ font-weight: 600;
97
+ overflow-wrap: anywhere;
98
+ }
99
+
100
+ .role {
101
+ padding: 0 6px;
102
+ border-radius: var(--pill);
103
+ background: var(--unrun);
104
+ font-size: 0.72rem;
105
+ }
106
+
107
+ .text {
108
+ max-height: 260px;
109
+ margin: 0;
110
+ padding: 8px 10px;
111
+ overflow: auto;
112
+ border: 1px solid var(--line, #dfe3e8);
113
+ border-radius: 6px;
114
+ background: #f6f7f9;
115
+ font: 0.78rem/1.45 ui-monospace, SFMono-Regular, Menlo, monospace;
116
+ white-space: pre-wrap;
117
+ overflow-wrap: anywhere;
118
+ }
119
+
120
+ .timeline,
121
+ .timeline ol {
122
+ display: grid;
123
+ gap: 4px;
124
+ margin: 0;
125
+ padding: 0;
126
+ list-style: none;
127
+ }
128
+
129
+ .timeline ol {
130
+ margin-left: 18px;
131
+ padding-left: 8px;
132
+ border-left: 1px solid var(--line, #dfe3e8);
133
+ }
134
+
135
+ .step {
136
+ display: grid;
137
+ gap: 4px;
138
+ }
139
+
140
+ .stepRow {
141
+ display: flex;
142
+ gap: 8px;
143
+ align-items: center;
144
+ font-size: 0.9rem;
145
+ }
146
+
147
+ .stepRow .spacer {
148
+ flex: 1;
149
+ }
150
+
151
+ .stepKind,
152
+ .stepTime {
153
+ font-size: 0.75rem;
154
+ color: var(--faint);
155
+ }
156
+
157
+ .step[data-status='failed'] > .stepRow .stepName {
158
+ color: var(--failed);
159
+ }
160
+
161
+ .stepDetail {
162
+ margin: 0 0 0 24px;
163
+ font-size: 0.82rem;
164
+ color: var(--ink-3);
165
+ white-space: pre-wrap;
166
+ }
167
+
168
+ .notes {
169
+ display: grid;
170
+ gap: 6px;
171
+ margin: 0;
172
+ padding: 0;
173
+ list-style: none;
174
+ }
175
+
176
+ .note {
177
+ display: flex;
178
+ gap: 8px;
179
+ align-items: baseline;
180
+ font-size: 0.88rem;
181
+ }
182
+
183
+ .level {
184
+ flex: none;
185
+ padding: 0 7px;
186
+ border-radius: var(--pill);
187
+ background: var(--unrun);
188
+ color: var(--ink-3);
189
+ font-size: 0.72rem;
190
+ text-transform: uppercase;
191
+ letter-spacing: 0.04em;
192
+ }
193
+
194
+ .level[data-level='warning'] {
195
+ background: #fff4d6;
196
+ color: #7a5200;
197
+ }
198
+
199
+ .level[data-level='error'] {
200
+ background: #fde4e4;
201
+ color: var(--failed);
202
+ }
203
+
204
+ .where {
205
+ color: var(--faint);
206
+ font-size: 0.78rem;
207
+ }
208
+
209
+ .raw summary {
210
+ cursor: pointer;
211
+ font-size: 0.82rem;
212
+ color: var(--ink-3);
213
+ }