@crvouga/atlas 0.4.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 +7 -1
- package/README.md +178 -617
- 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 +35 -5
- package/dist/reports/src/evidence-live.js +18 -2
- package/dist/reports/src/evidence-types.d.ts +68 -1
- package/dist/reports/src/evidence-types.js +5 -1
- package/dist/reports/src/evidence-view.d.ts +43 -4
- package/dist/reports/src/evidence-view.js +13 -2
- package/dist/reports/src/explorer-types.d.ts +40 -3
- package/dist/reports/src/explorer-types.js +8 -0
- package/dist/reports/src/explorer-versions.d.ts +9 -1
- package/dist/reports/src/explorer-versions.js +18 -1
- package/dist/reports/src/export-evidence.d.ts +16 -1
- package/dist/reports/src/export-evidence.js +52 -1
- package/dist/reports/src/fetcher.d.ts +27 -3
- package/dist/reports/src/fetcher.js +11 -3
- package/dist/reports/src/format-date.d.ts +6 -0
- package/dist/reports/src/format-date.js +6 -0
- package/dist/reports/src/format.d.ts +16 -0
- package/dist/reports/src/format.js +10 -0
- package/dist/reports/src/frames.d.ts +36 -3
- package/dist/reports/src/frames.js +22 -3
- package/dist/reports/src/hash.d.ts +18 -0
- package/dist/reports/src/hash.js +13 -0
- package/dist/reports/src/host.d.ts +29 -0
- package/dist/reports/src/host.js +64 -0
- package/dist/reports/src/html-frame.d.ts +5 -6
- package/dist/reports/src/html-frame.js +12 -1
- package/dist/reports/src/http-store.d.ts +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 +35 -5
- package/dist/reports/src/layer-axes.js +25 -5
- package/dist/reports/src/layer-catalog.d.ts +28 -1
- package/dist/reports/src/layer-catalog.js +18 -1
- package/dist/reports/src/layer-merge.d.ts +98 -9
- package/dist/reports/src/layer-merge.js +147 -31
- package/dist/reports/src/layer-source.d.ts +18 -0
- package/dist/reports/src/layer-source.js +11 -0
- package/dist/reports/src/layer-store.d.ts +42 -0
- package/dist/reports/src/layer-store.js +63 -5
- package/dist/reports/src/layer-types.d.ts +32 -1
- package/dist/reports/src/layers-route.d.ts +47 -4
- package/dist/reports/src/layers-route.js +86 -7
- package/dist/reports/src/media-types.d.ts +11 -0
- package/dist/reports/src/media-types.js +22 -0
- package/dist/reports/src/namespace-guard.d.ts +10 -1
- package/dist/reports/src/namespace-guard.js +10 -1
- package/dist/reports/src/navigation.d.ts +12 -0
- package/dist/reports/src/navigation.js +7 -0
- package/dist/reports/src/object-io.d.ts +11 -4
- package/dist/reports/src/object-io.js +4 -4
- package/dist/reports/src/object-store-conformance.d.ts +27 -0
- package/dist/reports/src/object-store-conformance.js +87 -0
- package/dist/reports/src/object-store.d.ts +33 -24
- package/dist/reports/src/object-store.js +17 -24
- package/dist/reports/src/parse-with.d.ts +16 -0
- package/dist/reports/src/parse-with.js +14 -0
- package/dist/reports/src/performance-part.d.ts +25 -1
- package/dist/reports/src/performance-part.js +15 -1
- package/dist/reports/src/performance.d.ts +22 -2
- package/dist/reports/src/performance.js +12 -2
- package/dist/reports/src/persist.d.ts +12 -3
- package/dist/reports/src/persist.js +12 -3
- package/dist/reports/src/phone.d.ts +18 -1
- package/dist/reports/src/phone.js +18 -1
- package/dist/reports/src/protocol.d.ts +26 -4
- package/dist/reports/src/protocol.js +15 -4
- package/dist/reports/src/publication.d.ts +21 -1
- package/dist/reports/src/publication.js +21 -1
- package/dist/reports/src/publish-checks.d.ts +35 -0
- package/dist/reports/src/publish-checks.js +122 -0
- package/dist/reports/src/publish.d.ts +42 -9
- package/dist/reports/src/publish.js +36 -7
- package/dist/reports/src/read.d.ts +46 -16
- package/dist/reports/src/read.js +38 -14
- package/dist/reports/src/recording-bundle.d.ts +13 -2
- package/dist/reports/src/recording-bundle.js +4 -2
- package/dist/reports/src/recording-keys.d.ts +15 -1
- package/dist/reports/src/recording-keys.js +15 -1
- package/dist/reports/src/remote-report.d.ts +28 -7
- package/dist/reports/src/remote-report.js +15 -7
- package/dist/reports/src/report-cli.d.ts +51 -0
- package/dist/reports/src/report-cli.js +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 +37 -3
- package/dist/reports/src/report-status.d.ts +48 -0
- package/dist/reports/src/report-status.js +156 -0
- package/dist/reports/src/report-types.d.ts +73 -0
- package/dist/reports/src/report-types.js +3 -0
- package/dist/reports/src/reporter.d.ts +67 -0
- package/dist/reports/src/reporter.js +128 -0
- package/dist/reports/src/reports-explorer-route.d.ts +541 -0
- package/dist/reports/src/reports-explorer-route.js +11 -0
- package/dist/reports/src/reports-list-route.d.ts +9 -0
- package/dist/reports/src/reports-list-route.js +9 -0
- package/dist/reports/src/reports-route.d.ts +561 -22
- package/dist/reports/src/reports-route.js +11 -3
- package/dist/reports/src/reports-store.d.ts +19 -2
- package/dist/reports/src/reports-store.js +10 -2
- package/dist/reports/src/resilient-cache.d.ts +14 -1
- package/dist/reports/src/resilient-cache.js +8 -1
- package/dist/reports/src/retention.d.ts +17 -1
- package/dist/reports/src/retention.js +5 -1
- package/dist/reports/src/run-bundle.d.ts +63 -0
- package/dist/reports/src/run-bundle.js +159 -0
- package/dist/reports/src/s3-store.d.ts +79 -0
- package/dist/reports/src/s3-store.js +295 -0
- package/dist/reports/src/scenario.d.ts +14 -4
- package/dist/reports/src/scenario.js +5 -4
- package/dist/reports/src/sigv4.d.ts +72 -0
- package/dist/reports/src/sigv4.js +96 -0
- package/dist/reports/src/snapshot-bundle.d.ts +24 -1
- package/dist/reports/src/snapshot-bundle.js +27 -2
- package/dist/reports/src/snapshot-surface.d.ts +20 -9
- package/dist/reports/src/snapshot-surface.js +32 -19
- package/dist/reports/src/source-content.d.ts +23 -0
- package/dist/reports/src/source-content.js +30 -1
- package/dist/reports/src/source-entries.d.ts +13 -0
- package/dist/reports/src/source-entries.js +6 -0
- package/dist/reports/src/source-part.d.ts +15 -0
- package/dist/reports/src/source-part.js +8 -0
- package/dist/reports/src/source-reader.d.ts +11 -0
- package/dist/reports/src/source-reader.js +5 -0
- package/dist/reports/src/spec-evidence.d.ts +12 -2
- package/dist/reports/src/spec-evidence.js +6 -2
- package/dist/reports/src/spec-inventory.d.ts +15 -1
- package/dist/reports/src/spec-inventory.js +22 -1
- package/dist/reports/src/spec-link.d.ts +28 -0
- package/dist/reports/src/spec-link.js +70 -0
- package/dist/reports/src/spec-set.d.ts +63 -0
- package/dist/reports/src/spec-set.js +177 -0
- package/dist/reports/src/status.d.ts +10 -0
- package/dist/reports/src/status.js +5 -0
- package/dist/reports/src/store-prefix.d.ts +28 -0
- package/dist/reports/src/store-prefix.js +22 -0
- package/dist/reports/src/store-url.d.ts +89 -0
- package/dist/reports/src/store-url.js +160 -0
- package/dist/reports/src/test-catalog-entity.d.ts +11 -0
- package/dist/reports/src/test-catalog-entity.js +6 -0
- package/dist/reports/src/types.d.ts +6 -1
- package/dist/reports/src/use-report-nav.d.ts +35 -0
- package/dist/reports/src/use-report-nav.js +12 -0
- package/dist/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 +32 -4
- package/packages/core/README.md +2 -1
- package/packages/reports/README.md +65 -0
- package/packages/reports/STORAGE.md +4 -0
- 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 +21 -0
- 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 +40 -3
- 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 +3 -1
- package/packages/visualizer/src/evidence/evidence-context.tsx +74 -12
- 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/vite/atlas-server-proxy.ts +36 -0
- package/packages/visualizer/vite.config.ts +11 -3
|
@@ -0,0 +1,462 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module Layer document and report status schemas
|
|
3
|
+
*
|
|
4
|
+
* The stable evidence format. A layer is the one unit of evidence every producer writes and every reader combines;
|
|
5
|
+
* this module also holds the input a producer hands to the publisher (`PublishChecksSchema`), the spec set a report is
|
|
6
|
+
* laid over (`SpecSetSchema`) and the JSON `atlas report status` prints (`ReportStatusSchema`). Reports are never
|
|
7
|
+
* stored
|
|
8
|
+
* as a whole; they are computed by layering.
|
|
9
|
+
*
|
|
10
|
+
* A layer is one focused recording: the checks one producer ran, on one environment, from one
|
|
11
|
+
* version and branch. It is immutable and addressed by its content.
|
|
12
|
+
*
|
|
13
|
+
* Compatibility, which is the point of this file:
|
|
14
|
+
* - Within a schema major version a change only adds optional fields. Readers ignore fields they
|
|
15
|
+
* do not know (every object here is loose), so an old reader reads a newer minor layer.
|
|
16
|
+
* - The closed enumerations (`status`, `freshness`) change only with a new major version.
|
|
17
|
+
* - `schemaVersion` is the major version. A reader that is older than a layer says so; it never
|
|
18
|
+
* guesses.
|
|
19
|
+
*
|
|
20
|
+
* Extensibility, which is what makes one format fit any test framework (docs/extending.md):
|
|
21
|
+
* - Evidence is open. Beyond its outcome a check may carry attachments of any media type, timeline
|
|
22
|
+
* steps, annotations, retry attempts, tags, labels, links and namespaced `extensions`. Every one of
|
|
23
|
+
* them is optional, so a layer without them is as valid as it ever was.
|
|
24
|
+
* - Vocabularies are open strings (`role`, `kind`, `type`, `level`, a step's `status`). The values
|
|
25
|
+
* documented here are conventions, not a list: a reader shows what it does not know in a generic way.
|
|
26
|
+
* - `extensions` is where a producer or a tool puts structured data of its own, under a key it owns
|
|
27
|
+
* (`com.example.coverage`). Readers keep it and never fail on it.
|
|
28
|
+
* - Evidence never decides a result. A reader that cannot read a piece of evidence drops that piece and
|
|
29
|
+
* keeps the check, its status and its place in the report.
|
|
30
|
+
*
|
|
31
|
+
* @see docs/report-contract.md
|
|
32
|
+
* @see docs/extending.md
|
|
33
|
+
*/
|
|
34
|
+
import { z } from 'zod/v4';
|
|
35
|
+
/**
|
|
36
|
+
* The major version of the layer format, written as `schemaVersion` in every layer. `parseLayerDocument` reads a
|
|
37
|
+
* layer without the field as this version and refuses any other.
|
|
38
|
+
*/
|
|
39
|
+
export const LAYER_SCHEMA_VERSION = 1;
|
|
40
|
+
/** Outcomes of a check. Anything not `passed` is not evidence of success. */
|
|
41
|
+
export const CHECK_STATUSES = ['passed', 'failed', 'running', 'not-run'];
|
|
42
|
+
/**
|
|
43
|
+
* Zod enum over `CHECK_STATUSES`, the closed vocabulary of a check's and an attempt's `status`. It changes only with
|
|
44
|
+
* a new `schemaVersion`.
|
|
45
|
+
*/
|
|
46
|
+
export const CheckStatusSchema = z.enum(CHECK_STATUSES);
|
|
47
|
+
/**
|
|
48
|
+
* Why a layer's origin is advisory: `modified` (recorded from a checkout with uncommitted changes) or `unknown`
|
|
49
|
+
* (origin not captured). Absent on a layer from a clean commit. It never changes a status. A closed vocabulary: it
|
|
50
|
+
* changes only with a new `schemaVersion`.
|
|
51
|
+
*/
|
|
52
|
+
export const FRESHNESS = ['unknown', 'modified'];
|
|
53
|
+
/**
|
|
54
|
+
* A number a check measured, such as a latency or a size, with its unit and an optional allowance. A measurement over
|
|
55
|
+
* its `limit` is reported and never changes the check's status.
|
|
56
|
+
*/
|
|
57
|
+
export const MeasurementSchema = z.looseObject({
|
|
58
|
+
name: z.string().min(1),
|
|
59
|
+
value: z.number().describe('A finite number'),
|
|
60
|
+
unit: z.string(),
|
|
61
|
+
limit: z.number().optional().describe('The allowance. A measurement over it is reported, and never fails the check by itself')
|
|
62
|
+
});
|
|
63
|
+
/**
|
|
64
|
+
* Structured data of a producer's or tool's own, by namespace: `{ "com.example.coverage": { … } }`.
|
|
65
|
+
* Use a name you own (a reverse domain, or your tool's name). Readers preserve it and never fail on it.
|
|
66
|
+
*/
|
|
67
|
+
export const ExtensionsSchema = z.record(z.string().min(1), z.unknown());
|
|
68
|
+
/** A link attached to a layer, part, check or annotation: a URL, an optional name and why it is linked. */
|
|
69
|
+
export const LinkSchema = z.looseObject({
|
|
70
|
+
url: z.string().min(1),
|
|
71
|
+
name: z.string().optional(),
|
|
72
|
+
rel: z.string().optional().describe('Why it is linked, in an open vocabulary: issue, run, trace, docs, source…')
|
|
73
|
+
});
|
|
74
|
+
/** Where in a check something happened: at a step, and/or this long after the check started. */
|
|
75
|
+
export const MomentSchema = z.looseObject({
|
|
76
|
+
step: z.string().optional().describe('The `id` of a step'),
|
|
77
|
+
offsetMs: z.number().nonnegative().optional()
|
|
78
|
+
});
|
|
79
|
+
/**
|
|
80
|
+
* A file or a value that goes with a result: a screenshot, a video, a trace, a log, a DOM or accessibility
|
|
81
|
+
* snapshot, a HAR, a coverage report, a diff, anything. `contentType` says how to read it; `role` says what
|
|
82
|
+
* it is for, in an open vocabulary (`screenshot`, `video`, `trace`, `log`, `snapshot`, `har`, `coverage`,
|
|
83
|
+
* `diff`, `report`…). The bytes are in the layer (`bundleKey`), somewhere else (`uri`), or small enough to
|
|
84
|
+
* sit inline (`text`).
|
|
85
|
+
*/
|
|
86
|
+
export const AttachmentSchema = z.looseObject({
|
|
87
|
+
id: z.string().optional().describe('Lets another attachment, step or annotation refer to this one'),
|
|
88
|
+
name: z.string().min(1),
|
|
89
|
+
role: z.string().optional(),
|
|
90
|
+
contentType: z.string().optional().describe('An IANA media type. Absent means application/octet-stream'),
|
|
91
|
+
bundleKey: z.string().optional().describe('The key of its bytes in the layer'),
|
|
92
|
+
uri: z.string().optional().describe('Where else to read it: an https URL, or a path a host understands'),
|
|
93
|
+
text: z.string().optional().describe('A small UTF-8 value, inline'),
|
|
94
|
+
bytes: z.number().int().nonnegative().optional(),
|
|
95
|
+
sha256: z.string().optional(),
|
|
96
|
+
width: z.number().nonnegative().optional(),
|
|
97
|
+
height: z.number().nonnegative().optional(),
|
|
98
|
+
durationMs: z.number().nonnegative().optional().describe('For video and audio'),
|
|
99
|
+
description: z.string().optional(),
|
|
100
|
+
at: MomentSchema.optional(),
|
|
101
|
+
poster: z.string().optional().describe('The `id` of an attachment that stands for this one before it is opened'),
|
|
102
|
+
captions: z.array(z.string()).optional().describe('The `id`s of attachments (WebVTT, SRT) that caption this one'),
|
|
103
|
+
extensions: ExtensionsSchema.optional()
|
|
104
|
+
});
|
|
105
|
+
/** A note about a result: a warning, a deprecation, a coverage figure, an accessibility finding, anything. */
|
|
106
|
+
export const AnnotationSchema = z.looseObject({
|
|
107
|
+
type: z.string().min(1).describe('An open vocabulary: note, warning, deprecation, issue, accessibility, coverage…'),
|
|
108
|
+
message: z.string(),
|
|
109
|
+
level: z.string().optional().describe('Conventionally info, warning or error. Anything else is shown as a note'),
|
|
110
|
+
at: MomentSchema.optional(),
|
|
111
|
+
source: z.looseObject({ file: z.string().optional(), line: z.number().int().optional(), column: z.number().int().optional() }).optional(),
|
|
112
|
+
links: z.array(LinkSchema).optional(),
|
|
113
|
+
extensions: ExtensionsSchema.optional()
|
|
114
|
+
});
|
|
115
|
+
/**
|
|
116
|
+
* One entry on a check's timeline. Steps are a flat list: nest them with `parentId`, order them by
|
|
117
|
+
* `offsetMs`. Whatever a framework calls its steps (actions, hooks, requests, spans), they fit.
|
|
118
|
+
*/
|
|
119
|
+
export const StepSchema = z.looseObject({
|
|
120
|
+
id: z.string().optional(),
|
|
121
|
+
parentId: z.string().optional(),
|
|
122
|
+
name: z.string(),
|
|
123
|
+
kind: z.string().optional().describe('An open vocabulary: action, assertion, hook, request, span, given, when, then…'),
|
|
124
|
+
status: z.string().optional().describe('Conventionally passed, failed, skipped. Anything else is shown as it is'),
|
|
125
|
+
offsetMs: z.number().nonnegative().optional().describe('When it started, after the check did'),
|
|
126
|
+
durationMs: z.number().nonnegative().optional(),
|
|
127
|
+
detail: z.string().optional(),
|
|
128
|
+
attachments: z.array(AttachmentSchema).optional(),
|
|
129
|
+
annotations: z.array(AnnotationSchema).optional(),
|
|
130
|
+
extensions: ExtensionsSchema.optional()
|
|
131
|
+
});
|
|
132
|
+
/** One try of a check that was retried. The check's own status is the last one. */
|
|
133
|
+
export const AttemptSchema = z.looseObject({
|
|
134
|
+
index: z.number().int().nonnegative(),
|
|
135
|
+
status: CheckStatusSchema,
|
|
136
|
+
durationMs: z.number().nonnegative().optional(),
|
|
137
|
+
startedAtMs: z.number().nonnegative().optional(),
|
|
138
|
+
detail: z.string().optional(),
|
|
139
|
+
steps: z.array(StepSchema).optional(),
|
|
140
|
+
attachments: z.array(AttachmentSchema).optional(),
|
|
141
|
+
annotations: z.array(AnnotationSchema).optional(),
|
|
142
|
+
extensions: ExtensionsSchema.optional()
|
|
143
|
+
});
|
|
144
|
+
/** What may be attached at any level: a layer, a part, a check. Every field is optional and open. */
|
|
145
|
+
const evidenceShape = {
|
|
146
|
+
tags: z.array(z.string()).optional().describe('Free labels to filter and group by: smoke, slow, flaky…'),
|
|
147
|
+
labels: z.record(z.string(), z.string()).optional().describe('Facts about the result, not part of its identity: owner, severity, browser, os, build. Unlike `parameters` they never split a result into another slot'),
|
|
148
|
+
links: z.array(LinkSchema).optional(),
|
|
149
|
+
attachments: z.array(AttachmentSchema).optional(),
|
|
150
|
+
annotations: z.array(AnnotationSchema).optional(),
|
|
151
|
+
extensions: ExtensionsSchema.optional()
|
|
152
|
+
};
|
|
153
|
+
/**
|
|
154
|
+
* One check: the unit that has a status, such as a Gherkin scenario, a test case or a lint rule. Its `id` is stable
|
|
155
|
+
* across layers; with the layer's producer, the part's kind, the spec, the environment and the parameters it names
|
|
156
|
+
* the slot a result fills, and the newest completed result of a slot wins. It carries the outcome (`status`,
|
|
157
|
+
* `detail`, `measurements`) and, optionally, evidence (tags, labels, links, attachments, annotations, steps,
|
|
158
|
+
* attempts, extensions) that never decides the result.
|
|
159
|
+
*/
|
|
160
|
+
export const CheckSchema = z.looseObject({
|
|
161
|
+
id: z.string().min(1).describe("The check's stable identity within its producer. The same check must keep the same id in every layer, or layers cannot replace one another"),
|
|
162
|
+
title: z.string(),
|
|
163
|
+
status: CheckStatusSchema,
|
|
164
|
+
detail: z.string().default('').describe('Why it failed, or what was measured'),
|
|
165
|
+
specPath: z.string().optional().describe('The spec this check belongs to, relative to the repository, such as specs/join.feature'),
|
|
166
|
+
measurements: z.array(MeasurementSchema).default([]),
|
|
167
|
+
durationMs: z.number().nonnegative().optional().describe('How long the check took, when the producer measured it'),
|
|
168
|
+
...evidenceShape,
|
|
169
|
+
steps: z.array(StepSchema).optional().describe("The check's timeline"),
|
|
170
|
+
attempts: z.array(AttemptSchema).optional().describe('Every try, when the check was retried')
|
|
171
|
+
});
|
|
172
|
+
/**
|
|
173
|
+
* A spec a layer was recorded against: its path, domain, kind and title, and an advisory fingerprint of its text so a
|
|
174
|
+
* reader can warn when the spec changed since.
|
|
175
|
+
*/
|
|
176
|
+
export const SpecEntrySchema = z.looseObject({
|
|
177
|
+
path: z.string().min(1),
|
|
178
|
+
domain: z.string(),
|
|
179
|
+
kind: z.string(),
|
|
180
|
+
title: z.string(),
|
|
181
|
+
fingerprint: z.string().optional().describe('Identity of the spec text the layer was recorded against, to warn when the spec changed since')
|
|
182
|
+
});
|
|
183
|
+
/**
|
|
184
|
+
* A group of checks inside a layer, with an open `kind` (`behavior`, `performance`, `accessibility`…) that is part of
|
|
185
|
+
* the slot a check fills. `publishChecks` writes a layer with one part. Parts may carry evidence of their own.
|
|
186
|
+
*/
|
|
187
|
+
export const ReportPartSchema = z.looseObject({
|
|
188
|
+
freshness: z.enum(FRESHNESS).optional(),
|
|
189
|
+
parameters: z.record(z.string(), z.string()).optional(),
|
|
190
|
+
sha: z.string().default('').describe('Repeats the layer sha; readers use the layer'),
|
|
191
|
+
id: z.string().min(1),
|
|
192
|
+
kind: z.string().min(1).describe('An open category: behavior, performance, accessibility… A new kind needs no change to a reader'),
|
|
193
|
+
title: z.string(),
|
|
194
|
+
recordedAtMs: z.number().nonnegative(),
|
|
195
|
+
checks: z.array(CheckSchema),
|
|
196
|
+
...evidenceShape
|
|
197
|
+
});
|
|
198
|
+
const RecordedTestSchema = z.looseObject({
|
|
199
|
+
testId: z.string().min(1),
|
|
200
|
+
title: z.string(),
|
|
201
|
+
status: z.enum(['passed', 'failed', 'skipped']),
|
|
202
|
+
system: z.string().default(''),
|
|
203
|
+
runtime: z.string().default(''),
|
|
204
|
+
seed: z.string().default(''),
|
|
205
|
+
durationMs: z.number().nonnegative().default(0)
|
|
206
|
+
});
|
|
207
|
+
/** A suite that recorded timelines and captured files, beside its checks. Absent for a layer of plain checks. */
|
|
208
|
+
export const LayerSourceSchema = z.looseObject({
|
|
209
|
+
source: z.string().min(1),
|
|
210
|
+
packageDir: z.string(),
|
|
211
|
+
bucket: z.string(),
|
|
212
|
+
status: z.enum(['passed', 'failed', 'skipped']),
|
|
213
|
+
updatedAtMs: z.number().nonnegative(),
|
|
214
|
+
environment: z.string().optional(),
|
|
215
|
+
parameters: z.record(z.string(), z.string()).optional(),
|
|
216
|
+
evidence: z.array(z.looseObject({ kind: z.string(), title: z.string(), checks: z.array(CheckSchema) })).optional(),
|
|
217
|
+
tests: z.array(RecordedTestSchema)
|
|
218
|
+
});
|
|
219
|
+
/**
|
|
220
|
+
* Where the recorded tests of a suite live in the store: in the layer's own bundle (`layer`), or at the older address
|
|
221
|
+
* of a commit and a source name (`commit`).
|
|
222
|
+
*/
|
|
223
|
+
export const RecordingAddressSchema = z.union([
|
|
224
|
+
z.looseObject({ type: z.literal('layer') }),
|
|
225
|
+
z.looseObject({ type: z.literal('commit'), sha: z.string().min(1), source: z.string().min(1) })
|
|
226
|
+
]);
|
|
227
|
+
/**
|
|
228
|
+
* The captured files of a layer by file key (`attachments/<sha256>` for attachments): each file's media type and the
|
|
229
|
+
* byte ranges (`blob` store key, `offset` and `size` in the decompressed blob) whose concatenation is the file.
|
|
230
|
+
* Written last when a layer is published.
|
|
231
|
+
*/
|
|
232
|
+
export const BundleIndexSchema = z.record(z.string(), z.looseObject({
|
|
233
|
+
contentType: z.string(),
|
|
234
|
+
parts: z.array(z.looseObject({ blob: z.string(), offset: z.number().int().nonnegative(), size: z.number().int().nonnegative() }))
|
|
235
|
+
}));
|
|
236
|
+
/** Pattern of a layer id, a spec-set id or a content digest: 64 lowercase hexadecimal digits (a SHA-256). */
|
|
237
|
+
export const LAYER_ID = /^[0-9a-f]{64}$/;
|
|
238
|
+
/**
|
|
239
|
+
* `layers/manifests/<id>.json`. The id is the SHA-256 of the document without it, so two producers
|
|
240
|
+
* that record the same thing write the same layer, and a retried publish changes nothing.
|
|
241
|
+
*/
|
|
242
|
+
export const LayerDocumentSchema = z.looseObject({
|
|
243
|
+
schemaVersion: z.literal(LAYER_SCHEMA_VERSION).describe('The major version of this contract'),
|
|
244
|
+
id: z.string().regex(LAYER_ID).describe('64 lowercase hex digits. Opaque to readers'),
|
|
245
|
+
producer: z.string().min(1).describe('A stable name for the suite or tool. With the check id, environment and parameters it picks the slot a layer fills'),
|
|
246
|
+
recordedAtMs: z.number().nonnegative().describe('When the checks ran, in epoch milliseconds. The newest completed result wins its slot'),
|
|
247
|
+
sha: z.string().nullable().describe('The commit the checks ran against. Provenance only; it never partitions a report'),
|
|
248
|
+
branch: z.string().nullable().describe('Provenance only'),
|
|
249
|
+
runUrl: z.string().nullable().describe('Where to read the CI run that produced it'),
|
|
250
|
+
environment: z.string().nullable().optional().describe('The client that was checked: desktop-web, mobile-web, ios, android, or any other lowercase name'),
|
|
251
|
+
parameters: z.record(z.string(), z.string()).default({}).describe('Other choices that make a result its own slot, such as a device size'),
|
|
252
|
+
freshness: z.enum(FRESHNESS).optional().describe('`modified`: recorded from a checkout with uncommitted changes. `unknown`: origin not captured'),
|
|
253
|
+
specs: z.array(SpecEntrySchema).default([]),
|
|
254
|
+
parts: z.array(ReportPartSchema),
|
|
255
|
+
source: LayerSourceSchema.nullable().default(null),
|
|
256
|
+
recording: RecordingAddressSchema.nullable().default(null),
|
|
257
|
+
bundle: BundleIndexSchema.default({}),
|
|
258
|
+
...evidenceShape
|
|
259
|
+
});
|
|
260
|
+
/**
|
|
261
|
+
* Evidence is decoration, never the result. A reader that meets a piece of evidence it cannot read (a
|
|
262
|
+
* malformed attachment, a field of the wrong type) drops that piece and keeps the check, its status and
|
|
263
|
+
* its place in the report. The strict schemas above are what producers validate against; this is how
|
|
264
|
+
* readers stay forgiving.
|
|
265
|
+
*/
|
|
266
|
+
function lenientList(value, schema, prepare = (item) => item) {
|
|
267
|
+
if (!Array.isArray(value))
|
|
268
|
+
return undefined;
|
|
269
|
+
return value.flatMap((item) => {
|
|
270
|
+
const parsed = schema.safeParse(prepare(item));
|
|
271
|
+
return parsed.success ? [parsed.data] : [];
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
const object = (value) => (typeof value === 'object' && value !== null && !Array.isArray(value) ? value : null);
|
|
275
|
+
function lenientEvidence(holder, nested = []) {
|
|
276
|
+
const out = { ...holder };
|
|
277
|
+
const set = (key, value) => { if (value === undefined)
|
|
278
|
+
delete out[key];
|
|
279
|
+
else
|
|
280
|
+
out[key] = value; };
|
|
281
|
+
if ('tags' in out)
|
|
282
|
+
set('tags', lenientList(out.tags, z.string()));
|
|
283
|
+
if ('links' in out)
|
|
284
|
+
set('links', lenientList(out.links, LinkSchema));
|
|
285
|
+
if ('attachments' in out)
|
|
286
|
+
set('attachments', lenientList(out.attachments, AttachmentSchema));
|
|
287
|
+
if ('annotations' in out)
|
|
288
|
+
set('annotations', lenientList(out.annotations, AnnotationSchema));
|
|
289
|
+
if ('labels' in out)
|
|
290
|
+
set('labels', z.record(z.string(), z.string()).safeParse(out.labels).data);
|
|
291
|
+
if ('extensions' in out)
|
|
292
|
+
set('extensions', ExtensionsSchema.safeParse(out.extensions).data);
|
|
293
|
+
if (nested.includes('steps') && 'steps' in out)
|
|
294
|
+
set('steps', lenientList(out.steps, StepSchema, (item) => { const step = object(item); return step ? lenientEvidence(step) : item; }));
|
|
295
|
+
if (nested.includes('attempts') && 'attempts' in out)
|
|
296
|
+
set('attempts', lenientList(out.attempts, AttemptSchema, (item) => { const attempt = object(item); return attempt ? lenientEvidence(attempt, ['steps']) : item; }));
|
|
297
|
+
return out;
|
|
298
|
+
}
|
|
299
|
+
function lenientLayer(raw) {
|
|
300
|
+
const layer = lenientEvidence(raw);
|
|
301
|
+
if (Array.isArray(layer.parts)) {
|
|
302
|
+
layer.parts = layer.parts.map((part) => {
|
|
303
|
+
const found = object(part);
|
|
304
|
+
if (!found)
|
|
305
|
+
return part;
|
|
306
|
+
const next = lenientEvidence(found);
|
|
307
|
+
if (Array.isArray(next.checks))
|
|
308
|
+
next.checks = next.checks.map((check) => { const c = object(check); return c ? lenientEvidence(c, ['steps', 'attempts']) : check; });
|
|
309
|
+
return next;
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
return layer;
|
|
313
|
+
}
|
|
314
|
+
/**
|
|
315
|
+
* Reads a layer manifest of any age and returns it, or the reason it cannot be read; it never throws. Layers written
|
|
316
|
+
* before `schemaVersion` existed are version 1. A layer of a newer major version is refused by name ("Upgrade
|
|
317
|
+
* @crvouga/atlas to read it") rather than half understood. Evidence the reader cannot read (a malformed attachment,
|
|
318
|
+
* step, annotation, link, tag, label or extension) is dropped and the check keeps its status and place; anything
|
|
319
|
+
* else that fails the schema refuses the whole layer, naming the first problem by its path. Callers treat a refused
|
|
320
|
+
* layer as unknown, never as passed.
|
|
321
|
+
*
|
|
322
|
+
* @see docs/report-contract.md
|
|
323
|
+
*
|
|
324
|
+
* @example
|
|
325
|
+
* ```ts
|
|
326
|
+
* import { parseLayerDocument } from '@crvouga/atlas/schema';
|
|
327
|
+
*
|
|
328
|
+
* const parsed = parseLayerDocument(JSON.parse(text));
|
|
329
|
+
* if (!parsed.ok) throw new Error(parsed.reason);
|
|
330
|
+
* for (const part of parsed.layer.parts) console.log(part.kind, part.checks.length);
|
|
331
|
+
* ```
|
|
332
|
+
*/
|
|
333
|
+
export function parseLayerDocument(raw) {
|
|
334
|
+
if (typeof raw !== 'object' || raw === null || Array.isArray(raw))
|
|
335
|
+
return { ok: false, reason: 'A layer is a JSON object' };
|
|
336
|
+
const version = raw.schemaVersion;
|
|
337
|
+
if (version !== undefined && version !== LAYER_SCHEMA_VERSION) {
|
|
338
|
+
return {
|
|
339
|
+
ok: false,
|
|
340
|
+
reason: typeof version === 'number' && version > LAYER_SCHEMA_VERSION
|
|
341
|
+
? `Written with layer schema version ${version}; this Atlas reads version ${LAYER_SCHEMA_VERSION}. Upgrade @crvouga/atlas to read it`
|
|
342
|
+
: `Unsupported layer schemaVersion ${JSON.stringify(version)}`
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
const parsed = LayerDocumentSchema.safeParse(lenientLayer({ ...raw, ...(version === undefined ? { schemaVersion: LAYER_SCHEMA_VERSION } : {}) }));
|
|
346
|
+
if (parsed.success)
|
|
347
|
+
return { ok: true, layer: parsed.data };
|
|
348
|
+
const issue = parsed.error.issues[0];
|
|
349
|
+
return { ok: false, reason: `Not a valid layer: ${issue ? `${issue.path.join('.') || '(root)'}: ${issue.message}` : 'unknown problem'}` };
|
|
350
|
+
}
|
|
351
|
+
/**
|
|
352
|
+
* An attachment as a producer hands it over, before the publisher stores its bytes. Give the bytes as
|
|
353
|
+
* `path` (a file, read by the CLI), `base64`, `data` (from code), or `text`; the publisher stores them in
|
|
354
|
+
* the layer, fills in `bundleKey`, `bytes` and `sha256`, and removes these four.
|
|
355
|
+
*/
|
|
356
|
+
export const AttachmentInputSchema = AttachmentSchema.extend({
|
|
357
|
+
path: z.string().optional().describe('A file to read, relative to the file this is in. Used by `atlas report publish`'),
|
|
358
|
+
base64: z.string().optional(),
|
|
359
|
+
data: z.custom((value) => value instanceof Uint8Array).optional().describe('From code: the bytes')
|
|
360
|
+
});
|
|
361
|
+
const StepInputSchema = StepSchema.extend({ attachments: z.array(AttachmentInputSchema).optional() });
|
|
362
|
+
const AttemptInputSchema = AttemptSchema.extend({ steps: z.array(StepInputSchema).optional(), attachments: z.array(AttachmentInputSchema).optional() });
|
|
363
|
+
const CheckInputSchema = CheckSchema.extend({
|
|
364
|
+
attachments: z.array(AttachmentInputSchema).optional(),
|
|
365
|
+
steps: z.array(StepInputSchema).optional(),
|
|
366
|
+
attempts: z.array(AttemptInputSchema).optional()
|
|
367
|
+
});
|
|
368
|
+
/**
|
|
369
|
+
* The checks of a layer as a producer hands them to the publisher (`publishChecks`, `atlas report publish --checks`),
|
|
370
|
+
* before the publisher fills in the rest: `id`, `schemaVersion`, the part around the checks and the bundle of
|
|
371
|
+
* attachment bytes. It is the whole integration surface of a producer that has no timelines to record. `kind` names
|
|
372
|
+
* the part and defaults to `checks`. Attachments may carry their bytes (`AttachmentInputSchema`). Emitted as
|
|
373
|
+
* `publish-checks.schema.json`.
|
|
374
|
+
*/
|
|
375
|
+
export const PublishChecksSchema = z.looseObject({
|
|
376
|
+
producer: z.string().min(1),
|
|
377
|
+
checks: z.array(CheckInputSchema),
|
|
378
|
+
kind: z.string().min(1).default('checks'),
|
|
379
|
+
title: z.string().optional(),
|
|
380
|
+
recordedAtMs: z.number().nonnegative(),
|
|
381
|
+
sha: z.string().nullable().default(null),
|
|
382
|
+
branch: z.string().nullable().default(null),
|
|
383
|
+
runUrl: z.string().nullable().default(null),
|
|
384
|
+
environment: z.string().nullable().optional(),
|
|
385
|
+
parameters: z.record(z.string(), z.string()).default({}),
|
|
386
|
+
freshness: z.enum(FRESHNESS).optional(),
|
|
387
|
+
specs: z.array(SpecEntrySchema).default([]),
|
|
388
|
+
tags: evidenceShape.tags,
|
|
389
|
+
labels: evidenceShape.labels,
|
|
390
|
+
links: evidenceShape.links,
|
|
391
|
+
attachments: z.array(AttachmentInputSchema).optional().describe('Attached to the whole layer: a CI log, a coverage report, a trace archive'),
|
|
392
|
+
annotations: evidenceShape.annotations,
|
|
393
|
+
extensions: evidenceShape.extensions
|
|
394
|
+
});
|
|
395
|
+
const CountsSchema = z.object({
|
|
396
|
+
passed: z.number().int().nonnegative(),
|
|
397
|
+
failed: z.number().int().nonnegative(),
|
|
398
|
+
running: z.number().int().nonnegative(),
|
|
399
|
+
'not-run': z.number().int().nonnegative(),
|
|
400
|
+
total: z.number().int().nonnegative()
|
|
401
|
+
});
|
|
402
|
+
/** The `schemaVersion` of the `ReportStatus` JSON, currently 1. */
|
|
403
|
+
export const REPORT_STATUS_SCHEMA_VERSION = 1;
|
|
404
|
+
/**
|
|
405
|
+
* What `atlas report status --format json` prints: the combined report in numbers, and whether it
|
|
406
|
+
* passes. `ok` is the gate: nothing failed, nothing is still running, nothing could not be read,
|
|
407
|
+
* and every required environment has results.
|
|
408
|
+
*/
|
|
409
|
+
export const ReportStatusSchema = z.looseObject({
|
|
410
|
+
schemaVersion: z.literal(REPORT_STATUS_SCHEMA_VERSION),
|
|
411
|
+
ok: z.boolean(),
|
|
412
|
+
reasons: z.array(z.string()).describe('Why `ok` is false, in words. Empty when it is true'),
|
|
413
|
+
checks: CountsSchema,
|
|
414
|
+
byEnvironment: z.record(z.string(), CountsSchema),
|
|
415
|
+
layers: z.number().int().nonnegative().describe('Distinct layers read, across all backends'),
|
|
416
|
+
backends: z.array(z.object({ id: z.string(), label: z.string(), available: z.boolean() })),
|
|
417
|
+
notices: z.array(z.string()).describe('What could not be read. Any notice fails the gate: unknown is not passed'),
|
|
418
|
+
specsWithoutEvidence: z.array(z.string()).describe('Specs of the outline with no result at all. The outline is `--specs <dir>`, or else the specs the layers recorded'),
|
|
419
|
+
specs: z.array(z.looseObject({
|
|
420
|
+
path: z.string().nullable().describe('Null for checks that name no spec in the outline'),
|
|
421
|
+
title: z.string(),
|
|
422
|
+
kind: z.string(),
|
|
423
|
+
checks: CountsSchema,
|
|
424
|
+
byEnvironment: z.record(z.string(), CountsSchema)
|
|
425
|
+
})).describe('The outcome of each spec, so a dashboard needs nothing else'),
|
|
426
|
+
failing: z.array(z.looseObject({
|
|
427
|
+
spec: z.string().nullable(),
|
|
428
|
+
check: z.string(),
|
|
429
|
+
title: z.string(),
|
|
430
|
+
status: z.enum(['failed', 'running']),
|
|
431
|
+
environment: z.string().nullable(),
|
|
432
|
+
producer: z.string(),
|
|
433
|
+
branch: z.string().nullable(),
|
|
434
|
+
sha: z.string().nullable(),
|
|
435
|
+
layer: z.string(),
|
|
436
|
+
backend: z.string(),
|
|
437
|
+
runUrl: z.string().nullable(),
|
|
438
|
+
detail: z.string()
|
|
439
|
+
})).describe('Each slot whose newest completed result failed, or that is still running')
|
|
440
|
+
});
|
|
441
|
+
/**
|
|
442
|
+
* The specs of one version of a project, as published: what the report is laid over. Layers of
|
|
443
|
+
* evidence say what ran; a spec set says what the specs were. Like layers, spec sets are immutable and
|
|
444
|
+
* never replace each other: the node shows the newest one of the branch you choose.
|
|
445
|
+
*/
|
|
446
|
+
export const SpecSetSchema = z.looseObject({
|
|
447
|
+
schemaVersion: z.literal(1),
|
|
448
|
+
id: z.string().regex(LAYER_ID).describe('SHA-256 of the branch, commit, freshness and the digest of every file: the same specs from the same commit are the same set'),
|
|
449
|
+
branch: z.string().nullable(),
|
|
450
|
+
sha: z.string().nullable(),
|
|
451
|
+
recordedAtMs: z.number().nonnegative().describe('For a clean checkout, the commit time; for a modified one, when it was published'),
|
|
452
|
+
freshness: z.enum(FRESHNESS).optional(),
|
|
453
|
+
files: z.array(z.looseObject({
|
|
454
|
+
path: z.string().min(1).describe('`specs/<path inside the specs directory>`, the name a check uses in `specPath`'),
|
|
455
|
+
digest: z.string().regex(LAYER_ID).describe('SHA-256 of the file. Its text is at layers/specs/blobs/<digest>.txt'),
|
|
456
|
+
fingerprint: z.string().describe('The advisory fingerprint layers record, to warn when a spec changed since they ran'),
|
|
457
|
+
kind: z.string(),
|
|
458
|
+
title: z.string(),
|
|
459
|
+
domain: z.string(),
|
|
460
|
+
bytes: z.number().int().nonnegative()
|
|
461
|
+
}))
|
|
462
|
+
});
|