@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,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,35 +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';
|
|
3
12
|
import type { LayerParameters } from './layer-types.js';
|
|
4
13
|
import type { ObjectStore } from './object-store.js';
|
|
5
14
|
/** What a running spec suite announces under `active/` in the local store. */
|
|
6
15
|
export type LiveRunRecord = {
|
|
16
|
+
/** The package directory the suite runs in, relative to the repository (`libs/chess`); with `bucket`, it names the source. */
|
|
7
17
|
readonly packageDir: string;
|
|
18
|
+
/** The suite's bucket within the package (`part-2`); where the run writes each finished test. */
|
|
8
19
|
readonly bucket: string;
|
|
20
|
+
/** When the run started, epoch milliseconds. A new value for the same source means a new run: tracking restarts. */
|
|
9
21
|
readonly startedAtMs: number;
|
|
22
|
+
/** How many checks the run expects to execute, when known. */
|
|
10
23
|
readonly plannedChecks?: number;
|
|
11
24
|
/** The client this suite is checking. A different client does not replace it. */
|
|
12
25
|
readonly environment?: string;
|
|
26
|
+
/** Other string choices that make the results their own slot; non-string or empty values are dropped on reading. */
|
|
13
27
|
readonly parameters?: LayerParameters;
|
|
14
28
|
};
|
|
15
|
-
/**
|
|
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
|
+
*/
|
|
16
34
|
export declare function liveRunRecord(value: unknown): LiveRunRecord | null;
|
|
17
35
|
/** How the host reaches the results a spec run has written so far. */
|
|
18
36
|
export type LiveSource = {
|
|
19
|
-
/** 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). */
|
|
20
38
|
readonly bucket: (run: LiveRunRecord) => ObjectStore | null;
|
|
21
|
-
/** 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. */
|
|
22
40
|
readonly origin?: () => Promise<{
|
|
23
41
|
readonly sha: string | null;
|
|
24
42
|
readonly branch: string | null;
|
|
25
43
|
}>;
|
|
26
|
-
/** The stylesheet the runs' snapshots render with. */
|
|
44
|
+
/** The stylesheet the runs' snapshots render with (served as `frame.css`). */
|
|
27
45
|
readonly frameCss?: () => Promise<string | null>;
|
|
28
46
|
};
|
|
47
|
+
/** The id of the temporary layer for a source: `live-<source>`. Not a content hash, unlike an archived layer's id. */
|
|
29
48
|
export declare function liveLayerId(source: string): string;
|
|
30
49
|
/**
|
|
31
|
-
*
|
|
50
|
+
* Creates the tracker of finished tests of runs that have not been collected into a layer yet, so a
|
|
32
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.
|
|
33
63
|
*/
|
|
34
64
|
export declare function createLiveEvidence(args: {
|
|
35
65
|
readonly live: LiveSource | undefined;
|
|
@@ -17,7 +17,11 @@ function readParameters(value) {
|
|
|
17
17
|
parameters[key] = item;
|
|
18
18
|
return parameters;
|
|
19
19
|
}
|
|
20
|
-
/**
|
|
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
|
+
*/
|
|
21
25
|
export function liveRunRecord(value) {
|
|
22
26
|
if (!value || typeof value !== 'object')
|
|
23
27
|
return null;
|
|
@@ -38,6 +42,7 @@ export function liveRunRecord(value) {
|
|
|
38
42
|
/** A finished run stays visible this long while its layer is being collected. */
|
|
39
43
|
const LINGER_MS = 30_000;
|
|
40
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. */
|
|
41
46
|
export function liveLayerId(source) {
|
|
42
47
|
return `live-${source}`;
|
|
43
48
|
}
|
|
@@ -50,8 +55,19 @@ function sourceOf(run) {
|
|
|
50
55
|
}
|
|
51
56
|
}
|
|
52
57
|
/**
|
|
53
|
-
*
|
|
58
|
+
* Creates the tracker of finished tests of runs that have not been collected into a layer yet, so a
|
|
54
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.
|
|
55
71
|
*/
|
|
56
72
|
export function createLiveEvidence(args) {
|
|
57
73
|
const tracked = new Map();
|
|
@@ -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,55 +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;
|
|
33
72
|
/** The client this layer checked. Null when the layer did not say. */
|
|
34
73
|
readonly environment: string | null;
|
|
74
|
+
/** Provenance: the CI run that made it. */
|
|
35
75
|
readonly runUrl: string | null;
|
|
76
|
+
/** Other string choices that make a result its own slot. */
|
|
36
77
|
readonly parameters: LayerParameters;
|
|
78
|
+
/** `modified`: recorded from a checkout with uncommitted changes; `unknown`: origin not captured; absent: clean. Advisory; never changes a status. */
|
|
37
79
|
readonly freshness?: 'unknown' | 'modified';
|
|
80
|
+
/** Where this layer's recorded timelines and captured files are read from, or null when it has none. */
|
|
38
81
|
readonly recording: RecordingAddress | null;
|
|
39
82
|
/** From a spec run that has not been collected into a layer yet. */
|
|
40
83
|
readonly live: boolean;
|
|
84
|
+
/** Its checks by status. Parts of kind `performance` are not counted when anything else was recorded. */
|
|
41
85
|
readonly counts: StatusCounts;
|
|
42
86
|
/** Equal for the mutable and the archived address of one execution. */
|
|
43
87
|
readonly execution: string;
|
|
88
|
+
/** What the run as a whole carries: its logs, links and labels. */
|
|
89
|
+
readonly evidence?: EvidenceSummary;
|
|
44
90
|
};
|
|
45
91
|
/** One check of one report part. */
|
|
46
92
|
export type LayerRow = {
|
|
93
|
+
/** The kind of the part holding the check (`behavior`, `performance`, ...). Part of the slot. */
|
|
47
94
|
readonly kind: string;
|
|
95
|
+
/** The check's stable id. Part of the slot. */
|
|
48
96
|
readonly id: string;
|
|
49
97
|
readonly title: string;
|
|
50
98
|
readonly status: EvidenceStatus;
|
|
99
|
+
/** The spec the check belongs to (`specs/<path>`), or null. */
|
|
51
100
|
readonly specPath: string | null;
|
|
101
|
+
/** Why it has this status, for a person; empty when there is nothing to add. */
|
|
52
102
|
readonly detail: string;
|
|
53
103
|
readonly measurements: readonly EvidenceMeasurement[];
|
|
54
104
|
/** The recorded test behind this check, when it has a timeline to open. */
|
|
55
105
|
readonly testId: string | null;
|
|
56
106
|
/** What distinguishes this recorded test from another run of the same check. */
|
|
57
107
|
readonly variant: LayerParameters;
|
|
108
|
+
/** How long the check took, milliseconds, or null when unknown. */
|
|
58
109
|
readonly durationMs: number | null;
|
|
110
|
+
/** What this result carries beyond its outcome. Absent when nothing. */
|
|
111
|
+
readonly evidence?: EvidenceSummary;
|
|
59
112
|
};
|
|
113
|
+
/** The checks of one layer, read separately from its header. */
|
|
60
114
|
export type LayerRows = {
|
|
115
|
+
/** The layer's id. */
|
|
61
116
|
readonly id: string;
|
|
62
117
|
readonly rows: readonly LayerRow[];
|
|
63
118
|
/** Spec path to the fingerprint the layer recorded for it. */
|
|
64
119
|
readonly fingerprints: Readonly<Record<string, string>>;
|
|
65
120
|
};
|
|
121
|
+
/** A layer's rows together with its header: everything composition needs to lay the layer over the specs. */
|
|
66
122
|
export type LayerEvidence = LayerRows & {
|
|
67
123
|
readonly layer: LayerHeader;
|
|
68
124
|
};
|
|
@@ -74,17 +130,28 @@ export type ActiveRun = {
|
|
|
74
130
|
readonly startedAtMs: number;
|
|
75
131
|
/** Spec files the run planned, when the producer recorded it. */
|
|
76
132
|
readonly plannedChecks?: number;
|
|
133
|
+
/** Checks finished so far: passed, failed or not-run. */
|
|
77
134
|
readonly completed: number;
|
|
135
|
+
/** Of those, how many failed. */
|
|
78
136
|
readonly failed: number;
|
|
79
137
|
};
|
|
138
|
+
/** One page of the layer listing the viewer reads. */
|
|
80
139
|
export type EvidencePage = {
|
|
140
|
+
/** The layers on this page, newest first. */
|
|
81
141
|
readonly layers: readonly LayerHeader[];
|
|
142
|
+
/** Opaque token for the next page, or null at the end. */
|
|
82
143
|
readonly cursor: string | null;
|
|
144
|
+
/** What could not be read; a gate treats any as unknown, never as a pass. */
|
|
83
145
|
readonly notices: readonly string[];
|
|
84
146
|
readonly backends: readonly BackendInfo[];
|
|
147
|
+
/** Runs writing results right now. */
|
|
85
148
|
readonly active: readonly ActiveRun[];
|
|
86
149
|
};
|
|
87
|
-
/**
|
|
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
|
+
*/
|
|
88
155
|
export declare function recordingPaths(address: RecordingAddress, testId: string): {
|
|
89
156
|
report: string;
|
|
90
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,78 +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
|
/**
|
|
46
74
|
* Sources left out of the combined report. An empty list means every layer
|
|
47
75
|
* on that axis contributes, including layers that arrive later.
|
|
48
76
|
*/
|
|
49
77
|
export type SourceSelection = {
|
|
78
|
+
/** Backend ids to leave out. */
|
|
50
79
|
readonly backends: readonly string[];
|
|
80
|
+
/** Branch names to leave out; `Unknown branch` stands for layers with no branch. */
|
|
51
81
|
readonly branches: readonly string[];
|
|
82
|
+
/** Commit shas to leave out; `unknown` stands for layers with no sha. */
|
|
52
83
|
readonly versions: readonly string[];
|
|
84
|
+
/** Environment slugs to leave out; `unspecified` stands for layers with none. */
|
|
53
85
|
readonly environments: readonly string[];
|
|
86
|
+
/** Layer ids to leave out. */
|
|
54
87
|
readonly layers: readonly string[];
|
|
55
88
|
};
|
|
89
|
+
/** The selection that leaves nothing out: every layer contributes. */
|
|
56
90
|
export declare const ALL_SOURCES: SourceSelection;
|
|
57
91
|
/** A saved selection from before versions and environments were axes still means "exclude nothing new". */
|
|
58
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. */
|
|
59
94
|
export declare function branchOf(layer: LayerHeader): string;
|
|
60
95
|
/** The commit a layer checked, or `unknown` when the layer did not record one. */
|
|
61
96
|
export declare function versionOf(layer: Pick<LayerHeader, 'sha'>): string;
|
|
62
97
|
/** The client a layer checked, or `unspecified` when it did not say. */
|
|
63
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. */
|
|
64
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. */
|
|
65
102
|
export type SourceFacet = {
|
|
66
103
|
readonly id: string;
|
|
67
104
|
readonly layers: number;
|
|
68
105
|
readonly latestAtMs: number;
|
|
69
106
|
};
|
|
107
|
+
/** Counts the layers per backend, branch, commit and environment, each list newest first (ties by name). */
|
|
70
108
|
export declare function sourceFacets(layers: readonly LayerHeader[]): {
|
|
71
109
|
backends: SourceFacet[];
|
|
72
110
|
branches: SourceFacet[];
|
|
73
111
|
versions: SourceFacet[];
|
|
74
112
|
environments: SourceFacet[];
|
|
75
113
|
};
|
|
114
|
+
/** One layer's record of a check. */
|
|
76
115
|
export type HistoryEntry = {
|
|
77
116
|
readonly layer: LayerHeader;
|
|
78
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,6 +86,7 @@ export function evidenceView(combined) {
|
|
|
79
86
|
}
|
|
80
87
|
return { specs, unassigned: checkSlots(combined.unassigned), total: addSummaries([...specs.values()].map(entry => entry.summary)) };
|
|
81
88
|
}
|
|
89
|
+
/** The selection that leaves nothing out: every layer contributes. */
|
|
82
90
|
export const ALL_SOURCES = { backends: [], branches: [], versions: [], environments: [], layers: [] };
|
|
83
91
|
/** A saved selection from before versions and environments were axes still means "exclude nothing new". */
|
|
84
92
|
export function sourceSelection(excluded) {
|
|
@@ -90,6 +98,7 @@ export function sourceSelection(excluded) {
|
|
|
90
98
|
layers: excluded?.layers ?? [],
|
|
91
99
|
};
|
|
92
100
|
}
|
|
101
|
+
/** The branch a layer was recorded on, or `Unknown branch` when it did not say. */
|
|
93
102
|
export function branchOf(layer) {
|
|
94
103
|
return layer.branch ?? 'Unknown branch';
|
|
95
104
|
}
|
|
@@ -101,6 +110,7 @@ export function versionOf(layer) {
|
|
|
101
110
|
export function environmentOf(layer) {
|
|
102
111
|
return layer.environment || 'unspecified';
|
|
103
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. */
|
|
104
114
|
export function contributes(layer, excluded) {
|
|
105
115
|
const selection = sourceSelection(excluded);
|
|
106
116
|
return !selection.backends.includes(layer.backend)
|
|
@@ -119,6 +129,7 @@ function facets(layers, id) {
|
|
|
119
129
|
}
|
|
120
130
|
return [...found].map(([key, facet]) => ({ id: key, ...facet })).sort((a, b) => b.latestAtMs - a.latestAtMs || (a.id < b.id ? -1 : 1));
|
|
121
131
|
}
|
|
132
|
+
/** Counts the layers per backend, branch, commit and environment, each list newest first (ties by name). */
|
|
122
133
|
export function sourceFacets(layers) {
|
|
123
134
|
return {
|
|
124
135
|
backends: facets(layers, layer => layer.backend),
|
|
@@ -1,50 +1,87 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/**
|
|
2
|
+
* @module Explorer data types
|
|
3
|
+
*
|
|
4
|
+
* The plain shapes the reports explorer shares between the store readers, the API routes and the viewer: a spec
|
|
5
|
+
* entry, a commit's spec inventory, the four result statuses, measurements, checks and parts. They are types only
|
|
6
|
+
* and carry no behaviour. The vocabulary matches the layer document (`docs/report-contract.md`): `status` is the
|
|
7
|
+
* closed set of four, everything else about a check is open.
|
|
8
|
+
*/
|
|
9
|
+
/** Commit-scoped definitions and independently produced evidence. One spec of the outline a report is laid over. */
|
|
2
10
|
export type SpecEntry = {
|
|
11
|
+
/** `specs/<path inside the specs directory>`. The `specPath` a check uses to say which spec it belongs to. */
|
|
3
12
|
readonly path: string;
|
|
13
|
+
/** Groups specs in the viewer (by default the project's folder name). */
|
|
4
14
|
readonly domain: string;
|
|
15
|
+
/** The notation: `gherkin`, `quint`, `dmn`, `alloy`, `openslo`, a file extension, or `document`. An open vocabulary. */
|
|
5
16
|
readonly kind: string;
|
|
6
17
|
readonly title: string;
|
|
18
|
+
/** A fingerprint of the spec's text, so a reader can warn when a spec changed since a layer recorded it. Opaque. */
|
|
7
19
|
readonly fingerprint?: string;
|
|
8
20
|
};
|
|
21
|
+
/** The specs of one commit. */
|
|
9
22
|
export type SpecInventory = {
|
|
23
|
+
/** The 40-hex-digit commit. */
|
|
10
24
|
readonly sha: string;
|
|
11
25
|
readonly entries: readonly SpecEntry[];
|
|
12
26
|
};
|
|
27
|
+
/** The four result statuses. The only closed vocabulary in the format: `not-run` is never a pass and `running` is not done. */
|
|
13
28
|
export type EvidenceStatus = 'passed' | 'failed' | 'running' | 'not-run';
|
|
29
|
+
/** A number a check recorded. Exceeding `limit` is shown and never fails the check by itself. */
|
|
14
30
|
export type EvidenceMeasurement = {
|
|
15
31
|
readonly name: string;
|
|
16
32
|
readonly value: number;
|
|
33
|
+
/** The unit of `value` and `limit`, such as `ms`. */
|
|
17
34
|
readonly unit: string;
|
|
35
|
+
/** The allowance for `value`, in the same unit, when there is one. */
|
|
18
36
|
readonly limit?: number;
|
|
19
37
|
};
|
|
20
|
-
|
|
38
|
+
/** What a check may carry beyond its outcome. All optional and open: see docs/extending.md. */
|
|
39
|
+
export type EvidenceExtras = Partial<Pick<import('../../schema/src/layer.js').Check, 'tags' | 'labels' | 'links' | 'steps' | 'attachments' | 'annotations' | 'attempts' | 'extensions'>>;
|
|
40
|
+
/** One check of a part, as recorded. */
|
|
41
|
+
export type EvidenceCheck = EvidenceExtras & {
|
|
42
|
+
/** Stable across runs; the newer result replaces the older one only if both used the same id. */
|
|
21
43
|
readonly id: string;
|
|
22
44
|
readonly title: string;
|
|
23
45
|
readonly status: EvidenceStatus;
|
|
46
|
+
/** Why the check has this status, for a person. */
|
|
24
47
|
readonly detail: string;
|
|
48
|
+
/** The spec the check belongs to, as `specs/<path>`. */
|
|
25
49
|
readonly specPath?: string;
|
|
26
50
|
readonly measurements: readonly EvidenceMeasurement[];
|
|
51
|
+
/** How long the check took, when the producer measured it. */
|
|
52
|
+
readonly durationMs?: number;
|
|
27
53
|
};
|
|
28
54
|
/** `kind` is an open producer key; new parts need no protocol change. */
|
|
29
|
-
export type ReportPart = {
|
|
55
|
+
export type ReportPart = EvidenceExtras & {
|
|
56
|
+
/** `modified`: recorded from a checkout with uncommitted changes; `unknown`: origin not captured. Advisory; never changes a status. */
|
|
30
57
|
readonly freshness?: 'unknown' | 'modified';
|
|
58
|
+
/** String choices that make a result its own slot (a device, a seed). */
|
|
31
59
|
readonly parameters?: Readonly<Record<string, string>>;
|
|
60
|
+
/** The commit the part was recorded at. */
|
|
32
61
|
readonly sha: string;
|
|
62
|
+
/** The part's id; when the part is published it is the `producer` of the layer made from it. */
|
|
33
63
|
readonly id: string;
|
|
64
|
+
/** Open category (`behavior`, `performance`, ...); part of the slot. */
|
|
34
65
|
readonly kind: string;
|
|
35
66
|
readonly title: string;
|
|
67
|
+
/** When the checks ran, epoch milliseconds. */
|
|
36
68
|
readonly recordedAtMs: number;
|
|
37
69
|
readonly checks: readonly EvidenceCheck[];
|
|
38
70
|
};
|
|
71
|
+
/** The part of a {@link ReportPart} that a spec run's evidence file carries: its `kind`, `title` and `checks`. */
|
|
39
72
|
export type ReportPartOutput = Pick<ReportPart, 'kind' | 'title' | 'checks'>;
|
|
73
|
+
/** What the explorer shows for one commit: its specs (or null when none were recorded) and the parts recorded against it. */
|
|
40
74
|
export type ExplorerData = {
|
|
41
75
|
readonly inventory: SpecInventory | null;
|
|
42
76
|
readonly parts: readonly ReportPart[];
|
|
77
|
+
/** A message to show instead of data, when something could not be read. */
|
|
43
78
|
readonly notice?: string;
|
|
44
79
|
};
|
|
80
|
+
/** The checkout a workspace reads from. */
|
|
45
81
|
export type WorkspaceVersion = {
|
|
46
82
|
readonly sha: string;
|
|
47
83
|
readonly branch: string;
|
|
84
|
+
/** True when the checkout has uncommitted changes. */
|
|
48
85
|
readonly dirty: boolean;
|
|
49
86
|
};
|
|
50
87
|
/** Only composition roots provide checkout content; it is never CI evidence. */
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module Explorer data types
|
|
3
|
+
*
|
|
4
|
+
* The plain shapes the reports explorer shares between the store readers, the API routes and the viewer: a spec
|
|
5
|
+
* entry, a commit's spec inventory, the four result statuses, measurements, checks and parts. They are types only
|
|
6
|
+
* and carry no behaviour. The vocabulary matches the layer document (`docs/report-contract.md`): `status` is the
|
|
7
|
+
* closed set of four, everything else about a check is open.
|
|
8
|
+
*/
|