@crvouga/atlas 0.0.0-stage → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -0
- package/LICENSE +21 -0
- package/README.md +649 -2
- package/dist/bun/src/driver.d.ts +67 -0
- package/dist/bun/src/driver.js +90 -0
- package/dist/bun/src/index.d.ts +2 -0
- package/dist/bun/src/index.js +2 -0
- package/dist/core/src/cli.d.ts +1 -0
- package/dist/core/src/cli.js +177 -0
- package/dist/core/src/config.d.ts +477 -0
- package/dist/core/src/config.js +100 -0
- package/dist/core/src/events/cloudevents.d.ts +56 -0
- package/dist/core/src/events/cloudevents.js +152 -0
- package/dist/core/src/graph.d.ts +113 -0
- package/dist/core/src/graph.js +221 -0
- package/dist/core/src/index.d.ts +17 -0
- package/dist/core/src/index.js +17 -0
- package/dist/core/src/lint.d.ts +53 -0
- package/dist/core/src/lint.js +138 -0
- package/dist/core/src/plan.d.ts +109 -0
- package/dist/core/src/plan.js +324 -0
- package/dist/core/src/privacy.d.ts +8 -0
- package/dist/core/src/privacy.js +42 -0
- package/dist/core/src/report/formats.d.ts +69 -0
- package/dist/core/src/report/formats.js +109 -0
- package/dist/core/src/report/markdown.d.ts +8 -0
- package/dist/core/src/report/markdown.js +140 -0
- package/dist/core/src/run/clients.d.ts +42 -0
- package/dist/core/src/run/clients.js +250 -0
- package/dist/core/src/run/execute.d.ts +854 -0
- package/dist/core/src/run/execute.js +877 -0
- package/dist/core/src/run/frames.d.ts +16 -0
- package/dist/core/src/run/frames.js +52 -0
- package/dist/core/src/run/http.d.ts +41 -0
- package/dist/core/src/run/http.js +71 -0
- package/dist/core/src/run/types.d.ts +248 -0
- package/dist/core/src/run/types.js +39 -0
- package/dist/core/src/signals.d.ts +110 -0
- package/dist/core/src/signals.js +121 -0
- package/dist/core/src/spec/compose.d.ts +28 -0
- package/dist/core/src/spec/compose.js +71 -0
- package/dist/core/src/spec/index.d.ts +5 -0
- package/dist/core/src/spec/index.js +5 -0
- package/dist/core/src/spec/load.d.ts +8 -0
- package/dist/core/src/spec/load.js +46 -0
- package/dist/core/src/spec/parse.d.ts +5 -0
- package/dist/core/src/spec/parse.js +32 -0
- package/dist/core/src/spec/scxml.d.ts +19 -0
- package/dist/core/src/spec/scxml.js +239 -0
- package/dist/core/src/spec/types.d.ts +96 -0
- package/dist/core/src/spec/types.js +25 -0
- package/dist/detox/src/index.d.ts +75 -0
- package/dist/detox/src/index.js +110 -0
- package/dist/playwright/src/driver.d.ts +44 -0
- package/dist/playwright/src/driver.js +82 -0
- package/dist/playwright/src/index.d.ts +3 -0
- package/dist/playwright/src/index.js +3 -0
- package/dist/playwright/src/media.d.ts +68 -0
- package/dist/playwright/src/media.js +283 -0
- package/dist/playwright/src/signals.d.ts +34 -0
- package/dist/playwright/src/signals.js +56 -0
- package/dist/reports/src/browser-navigation.d.ts +2 -0
- package/dist/reports/src/browser-navigation.js +28 -0
- package/dist/reports/src/bundle.d.ts +24 -0
- package/dist/reports/src/bundle.js +114 -0
- package/dist/reports/src/catalog.d.ts +37 -0
- package/dist/reports/src/catalog.js +127 -0
- package/dist/reports/src/clock.d.ts +14 -0
- package/dist/reports/src/clock.js +4 -0
- package/dist/reports/src/completion-order.d.ts +10 -0
- package/dist/reports/src/completion-order.js +76 -0
- package/dist/reports/src/evidence-live.d.ts +40 -0
- package/dist/reports/src/evidence-live.js +118 -0
- package/dist/reports/src/evidence-types.d.ts +90 -0
- package/dist/reports/src/evidence-types.js +15 -0
- package/dist/reports/src/evidence-view.d.ts +68 -0
- package/dist/reports/src/evidence-view.js +113 -0
- package/dist/reports/src/explorer-types.d.ts +56 -0
- package/dist/reports/src/explorer-types.js +0 -0
- package/dist/reports/src/explorer-versions.d.ts +4 -0
- package/dist/reports/src/explorer-versions.js +21 -0
- package/dist/reports/src/export-evidence.d.ts +11 -0
- package/dist/reports/src/export-evidence.js +94 -0
- package/dist/reports/src/fetcher.d.ts +50 -0
- package/dist/reports/src/fetcher.js +205 -0
- package/dist/reports/src/format-date.d.ts +1 -0
- package/dist/reports/src/format-date.js +10 -0
- package/dist/reports/src/format.d.ts +16 -0
- package/dist/reports/src/format.js +71 -0
- package/dist/reports/src/frames.d.ts +46 -0
- package/dist/reports/src/frames.js +245 -0
- package/dist/reports/src/hash.d.ts +17 -0
- package/dist/reports/src/hash.js +43 -0
- package/dist/reports/src/html-frame.d.ts +13 -0
- package/dist/reports/src/html-frame.js +63 -0
- package/dist/reports/src/http.d.ts +3 -0
- package/dist/reports/src/http.js +0 -0
- package/dist/reports/src/junit.d.ts +23 -0
- package/dist/reports/src/junit.js +119 -0
- package/dist/reports/src/keyboard.d.ts +16 -0
- package/dist/reports/src/keyboard.js +135 -0
- package/dist/reports/src/keys.d.ts +53 -0
- package/dist/reports/src/keys.js +122 -0
- package/dist/reports/src/layer-catalog.d.ts +5 -0
- package/dist/reports/src/layer-catalog.js +39 -0
- package/dist/reports/src/layer-merge.d.ts +43 -0
- package/dist/reports/src/layer-merge.js +154 -0
- package/dist/reports/src/layer-source.d.ts +6 -0
- package/dist/reports/src/layer-source.js +32 -0
- package/dist/reports/src/layer-store.d.ts +10 -0
- package/dist/reports/src/layer-store.js +36 -0
- package/dist/reports/src/layer-types.d.ts +32 -0
- package/dist/reports/src/layer-types.js +0 -0
- package/dist/reports/src/layers-route.d.ts +20 -0
- package/dist/reports/src/layers-route.js +259 -0
- package/dist/reports/src/namespace-guard.d.ts +11 -0
- package/dist/reports/src/namespace-guard.js +40 -0
- package/dist/reports/src/navigation.d.ts +8 -0
- package/dist/reports/src/navigation.js +0 -0
- package/dist/reports/src/object-io.d.ts +9 -0
- package/dist/reports/src/object-io.js +41 -0
- package/dist/reports/src/object-store.d.ts +67 -0
- package/dist/reports/src/object-store.js +32 -0
- package/dist/reports/src/performance-part.d.ts +9 -0
- package/dist/reports/src/performance-part.js +42 -0
- package/dist/reports/src/performance.d.ts +13 -0
- package/dist/reports/src/performance.js +45 -0
- package/dist/reports/src/persist.d.ts +10 -0
- package/dist/reports/src/persist.js +38 -0
- package/dist/reports/src/phone.d.ts +5 -0
- package/dist/reports/src/phone.js +18 -0
- package/dist/reports/src/protocol.d.ts +78 -0
- package/dist/reports/src/protocol.js +36 -0
- package/dist/reports/src/publication.d.ts +4 -0
- package/dist/reports/src/publication.js +35 -0
- package/dist/reports/src/publish.d.ts +52 -0
- package/dist/reports/src/publish.js +189 -0
- package/dist/reports/src/read.d.ts +62 -0
- package/dist/reports/src/read.js +214 -0
- package/dist/reports/src/recording-bundle.d.ts +22 -0
- package/dist/reports/src/recording-bundle.js +4 -0
- package/dist/reports/src/recording-keys.d.ts +9 -0
- package/dist/reports/src/recording-keys.js +50 -0
- package/dist/reports/src/remote-report.d.ts +39 -0
- package/dist/reports/src/remote-report.js +304 -0
- package/dist/reports/src/report-loader.d.ts +22 -0
- package/dist/reports/src/report-loader.js +0 -0
- package/dist/reports/src/report-parts.d.ts +10 -0
- package/dist/reports/src/report-parts.js +112 -0
- package/dist/reports/src/report-types.d.ts +194 -0
- package/dist/reports/src/report-types.js +29 -0
- package/dist/reports/src/reports-explorer-route.d.ts +90 -0
- package/dist/reports/src/reports-explorer-route.js +65 -0
- package/dist/reports/src/reports-list-route.d.ts +23 -0
- package/dist/reports/src/reports-list-route.js +55 -0
- package/dist/reports/src/reports-route.d.ts +267 -0
- package/dist/reports/src/reports-route.js +204 -0
- package/dist/reports/src/reports-store.d.ts +11 -0
- package/dist/reports/src/reports-store.js +28 -0
- package/dist/reports/src/resilient-cache.d.ts +3 -0
- package/dist/reports/src/resilient-cache.js +33 -0
- package/dist/reports/src/retention.d.ts +9 -0
- package/dist/reports/src/retention.js +16 -0
- package/dist/reports/src/scenario.d.ts +15 -0
- package/dist/reports/src/scenario.js +35 -0
- package/dist/reports/src/snapshot-bundle.d.ts +18 -0
- package/dist/reports/src/snapshot-bundle.js +94 -0
- package/dist/reports/src/snapshot-surface.d.ts +29 -0
- package/dist/reports/src/snapshot-surface.js +339 -0
- package/dist/reports/src/source-content.d.ts +30 -0
- package/dist/reports/src/source-content.js +63 -0
- package/dist/reports/src/source-entries.d.ts +4 -0
- package/dist/reports/src/source-entries.js +34 -0
- package/dist/reports/src/source-part.d.ts +4 -0
- package/dist/reports/src/source-part.js +66 -0
- package/dist/reports/src/source-reader.d.ts +6 -0
- package/dist/reports/src/source-reader.js +17 -0
- package/dist/reports/src/spec-evidence.d.ts +6 -0
- package/dist/reports/src/spec-evidence.js +20 -0
- package/dist/reports/src/spec-inventory.d.ts +14 -0
- package/dist/reports/src/spec-inventory.js +75 -0
- package/dist/reports/src/status.d.ts +4 -0
- package/dist/reports/src/status.js +8 -0
- package/dist/reports/src/store-prefix.d.ts +21 -0
- package/dist/reports/src/store-prefix.js +53 -0
- package/dist/reports/src/test-catalog-entity.d.ts +3 -0
- package/dist/reports/src/test-catalog-entity.js +34 -0
- package/dist/reports/src/types.d.ts +5 -0
- package/dist/reports/src/types.js +0 -0
- package/dist/reports/src/use-report-nav.d.ts +25 -0
- package/dist/reports/src/use-report-nav.js +181 -0
- package/dist/schema/src/common.d.ts +57 -0
- package/dist/schema/src/common.js +32 -0
- package/dist/schema/src/index.d.ts +5 -0
- package/dist/schema/src/index.js +5 -0
- package/dist/schema/src/manifest.d.ts +457 -0
- package/dist/schema/src/manifest.js +247 -0
- package/dist/schema/src/report-sources.d.ts +68 -0
- package/dist/schema/src/report-sources.js +53 -0
- package/dist/schema/src/runs-index.d.ts +52 -0
- package/dist/schema/src/runs-index.js +24 -0
- package/dist/schema/src/spec.d.ts +245 -0
- package/dist/schema/src/spec.js +133 -0
- package/dist/vitest/src/index.d.ts +22 -0
- package/dist/vitest/src/index.js +33 -0
- package/dist/webdriver/src/driver.d.ts +69 -0
- package/dist/webdriver/src/driver.js +230 -0
- package/dist/webdriver/src/index.d.ts +11 -0
- package/dist/webdriver/src/index.js +10 -0
- package/dist/webdriver/src/protocol.d.ts +60 -0
- package/dist/webdriver/src/protocol.js +146 -0
- package/package.json +181 -4
- package/packages/bun/README.md +69 -0
- package/packages/core/README.md +78 -0
- package/packages/core/bin/atlas.mjs +8 -0
- package/packages/detox/README.md +73 -0
- package/packages/playwright/README.md +77 -0
- package/packages/reports/README.md +47 -0
- package/packages/reports/STORAGE.md +73 -0
- package/packages/schema/README.md +23 -0
- package/packages/schema/schemas/atlas-change.schema.json +34 -0
- package/packages/schema/schemas/atlas.schema.json +32 -0
- package/packages/schema/schemas/cloud-event.schema.json +40 -0
- package/packages/schema/schemas/journeys.schema.json +17 -0
- package/packages/schema/schemas/manifest.schema.json +160 -0
- package/packages/schema/schemas/path-record.schema.json +151 -0
- package/packages/schema/schemas/report-sources.schema.json +95 -0
- package/packages/schema/schemas/run-info.schema.json +176 -0
- package/packages/schema/schemas/run-summary.schema.json +206 -0
- package/packages/schema/schemas/runs-index.schema.json +23 -0
- package/packages/schema/schemas/state-node.schema.json +238 -0
- package/packages/schema/schemas/state-record.schema.json +354 -0
- package/packages/schema/schemas/timeline-entry.schema.json +124 -0
- package/packages/schema/schemas/transition-record.schema.json +125 -0
- package/packages/visualizer/README.md +154 -0
- package/packages/visualizer/bin/atlas-visualizer.mjs +176 -0
- package/packages/visualizer/example/specs/coffee/ordering/checkout.scxml +82 -0
- package/packages/visualizer/example/specs/coffee/ordering/journeys.yaml +129 -0
- package/packages/visualizer/example/specs/coffee/ordering/known-issues.md +14 -0
- package/packages/visualizer/example/specs/coffee/ordering/machine.yaml +158 -0
- package/packages/visualizer/example/specs/coffee/ordering/open-questions.md +25 -0
- package/packages/visualizer/fixtures/failures/runs/2026-10-06T14-40-00-000Z-showcase/chart.mmd +67 -0
- package/packages/visualizer/fixtures/failures/runs/2026-10-06T14-40-00-000Z-showcase/ctrf.json +245 -0
- package/packages/visualizer/fixtures/failures/runs/2026-10-06T14-40-00-000Z-showcase/junit.xml +12 -0
- package/packages/visualizer/fixtures/failures/runs/2026-10-06T14-40-00-000Z-showcase/manifest.json +2003 -0
- package/packages/visualizer/fixtures/failures/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/001.vtt +9 -0
- package/packages/visualizer/fixtures/failures/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/002.vtt +9 -0
- package/packages/visualizer/fixtures/failures/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/003.vtt +9 -0
- package/packages/visualizer/fixtures/failures/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/004.vtt +9 -0
- package/packages/visualizer/fixtures/failures/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/005.vtt +17 -0
- package/packages/visualizer/fixtures/failures/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/006.vtt +9 -0
- package/packages/visualizer/fixtures/failures/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/007.vtt +17 -0
- package/packages/visualizer/fixtures/failures/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/008.vtt +17 -0
- package/packages/visualizer/fixtures/failures/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/009.vtt +13 -0
- package/packages/visualizer/fixtures/failures/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/010.vtt +9 -0
- package/packages/visualizer/fixtures/failures/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/011.vtt +9 -0
- package/packages/visualizer/fixtures/failures/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/012.vtt +9 -0
- package/packages/visualizer/fixtures/failures/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/013.vtt +9 -0
- package/packages/visualizer/fixtures/failures/runs/2026-10-06T14-40-00-000Z-showcase/media/clips/journey-1/01.mp4 +0 -0
- package/packages/visualizer/fixtures/failures/runs/2026-10-06T14-40-00-000Z-showcase/media/clips/journey-1/01.poster.webp +0 -0
- package/packages/visualizer/fixtures/failures/runs/2026-10-06T14-40-00-000Z-showcase/media/clips/journey-1/02.mp4 +0 -0
- package/packages/visualizer/fixtures/failures/runs/2026-10-06T14-40-00-000Z-showcase/media/clips/journey-1/02.poster.webp +0 -0
- package/packages/visualizer/fixtures/failures/runs/2026-10-06T14-40-00-000Z-showcase/media/clips/journey-1/03.mp4 +0 -0
- package/packages/visualizer/fixtures/failures/runs/2026-10-06T14-40-00-000Z-showcase/media/clips/journey-1/03.poster.webp +0 -0
- package/packages/visualizer/fixtures/failures/runs/2026-10-06T14-40-00-000Z-showcase/media/clips/journey-1/04.mp4 +0 -0
- package/packages/visualizer/fixtures/failures/runs/2026-10-06T14-40-00-000Z-showcase/media/clips/journey-1/04.poster.webp +0 -0
- package/packages/visualizer/fixtures/failures/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/01.png +0 -0
- package/packages/visualizer/fixtures/failures/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/01.webp +0 -0
- package/packages/visualizer/fixtures/failures/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/02.png +0 -0
- package/packages/visualizer/fixtures/failures/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/02.webp +0 -0
- package/packages/visualizer/fixtures/failures/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/03.png +0 -0
- package/packages/visualizer/fixtures/failures/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/03.webp +0 -0
- package/packages/visualizer/fixtures/failures/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/04.png +0 -0
- package/packages/visualizer/fixtures/failures/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/04.webp +0 -0
- package/packages/visualizer/fixtures/failures/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/05.png +0 -0
- package/packages/visualizer/fixtures/failures/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/05.webp +0 -0
- package/packages/visualizer/fixtures/failures/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-2/01.png +0 -0
- package/packages/visualizer/fixtures/failures/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-2/01.webp +0 -0
- package/packages/visualizer/fixtures/failures/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-3/failed-01.png +0 -0
- package/packages/visualizer/fixtures/failures/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-3/failed-01.webp +0 -0
- package/packages/visualizer/fixtures/failures/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-4/01.png +0 -0
- package/packages/visualizer/fixtures/failures/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-4/01.webp +0 -0
- package/packages/visualizer/fixtures/failures/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-5/01.png +0 -0
- package/packages/visualizer/fixtures/failures/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-5/01.webp +0 -0
- package/packages/visualizer/fixtures/failures/specs/coffee/ordering/checkout.scxml +82 -0
- package/packages/visualizer/fixtures/failures/specs/coffee/ordering/journeys.yaml +129 -0
- package/packages/visualizer/fixtures/failures/specs/coffee/ordering/known-issues.md +14 -0
- package/packages/visualizer/fixtures/failures/specs/coffee/ordering/machine.yaml +158 -0
- package/packages/visualizer/fixtures/failures/specs/coffee/ordering/open-questions.md +25 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-02T09-10-00-000Z-fast/chart.mmd +67 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-02T09-10-00-000Z-fast/ctrf.json +247 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-02T09-10-00-000Z-fast/junit.xml +12 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-02T09-10-00-000Z-fast/manifest.json +1875 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-04T16-20-00-000Z-showcase/chart.mmd +67 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-04T16-20-00-000Z-showcase/ctrf.json +244 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-04T16-20-00-000Z-showcase/junit.xml +12 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-04T16-20-00-000Z-showcase/manifest.json +1977 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-04T16-20-00-000Z-showcase/media/captions/001.vtt +9 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-04T16-20-00-000Z-showcase/media/captions/002.vtt +9 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-04T16-20-00-000Z-showcase/media/captions/003.vtt +9 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-04T16-20-00-000Z-showcase/media/captions/004.vtt +9 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-04T16-20-00-000Z-showcase/media/captions/005.vtt +17 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-04T16-20-00-000Z-showcase/media/captions/006.vtt +9 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-04T16-20-00-000Z-showcase/media/clips/journey-1/01.mp4 +0 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-04T16-20-00-000Z-showcase/media/clips/journey-1/01.poster.webp +0 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-04T16-20-00-000Z-showcase/media/clips/journey-1/02.mp4 +0 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-04T16-20-00-000Z-showcase/media/clips/journey-1/02.poster.webp +0 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-04T16-20-00-000Z-showcase/media/paths/journey-1/01.png +0 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-04T16-20-00-000Z-showcase/media/paths/journey-1/01.webp +0 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-04T16-20-00-000Z-showcase/media/paths/journey-1/02.png +0 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-04T16-20-00-000Z-showcase/media/paths/journey-1/02.webp +0 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-04T16-20-00-000Z-showcase/media/paths/journey-1/03.png +0 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-04T16-20-00-000Z-showcase/media/paths/journey-1/03.webp +0 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-04T16-20-00-000Z-showcase/media/paths/journey-2/01.png +0 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-04T16-20-00-000Z-showcase/media/paths/journey-2/01.webp +0 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/chart.mmd +67 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/ctrf.json +472 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/junit.xml +16 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/manifest.json +3701 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/001.vtt +9 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/002.vtt +9 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/003.vtt +9 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/004.vtt +9 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/005.vtt +9 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/006.vtt +13 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/007.vtt +9 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/008.vtt +13 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/009.vtt +17 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/010.vtt +9 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/011.vtt +13 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/012.vtt +9 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/013.vtt +9 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/014.vtt +9 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/015.vtt +9 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/016.vtt +9 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/017.vtt +9 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/018.vtt +17 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/019.vtt +9 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/020.vtt +9 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/021.vtt +9 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/022.vtt +17 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/023.vtt +17 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/024.vtt +13 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/025.vtt +9 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/026.vtt +9 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/027.vtt +9 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/028.vtt +9 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/clips/journey-1/01.mp4 +0 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/clips/journey-1/01.poster.webp +0 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/clips/journey-1/02.mp4 +0 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/clips/journey-1/02.poster.webp +0 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/clips/journey-1/03.mp4 +0 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/clips/journey-1/03.poster.webp +0 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/clips/journey-1/04.mp4 +0 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/clips/journey-1/04.poster.webp +0 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/01.png +0 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/01.webp +0 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/02.png +0 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/02.webp +0 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/03.png +0 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/03.webp +0 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/04.png +0 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/04.webp +0 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/05.png +0 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/05.webp +0 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/06.png +0 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/06.webp +0 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/07.png +0 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/07.webp +0 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/08.png +0 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/08.webp +0 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/09.png +0 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/09.webp +0 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/10.png +0 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/10.webp +0 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/11.png +0 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/11.webp +0 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/12.png +0 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/12.webp +0 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/13.png +0 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/13.webp +0 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/14.png +0 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/14.webp +0 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-2/01.png +0 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-2/01.webp +0 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-3/01.png +0 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-3/01.webp +0 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-4/01.png +0 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-4/01.webp +0 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-5/01.png +0 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-5/01.webp +0 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-6/01.png +0 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-6/01.webp +0 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-7/01.png +0 -0
- package/packages/visualizer/fixtures/full/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-7/01.webp +0 -0
- package/packages/visualizer/fixtures/full/specs/coffee/ordering/checkout.scxml +82 -0
- package/packages/visualizer/fixtures/full/specs/coffee/ordering/journeys.yaml +129 -0
- package/packages/visualizer/fixtures/full/specs/coffee/ordering/known-issues.md +14 -0
- package/packages/visualizer/fixtures/full/specs/coffee/ordering/machine.yaml +158 -0
- package/packages/visualizer/fixtures/full/specs/coffee/ordering/open-questions.md +25 -0
- package/packages/visualizer/fixtures/large/runs/2026-10-06T14-40-00-000Z-fast/ctrf.json +1280 -0
- package/packages/visualizer/fixtures/large/runs/2026-10-06T14-40-00-000Z-fast/junit.xml +26 -0
- package/packages/visualizer/fixtures/large/runs/2026-10-06T14-40-00-000Z-fast/manifest.json +28393 -0
- package/packages/visualizer/fixtures/large/specs/accounts/sign-up/identity-check.machine.yaml +397 -0
- package/packages/visualizer/fixtures/large/specs/accounts/sign-up/journeys.yaml +129 -0
- package/packages/visualizer/fixtures/large/specs/accounts/sign-up/machine.yaml +830 -0
- package/packages/visualizer/fixtures/large/specs/atlas.yaml +65 -0
- package/packages/visualizer/fixtures/large/specs/billing/invoices/journeys.yaml +132 -0
- package/packages/visualizer/fixtures/large/specs/billing/invoices/machine.yaml +830 -0
- package/packages/visualizer/fixtures/large/specs/billing/invoices/refund-handling.scxml +288 -0
- package/packages/visualizer/fixtures/large/specs/catalog/browsing/journeys.yaml +126 -0
- package/packages/visualizer/fixtures/large/specs/catalog/browsing/machine.yaml +831 -0
- package/packages/visualizer/fixtures/large/specs/catalog/browsing/product-comparison.machine.yaml +395 -0
- package/packages/visualizer/fixtures/large/specs/orders/checkout/journeys.yaml +120 -0
- package/packages/visualizer/fixtures/large/specs/orders/checkout/machine.yaml +724 -0
- package/packages/visualizer/fixtures/large/specs/orders/checkout/open-questions.md +16 -0
- package/packages/visualizer/fixtures/large/specs/orders/checkout/payment.scxml +288 -0
- package/packages/visualizer/fixtures/large/specs/shipping/deliveries/delivery-scheduling.machine.yaml +397 -0
- package/packages/visualizer/fixtures/large/specs/shipping/deliveries/journeys.yaml +132 -0
- package/packages/visualizer/fixtures/large/specs/shipping/deliveries/known-issues.md +13 -0
- package/packages/visualizer/fixtures/large/specs/shipping/deliveries/machine.yaml +830 -0
- package/packages/visualizer/fixtures/large/specs/support/tickets/escalation.machine.yaml +395 -0
- package/packages/visualizer/fixtures/large/specs/support/tickets/journeys.yaml +123 -0
- package/packages/visualizer/fixtures/large/specs/support/tickets/machine.yaml +720 -0
- package/packages/visualizer/fixtures/malformed/runs/2026-10-06T10-00-00-000Z-showcase/manifest.json +68 -0
- package/packages/visualizer/fixtures/malformed/runs/2026-10-06T11-00-00-000Z-showcase/ctrf.json +50 -0
- package/packages/visualizer/fixtures/malformed/runs/2026-10-06T11-00-00-000Z-showcase/junit.xml +6 -0
- package/packages/visualizer/fixtures/malformed/runs/2026-10-06T11-00-00-000Z-showcase/manifest.json +1281 -0
- package/packages/visualizer/fixtures/malformed/runs/2026-10-06T12-00-00-000Z-showcase/ctrf.json +50 -0
- package/packages/visualizer/fixtures/malformed/runs/2026-10-06T12-00-00-000Z-showcase/junit.xml +6 -0
- package/packages/visualizer/fixtures/malformed/runs/2026-10-06T12-00-00-000Z-showcase/manifest.json +1232 -0
- package/packages/visualizer/fixtures/malformed/runs/2026-10-06T13-00-00-000Z-showcase/manifest.json +1244 -0
- package/packages/visualizer/fixtures/malformed/specs/coffee/ordering/checkout.scxml +82 -0
- package/packages/visualizer/fixtures/malformed/specs/coffee/ordering/journeys.yaml +134 -0
- package/packages/visualizer/fixtures/malformed/specs/coffee/ordering/known-issues.md +14 -0
- package/packages/visualizer/fixtures/malformed/specs/coffee/ordering/loyalty.scxml +4 -0
- package/packages/visualizer/fixtures/malformed/specs/coffee/ordering/machine.yaml +156 -0
- package/packages/visualizer/fixtures/malformed/specs/coffee/ordering/open-questions.md +25 -0
- package/packages/visualizer/fixtures/partial/runs/2026-10-06T14-40-00-000Z-showcase/chart.mmd +67 -0
- package/packages/visualizer/fixtures/partial/runs/2026-10-06T14-40-00-000Z-showcase/ctrf.json +149 -0
- package/packages/visualizer/fixtures/partial/runs/2026-10-06T14-40-00-000Z-showcase/junit.xml +9 -0
- package/packages/visualizer/fixtures/partial/runs/2026-10-06T14-40-00-000Z-showcase/manifest.json +1718 -0
- package/packages/visualizer/fixtures/partial/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/001.vtt +9 -0
- package/packages/visualizer/fixtures/partial/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/002.vtt +17 -0
- package/packages/visualizer/fixtures/partial/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/003.vtt +17 -0
- package/packages/visualizer/fixtures/partial/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/004.vtt +9 -0
- package/packages/visualizer/fixtures/partial/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/005.vtt +9 -0
- package/packages/visualizer/fixtures/partial/runs/2026-10-06T14-40-00-000Z-showcase/media/clips/journey-1/01.mp4 +0 -0
- package/packages/visualizer/fixtures/partial/runs/2026-10-06T14-40-00-000Z-showcase/media/clips/journey-1/01.poster.webp +0 -0
- package/packages/visualizer/fixtures/partial/runs/2026-10-06T14-40-00-000Z-showcase/media/clips/journey-1/02.mp4 +0 -0
- package/packages/visualizer/fixtures/partial/runs/2026-10-06T14-40-00-000Z-showcase/media/clips/journey-1/02.poster.webp +0 -0
- package/packages/visualizer/fixtures/partial/runs/2026-10-06T14-40-00-000Z-showcase/media/clips/journey-1/03.mp4 +0 -0
- package/packages/visualizer/fixtures/partial/runs/2026-10-06T14-40-00-000Z-showcase/media/clips/journey-1/03.poster.webp +0 -0
- package/packages/visualizer/fixtures/partial/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/01.png +0 -0
- package/packages/visualizer/fixtures/partial/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/01.webp +0 -0
- package/packages/visualizer/fixtures/partial/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/02.png +0 -0
- package/packages/visualizer/fixtures/partial/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/02.webp +0 -0
- package/packages/visualizer/fixtures/partial/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/03.png +0 -0
- package/packages/visualizer/fixtures/partial/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/03.webp +0 -0
- package/packages/visualizer/fixtures/partial/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/05.png +0 -0
- package/packages/visualizer/fixtures/partial/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/05.webp +0 -0
- package/packages/visualizer/fixtures/partial/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-3/01.png +0 -0
- package/packages/visualizer/fixtures/partial/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-3/01.webp +0 -0
- package/packages/visualizer/fixtures/partial/runs/2026-10-06T15-05-00-000Z-showcase/media/paths/journey-1/00-start-01.png +0 -0
- package/packages/visualizer/fixtures/partial/runs/2026-10-06T15-05-00-000Z-showcase/media/paths/journey-1/00-start-01.webp +0 -0
- package/packages/visualizer/fixtures/partial/runs/2026-10-06T15-05-00-000Z-showcase/media/paths/journey-1/02.png +0 -0
- package/packages/visualizer/fixtures/partial/runs/2026-10-06T15-05-00-000Z-showcase/media/paths/journey-1/02.webp +0 -0
- package/packages/visualizer/fixtures/partial/specs/coffee/ordering/checkout.scxml +82 -0
- package/packages/visualizer/fixtures/partial/specs/coffee/ordering/journeys.yaml +129 -0
- package/packages/visualizer/fixtures/partial/specs/coffee/ordering/known-issues.md +14 -0
- package/packages/visualizer/fixtures/partial/specs/coffee/ordering/machine.yaml +158 -0
- package/packages/visualizer/fixtures/partial/specs/coffee/ordering/open-questions.md +25 -0
- package/packages/visualizer/fixtures/removed/runs/2026-10-06T14-40-00-000Z-showcase/ctrf.json +79 -0
- package/packages/visualizer/fixtures/removed/runs/2026-10-06T14-40-00-000Z-showcase/junit.xml +7 -0
- package/packages/visualizer/fixtures/removed/runs/2026-10-06T14-40-00-000Z-showcase/manifest.json +1504 -0
- package/packages/visualizer/fixtures/removed/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/001.vtt +9 -0
- package/packages/visualizer/fixtures/removed/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/002.vtt +9 -0
- package/packages/visualizer/fixtures/removed/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/003.vtt +9 -0
- package/packages/visualizer/fixtures/removed/runs/2026-10-06T14-40-00-000Z-showcase/media/captions/004.vtt +17 -0
- package/packages/visualizer/fixtures/removed/runs/2026-10-06T14-40-00-000Z-showcase/media/clips/journey-1/01.mp4 +0 -0
- package/packages/visualizer/fixtures/removed/runs/2026-10-06T14-40-00-000Z-showcase/media/clips/journey-1/01.poster.webp +0 -0
- package/packages/visualizer/fixtures/removed/runs/2026-10-06T14-40-00-000Z-showcase/media/clips/journey-1/02.mp4 +0 -0
- package/packages/visualizer/fixtures/removed/runs/2026-10-06T14-40-00-000Z-showcase/media/clips/journey-1/02.poster.webp +0 -0
- package/packages/visualizer/fixtures/removed/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/01.png +0 -0
- package/packages/visualizer/fixtures/removed/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/01.webp +0 -0
- package/packages/visualizer/fixtures/removed/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/02.png +0 -0
- package/packages/visualizer/fixtures/removed/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/02.webp +0 -0
- package/packages/visualizer/fixtures/removed/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/03.png +0 -0
- package/packages/visualizer/fixtures/removed/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/03.webp +0 -0
- package/packages/visualizer/fixtures/removed/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/04.png +0 -0
- package/packages/visualizer/fixtures/removed/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-1/04.webp +0 -0
- package/packages/visualizer/fixtures/removed/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-2/01.png +0 -0
- package/packages/visualizer/fixtures/removed/runs/2026-10-06T14-40-00-000Z-showcase/media/paths/journey-2/01.webp +0 -0
- package/packages/visualizer/fixtures/removed/specs/coffee/ordering/checkout.scxml +82 -0
- package/packages/visualizer/fixtures/removed/specs/coffee/ordering/journeys.yaml +129 -0
- package/packages/visualizer/fixtures/removed/specs/coffee/ordering/known-issues.md +14 -0
- package/packages/visualizer/fixtures/removed/specs/coffee/ordering/machine.yaml +158 -0
- package/packages/visualizer/fixtures/removed/specs/coffee/ordering/open-questions.md +25 -0
- package/packages/visualizer/fixtures/spec-only/runs/.gitkeep +0 -0
- package/packages/visualizer/fixtures/spec-only/specs/coffee/ordering/checkout.scxml +82 -0
- package/packages/visualizer/fixtures/spec-only/specs/coffee/ordering/journeys.yaml +129 -0
- package/packages/visualizer/fixtures/spec-only/specs/coffee/ordering/known-issues.md +14 -0
- package/packages/visualizer/fixtures/spec-only/specs/coffee/ordering/machine.yaml +158 -0
- package/packages/visualizer/fixtures/spec-only/specs/coffee/ordering/open-questions.md +25 -0
- package/packages/visualizer/index.html +13 -0
- package/packages/visualizer/scripts/make-fixtures.ts +1490 -0
- package/packages/visualizer/scripts/publish-data.ts +22 -0
- package/packages/visualizer/src/app/AppShell.tsx +56 -0
- package/packages/visualizer/src/app/atlas-context.tsx +23 -0
- package/packages/visualizer/src/app/gazetteer.module.css +97 -0
- package/packages/visualizer/src/app/gazetteer.tsx +105 -0
- package/packages/visualizer/src/app/report-sources.tsx +122 -0
- package/packages/visualizer/src/app/router.tsx +69 -0
- package/packages/visualizer/src/app/search-query.tsx +14 -0
- package/packages/visualizer/src/components/DataIssues.module.css +136 -0
- package/packages/visualizer/src/components/DataIssues.tsx +70 -0
- package/packages/visualizer/src/components/Header.module.css +189 -0
- package/packages/visualizer/src/components/Header.tsx +127 -0
- package/packages/visualizer/src/components/Legend.module.css +84 -0
- package/packages/visualizer/src/components/Legend.tsx +50 -0
- package/packages/visualizer/src/components/Screen.module.css +312 -0
- package/packages/visualizer/src/components/Screen.tsx +141 -0
- package/packages/visualizer/src/components/Status.module.css +116 -0
- package/packages/visualizer/src/components/Status.tsx +60 -0
- package/packages/visualizer/src/components/icons.tsx +156 -0
- package/packages/visualizer/src/components/labels.tsx +22 -0
- package/packages/visualizer/src/data/adapters/adapter.ts +112 -0
- package/packages/visualizer/src/data/adapters/context.tsx +11 -0
- package/packages/visualizer/src/data/adapters/index.ts +29 -0
- package/packages/visualizer/src/data/adapters/report-backend.test.ts +96 -0
- package/packages/visualizer/src/data/adapters/report-backend.ts +42 -0
- package/packages/visualizer/src/data/fixtures.test.ts +407 -0
- package/packages/visualizer/src/data/model/build.ts +418 -0
- package/packages/visualizer/src/data/model/event-kind.test.ts +59 -0
- package/packages/visualizer/src/data/model/event-kind.ts +54 -0
- package/packages/visualizer/src/data/model/index.ts +3 -0
- package/packages/visualizer/src/data/model/journeys.test.ts +159 -0
- package/packages/visualizer/src/data/model/journeys.ts +29 -0
- package/packages/visualizer/src/data/model/simulation.test.ts +115 -0
- package/packages/visualizer/src/data/model/simulation.ts +187 -0
- package/packages/visualizer/src/data/model/status.test.ts +34 -0
- package/packages/visualizer/src/data/model/status.ts +43 -0
- package/packages/visualizer/src/data/model/types.ts +280 -0
- package/packages/visualizer/src/data/parse/issues.test.ts +82 -0
- package/packages/visualizer/src/data/parse/issues.ts +114 -0
- package/packages/visualizer/src/data/parse/manifest.ts +173 -0
- package/packages/visualizer/src/data/parse/markdown.test.ts +119 -0
- package/packages/visualizer/src/data/parse/markdown.ts +133 -0
- package/packages/visualizer/src/data/parse/spec.test.ts +106 -0
- package/packages/visualizer/src/data/parse/spec.ts +557 -0
- package/packages/visualizer/src/data/parse/structured.ts +44 -0
- package/packages/visualizer/src/data/queries.ts +369 -0
- package/packages/visualizer/src/data/testing/load-fixture.ts +63 -0
- package/packages/visualizer/src/evidence/api.ts +41 -0
- package/packages/visualizer/src/evidence/check-detail.tsx +257 -0
- package/packages/visualizer/src/evidence/decision-table.tsx +68 -0
- package/packages/visualizer/src/evidence/document-page.tsx +40 -0
- package/packages/visualizer/src/evidence/evidence-context.tsx +159 -0
- package/packages/visualizer/src/evidence/evidence.module.css +877 -0
- package/packages/visualizer/src/evidence/feature-page.tsx +88 -0
- package/packages/visualizer/src/evidence/format.ts +56 -0
- package/packages/visualizer/src/evidence/gherkin.ts +98 -0
- package/packages/visualizer/src/evidence/marks.tsx +118 -0
- package/packages/visualizer/src/evidence/model-page.tsx +60 -0
- package/packages/visualizer/src/evidence/model-trace.tsx +57 -0
- package/packages/visualizer/src/evidence/overview.tsx +134 -0
- package/packages/visualizer/src/evidence/quint.ts +45 -0
- package/packages/visualizer/src/evidence/recording-page.tsx +69 -0
- package/packages/visualizer/src/evidence/recording.module.css +464 -0
- package/packages/visualizer/src/evidence/recording.tsx +197 -0
- package/packages/visualizer/src/evidence/snapshot.tsx +108 -0
- package/packages/visualizer/src/evidence/sources-panel.tsx +134 -0
- package/packages/visualizer/src/evidence/sources.module.css +168 -0
- package/packages/visualizer/src/evidence/spec-header.tsx +64 -0
- package/packages/visualizer/src/evidence/spec-index.tsx +82 -0
- package/packages/visualizer/src/evidence/spec-page.tsx +92 -0
- package/packages/visualizer/src/evidence/spec-snapshot.tsx +26 -0
- package/packages/visualizer/src/layout/graph.test.ts +113 -0
- package/packages/visualizer/src/layout/graph.ts +324 -0
- package/packages/visualizer/src/layout/layout.ts +237 -0
- package/packages/visualizer/src/layout/use-chart-layout.ts +18 -0
- package/packages/visualizer/src/main.tsx +31 -0
- package/packages/visualizer/src/map/ChartMap.tsx +252 -0
- package/packages/visualizer/src/map/EventEdge.tsx +138 -0
- package/packages/visualizer/src/map/map.module.css +546 -0
- package/packages/visualizer/src/map/nodes.tsx +145 -0
- package/packages/visualizer/src/map/path.ts +24 -0
- package/packages/visualizer/src/reachable.test.ts +54 -0
- package/packages/visualizer/src/spec-kind.test.ts +27 -0
- package/packages/visualizer/src/spec-kind.ts +56 -0
- package/packages/visualizer/src/state/ui-store.ts +44 -0
- package/packages/visualizer/src/styles/global.css +105 -0
- package/packages/visualizer/src/styles/tokens.css +80 -0
- package/packages/visualizer/src/views/ChartPage.tsx +52 -0
- package/packages/visualizer/src/views/ProductPage.tsx +35 -0
- package/packages/visualizer/src/views/SidePanel.tsx +339 -0
- package/packages/visualizer/src/views/views.module.css +546 -0
- package/packages/visualizer/vite/atlas-dev-plugin.ts +169 -0
- package/packages/visualizer/vite/atlas-files.test.ts +119 -0
- package/packages/visualizer/vite/atlas-files.ts +199 -0
- package/packages/visualizer/vite/publish.test.ts +52 -0
- package/packages/visualizer/vite/publish.ts +99 -0
- package/packages/visualizer/vite/reports-dev-plugin.ts +82 -0
- package/packages/visualizer/vite.config.ts +13 -0
- package/packages/vitest/README.md +58 -0
- package/packages/webdriver/README.md +77 -0
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
BusinessEvents,
|
|
3
|
+
BusinessContract,
|
|
4
|
+
CheckResult,
|
|
5
|
+
Confidence,
|
|
6
|
+
DataIssue,
|
|
7
|
+
DesignLink,
|
|
8
|
+
EventKind,
|
|
9
|
+
LayoutFile,
|
|
10
|
+
RunInfo,
|
|
11
|
+
RunStatus,
|
|
12
|
+
RunSummary,
|
|
13
|
+
StateType,
|
|
14
|
+
TimelineEntry
|
|
15
|
+
} from '@crvouga/atlas/schema';
|
|
16
|
+
import type { ChartSimulation } from './simulation';
|
|
17
|
+
|
|
18
|
+
export const ITEM_STATUSES = ['passed', 'flaky', 'failed', 'not-reached', 'not-yet-run', 'spec-only'] as const;
|
|
19
|
+
export type ItemStatus = (typeof ITEM_STATUSES)[number];
|
|
20
|
+
|
|
21
|
+
export type StatusCounts = Record<ItemStatus, number> & { total: number };
|
|
22
|
+
|
|
23
|
+
/** Where an item stands against a second spec version laid over this one. Always `same` today. */
|
|
24
|
+
export type SpecChange = 'same' | 'added' | 'removed' | 'changed';
|
|
25
|
+
|
|
26
|
+
export type MediaState = 'available' | 'missing' | 'processing';
|
|
27
|
+
|
|
28
|
+
export type ImageMedia = { state: MediaState; thumb: string | null; full: string | null };
|
|
29
|
+
|
|
30
|
+
export type VideoMedia = {
|
|
31
|
+
state: MediaState;
|
|
32
|
+
src: string | null;
|
|
33
|
+
poster: string | null;
|
|
34
|
+
durationMs: number | null;
|
|
35
|
+
captions: string | null;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export type StateKind = 'screen' | 'final' | 'group' | 'parallel' | 'region' | 'chart-link';
|
|
39
|
+
|
|
40
|
+
export type QuestionView = {
|
|
41
|
+
id: string;
|
|
42
|
+
number: number;
|
|
43
|
+
section: string;
|
|
44
|
+
title: string;
|
|
45
|
+
body: string;
|
|
46
|
+
specToday: string | null;
|
|
47
|
+
file: string;
|
|
48
|
+
states: string[];
|
|
49
|
+
transitions: string[];
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export type NoteView = { title: string; body: string; file: string };
|
|
53
|
+
|
|
54
|
+
export type PathRef = { id: string; name: string };
|
|
55
|
+
|
|
56
|
+
export type StateResult = {
|
|
57
|
+
runId: string;
|
|
58
|
+
status: RunStatus;
|
|
59
|
+
screenshot: ImageMedia;
|
|
60
|
+
screenshotsByPath: { path: PathRef; image: ImageMedia }[];
|
|
61
|
+
checks: CheckResult[];
|
|
62
|
+
/** Expected, observed and missing business events, with the CloudEvents seen when the run had a source. */
|
|
63
|
+
businessEvents: BusinessEvents | null;
|
|
64
|
+
reachedBy: PathRef[];
|
|
65
|
+
notes: string[];
|
|
66
|
+
looksLike: string[];
|
|
67
|
+
recognizer: { matched: boolean; signals: { signal: string; expected: string; visible: boolean }[] } | null;
|
|
68
|
+
/** Seeds that start paths with this screen showing. */
|
|
69
|
+
seeds: string[];
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
export type StateView = {
|
|
73
|
+
key: string;
|
|
74
|
+
name: string;
|
|
75
|
+
chartId: string;
|
|
76
|
+
contextId: string;
|
|
77
|
+
file: string;
|
|
78
|
+
parent: string | null;
|
|
79
|
+
children: string[];
|
|
80
|
+
depth: number;
|
|
81
|
+
type: StateType;
|
|
82
|
+
kind: StateKind;
|
|
83
|
+
initial: string | null;
|
|
84
|
+
description: string;
|
|
85
|
+
snapshot: string | null;
|
|
86
|
+
confidence: Confidence | null;
|
|
87
|
+
sources: string[];
|
|
88
|
+
owner: string | null;
|
|
89
|
+
tier: string | null;
|
|
90
|
+
quint: string | null;
|
|
91
|
+
gherkin: string[];
|
|
92
|
+
contracts: BusinessContract[];
|
|
93
|
+
design: DesignLink | null;
|
|
94
|
+
hints: string[];
|
|
95
|
+
specChecks: string[];
|
|
96
|
+
specBusinessEvents: string[];
|
|
97
|
+
deadEnd: string | null;
|
|
98
|
+
childChartId: string | null;
|
|
99
|
+
incoming: string[];
|
|
100
|
+
outgoing: string[];
|
|
101
|
+
questionIds: string[];
|
|
102
|
+
knownIssues: NoteView[];
|
|
103
|
+
status: ItemStatus;
|
|
104
|
+
result: StateResult | null;
|
|
105
|
+
change: SpecChange;
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
export type EventKindSource = 'spec' | 'structure' | 'run' | 'guess';
|
|
109
|
+
|
|
110
|
+
export type TransitionResult = {
|
|
111
|
+
runId: string;
|
|
112
|
+
status: RunStatus;
|
|
113
|
+
how: string | null;
|
|
114
|
+
reason: string | null;
|
|
115
|
+
clip: VideoMedia;
|
|
116
|
+
timeline: TimelineEntry[];
|
|
117
|
+
reachedBy: PathRef[];
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
export type TransitionView = {
|
|
121
|
+
id: string;
|
|
122
|
+
chartId: string;
|
|
123
|
+
source: string;
|
|
124
|
+
target: string | null;
|
|
125
|
+
event: string;
|
|
126
|
+
kind: EventKind;
|
|
127
|
+
kindSource: EventKindSource;
|
|
128
|
+
timeSpan: string | null;
|
|
129
|
+
handOff: boolean;
|
|
130
|
+
carriedBy: string | null;
|
|
131
|
+
questionIds: string[];
|
|
132
|
+
knownIssues: NoteView[];
|
|
133
|
+
status: ItemStatus;
|
|
134
|
+
result: TransitionResult | null;
|
|
135
|
+
change: SpecChange;
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
export type JourneyStep = {
|
|
139
|
+
index: number;
|
|
140
|
+
event: string;
|
|
141
|
+
transitionIds: string[];
|
|
142
|
+
handOff: boolean;
|
|
143
|
+
from: string[];
|
|
144
|
+
to: string[];
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
export type JourneyRunPath = {
|
|
148
|
+
id: string;
|
|
149
|
+
name: string;
|
|
150
|
+
/** The seed the stretch started from; null when it started at the beginning. */
|
|
151
|
+
seed: string | null;
|
|
152
|
+
status: RunStatus;
|
|
153
|
+
progress: 'queued' | 'running' | 'complete';
|
|
154
|
+
stoppedAt: string | null;
|
|
155
|
+
error: string | null;
|
|
156
|
+
steps: { transition: string | null; event: string; status: RunStatus | null; reason: string | null }[];
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
export type JourneyView = {
|
|
160
|
+
id: string;
|
|
161
|
+
name: string;
|
|
162
|
+
description: string;
|
|
163
|
+
file: string;
|
|
164
|
+
chartIds: string[];
|
|
165
|
+
owner: string | null;
|
|
166
|
+
tour: boolean;
|
|
167
|
+
events: string[];
|
|
168
|
+
endsIn: string[];
|
|
169
|
+
steps: JourneyStep[];
|
|
170
|
+
replay: { ok: boolean; failedAt: string | null; missingEnds: string[] };
|
|
171
|
+
status: ItemStatus;
|
|
172
|
+
runPaths: JourneyRunPath[];
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
export type ChartView = {
|
|
176
|
+
id: string;
|
|
177
|
+
name: string;
|
|
178
|
+
file: string;
|
|
179
|
+
dir: string;
|
|
180
|
+
contextId: string;
|
|
181
|
+
description: string;
|
|
182
|
+
contracts: BusinessContract[];
|
|
183
|
+
parent: { chartId: string; state: string } | null;
|
|
184
|
+
childChartIds: string[];
|
|
185
|
+
initial: string | null;
|
|
186
|
+
rootStates: string[];
|
|
187
|
+
states: string[];
|
|
188
|
+
transitions: string[];
|
|
189
|
+
journeyIds: string[];
|
|
190
|
+
layout: LayoutFile | null;
|
|
191
|
+
screens: StatusCounts;
|
|
192
|
+
events: StatusCounts;
|
|
193
|
+
/** Changes only when states, nesting or transitions change, never when results do. */
|
|
194
|
+
structureKey: string;
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
export type ContextView = {
|
|
198
|
+
id: string;
|
|
199
|
+
name: string;
|
|
200
|
+
description: string;
|
|
201
|
+
owner: string | null;
|
|
202
|
+
chartIds: string[];
|
|
203
|
+
topChartIds: string[];
|
|
204
|
+
screens: StatusCounts;
|
|
205
|
+
events: StatusCounts;
|
|
206
|
+
journeys: StatusCounts;
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
export type HandOffView = {
|
|
210
|
+
from: string;
|
|
211
|
+
to: string;
|
|
212
|
+
event: string;
|
|
213
|
+
description: string;
|
|
214
|
+
fromContext: string;
|
|
215
|
+
toContext: string;
|
|
216
|
+
fromState: string | null;
|
|
217
|
+
toState: string | null;
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
export type RunReport = { label: string; url: string };
|
|
221
|
+
|
|
222
|
+
export type RunView = {
|
|
223
|
+
id: string;
|
|
224
|
+
info: RunInfo;
|
|
225
|
+
progress: 'running' | 'complete';
|
|
226
|
+
privacyPassed: boolean | null;
|
|
227
|
+
outOfDate: boolean;
|
|
228
|
+
/** Links to the other formats the run wrote: JUnit, CTRF, Mermaid. */
|
|
229
|
+
reports: RunReport[];
|
|
230
|
+
paths: {
|
|
231
|
+
id: string;
|
|
232
|
+
name: string;
|
|
233
|
+
kind: 'journey' | 'generated';
|
|
234
|
+
status: ItemStatus;
|
|
235
|
+
progress: 'queued' | 'running' | 'complete';
|
|
236
|
+
steps: number;
|
|
237
|
+
seed: string | null;
|
|
238
|
+
durationMs: number | null;
|
|
239
|
+
error: string | null;
|
|
240
|
+
stoppedAt: string | null;
|
|
241
|
+
traces: RunReport[];
|
|
242
|
+
}[];
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
export type RemovedState = {
|
|
246
|
+
name: string;
|
|
247
|
+
chartId: string | null;
|
|
248
|
+
status: RunStatus;
|
|
249
|
+
screenshot: ImageMedia;
|
|
250
|
+
description: string;
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
export type RemovedTransition = {
|
|
254
|
+
id: string;
|
|
255
|
+
source: string;
|
|
256
|
+
event: string;
|
|
257
|
+
target: string | null;
|
|
258
|
+
status: RunStatus;
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
export type AtlasView = {
|
|
262
|
+
simulation: (chartId: string) => ChartSimulation;
|
|
263
|
+
title: string;
|
|
264
|
+
description: string;
|
|
265
|
+
ref: string | null;
|
|
266
|
+
mode: 'spec-only' | 'run';
|
|
267
|
+
contexts: ContextView[];
|
|
268
|
+
charts: Map<string, ChartView>;
|
|
269
|
+
states: Map<string, StateView>;
|
|
270
|
+
transitions: Map<string, TransitionView>;
|
|
271
|
+
journeys: JourneyView[];
|
|
272
|
+
questions: Map<string, QuestionView>;
|
|
273
|
+
excluded: string[];
|
|
274
|
+
handOffs: HandOffView[];
|
|
275
|
+
run: RunView | null;
|
|
276
|
+
runs: RunSummary[];
|
|
277
|
+
removed: { states: RemovedState[]; transitions: RemovedTransition[] };
|
|
278
|
+
totals: { screens: StatusCounts; events: StatusCounts; journeys: StatusCounts };
|
|
279
|
+
issues: DataIssue[];
|
|
280
|
+
};
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { describe, expect, it } from 'bun:test';
|
|
2
|
+
import { z } from 'zod/v4';
|
|
3
|
+
|
|
4
|
+
import { createIssueSink, formatPath, parseFields, parseOne, plainZodMessage } from './issues';
|
|
5
|
+
|
|
6
|
+
describe('plainZodMessage', () => {
|
|
7
|
+
it.each<[string, z.ZodType, unknown, string]>([
|
|
8
|
+
['a missing field', z.object({ name: z.string() }), {}, '"name" is missing.'],
|
|
9
|
+
['text where a number belongs', z.object({ count: z.number() }), { count: 'many' }, '"count" should be a number, but is text.'],
|
|
10
|
+
['a number where text belongs', z.object({ name: z.string() }), { name: 3 }, '"name" should be text, but is a number.'],
|
|
11
|
+
['text where a list belongs', z.array(z.string()), 'abc', 'should be a list, but is text.'],
|
|
12
|
+
['a list where fields belong', z.object({ a: z.string() }), ['x'], 'should be a group of fields, but is a list.'],
|
|
13
|
+
['a nested yes-or-no', z.object({ a: z.object({ on: z.boolean() }) }), { a: { on: 'yes' } }, '"a › on" should be yes or no, but is text.'],
|
|
14
|
+
['null where text belongs', z.object({ name: z.string() }), { name: null }, '"name" should be text, but is empty.'],
|
|
15
|
+
['a value outside a list of choices', z.enum(['confirmed', 'assumed']), 'maybe', 'should be one of "confirmed", "assumed", but is "maybe".'],
|
|
16
|
+
['empty text', z.string().min(1), '', 'is empty.'],
|
|
17
|
+
['an unknown shape', z.union([z.string(), z.object({ target: z.string() })]), 5, 'is not in a shape this app understands.']
|
|
18
|
+
])('explains %s', (_label, schema, value, expected) => {
|
|
19
|
+
const result = schema.safeParse(value);
|
|
20
|
+
expect(result.success).toBe(false);
|
|
21
|
+
expect(plainZodMessage(result.error!, value)).toBe(expected);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('falls back to the schema message for other problems', () => {
|
|
25
|
+
const value = 5;
|
|
26
|
+
const result = z.number().max(3).safeParse(value);
|
|
27
|
+
expect(plainZodMessage(result.error!, value)).toMatch(/3/);
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
describe('formatPath', () => {
|
|
32
|
+
it.each<[(string | number)[], string]>([
|
|
33
|
+
[[], ''],
|
|
34
|
+
[['states', 'Drink ready'], 'states › Drink ready'],
|
|
35
|
+
[['paths', 0, 'steps', 2], 'paths › item 1 › steps › item 3']
|
|
36
|
+
])('formats %j as %j', (path, expected) => {
|
|
37
|
+
expect(formatPath(path)).toBe(expected);
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
describe('parseFields', () => {
|
|
42
|
+
const shape = { name: z.string(), count: z.number(), tags: z.array(z.string()) };
|
|
43
|
+
|
|
44
|
+
it.each<[string, unknown, Record<string, unknown>, { path: string; message: string }[]]>([
|
|
45
|
+
['all valid', { name: 'x', count: 2, tags: ['a'] }, { name: 'x', count: 2, tags: ['a'] }, []],
|
|
46
|
+
['one invalid field', { name: 'x', count: 'many', tags: ['a'] }, { name: 'x', tags: ['a'] }, [{ path: 'meta › count', message: 'should be a number, but is text.' }]],
|
|
47
|
+
[
|
|
48
|
+
'two invalid fields',
|
|
49
|
+
{ name: 1, count: 2, tags: 'a' },
|
|
50
|
+
{ count: 2 },
|
|
51
|
+
[
|
|
52
|
+
{ path: 'meta › name', message: 'should be text, but is a number.' },
|
|
53
|
+
{ path: 'meta › tags', message: 'should be a list, but is text.' }
|
|
54
|
+
]
|
|
55
|
+
],
|
|
56
|
+
['unknown fields', { name: 'x', colour: 'red', future: { a: 1 } }, { name: 'x' }, []],
|
|
57
|
+
['missing fields', { count: 1 }, { count: 1 }, []],
|
|
58
|
+
['nothing at all', undefined, {}, []],
|
|
59
|
+
['null', null, {}, []],
|
|
60
|
+
['a list instead of fields', ['x'], {}, [{ path: 'meta', message: 'should be a group of fields, but is a list.' }]]
|
|
61
|
+
])('keeps valid fields and reports invalid ones: %s', (_label, raw, expected, issues) => {
|
|
62
|
+
const sink = createIssueSink();
|
|
63
|
+
expect(parseFields(shape, raw, sink, 'machine.yaml', ['meta'])).toEqual(expected);
|
|
64
|
+
expect(sink.issues.map(({ path, message }) => ({ path, message }))).toEqual(issues);
|
|
65
|
+
expect(sink.issues.every((i) => i.file === 'machine.yaml' && i.severity === 'warning')).toBe(true);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('reports at the severity asked for', () => {
|
|
69
|
+
const sink = createIssueSink();
|
|
70
|
+
parseFields(shape, { count: 'x' }, sink, 'f', [], 'error');
|
|
71
|
+
expect(sink.issues[0]).toMatchObject({ severity: 'error', path: 'count' });
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
describe('parseOne', () => {
|
|
76
|
+
it('returns the value or null and an issue, never throwing', () => {
|
|
77
|
+
const sink = createIssueSink();
|
|
78
|
+
expect(parseOne(z.object({ a: z.string() }), { a: 'x' }, sink, 'f', [])).toEqual({ a: 'x' });
|
|
79
|
+
expect(parseOne(z.object({ a: z.string() }), { a: 1 }, sink, 'f', ['root'])).toBeNull();
|
|
80
|
+
expect(sink.issues).toEqual([{ severity: 'warning', file: 'f', path: 'root', message: '"a" should be text, but is a number.' }]);
|
|
81
|
+
});
|
|
82
|
+
});
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import type { DataIssue, IssueSeverity } from '@crvouga/atlas/schema';
|
|
2
|
+
import type { z } from 'zod/v4';
|
|
3
|
+
|
|
4
|
+
export type IssueSink = {
|
|
5
|
+
add: (severity: IssueSeverity, file: string, path: readonly (string | number)[], message: string) => void;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export function createIssueSink(into: DataIssue[] = []) {
|
|
9
|
+
const sink: IssueSink & { issues: DataIssue[] } = {
|
|
10
|
+
issues: into,
|
|
11
|
+
add: (severity, file, path, message) => {
|
|
12
|
+
into.push({ severity, file, path: formatPath(path), message });
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
return sink;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function formatPath(path: readonly (string | number)[]) {
|
|
19
|
+
return path.map((part) => (typeof part === 'number' ? `item ${part + 1}` : part)).join(' › ');
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function describeValue(value: unknown) {
|
|
23
|
+
if (value === null) return 'empty';
|
|
24
|
+
if (value === undefined) return 'missing';
|
|
25
|
+
if (Array.isArray(value)) return 'a list';
|
|
26
|
+
if (typeof value === 'object') return 'a group of fields';
|
|
27
|
+
if (typeof value === 'string') return value.trim() ? 'text' : 'empty text';
|
|
28
|
+
return `a ${typeof value}`;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const EXPECTED_WORDS: Record<string, string> = {
|
|
32
|
+
string: 'text',
|
|
33
|
+
number: 'a number',
|
|
34
|
+
int: 'a whole number',
|
|
35
|
+
boolean: 'yes or no',
|
|
36
|
+
array: 'a list',
|
|
37
|
+
object: 'a group of fields',
|
|
38
|
+
record: 'a group of fields',
|
|
39
|
+
null: 'empty'
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
/** One sentence a non-engineer can read, for the first problem Zod found. */
|
|
43
|
+
export function plainZodMessage(error: z.ZodError, value: unknown) {
|
|
44
|
+
const first = error.issues[0];
|
|
45
|
+
if (!first) return 'This value could not be read.';
|
|
46
|
+
const where = first.path.length ? `"${first.path.join(' › ')}" ` : '';
|
|
47
|
+
const actual = first.path.reduce<unknown>(
|
|
48
|
+
(v, key) => (v && typeof v === 'object' ? (v as Record<PropertyKey, unknown>)[key] : undefined),
|
|
49
|
+
value
|
|
50
|
+
);
|
|
51
|
+
switch (first.code) {
|
|
52
|
+
case 'invalid_type':
|
|
53
|
+
return actual === undefined
|
|
54
|
+
? `${where || 'A required value '}is missing.`
|
|
55
|
+
: `${where}should be ${EXPECTED_WORDS[first.expected] ?? first.expected}, but is ${describeValue(actual)}.`;
|
|
56
|
+
case 'invalid_value':
|
|
57
|
+
return `${where}should be one of ${first.values.map((v) => `"${String(v)}"`).join(', ')}, but is "${String(actual)}".`;
|
|
58
|
+
case 'too_small':
|
|
59
|
+
return `${where}is empty.`;
|
|
60
|
+
case 'invalid_union':
|
|
61
|
+
return `${where}is not in a shape this app understands.`;
|
|
62
|
+
default:
|
|
63
|
+
return `${where}${first.message}`;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function isRecord(value: unknown): value is Record<string, unknown> {
|
|
68
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Validate an object one field at a time: keep the fields that parse, report the ones that don't,
|
|
73
|
+
* and ignore fields the shape doesn't know, so newer data never breaks an older build.
|
|
74
|
+
*/
|
|
75
|
+
export function parseFields<Shape extends Record<string, z.ZodType>>(
|
|
76
|
+
shape: Shape,
|
|
77
|
+
raw: unknown,
|
|
78
|
+
sink: IssueSink,
|
|
79
|
+
file: string,
|
|
80
|
+
path: readonly (string | number)[],
|
|
81
|
+
severity: 'warning' | 'error' = 'warning'
|
|
82
|
+
) {
|
|
83
|
+
const out: Partial<{ [K in keyof Shape]: z.infer<Shape[K]> }> = {};
|
|
84
|
+
if (raw === undefined || raw === null) return out;
|
|
85
|
+
if (!isRecord(raw)) {
|
|
86
|
+
sink.add(severity, file, path, `should be a group of fields, but is ${describeValue(raw)}.`);
|
|
87
|
+
return out;
|
|
88
|
+
}
|
|
89
|
+
for (const key of Object.keys(shape) as (keyof Shape & string)[]) {
|
|
90
|
+
if (!(key in raw) || raw[key] === undefined) continue;
|
|
91
|
+
const result = shape[key]!.safeParse(raw[key]);
|
|
92
|
+
if (result.success) {
|
|
93
|
+
out[key] = result.data as z.infer<Shape[typeof key]>;
|
|
94
|
+
} else {
|
|
95
|
+
sink.add(severity, file, [...path, key], plainZodMessage(result.error, raw[key]).replace(/^"" /, ''));
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return out;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** Parse a value with a schema, reporting and returning null instead of throwing. */
|
|
102
|
+
export function parseOne<T>(
|
|
103
|
+
schema: z.ZodType<T>,
|
|
104
|
+
raw: unknown,
|
|
105
|
+
sink: IssueSink,
|
|
106
|
+
file: string,
|
|
107
|
+
path: readonly (string | number)[],
|
|
108
|
+
severity: IssueSeverity = 'warning'
|
|
109
|
+
): T | null {
|
|
110
|
+
const result = schema.safeParse(raw);
|
|
111
|
+
if (result.success) return result.data;
|
|
112
|
+
sink.add(severity, file, path, plainZodMessage(result.error, raw));
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import {
|
|
2
|
+
LATEST_MANIFEST_SCHEMA_VERSION,
|
|
3
|
+
JourneyRecordSchema,
|
|
4
|
+
ManifestChartSchema,
|
|
5
|
+
MapEdgeSchema,
|
|
6
|
+
PathRecordSchema,
|
|
7
|
+
PathStepSchema,
|
|
8
|
+
PrivacySchema,
|
|
9
|
+
ReportsSchema,
|
|
10
|
+
RunInfoSchema,
|
|
11
|
+
RunSummarySchema,
|
|
12
|
+
RunsIndexSchema,
|
|
13
|
+
SeedRecordSchema,
|
|
14
|
+
StateRecordSchema,
|
|
15
|
+
TimelineEntrySchema,
|
|
16
|
+
TransitionRecordSchema,
|
|
17
|
+
transitionId,
|
|
18
|
+
type EventKind,
|
|
19
|
+
type JourneyRecord,
|
|
20
|
+
type PathRecord,
|
|
21
|
+
type SeedRecord,
|
|
22
|
+
type RunInfo,
|
|
23
|
+
type RunSummary,
|
|
24
|
+
type StateRecord,
|
|
25
|
+
type TimelineEntry,
|
|
26
|
+
type TransitionRecord
|
|
27
|
+
} from '@crvouga/atlas/schema';
|
|
28
|
+
import type { z } from 'zod/v4';
|
|
29
|
+
|
|
30
|
+
import { createIssueSink, isRecord, parseFields, parseOne, type IssueSink } from './issues';
|
|
31
|
+
|
|
32
|
+
export type ParsedTransitionRecord = Omit<TransitionRecord, 'timeline'> & { id: string; timeline: TimelineEntry[] };
|
|
33
|
+
|
|
34
|
+
export type ParsedPath = Omit<PathRecord, 'steps'> & { steps: z.infer<typeof PathStepSchema>[] };
|
|
35
|
+
|
|
36
|
+
export type ParsedRun = {
|
|
37
|
+
id: string;
|
|
38
|
+
file: string;
|
|
39
|
+
schemaVersion: number;
|
|
40
|
+
info: RunInfo;
|
|
41
|
+
charts: z.infer<typeof ManifestChartSchema>[];
|
|
42
|
+
states: Map<string, StateRecord>;
|
|
43
|
+
transitions: Map<string, ParsedTransitionRecord>;
|
|
44
|
+
paths: ParsedPath[];
|
|
45
|
+
/** Journeys as the run composed them from their seeded stretches. */
|
|
46
|
+
journeys: JourneyRecord[];
|
|
47
|
+
seeds: SeedRecord[];
|
|
48
|
+
mapKinds: Map<string, EventKind>;
|
|
49
|
+
privacy: z.infer<typeof PrivacySchema> | null;
|
|
50
|
+
/** Other formats the run wrote beside its manifest: JUnit XML, CTRF JSON, a Mermaid diagram. */
|
|
51
|
+
reports: z.infer<typeof ReportsSchema> | null;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
function startedAtFromId(id: string) {
|
|
55
|
+
const m = id.match(/^(\d{4}-\d{2}-\d{2})T(\d{2})-(\d{2})-(\d{2})-(\d{3})Z/);
|
|
56
|
+
return m ? `${m[1]}T${m[2]}:${m[3]}:${m[4]}.${m[5]}Z` : new Date(0).toISOString();
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function parseTimeline(raw: unknown, sink: IssueSink, file: string, path: (string | number)[]) {
|
|
60
|
+
if (raw === undefined) return [];
|
|
61
|
+
if (!Array.isArray(raw)) {
|
|
62
|
+
sink.add('warning', file, path, 'should be a list of steps.');
|
|
63
|
+
return [];
|
|
64
|
+
}
|
|
65
|
+
const entries: TimelineEntry[] = [];
|
|
66
|
+
raw.forEach((entry, i) => {
|
|
67
|
+
if (isRecord(entry) && typeof entry.kind === 'string' && !TimelineEntrySchema.shape.kind.options.includes(entry.kind as TimelineEntry['kind'])) {
|
|
68
|
+
sink.add('info', file, [...path, i, 'kind'], `"${entry.kind}" is a step type this app doesn't know yet; it is shown as a note.`);
|
|
69
|
+
entry = { ...entry, kind: 'note' };
|
|
70
|
+
}
|
|
71
|
+
const parsed = parseOne(TimelineEntrySchema, entry, sink, file, [...path, i]);
|
|
72
|
+
if (parsed) entries.push(parsed);
|
|
73
|
+
});
|
|
74
|
+
return entries.sort((a, b) => a.atMs - b.atMs);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* One run's manifest, record by record. A broken state, transition or path is reported and
|
|
79
|
+
* dropped; the rest of the run still shows. Unknown fields are ignored.
|
|
80
|
+
*/
|
|
81
|
+
export function parseManifest(runId: string, file: string, raw: unknown, sink: IssueSink = createIssueSink()): ParsedRun | null {
|
|
82
|
+
if (!isRecord(raw)) {
|
|
83
|
+
sink.add('error', file, [], 'This run file is empty or not in a shape this app understands, so the run is skipped.');
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
let schemaVersion = typeof raw.schemaVersion === 'number' ? raw.schemaVersion : 0;
|
|
87
|
+
if (!schemaVersion) {
|
|
88
|
+
sink.add('warning', file, ['schemaVersion'], `The run doesn't say which format it uses; it is read as format ${LATEST_MANIFEST_SCHEMA_VERSION}.`);
|
|
89
|
+
schemaVersion = LATEST_MANIFEST_SCHEMA_VERSION;
|
|
90
|
+
} else if (schemaVersion > LATEST_MANIFEST_SCHEMA_VERSION) {
|
|
91
|
+
sink.add('info', file, ['schemaVersion'], `This run uses a newer format (${schemaVersion}) than this app knows (${LATEST_MANIFEST_SCHEMA_VERSION}). The parts it recognizes are shown.`);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
let info = RunInfoSchema.safeParse(raw.run).success ? RunInfoSchema.parse(raw.run) : null;
|
|
95
|
+
if (!info) {
|
|
96
|
+
if (raw.run === undefined || raw.run === null) {
|
|
97
|
+
sink.add('warning', file, ['run'], "The run has no details section, so its name, date and type are taken from its folder name.");
|
|
98
|
+
}
|
|
99
|
+
const partial = parseFields(RunInfoSchema.shape, raw.run, sink, file, ['run']);
|
|
100
|
+
info = {
|
|
101
|
+
id: partial.id ?? runId,
|
|
102
|
+
startedAt: partial.startedAt ?? startedAtFromId(runId),
|
|
103
|
+
durationMs: partial.durationMs ?? 0,
|
|
104
|
+
mode: partial.mode ?? (runId.endsWith('showcase') ? 'showcase' : 'fast'),
|
|
105
|
+
specVersion: partial.specVersion ?? 'unknown',
|
|
106
|
+
...partial
|
|
107
|
+
} as RunInfo;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const charts = (Array.isArray(raw.charts) ? raw.charts : []).flatMap((c, i) => {
|
|
111
|
+
const parsed = parseOne(ManifestChartSchema, c, sink, file, ['charts', i]);
|
|
112
|
+
return parsed ? [parsed] : [];
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
const states = new Map<string, StateRecord>();
|
|
116
|
+
if (raw.states !== undefined && !isRecord(raw.states)) sink.add('warning', file, ['states'], 'should list screens by name.');
|
|
117
|
+
for (const [name, record] of Object.entries(isRecord(raw.states) ? raw.states : {})) {
|
|
118
|
+
const parsed = parseOne(StateRecordSchema, isRecord(record) ? { name, ...record } : record, sink, file, ['states', name]);
|
|
119
|
+
if (parsed) states.set(name, parsed);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const transitions = new Map<string, ParsedTransitionRecord>();
|
|
123
|
+
if (raw.transitions !== undefined && !isRecord(raw.transitions)) sink.add('warning', file, ['transitions'], 'should list events by name.');
|
|
124
|
+
for (const [key, record] of Object.entries(isRecord(raw.transitions) ? raw.transitions : {})) {
|
|
125
|
+
const { timeline: rawTimeline, ...rest } = isRecord(record) ? record : { value: record };
|
|
126
|
+
const parsed = parseOne(TransitionRecordSchema, isRecord(record) ? rest : record, sink, file, ['transitions', key]);
|
|
127
|
+
if (!parsed) continue;
|
|
128
|
+
const id = parsed.id ?? (key.includes(' :: ') ? key : transitionId(parsed.source, parsed.event));
|
|
129
|
+
transitions.set(id, { ...parsed, id, timeline: parseTimeline(rawTimeline, sink, file, ['transitions', key, 'timeline']) });
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const paths: ParsedPath[] = (Array.isArray(raw.paths) ? raw.paths : []).flatMap((p, i) => {
|
|
133
|
+
const parsed = parseOne(PathRecordSchema, p, sink, file, ['paths', i]);
|
|
134
|
+
if (!parsed) return [];
|
|
135
|
+
const steps = parsed.steps.flatMap((s, j) => {
|
|
136
|
+
const step = parseOne(PathStepSchema, s, sink, file, ['paths', i, 'steps', j]);
|
|
137
|
+
return step ? [step] : [];
|
|
138
|
+
});
|
|
139
|
+
return [{ ...parsed, steps }];
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
const mapKinds = new Map<string, EventKind>();
|
|
143
|
+
const edges = isRecord(raw.map) && Array.isArray(raw.map.edges) ? raw.map.edges : [];
|
|
144
|
+
for (const edge of edges) {
|
|
145
|
+
const parsed = MapEdgeSchema.safeParse(edge);
|
|
146
|
+
if (parsed.success) mapKinds.set(parsed.data.id, parsed.data.kind);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const privacy = raw.privacy === undefined ? null : parseOne(PrivacySchema, raw.privacy, sink, file, ['privacy']);
|
|
150
|
+
const reports = raw.reports === undefined ? null : parseOne(ReportsSchema, raw.reports, sink, file, ['reports']);
|
|
151
|
+
const journeys = (Array.isArray(raw.journeys) ? raw.journeys : []).flatMap((j, i) => {
|
|
152
|
+
const parsed = parseOne(JourneyRecordSchema, j, sink, file, ['journeys', i]);
|
|
153
|
+
return parsed ? [parsed] : [];
|
|
154
|
+
});
|
|
155
|
+
const seeds = (Array.isArray(raw.seeds) ? raw.seeds : []).flatMap((s, i) => {
|
|
156
|
+
const parsed = parseOne(SeedRecordSchema, s, sink, file, ['seeds', i]);
|
|
157
|
+
return parsed ? [parsed] : [];
|
|
158
|
+
});
|
|
159
|
+
return { id: runId, file, schemaVersion, info, charts, states, transitions, paths, journeys, seeds, mapKinds, privacy, reports };
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export type ParsedRunsIndex = { runs: RunSummary[]; excluded: string[] };
|
|
163
|
+
|
|
164
|
+
export function parseRunsIndex(file: string, raw: unknown, sink: IssueSink = createIssueSink()): ParsedRunsIndex {
|
|
165
|
+
const index = parseOne(RunsIndexSchema, raw, sink, file, [], 'error');
|
|
166
|
+
if (!index) return { runs: [], excluded: [] };
|
|
167
|
+
const runs = index.runs.flatMap((r, i) => {
|
|
168
|
+
const parsed = parseOne(RunSummarySchema, r, sink, file, ['runs', i]);
|
|
169
|
+
return parsed ? [parsed] : [];
|
|
170
|
+
});
|
|
171
|
+
runs.sort((a, b) => Date.parse(b.startedAt) - Date.parse(a.startedAt));
|
|
172
|
+
return { runs, excluded: index.excluded ?? [] };
|
|
173
|
+
}
|