@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.
- package/CHANGELOG.md +13 -1
- package/README.md +191 -611
- package/dist/bun/src/driver.d.ts +41 -7
- package/dist/bun/src/driver.js +47 -6
- package/dist/bun/src/index.d.ts +8 -0
- package/dist/bun/src/index.js +8 -0
- package/dist/core/src/cli.d.ts +14 -1
- package/dist/core/src/cli.js +35 -1
- package/dist/core/src/config.d.ts +77 -4
- package/dist/core/src/config.js +110 -17
- package/dist/core/src/events/cloudevents.d.ts +54 -9
- package/dist/core/src/events/cloudevents.js +60 -9
- package/dist/core/src/graph.d.ts +21 -0
- package/dist/core/src/graph.js +26 -0
- package/dist/core/src/index.d.ts +11 -0
- package/dist/core/src/index.js +11 -0
- package/dist/core/src/lint.d.ts +30 -0
- package/dist/core/src/lint.js +13 -0
- package/dist/core/src/plan.d.ts +39 -1
- package/dist/core/src/plan.js +28 -1
- package/dist/core/src/privacy.d.ts +29 -2
- package/dist/core/src/privacy.js +19 -2
- package/dist/core/src/report/formats.d.ts +34 -4
- package/dist/core/src/report/formats.js +24 -4
- package/dist/core/src/report/markdown.d.ts +15 -0
- package/dist/core/src/report/markdown.js +8 -0
- package/dist/core/src/run/clients.d.ts +44 -5
- package/dist/core/src/run/clients.js +46 -2
- package/dist/core/src/run/execute.d.ts +75 -16
- package/dist/core/src/run/execute.js +71 -18
- package/dist/core/src/run/frames.d.ts +13 -4
- package/dist/core/src/run/frames.js +19 -4
- package/dist/core/src/run/http.d.ts +34 -1
- package/dist/core/src/run/http.js +14 -1
- package/dist/core/src/run/types.d.ts +76 -3
- package/dist/core/src/run/types.js +19 -3
- package/dist/core/src/signals.d.ts +49 -5
- package/dist/core/src/signals.js +22 -5
- package/dist/core/src/spec/compose.d.ts +23 -1
- package/dist/core/src/spec/compose.js +10 -1
- package/dist/core/src/spec/index.d.ts +9 -1
- package/dist/core/src/spec/index.js +9 -1
- package/dist/core/src/spec/load.d.ts +22 -1
- package/dist/core/src/spec/load.js +31 -1
- package/dist/core/src/spec/parse.d.ts +16 -2
- package/dist/core/src/spec/parse.js +25 -2
- package/dist/core/src/spec/scxml.d.ts +25 -4
- package/dist/core/src/spec/scxml.js +36 -4
- package/dist/core/src/spec/types.d.ts +52 -1
- package/dist/core/src/spec/types.js +24 -0
- package/dist/detox/src/index.d.ts +54 -11
- package/dist/detox/src/index.js +45 -7
- package/dist/playwright/src/driver.d.ts +39 -3
- package/dist/playwright/src/driver.js +27 -3
- package/dist/playwright/src/index.d.ts +10 -0
- package/dist/playwright/src/index.js +10 -0
- package/dist/playwright/src/media.d.ts +50 -4
- package/dist/playwright/src/media.js +55 -4
- package/dist/playwright/src/signals.d.ts +44 -5
- package/dist/playwright/src/signals.js +29 -5
- package/dist/reports/src/attachment-files.d.ts +44 -0
- package/dist/reports/src/attachment-files.js +121 -0
- package/dist/reports/src/attachment-response.d.ts +31 -0
- package/dist/reports/src/attachment-response.js +55 -0
- package/dist/reports/src/aws-credentials.d.ts +48 -0
- package/dist/reports/src/aws-credentials.js +221 -0
- package/dist/reports/src/browser-navigation.d.ts +14 -0
- package/dist/reports/src/browser-navigation.js +7 -0
- package/dist/reports/src/bundle.d.ts +34 -2
- package/dist/reports/src/bundle.js +38 -2
- package/dist/reports/src/catalog.d.ts +34 -3
- package/dist/reports/src/catalog.js +17 -3
- package/dist/reports/src/cli-stores.d.ts +36 -0
- package/dist/reports/src/cli-stores.js +73 -0
- package/dist/reports/src/clock.d.ts +7 -2
- package/dist/reports/src/clock.js +5 -2
- package/dist/reports/src/completion-order.d.ts +18 -5
- package/dist/reports/src/completion-order.js +18 -5
- package/dist/reports/src/compose.d.ts +80 -0
- package/dist/reports/src/compose.js +117 -0
- package/dist/reports/src/copy-store.d.ts +29 -0
- package/dist/reports/src/copy-store.js +52 -0
- package/dist/reports/src/directory-outline.d.ts +38 -0
- package/dist/reports/src/directory-outline.js +103 -0
- package/dist/reports/src/directory-store.d.ts +20 -0
- package/dist/reports/src/directory-store.js +166 -0
- package/dist/reports/src/doctor.d.ts +29 -0
- package/dist/reports/src/doctor.js +85 -0
- package/dist/reports/src/effective-delta.d.ts +34 -0
- package/dist/reports/src/effective-delta.js +25 -0
- package/dist/reports/src/evidence-detail.d.ts +64 -0
- package/dist/reports/src/evidence-detail.js +92 -0
- package/dist/reports/src/evidence-live.d.ts +35 -5
- package/dist/reports/src/evidence-live.js +18 -2
- package/dist/reports/src/evidence-types.d.ts +68 -1
- package/dist/reports/src/evidence-types.js +5 -1
- package/dist/reports/src/evidence-view.d.ts +43 -4
- package/dist/reports/src/evidence-view.js +13 -2
- package/dist/reports/src/explorer-types.d.ts +40 -3
- package/dist/reports/src/explorer-types.js +8 -0
- package/dist/reports/src/explorer-versions.d.ts +9 -1
- package/dist/reports/src/explorer-versions.js +18 -1
- package/dist/reports/src/export-evidence.d.ts +16 -1
- package/dist/reports/src/export-evidence.js +52 -1
- package/dist/reports/src/fetcher.d.ts +27 -3
- package/dist/reports/src/fetcher.js +11 -3
- package/dist/reports/src/format-date.d.ts +6 -0
- package/dist/reports/src/format-date.js +6 -0
- package/dist/reports/src/format.d.ts +16 -0
- package/dist/reports/src/format.js +10 -0
- package/dist/reports/src/frames.d.ts +36 -3
- package/dist/reports/src/frames.js +22 -3
- package/dist/reports/src/hash.d.ts +18 -0
- package/dist/reports/src/hash.js +13 -0
- package/dist/reports/src/host.d.ts +29 -0
- package/dist/reports/src/host.js +64 -0
- package/dist/reports/src/html-frame.d.ts +5 -6
- package/dist/reports/src/html-frame.js +12 -1
- package/dist/reports/src/http-store.d.ts +56 -0
- package/dist/reports/src/http-store.js +148 -0
- package/dist/reports/src/http.d.ts +7 -0
- package/dist/reports/src/http.js +6 -0
- package/dist/reports/src/ingest.d.ts +54 -0
- package/dist/reports/src/ingest.js +210 -0
- package/dist/reports/src/init.d.ts +40 -0
- package/dist/reports/src/init.js +369 -0
- package/dist/reports/src/junit.d.ts +33 -6
- package/dist/reports/src/junit.js +87 -21
- package/dist/reports/src/keyboard.d.ts +20 -0
- package/dist/reports/src/keyboard.js +11 -0
- package/dist/reports/src/keys.d.ts +21 -3
- package/dist/reports/src/keys.js +21 -3
- package/dist/reports/src/layer-axes.d.ts +35 -5
- package/dist/reports/src/layer-axes.js +25 -5
- package/dist/reports/src/layer-catalog.d.ts +28 -1
- package/dist/reports/src/layer-catalog.js +18 -1
- package/dist/reports/src/layer-merge.d.ts +98 -9
- package/dist/reports/src/layer-merge.js +147 -31
- package/dist/reports/src/layer-source.d.ts +18 -0
- package/dist/reports/src/layer-source.js +11 -0
- package/dist/reports/src/layer-store.d.ts +42 -0
- package/dist/reports/src/layer-store.js +63 -5
- package/dist/reports/src/layer-types.d.ts +32 -1
- package/dist/reports/src/layers-route.d.ts +47 -4
- package/dist/reports/src/layers-route.js +86 -7
- package/dist/reports/src/live-reporter.d.ts +34 -0
- package/dist/reports/src/live-reporter.js +122 -0
- package/dist/reports/src/media-types.d.ts +11 -0
- package/dist/reports/src/media-types.js +22 -0
- package/dist/reports/src/namespace-guard.d.ts +10 -1
- package/dist/reports/src/namespace-guard.js +10 -1
- package/dist/reports/src/navigation.d.ts +12 -0
- package/dist/reports/src/navigation.js +7 -0
- package/dist/reports/src/object-io.d.ts +11 -4
- package/dist/reports/src/object-io.js +4 -4
- package/dist/reports/src/object-store-conformance.d.ts +27 -0
- package/dist/reports/src/object-store-conformance.js +87 -0
- package/dist/reports/src/object-store.d.ts +33 -24
- package/dist/reports/src/object-store.js +17 -24
- package/dist/reports/src/parse-with.d.ts +16 -0
- package/dist/reports/src/parse-with.js +14 -0
- package/dist/reports/src/performance-part.d.ts +25 -1
- package/dist/reports/src/performance-part.js +15 -1
- package/dist/reports/src/performance.d.ts +22 -2
- package/dist/reports/src/performance.js +12 -2
- package/dist/reports/src/persist.d.ts +12 -3
- package/dist/reports/src/persist.js +12 -3
- package/dist/reports/src/phone.d.ts +18 -1
- package/dist/reports/src/phone.js +18 -1
- package/dist/reports/src/protocol.d.ts +26 -4
- package/dist/reports/src/protocol.js +15 -4
- package/dist/reports/src/publication.d.ts +21 -1
- package/dist/reports/src/publication.js +21 -1
- package/dist/reports/src/publish-checks.d.ts +35 -0
- package/dist/reports/src/publish-checks.js +122 -0
- package/dist/reports/src/publish.d.ts +42 -9
- package/dist/reports/src/publish.js +36 -7
- package/dist/reports/src/read.d.ts +46 -16
- package/dist/reports/src/read.js +38 -14
- package/dist/reports/src/recording-bundle.d.ts +13 -2
- package/dist/reports/src/recording-bundle.js +4 -2
- package/dist/reports/src/recording-keys.d.ts +15 -1
- package/dist/reports/src/recording-keys.js +15 -1
- package/dist/reports/src/remote-report.d.ts +28 -7
- package/dist/reports/src/remote-report.js +15 -7
- package/dist/reports/src/report-cli.d.ts +51 -0
- package/dist/reports/src/report-cli.js +486 -0
- package/dist/reports/src/report-index.d.ts +99 -0
- package/dist/reports/src/report-index.js +337 -0
- package/dist/reports/src/report-loader.d.ts +10 -0
- package/dist/reports/src/report-loader.js +6 -0
- package/dist/reports/src/report-parts.d.ts +29 -3
- package/dist/reports/src/report-parts.js +37 -3
- package/dist/reports/src/report-status.d.ts +48 -0
- package/dist/reports/src/report-status.js +156 -0
- package/dist/reports/src/report-types.d.ts +73 -0
- package/dist/reports/src/report-types.js +3 -0
- package/dist/reports/src/reporter.d.ts +67 -0
- package/dist/reports/src/reporter.js +128 -0
- package/dist/reports/src/reports-explorer-route.d.ts +541 -0
- package/dist/reports/src/reports-explorer-route.js +11 -0
- package/dist/reports/src/reports-list-route.d.ts +9 -0
- package/dist/reports/src/reports-list-route.js +9 -0
- package/dist/reports/src/reports-route.d.ts +561 -22
- package/dist/reports/src/reports-route.js +11 -3
- package/dist/reports/src/reports-store.d.ts +19 -2
- package/dist/reports/src/reports-store.js +10 -2
- package/dist/reports/src/resilient-cache.d.ts +14 -1
- package/dist/reports/src/resilient-cache.js +8 -1
- package/dist/reports/src/retention.d.ts +17 -1
- package/dist/reports/src/retention.js +5 -1
- package/dist/reports/src/run-bundle.d.ts +63 -0
- package/dist/reports/src/run-bundle.js +159 -0
- package/dist/reports/src/s3-store.d.ts +79 -0
- package/dist/reports/src/s3-store.js +295 -0
- package/dist/reports/src/scenario.d.ts +14 -4
- package/dist/reports/src/scenario.js +5 -4
- package/dist/reports/src/sigv4.d.ts +72 -0
- package/dist/reports/src/sigv4.js +96 -0
- package/dist/reports/src/snapshot-bundle.d.ts +24 -1
- package/dist/reports/src/snapshot-bundle.js +27 -2
- package/dist/reports/src/snapshot-surface.d.ts +20 -9
- package/dist/reports/src/snapshot-surface.js +32 -19
- package/dist/reports/src/source-content.d.ts +23 -0
- package/dist/reports/src/source-content.js +30 -1
- package/dist/reports/src/source-entries.d.ts +13 -0
- package/dist/reports/src/source-entries.js +6 -0
- package/dist/reports/src/source-part.d.ts +15 -0
- package/dist/reports/src/source-part.js +8 -0
- package/dist/reports/src/source-reader.d.ts +11 -0
- package/dist/reports/src/source-reader.js +5 -0
- package/dist/reports/src/spec-evidence.d.ts +12 -2
- package/dist/reports/src/spec-evidence.js +6 -2
- package/dist/reports/src/spec-inventory.d.ts +15 -1
- package/dist/reports/src/spec-inventory.js +22 -1
- package/dist/reports/src/spec-link.d.ts +28 -0
- package/dist/reports/src/spec-link.js +70 -0
- package/dist/reports/src/spec-set.d.ts +63 -0
- package/dist/reports/src/spec-set.js +177 -0
- package/dist/reports/src/status.d.ts +10 -0
- package/dist/reports/src/status.js +5 -0
- package/dist/reports/src/store-prefix.d.ts +28 -0
- package/dist/reports/src/store-prefix.js +22 -0
- package/dist/reports/src/store-url.d.ts +89 -0
- package/dist/reports/src/store-url.js +160 -0
- package/dist/reports/src/test-catalog-entity.d.ts +11 -0
- package/dist/reports/src/test-catalog-entity.js +6 -0
- package/dist/reports/src/types.d.ts +6 -1
- package/dist/reports/src/use-report-nav.d.ts +35 -0
- package/dist/reports/src/use-report-nav.js +12 -0
- package/dist/runners/src/codecept.d.ts +14 -0
- package/dist/runners/src/codecept.js +16 -0
- package/dist/runners/src/jest.d.ts +50 -0
- package/dist/runners/src/jest.js +52 -0
- package/dist/runners/src/mocha.d.ts +40 -0
- package/dist/runners/src/mocha.js +39 -0
- package/dist/runners/src/node-test.d.ts +28 -0
- package/dist/runners/src/node-test.js +44 -0
- package/dist/runners/src/publish.d.ts +30 -0
- package/dist/runners/src/publish.js +68 -0
- package/dist/schema/src/api.d.ts +800 -0
- package/dist/schema/src/api.js +201 -0
- package/dist/schema/src/cli-results.d.ts +210 -0
- package/dist/schema/src/cli-results.js +124 -0
- package/dist/schema/src/common.d.ts +53 -0
- package/dist/schema/src/common.js +42 -0
- package/dist/schema/src/index.d.ts +16 -0
- package/dist/schema/src/index.js +16 -0
- package/dist/schema/src/layer.d.ts +2273 -0
- package/dist/schema/src/layer.js +462 -0
- package/dist/schema/src/live-run.d.ts +123 -0
- package/dist/schema/src/live-run.js +30 -0
- package/dist/schema/src/manifest.d.ts +97 -2
- package/dist/schema/src/manifest.js +84 -0
- package/dist/schema/src/project.d.ts +20 -0
- package/dist/schema/src/project.js +10 -0
- package/dist/schema/src/report-sources.d.ts +29 -0
- package/dist/schema/src/report-sources.js +27 -1
- package/dist/schema/src/runs-index.d.ts +17 -1
- package/dist/schema/src/runs-index.js +15 -0
- package/dist/schema/src/server-config.d.ts +177 -0
- package/dist/schema/src/server-config.js +85 -0
- package/dist/schema/src/spec.d.ts +69 -0
- package/dist/schema/src/spec.js +58 -0
- package/dist/server/src/app.d.ts +72 -0
- package/dist/server/src/app.js +557 -0
- package/dist/server/src/auth.d.ts +41 -0
- package/dist/server/src/auth.js +59 -0
- package/dist/server/src/config.d.ts +83 -0
- package/dist/server/src/config.js +204 -0
- package/dist/server/src/dev.d.ts +12 -0
- package/dist/server/src/dev.js +89 -0
- package/dist/server/src/events.d.ts +29 -0
- package/dist/server/src/events.js +101 -0
- package/dist/server/src/host.d.ts +30 -0
- package/dist/server/src/host.js +115 -0
- package/dist/server/src/index.d.ts +18 -0
- package/dist/server/src/index.js +18 -0
- package/dist/server/src/live-runs.d.ts +52 -0
- package/dist/server/src/live-runs.js +229 -0
- package/dist/server/src/llms.d.ts +19 -0
- package/dist/server/src/llms.js +63 -0
- package/dist/server/src/main.d.ts +30 -0
- package/dist/server/src/main.js +114 -0
- package/dist/server/src/openapi.d.ts +56 -0
- package/dist/server/src/openapi.js +438 -0
- package/dist/server/src/problem.d.ts +8 -0
- package/dist/server/src/problem.js +39 -0
- package/dist/server/src/projects.d.ts +3 -0
- package/dist/server/src/projects.js +154 -0
- package/dist/server/src/settings-page.d.ts +9 -0
- package/dist/server/src/settings-page.js +93 -0
- package/dist/server/src/settings.d.ts +23 -0
- package/dist/server/src/settings.js +72 -0
- package/dist/server/src/site.d.ts +15 -0
- package/dist/server/src/site.js +78 -0
- package/dist/server/src/store-api.d.ts +28 -0
- package/dist/server/src/store-api.js +180 -0
- package/dist/server/src/validate.d.ts +71 -0
- package/dist/server/src/validate.js +197 -0
- package/dist/vitest/src/index.d.ts +27 -5
- package/dist/vitest/src/index.js +18 -5
- package/dist/webdriver/src/driver.d.ts +63 -11
- package/dist/webdriver/src/driver.js +57 -6
- package/dist/webdriver/src/index.d.ts +8 -0
- package/dist/webdriver/src/index.js +1 -0
- package/dist/webdriver/src/protocol.d.ts +75 -6
- package/dist/webdriver/src/protocol.js +79 -6
- package/docs/agents.md +137 -0
- package/docs/deploying.md +345 -0
- package/docs/dogfooding.md +175 -0
- package/docs/extending.md +242 -0
- package/docs/integrating.md +485 -0
- package/docs/reference/bun.md +141 -0
- package/docs/reference/cli.md +148 -0
- package/docs/reference/core.md +2432 -0
- package/docs/reference/detox.md +150 -0
- package/docs/reference/http-api.md +799 -0
- package/docs/reference/playwright.md +303 -0
- package/docs/reference/reports.md +7152 -0
- package/docs/reference/runners.md +169 -0
- package/docs/reference/schema.md +1762 -0
- package/docs/reference/server.md +825 -0
- package/docs/reference/vitest.md +45 -0
- package/docs/reference/webdriver.md +330 -0
- package/docs/report-backends.md +144 -0
- package/docs/report-contract.md +400 -0
- package/docs/reporting-issues.md +160 -0
- package/docs/runners.md +69 -0
- package/docs/server.md +321 -0
- package/docs/statecharts.md +643 -0
- package/llms-full.txt +4114 -0
- package/llms.txt +49 -0
- package/package.json +61 -11
- package/packages/core/README.md +2 -1
- package/packages/reports/README.md +65 -0
- package/packages/reports/STORAGE.md +4 -0
- package/packages/runners/README.md +6 -0
- package/packages/runners/compat/codecept.cjs +2 -0
- package/packages/runners/compat/jest.cjs +2 -0
- package/packages/runners/compat/mocha.cjs +2 -0
- package/packages/schema/README.md +14 -0
- package/packages/schema/schemas/atlas-change.schema.json +4 -0
- package/packages/schema/schemas/check-detail.schema.json +1032 -0
- package/packages/schema/schemas/checks-result.schema.json +26 -0
- package/packages/schema/schemas/command-error.schema.json +35 -0
- package/packages/schema/schemas/doctor-result.schema.json +60 -0
- package/packages/schema/schemas/effective-delta.schema.json +490 -0
- package/packages/schema/schemas/effective-evidence.schema.json +471 -0
- package/packages/schema/schemas/evidence-page.schema.json +271 -0
- package/packages/schema/schemas/hello-event.schema.json +26 -0
- package/packages/schema/schemas/init-plan.schema.json +347 -0
- package/packages/schema/schemas/layer-rows.schema.json +199 -0
- package/packages/schema/schemas/layer.schema.json +2888 -0
- package/packages/schema/schemas/live-run-start.schema.json +42 -0
- package/packages/schema/schemas/live-run-update.schema.json +93 -0
- package/packages/schema/schemas/live-run.schema.json +225 -0
- package/packages/schema/schemas/merge-result.schema.json +40 -0
- package/packages/schema/schemas/outline.schema.json +62 -0
- package/packages/schema/schemas/problem.schema.json +37 -0
- package/packages/schema/schemas/project-slug.schema.json +8 -0
- package/packages/schema/schemas/project.schema.json +22 -0
- package/packages/schema/schemas/projects.schema.json +33 -0
- package/packages/schema/schemas/publish-checks.schema.json +1413 -0
- package/packages/schema/schemas/publish-result.schema.json +168 -0
- package/packages/schema/schemas/ready.schema.json +68 -0
- package/packages/schema/schemas/report-event.schema.json +100 -0
- package/packages/schema/schemas/report-status.schema.json +370 -0
- package/packages/schema/schemas/server-config.schema.json +208 -0
- package/packages/schema/schemas/server-editable-settings.schema.json +132 -0
- package/packages/schema/schemas/server-info.schema.json +114 -0
- package/packages/schema/schemas/server-settings-snapshot.schema.json +285 -0
- package/packages/schema/schemas/spec-set.schema.json +103 -0
- package/packages/schema/schemas/store-listing.schema.json +29 -0
- package/packages/schema/schemas/store-write-result.schema.json +28 -0
- package/packages/schema/schemas/stream-result.schema.json +59 -0
- package/packages/server/README.md +43 -0
- package/packages/server/openapi.json +11021 -0
- package/packages/visualizer/README.md +32 -11
- package/packages/visualizer/bin/atlas-client.mjs +176 -0
- package/packages/visualizer/bin/atlas-visualizer.mjs +2 -175
- package/packages/visualizer/src/app/AppShell.tsx +32 -16
- package/packages/visualizer/src/app/report-sources.tsx +7 -4
- package/packages/visualizer/src/app/router.tsx +3 -3
- package/packages/visualizer/src/components/ActivityIndicator.module.css +4 -0
- package/packages/visualizer/src/components/ActivityIndicator.tsx +6 -0
- package/packages/visualizer/src/components/Header.module.css +101 -192
- package/packages/visualizer/src/components/Header.tsx +88 -13
- package/packages/visualizer/src/components/ProjectPicker.tsx +64 -0
- package/packages/visualizer/src/data/adapters/adapter.ts +5 -17
- package/packages/visualizer/src/data/adapters/index.ts +10 -1
- package/packages/visualizer/src/data/adapters/report-backend.test.ts +3 -2
- package/packages/visualizer/src/data/adapters/report-backend.ts +1 -1
- package/packages/visualizer/src/data/queries.ts +14 -7
- package/packages/visualizer/src/evidence/api.ts +16 -3
- package/packages/visualizer/src/evidence/artifacts.module.css +213 -0
- package/packages/visualizer/src/evidence/artifacts.tsx +272 -0
- package/packages/visualizer/src/evidence/check-detail.tsx +21 -4
- package/packages/visualizer/src/evidence/document-page.tsx +6 -2
- package/packages/visualizer/src/evidence/evidence-context.tsx +79 -12
- package/packages/visualizer/src/evidence/evidence.module.css +11 -1
- package/packages/visualizer/src/evidence/feature-page.tsx +7 -3
- package/packages/visualizer/src/evidence/model-page.tsx +6 -2
- package/packages/visualizer/src/evidence/project-home.tsx +45 -0
- package/packages/visualizer/src/evidence/recording-page.tsx +4 -1
- package/packages/visualizer/src/evidence/spec-index.tsx +2 -1
- package/packages/visualizer/src/evidence/spec-page.tsx +23 -16
- package/packages/visualizer/src/evidence/test-filter.tsx +21 -0
- package/packages/visualizer/src/find/Finder.tsx +1 -7
- package/packages/visualizer/src/find/open.ts +4 -0
- package/packages/visualizer/src/live/live-query.ts +21 -0
- package/packages/visualizer/src/live/live-runs.tsx +69 -0
- package/packages/visualizer/src/live/live-stream.ts +225 -0
- package/packages/visualizer/src/live/report-progress.ts +62 -0
- package/packages/visualizer/src/live/test-progress.tsx +34 -0
- package/packages/visualizer/src/runtime.ts +35 -0
- package/packages/visualizer/src/state/ui-store.ts +3 -2
- package/packages/visualizer/src/views/ChartPage.tsx +2 -0
- package/packages/visualizer/src/views/ChartTests.tsx +43 -0
- package/packages/visualizer/src/views/SidePanel.tsx +2 -0
- package/packages/visualizer/vite/atlas-files.test.ts +4 -4
- package/packages/visualizer/vite/atlas-files.ts +1 -5
- package/packages/visualizer/vite/atlas-server-proxy.ts +37 -0
- 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
|
|
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
|
|
52
|
-
atlas
|
|
53
|
-
atlas
|
|
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
|
|
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
|
|
88
|
+
bun client:fixtures # regenerate the fixtures (needs ImageMagick, cwebp, ffmpeg)
|
|
68
89
|
```
|
|
69
90
|
|
|
70
|
-
`bun
|
|
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
|
|
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-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
3
|
-
import
|
|
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
|
-
<
|
|
40
|
-
<
|
|
41
|
-
<
|
|
42
|
-
<
|
|
43
|
-
|
|
44
|
-
<
|
|
45
|
-
|
|
46
|
-
<
|
|
47
|
-
|
|
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
|
-
|
|
50
|
-
|
|
51
|
-
|
|
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
|
-
|
|
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
|
+
}
|