@crvouga/atlas 1.2.0 → 1.3.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 +6 -0
- package/dist/reports/src/evidence-detail.d.ts +14 -3
- package/dist/reports/src/evidence-detail.js +33 -19
- package/dist/reports/src/evidence-scope.d.ts +83 -0
- package/dist/reports/src/evidence-scope.js +129 -0
- package/dist/reports/src/evidence-types.d.ts +3 -1
- package/dist/reports/src/explorer-types.d.ts +1 -1
- package/dist/reports/src/layer-merge.js +3 -2
- package/dist/reports/src/layer-types.d.ts +1 -1
- package/dist/reports/src/publish-checks.js +1 -0
- package/dist/reports/src/reporter.d.ts +1 -1
- package/dist/reports/src/reports-explorer-route.d.ts +276 -0
- package/dist/reports/src/reports-route.d.ts +276 -0
- package/dist/reports/src/test-catalog.d.ts +7 -1
- package/dist/reports/src/test-catalog.js +15 -3
- package/dist/schema/src/api.d.ts +520 -0
- package/dist/schema/src/api.js +7 -4
- package/dist/schema/src/layer.d.ts +981 -0
- package/dist/schema/src/layer.js +43 -4
- package/dist/server/src/openapi.js +1 -0
- package/docs/extending.md +1 -0
- package/docs/reference/http-api.md +2 -0
- package/docs/reference/reports.md +179 -7
- package/docs/reference/schema.md +23 -0
- package/docs/rich-evidence.md +47 -0
- package/llms-full.txt +50 -0
- package/package.json +1 -1
- package/packages/schema/schemas/check-detail.schema.json +673 -1
- package/packages/schema/schemas/effective-delta.schema.json +192 -0
- package/packages/schema/schemas/effective-evidence.schema.json +192 -0
- package/packages/schema/schemas/evidence-page.schema.json +96 -0
- package/packages/schema/schemas/layer-rows.schema.json +96 -0
- package/packages/schema/schemas/layer.schema.json +1598 -62
- package/packages/schema/schemas/publish-checks.schema.json +768 -0
- package/packages/schema/schemas/report-run.schema.json +96 -0
- package/packages/schema/schemas/test-catalog.schema.json +128 -0
- package/packages/schema/schemas/test-history.schema.json +202 -0
- package/packages/server/openapi.json +2581 -23
- package/packages/visualizer/src/evidence/artifacts.tsx +24 -18
- package/packages/visualizer/src/evidence/evidence-scope.tsx +106 -0
- package/packages/visualizer/src/evidence/format.ts +11 -0
- package/packages/visualizer/src/explorer/Explorer.tsx +19 -0
- package/packages/visualizer/src/explorer/search.ts +5 -1
|
@@ -105,6 +105,29 @@ export declare function createReportsApp(deps: ReportsAppDeps): import("hono/hon
|
|
|
105
105
|
} | null;
|
|
106
106
|
parts: readonly {
|
|
107
107
|
tags?: string[] | undefined;
|
|
108
|
+
scope?: {
|
|
109
|
+
[x: string]: import("hono/utils/types").JSONValue;
|
|
110
|
+
surface?: string | undefined;
|
|
111
|
+
deviceKind?: string | undefined;
|
|
112
|
+
syntheticData?: boolean | undefined;
|
|
113
|
+
dependencies?: string | undefined;
|
|
114
|
+
audioCaptured?: boolean | undefined;
|
|
115
|
+
pacing?: string | undefined;
|
|
116
|
+
asserted?: string[] | undefined;
|
|
117
|
+
limitations?: string[] | undefined;
|
|
118
|
+
declaredBy?: string | undefined;
|
|
119
|
+
verified?: string[] | undefined;
|
|
120
|
+
verifiedBy?: string | undefined;
|
|
121
|
+
provenance?: {
|
|
122
|
+
[x: string]: import("hono/utils/types").JSONValue;
|
|
123
|
+
url: string;
|
|
124
|
+
name?: string | undefined;
|
|
125
|
+
rel?: string | undefined;
|
|
126
|
+
}[] | undefined;
|
|
127
|
+
extensions?: {
|
|
128
|
+
[x: string]: import("hono/utils/types").JSONValue;
|
|
129
|
+
} | undefined;
|
|
130
|
+
} | undefined;
|
|
108
131
|
attachments?: {
|
|
109
132
|
[x: string]: import("hono/utils/types").JSONValue;
|
|
110
133
|
name: string;
|
|
@@ -121,6 +144,29 @@ export declare function createReportsApp(deps: ReportsAppDeps): import("hono/hon
|
|
|
121
144
|
durationMs?: number | undefined;
|
|
122
145
|
description?: string | undefined;
|
|
123
146
|
omitted?: string | undefined;
|
|
147
|
+
scope?: {
|
|
148
|
+
[x: string]: import("hono/utils/types").JSONValue;
|
|
149
|
+
surface?: string | undefined;
|
|
150
|
+
deviceKind?: string | undefined;
|
|
151
|
+
syntheticData?: boolean | undefined;
|
|
152
|
+
dependencies?: string | undefined;
|
|
153
|
+
audioCaptured?: boolean | undefined;
|
|
154
|
+
pacing?: string | undefined;
|
|
155
|
+
asserted?: string[] | undefined;
|
|
156
|
+
limitations?: string[] | undefined;
|
|
157
|
+
declaredBy?: string | undefined;
|
|
158
|
+
verified?: string[] | undefined;
|
|
159
|
+
verifiedBy?: string | undefined;
|
|
160
|
+
provenance?: {
|
|
161
|
+
[x: string]: import("hono/utils/types").JSONValue;
|
|
162
|
+
url: string;
|
|
163
|
+
name?: string | undefined;
|
|
164
|
+
rel?: string | undefined;
|
|
165
|
+
}[] | undefined;
|
|
166
|
+
extensions?: {
|
|
167
|
+
[x: string]: import("hono/utils/types").JSONValue;
|
|
168
|
+
} | undefined;
|
|
169
|
+
} | undefined;
|
|
124
170
|
tags?: string[] | undefined;
|
|
125
171
|
at?: {
|
|
126
172
|
[x: string]: import("hono/utils/types").JSONValue;
|
|
@@ -140,6 +186,29 @@ export declare function createReportsApp(deps: ReportsAppDeps): import("hono/hon
|
|
|
140
186
|
durationMs?: number | undefined;
|
|
141
187
|
startedAtMs?: number | undefined;
|
|
142
188
|
detail?: string | undefined;
|
|
189
|
+
scope?: {
|
|
190
|
+
[x: string]: import("hono/utils/types").JSONValue;
|
|
191
|
+
surface?: string | undefined;
|
|
192
|
+
deviceKind?: string | undefined;
|
|
193
|
+
syntheticData?: boolean | undefined;
|
|
194
|
+
dependencies?: string | undefined;
|
|
195
|
+
audioCaptured?: boolean | undefined;
|
|
196
|
+
pacing?: string | undefined;
|
|
197
|
+
asserted?: string[] | undefined;
|
|
198
|
+
limitations?: string[] | undefined;
|
|
199
|
+
declaredBy?: string | undefined;
|
|
200
|
+
verified?: string[] | undefined;
|
|
201
|
+
verifiedBy?: string | undefined;
|
|
202
|
+
provenance?: {
|
|
203
|
+
[x: string]: import("hono/utils/types").JSONValue;
|
|
204
|
+
url: string;
|
|
205
|
+
name?: string | undefined;
|
|
206
|
+
rel?: string | undefined;
|
|
207
|
+
}[] | undefined;
|
|
208
|
+
extensions?: {
|
|
209
|
+
[x: string]: import("hono/utils/types").JSONValue;
|
|
210
|
+
} | undefined;
|
|
211
|
+
} | undefined;
|
|
143
212
|
steps?: {
|
|
144
213
|
[x: string]: import("hono/utils/types").JSONValue;
|
|
145
214
|
name: string;
|
|
@@ -166,6 +235,29 @@ export declare function createReportsApp(deps: ReportsAppDeps): import("hono/hon
|
|
|
166
235
|
durationMs?: number | undefined;
|
|
167
236
|
description?: string | undefined;
|
|
168
237
|
omitted?: string | undefined;
|
|
238
|
+
scope?: {
|
|
239
|
+
[x: string]: import("hono/utils/types").JSONValue;
|
|
240
|
+
surface?: string | undefined;
|
|
241
|
+
deviceKind?: string | undefined;
|
|
242
|
+
syntheticData?: boolean | undefined;
|
|
243
|
+
dependencies?: string | undefined;
|
|
244
|
+
audioCaptured?: boolean | undefined;
|
|
245
|
+
pacing?: string | undefined;
|
|
246
|
+
asserted?: string[] | undefined;
|
|
247
|
+
limitations?: string[] | undefined;
|
|
248
|
+
declaredBy?: string | undefined;
|
|
249
|
+
verified?: string[] | undefined;
|
|
250
|
+
verifiedBy?: string | undefined;
|
|
251
|
+
provenance?: {
|
|
252
|
+
[x: string]: import("hono/utils/types").JSONValue;
|
|
253
|
+
url: string;
|
|
254
|
+
name?: string | undefined;
|
|
255
|
+
rel?: string | undefined;
|
|
256
|
+
}[] | undefined;
|
|
257
|
+
extensions?: {
|
|
258
|
+
[x: string]: import("hono/utils/types").JSONValue;
|
|
259
|
+
} | undefined;
|
|
260
|
+
} | undefined;
|
|
169
261
|
tags?: string[] | undefined;
|
|
170
262
|
at?: {
|
|
171
263
|
[x: string]: import("hono/utils/types").JSONValue;
|
|
@@ -224,6 +316,29 @@ export declare function createReportsApp(deps: ReportsAppDeps): import("hono/hon
|
|
|
224
316
|
durationMs?: number | undefined;
|
|
225
317
|
description?: string | undefined;
|
|
226
318
|
omitted?: string | undefined;
|
|
319
|
+
scope?: {
|
|
320
|
+
[x: string]: import("hono/utils/types").JSONValue;
|
|
321
|
+
surface?: string | undefined;
|
|
322
|
+
deviceKind?: string | undefined;
|
|
323
|
+
syntheticData?: boolean | undefined;
|
|
324
|
+
dependencies?: string | undefined;
|
|
325
|
+
audioCaptured?: boolean | undefined;
|
|
326
|
+
pacing?: string | undefined;
|
|
327
|
+
asserted?: string[] | undefined;
|
|
328
|
+
limitations?: string[] | undefined;
|
|
329
|
+
declaredBy?: string | undefined;
|
|
330
|
+
verified?: string[] | undefined;
|
|
331
|
+
verifiedBy?: string | undefined;
|
|
332
|
+
provenance?: {
|
|
333
|
+
[x: string]: import("hono/utils/types").JSONValue;
|
|
334
|
+
url: string;
|
|
335
|
+
name?: string | undefined;
|
|
336
|
+
rel?: string | undefined;
|
|
337
|
+
}[] | undefined;
|
|
338
|
+
extensions?: {
|
|
339
|
+
[x: string]: import("hono/utils/types").JSONValue;
|
|
340
|
+
} | undefined;
|
|
341
|
+
} | undefined;
|
|
227
342
|
tags?: string[] | undefined;
|
|
228
343
|
at?: {
|
|
229
344
|
[x: string]: import("hono/utils/types").JSONValue;
|
|
@@ -292,6 +407,29 @@ export declare function createReportsApp(deps: ReportsAppDeps): import("hono/hon
|
|
|
292
407
|
durationMs?: number | undefined;
|
|
293
408
|
description?: string | undefined;
|
|
294
409
|
omitted?: string | undefined;
|
|
410
|
+
scope?: {
|
|
411
|
+
[x: string]: import("hono/utils/types").JSONValue;
|
|
412
|
+
surface?: string | undefined;
|
|
413
|
+
deviceKind?: string | undefined;
|
|
414
|
+
syntheticData?: boolean | undefined;
|
|
415
|
+
dependencies?: string | undefined;
|
|
416
|
+
audioCaptured?: boolean | undefined;
|
|
417
|
+
pacing?: string | undefined;
|
|
418
|
+
asserted?: string[] | undefined;
|
|
419
|
+
limitations?: string[] | undefined;
|
|
420
|
+
declaredBy?: string | undefined;
|
|
421
|
+
verified?: string[] | undefined;
|
|
422
|
+
verifiedBy?: string | undefined;
|
|
423
|
+
provenance?: {
|
|
424
|
+
[x: string]: import("hono/utils/types").JSONValue;
|
|
425
|
+
url: string;
|
|
426
|
+
name?: string | undefined;
|
|
427
|
+
rel?: string | undefined;
|
|
428
|
+
}[] | undefined;
|
|
429
|
+
extensions?: {
|
|
430
|
+
[x: string]: import("hono/utils/types").JSONValue;
|
|
431
|
+
} | undefined;
|
|
432
|
+
} | undefined;
|
|
295
433
|
tags?: string[] | undefined;
|
|
296
434
|
at?: {
|
|
297
435
|
[x: string]: import("hono/utils/types").JSONValue;
|
|
@@ -383,6 +521,29 @@ export declare function createReportsApp(deps: ReportsAppDeps): import("hono/hon
|
|
|
383
521
|
readonly recordedAtMs: number;
|
|
384
522
|
readonly checks: readonly {
|
|
385
523
|
tags?: string[] | undefined;
|
|
524
|
+
scope?: {
|
|
525
|
+
[x: string]: import("hono/utils/types").JSONValue;
|
|
526
|
+
surface?: string | undefined;
|
|
527
|
+
deviceKind?: string | undefined;
|
|
528
|
+
syntheticData?: boolean | undefined;
|
|
529
|
+
dependencies?: string | undefined;
|
|
530
|
+
audioCaptured?: boolean | undefined;
|
|
531
|
+
pacing?: string | undefined;
|
|
532
|
+
asserted?: string[] | undefined;
|
|
533
|
+
limitations?: string[] | undefined;
|
|
534
|
+
declaredBy?: string | undefined;
|
|
535
|
+
verified?: string[] | undefined;
|
|
536
|
+
verifiedBy?: string | undefined;
|
|
537
|
+
provenance?: {
|
|
538
|
+
[x: string]: import("hono/utils/types").JSONValue;
|
|
539
|
+
url: string;
|
|
540
|
+
name?: string | undefined;
|
|
541
|
+
rel?: string | undefined;
|
|
542
|
+
}[] | undefined;
|
|
543
|
+
extensions?: {
|
|
544
|
+
[x: string]: import("hono/utils/types").JSONValue;
|
|
545
|
+
} | undefined;
|
|
546
|
+
} | undefined;
|
|
386
547
|
attachments?: {
|
|
387
548
|
[x: string]: import("hono/utils/types").JSONValue;
|
|
388
549
|
name: string;
|
|
@@ -399,6 +560,29 @@ export declare function createReportsApp(deps: ReportsAppDeps): import("hono/hon
|
|
|
399
560
|
durationMs?: number | undefined;
|
|
400
561
|
description?: string | undefined;
|
|
401
562
|
omitted?: string | undefined;
|
|
563
|
+
scope?: {
|
|
564
|
+
[x: string]: import("hono/utils/types").JSONValue;
|
|
565
|
+
surface?: string | undefined;
|
|
566
|
+
deviceKind?: string | undefined;
|
|
567
|
+
syntheticData?: boolean | undefined;
|
|
568
|
+
dependencies?: string | undefined;
|
|
569
|
+
audioCaptured?: boolean | undefined;
|
|
570
|
+
pacing?: string | undefined;
|
|
571
|
+
asserted?: string[] | undefined;
|
|
572
|
+
limitations?: string[] | undefined;
|
|
573
|
+
declaredBy?: string | undefined;
|
|
574
|
+
verified?: string[] | undefined;
|
|
575
|
+
verifiedBy?: string | undefined;
|
|
576
|
+
provenance?: {
|
|
577
|
+
[x: string]: import("hono/utils/types").JSONValue;
|
|
578
|
+
url: string;
|
|
579
|
+
name?: string | undefined;
|
|
580
|
+
rel?: string | undefined;
|
|
581
|
+
}[] | undefined;
|
|
582
|
+
extensions?: {
|
|
583
|
+
[x: string]: import("hono/utils/types").JSONValue;
|
|
584
|
+
} | undefined;
|
|
585
|
+
} | undefined;
|
|
402
586
|
tags?: string[] | undefined;
|
|
403
587
|
at?: {
|
|
404
588
|
[x: string]: import("hono/utils/types").JSONValue;
|
|
@@ -418,6 +602,29 @@ export declare function createReportsApp(deps: ReportsAppDeps): import("hono/hon
|
|
|
418
602
|
durationMs?: number | undefined;
|
|
419
603
|
startedAtMs?: number | undefined;
|
|
420
604
|
detail?: string | undefined;
|
|
605
|
+
scope?: {
|
|
606
|
+
[x: string]: import("hono/utils/types").JSONValue;
|
|
607
|
+
surface?: string | undefined;
|
|
608
|
+
deviceKind?: string | undefined;
|
|
609
|
+
syntheticData?: boolean | undefined;
|
|
610
|
+
dependencies?: string | undefined;
|
|
611
|
+
audioCaptured?: boolean | undefined;
|
|
612
|
+
pacing?: string | undefined;
|
|
613
|
+
asserted?: string[] | undefined;
|
|
614
|
+
limitations?: string[] | undefined;
|
|
615
|
+
declaredBy?: string | undefined;
|
|
616
|
+
verified?: string[] | undefined;
|
|
617
|
+
verifiedBy?: string | undefined;
|
|
618
|
+
provenance?: {
|
|
619
|
+
[x: string]: import("hono/utils/types").JSONValue;
|
|
620
|
+
url: string;
|
|
621
|
+
name?: string | undefined;
|
|
622
|
+
rel?: string | undefined;
|
|
623
|
+
}[] | undefined;
|
|
624
|
+
extensions?: {
|
|
625
|
+
[x: string]: import("hono/utils/types").JSONValue;
|
|
626
|
+
} | undefined;
|
|
627
|
+
} | undefined;
|
|
421
628
|
steps?: {
|
|
422
629
|
[x: string]: import("hono/utils/types").JSONValue;
|
|
423
630
|
name: string;
|
|
@@ -444,6 +651,29 @@ export declare function createReportsApp(deps: ReportsAppDeps): import("hono/hon
|
|
|
444
651
|
durationMs?: number | undefined;
|
|
445
652
|
description?: string | undefined;
|
|
446
653
|
omitted?: string | undefined;
|
|
654
|
+
scope?: {
|
|
655
|
+
[x: string]: import("hono/utils/types").JSONValue;
|
|
656
|
+
surface?: string | undefined;
|
|
657
|
+
deviceKind?: string | undefined;
|
|
658
|
+
syntheticData?: boolean | undefined;
|
|
659
|
+
dependencies?: string | undefined;
|
|
660
|
+
audioCaptured?: boolean | undefined;
|
|
661
|
+
pacing?: string | undefined;
|
|
662
|
+
asserted?: string[] | undefined;
|
|
663
|
+
limitations?: string[] | undefined;
|
|
664
|
+
declaredBy?: string | undefined;
|
|
665
|
+
verified?: string[] | undefined;
|
|
666
|
+
verifiedBy?: string | undefined;
|
|
667
|
+
provenance?: {
|
|
668
|
+
[x: string]: import("hono/utils/types").JSONValue;
|
|
669
|
+
url: string;
|
|
670
|
+
name?: string | undefined;
|
|
671
|
+
rel?: string | undefined;
|
|
672
|
+
}[] | undefined;
|
|
673
|
+
extensions?: {
|
|
674
|
+
[x: string]: import("hono/utils/types").JSONValue;
|
|
675
|
+
} | undefined;
|
|
676
|
+
} | undefined;
|
|
447
677
|
tags?: string[] | undefined;
|
|
448
678
|
at?: {
|
|
449
679
|
[x: string]: import("hono/utils/types").JSONValue;
|
|
@@ -502,6 +732,29 @@ export declare function createReportsApp(deps: ReportsAppDeps): import("hono/hon
|
|
|
502
732
|
durationMs?: number | undefined;
|
|
503
733
|
description?: string | undefined;
|
|
504
734
|
omitted?: string | undefined;
|
|
735
|
+
scope?: {
|
|
736
|
+
[x: string]: import("hono/utils/types").JSONValue;
|
|
737
|
+
surface?: string | undefined;
|
|
738
|
+
deviceKind?: string | undefined;
|
|
739
|
+
syntheticData?: boolean | undefined;
|
|
740
|
+
dependencies?: string | undefined;
|
|
741
|
+
audioCaptured?: boolean | undefined;
|
|
742
|
+
pacing?: string | undefined;
|
|
743
|
+
asserted?: string[] | undefined;
|
|
744
|
+
limitations?: string[] | undefined;
|
|
745
|
+
declaredBy?: string | undefined;
|
|
746
|
+
verified?: string[] | undefined;
|
|
747
|
+
verifiedBy?: string | undefined;
|
|
748
|
+
provenance?: {
|
|
749
|
+
[x: string]: import("hono/utils/types").JSONValue;
|
|
750
|
+
url: string;
|
|
751
|
+
name?: string | undefined;
|
|
752
|
+
rel?: string | undefined;
|
|
753
|
+
}[] | undefined;
|
|
754
|
+
extensions?: {
|
|
755
|
+
[x: string]: import("hono/utils/types").JSONValue;
|
|
756
|
+
} | undefined;
|
|
757
|
+
} | undefined;
|
|
505
758
|
tags?: string[] | undefined;
|
|
506
759
|
at?: {
|
|
507
760
|
[x: string]: import("hono/utils/types").JSONValue;
|
|
@@ -570,6 +823,29 @@ export declare function createReportsApp(deps: ReportsAppDeps): import("hono/hon
|
|
|
570
823
|
durationMs?: number | undefined;
|
|
571
824
|
description?: string | undefined;
|
|
572
825
|
omitted?: string | undefined;
|
|
826
|
+
scope?: {
|
|
827
|
+
[x: string]: import("hono/utils/types").JSONValue;
|
|
828
|
+
surface?: string | undefined;
|
|
829
|
+
deviceKind?: string | undefined;
|
|
830
|
+
syntheticData?: boolean | undefined;
|
|
831
|
+
dependencies?: string | undefined;
|
|
832
|
+
audioCaptured?: boolean | undefined;
|
|
833
|
+
pacing?: string | undefined;
|
|
834
|
+
asserted?: string[] | undefined;
|
|
835
|
+
limitations?: string[] | undefined;
|
|
836
|
+
declaredBy?: string | undefined;
|
|
837
|
+
verified?: string[] | undefined;
|
|
838
|
+
verifiedBy?: string | undefined;
|
|
839
|
+
provenance?: {
|
|
840
|
+
[x: string]: import("hono/utils/types").JSONValue;
|
|
841
|
+
url: string;
|
|
842
|
+
name?: string | undefined;
|
|
843
|
+
rel?: string | undefined;
|
|
844
|
+
}[] | undefined;
|
|
845
|
+
extensions?: {
|
|
846
|
+
[x: string]: import("hono/utils/types").JSONValue;
|
|
847
|
+
} | undefined;
|
|
848
|
+
} | undefined;
|
|
573
849
|
tags?: string[] | undefined;
|
|
574
850
|
at?: {
|
|
575
851
|
[x: string]: import("hono/utils/types").JSONValue;
|
|
@@ -99,6 +99,8 @@ export type TestSummary = {
|
|
|
99
99
|
readonly artifactTags?: readonly string[];
|
|
100
100
|
/** The normalized tags its attachments inherit from their check and run (those of the results that have attachments), apart from `artifactTags`. What an inherited artifact query also matches. */
|
|
101
101
|
readonly inheritedArtifactTags?: readonly string[];
|
|
102
|
+
/** What each distinct playable recording of its current results declares it shows, as filter tokens (`device:physical`, `audio:silent`; see `scopeTokens`). A recording that declared nothing is `['unknown']`. Absent when it has no playable recording. A query's `scope` must hold for one recording, so evidence from two different devices never combines. */
|
|
103
|
+
readonly scopeProfiles?: readonly (readonly string[])[];
|
|
102
104
|
readonly labels: Readonly<Record<string, string>>;
|
|
103
105
|
/** What its current results carry: attachments, steps, retries, and the kinds of attachment. */
|
|
104
106
|
readonly evidence: {
|
|
@@ -172,6 +174,8 @@ export type CatalogFacets = {
|
|
|
172
174
|
readonly tags: readonly FacetValue[];
|
|
173
175
|
/** The tags of attachments, normalized. Absent in documents of older servers. */
|
|
174
176
|
readonly artifactTags?: readonly FacetValue[];
|
|
177
|
+
/** The scope tokens of its recordings (`device:simulator`, `audio:recorded`, `unknown`), with how many tests have a recording that states each. Absent in documents of older servers. */
|
|
178
|
+
readonly scopes?: readonly FacetValue[];
|
|
175
179
|
readonly branches: readonly FacetValue[];
|
|
176
180
|
readonly labels: Readonly<Record<string, readonly FacetValue[]>>;
|
|
177
181
|
};
|
|
@@ -195,6 +199,8 @@ export type TestQuery = {
|
|
|
195
199
|
readonly artifactTag?: readonly string[];
|
|
196
200
|
/** With `artifactTag`: also match the tags an attachment inherits from its check and run (a recording without a tag of its own under a run tagged `demo`). Default false: the exact query. */
|
|
197
201
|
readonly inherited?: boolean;
|
|
202
|
+
/** Only tests with one playable recording that states every one of these (`device:physical`, `audio:recorded`, `data:synthetic`, `dependencies:live`, `surface:browser`, `pacing:paced`; see `scopeTokens`). Evidence that did not declare a value never matches it; `unknown` matches only a recording that declared no scope. */
|
|
203
|
+
readonly scope?: readonly string[];
|
|
198
204
|
/** Only tests recorded in this run (a key from `runList`). */
|
|
199
205
|
readonly run?: string;
|
|
200
206
|
readonly sort?: TestSort;
|
|
@@ -499,7 +505,7 @@ export declare function artifactList(history: TestHistory, version: string, quer
|
|
|
499
505
|
}): ArtifactList;
|
|
500
506
|
/** One run with every result it recorded, or null when no run has that key. */
|
|
501
507
|
export declare function runDetail(history: TestHistory, version: string, key: string): RunDetail | null;
|
|
502
|
-
/** Reads a `TestQuery` from URL search parameters: `q`, `sort`, `offset`, `limit`, `run`, and repeatable or comma-separated `status`, `environment`, `producer`, `domain`, `spec`, `tag`, `artifactTag`, and `inherited=1`. */
|
|
508
|
+
/** Reads a `TestQuery` from URL search parameters: `q`, `sort`, `offset`, `limit`, `run`, and repeatable or comma-separated `status`, `environment`, `producer`, `domain`, `spec`, `tag`, `artifactTag`, `scope` (every token must hold for one recording), and `inherited=1`. */
|
|
503
509
|
export declare function parseTestQuery(params: URLSearchParams): TestQuery;
|
|
504
510
|
/** A duration for a person: `850 ms`, `12.3 s`, `4m 05s`. */
|
|
505
511
|
export declare function formatMs(ms: number | null | undefined): string;
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
import { displayTag, normalizeTag, normalizeTags } from './artifact-tags.js';
|
|
26
26
|
import { facetsOf, forRuns, labelList, matchesFilter, outcomeOfCounts, splitList, usedFilters } from './artifact-query.js';
|
|
27
27
|
import { videoCounts } from './evidence-detail.js';
|
|
28
|
+
import { matchesScope, scopeTokens, UNKNOWN_SCOPE } from './evidence-scope.js';
|
|
28
29
|
import { checkSlots } from './evidence-view.js';
|
|
29
30
|
import { combineEvidence, emptyCounts, slotKey, specPath as normalSpecPath } from './layer-merge.js';
|
|
30
31
|
import { RecordedSpecs } from './recorded-specs.js';
|
|
@@ -448,10 +449,16 @@ function summaryOf(slot, history, known) {
|
|
|
448
449
|
const labels = {};
|
|
449
450
|
let attachments = 0, steps = 0, attempts = 0;
|
|
450
451
|
const roles = new Set();
|
|
452
|
+
const profiles = new Map();
|
|
451
453
|
for (const [at, outcome] of outcomes.entries()) {
|
|
452
454
|
const evidence = outcome.evidence;
|
|
453
455
|
if (!evidence)
|
|
454
456
|
continue;
|
|
457
|
+
for (const video of evidence.videos ?? [])
|
|
458
|
+
if (video.available) {
|
|
459
|
+
const tokens = scopeTokens(video.scope);
|
|
460
|
+
profiles.set(tokens.join('\n'), tokens);
|
|
461
|
+
}
|
|
455
462
|
if (evidence.attachments > 0)
|
|
456
463
|
for (const tag of normalizeTags([...evidence.tags, ...(slot.outcomes[at]?.layer.evidence?.tags ?? [])]))
|
|
457
464
|
inheritedTags.add(tag);
|
|
@@ -481,6 +488,7 @@ function summaryOf(slot, history, known) {
|
|
|
481
488
|
tags: tagList,
|
|
482
489
|
...(artifactTags.size ? { artifactTags: [...artifactTags].sort() } : {}),
|
|
483
490
|
...([...inheritedTags].some(tag => !artifactTags.has(tag)) ? { inheritedArtifactTags: [...inheritedTags].filter(tag => !artifactTags.has(tag)).sort() } : {}),
|
|
491
|
+
...(profiles.size ? { scopeProfiles: [...profiles.values()] } : {}),
|
|
484
492
|
labels,
|
|
485
493
|
evidence: { attachments, steps, attempts, roles: [...roles].sort() },
|
|
486
494
|
lastRecordedAtMs: slot.latestAtMs,
|
|
@@ -490,7 +498,7 @@ function summaryOf(slot, history, known) {
|
|
|
490
498
|
/** The lowercase text a search matches a test against: title, id, spec, domain, tags, labels, environments and producers. */
|
|
491
499
|
export function haystackOf(test) {
|
|
492
500
|
return [
|
|
493
|
-
test.title, test.id, test.specPath ?? '', test.spec?.title ?? '', test.spec?.domain ?? '', ...test.tags, ...(test.artifactTags ?? []).map(displayTag),
|
|
501
|
+
test.title, test.id, test.specPath ?? '', test.spec?.title ?? '', test.spec?.domain ?? '', ...test.tags, ...(test.artifactTags ?? []).map(displayTag), ...(test.scopeProfiles ?? []).flat().filter(token => token !== UNKNOWN_SCOPE),
|
|
494
502
|
...Object.entries(test.labels).flatMap(([key, value]) => [key, value]), ...test.environments, ...test.producers,
|
|
495
503
|
].join('\n').toLowerCase();
|
|
496
504
|
}
|
|
@@ -597,6 +605,7 @@ export function catalogModel(layers, sources = {}) {
|
|
|
597
605
|
specs: [...specGroups].map(([, list]) => { const g = group(list[0].spec.path, list); return { ...list[0].spec, tests: g.tests, counts: g.counts }; }).sort((a, b) => compareText(a.domain, b.domain) || compareText(a.title, b.title)),
|
|
598
606
|
tags: facet(tests, test => test.tags),
|
|
599
607
|
artifactTags: facet(tests, test => test.artifactTags ?? []),
|
|
608
|
+
scopes: facet(tests, test => (test.scopeProfiles ?? []).flat()),
|
|
600
609
|
branches: facet(tests, test => test.stats.branches),
|
|
601
610
|
labels: Object.fromEntries([...labelKeys].sort().map(key => [key, facet(tests, test => (test.labels[key] === undefined ? [] : [test.labels[key]]))])),
|
|
602
611
|
};
|
|
@@ -633,6 +642,8 @@ export function matchesQuery(model, test, query, inRun) {
|
|
|
633
642
|
return false;
|
|
634
643
|
if (query.artifactTag?.length && !query.artifactTag.some(tag => { const wanted = normalizeTag(tag); return wanted !== null && ((test.artifactTags?.includes(wanted) ?? false) || (query.inherited === true && (test.inheritedArtifactTags?.includes(wanted) ?? false))); }))
|
|
635
644
|
return false;
|
|
645
|
+
if (query.scope?.length && !(test.scopeProfiles ?? []).some(tokens => matchesScope(tokens, query.scope)))
|
|
646
|
+
return false;
|
|
636
647
|
if (inRun && !inRun.has(test.key))
|
|
637
648
|
return false;
|
|
638
649
|
return true;
|
|
@@ -725,7 +736,7 @@ export function runDetail(history, version, key) {
|
|
|
725
736
|
const found = history.run(key);
|
|
726
737
|
return found ? { schemaVersion: TEST_CATALOG_VERSION, version, ...found } : null;
|
|
727
738
|
}
|
|
728
|
-
/** Reads a `TestQuery` from URL search parameters: `q`, `sort`, `offset`, `limit`, `run`, and repeatable or comma-separated `status`, `environment`, `producer`, `domain`, `spec`, `tag`, `artifactTag`, and `inherited=1`. */
|
|
739
|
+
/** Reads a `TestQuery` from URL search parameters: `q`, `sort`, `offset`, `limit`, `run`, and repeatable or comma-separated `status`, `environment`, `producer`, `domain`, `spec`, `tag`, `artifactTag`, `scope` (every token must hold for one recording), and `inherited=1`. */
|
|
729
740
|
export function parseTestQuery(params) {
|
|
730
741
|
const many = (name) => params.getAll(name).flatMap(splitList);
|
|
731
742
|
const number = (name) => { const value = params.get(name); return value !== null && /^\d+$/.test(value) ? Number(value) : undefined; };
|
|
@@ -734,7 +745,7 @@ export function parseTestQuery(params) {
|
|
|
734
745
|
const q = params.get('q');
|
|
735
746
|
if (q)
|
|
736
747
|
query.q = q;
|
|
737
|
-
for (const name of ['status', 'environment', 'producer', 'domain', 'spec', 'tag', 'artifactTag']) {
|
|
748
|
+
for (const name of ['status', 'environment', 'producer', 'domain', 'spec', 'tag', 'artifactTag', 'scope']) {
|
|
738
749
|
const values = many(name);
|
|
739
750
|
if (values.length)
|
|
740
751
|
query[name] = values;
|
|
@@ -815,6 +826,7 @@ export function historyMarkdown(document) {
|
|
|
815
826
|
`- Duration: ${d ? `avg ${formatMs(d.avgMs)}, p50 ${formatMs(d.p50Ms)}, p95 ${formatMs(d.p95Ms)}, min ${formatMs(d.minMs)}, max ${formatMs(d.maxMs)}, last ${formatMs(d.lastMs)}` : 'not measured'}`,
|
|
816
827
|
...(test.tags.length ? [`- Tags: ${test.tags.join(', ')}`] : []),
|
|
817
828
|
...(test.artifactTags?.length ? [`- Artifact tags: ${test.artifactTags.map(displayTag).join(', ')}`] : []),
|
|
829
|
+
...(test.scopeProfiles?.length ? [`- Recording scope: ${test.scopeProfiles.map(tokens => tokens.join(' ')).join('; ')}`] : []),
|
|
818
830
|
...(Object.keys(test.labels).length ? [`- Labels: ${Object.entries(test.labels).map(([k, v]) => `${k}=${v}`).join(', ')}`] : []),
|
|
819
831
|
`- Evidence: ${test.evidence.attachments} attachments${test.evidence.roles.length ? ` (${test.evidence.roles.join(', ')})` : ''}, ${test.evidence.steps} steps`,
|
|
820
832
|
'',
|