@crvouga/atlas 0.4.0 → 0.6.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 (444) hide show
  1. package/CHANGELOG.md +13 -1
  2. package/README.md +191 -611
  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 +35 -1
  9. package/dist/core/src/config.d.ts +77 -4
  10. package/dist/core/src/config.js +110 -17
  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 +75 -16
  30. package/dist/core/src/run/execute.js +71 -18
  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 +85 -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 +56 -0
  120. package/dist/reports/src/http-store.js +148 -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 +369 -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/live-reporter.d.ts +34 -0
  147. package/dist/reports/src/live-reporter.js +122 -0
  148. package/dist/reports/src/media-types.d.ts +11 -0
  149. package/dist/reports/src/media-types.js +22 -0
  150. package/dist/reports/src/namespace-guard.d.ts +10 -1
  151. package/dist/reports/src/namespace-guard.js +10 -1
  152. package/dist/reports/src/navigation.d.ts +12 -0
  153. package/dist/reports/src/navigation.js +7 -0
  154. package/dist/reports/src/object-io.d.ts +11 -4
  155. package/dist/reports/src/object-io.js +4 -4
  156. package/dist/reports/src/object-store-conformance.d.ts +27 -0
  157. package/dist/reports/src/object-store-conformance.js +87 -0
  158. package/dist/reports/src/object-store.d.ts +33 -24
  159. package/dist/reports/src/object-store.js +17 -24
  160. package/dist/reports/src/parse-with.d.ts +16 -0
  161. package/dist/reports/src/parse-with.js +14 -0
  162. package/dist/reports/src/performance-part.d.ts +25 -1
  163. package/dist/reports/src/performance-part.js +15 -1
  164. package/dist/reports/src/performance.d.ts +22 -2
  165. package/dist/reports/src/performance.js +12 -2
  166. package/dist/reports/src/persist.d.ts +12 -3
  167. package/dist/reports/src/persist.js +12 -3
  168. package/dist/reports/src/phone.d.ts +18 -1
  169. package/dist/reports/src/phone.js +18 -1
  170. package/dist/reports/src/protocol.d.ts +26 -4
  171. package/dist/reports/src/protocol.js +15 -4
  172. package/dist/reports/src/publication.d.ts +21 -1
  173. package/dist/reports/src/publication.js +21 -1
  174. package/dist/reports/src/publish-checks.d.ts +35 -0
  175. package/dist/reports/src/publish-checks.js +122 -0
  176. package/dist/reports/src/publish.d.ts +42 -9
  177. package/dist/reports/src/publish.js +36 -7
  178. package/dist/reports/src/read.d.ts +46 -16
  179. package/dist/reports/src/read.js +38 -14
  180. package/dist/reports/src/recording-bundle.d.ts +13 -2
  181. package/dist/reports/src/recording-bundle.js +4 -2
  182. package/dist/reports/src/recording-keys.d.ts +15 -1
  183. package/dist/reports/src/recording-keys.js +15 -1
  184. package/dist/reports/src/remote-report.d.ts +28 -7
  185. package/dist/reports/src/remote-report.js +15 -7
  186. package/dist/reports/src/report-cli.d.ts +51 -0
  187. package/dist/reports/src/report-cli.js +486 -0
  188. package/dist/reports/src/report-index.d.ts +99 -0
  189. package/dist/reports/src/report-index.js +337 -0
  190. package/dist/reports/src/report-loader.d.ts +10 -0
  191. package/dist/reports/src/report-loader.js +6 -0
  192. package/dist/reports/src/report-parts.d.ts +29 -3
  193. package/dist/reports/src/report-parts.js +37 -3
  194. package/dist/reports/src/report-status.d.ts +48 -0
  195. package/dist/reports/src/report-status.js +156 -0
  196. package/dist/reports/src/report-types.d.ts +73 -0
  197. package/dist/reports/src/report-types.js +3 -0
  198. package/dist/reports/src/reporter.d.ts +67 -0
  199. package/dist/reports/src/reporter.js +128 -0
  200. package/dist/reports/src/reports-explorer-route.d.ts +541 -0
  201. package/dist/reports/src/reports-explorer-route.js +11 -0
  202. package/dist/reports/src/reports-list-route.d.ts +9 -0
  203. package/dist/reports/src/reports-list-route.js +9 -0
  204. package/dist/reports/src/reports-route.d.ts +561 -22
  205. package/dist/reports/src/reports-route.js +11 -3
  206. package/dist/reports/src/reports-store.d.ts +19 -2
  207. package/dist/reports/src/reports-store.js +10 -2
  208. package/dist/reports/src/resilient-cache.d.ts +14 -1
  209. package/dist/reports/src/resilient-cache.js +8 -1
  210. package/dist/reports/src/retention.d.ts +17 -1
  211. package/dist/reports/src/retention.js +5 -1
  212. package/dist/reports/src/run-bundle.d.ts +63 -0
  213. package/dist/reports/src/run-bundle.js +159 -0
  214. package/dist/reports/src/s3-store.d.ts +79 -0
  215. package/dist/reports/src/s3-store.js +295 -0
  216. package/dist/reports/src/scenario.d.ts +14 -4
  217. package/dist/reports/src/scenario.js +5 -4
  218. package/dist/reports/src/sigv4.d.ts +72 -0
  219. package/dist/reports/src/sigv4.js +96 -0
  220. package/dist/reports/src/snapshot-bundle.d.ts +24 -1
  221. package/dist/reports/src/snapshot-bundle.js +27 -2
  222. package/dist/reports/src/snapshot-surface.d.ts +20 -9
  223. package/dist/reports/src/snapshot-surface.js +32 -19
  224. package/dist/reports/src/source-content.d.ts +23 -0
  225. package/dist/reports/src/source-content.js +30 -1
  226. package/dist/reports/src/source-entries.d.ts +13 -0
  227. package/dist/reports/src/source-entries.js +6 -0
  228. package/dist/reports/src/source-part.d.ts +15 -0
  229. package/dist/reports/src/source-part.js +8 -0
  230. package/dist/reports/src/source-reader.d.ts +11 -0
  231. package/dist/reports/src/source-reader.js +5 -0
  232. package/dist/reports/src/spec-evidence.d.ts +12 -2
  233. package/dist/reports/src/spec-evidence.js +6 -2
  234. package/dist/reports/src/spec-inventory.d.ts +15 -1
  235. package/dist/reports/src/spec-inventory.js +22 -1
  236. package/dist/reports/src/spec-link.d.ts +28 -0
  237. package/dist/reports/src/spec-link.js +70 -0
  238. package/dist/reports/src/spec-set.d.ts +63 -0
  239. package/dist/reports/src/spec-set.js +177 -0
  240. package/dist/reports/src/status.d.ts +10 -0
  241. package/dist/reports/src/status.js +5 -0
  242. package/dist/reports/src/store-prefix.d.ts +28 -0
  243. package/dist/reports/src/store-prefix.js +22 -0
  244. package/dist/reports/src/store-url.d.ts +89 -0
  245. package/dist/reports/src/store-url.js +160 -0
  246. package/dist/reports/src/test-catalog-entity.d.ts +11 -0
  247. package/dist/reports/src/test-catalog-entity.js +6 -0
  248. package/dist/reports/src/types.d.ts +6 -1
  249. package/dist/reports/src/use-report-nav.d.ts +35 -0
  250. package/dist/reports/src/use-report-nav.js +12 -0
  251. package/dist/runners/src/codecept.d.ts +14 -0
  252. package/dist/runners/src/codecept.js +16 -0
  253. package/dist/runners/src/jest.d.ts +50 -0
  254. package/dist/runners/src/jest.js +52 -0
  255. package/dist/runners/src/mocha.d.ts +40 -0
  256. package/dist/runners/src/mocha.js +39 -0
  257. package/dist/runners/src/node-test.d.ts +28 -0
  258. package/dist/runners/src/node-test.js +44 -0
  259. package/dist/runners/src/publish.d.ts +30 -0
  260. package/dist/runners/src/publish.js +68 -0
  261. package/dist/schema/src/api.d.ts +800 -0
  262. package/dist/schema/src/api.js +201 -0
  263. package/dist/schema/src/cli-results.d.ts +210 -0
  264. package/dist/schema/src/cli-results.js +124 -0
  265. package/dist/schema/src/common.d.ts +53 -0
  266. package/dist/schema/src/common.js +42 -0
  267. package/dist/schema/src/index.d.ts +16 -0
  268. package/dist/schema/src/index.js +16 -0
  269. package/dist/schema/src/layer.d.ts +2273 -0
  270. package/dist/schema/src/layer.js +462 -0
  271. package/dist/schema/src/live-run.d.ts +123 -0
  272. package/dist/schema/src/live-run.js +30 -0
  273. package/dist/schema/src/manifest.d.ts +97 -2
  274. package/dist/schema/src/manifest.js +84 -0
  275. package/dist/schema/src/project.d.ts +20 -0
  276. package/dist/schema/src/project.js +10 -0
  277. package/dist/schema/src/report-sources.d.ts +29 -0
  278. package/dist/schema/src/report-sources.js +27 -1
  279. package/dist/schema/src/runs-index.d.ts +17 -1
  280. package/dist/schema/src/runs-index.js +15 -0
  281. package/dist/schema/src/server-config.d.ts +177 -0
  282. package/dist/schema/src/server-config.js +85 -0
  283. package/dist/schema/src/spec.d.ts +69 -0
  284. package/dist/schema/src/spec.js +58 -0
  285. package/dist/server/src/app.d.ts +72 -0
  286. package/dist/server/src/app.js +557 -0
  287. package/dist/server/src/auth.d.ts +41 -0
  288. package/dist/server/src/auth.js +59 -0
  289. package/dist/server/src/config.d.ts +83 -0
  290. package/dist/server/src/config.js +204 -0
  291. package/dist/server/src/dev.d.ts +12 -0
  292. package/dist/server/src/dev.js +89 -0
  293. package/dist/server/src/events.d.ts +29 -0
  294. package/dist/server/src/events.js +101 -0
  295. package/dist/server/src/host.d.ts +30 -0
  296. package/dist/server/src/host.js +115 -0
  297. package/dist/server/src/index.d.ts +18 -0
  298. package/dist/server/src/index.js +18 -0
  299. package/dist/server/src/live-runs.d.ts +52 -0
  300. package/dist/server/src/live-runs.js +229 -0
  301. package/dist/server/src/llms.d.ts +19 -0
  302. package/dist/server/src/llms.js +63 -0
  303. package/dist/server/src/main.d.ts +30 -0
  304. package/dist/server/src/main.js +114 -0
  305. package/dist/server/src/openapi.d.ts +56 -0
  306. package/dist/server/src/openapi.js +438 -0
  307. package/dist/server/src/problem.d.ts +8 -0
  308. package/dist/server/src/problem.js +39 -0
  309. package/dist/server/src/projects.d.ts +3 -0
  310. package/dist/server/src/projects.js +154 -0
  311. package/dist/server/src/settings-page.d.ts +9 -0
  312. package/dist/server/src/settings-page.js +93 -0
  313. package/dist/server/src/settings.d.ts +23 -0
  314. package/dist/server/src/settings.js +72 -0
  315. package/dist/server/src/site.d.ts +15 -0
  316. package/dist/server/src/site.js +78 -0
  317. package/dist/server/src/store-api.d.ts +28 -0
  318. package/dist/server/src/store-api.js +180 -0
  319. package/dist/server/src/validate.d.ts +71 -0
  320. package/dist/server/src/validate.js +197 -0
  321. package/dist/vitest/src/index.d.ts +27 -5
  322. package/dist/vitest/src/index.js +18 -5
  323. package/dist/webdriver/src/driver.d.ts +63 -11
  324. package/dist/webdriver/src/driver.js +57 -6
  325. package/dist/webdriver/src/index.d.ts +8 -0
  326. package/dist/webdriver/src/index.js +1 -0
  327. package/dist/webdriver/src/protocol.d.ts +75 -6
  328. package/dist/webdriver/src/protocol.js +79 -6
  329. package/docs/agents.md +137 -0
  330. package/docs/deploying.md +345 -0
  331. package/docs/dogfooding.md +175 -0
  332. package/docs/extending.md +242 -0
  333. package/docs/integrating.md +485 -0
  334. package/docs/reference/bun.md +141 -0
  335. package/docs/reference/cli.md +148 -0
  336. package/docs/reference/core.md +2432 -0
  337. package/docs/reference/detox.md +150 -0
  338. package/docs/reference/http-api.md +799 -0
  339. package/docs/reference/playwright.md +303 -0
  340. package/docs/reference/reports.md +7152 -0
  341. package/docs/reference/runners.md +169 -0
  342. package/docs/reference/schema.md +1762 -0
  343. package/docs/reference/server.md +825 -0
  344. package/docs/reference/vitest.md +45 -0
  345. package/docs/reference/webdriver.md +330 -0
  346. package/docs/report-backends.md +144 -0
  347. package/docs/report-contract.md +400 -0
  348. package/docs/reporting-issues.md +160 -0
  349. package/docs/runners.md +69 -0
  350. package/docs/server.md +321 -0
  351. package/docs/statecharts.md +643 -0
  352. package/llms-full.txt +4114 -0
  353. package/llms.txt +49 -0
  354. package/package.json +61 -11
  355. package/packages/core/README.md +2 -1
  356. package/packages/reports/README.md +65 -0
  357. package/packages/reports/STORAGE.md +4 -0
  358. package/packages/runners/README.md +6 -0
  359. package/packages/runners/compat/codecept.cjs +2 -0
  360. package/packages/runners/compat/jest.cjs +2 -0
  361. package/packages/runners/compat/mocha.cjs +2 -0
  362. package/packages/schema/README.md +14 -0
  363. package/packages/schema/schemas/atlas-change.schema.json +4 -0
  364. package/packages/schema/schemas/check-detail.schema.json +1032 -0
  365. package/packages/schema/schemas/checks-result.schema.json +26 -0
  366. package/packages/schema/schemas/command-error.schema.json +35 -0
  367. package/packages/schema/schemas/doctor-result.schema.json +60 -0
  368. package/packages/schema/schemas/effective-delta.schema.json +490 -0
  369. package/packages/schema/schemas/effective-evidence.schema.json +471 -0
  370. package/packages/schema/schemas/evidence-page.schema.json +271 -0
  371. package/packages/schema/schemas/hello-event.schema.json +26 -0
  372. package/packages/schema/schemas/init-plan.schema.json +347 -0
  373. package/packages/schema/schemas/layer-rows.schema.json +199 -0
  374. package/packages/schema/schemas/layer.schema.json +2888 -0
  375. package/packages/schema/schemas/live-run-start.schema.json +42 -0
  376. package/packages/schema/schemas/live-run-update.schema.json +93 -0
  377. package/packages/schema/schemas/live-run.schema.json +225 -0
  378. package/packages/schema/schemas/merge-result.schema.json +40 -0
  379. package/packages/schema/schemas/outline.schema.json +62 -0
  380. package/packages/schema/schemas/problem.schema.json +37 -0
  381. package/packages/schema/schemas/project-slug.schema.json +8 -0
  382. package/packages/schema/schemas/project.schema.json +22 -0
  383. package/packages/schema/schemas/projects.schema.json +33 -0
  384. package/packages/schema/schemas/publish-checks.schema.json +1413 -0
  385. package/packages/schema/schemas/publish-result.schema.json +168 -0
  386. package/packages/schema/schemas/ready.schema.json +68 -0
  387. package/packages/schema/schemas/report-event.schema.json +100 -0
  388. package/packages/schema/schemas/report-status.schema.json +370 -0
  389. package/packages/schema/schemas/server-config.schema.json +208 -0
  390. package/packages/schema/schemas/server-editable-settings.schema.json +132 -0
  391. package/packages/schema/schemas/server-info.schema.json +114 -0
  392. package/packages/schema/schemas/server-settings-snapshot.schema.json +285 -0
  393. package/packages/schema/schemas/spec-set.schema.json +103 -0
  394. package/packages/schema/schemas/store-listing.schema.json +29 -0
  395. package/packages/schema/schemas/store-write-result.schema.json +28 -0
  396. package/packages/schema/schemas/stream-result.schema.json +59 -0
  397. package/packages/server/README.md +43 -0
  398. package/packages/server/openapi.json +11021 -0
  399. package/packages/visualizer/README.md +32 -11
  400. package/packages/visualizer/bin/atlas-client.mjs +176 -0
  401. package/packages/visualizer/bin/atlas-visualizer.mjs +2 -175
  402. package/packages/visualizer/src/app/AppShell.tsx +32 -16
  403. package/packages/visualizer/src/app/report-sources.tsx +7 -4
  404. package/packages/visualizer/src/app/router.tsx +3 -3
  405. package/packages/visualizer/src/components/ActivityIndicator.module.css +4 -0
  406. package/packages/visualizer/src/components/ActivityIndicator.tsx +6 -0
  407. package/packages/visualizer/src/components/Header.module.css +101 -192
  408. package/packages/visualizer/src/components/Header.tsx +88 -13
  409. package/packages/visualizer/src/components/ProjectPicker.tsx +64 -0
  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/data/adapters/report-backend.test.ts +3 -2
  413. package/packages/visualizer/src/data/adapters/report-backend.ts +1 -1
  414. package/packages/visualizer/src/data/queries.ts +14 -7
  415. package/packages/visualizer/src/evidence/api.ts +16 -3
  416. package/packages/visualizer/src/evidence/artifacts.module.css +213 -0
  417. package/packages/visualizer/src/evidence/artifacts.tsx +272 -0
  418. package/packages/visualizer/src/evidence/check-detail.tsx +21 -4
  419. package/packages/visualizer/src/evidence/document-page.tsx +6 -2
  420. package/packages/visualizer/src/evidence/evidence-context.tsx +79 -12
  421. package/packages/visualizer/src/evidence/evidence.module.css +11 -1
  422. package/packages/visualizer/src/evidence/feature-page.tsx +7 -3
  423. package/packages/visualizer/src/evidence/model-page.tsx +6 -2
  424. package/packages/visualizer/src/evidence/project-home.tsx +45 -0
  425. package/packages/visualizer/src/evidence/recording-page.tsx +4 -1
  426. package/packages/visualizer/src/evidence/spec-index.tsx +2 -1
  427. package/packages/visualizer/src/evidence/spec-page.tsx +23 -16
  428. package/packages/visualizer/src/evidence/test-filter.tsx +21 -0
  429. package/packages/visualizer/src/find/Finder.tsx +1 -7
  430. package/packages/visualizer/src/find/open.ts +4 -0
  431. package/packages/visualizer/src/live/live-query.ts +21 -0
  432. package/packages/visualizer/src/live/live-runs.tsx +69 -0
  433. package/packages/visualizer/src/live/live-stream.ts +225 -0
  434. package/packages/visualizer/src/live/report-progress.ts +62 -0
  435. package/packages/visualizer/src/live/test-progress.tsx +34 -0
  436. package/packages/visualizer/src/runtime.ts +35 -0
  437. package/packages/visualizer/src/state/ui-store.ts +3 -2
  438. package/packages/visualizer/src/views/ChartPage.tsx +2 -0
  439. package/packages/visualizer/src/views/ChartTests.tsx +43 -0
  440. package/packages/visualizer/src/views/SidePanel.tsx +2 -0
  441. package/packages/visualizer/vite/atlas-files.test.ts +4 -4
  442. package/packages/visualizer/vite/atlas-files.ts +1 -5
  443. package/packages/visualizer/vite/atlas-server-proxy.ts +37 -0
  444. package/packages/visualizer/vite.config.ts +11 -3
@@ -23,7 +23,7 @@ the API contract, streaming notifications and adding another transport.
23
23
  ## Point it at your project
24
24
 
25
25
  ```sh
26
- atlas-visualizer dev --specs ./specs --runs ./atlas-runs
26
+ atlas client dev --specs ./specs --runs ./atlas-runs
27
27
  ```
28
28
 
29
29
  | Flag | Meaning |
@@ -45,12 +45,33 @@ 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 client 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 client 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
51
- atlas-visualizer dev --specs <dir> --runs <dir> # live, from disk
52
- atlas-visualizer build --specs <dir> --runs <dir> --out site # static site + its data
53
- atlas-visualizer publish-data --specs <dir> --runs <dir> --out data # data only
72
+ atlas client dev --specs <dir> --runs <dir> # live, from disk
73
+ atlas client build --specs <dir> --runs <dir> --out site # static site + its data
74
+ atlas client publish-data --specs <dir> --runs <dir> --out data # data only
54
75
  ```
55
76
 
56
77
  `build` writes a static site that any file server can host, with the spec and the newest privacy-checked snapshots
@@ -62,14 +83,14 @@ sets how often the site checks for new runs (default 60 s, plus when the tab reg
62
83
  ### Inside this repository
63
84
 
64
85
  ```sh
65
- bun run viz:dev # the full coffee example, charts and layered report
86
+ bun client:dev # the full coffee example, charts and layered report
66
87
  bun test packages/visualizer/src packages/visualizer/vite # parsing, composition, statuses, every fixture
67
- bun run viz:fixtures # regenerate the fixtures (needs ImageMagick, cwebp, ffmpeg)
88
+ bun client:fixtures # regenerate the fixtures (needs ImageMagick, cwebp, ffmpeg)
68
89
  ```
69
90
 
70
- `bun run viz:publish-data` and `bun run viz:build` do the same as the CLI, writing to `public/data` (or
91
+ `bun client:publish-data` and `bun client:build` do the same as the CLI, writing to `public/data` (or
71
92
  `ATLAS_OUT`) and `dist`. `ATLAS_SOURCES_FILE` supplies additional sources, and
72
- `VITE_ATLAS_SOURCES_URL` can point a hosted viewer at a separate source descriptor file.
93
+ `VITE_ATLAS_SOURCES_URL` can point a hosted client at a separate source descriptor file.
73
94
 
74
95
  ## What it reads
75
96
 
@@ -103,7 +124,7 @@ The spec parsing is `@crvouga/atlas/spec`, the browser-safe part of the Atlas co
103
124
  ## How it fits together
104
125
 
105
126
  ```
106
- bin/atlas-visualizer.mjs CLI: dev, build and publish-data through Vite's JS API
127
+ bin/atlas-client.mjs CLI: dev, build and publish-data through Vite's JS API
107
128
  vite/atlas-files.ts node: resolve the roots, find spec files, summarize run directories, safe paths
108
129
  vite/atlas-dev-plugin.ts dev adapter: serves /__atlas/{specs,runs}, range requests, change events over HMR
109
130
  vite/publish.ts lays out specs/ and runs/ (with index.json files) for a static host
@@ -150,8 +171,8 @@ Beside the statechart map, every other kind of spec has its own tab: Gherkin fea
150
171
 
151
172
  Results come from the Atlas reports API (`/api/reports` in dev, `VITE_ATLAS_REPORTS_URL` when hosted elsewhere) or from a static `data/evidence` export. They combine across backends, branches, versions, environments and layers; **Sources** in the header switches any of them off. While a spec suite is running, the header says so and each finished test appears within a couple of seconds. See [Atlas reports](../reports/README.md) for the API and host configuration.
152
173
 
153
- The coffee example ships those documents and a layered report. `bun run viz:dev` opens that full example, and the same report is the fallback when a working directory has no `specs/` and `ATLAS_REPORTS_MODULE` is unset. `atlas:viz` serves the same kind of report for the todo example, and adds the desktop web layer `example:run` writes. Each built-in report is five layers: an older desktop web run, a mobile web branch, Android from a device lab, a focused iOS run, and a newer desktop web run that repeated one scenario. The same check stays visible on each environment. A feature with no layer stays empty.
174
+ `bun atlas` starts a project-agnostic node. The Todo example publishes into project `todo-app` through `bun example:run`; it never configures the server. A project without specs or reports stays empty. Bundled coffee fixtures are available only through explicit `atlas client` fixture options for client development and tests.
154
175
 
155
176
  The header title is the product's name, taken from `atlas.yaml`'s `title` up to its first dash or colon, or "Atlas".
156
177
 
157
- `src/evidence/` holds this part of the viewer: `evidence-context.tsx` reads and combines the layers, `spec-index.tsx` is the index, `feature-page.tsx`, `model-page.tsx` and `document-page.tsx` are the three kinds of spec page, `check-detail.tsx` is one check's results, and `recording.tsx` is the recorded timeline. `tests/reports.test.tsx` drives the whole viewer against an in-memory reports API.
178
+ `src/evidence/` holds this part of the client: `evidence-context.tsx` reads and combines the layers, `spec-index.tsx` is the index, `feature-page.tsx`, `model-page.tsx` and `document-page.tsx` are the three kinds of spec page, `check-detail.tsx` is one check's results, and `recording.tsx` is the recorded timeline. `tests/reports.test.tsx` drives the whole client against an in-memory reports API.
@@ -0,0 +1,176 @@
1
+ #!/usr/bin/env node
2
+ import { existsSync, readdirSync, statSync } from 'node:fs';
3
+ import os from 'node:os';
4
+ import path from 'node:path';
5
+ import { fileURLToPath } from 'node:url';
6
+ import { parseArgs } from 'node:util';
7
+
8
+ const APP_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
9
+ const CONFIG_FILE = path.join(APP_ROOT, 'vite.config.ts');
10
+
11
+ const USAGE = `atlas client: browse project specifications and their test reports
12
+
13
+ Usage
14
+ atlas client dev [--specs <dir>] [--runs <dir>] [--port <n>] [--host <host>]
15
+ atlas client build [--specs <dir>] [--runs <dir>] [--out <dir>] [--keep <n>] [--no-data]
16
+ atlas client publish-data [--specs <dir>] [--runs <dir>] [--out <dir>] [--keep <n>]
17
+
18
+ Options
19
+ --specs <dir> all specs: statecharts, Gherkin, Quint, DMN, Alloy, notes (default: ./specs)
20
+ --runs <dir> run directories, each with a manifest.json (default: ./atlas-runs)
21
+ --sources <file> additional directory, HTTP and streaming API report sources (JSON)
22
+ --fixture <name> show a bundled example instead (spec-only, partial, full, failures, removed, malformed, large)
23
+ --out <dir> build: the static site (default ./atlas-site); publish-data: the data (default ./atlas-data)
24
+ --keep <n> how many of the newest runs to publish (default 30)
25
+ --no-data build: leave the data out; point the site at it with VITE_ATLAS_BASE_URL
26
+ --port <n> dev: port to listen on (default 5180, or the next free one)
27
+ --host <host> dev: address to listen on (default localhost)
28
+
29
+ Relative paths resolve against the directory the command was run from. ATLAS_SPECS_ROOT and
30
+ ATLAS_RUNS_ROOT are read when the flags are absent.
31
+ `;
32
+
33
+ class UsageError extends Error {}
34
+
35
+ function parse(argv) {
36
+ const { values, positionals } = parseArgs({
37
+ args: argv,
38
+ allowPositionals: true,
39
+ strict: true,
40
+ options: {
41
+ specs: { type: 'string' },
42
+ runs: { type: 'string' },
43
+ sources: { type: 'string' },
44
+ fixture: { type: 'string' },
45
+ out: { type: 'string' },
46
+ keep: { type: 'string' },
47
+ port: { type: 'string' },
48
+ host: { type: 'string' },
49
+ 'no-data': { type: 'boolean', default: false },
50
+ help: { type: 'boolean', short: 'h', default: false }
51
+ }
52
+ });
53
+ return { command: positionals[0], extra: positionals.slice(1), values };
54
+ }
55
+
56
+ function integer(name, value, min, max) {
57
+ if (value === undefined) return undefined;
58
+ const n = Number(value);
59
+ if (!Number.isInteger(n) || n < min || n > max) throw new UsageError(`--${name} must be a whole number from ${min} to ${max}, got "${value}"`);
60
+ return n;
61
+ }
62
+
63
+ /** Point the shared root resolution (vite/atlas-files.ts) at the flags, as absolute paths. */
64
+ function applyRoots(values, base) {
65
+ if (values.sources) process.env.ATLAS_SOURCES_FILE = path.resolve(base, values.sources);
66
+ if (values.fixture) {
67
+ if (values.specs || values.runs) throw new UsageError('--fixture cannot be combined with --specs or --runs');
68
+ process.env.ATLAS_FIXTURE = values.fixture;
69
+ return;
70
+ }
71
+ if (values.specs || values.runs) delete process.env.ATLAS_FIXTURE;
72
+ if (values.specs) {
73
+ const specs = path.resolve(base, values.specs);
74
+ if (!existsSync(specs) || !statSync(specs).isDirectory()) throw new UsageError(`No specs directory at ${specs}`);
75
+ process.env.ATLAS_SPECS_ROOT = specs;
76
+ } else if (process.env.ATLAS_SPECS_ROOT) {
77
+ process.env.ATLAS_SPECS_ROOT = path.resolve(base, process.env.ATLAS_SPECS_ROOT);
78
+ }
79
+ if (values.runs) process.env.ATLAS_RUNS_ROOT = path.resolve(base, values.runs);
80
+ else if (process.env.ATLAS_RUNS_ROOT) process.env.ATLAS_RUNS_ROOT = path.resolve(base, process.env.ATLAS_RUNS_ROOT);
81
+ process.env.INIT_CWD = base;
82
+ }
83
+
84
+ /** Refuse to empty a directory that is not a previous build: the base, its ancestors, home, or anything else with files. */
85
+ function assertSafeOut(out, base) {
86
+ const rel = path.relative(out, base);
87
+ if (out === path.parse(out).root || out === os.homedir() || rel === '' || !rel.startsWith('..')) {
88
+ throw new UsageError(`Refusing to build into ${out}: it holds the directory you ran from. Pass --out <new dir>.`);
89
+ }
90
+ if (existsSync(out)) {
91
+ const entries = readdirSync(out).filter((n) => n !== '.DS_Store');
92
+ if (entries.length && !entries.includes('index.html')) {
93
+ throw new UsageError(`Refusing to replace ${out}: it is not empty and is not a previous build. Pass --out <new dir>.`);
94
+ }
95
+ }
96
+ }
97
+
98
+ async function loadNodeSide() {
99
+ const { runnerImport } = await import('vite');
100
+ const files = await runnerImport(path.join(APP_ROOT, 'vite/atlas-files.ts'));
101
+ const publish = await runnerImport(path.join(APP_ROOT, 'vite/publish.ts'));
102
+ return { resolveRoots: files.module.resolveRoots, publishData: publish.module.publishData };
103
+ }
104
+
105
+ function publish(publishData, roots, out, keep) {
106
+ const result = publishData({ roots, out, keep: keep ?? 30 });
107
+ process.stdout.write(`Published ${result.files.length} spec file(s) and ${result.published.length} run(s) to ${out}\n`);
108
+ if (result.skipped.length) process.stdout.write(`Not published, privacy check missing or failed: ${result.skipped.join(', ')}\n`);
109
+ }
110
+
111
+ async function main(argv) {
112
+ const { command, extra, values } = parse(argv);
113
+ if (values.help || !command) {
114
+ process.stdout.write(USAGE);
115
+ return command || values.help ? 0 : 1;
116
+ }
117
+ if (extra.length) throw new UsageError(`Unexpected argument: ${extra.join(' ')}`);
118
+ const base = process.env.INIT_CWD || process.cwd();
119
+ const port = integer('port', values.port, 0, 65_535);
120
+ const keep = integer('keep', values.keep, 0, 10_000);
121
+ applyRoots(values, base);
122
+
123
+ if (command === 'dev') {
124
+ const { createServer } = await import('vite');
125
+ const server = await createServer({
126
+ configFile: CONFIG_FILE,
127
+ configLoader: 'runner',
128
+ root: APP_ROOT,
129
+ server: { ...(port !== undefined ? { port } : {}), ...(values.host ? { host: values.host } : {}), strictPort: false }
130
+ });
131
+ await server.listen();
132
+ server.printUrls();
133
+ server.bindCLIShortcuts({ print: true });
134
+ return null;
135
+ }
136
+
137
+ if (command === 'build') {
138
+ const out = path.resolve(base, values.out ?? 'atlas-site');
139
+ assertSafeOut(out, base);
140
+ const { build } = await import('vite');
141
+ await build({
142
+ configFile: CONFIG_FILE,
143
+ configLoader: 'runner',
144
+ root: APP_ROOT,
145
+ publicDir: false,
146
+ build: { outDir: out, emptyOutDir: true }
147
+ });
148
+ if (!values['no-data']) {
149
+ const { resolveRoots, publishData } = await loadNodeSide();
150
+ publish(publishData, resolveRoots(), path.join(out, 'data'), keep);
151
+ }
152
+ process.stdout.write(`Built the site in ${out}; serve it with any static file server.\n`);
153
+ return 0;
154
+ }
155
+
156
+ if (command === 'publish-data') {
157
+ const out = path.resolve(base, values.out ?? 'atlas-data');
158
+ const { resolveRoots, publishData } = await loadNodeSide();
159
+ publish(publishData, resolveRoots(), out, keep);
160
+ return 0;
161
+ }
162
+
163
+ throw new UsageError(`Unknown command "${command}"`);
164
+ }
165
+
166
+ main(process.argv.slice(2)).then(
167
+ (code) => {
168
+ if (typeof code === 'number') process.exitCode = code;
169
+ },
170
+ (error) => {
171
+ const usage = error instanceof UsageError || error?.code === 'ERR_PARSE_ARGS_UNKNOWN_OPTION' || error?.code === 'ERR_PARSE_ARGS_INVALID_OPTION_VALUE';
172
+ process.stderr.write(`atlas client: ${error instanceof Error ? error.message : String(error)}\n`);
173
+ if (usage) process.stderr.write(`\n${USAGE}`);
174
+ process.exitCode = 1;
175
+ }
176
+ );
@@ -1,176 +1,3 @@
1
1
  #!/usr/bin/env node
2
- import { existsSync, readdirSync, statSync } from 'node:fs';
3
- import os from 'node:os';
4
- import path from 'node:path';
5
- import { fileURLToPath } from 'node:url';
6
- import { parseArgs } from 'node:util';
7
-
8
- const APP_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
9
- const CONFIG_FILE = path.join(APP_ROOT, 'vite.config.ts');
10
-
11
- const USAGE = `atlas-visualizer: browse statechart specs and the runs that execute them
12
-
13
- Usage
14
- atlas-visualizer dev [--specs <dir>] [--runs <dir>] [--port <n>] [--host <host>]
15
- atlas-visualizer build [--specs <dir>] [--runs <dir>] [--out <dir>] [--keep <n>] [--no-data]
16
- atlas-visualizer publish-data [--specs <dir>] [--runs <dir>] [--out <dir>] [--keep <n>]
17
-
18
- Options
19
- --specs <dir> all specs: statecharts, Gherkin, Quint, DMN, Alloy, notes (default: ./specs)
20
- --runs <dir> run directories, each with a manifest.json (default: ./atlas-runs)
21
- --sources <file> additional directory, HTTP and streaming API report sources (JSON)
22
- --fixture <name> show a bundled example instead (spec-only, partial, full, failures, removed, malformed, large)
23
- --out <dir> build: the static site (default ./atlas-site); publish-data: the data (default ./atlas-data)
24
- --keep <n> how many of the newest runs to publish (default 30)
25
- --no-data build: leave the data out; point the site at it with VITE_ATLAS_BASE_URL
26
- --port <n> dev: port to listen on (default 5180, or the next free one)
27
- --host <host> dev: address to listen on (default localhost)
28
-
29
- Relative paths resolve against the directory the command was run from. ATLAS_SPECS_ROOT and
30
- ATLAS_RUNS_ROOT are read when the flags are absent.
31
- `;
32
-
33
- class UsageError extends Error {}
34
-
35
- function parse(argv) {
36
- const { values, positionals } = parseArgs({
37
- args: argv,
38
- allowPositionals: true,
39
- strict: true,
40
- options: {
41
- specs: { type: 'string' },
42
- runs: { type: 'string' },
43
- sources: { type: 'string' },
44
- fixture: { type: 'string' },
45
- out: { type: 'string' },
46
- keep: { type: 'string' },
47
- port: { type: 'string' },
48
- host: { type: 'string' },
49
- 'no-data': { type: 'boolean', default: false },
50
- help: { type: 'boolean', short: 'h', default: false }
51
- }
52
- });
53
- return { command: positionals[0], extra: positionals.slice(1), values };
54
- }
55
-
56
- function integer(name, value, min, max) {
57
- if (value === undefined) return undefined;
58
- const n = Number(value);
59
- if (!Number.isInteger(n) || n < min || n > max) throw new UsageError(`--${name} must be a whole number from ${min} to ${max}, got "${value}"`);
60
- return n;
61
- }
62
-
63
- /** Point the shared root resolution (vite/atlas-files.ts) at the flags, as absolute paths. */
64
- function applyRoots(values, base) {
65
- if (values.sources) process.env.ATLAS_SOURCES_FILE = path.resolve(base, values.sources);
66
- if (values.fixture) {
67
- if (values.specs || values.runs) throw new UsageError('--fixture cannot be combined with --specs or --runs');
68
- process.env.ATLAS_FIXTURE = values.fixture;
69
- return;
70
- }
71
- if (values.specs || values.runs) delete process.env.ATLAS_FIXTURE;
72
- if (values.specs) {
73
- const specs = path.resolve(base, values.specs);
74
- if (!existsSync(specs) || !statSync(specs).isDirectory()) throw new UsageError(`No specs directory at ${specs}`);
75
- process.env.ATLAS_SPECS_ROOT = specs;
76
- } else if (process.env.ATLAS_SPECS_ROOT) {
77
- process.env.ATLAS_SPECS_ROOT = path.resolve(base, process.env.ATLAS_SPECS_ROOT);
78
- }
79
- if (values.runs) process.env.ATLAS_RUNS_ROOT = path.resolve(base, values.runs);
80
- else if (process.env.ATLAS_RUNS_ROOT) process.env.ATLAS_RUNS_ROOT = path.resolve(base, process.env.ATLAS_RUNS_ROOT);
81
- process.env.INIT_CWD = base;
82
- }
83
-
84
- /** Refuse to empty a directory that is not a previous build: the base, its ancestors, home, or anything else with files. */
85
- function assertSafeOut(out, base) {
86
- const rel = path.relative(out, base);
87
- if (out === path.parse(out).root || out === os.homedir() || rel === '' || !rel.startsWith('..')) {
88
- throw new UsageError(`Refusing to build into ${out}: it holds the directory you ran from. Pass --out <new dir>.`);
89
- }
90
- if (existsSync(out)) {
91
- const entries = readdirSync(out).filter((n) => n !== '.DS_Store');
92
- if (entries.length && !entries.includes('index.html')) {
93
- throw new UsageError(`Refusing to replace ${out}: it is not empty and is not a previous build. Pass --out <new dir>.`);
94
- }
95
- }
96
- }
97
-
98
- async function loadNodeSide() {
99
- const { runnerImport } = await import('vite');
100
- const files = await runnerImport(path.join(APP_ROOT, 'vite/atlas-files.ts'));
101
- const publish = await runnerImport(path.join(APP_ROOT, 'vite/publish.ts'));
102
- return { resolveRoots: files.module.resolveRoots, publishData: publish.module.publishData };
103
- }
104
-
105
- function publish(publishData, roots, out, keep) {
106
- const result = publishData({ roots, out, keep: keep ?? 30 });
107
- process.stdout.write(`Published ${result.files.length} spec file(s) and ${result.published.length} run(s) to ${out}\n`);
108
- if (result.skipped.length) process.stdout.write(`Not published, privacy check missing or failed: ${result.skipped.join(', ')}\n`);
109
- }
110
-
111
- async function main(argv) {
112
- const { command, extra, values } = parse(argv);
113
- if (values.help || !command) {
114
- process.stdout.write(USAGE);
115
- return command || values.help ? 0 : 1;
116
- }
117
- if (extra.length) throw new UsageError(`Unexpected argument: ${extra.join(' ')}`);
118
- const base = process.env.INIT_CWD || process.cwd();
119
- const port = integer('port', values.port, 0, 65_535);
120
- const keep = integer('keep', values.keep, 0, 10_000);
121
- applyRoots(values, base);
122
-
123
- if (command === 'dev') {
124
- const { createServer } = await import('vite');
125
- const server = await createServer({
126
- configFile: CONFIG_FILE,
127
- configLoader: 'runner',
128
- root: APP_ROOT,
129
- server: { ...(port !== undefined ? { port } : {}), ...(values.host ? { host: values.host } : {}), strictPort: false }
130
- });
131
- await server.listen();
132
- server.printUrls();
133
- server.bindCLIShortcuts({ print: true });
134
- return null;
135
- }
136
-
137
- if (command === 'build') {
138
- const out = path.resolve(base, values.out ?? 'atlas-site');
139
- assertSafeOut(out, base);
140
- const { build } = await import('vite');
141
- await build({
142
- configFile: CONFIG_FILE,
143
- configLoader: 'runner',
144
- root: APP_ROOT,
145
- publicDir: false,
146
- build: { outDir: out, emptyOutDir: true }
147
- });
148
- if (!values['no-data']) {
149
- const { resolveRoots, publishData } = await loadNodeSide();
150
- publish(publishData, resolveRoots(), path.join(out, 'data'), keep);
151
- }
152
- process.stdout.write(`Built the site in ${out}; serve it with any static file server.\n`);
153
- return 0;
154
- }
155
-
156
- if (command === 'publish-data') {
157
- const out = path.resolve(base, values.out ?? 'atlas-data');
158
- const { resolveRoots, publishData } = await loadNodeSide();
159
- publish(publishData, resolveRoots(), out, keep);
160
- return 0;
161
- }
162
-
163
- throw new UsageError(`Unknown command "${command}"`);
164
- }
165
-
166
- main(process.argv.slice(2)).then(
167
- (code) => {
168
- if (typeof code === 'number') process.exitCode = code;
169
- },
170
- (error) => {
171
- const usage = error instanceof UsageError || error?.code === 'ERR_PARSE_ARGS_UNKNOWN_OPTION' || error?.code === 'ERR_PARSE_ARGS_INVALID_OPTION_VALUE';
172
- process.stderr.write(`atlas-visualizer: ${error instanceof Error ? error.message : String(error)}\n`);
173
- if (usage) process.stderr.write(`\n${USAGE}`);
174
- process.exitCode = 1;
175
- }
176
- );
2
+ // Compatibility entry point for already installed clients.
3
+ import './atlas-client.mjs';
@@ -1,24 +1,36 @@
1
1
  import { Outlet, useLocation, useSearch } from '@tanstack/react-router';
2
+ import { lazy, Suspense } from 'react';
2
3
 
4
+ import { LiveRunsProvider } from '../live/live-runs';
3
5
  import { Header } from '../components/Header';
4
6
  import { EvidenceProvider } from '../evidence/evidence-context';
5
7
  import { SourcesPanel } from '../evidence/sources-panel';
6
8
  import { SpecIndex } from '../evidence/spec-index';
7
9
  import { kindOfAddress } from '../spec-kind';
8
10
  import styles from '../views/views.module.css';
9
- import { Finder } from '../find/Finder';
10
11
  import { AtlasProvider, useAtlas } from './atlas-context';
11
12
  import { Gazetteer } from './gazetteer';
13
+ import { useEvidence } from '../evidence/evidence-context';
14
+ import { isStatechartFile } from '@crvouga/atlas/schema';
15
+ import { ProjectHome, ProjectSpecIndex } from '../evidence/project-home';
16
+
17
+ // The finder brings every spec parser with it. It loads as soon as the page has drawn, long before anyone presses ⌘K.
18
+ const loadFinder = () => import('../find/Finder').then((m) => ({ default: m.Finder }));
19
+ const Finder = lazy(loadFinder);
20
+ if (typeof window !== 'undefined') setTimeout(() => void loadFinder(), 0);
12
21
 
13
22
  function Index() {
23
+ const evidence = useEvidence();
14
24
  const { pathname } = useLocation();
15
25
  const kind = kindOfAddress(pathname);
16
- return kind ? <SpecIndex kind={kind} /> : pathname.startsWith('/specs/') ? <nav aria-label="Specs" /> : <Gazetteer />;
26
+ return kind ? <SpecIndex kind={kind} /> : pathname.startsWith('/specs/') ? <nav aria-label="Specs" /> : evidence.ready && !evidence.files.some(isStatechartFile) ? <ProjectSpecIndex /> : <Gazetteer />;
17
27
  }
18
28
 
19
29
  function Body() {
30
+ const evidence = useEvidence();
20
31
  const { view, spec } = useAtlas();
21
32
  const { pathname } = useLocation();
33
+ if (pathname === '/' && evidence.ready && !evidence.filesPending && !evidence.files.some(isStatechartFile)) return <ProjectHome />;
22
34
  if (!pathname.startsWith('/specs/') && !view && spec.error) {
23
35
  return (
24
36
  <div className={styles.message} role="alert">
@@ -36,20 +48,24 @@ function Body() {
36
48
  export function AppShell() {
37
49
  const { run } = useSearch({ strict: false });
38
50
  return (
39
- <AtlasProvider run={run}>
40
- <EvidenceProvider>
41
- <div className={styles.app}>
42
- <Header />
43
- <div className={styles.frame}>
44
- <Index />
45
- <main className={styles.main}>
46
- <Body />
47
- </main>
51
+ <LiveRunsProvider enabled={!run}>
52
+ <AtlasProvider run={run}>
53
+ <EvidenceProvider>
54
+ <div className={styles.app}>
55
+ <Header />
56
+ <div className={styles.frame}>
57
+ <Index />
58
+ <main className={styles.main}>
59
+ <Body />
60
+ </main>
61
+ </div>
62
+ <SourcesPanel />
63
+ <Suspense fallback={null}>
64
+ <Finder />
65
+ </Suspense>
48
66
  </div>
49
- <SourcesPanel />
50
- <Finder />
51
- </div>
52
- </EvidenceProvider>
53
- </AtlasProvider>
67
+ </EvidenceProvider>
68
+ </AtlasProvider>
69
+ </LiveRunsProvider>
54
70
  );
55
71
  }
@@ -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,17 @@ 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',
47
+ ...(server ? { eventsUrl: runtimeUrl(runtimeConfig()?.eventsUrl) } : {})
45
48
  }),
46
- [adapter]
49
+ [adapter, server]
47
50
  );
48
51
  const configBase = defaultConfig.baseUrl.endsWith('/') ? defaultConfig.baseUrl : `${defaultConfig.baseUrl}/`;
49
52
  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,
@@ -0,0 +1,4 @@
1
+ .activity { display: inline-flex; align-items: center; justify-content: center; width: 12px; height: 12px; flex: none; color: var(--place); }
2
+ .ring { box-sizing: border-box; width: 10px; height: 10px; border: 1.5px solid currentColor; border-right-color: transparent; border-bottom-color: transparent; border-radius: 50%; opacity: .65; animation: turn 1.8s linear infinite; }
3
+ @keyframes turn { to { transform: rotate(360deg); } }
4
+ @media (prefers-reduced-motion: reduce) { .ring { animation: none; } }
@@ -0,0 +1,6 @@
1
+ import styles from './ActivityIndicator.module.css';
2
+
3
+ /** A quiet indication of active work; reduced motion keeps a static ring. */
4
+ export function ActivityIndicator({ label = 'Working' }: { label?: string }) {
5
+ return <span className={styles.activity} role="status" aria-label={label} title={label}><span className={styles.ring} aria-hidden="true" /></span>;
6
+ }