@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,121 @@
|
|
|
1
|
+
export const testId = (id) => ({ testId: id });
|
|
2
|
+
export const text = (value, options = {}) => ({ text: value, ...options });
|
|
3
|
+
export const role = (r, name, options = {}) => ({ role: r, ...(name ? { name } : {}), ...options });
|
|
4
|
+
export const label = (value) => ({ label: value });
|
|
5
|
+
export const css = (selector) => ({ css: selector });
|
|
6
|
+
export function describeSignal(signal) {
|
|
7
|
+
if ('testId' in signal)
|
|
8
|
+
return `test ID "${signal.testId}"`;
|
|
9
|
+
if ('role' in signal)
|
|
10
|
+
return `${signal.role}${signal.name ? ` "${String(signal.name)}"` : ''}`;
|
|
11
|
+
if ('label' in signal)
|
|
12
|
+
return `label ${String(signal.label)}`;
|
|
13
|
+
if ('css' in signal)
|
|
14
|
+
return `selector ${signal.css}`;
|
|
15
|
+
return `text ${String(signal.text)}`;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Build `screen()` for a driver from one question: is this signal visible right now? A state is
|
|
19
|
+
* recognised when every `all` is visible, one `any` is visible and no `none` is. Drivers with
|
|
20
|
+
* their own locators (WebDriver's) pass their signal type and how to describe it.
|
|
21
|
+
*/
|
|
22
|
+
export function signatureScreen(isVisible, describe) {
|
|
23
|
+
const visible = (ctx, signal) => isVisible(ctx, signal).catch(() => false);
|
|
24
|
+
const describeSignal = describe;
|
|
25
|
+
return function screen(spec) {
|
|
26
|
+
return {
|
|
27
|
+
async recognize(ctx) {
|
|
28
|
+
const signals = [];
|
|
29
|
+
for (const s of spec.all ?? [])
|
|
30
|
+
signals.push({ signal: describeSignal(s), expected: 'visible', visible: await visible(ctx, s) });
|
|
31
|
+
if (spec.any?.length) {
|
|
32
|
+
let seen = false;
|
|
33
|
+
for (const s of spec.any)
|
|
34
|
+
if (await visible(ctx, s))
|
|
35
|
+
seen = true;
|
|
36
|
+
signals.push({ signal: `one of: ${spec.any.map(describeSignal).join(' | ')}`, expected: 'visible', visible: seen });
|
|
37
|
+
}
|
|
38
|
+
for (const s of spec.none ?? [])
|
|
39
|
+
signals.push({ signal: describeSignal(s), expected: 'hidden', visible: await visible(ctx, s) });
|
|
40
|
+
return { matched: signals.every((x) => (x.expected === 'visible' ? x.visible : !x.visible)), signals };
|
|
41
|
+
},
|
|
42
|
+
...(spec.checks
|
|
43
|
+
? {
|
|
44
|
+
async checks(ctx) {
|
|
45
|
+
const results = [];
|
|
46
|
+
for (const { check, signal } of spec.checks) {
|
|
47
|
+
const seen = await visible(ctx, signal);
|
|
48
|
+
results.push({ check, passed: seen, expected: `${describeSignal(signal)} visible`, actual: seen ? 'visible' : 'not on screen' });
|
|
49
|
+
}
|
|
50
|
+
return results;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
: {}),
|
|
54
|
+
...(spec.transient ? { transient: true } : {}),
|
|
55
|
+
...(spec.sameAs ? { sameAs: spec.sameAs } : {}),
|
|
56
|
+
...(spec.lookIn ? { lookIn: spec.lookIn } : {}),
|
|
57
|
+
...(spec.settle ? { settle: spec.settle } : {})
|
|
58
|
+
};
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
/** A state recognised from signals on any driver that provides a `SignalContext`. */
|
|
62
|
+
export const screen = signatureScreen((ctx, signal) => ctx.isVisible(signal), describeSignal);
|
|
63
|
+
const wire = (value, exact = false) => typeof value === 'string' ? { text: value, exact } : { regex: value.source, flags: value.flags };
|
|
64
|
+
/** A signal as JSON, for a probe that runs inside the page. */
|
|
65
|
+
export function toWire(signal) {
|
|
66
|
+
if ('testId' in signal)
|
|
67
|
+
return { kind: 'testId', id: signal.testId };
|
|
68
|
+
if ('role' in signal)
|
|
69
|
+
return { kind: 'role', role: signal.role, name: signal.name ? wire(signal.name, signal.exact) : null };
|
|
70
|
+
if ('label' in signal)
|
|
71
|
+
return { kind: 'label', match: wire(signal.label) };
|
|
72
|
+
if ('css' in signal)
|
|
73
|
+
return { kind: 'css', selector: signal.css };
|
|
74
|
+
return { kind: 'text', match: wire(signal.text, signal.exact) };
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* A function expression, as source, that finds a wire signal in a DOM: `(signal, scroll,
|
|
78
|
+
* withElement) => DomProbeResult`, plus the element itself when `withElement` is set (WebDriver
|
|
79
|
+
* returns it as an element reference). It is plain JavaScript so any driver can run it in a page (Bun WebView's
|
|
80
|
+
* `evaluate`, WebDriver's Execute Script). Roles follow the HTML-AAM implicit role mapping for the
|
|
81
|
+
* common elements; names follow aria-label, the associated label, then text content.
|
|
82
|
+
*/
|
|
83
|
+
export const DOM_PROBE = `(signal, scroll, withElement) => {
|
|
84
|
+
const norm = (s) => String(s || '').replace(/\\s+/g, ' ').trim();
|
|
85
|
+
const matches = (value, m) => (m.regex !== undefined ? new RegExp(m.regex, m.flags).test(value) : m.exact ? value === m.text : value.toLowerCase().includes(m.text.toLowerCase()));
|
|
86
|
+
const shown = (el) => {
|
|
87
|
+
const r = el.getBoundingClientRect();
|
|
88
|
+
const st = getComputedStyle(el);
|
|
89
|
+
return r.width > 0 && r.height > 0 && st.visibility !== 'hidden' && st.display !== 'none' && Number(st.opacity) !== 0;
|
|
90
|
+
};
|
|
91
|
+
const ROLES = {
|
|
92
|
+
button: 'button,[role=button],input[type=button],input[type=submit],input[type=reset]',
|
|
93
|
+
heading: 'h1,h2,h3,h4,h5,h6,[role=heading]',
|
|
94
|
+
link: 'a[href],[role=link]',
|
|
95
|
+
textbox: 'input:not([type]),input[type=text],input[type=email],input[type=tel],input[type=url],input[type=search],input[type=password],textarea,[role=textbox]',
|
|
96
|
+
checkbox: 'input[type=checkbox],[role=checkbox]',
|
|
97
|
+
radio: 'input[type=radio],[role=radio]',
|
|
98
|
+
combobox: 'select,[role=combobox]',
|
|
99
|
+
listitem: 'li,[role=listitem]',
|
|
100
|
+
list: 'ul,ol,[role=list]',
|
|
101
|
+
status: 'output,[role=status]',
|
|
102
|
+
alert: '[role=alert]',
|
|
103
|
+
dialog: 'dialog,[role=dialog]',
|
|
104
|
+
img: 'img[alt],[role=img]'
|
|
105
|
+
};
|
|
106
|
+
const labelOf = (el) => norm(el.getAttribute('aria-label') || (el.labels && el.labels[0] ? el.labels[0].textContent : ''));
|
|
107
|
+
const nameOf = (el) => labelOf(el) || norm(el.textContent) || norm(el.getAttribute('alt') || el.getAttribute('title') || el.getAttribute('placeholder') || (el.type === 'submit' || el.type === 'button' ? el.value : ''));
|
|
108
|
+
const ownText = (el) => Array.from(el.childNodes).some((n) => n.nodeType === 3 && n.textContent.trim());
|
|
109
|
+
let found = [];
|
|
110
|
+
if (signal.kind === 'css') found = Array.from(document.querySelectorAll(signal.selector));
|
|
111
|
+
else if (signal.kind === 'testId') found = Array.from(document.querySelectorAll('[data-testid="' + CSS.escape(signal.id) + '"]'));
|
|
112
|
+
else if (signal.kind === 'role') found = Array.from(document.querySelectorAll(ROLES[signal.role] || '[role="' + signal.role + '"]')).filter((el) => !signal.name || matches(nameOf(el), signal.name));
|
|
113
|
+
else if (signal.kind === 'label') found = Array.from(document.querySelectorAll('input,textarea,select,[aria-label]')).filter((el) => matches(labelOf(el), signal.match));
|
|
114
|
+
else found = Array.from(document.querySelectorAll('body *')).filter((el) => ownText(el) && matches(norm(el.textContent), signal.match));
|
|
115
|
+
const el = found.find(shown);
|
|
116
|
+
if (!el) return { visible: false, count: found.length };
|
|
117
|
+
if (scroll) el.scrollIntoView({ block: 'center', inline: 'center' });
|
|
118
|
+
const r = el.getBoundingClientRect();
|
|
119
|
+
const at = { visible: true, count: found.length, x: r.x + r.width / 2, y: r.y + r.height / 2 };
|
|
120
|
+
return withElement ? Object.assign(at, { element: el }) : at;
|
|
121
|
+
}`;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { MachineConfig, SpecBundle } from './types.js';
|
|
2
|
+
export type HandOff = {
|
|
3
|
+
finalState: string;
|
|
4
|
+
event: string;
|
|
5
|
+
parentState: string;
|
|
6
|
+
childChart: string;
|
|
7
|
+
};
|
|
8
|
+
export type Composition = {
|
|
9
|
+
machine: MachineConfig;
|
|
10
|
+
/** Which chart each state came from. */
|
|
11
|
+
chartOf: Map<string, string>;
|
|
12
|
+
/** A child's former final states and the event each hands to its parent. */
|
|
13
|
+
handOffs: HandOff[];
|
|
14
|
+
/** The state each child chart is inlined into. */
|
|
15
|
+
hosts: Map<string, string>;
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Inline every child chart where its parent invokes it, so XState's graph traversal (which cannot
|
|
19
|
+
* enter an invoked actor) walks straight through. Supports:
|
|
20
|
+
*
|
|
21
|
+
* - XState `invoke: { src, id, onDone }` and SCXML `<invoke>` + `done.invoke.<id>`;
|
|
22
|
+
* - a final state's `meta.doneEvent`, naming the event the parent receives from that final;
|
|
23
|
+
* - the legacy `meta.childMachine` + `meta.childFinalEvents` on the parent state.
|
|
24
|
+
*
|
|
25
|
+
* A child's final states stay as states (they keep their screenshots and checks) but stop being
|
|
26
|
+
* final: each takes the parent's transition for its hand-off event.
|
|
27
|
+
*/
|
|
28
|
+
export declare function composeCharts(bundle: SpecBundle): Composition;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { invokesOf, targetOf, walkStates } from './types.js';
|
|
2
|
+
function asTransition(t) {
|
|
3
|
+
const target = targetOf(t);
|
|
4
|
+
return target ? { target: `#${target}` } : undefined;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Inline every child chart where its parent invokes it, so XState's graph traversal (which cannot
|
|
8
|
+
* enter an invoked actor) walks straight through. Supports:
|
|
9
|
+
*
|
|
10
|
+
* - XState `invoke: { src, id, onDone }` and SCXML `<invoke>` + `done.invoke.<id>`;
|
|
11
|
+
* - a final state's `meta.doneEvent`, naming the event the parent receives from that final;
|
|
12
|
+
* - the legacy `meta.childMachine` + `meta.childFinalEvents` on the parent state.
|
|
13
|
+
*
|
|
14
|
+
* A child's final states stay as states (they keep their screenshots and checks) but stop being
|
|
15
|
+
* final: each takes the parent's transition for its hand-off event.
|
|
16
|
+
*/
|
|
17
|
+
export function composeCharts(bundle) {
|
|
18
|
+
const byId = new Map(bundle.charts.map((c) => [c.machine.id, c.machine]));
|
|
19
|
+
const chartOf = new Map();
|
|
20
|
+
const handOffs = [];
|
|
21
|
+
const hosts = new Map();
|
|
22
|
+
const root = byId.get(bundle.root);
|
|
23
|
+
if (!root)
|
|
24
|
+
throw new Error(`Root chart "${bundle.root}" is not loaded`);
|
|
25
|
+
const inline = (name, node, chart, stack) => {
|
|
26
|
+
chartOf.set(name, chart);
|
|
27
|
+
const invoke = invokesOf(node)[0] ?? (typeof node.meta?.childMachine === 'string' ? { src: node.meta.childMachine } : undefined);
|
|
28
|
+
if (invoke) {
|
|
29
|
+
const child = byId.get(invoke.src);
|
|
30
|
+
if (!child)
|
|
31
|
+
throw new Error(`"${name}" invokes chart "${invoke.src}", which is not loaded`);
|
|
32
|
+
hosts.set(invoke.src, name);
|
|
33
|
+
if (stack.includes(invoke.src))
|
|
34
|
+
throw new Error(`Charts invoke each other in a cycle: ${[...stack, invoke.src].join(' → ')}`);
|
|
35
|
+
const legacy = node.meta?.childFinalEvents ?? {};
|
|
36
|
+
const doneEvent = `done.invoke.${invoke.id ?? invoke.src}`;
|
|
37
|
+
const consumed = new Set();
|
|
38
|
+
const retarget = (states) => Object.fromEntries(Object.entries(states).map(([k, s]) => {
|
|
39
|
+
if (s.type === 'final') {
|
|
40
|
+
const event = legacy[k] ?? (typeof s.meta?.doneEvent === 'string' ? s.meta.doneEvent : doneEvent);
|
|
41
|
+
const transition = asTransition(node.on?.[event]) ?? asTransition(invoke.onDone);
|
|
42
|
+
if (!transition)
|
|
43
|
+
throw new Error(`Child final "${k}" has nowhere to go in "${name}" (no "${event}" or onDone)`);
|
|
44
|
+
consumed.add(event);
|
|
45
|
+
handOffs.push({ finalState: k, event, parentState: name, childChart: invoke.src });
|
|
46
|
+
const { type: _final, ...rest } = s;
|
|
47
|
+
chartOf.set(k, invoke.src);
|
|
48
|
+
return [k, { ...rest, on: { ...rest.on, [event]: transition } }];
|
|
49
|
+
}
|
|
50
|
+
return [k, inline(k, s, invoke.src, [...stack, invoke.src])];
|
|
51
|
+
}));
|
|
52
|
+
const states = retarget(child.states ?? {});
|
|
53
|
+
const { invoke: _invoke, initial: _initial, on: _on, ...withoutInvoke } = node;
|
|
54
|
+
const on = Object.fromEntries(Object.entries(node.on ?? {}).filter(([event]) => !consumed.has(event)));
|
|
55
|
+
return { ...withoutInvoke, ...(child.initial === undefined ? {} : { initial: child.initial }), states, ...(Object.keys(on).length ? { on } : {}) };
|
|
56
|
+
}
|
|
57
|
+
if (!node.states)
|
|
58
|
+
return node;
|
|
59
|
+
return {
|
|
60
|
+
...node,
|
|
61
|
+
states: Object.fromEntries(Object.entries(node.states).map(([k, v]) => [k, inline(k, v, chart, stack)]))
|
|
62
|
+
};
|
|
63
|
+
};
|
|
64
|
+
const states = Object.fromEntries(Object.entries(root.states ?? {}).map(([k, v]) => [k, inline(k, v, root.id, [root.id])]));
|
|
65
|
+
const machine = JSON.parse(JSON.stringify({ ...root, states }));
|
|
66
|
+
for (const { name, node } of walkStates(machine)) {
|
|
67
|
+
if (!node.id)
|
|
68
|
+
node.id = name;
|
|
69
|
+
}
|
|
70
|
+
return { machine, chartOf, handOffs, hosts };
|
|
71
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/** Browser-safe: parsing, SCXML conversion and composition, with no Node built-ins. */
|
|
2
|
+
export * from './types.js';
|
|
3
|
+
export { parseChart, parseJourneys } from './parse.js';
|
|
4
|
+
export { scxmlToMachine, machineToScxml, scxmlToken, ATLAS_NS } from './scxml.js';
|
|
5
|
+
export { composeCharts, type Composition, type HandOff } from './compose.js';
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/** Browser-safe: parsing, SCXML conversion and composition, with no Node built-ins. */
|
|
2
|
+
export * from './types.js';
|
|
3
|
+
export { parseChart, parseJourneys } from './parse.js';
|
|
4
|
+
export { scxmlToMachine, machineToScxml, scxmlToken, ATLAS_NS } from './scxml.js';
|
|
5
|
+
export { composeCharts } from './compose.js';
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Chart, Journey, MachineConfig, SpecBundle } from './types.js';
|
|
2
|
+
import { parseChart, parseJourneys } from './parse.js';
|
|
3
|
+
export { parseChart, parseJourneys };
|
|
4
|
+
/** The ids of the charts a machine composes, by `invoke` or the legacy `meta.childMachine`. */
|
|
5
|
+
export declare function childChartIds(machine: MachineConfig): Set<string>;
|
|
6
|
+
export declare function bundleOf(directory: string, charts: Chart[], journeys: Journey[]): SpecBundle;
|
|
7
|
+
/** Load every chart and journeys file directly inside `directory`. */
|
|
8
|
+
export declare function loadSpecDirectory(directory: string): SpecBundle;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { existsSync, readdirSync, readFileSync } from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { parseChart, parseJourneys } from './parse.js';
|
|
4
|
+
import { invokesOf, walkStates } from './types.js';
|
|
5
|
+
export { parseChart, parseJourneys };
|
|
6
|
+
const CHART_FILE = /(\.scxml|(^|\.)machine\.(ya?ml|json))$/;
|
|
7
|
+
const JOURNEY_FILE = /^journeys\.(ya?ml|json)$/;
|
|
8
|
+
/** The ids of the charts a machine composes, by `invoke` or the legacy `meta.childMachine`. */
|
|
9
|
+
export function childChartIds(machine) {
|
|
10
|
+
const ids = new Set();
|
|
11
|
+
for (const { node } of walkStates(machine)) {
|
|
12
|
+
for (const invoke of invokesOf(node))
|
|
13
|
+
ids.add(invoke.src);
|
|
14
|
+
if (typeof node.meta?.childMachine === 'string')
|
|
15
|
+
ids.add(node.meta.childMachine);
|
|
16
|
+
}
|
|
17
|
+
return ids;
|
|
18
|
+
}
|
|
19
|
+
export function bundleOf(directory, charts, journeys) {
|
|
20
|
+
const invoked = new Set(charts.flatMap((c) => [...childChartIds(c.machine)]));
|
|
21
|
+
const roots = charts.filter((c) => !invoked.has(c.machine.id));
|
|
22
|
+
const root = roots[0]?.machine.id ?? charts[0]?.machine.id;
|
|
23
|
+
if (!root)
|
|
24
|
+
throw new Error(`No statechart found in ${directory}`);
|
|
25
|
+
return { directory, charts, journeys, root };
|
|
26
|
+
}
|
|
27
|
+
/** Load every chart and journeys file directly inside `directory`. */
|
|
28
|
+
export function loadSpecDirectory(directory) {
|
|
29
|
+
if (!existsSync(directory))
|
|
30
|
+
throw new Error(`Spec directory not found: ${directory}`);
|
|
31
|
+
const files = readdirSync(directory).sort();
|
|
32
|
+
const rank = (f) => (f.endsWith('.scxml') ? 0 : /\.ya?ml$/.test(f) ? 1 : 2);
|
|
33
|
+
const parsed = files
|
|
34
|
+
.filter((f) => CHART_FILE.test(f))
|
|
35
|
+
.sort((a, b) => rank(a) - rank(b) || a.localeCompare(b))
|
|
36
|
+
.map((f) => parseChart(f, readFileSync(path.join(directory, f), 'utf8')));
|
|
37
|
+
const byId = new Map();
|
|
38
|
+
for (const chart of parsed)
|
|
39
|
+
if (!byId.has(chart.machine.id))
|
|
40
|
+
byId.set(chart.machine.id, chart);
|
|
41
|
+
const charts = [...byId.values()];
|
|
42
|
+
const journeys = files
|
|
43
|
+
.filter((f) => JOURNEY_FILE.test(f))
|
|
44
|
+
.flatMap((f) => parseJourneys(f, readFileSync(path.join(directory, f), 'utf8')));
|
|
45
|
+
return bundleOf(directory, charts, journeys);
|
|
46
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { Chart, Journey } from './types.js';
|
|
2
|
+
/** Parse one chart from its text, by file name: SCXML or XState config in JSON or YAML. */
|
|
3
|
+
export declare function parseChart(file: string, text: string): Chart;
|
|
4
|
+
/** Parse a journeys file: `{ journeys: { <name>: { description, events, endsIn } } }`. */
|
|
5
|
+
export declare function parseJourneys(file: string, text: string): Journey[];
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import YAML from 'yaml';
|
|
2
|
+
import { scxmlToMachine } from './scxml.js';
|
|
3
|
+
function isRecord(value) {
|
|
4
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
5
|
+
}
|
|
6
|
+
/** Parse one chart from its text, by file name: SCXML or XState config in JSON or YAML. */
|
|
7
|
+
export function parseChart(file, text) {
|
|
8
|
+
if (file.endsWith('.scxml')) {
|
|
9
|
+
return { file, format: 'scxml', machine: scxmlToMachine(text, file.replace(/^.*[\\/]/, '').replace(/\.scxml$/, '')) };
|
|
10
|
+
}
|
|
11
|
+
const value = file.endsWith('.json') ? JSON.parse(text) : YAML.parse(text);
|
|
12
|
+
if (!isRecord(value) || typeof value['id'] !== 'string' || !isRecord(value['states'])) {
|
|
13
|
+
throw new Error(`${file} is not an XState machine config with an id and states`);
|
|
14
|
+
}
|
|
15
|
+
return { file, format: 'xstate', machine: value };
|
|
16
|
+
}
|
|
17
|
+
/** Parse a journeys file: `{ journeys: { <name>: { description, events, endsIn } } }`. */
|
|
18
|
+
export function parseJourneys(file, text) {
|
|
19
|
+
const value = file.endsWith('.json') ? JSON.parse(text) : YAML.parse(text);
|
|
20
|
+
if (!isRecord(value) || !isRecord(value['journeys']))
|
|
21
|
+
throw new Error(`${file} has no journeys map`);
|
|
22
|
+
return Object.entries(value['journeys']).map(([name, raw]) => {
|
|
23
|
+
if (!isRecord(raw) || !Array.isArray(raw['events']))
|
|
24
|
+
throw new Error(`${file}: journey "${name}" needs an events list`);
|
|
25
|
+
return {
|
|
26
|
+
name,
|
|
27
|
+
description: typeof raw['description'] === 'string' ? raw['description'] : '',
|
|
28
|
+
events: raw['events'].map(String),
|
|
29
|
+
endsIn: Array.isArray(raw['endsIn']) ? raw['endsIn'].map(String) : []
|
|
30
|
+
};
|
|
31
|
+
});
|
|
32
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { MachineConfig } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* W3C SCXML ↔ XState config, for the pure subset Atlas executes.
|
|
4
|
+
*
|
|
5
|
+
* - `<state>`, `<parallel>`, `<final>`, `initial`, `<transition event target>`.
|
|
6
|
+
* - `<invoke type="scxml" src="other.scxml" id>` composes another chart; a transition on
|
|
7
|
+
* `done.invoke.<id>` (or `done.invoke`) becomes the invoke's `onDone`.
|
|
8
|
+
* - Business metadata lives in a foreign namespace, as SCXML allows:
|
|
9
|
+
* `<atlas:meta><atlas:description>…</atlas:description><atlas:event>…</atlas:event></atlas:meta>`.
|
|
10
|
+
* A final state's `atlas:done-event` attribute names the event its parent receives. Meta values
|
|
11
|
+
* that are not strings are written as JSON in an element marked `type="json"`.
|
|
12
|
+
*/
|
|
13
|
+
export declare const ATLAS_NS = "https://github.com/crvouga/atlas/ns/1";
|
|
14
|
+
/** SCXML ids are XML IDs and event attributes are token lists, so names travel in `atlas:name`. */
|
|
15
|
+
export declare function scxmlToken(name: string): string;
|
|
16
|
+
/** Read an SCXML document into an XState machine config. */
|
|
17
|
+
export declare function scxmlToMachine(xml: string, fallbackId?: string): MachineConfig;
|
|
18
|
+
/** Write an XState machine config as an SCXML document. */
|
|
19
|
+
export declare function machineToScxml(machine: MachineConfig): string;
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import { XMLBuilder, XMLParser } from 'fast-xml-parser';
|
|
2
|
+
import { invokesOf, targetOf } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* W3C SCXML ↔ XState config, for the pure subset Atlas executes.
|
|
5
|
+
*
|
|
6
|
+
* - `<state>`, `<parallel>`, `<final>`, `initial`, `<transition event target>`.
|
|
7
|
+
* - `<invoke type="scxml" src="other.scxml" id>` composes another chart; a transition on
|
|
8
|
+
* `done.invoke.<id>` (or `done.invoke`) becomes the invoke's `onDone`.
|
|
9
|
+
* - Business metadata lives in a foreign namespace, as SCXML allows:
|
|
10
|
+
* `<atlas:meta><atlas:description>…</atlas:description><atlas:event>…</atlas:event></atlas:meta>`.
|
|
11
|
+
* A final state's `atlas:done-event` attribute names the event its parent receives. Meta values
|
|
12
|
+
* that are not strings are written as JSON in an element marked `type="json"`.
|
|
13
|
+
*/
|
|
14
|
+
export const ATLAS_NS = 'https://github.com/crvouga/atlas/ns/1';
|
|
15
|
+
const SCXML_NS = 'http://www.w3.org/2005/07/scxml';
|
|
16
|
+
const parser = new XMLParser({
|
|
17
|
+
ignoreAttributes: false,
|
|
18
|
+
attributeNamePrefix: '',
|
|
19
|
+
preserveOrder: true,
|
|
20
|
+
trimValues: true
|
|
21
|
+
});
|
|
22
|
+
function tagOf(node) {
|
|
23
|
+
return Object.keys(node).find((k) => k !== ':@') ?? '';
|
|
24
|
+
}
|
|
25
|
+
function local(tag) {
|
|
26
|
+
return tag.includes(':') ? tag.slice(tag.indexOf(':') + 1) : tag;
|
|
27
|
+
}
|
|
28
|
+
function children(node) {
|
|
29
|
+
const value = node[tagOf(node)];
|
|
30
|
+
return Array.isArray(value) ? value : [];
|
|
31
|
+
}
|
|
32
|
+
function text(node) {
|
|
33
|
+
return children(node)
|
|
34
|
+
.map((c) => (typeof c['#text'] === 'string' || typeof c['#text'] === 'number' ? String(c['#text']) : text(c)))
|
|
35
|
+
.join('')
|
|
36
|
+
.trim();
|
|
37
|
+
}
|
|
38
|
+
function attrs(node) {
|
|
39
|
+
return node[':@'] ?? {};
|
|
40
|
+
}
|
|
41
|
+
const META_LISTS = new Set(['event', 'source', 'check', 'hint']);
|
|
42
|
+
const META_LIST_KEYS = { event: 'events', source: 'source', check: 'checks', hint: 'hints' };
|
|
43
|
+
function readMeta(node) {
|
|
44
|
+
const meta = {};
|
|
45
|
+
for (const child of children(node)) {
|
|
46
|
+
const key = local(tagOf(child));
|
|
47
|
+
if (META_LISTS.has(key)) {
|
|
48
|
+
const listKey = META_LIST_KEYS[key];
|
|
49
|
+
meta[listKey] = [...(meta[listKey] ?? []), text(child)];
|
|
50
|
+
}
|
|
51
|
+
else if (key === 'deadEnd' || key === 'dead-end') {
|
|
52
|
+
meta.deadEnd = text(child);
|
|
53
|
+
}
|
|
54
|
+
else if (attrs(child)['type'] === 'json') {
|
|
55
|
+
try {
|
|
56
|
+
meta[key] = JSON.parse(text(child));
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
meta[key] = text(child);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
meta[key] = text(child);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return meta;
|
|
67
|
+
}
|
|
68
|
+
/** SCXML ids are XML IDs and event attributes are token lists, so names travel in `atlas:name`. */
|
|
69
|
+
export function scxmlToken(name) {
|
|
70
|
+
const token = name
|
|
71
|
+
.normalize('NFKD')
|
|
72
|
+
.replace(/[^A-Za-z0-9]+/g, '-')
|
|
73
|
+
.replace(/^-+|-+$/g, '')
|
|
74
|
+
.toLowerCase();
|
|
75
|
+
return /^[a-z_]/.test(token) ? token : `s-${token}`;
|
|
76
|
+
}
|
|
77
|
+
function collectNames(nodes, names) {
|
|
78
|
+
for (const node of nodes) {
|
|
79
|
+
const tag = local(tagOf(node));
|
|
80
|
+
if (tag === 'state' || tag === 'parallel' || tag === 'final') {
|
|
81
|
+
const a = attrs(node);
|
|
82
|
+
if (a['id'])
|
|
83
|
+
names.set(a['id'], a['atlas:name'] ?? a['id']);
|
|
84
|
+
}
|
|
85
|
+
collectNames(children(node), names);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
let currentNames = new Map();
|
|
89
|
+
const nameOf = (id) => currentNames.get(id) ?? id;
|
|
90
|
+
function readState(node, invokeIds) {
|
|
91
|
+
const tag = local(tagOf(node));
|
|
92
|
+
const a = attrs(node);
|
|
93
|
+
const name = nameOf(a['id'] ?? '');
|
|
94
|
+
const state = { id: name };
|
|
95
|
+
if (tag === 'parallel')
|
|
96
|
+
state.type = 'parallel';
|
|
97
|
+
if (tag === 'final')
|
|
98
|
+
state.type = 'final';
|
|
99
|
+
const states = {};
|
|
100
|
+
const invokes = [];
|
|
101
|
+
for (const child of children(node)) {
|
|
102
|
+
if (local(tagOf(child)) !== 'invoke')
|
|
103
|
+
continue;
|
|
104
|
+
const ca = attrs(child);
|
|
105
|
+
const src = (ca['src'] ?? '').replace(/\.scxml$/, '').replace(/^.*\//, '');
|
|
106
|
+
const invoke = { src: ca['atlas:name'] ?? src, ...(ca['id'] ? { id: ca['id'] } : {}) };
|
|
107
|
+
invokes.push(invoke);
|
|
108
|
+
if (ca['id'])
|
|
109
|
+
invokeIds.set(ca['id'], invoke);
|
|
110
|
+
}
|
|
111
|
+
for (const child of children(node)) {
|
|
112
|
+
const childTag = local(tagOf(child));
|
|
113
|
+
const ca = attrs(child);
|
|
114
|
+
if (childTag === 'state' || childTag === 'parallel' || childTag === 'final') {
|
|
115
|
+
const [k, v] = readState(child, invokeIds);
|
|
116
|
+
states[k] = v;
|
|
117
|
+
}
|
|
118
|
+
else if (childTag === 'transition') {
|
|
119
|
+
const event = ca['atlas:name'] ?? ca['event'];
|
|
120
|
+
if (!event || !ca['target'])
|
|
121
|
+
continue;
|
|
122
|
+
const target = nameOf(ca['target']);
|
|
123
|
+
const raw = ca['atlas:name'] ? '' : event;
|
|
124
|
+
if (raw === 'done.invoke' || raw.startsWith('done.invoke.')) {
|
|
125
|
+
const id = raw.slice('done.invoke.'.length);
|
|
126
|
+
const invoke = invokes.find((i) => !id || i.id === id) ?? invokeIds.get(id);
|
|
127
|
+
if (invoke) {
|
|
128
|
+
invoke.onDone = { target: `#${target}` };
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
state.on = { ...state.on, [event]: { target: `#${target}` } };
|
|
133
|
+
}
|
|
134
|
+
else if (childTag === 'meta' || tagOf(child).startsWith('atlas:')) {
|
|
135
|
+
state.meta = { ...state.meta, ...readMeta(child) };
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
const doneEvent = a['atlas:done-event'];
|
|
139
|
+
if (doneEvent)
|
|
140
|
+
state.meta = { ...state.meta, doneEvent };
|
|
141
|
+
if (a['initial'])
|
|
142
|
+
state.initial = nameOf(a['initial']);
|
|
143
|
+
else if (Object.keys(states).length && tag !== 'parallel')
|
|
144
|
+
state.initial = Object.keys(states)[0];
|
|
145
|
+
if (Object.keys(states).length)
|
|
146
|
+
state.states = states;
|
|
147
|
+
if (invokes.length)
|
|
148
|
+
state.invoke = invokes.length === 1 ? invokes[0] : invokes;
|
|
149
|
+
return [name, state];
|
|
150
|
+
}
|
|
151
|
+
/** Read an SCXML document into an XState machine config. */
|
|
152
|
+
export function scxmlToMachine(xml, fallbackId = 'machine') {
|
|
153
|
+
const doc = parser.parse(xml);
|
|
154
|
+
const root = doc.find((n) => local(tagOf(n)) === 'scxml');
|
|
155
|
+
if (!root)
|
|
156
|
+
throw new Error('Not an SCXML document: no <scxml> root element');
|
|
157
|
+
const a = attrs(root);
|
|
158
|
+
currentNames = new Map();
|
|
159
|
+
collectNames(children(root), currentNames);
|
|
160
|
+
const invokeIds = new Map();
|
|
161
|
+
const machine = { id: a['name'] ?? fallbackId };
|
|
162
|
+
const states = {};
|
|
163
|
+
for (const child of children(root)) {
|
|
164
|
+
const tag = local(tagOf(child));
|
|
165
|
+
if (tag === 'state' || tag === 'parallel' || tag === 'final') {
|
|
166
|
+
const [k, v] = readState(child, invokeIds);
|
|
167
|
+
states[k] = v;
|
|
168
|
+
}
|
|
169
|
+
else if (tag === 'meta' || tagOf(child).startsWith('atlas:')) {
|
|
170
|
+
machine.meta = { ...machine.meta, ...readMeta(child) };
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
const initial = a['initial'] ? nameOf(a['initial']) : Object.keys(states)[0];
|
|
174
|
+
if (initial !== undefined)
|
|
175
|
+
machine.initial = initial;
|
|
176
|
+
for (const [name, state] of Object.entries(states))
|
|
177
|
+
state.id = name;
|
|
178
|
+
machine.states = states;
|
|
179
|
+
return machine;
|
|
180
|
+
}
|
|
181
|
+
function metaToXml(meta) {
|
|
182
|
+
if (!meta)
|
|
183
|
+
return [];
|
|
184
|
+
const items = [];
|
|
185
|
+
for (const [key, value] of Object.entries(meta)) {
|
|
186
|
+
if (key === 'doneEvent' || value === undefined)
|
|
187
|
+
continue;
|
|
188
|
+
const listTag = Object.entries(META_LIST_KEYS).find(([, k]) => k === key)?.[0];
|
|
189
|
+
if (listTag && Array.isArray(value)) {
|
|
190
|
+
for (const v of value)
|
|
191
|
+
items.push({ [`atlas:${listTag}`]: [{ '#text': String(v) }] });
|
|
192
|
+
}
|
|
193
|
+
else if (typeof value === 'string') {
|
|
194
|
+
items.push({ [`atlas:${key}`]: [{ '#text': value }] });
|
|
195
|
+
}
|
|
196
|
+
else {
|
|
197
|
+
items.push({ [`atlas:${key}`]: [{ '#text': JSON.stringify(value) }], ':@': { type: 'json' } });
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
return items.length ? [{ 'atlas:meta': items }] : [];
|
|
201
|
+
}
|
|
202
|
+
function stateToXml(name, node) {
|
|
203
|
+
const tag = node.type === 'parallel' ? 'parallel' : node.type === 'final' ? 'final' : 'state';
|
|
204
|
+
const kids = [...metaToXml(node.meta)];
|
|
205
|
+
for (const [event, t] of Object.entries(node.on ?? {})) {
|
|
206
|
+
kids.push({ transition: [], ':@': { event: scxmlToken(event), target: scxmlToken(targetOf(t)), 'atlas:name': event } });
|
|
207
|
+
}
|
|
208
|
+
for (const [i, invoke] of invokesOf(node).entries()) {
|
|
209
|
+
const id = scxmlToken(invoke.id ?? `${name} invoke ${i}`);
|
|
210
|
+
kids.push({ invoke: [], ':@': { type: 'scxml', src: `${scxmlToken(invoke.src)}.scxml`, id, 'atlas:name': invoke.src } });
|
|
211
|
+
if (invoke.onDone)
|
|
212
|
+
kids.push({ transition: [], ':@': { event: `done.invoke.${id}`, target: scxmlToken(targetOf(invoke.onDone)) } });
|
|
213
|
+
}
|
|
214
|
+
for (const [k, v] of Object.entries(node.states ?? {}))
|
|
215
|
+
kids.push(stateToXml(k, v));
|
|
216
|
+
const at = { id: scxmlToken(name), 'atlas:name': name };
|
|
217
|
+
if (node.initial && node.type !== 'parallel')
|
|
218
|
+
at['initial'] = scxmlToken(node.initial);
|
|
219
|
+
if (typeof node.meta?.doneEvent === 'string')
|
|
220
|
+
at['atlas:done-event'] = node.meta.doneEvent;
|
|
221
|
+
return { [tag]: kids, ':@': at };
|
|
222
|
+
}
|
|
223
|
+
/** Write an XState machine config as an SCXML document. */
|
|
224
|
+
export function machineToScxml(machine) {
|
|
225
|
+
const kids = [...metaToXml(machine.meta)];
|
|
226
|
+
for (const [k, v] of Object.entries(machine.states ?? {}))
|
|
227
|
+
kids.push(stateToXml(k, v));
|
|
228
|
+
const at = {
|
|
229
|
+
xmlns: SCXML_NS,
|
|
230
|
+
'xmlns:atlas': ATLAS_NS,
|
|
231
|
+
version: '1.0',
|
|
232
|
+
name: machine.id,
|
|
233
|
+
datamodel: 'null'
|
|
234
|
+
};
|
|
235
|
+
if (machine.initial)
|
|
236
|
+
at['initial'] = scxmlToken(machine.initial);
|
|
237
|
+
const builder = new XMLBuilder({ ignoreAttributes: false, attributeNamePrefix: '', preserveOrder: true, format: true, suppressEmptyNode: true });
|
|
238
|
+
return `<?xml version="1.0" encoding="UTF-8"?>\n${String(builder.build([{ scxml: kids, ':@': at }])).trim()}\n`;
|
|
239
|
+
}
|