@crvouga/atlas 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/README.md +178 -604
- package/dist/bun/src/driver.d.ts +41 -7
- package/dist/bun/src/driver.js +47 -6
- package/dist/bun/src/index.d.ts +8 -0
- package/dist/bun/src/index.js +8 -0
- package/dist/core/src/cli.d.ts +14 -1
- package/dist/core/src/cli.js +30 -1
- package/dist/core/src/config.d.ts +70 -4
- package/dist/core/src/config.js +62 -3
- package/dist/core/src/events/cloudevents.d.ts +54 -9
- package/dist/core/src/events/cloudevents.js +60 -9
- package/dist/core/src/graph.d.ts +21 -0
- package/dist/core/src/graph.js +26 -0
- package/dist/core/src/index.d.ts +11 -0
- package/dist/core/src/index.js +11 -0
- package/dist/core/src/lint.d.ts +30 -0
- package/dist/core/src/lint.js +13 -0
- package/dist/core/src/plan.d.ts +39 -1
- package/dist/core/src/plan.js +28 -1
- package/dist/core/src/privacy.d.ts +29 -2
- package/dist/core/src/privacy.js +19 -2
- package/dist/core/src/report/formats.d.ts +34 -4
- package/dist/core/src/report/formats.js +24 -4
- package/dist/core/src/report/markdown.d.ts +15 -0
- package/dist/core/src/report/markdown.js +8 -0
- package/dist/core/src/run/clients.d.ts +44 -5
- package/dist/core/src/run/clients.js +46 -2
- package/dist/core/src/run/execute.d.ts +69 -16
- package/dist/core/src/run/execute.js +67 -16
- package/dist/core/src/run/frames.d.ts +13 -4
- package/dist/core/src/run/frames.js +19 -4
- package/dist/core/src/run/http.d.ts +34 -1
- package/dist/core/src/run/http.js +14 -1
- package/dist/core/src/run/types.d.ts +76 -3
- package/dist/core/src/run/types.js +19 -3
- package/dist/core/src/signals.d.ts +49 -5
- package/dist/core/src/signals.js +22 -5
- package/dist/core/src/spec/compose.d.ts +23 -1
- package/dist/core/src/spec/compose.js +10 -1
- package/dist/core/src/spec/index.d.ts +9 -1
- package/dist/core/src/spec/index.js +9 -1
- package/dist/core/src/spec/load.d.ts +22 -1
- package/dist/core/src/spec/load.js +31 -1
- package/dist/core/src/spec/parse.d.ts +16 -2
- package/dist/core/src/spec/parse.js +25 -2
- package/dist/core/src/spec/scxml.d.ts +25 -4
- package/dist/core/src/spec/scxml.js +36 -4
- package/dist/core/src/spec/types.d.ts +52 -1
- package/dist/core/src/spec/types.js +24 -0
- package/dist/detox/src/index.d.ts +54 -11
- package/dist/detox/src/index.js +45 -7
- package/dist/playwright/src/driver.d.ts +39 -3
- package/dist/playwright/src/driver.js +27 -3
- package/dist/playwright/src/index.d.ts +10 -0
- package/dist/playwright/src/index.js +10 -0
- package/dist/playwright/src/media.d.ts +50 -4
- package/dist/playwright/src/media.js +55 -4
- package/dist/playwright/src/signals.d.ts +44 -5
- package/dist/playwright/src/signals.js +29 -5
- package/dist/reports/src/attachment-files.d.ts +44 -0
- package/dist/reports/src/attachment-files.js +121 -0
- package/dist/reports/src/attachment-response.d.ts +31 -0
- package/dist/reports/src/attachment-response.js +55 -0
- package/dist/reports/src/aws-credentials.d.ts +48 -0
- package/dist/reports/src/aws-credentials.js +221 -0
- package/dist/reports/src/browser-navigation.d.ts +14 -0
- package/dist/reports/src/browser-navigation.js +7 -0
- package/dist/reports/src/bundle.d.ts +34 -2
- package/dist/reports/src/bundle.js +38 -2
- package/dist/reports/src/catalog.d.ts +34 -3
- package/dist/reports/src/catalog.js +17 -3
- package/dist/reports/src/cli-stores.d.ts +36 -0
- package/dist/reports/src/cli-stores.js +73 -0
- package/dist/reports/src/clock.d.ts +7 -2
- package/dist/reports/src/clock.js +5 -2
- package/dist/reports/src/completion-order.d.ts +18 -5
- package/dist/reports/src/completion-order.js +18 -5
- package/dist/reports/src/compose.d.ts +80 -0
- package/dist/reports/src/compose.js +117 -0
- package/dist/reports/src/copy-store.d.ts +29 -0
- package/dist/reports/src/copy-store.js +52 -0
- package/dist/reports/src/directory-outline.d.ts +38 -0
- package/dist/reports/src/directory-outline.js +103 -0
- package/dist/reports/src/directory-store.d.ts +20 -0
- package/dist/reports/src/directory-store.js +166 -0
- package/dist/reports/src/doctor.d.ts +29 -0
- package/dist/reports/src/doctor.js +83 -0
- package/dist/reports/src/effective-delta.d.ts +34 -0
- package/dist/reports/src/effective-delta.js +25 -0
- package/dist/reports/src/evidence-detail.d.ts +64 -0
- package/dist/reports/src/evidence-detail.js +92 -0
- package/dist/reports/src/evidence-live.d.ts +40 -4
- package/dist/reports/src/evidence-live.js +52 -4
- package/dist/reports/src/evidence-types.d.ts +70 -1
- package/dist/reports/src/evidence-types.js +5 -1
- package/dist/reports/src/evidence-view.d.ts +58 -6
- package/dist/reports/src/evidence-view.js +44 -5
- package/dist/reports/src/explorer-types.d.ts +40 -3
- package/dist/reports/src/explorer-types.js +8 -0
- package/dist/reports/src/explorer-versions.d.ts +9 -1
- package/dist/reports/src/explorer-versions.js +18 -1
- package/dist/reports/src/export-evidence.d.ts +16 -1
- package/dist/reports/src/export-evidence.js +52 -1
- package/dist/reports/src/fetcher.d.ts +27 -3
- package/dist/reports/src/fetcher.js +11 -3
- package/dist/reports/src/format-date.d.ts +6 -0
- package/dist/reports/src/format-date.js +6 -0
- package/dist/reports/src/format.d.ts +16 -0
- package/dist/reports/src/format.js +10 -0
- package/dist/reports/src/frames.d.ts +36 -3
- package/dist/reports/src/frames.js +22 -3
- package/dist/reports/src/hash.d.ts +18 -0
- package/dist/reports/src/hash.js +13 -0
- package/dist/reports/src/host.d.ts +29 -0
- package/dist/reports/src/host.js +64 -0
- package/dist/reports/src/html-frame.d.ts +5 -6
- package/dist/reports/src/html-frame.js +12 -1
- package/dist/reports/src/http-store.d.ts +52 -0
- package/dist/reports/src/http-store.js +145 -0
- package/dist/reports/src/http.d.ts +7 -0
- package/dist/reports/src/http.js +6 -0
- package/dist/reports/src/ingest.d.ts +54 -0
- package/dist/reports/src/ingest.js +210 -0
- package/dist/reports/src/init.d.ts +40 -0
- package/dist/reports/src/init.js +364 -0
- package/dist/reports/src/junit.d.ts +33 -6
- package/dist/reports/src/junit.js +87 -21
- package/dist/reports/src/keyboard.d.ts +20 -0
- package/dist/reports/src/keyboard.js +11 -0
- package/dist/reports/src/keys.d.ts +21 -3
- package/dist/reports/src/keys.js +21 -3
- package/dist/reports/src/layer-axes.d.ts +48 -0
- package/dist/reports/src/layer-axes.js +71 -0
- package/dist/reports/src/layer-catalog.d.ts +28 -1
- package/dist/reports/src/layer-catalog.js +19 -2
- package/dist/reports/src/layer-merge.d.ts +98 -9
- package/dist/reports/src/layer-merge.js +154 -33
- package/dist/reports/src/layer-source.d.ts +18 -0
- package/dist/reports/src/layer-source.js +14 -0
- package/dist/reports/src/layer-store.d.ts +42 -0
- package/dist/reports/src/layer-store.js +63 -5
- package/dist/reports/src/layer-types.d.ts +34 -1
- package/dist/reports/src/layers-route.d.ts +47 -4
- package/dist/reports/src/layers-route.js +88 -12
- package/dist/reports/src/media-types.d.ts +11 -0
- package/dist/reports/src/media-types.js +22 -0
- package/dist/reports/src/namespace-guard.d.ts +10 -1
- package/dist/reports/src/namespace-guard.js +10 -1
- package/dist/reports/src/navigation.d.ts +12 -0
- package/dist/reports/src/navigation.js +7 -0
- package/dist/reports/src/object-io.d.ts +11 -4
- package/dist/reports/src/object-io.js +4 -4
- package/dist/reports/src/object-store-conformance.d.ts +27 -0
- package/dist/reports/src/object-store-conformance.js +87 -0
- package/dist/reports/src/object-store.d.ts +33 -24
- package/dist/reports/src/object-store.js +17 -24
- package/dist/reports/src/parse-with.d.ts +16 -0
- package/dist/reports/src/parse-with.js +14 -0
- package/dist/reports/src/performance-part.d.ts +25 -1
- package/dist/reports/src/performance-part.js +15 -1
- package/dist/reports/src/performance.d.ts +22 -2
- package/dist/reports/src/performance.js +12 -2
- package/dist/reports/src/persist.d.ts +12 -3
- package/dist/reports/src/persist.js +12 -3
- package/dist/reports/src/phone.d.ts +18 -1
- package/dist/reports/src/phone.js +18 -1
- package/dist/reports/src/protocol.d.ts +30 -4
- package/dist/reports/src/protocol.js +15 -4
- package/dist/reports/src/publication.d.ts +21 -1
- package/dist/reports/src/publication.js +21 -1
- package/dist/reports/src/publish-checks.d.ts +35 -0
- package/dist/reports/src/publish-checks.js +122 -0
- package/dist/reports/src/publish.d.ts +44 -9
- package/dist/reports/src/publish.js +40 -7
- package/dist/reports/src/read.d.ts +46 -16
- package/dist/reports/src/read.js +38 -14
- package/dist/reports/src/recording-bundle.d.ts +13 -2
- package/dist/reports/src/recording-bundle.js +4 -2
- package/dist/reports/src/recording-keys.d.ts +15 -1
- package/dist/reports/src/recording-keys.js +15 -1
- package/dist/reports/src/remote-report.d.ts +28 -7
- package/dist/reports/src/remote-report.js +15 -7
- package/dist/reports/src/report-cli.d.ts +51 -0
- package/dist/reports/src/report-cli.js +485 -0
- package/dist/reports/src/report-index.d.ts +99 -0
- package/dist/reports/src/report-index.js +337 -0
- package/dist/reports/src/report-loader.d.ts +10 -0
- package/dist/reports/src/report-loader.js +6 -0
- package/dist/reports/src/report-parts.d.ts +29 -3
- package/dist/reports/src/report-parts.js +40 -3
- package/dist/reports/src/report-status.d.ts +48 -0
- package/dist/reports/src/report-status.js +156 -0
- package/dist/reports/src/report-types.d.ts +73 -0
- package/dist/reports/src/report-types.js +3 -0
- package/dist/reports/src/reporter.d.ts +67 -0
- package/dist/reports/src/reporter.js +128 -0
- package/dist/reports/src/reports-explorer-route.d.ts +541 -0
- package/dist/reports/src/reports-explorer-route.js +11 -0
- package/dist/reports/src/reports-list-route.d.ts +9 -0
- package/dist/reports/src/reports-list-route.js +9 -0
- package/dist/reports/src/reports-route.d.ts +561 -22
- package/dist/reports/src/reports-route.js +11 -3
- package/dist/reports/src/reports-store.d.ts +19 -2
- package/dist/reports/src/reports-store.js +10 -2
- package/dist/reports/src/resilient-cache.d.ts +14 -1
- package/dist/reports/src/resilient-cache.js +8 -1
- package/dist/reports/src/retention.d.ts +17 -1
- package/dist/reports/src/retention.js +5 -1
- package/dist/reports/src/run-bundle.d.ts +63 -0
- package/dist/reports/src/run-bundle.js +159 -0
- package/dist/reports/src/s3-store.d.ts +79 -0
- package/dist/reports/src/s3-store.js +295 -0
- package/dist/reports/src/scenario.d.ts +14 -4
- package/dist/reports/src/scenario.js +5 -4
- package/dist/reports/src/sigv4.d.ts +72 -0
- package/dist/reports/src/sigv4.js +96 -0
- package/dist/reports/src/snapshot-bundle.d.ts +24 -1
- package/dist/reports/src/snapshot-bundle.js +27 -2
- package/dist/reports/src/snapshot-surface.d.ts +20 -9
- package/dist/reports/src/snapshot-surface.js +32 -19
- package/dist/reports/src/source-content.d.ts +23 -0
- package/dist/reports/src/source-content.js +30 -1
- package/dist/reports/src/source-entries.d.ts +13 -0
- package/dist/reports/src/source-entries.js +6 -0
- package/dist/reports/src/source-part.d.ts +15 -0
- package/dist/reports/src/source-part.js +8 -0
- package/dist/reports/src/source-reader.d.ts +11 -0
- package/dist/reports/src/source-reader.js +5 -0
- package/dist/reports/src/spec-evidence.d.ts +12 -2
- package/dist/reports/src/spec-evidence.js +6 -2
- package/dist/reports/src/spec-inventory.d.ts +15 -1
- package/dist/reports/src/spec-inventory.js +22 -1
- package/dist/reports/src/spec-link.d.ts +28 -0
- package/dist/reports/src/spec-link.js +70 -0
- package/dist/reports/src/spec-set.d.ts +63 -0
- package/dist/reports/src/spec-set.js +177 -0
- package/dist/reports/src/status.d.ts +10 -0
- package/dist/reports/src/status.js +5 -0
- package/dist/reports/src/store-prefix.d.ts +28 -0
- package/dist/reports/src/store-prefix.js +22 -0
- package/dist/reports/src/store-url.d.ts +89 -0
- package/dist/reports/src/store-url.js +160 -0
- package/dist/reports/src/test-catalog-entity.d.ts +11 -0
- package/dist/reports/src/test-catalog-entity.js +6 -0
- package/dist/reports/src/types.d.ts +6 -1
- package/dist/reports/src/use-report-nav.d.ts +35 -0
- package/dist/reports/src/use-report-nav.js +12 -0
- package/dist/schema/src/api.d.ts +799 -0
- package/dist/schema/src/api.js +201 -0
- package/dist/schema/src/cli-results.d.ts +210 -0
- package/dist/schema/src/cli-results.js +124 -0
- package/dist/schema/src/common.d.ts +53 -0
- package/dist/schema/src/common.js +42 -0
- package/dist/schema/src/index.d.ts +14 -0
- package/dist/schema/src/index.js +14 -0
- package/dist/schema/src/layer.d.ts +2273 -0
- package/dist/schema/src/layer.js +462 -0
- package/dist/schema/src/manifest.d.ts +95 -0
- package/dist/schema/src/manifest.js +84 -0
- package/dist/schema/src/report-sources.d.ts +28 -0
- package/dist/schema/src/report-sources.js +25 -0
- package/dist/schema/src/runs-index.d.ts +16 -0
- package/dist/schema/src/runs-index.js +15 -0
- package/dist/schema/src/server-config.d.ts +93 -0
- package/dist/schema/src/server-config.js +73 -0
- package/dist/schema/src/spec.d.ts +69 -0
- package/dist/schema/src/spec.js +58 -0
- package/dist/server/src/app.d.ts +62 -0
- package/dist/server/src/app.js +419 -0
- package/dist/server/src/auth.d.ts +41 -0
- package/dist/server/src/auth.js +59 -0
- package/dist/server/src/config.d.ts +83 -0
- package/dist/server/src/config.js +187 -0
- package/dist/server/src/dev.d.ts +12 -0
- package/dist/server/src/dev.js +80 -0
- package/dist/server/src/events.d.ts +29 -0
- package/dist/server/src/events.js +101 -0
- package/dist/server/src/host.d.ts +30 -0
- package/dist/server/src/host.js +115 -0
- package/dist/server/src/index.d.ts +16 -0
- package/dist/server/src/index.js +16 -0
- package/dist/server/src/llms.d.ts +19 -0
- package/dist/server/src/llms.js +63 -0
- package/dist/server/src/main.d.ts +30 -0
- package/dist/server/src/main.js +112 -0
- package/dist/server/src/openapi.d.ts +59 -0
- package/dist/server/src/openapi.js +374 -0
- package/dist/server/src/problem.d.ts +8 -0
- package/dist/server/src/problem.js +39 -0
- package/dist/server/src/site.d.ts +13 -0
- package/dist/server/src/site.js +43 -0
- package/dist/server/src/store-api.d.ts +28 -0
- package/dist/server/src/store-api.js +159 -0
- package/dist/server/src/validate.d.ts +71 -0
- package/dist/server/src/validate.js +197 -0
- package/dist/vitest/src/index.d.ts +27 -5
- package/dist/vitest/src/index.js +18 -5
- package/dist/webdriver/src/driver.d.ts +63 -11
- package/dist/webdriver/src/driver.js +57 -6
- package/dist/webdriver/src/index.d.ts +8 -0
- package/dist/webdriver/src/index.js +1 -0
- package/dist/webdriver/src/protocol.d.ts +75 -6
- package/dist/webdriver/src/protocol.js +79 -6
- package/docs/agents.md +137 -0
- package/docs/deploying.md +345 -0
- package/docs/dogfooding.md +175 -0
- package/docs/extending.md +242 -0
- package/docs/integrating.md +485 -0
- package/docs/reference/bun.md +141 -0
- package/docs/reference/cli.md +146 -0
- package/docs/reference/core.md +2426 -0
- package/docs/reference/detox.md +150 -0
- package/docs/reference/http-api.md +303 -0
- package/docs/reference/playwright.md +303 -0
- package/docs/reference/reports.md +7098 -0
- package/docs/reference/schema.md +1651 -0
- package/docs/reference/server.md +685 -0
- package/docs/reference/vitest.md +45 -0
- package/docs/reference/webdriver.md +330 -0
- package/docs/report-backends.md +144 -0
- package/docs/report-contract.md +400 -0
- package/docs/reporting-issues.md +160 -0
- package/docs/server.md +285 -0
- package/docs/statecharts.md +641 -0
- package/llms-full.txt +3506 -0
- package/llms.txt +47 -0
- package/package.json +33 -5
- package/packages/core/README.md +2 -1
- package/packages/reports/README.md +69 -2
- package/packages/reports/STORAGE.md +5 -1
- package/packages/schema/README.md +14 -0
- package/packages/schema/schemas/check-detail.schema.json +1032 -0
- package/packages/schema/schemas/checks-result.schema.json +26 -0
- package/packages/schema/schemas/command-error.schema.json +35 -0
- package/packages/schema/schemas/doctor-result.schema.json +60 -0
- package/packages/schema/schemas/effective-delta.schema.json +490 -0
- package/packages/schema/schemas/effective-evidence.schema.json +471 -0
- package/packages/schema/schemas/evidence-page.schema.json +271 -0
- package/packages/schema/schemas/hello-event.schema.json +22 -0
- package/packages/schema/schemas/init-plan.schema.json +347 -0
- package/packages/schema/schemas/layer-rows.schema.json +199 -0
- package/packages/schema/schemas/layer.schema.json +2888 -0
- package/packages/schema/schemas/merge-result.schema.json +40 -0
- package/packages/schema/schemas/outline.schema.json +62 -0
- package/packages/schema/schemas/problem.schema.json +37 -0
- package/packages/schema/schemas/publish-checks.schema.json +1413 -0
- package/packages/schema/schemas/publish-result.schema.json +168 -0
- package/packages/schema/schemas/ready.schema.json +68 -0
- package/packages/schema/schemas/report-event.schema.json +100 -0
- package/packages/schema/schemas/report-status.schema.json +370 -0
- package/packages/schema/schemas/server-config.schema.json +194 -0
- package/packages/schema/schemas/server-info.schema.json +114 -0
- package/packages/schema/schemas/spec-set.schema.json +103 -0
- package/packages/schema/schemas/store-listing.schema.json +29 -0
- package/packages/schema/schemas/store-write-result.schema.json +28 -0
- package/packages/schema/schemas/stream-result.schema.json +59 -0
- package/packages/server/README.md +43 -0
- package/packages/server/openapi.json +6699 -0
- package/packages/visualizer/README.md +27 -5
- package/packages/visualizer/example/specs/coffee/ordering/cart.qnt +7 -0
- package/packages/visualizer/example/specs/coffee/ordering/checkout.openslo.yaml +10 -0
- package/packages/visualizer/example/specs/coffee/ordering/drink-price.dmn +24 -0
- package/packages/visualizer/example/specs/coffee/ordering/loyalty.feature +7 -0
- package/packages/visualizer/example/specs/coffee/ordering/ordering.feature +36 -0
- package/packages/visualizer/example/specs/coffee/ordering/payment.als +4 -0
- package/packages/visualizer/fixtures/failures/specs/coffee/ordering/cart.qnt +7 -0
- package/packages/visualizer/fixtures/failures/specs/coffee/ordering/checkout.openslo.yaml +10 -0
- package/packages/visualizer/fixtures/failures/specs/coffee/ordering/drink-price.dmn +24 -0
- package/packages/visualizer/fixtures/failures/specs/coffee/ordering/loyalty.feature +7 -0
- package/packages/visualizer/fixtures/failures/specs/coffee/ordering/ordering.feature +36 -0
- package/packages/visualizer/fixtures/failures/specs/coffee/ordering/payment.als +4 -0
- package/packages/visualizer/fixtures/full/specs/coffee/ordering/cart.qnt +7 -0
- package/packages/visualizer/fixtures/full/specs/coffee/ordering/checkout.openslo.yaml +10 -0
- package/packages/visualizer/fixtures/full/specs/coffee/ordering/drink-price.dmn +24 -0
- package/packages/visualizer/fixtures/full/specs/coffee/ordering/loyalty.feature +7 -0
- package/packages/visualizer/fixtures/full/specs/coffee/ordering/ordering.feature +36 -0
- package/packages/visualizer/fixtures/full/specs/coffee/ordering/payment.als +4 -0
- package/packages/visualizer/fixtures/malformed/specs/coffee/ordering/cart.qnt +7 -0
- package/packages/visualizer/fixtures/malformed/specs/coffee/ordering/checkout.openslo.yaml +10 -0
- package/packages/visualizer/fixtures/malformed/specs/coffee/ordering/drink-price.dmn +24 -0
- package/packages/visualizer/fixtures/malformed/specs/coffee/ordering/loyalty.feature +7 -0
- package/packages/visualizer/fixtures/malformed/specs/coffee/ordering/ordering.feature +36 -0
- package/packages/visualizer/fixtures/malformed/specs/coffee/ordering/payment.als +4 -0
- package/packages/visualizer/fixtures/partial/specs/coffee/ordering/cart.qnt +7 -0
- package/packages/visualizer/fixtures/partial/specs/coffee/ordering/checkout.openslo.yaml +10 -0
- package/packages/visualizer/fixtures/partial/specs/coffee/ordering/drink-price.dmn +24 -0
- package/packages/visualizer/fixtures/partial/specs/coffee/ordering/loyalty.feature +7 -0
- package/packages/visualizer/fixtures/partial/specs/coffee/ordering/ordering.feature +36 -0
- package/packages/visualizer/fixtures/partial/specs/coffee/ordering/payment.als +4 -0
- package/packages/visualizer/fixtures/removed/specs/coffee/ordering/cart.qnt +7 -0
- package/packages/visualizer/fixtures/removed/specs/coffee/ordering/checkout.openslo.yaml +10 -0
- package/packages/visualizer/fixtures/removed/specs/coffee/ordering/drink-price.dmn +24 -0
- package/packages/visualizer/fixtures/removed/specs/coffee/ordering/loyalty.feature +7 -0
- package/packages/visualizer/fixtures/removed/specs/coffee/ordering/ordering.feature +36 -0
- package/packages/visualizer/fixtures/removed/specs/coffee/ordering/payment.als +4 -0
- package/packages/visualizer/fixtures/spec-only/specs/coffee/ordering/cart.qnt +7 -0
- package/packages/visualizer/fixtures/spec-only/specs/coffee/ordering/checkout.openslo.yaml +10 -0
- package/packages/visualizer/fixtures/spec-only/specs/coffee/ordering/drink-price.dmn +24 -0
- package/packages/visualizer/fixtures/spec-only/specs/coffee/ordering/loyalty.feature +7 -0
- package/packages/visualizer/fixtures/spec-only/specs/coffee/ordering/ordering.feature +36 -0
- package/packages/visualizer/fixtures/spec-only/specs/coffee/ordering/payment.als +4 -0
- package/packages/visualizer/scripts/make-fixtures.ts +2 -0
- package/packages/visualizer/scripts/publish-data.ts +1 -2
- package/packages/visualizer/src/app/AppShell.tsx +9 -2
- package/packages/visualizer/src/app/report-sources.tsx +6 -4
- package/packages/visualizer/src/app/router.tsx +3 -3
- package/packages/visualizer/src/components/Header.module.css +35 -0
- package/packages/visualizer/src/components/Header.tsx +41 -4
- package/packages/visualizer/src/data/adapters/adapter.ts +5 -17
- package/packages/visualizer/src/data/adapters/index.ts +10 -1
- package/packages/visualizer/src/evidence/api.ts +14 -2
- package/packages/visualizer/src/evidence/artifacts.module.css +213 -0
- package/packages/visualizer/src/evidence/artifacts.tsx +272 -0
- package/packages/visualizer/src/evidence/check-detail.tsx +38 -4
- package/packages/visualizer/src/evidence/evidence-context.tsx +74 -12
- package/packages/visualizer/src/evidence/evidence.module.css +20 -0
- package/packages/visualizer/src/evidence/marks.tsx +2 -0
- package/packages/visualizer/src/evidence/overview.tsx +3 -3
- package/packages/visualizer/src/evidence/sources-panel.tsx +88 -34
- package/packages/visualizer/src/evidence/spec-page.tsx +23 -16
- package/packages/visualizer/src/find/Finder.tsx +1 -7
- package/packages/visualizer/src/find/open.ts +4 -0
- package/packages/visualizer/src/live/live-stream.ts +204 -0
- package/packages/visualizer/src/runtime.ts +19 -0
- package/packages/visualizer/src/state/ui-store.ts +6 -2
- package/packages/visualizer/vite/atlas-server-proxy.ts +36 -0
- package/packages/visualizer/vite/example-reports.ts +475 -0
- package/packages/visualizer/vite.config.ts +18 -2
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { contentTypeOf } from './media-types.js';
|
|
2
|
+
function count(holder) {
|
|
3
|
+
return (holder.attachments?.length ?? 0) + (holder.steps?.length ?? 0) + (holder.annotations?.length ?? 0) + (holder.attempts?.length ?? 0) + (holder.tags?.length ?? 0) + (holder.links?.length ?? 0) + Object.keys(holder.labels ?? {}).length + Object.keys(holder.extensions ?? {}).length;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* What a result carries beyond its outcome, as counts and short facts. It rides in every row of the
|
|
7
|
+
* report so a list can show "3 attachments, 12 steps" without opening anything; the evidence itself is
|
|
8
|
+
* read when someone asks for it. Undefined when there is none.
|
|
9
|
+
*
|
|
10
|
+
* Several holders (for example a check and its layer) are summed: `attachments` also counts those on steps and
|
|
11
|
+
* attempts, `steps` counts only each holder's own top-level steps, tags are de-duplicated, and for a repeated
|
|
12
|
+
* label the later holder wins. `roles` are the attachments' roles (or, without one, what their media type or file
|
|
13
|
+
* name implies), sorted and distinct.
|
|
14
|
+
*/
|
|
15
|
+
export function summarizeEvidence(...holders) {
|
|
16
|
+
if (!holders.some(holder => count(holder) > 0))
|
|
17
|
+
return undefined;
|
|
18
|
+
const roles = new Set();
|
|
19
|
+
const tags = new Set();
|
|
20
|
+
const labels = {};
|
|
21
|
+
let attachments = 0, steps = 0, annotations = 0, attempts = 0, links = 0;
|
|
22
|
+
const attachmentsOf = (list) => { for (const item of list ?? []) {
|
|
23
|
+
attachments += 1;
|
|
24
|
+
roles.add(item.role ?? roleOfType(item.contentType ?? contentTypeOf(item.name)));
|
|
25
|
+
} };
|
|
26
|
+
for (const holder of holders) {
|
|
27
|
+
attachmentsOf(holder.attachments);
|
|
28
|
+
// Attachments on steps and attempts are part of the check too.
|
|
29
|
+
for (const step of holder.steps ?? [])
|
|
30
|
+
attachmentsOf(step.attachments);
|
|
31
|
+
for (const attempt of holder.attempts ?? []) {
|
|
32
|
+
attachmentsOf(attempt.attachments);
|
|
33
|
+
for (const step of attempt.steps ?? [])
|
|
34
|
+
attachmentsOf(step.attachments);
|
|
35
|
+
}
|
|
36
|
+
steps += holder.steps?.length ?? 0;
|
|
37
|
+
annotations += holder.annotations?.length ?? 0;
|
|
38
|
+
attempts += holder.attempts?.length ?? 0;
|
|
39
|
+
links += holder.links?.length ?? 0;
|
|
40
|
+
for (const tag of holder.tags ?? [])
|
|
41
|
+
tags.add(tag);
|
|
42
|
+
Object.assign(labels, holder.labels);
|
|
43
|
+
}
|
|
44
|
+
return { attachments, steps, annotations, attempts, links, tags: [...tags], labels, roles: [...roles].sort() };
|
|
45
|
+
}
|
|
46
|
+
/** A role to show when a producer gave none, from the media type: `image`, `video`, `audio`, `snapshot` (text/html), `text` (text, JSON, XML) or `file`. Roles are an open vocabulary; a producer's own role always wins. */
|
|
47
|
+
export function roleOfType(contentType) {
|
|
48
|
+
const type = (contentType ?? '').toLowerCase();
|
|
49
|
+
if (type.startsWith('image/'))
|
|
50
|
+
return 'image';
|
|
51
|
+
if (type.startsWith('video/'))
|
|
52
|
+
return 'video';
|
|
53
|
+
if (type.startsWith('audio/'))
|
|
54
|
+
return 'audio';
|
|
55
|
+
if (type === 'text/html')
|
|
56
|
+
return 'snapshot';
|
|
57
|
+
if (type.startsWith('text/') || type.includes('json') || type.includes('xml'))
|
|
58
|
+
return 'text';
|
|
59
|
+
return 'file';
|
|
60
|
+
}
|
|
61
|
+
/** One result's evidence, from a layer: the first check with id `checkId` (in a part of kind `kind`, when given) plus the layer's own evidence. Null when the layer holds no such check. */
|
|
62
|
+
export function checkDetailOf(layer, checkId, kind) {
|
|
63
|
+
for (const part of layer.parts) {
|
|
64
|
+
if (kind !== undefined && part.kind !== kind)
|
|
65
|
+
continue;
|
|
66
|
+
const check = part.checks.find(item => item.id === checkId);
|
|
67
|
+
if (check)
|
|
68
|
+
return { layer: layer.id, kind: part.kind, check, run: runEvidence(layer) };
|
|
69
|
+
}
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
/** What a layer carries as a whole (its tags, labels, links, attachments, annotations and extensions; not its parts' checks). Absent fields stay absent. */
|
|
73
|
+
export function runEvidence(layer) {
|
|
74
|
+
const { tags, labels, links, attachments, annotations, extensions } = layer;
|
|
75
|
+
return { ...(tags ? { tags } : {}), ...(labels ? { labels } : {}), ...(links ? { links } : {}), ...(attachments ? { attachments } : {}), ...(annotations ? { annotations } : {}), ...(extensions ? { extensions } : {}) };
|
|
76
|
+
}
|
|
77
|
+
/** Every distinct bundle key (`bundleKey`) that the attachments of a holder, its steps and its attempts point to, so a layer's files can be copied. Attachments held by `uri` or inline `text` have none. */
|
|
78
|
+
export function bundleKeysOf(holder) {
|
|
79
|
+
const keys = new Set();
|
|
80
|
+
const take = (list) => { for (const item of list ?? [])
|
|
81
|
+
if (item.bundleKey)
|
|
82
|
+
keys.add(item.bundleKey); };
|
|
83
|
+
take(holder.attachments);
|
|
84
|
+
for (const step of holder.steps ?? [])
|
|
85
|
+
take(step.attachments);
|
|
86
|
+
for (const attempt of holder.attempts ?? []) {
|
|
87
|
+
take(attempt.attachments);
|
|
88
|
+
for (const step of attempt.steps ?? [])
|
|
89
|
+
take(step.attachments);
|
|
90
|
+
}
|
|
91
|
+
return [...keys];
|
|
92
|
+
}
|
|
@@ -1,29 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module Live evidence from running suites
|
|
3
|
+
*
|
|
4
|
+
* Shows results the moment each test finishes, before its package has been collected into an archived layer. A
|
|
5
|
+
* running suite announces itself with a record under `active/` in the local store (see {@link LiveRunRecord});
|
|
6
|
+
* this module reads the test reports that suite has written so far into a temporary layer (id `live-<source>`,
|
|
7
|
+
* `live: true`) and drops it once the archived layer arrives or after a short linger. Live layers are a viewing
|
|
8
|
+
* aid only (this module writes nothing), and an unreadable or half-written report is skipped, not guessed at.
|
|
9
|
+
*/
|
|
1
10
|
import type { Clock } from './clock.js';
|
|
2
11
|
import type { ActiveRun, LayerEvidence, LayerHeader } from './evidence-types.js';
|
|
12
|
+
import type { LayerParameters } from './layer-types.js';
|
|
3
13
|
import type { ObjectStore } from './object-store.js';
|
|
4
14
|
/** What a running spec suite announces under `active/` in the local store. */
|
|
5
15
|
export type LiveRunRecord = {
|
|
16
|
+
/** The package directory the suite runs in, relative to the repository (`libs/chess`); with `bucket`, it names the source. */
|
|
6
17
|
readonly packageDir: string;
|
|
18
|
+
/** The suite's bucket within the package (`part-2`); where the run writes each finished test. */
|
|
7
19
|
readonly bucket: string;
|
|
20
|
+
/** When the run started, epoch milliseconds. A new value for the same source means a new run: tracking restarts. */
|
|
8
21
|
readonly startedAtMs: number;
|
|
22
|
+
/** How many checks the run expects to execute, when known. */
|
|
9
23
|
readonly plannedChecks?: number;
|
|
24
|
+
/** The client this suite is checking. A different client does not replace it. */
|
|
25
|
+
readonly environment?: string;
|
|
26
|
+
/** Other string choices that make the results their own slot; non-string or empty values are dropped on reading. */
|
|
27
|
+
readonly parameters?: LayerParameters;
|
|
10
28
|
};
|
|
29
|
+
/**
|
|
30
|
+
* Validates a parsed `active/` announcement and keeps only the fields the live layer can trust (the environment is
|
|
31
|
+
* normalised to its slug, parameters are string-to-string). Null when `packageDir`, `bucket` or a numeric
|
|
32
|
+
* `startedAtMs` is missing, so garbage in `active/` is ignored rather than shown.
|
|
33
|
+
*/
|
|
34
|
+
export declare function liveRunRecord(value: unknown): LiveRunRecord | null;
|
|
11
35
|
/** How the host reaches the results a spec run has written so far. */
|
|
12
36
|
export type LiveSource = {
|
|
13
|
-
/** The bucket the run writes each finished test into, or null when it cannot be read. */
|
|
37
|
+
/** The bucket the run writes each finished test into, or null when it cannot be read (the run is then not tracked). */
|
|
14
38
|
readonly bucket: (run: LiveRunRecord) => ObjectStore | null;
|
|
15
|
-
/** The checkout the runs execute. */
|
|
39
|
+
/** The checkout the runs execute: its commit and branch, stamped on every live layer. Asked once per refresh pass. */
|
|
16
40
|
readonly origin?: () => Promise<{
|
|
17
41
|
readonly sha: string | null;
|
|
18
42
|
readonly branch: string | null;
|
|
19
43
|
}>;
|
|
20
|
-
/** The stylesheet the runs' snapshots render with. */
|
|
44
|
+
/** The stylesheet the runs' snapshots render with (served as `frame.css`). */
|
|
21
45
|
readonly frameCss?: () => Promise<string | null>;
|
|
22
46
|
};
|
|
47
|
+
/** The id of the temporary layer for a source: `live-<source>`. Not a content hash, unlike an archived layer's id. */
|
|
23
48
|
export declare function liveLayerId(source: string): string;
|
|
24
49
|
/**
|
|
25
|
-
*
|
|
50
|
+
* Creates the tracker of finished tests of runs that have not been collected into a layer yet, so a
|
|
26
51
|
* result appears the moment its test ends instead of when its package does.
|
|
52
|
+
*
|
|
53
|
+
* The tracker is stateful and meant to live as long as the server: call `refresh` on each poll with the current
|
|
54
|
+
* `active/` records and the newest archived layers. It returns the live layers (only those with at least one result)
|
|
55
|
+
* and the runs still in progress with their completed and failed counts. A run that disappears is read one last
|
|
56
|
+
* time and kept for 30 seconds, or until an archived (non-live) layer of the same producer and environment recorded
|
|
57
|
+
* at or after its start appears. `find(id)` returns a live layer by `live-<source>` id, and `store(source)` the
|
|
58
|
+
* bucket its files are read from. A bucket that is being replaced or a report still being written is skipped until
|
|
59
|
+
* the next pass; `refresh` does not throw for those.
|
|
60
|
+
*
|
|
61
|
+
* @param args.live How the host reaches the buckets; without it nothing is tracked.
|
|
62
|
+
* @param args.backend The backend id stamped on live layers.
|
|
27
63
|
*/
|
|
28
64
|
export declare function createLiveEvidence(args: {
|
|
29
65
|
readonly live: LiveSource | undefined;
|
|
@@ -1,14 +1,48 @@
|
|
|
1
1
|
import { eachLimited } from './completion-order.js';
|
|
2
|
+
import { environmentOf } from './evidence-view.js';
|
|
2
3
|
import { sourceId } from './keys.js';
|
|
4
|
+
import { normalizeEnvironment } from './layer-axes.js';
|
|
3
5
|
import { layerEvidence } from './layer-merge.js';
|
|
4
6
|
import { readUtf8 } from './object-io.js';
|
|
5
7
|
import { SPEC_RUN_ID } from './publish.js';
|
|
6
8
|
import { testPrefix } from './recording-keys.js';
|
|
7
9
|
import { sourcePart } from './source-part.js';
|
|
8
10
|
import { suiteStatus } from './status.js';
|
|
11
|
+
function readParameters(value) {
|
|
12
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
13
|
+
return {};
|
|
14
|
+
const parameters = {};
|
|
15
|
+
for (const [key, item] of Object.entries(value))
|
|
16
|
+
if (typeof item === 'string' && item)
|
|
17
|
+
parameters[key] = item;
|
|
18
|
+
return parameters;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Validates a parsed `active/` announcement and keeps only the fields the live layer can trust (the environment is
|
|
22
|
+
* normalised to its slug, parameters are string-to-string). Null when `packageDir`, `bucket` or a numeric
|
|
23
|
+
* `startedAtMs` is missing, so garbage in `active/` is ignored rather than shown.
|
|
24
|
+
*/
|
|
25
|
+
export function liveRunRecord(value) {
|
|
26
|
+
if (!value || typeof value !== 'object')
|
|
27
|
+
return null;
|
|
28
|
+
const run = value;
|
|
29
|
+
if (!run.packageDir || !run.bucket || typeof run.startedAtMs !== 'number')
|
|
30
|
+
return null;
|
|
31
|
+
const parameters = readParameters(run.parameters);
|
|
32
|
+
const environment = typeof run.environment === 'string' ? normalizeEnvironment(run.environment) : null;
|
|
33
|
+
return {
|
|
34
|
+
packageDir: run.packageDir,
|
|
35
|
+
bucket: run.bucket,
|
|
36
|
+
startedAtMs: run.startedAtMs,
|
|
37
|
+
...(typeof run.plannedChecks === 'number' ? { plannedChecks: run.plannedChecks } : {}),
|
|
38
|
+
...(environment ? { environment } : {}),
|
|
39
|
+
...(Object.keys(parameters).length > 0 ? { parameters } : {}),
|
|
40
|
+
};
|
|
41
|
+
}
|
|
9
42
|
/** A finished run stays visible this long while its layer is being collected. */
|
|
10
43
|
const LINGER_MS = 30_000;
|
|
11
44
|
const REPORT = '/report.json';
|
|
45
|
+
/** The id of the temporary layer for a source: `live-<source>`. Not a content hash, unlike an archived layer's id. */
|
|
12
46
|
export function liveLayerId(source) {
|
|
13
47
|
return `live-${source}`;
|
|
14
48
|
}
|
|
@@ -21,8 +55,19 @@ function sourceOf(run) {
|
|
|
21
55
|
}
|
|
22
56
|
}
|
|
23
57
|
/**
|
|
24
|
-
*
|
|
58
|
+
* Creates the tracker of finished tests of runs that have not been collected into a layer yet, so a
|
|
25
59
|
* result appears the moment its test ends instead of when its package does.
|
|
60
|
+
*
|
|
61
|
+
* The tracker is stateful and meant to live as long as the server: call `refresh` on each poll with the current
|
|
62
|
+
* `active/` records and the newest archived layers. It returns the live layers (only those with at least one result)
|
|
63
|
+
* and the runs still in progress with their completed and failed counts. A run that disappears is read one last
|
|
64
|
+
* time and kept for 30 seconds, or until an archived (non-live) layer of the same producer and environment recorded
|
|
65
|
+
* at or after its start appears. `find(id)` returns a live layer by `live-<source>` id, and `store(source)` the
|
|
66
|
+
* bucket its files are read from. A bucket that is being replaced or a report still being written is skipped until
|
|
67
|
+
* the next pass; `refresh` does not throw for those.
|
|
68
|
+
*
|
|
69
|
+
* @param args.live How the host reaches the buckets; without it nothing is tracked.
|
|
70
|
+
* @param args.backend The backend id stamped on live layers.
|
|
26
71
|
*/
|
|
27
72
|
export function createLiveEvidence(args) {
|
|
28
73
|
const tracked = new Map();
|
|
@@ -46,8 +91,9 @@ export function createLiveEvidence(args) {
|
|
|
46
91
|
}
|
|
47
92
|
});
|
|
48
93
|
const tests = [...state.metas.values()].sort((a, b) => (a.testId < b.testId ? -1 : a.testId > b.testId ? 1 : 0));
|
|
49
|
-
const
|
|
50
|
-
const
|
|
94
|
+
const environment = normalizeEnvironment(state.run.environment ?? state.run.parameters?.environment);
|
|
95
|
+
const record = { source, packageDir: state.run.packageDir, bucket: state.run.bucket, status: suiteStatus(tests), updatedAtMs: args.clock.now(), ...(environment ? { environment } : {}), ...(state.run.parameters ? { parameters: state.run.parameters } : {}), tests };
|
|
96
|
+
const summary = { id: liveLayerId(source), producer: source, recordedAtMs: record.updatedAtMs, sha: origin.sha, branch: origin.branch, runUrl: null, ...(environment ? { environment } : {}), parameters: state.run.parameters ?? {}, specs: [], parts: [sourcePart(origin.sha ?? '', record)], source: record, recording: null };
|
|
51
97
|
const evidence = layerEvidence(summary, args.backend);
|
|
52
98
|
const layer = { ...evidence.layer, live: true, recording: { type: 'live', source }, execution: `live:${state.run.startedAtMs}:${tests.length}` };
|
|
53
99
|
state.evidence = { ...evidence, layer };
|
|
@@ -98,7 +144,9 @@ export function createLiveEvidence(args) {
|
|
|
98
144
|
/* keep what was read while it ran */
|
|
99
145
|
}
|
|
100
146
|
}
|
|
101
|
-
const
|
|
147
|
+
const declared = normalizeEnvironment(state.run.environment ?? state.run.parameters?.environment) ?? 'unspecified';
|
|
148
|
+
// A collected layer of another client is a different result, so this suite stays up.
|
|
149
|
+
const archived = collected.some(layer => layer.producer === source && !layer.live && layer.recordedAtMs >= state.run.startedAtMs && environmentOf(layer) === declared);
|
|
102
150
|
if (archived || now - state.goneAt > LINGER_MS)
|
|
103
151
|
tracked.delete(source);
|
|
104
152
|
}
|
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module Report evidence types
|
|
3
|
+
*
|
|
4
|
+
* The shapes the report API and the viewer exchange about layers: a layer's header (origin and totals), its rows
|
|
5
|
+
* (one per check), the backends that contributed, runs in progress, and the address of a recorded test's
|
|
6
|
+
* timeline. These are the wire shapes of `/api/reports/*`, so within a version they only gain optional fields.
|
|
7
|
+
* Also holds {@link recordingPaths}, the one place the URL layout of a recording is defined.
|
|
8
|
+
*
|
|
9
|
+
* @see docs/report-contract.md
|
|
10
|
+
*/
|
|
1
11
|
import type { EvidenceMeasurement, EvidenceStatus } from './explorer-types.js';
|
|
2
12
|
import type { LayerParameters } from './layer-types.js';
|
|
3
13
|
/** Where a recorded test's timeline and captured files are read from. */
|
|
@@ -14,53 +24,101 @@ export type RecordingAddress = {
|
|
|
14
24
|
};
|
|
15
25
|
/** One store that contributes report layers. */
|
|
16
26
|
export type BackendInfo = {
|
|
27
|
+
/** Stable backend id (lowercase letters, digits, hyphens); names where a result came from. */
|
|
17
28
|
readonly id: string;
|
|
29
|
+
/** What a person sees for the backend. */
|
|
18
30
|
readonly label: string;
|
|
19
31
|
/** Spec runs on this machine write here while they are still running. */
|
|
20
32
|
readonly live: boolean;
|
|
33
|
+
/** False when the backend could not be read; a notice says why, and its results are unknown, not passed. */
|
|
21
34
|
readonly available: boolean;
|
|
22
35
|
};
|
|
36
|
+
/** How many results have each status (`passed`, `failed`, `running`, `not-run`). */
|
|
23
37
|
export type StatusCounts = Readonly<Record<EvidenceStatus, number>>;
|
|
38
|
+
/**
|
|
39
|
+
* What a result (or a run) carries beyond its outcome, as counts and short facts: attachments, steps,
|
|
40
|
+
* annotations, retries, tags, labels, links. It is only there when there is something. The evidence itself
|
|
41
|
+
* is read when someone opens the result.
|
|
42
|
+
*/
|
|
43
|
+
export type EvidenceSummary = {
|
|
44
|
+
/** Attachments on the result, including those on its steps and attempts. */
|
|
45
|
+
readonly attachments: number;
|
|
46
|
+
readonly steps: number;
|
|
47
|
+
readonly annotations: number;
|
|
48
|
+
/** How many tries were recorded when the check was retried. */
|
|
49
|
+
readonly attempts: number;
|
|
50
|
+
readonly links: number;
|
|
51
|
+
readonly tags: readonly string[];
|
|
52
|
+
readonly labels: Readonly<Record<string, string>>;
|
|
53
|
+
/** The distinct roles of its attachments (or what their media types say), to show what kind of evidence there is. */
|
|
54
|
+
readonly roles: readonly string[];
|
|
55
|
+
};
|
|
24
56
|
/** A report layer's origin and totals. Its checks are read separately, once. */
|
|
25
57
|
export type LayerHeader = {
|
|
58
|
+
/** The layer's id: 64 hex digits for an archived layer, `live-<source>` for a live one. */
|
|
26
59
|
readonly id: string;
|
|
60
|
+
/** The id of the backend it was read from (the one whose id sorts first when several hold it). */
|
|
27
61
|
readonly backend: string;
|
|
62
|
+
/** The suite or tool that made it. Part of the slot. */
|
|
28
63
|
readonly producer: string;
|
|
64
|
+
/** The package directory of a spec run, when the layer came from one. */
|
|
29
65
|
readonly packageDir: string | null;
|
|
66
|
+
/** When the checks ran, epoch milliseconds; the newest completed result wins its slot. */
|
|
30
67
|
readonly recordedAtMs: number;
|
|
68
|
+
/** Provenance: the commit it was recorded at. Never part of the slot. */
|
|
31
69
|
readonly sha: string | null;
|
|
70
|
+
/** Provenance: the branch it was recorded on. Never part of the slot. */
|
|
32
71
|
readonly branch: string | null;
|
|
72
|
+
/** The client this layer checked. Null when the layer did not say. */
|
|
73
|
+
readonly environment: string | null;
|
|
74
|
+
/** Provenance: the CI run that made it. */
|
|
33
75
|
readonly runUrl: string | null;
|
|
76
|
+
/** Other string choices that make a result its own slot. */
|
|
34
77
|
readonly parameters: LayerParameters;
|
|
78
|
+
/** `modified`: recorded from a checkout with uncommitted changes; `unknown`: origin not captured; absent: clean. Advisory; never changes a status. */
|
|
35
79
|
readonly freshness?: 'unknown' | 'modified';
|
|
80
|
+
/** Where this layer's recorded timelines and captured files are read from, or null when it has none. */
|
|
36
81
|
readonly recording: RecordingAddress | null;
|
|
37
82
|
/** From a spec run that has not been collected into a layer yet. */
|
|
38
83
|
readonly live: boolean;
|
|
84
|
+
/** Its checks by status. Parts of kind `performance` are not counted when anything else was recorded. */
|
|
39
85
|
readonly counts: StatusCounts;
|
|
40
86
|
/** Equal for the mutable and the archived address of one execution. */
|
|
41
87
|
readonly execution: string;
|
|
88
|
+
/** What the run as a whole carries: its logs, links and labels. */
|
|
89
|
+
readonly evidence?: EvidenceSummary;
|
|
42
90
|
};
|
|
43
91
|
/** One check of one report part. */
|
|
44
92
|
export type LayerRow = {
|
|
93
|
+
/** The kind of the part holding the check (`behavior`, `performance`, ...). Part of the slot. */
|
|
45
94
|
readonly kind: string;
|
|
95
|
+
/** The check's stable id. Part of the slot. */
|
|
46
96
|
readonly id: string;
|
|
47
97
|
readonly title: string;
|
|
48
98
|
readonly status: EvidenceStatus;
|
|
99
|
+
/** The spec the check belongs to (`specs/<path>`), or null. */
|
|
49
100
|
readonly specPath: string | null;
|
|
101
|
+
/** Why it has this status, for a person; empty when there is nothing to add. */
|
|
50
102
|
readonly detail: string;
|
|
51
103
|
readonly measurements: readonly EvidenceMeasurement[];
|
|
52
104
|
/** The recorded test behind this check, when it has a timeline to open. */
|
|
53
105
|
readonly testId: string | null;
|
|
54
106
|
/** What distinguishes this recorded test from another run of the same check. */
|
|
55
107
|
readonly variant: LayerParameters;
|
|
108
|
+
/** How long the check took, milliseconds, or null when unknown. */
|
|
56
109
|
readonly durationMs: number | null;
|
|
110
|
+
/** What this result carries beyond its outcome. Absent when nothing. */
|
|
111
|
+
readonly evidence?: EvidenceSummary;
|
|
57
112
|
};
|
|
113
|
+
/** The checks of one layer, read separately from its header. */
|
|
58
114
|
export type LayerRows = {
|
|
115
|
+
/** The layer's id. */
|
|
59
116
|
readonly id: string;
|
|
60
117
|
readonly rows: readonly LayerRow[];
|
|
61
118
|
/** Spec path to the fingerprint the layer recorded for it. */
|
|
62
119
|
readonly fingerprints: Readonly<Record<string, string>>;
|
|
63
120
|
};
|
|
121
|
+
/** A layer's rows together with its header: everything composition needs to lay the layer over the specs. */
|
|
64
122
|
export type LayerEvidence = LayerRows & {
|
|
65
123
|
readonly layer: LayerHeader;
|
|
66
124
|
};
|
|
@@ -72,17 +130,28 @@ export type ActiveRun = {
|
|
|
72
130
|
readonly startedAtMs: number;
|
|
73
131
|
/** Spec files the run planned, when the producer recorded it. */
|
|
74
132
|
readonly plannedChecks?: number;
|
|
133
|
+
/** Checks finished so far: passed, failed or not-run. */
|
|
75
134
|
readonly completed: number;
|
|
135
|
+
/** Of those, how many failed. */
|
|
76
136
|
readonly failed: number;
|
|
77
137
|
};
|
|
138
|
+
/** One page of the layer listing the viewer reads. */
|
|
78
139
|
export type EvidencePage = {
|
|
140
|
+
/** The layers on this page, newest first. */
|
|
79
141
|
readonly layers: readonly LayerHeader[];
|
|
142
|
+
/** Opaque token for the next page, or null at the end. */
|
|
80
143
|
readonly cursor: string | null;
|
|
144
|
+
/** What could not be read; a gate treats any as unknown, never as a pass. */
|
|
81
145
|
readonly notices: readonly string[];
|
|
82
146
|
readonly backends: readonly BackendInfo[];
|
|
147
|
+
/** Runs writing results right now. */
|
|
83
148
|
readonly active: readonly ActiveRun[];
|
|
84
149
|
};
|
|
85
|
-
/**
|
|
150
|
+
/**
|
|
151
|
+
* The report API paths (relative to the reports API root) of one recorded test: its `report` JSON, a `file(path)`
|
|
152
|
+
* builder for its captured files (each path segment is URL-encoded), and the `css` its snapshots render with.
|
|
153
|
+
* The layout depends on where the recording lives: an archived layer, a commit (the older layout) or a live run.
|
|
154
|
+
*/
|
|
86
155
|
export declare function recordingPaths(address: RecordingAddress, testId: string): {
|
|
87
156
|
report: string;
|
|
88
157
|
file: (path: string) => string;
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/**
|
|
2
|
+
* The report API paths (relative to the reports API root) of one recorded test: its `report` JSON, a `file(path)`
|
|
3
|
+
* builder for its captured files (each path segment is URL-encoded), and the `css` its snapshots render with.
|
|
4
|
+
* The layout depends on where the recording lives: an archived layer, a commit (the older layout) or a live run.
|
|
5
|
+
*/
|
|
2
6
|
export function recordingPaths(address, testId) {
|
|
3
7
|
const test = encodeURIComponent(testId);
|
|
4
8
|
const file = (base) => (path) => `${base}/files/${path.split('/').map(encodeURIComponent).join('/')}`;
|
|
@@ -1,65 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module The viewer's model of a combined report
|
|
3
|
+
*
|
|
4
|
+
* Pure functions that reshape a combined report for display: results grouped per check (a {@link CheckSlot}), rolled
|
|
5
|
+
* up per spec and overall, the facets by which layers are included or left out, and the history of one check across
|
|
6
|
+
* layers. They never change a result: statuses come from the composition (`layer-merge.ts`), and a spec with no
|
|
7
|
+
* recorded check is `none`, never a pass.
|
|
8
|
+
*
|
|
9
|
+
* @see docs/report-contract.md
|
|
10
|
+
*/
|
|
1
11
|
import type { EvidenceStatus } from './explorer-types.js';
|
|
2
12
|
import type { LayerEvidence, LayerHeader, LayerRow, StatusCounts } from './evidence-types.js';
|
|
3
13
|
import { type CombinedEvidence, type EvidenceResult } from './layer-merge.js';
|
|
4
14
|
/** Everything the combined report holds for one check. */
|
|
5
15
|
export type CheckSlot = {
|
|
16
|
+
/** The check's key (its spec path and id), shared by all of its results. */
|
|
6
17
|
readonly key: string;
|
|
7
18
|
readonly id: string;
|
|
8
19
|
readonly title: string;
|
|
9
20
|
readonly specPath: string | null;
|
|
10
|
-
/** The worst outcome across the check's parameter sets. */
|
|
21
|
+
/** The worst outcome across the check's parameter sets (failed, then running, then not-run, then passed). */
|
|
11
22
|
readonly status: EvidenceStatus;
|
|
12
|
-
/** What happened, newest first. One entry per producer and parameter set. */
|
|
23
|
+
/** What happened, newest first. One entry per producer, environment and parameter set. */
|
|
13
24
|
readonly outcomes: readonly EvidenceResult[];
|
|
14
25
|
/** Recorded timings of the same check. They never change its outcome. */
|
|
15
26
|
readonly diagnostics: readonly EvidenceResult[];
|
|
27
|
+
/** The newest `recordedAtMs` among all of its results, diagnostics included. */
|
|
16
28
|
readonly latestAtMs: number;
|
|
29
|
+
/** True when any result comes from a run that is still in progress. */
|
|
17
30
|
readonly live: boolean;
|
|
18
31
|
};
|
|
19
|
-
/**
|
|
32
|
+
/**
|
|
33
|
+
* Groups the combined results (the winners of the slots) by check, in the order the checks were first seen.
|
|
34
|
+
* A check's status is the worst of its non-diagnostic results; if it has only diagnostics (timings), those are its
|
|
35
|
+
* outcomes. The title and id shown are the newest result's.
|
|
36
|
+
*/
|
|
20
37
|
export declare function checkSlots(results: readonly EvidenceResult[]): CheckSlot[];
|
|
38
|
+
/** A spec's rolled-up status: the worst of its checks, or `none` when nothing was recorded for it. */
|
|
21
39
|
export type SpecStatus = EvidenceStatus | 'none';
|
|
40
|
+
/** The rollup of a set of checks (one spec, or the whole report). */
|
|
22
41
|
export type SpecSummary = {
|
|
42
|
+
/** `failed` if any check failed, else `running`, else `passed` if any passed, else `not-run`; `none` with no checks. */
|
|
23
43
|
readonly status: SpecStatus;
|
|
44
|
+
/** Checks by their own status. */
|
|
24
45
|
readonly counts: StatusCounts;
|
|
25
46
|
readonly total: number;
|
|
47
|
+
/** The newest `recordedAtMs` among them, or null with no checks. */
|
|
26
48
|
readonly latestAtMs: number | null;
|
|
27
49
|
readonly live: boolean;
|
|
28
50
|
};
|
|
51
|
+
/** The summary of a spec with no recorded check: status `none`, which is not a pass. */
|
|
29
52
|
export declare const NO_EVIDENCE: SpecSummary;
|
|
30
|
-
/** No recorded check is `none`, never a pass. */
|
|
53
|
+
/** Rolls the checks of one spec up into a {@link SpecSummary}. No recorded check is `none`, never a pass. */
|
|
31
54
|
export declare function summarizeSlots(slots: readonly CheckSlot[]): SpecSummary;
|
|
55
|
+
/** Adds several summaries (for example every spec's) into one, with the same status rule; `none` when they hold no checks at all. */
|
|
32
56
|
export declare function addSummaries(summaries: readonly SpecSummary[]): SpecSummary;
|
|
57
|
+
/** One spec's checks and their rollup. */
|
|
33
58
|
export type SpecEvidence = {
|
|
34
59
|
readonly slots: readonly CheckSlot[];
|
|
35
60
|
readonly summary: SpecSummary;
|
|
36
61
|
};
|
|
62
|
+
/** The combined report as the viewer draws it. */
|
|
37
63
|
export type EvidenceView = {
|
|
38
64
|
/** Keyed by the spec path the outline uses (`specs/...`). */
|
|
39
65
|
readonly specs: ReadonlyMap<string, SpecEvidence>;
|
|
40
66
|
/** Results whose spec is no longer in the outline, or that name no spec. */
|
|
41
67
|
readonly unassigned: readonly CheckSlot[];
|
|
68
|
+
/** The rollup of the specs in the outline only; `unassigned` results are not counted in it. */
|
|
42
69
|
readonly total: SpecSummary;
|
|
43
70
|
};
|
|
71
|
+
/** Builds the {@link EvidenceView} of a combined report: every spec of the outline gets an entry, with an empty one when it has no results. */
|
|
44
72
|
export declare function evidenceView(combined: CombinedEvidence): EvidenceView;
|
|
45
|
-
/**
|
|
73
|
+
/**
|
|
74
|
+
* Sources left out of the combined report. An empty list means every layer
|
|
75
|
+
* on that axis contributes, including layers that arrive later.
|
|
76
|
+
*/
|
|
46
77
|
export type SourceSelection = {
|
|
78
|
+
/** Backend ids to leave out. */
|
|
47
79
|
readonly backends: readonly string[];
|
|
80
|
+
/** Branch names to leave out; `Unknown branch` stands for layers with no branch. */
|
|
48
81
|
readonly branches: readonly string[];
|
|
82
|
+
/** Commit shas to leave out; `unknown` stands for layers with no sha. */
|
|
83
|
+
readonly versions: readonly string[];
|
|
84
|
+
/** Environment slugs to leave out; `unspecified` stands for layers with none. */
|
|
85
|
+
readonly environments: readonly string[];
|
|
86
|
+
/** Layer ids to leave out. */
|
|
49
87
|
readonly layers: readonly string[];
|
|
50
88
|
};
|
|
89
|
+
/** The selection that leaves nothing out: every layer contributes. */
|
|
51
90
|
export declare const ALL_SOURCES: SourceSelection;
|
|
91
|
+
/** A saved selection from before versions and environments were axes still means "exclude nothing new". */
|
|
92
|
+
export declare function sourceSelection(excluded: Partial<SourceSelection> | null | undefined): SourceSelection;
|
|
93
|
+
/** The branch a layer was recorded on, or `Unknown branch` when it did not say. */
|
|
52
94
|
export declare function branchOf(layer: LayerHeader): string;
|
|
53
|
-
|
|
95
|
+
/** The commit a layer checked, or `unknown` when the layer did not record one. */
|
|
96
|
+
export declare function versionOf(layer: Pick<LayerHeader, 'sha'>): string;
|
|
97
|
+
/** The client a layer checked, or `unspecified` when it did not say. */
|
|
98
|
+
export declare function environmentOf(layer: Pick<LayerHeader, 'environment'>): string;
|
|
99
|
+
/** Whether a layer takes part given what is left out: false when its backend, branch, commit, environment or id is excluded. Missing axes in `excluded` exclude nothing. */
|
|
100
|
+
export declare function contributes(layer: LayerHeader, excluded: Partial<SourceSelection>): boolean;
|
|
101
|
+
/** One choice on an axis of the sources panel: its value, how many layers have it, and the newest `recordedAtMs` among them. */
|
|
54
102
|
export type SourceFacet = {
|
|
55
103
|
readonly id: string;
|
|
56
104
|
readonly layers: number;
|
|
57
105
|
readonly latestAtMs: number;
|
|
58
106
|
};
|
|
107
|
+
/** Counts the layers per backend, branch, commit and environment, each list newest first (ties by name). */
|
|
59
108
|
export declare function sourceFacets(layers: readonly LayerHeader[]): {
|
|
60
109
|
backends: SourceFacet[];
|
|
61
110
|
branches: SourceFacet[];
|
|
111
|
+
versions: SourceFacet[];
|
|
112
|
+
environments: SourceFacet[];
|
|
62
113
|
};
|
|
114
|
+
/** One layer's record of a check. */
|
|
63
115
|
export type HistoryEntry = {
|
|
64
116
|
readonly layer: LayerHeader;
|
|
65
117
|
readonly row: LayerRow;
|
|
@@ -10,7 +10,11 @@ function worst(statuses) {
|
|
|
10
10
|
function newestFirst(a, b) {
|
|
11
11
|
return b.layer.recordedAtMs - a.layer.recordedAtMs || (a.key < b.key ? -1 : a.key > b.key ? 1 : 0);
|
|
12
12
|
}
|
|
13
|
-
/**
|
|
13
|
+
/**
|
|
14
|
+
* Groups the combined results (the winners of the slots) by check, in the order the checks were first seen.
|
|
15
|
+
* A check's status is the worst of its non-diagnostic results; if it has only diagnostics (timings), those are its
|
|
16
|
+
* outcomes. The title and id shown are the newest result's.
|
|
17
|
+
*/
|
|
14
18
|
export function checkSlots(results) {
|
|
15
19
|
const groups = new Map();
|
|
16
20
|
for (const result of results) {
|
|
@@ -38,8 +42,9 @@ export function checkSlots(results) {
|
|
|
38
42
|
};
|
|
39
43
|
});
|
|
40
44
|
}
|
|
45
|
+
/** The summary of a spec with no recorded check: status `none`, which is not a pass. */
|
|
41
46
|
export const NO_EVIDENCE = { status: 'none', counts: emptyCounts(), total: 0, latestAtMs: null, live: false };
|
|
42
|
-
/** No recorded check is `none`, never a pass. */
|
|
47
|
+
/** Rolls the checks of one spec up into a {@link SpecSummary}. No recorded check is `none`, never a pass. */
|
|
43
48
|
export function summarizeSlots(slots) {
|
|
44
49
|
if (!slots.length)
|
|
45
50
|
return NO_EVIDENCE;
|
|
@@ -55,6 +60,7 @@ export function summarizeSlots(slots) {
|
|
|
55
60
|
live: slots.some(slot => slot.live),
|
|
56
61
|
};
|
|
57
62
|
}
|
|
63
|
+
/** Adds several summaries (for example every spec's) into one, with the same status rule; `none` when they hold no checks at all. */
|
|
58
64
|
export function addSummaries(summaries) {
|
|
59
65
|
const counts = emptyCounts();
|
|
60
66
|
let total = 0, latestAtMs = null, live = false;
|
|
@@ -71,6 +77,7 @@ export function addSummaries(summaries) {
|
|
|
71
77
|
const status = counts.failed ? 'failed' : counts.running ? 'running' : counts.passed ? 'passed' : 'not-run';
|
|
72
78
|
return { status, counts, total, latestAtMs, live };
|
|
73
79
|
}
|
|
80
|
+
/** Builds the {@link EvidenceView} of a combined report: every spec of the outline gets an entry, with an empty one when it has no results. */
|
|
74
81
|
export function evidenceView(combined) {
|
|
75
82
|
const specs = new Map();
|
|
76
83
|
for (const { spec, results } of combined.specs) {
|
|
@@ -79,12 +86,38 @@ export function evidenceView(combined) {
|
|
|
79
86
|
}
|
|
80
87
|
return { specs, unassigned: checkSlots(combined.unassigned), total: addSummaries([...specs.values()].map(entry => entry.summary)) };
|
|
81
88
|
}
|
|
82
|
-
|
|
89
|
+
/** The selection that leaves nothing out: every layer contributes. */
|
|
90
|
+
export const ALL_SOURCES = { backends: [], branches: [], versions: [], environments: [], layers: [] };
|
|
91
|
+
/** A saved selection from before versions and environments were axes still means "exclude nothing new". */
|
|
92
|
+
export function sourceSelection(excluded) {
|
|
93
|
+
return {
|
|
94
|
+
backends: excluded?.backends ?? [],
|
|
95
|
+
branches: excluded?.branches ?? [],
|
|
96
|
+
versions: excluded?.versions ?? [],
|
|
97
|
+
environments: excluded?.environments ?? [],
|
|
98
|
+
layers: excluded?.layers ?? [],
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
/** The branch a layer was recorded on, or `Unknown branch` when it did not say. */
|
|
83
102
|
export function branchOf(layer) {
|
|
84
103
|
return layer.branch ?? 'Unknown branch';
|
|
85
104
|
}
|
|
105
|
+
/** The commit a layer checked, or `unknown` when the layer did not record one. */
|
|
106
|
+
export function versionOf(layer) {
|
|
107
|
+
return layer.sha ?? 'unknown';
|
|
108
|
+
}
|
|
109
|
+
/** The client a layer checked, or `unspecified` when it did not say. */
|
|
110
|
+
export function environmentOf(layer) {
|
|
111
|
+
return layer.environment || 'unspecified';
|
|
112
|
+
}
|
|
113
|
+
/** Whether a layer takes part given what is left out: false when its backend, branch, commit, environment or id is excluded. Missing axes in `excluded` exclude nothing. */
|
|
86
114
|
export function contributes(layer, excluded) {
|
|
87
|
-
|
|
115
|
+
const selection = sourceSelection(excluded);
|
|
116
|
+
return !selection.backends.includes(layer.backend)
|
|
117
|
+
&& !selection.branches.includes(branchOf(layer))
|
|
118
|
+
&& !selection.versions.includes(versionOf(layer))
|
|
119
|
+
&& !selection.environments.includes(environmentOf(layer))
|
|
120
|
+
&& !selection.layers.includes(layer.id);
|
|
88
121
|
}
|
|
89
122
|
function facets(layers, id) {
|
|
90
123
|
const found = new Map();
|
|
@@ -96,8 +129,14 @@ function facets(layers, id) {
|
|
|
96
129
|
}
|
|
97
130
|
return [...found].map(([key, facet]) => ({ id: key, ...facet })).sort((a, b) => b.latestAtMs - a.latestAtMs || (a.id < b.id ? -1 : 1));
|
|
98
131
|
}
|
|
132
|
+
/** Counts the layers per backend, branch, commit and environment, each list newest first (ties by name). */
|
|
99
133
|
export function sourceFacets(layers) {
|
|
100
|
-
return {
|
|
134
|
+
return {
|
|
135
|
+
backends: facets(layers, layer => layer.backend),
|
|
136
|
+
branches: facets(layers, branchOf),
|
|
137
|
+
versions: facets(layers, versionOf),
|
|
138
|
+
environments: facets(layers, environmentOf),
|
|
139
|
+
};
|
|
101
140
|
}
|
|
102
141
|
/** Every layer's own record of one check, newest first. A superseded result stays readable here. */
|
|
103
142
|
export function checkHistory(layers, path, id) {
|