@crvouga/atlas 1.12.0 → 1.14.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 +13 -1
- package/dist/critics/src/contract.d.ts +113 -0
- package/dist/critics/src/contract.js +48 -0
- package/dist/critics/src/credentials.d.ts +72 -0
- package/dist/critics/src/credentials.js +136 -0
- package/dist/critics/src/critique.d.ts +128 -0
- package/dist/critics/src/critique.js +163 -0
- package/dist/critics/src/index.d.ts +21 -0
- package/dist/critics/src/index.js +21 -0
- package/dist/critics/src/judging.d.ts +153 -0
- package/dist/critics/src/judging.js +145 -0
- package/dist/critics/src/publish.d.ts +23 -0
- package/dist/critics/src/publish.js +21 -0
- package/dist/critics/src/runner.d.ts +104 -0
- package/dist/critics/src/runner.js +202 -0
- package/dist/critics/src/scope.d.ts +8 -0
- package/dist/critics/src/scope.js +26 -0
- package/dist/playwright/src/index.d.ts +4 -0
- package/dist/playwright/src/index.js +4 -0
- package/dist/playwright/src/ui-catalog.d.ts +51 -0
- package/dist/playwright/src/ui-catalog.js +44 -0
- package/dist/reports/src/compose.d.ts +2 -2
- package/dist/reports/src/compose.js +6 -5
- package/dist/reports/src/evidence-types.d.ts +3 -0
- package/dist/reports/src/evidence-view.d.ts +2 -1
- package/dist/reports/src/evidence-view.js +5 -2
- package/dist/reports/src/layer-merge.d.ts +16 -4
- package/dist/reports/src/layer-merge.js +23 -5
- package/dist/reports/src/report-index.js +9 -5
- package/dist/reports/src/report-status.d.ts +1 -1
- package/dist/reports/src/report-status.js +6 -4
- package/dist/reports/src/ui-catalog-publish.d.ts +92 -0
- package/dist/reports/src/ui-catalog-publish.js +68 -0
- package/dist/reports/src/ui-catalog.d.ts +229 -0
- package/dist/reports/src/ui-catalog.js +273 -0
- package/dist/reports/src/ui-state.d.ts +70 -0
- package/dist/reports/src/ui-state.js +66 -0
- package/dist/schema/src/api.d.ts +351 -0
- package/dist/schema/src/api.js +72 -1
- package/dist/schema/src/critique.d.ts +144 -0
- package/dist/schema/src/critique.js +96 -0
- package/dist/schema/src/index.d.ts +2 -1
- package/dist/schema/src/index.js +2 -1
- package/dist/schema/src/server-config.d.ts +7 -1
- package/dist/schema/src/server-config.js +6 -3
- package/dist/server/src/app.js +15 -0
- package/dist/server/src/auth.d.ts +1 -1
- package/dist/server/src/auth.js +2 -2
- package/dist/server/src/config.js +2 -2
- package/dist/server/src/openapi.d.ts +3 -3
- package/dist/server/src/openapi.js +29 -6
- package/dist/server/src/store-api.js +39 -1
- package/docs/agents.md +1 -0
- package/docs/critics.md +190 -0
- package/docs/extending.md +1 -0
- package/docs/reference/critics.md +840 -0
- package/docs/reference/http-api.md +52 -2
- package/docs/reference/playwright.md +63 -0
- package/docs/reference/reports.md +608 -8
- package/docs/reference/schema.md +152 -2
- package/docs/reference/server.md +3 -3
- package/docs/report-contract.md +6 -2
- package/docs/rich-evidence.md +5 -0
- package/docs/server.md +8 -4
- package/docs/ui-catalog.md +173 -0
- package/docs/ui.md +15 -0
- package/llms-full.txt +457 -8
- package/llms.txt +3 -0
- package/package.json +8 -2
- package/packages/critics/README.md +18 -0
- package/packages/schema/schemas/critique.schema.json +254 -0
- package/packages/schema/schemas/effective-delta.schema.json +73 -0
- package/packages/schema/schemas/effective-evidence.schema.json +73 -0
- package/packages/schema/schemas/layer-rows.schema.json +73 -0
- package/packages/schema/schemas/server-config.schema.json +2 -1
- package/packages/schema/schemas/server-info.schema.json +1 -0
- package/packages/server/openapi.json +1443 -80
- package/packages/visualizer/src/app/AppShell.tsx +36 -30
- package/packages/visualizer/src/app/router.tsx +10 -1
- package/packages/visualizer/src/catalog/UiCatalogFilters.tsx +65 -0
- package/packages/visualizer/src/catalog/UiCatalogPage.tsx +342 -0
- package/packages/visualizer/src/catalog/provider.tsx +25 -0
- package/packages/visualizer/src/catalog/search.ts +68 -0
- package/packages/visualizer/src/catalog/words.ts +26 -0
- package/packages/visualizer/src/components/Header.tsx +17 -4
|
@@ -414,7 +414,7 @@ Responses: `200` It exists.; `401` No token, or an unknown one. Send `Authorizat
|
|
|
414
414
|
|
|
415
415
|
### Upload an object
|
|
416
416
|
|
|
417
|
-
`PUT /api/store/{key}` · role:
|
|
417
|
+
`PUT /api/store/{key}` · role: critic
|
|
418
418
|
|
|
419
419
|
The write half of the `ObjectStore` interface. The server checks what it can from the bytes in hand and says no to what would corrupt the report for everyone:
|
|
420
420
|
|
|
@@ -426,6 +426,8 @@ The write half of the `ObjectStore` interface. The server checks what it can fro
|
|
|
426
426
|
|
|
427
427
|
Upload in dependency order: blobs, then the manifest, then the catalog entry. `atlas report publish`, `atlas report stream` and `httpStore` do. Needs a `Bearer` token of role `publish` or `admin`; a browser's sign-in box is refused.
|
|
428
428
|
|
|
429
|
+
A token of role `critic` may upload only what a critic publishes: blobs, and layers (and their catalog entries) whose `producer` is `critic.<name>` and whose parts are all of kind `critique`. Anything else is `403` with the rule it broke, and nothing is stored.
|
|
430
|
+
|
|
429
431
|
- `key` (path, required): An object key under `layers/`, such as `layers/manifests/<id>.json`. It contains slashes, which are not percent-encoded; every other character is.
|
|
430
432
|
- `content-type` (header): Stored with the object and returned when it is read.
|
|
431
433
|
- `project` (query): Project slug. Alternatively send X-Atlas-Project or use the project-prefixed route. Omit for the default project. Conflicting selectors are refused.
|
|
@@ -434,7 +436,7 @@ Responses: `200` Already there with the same bytes.; `201` Stored.; `401` No tok
|
|
|
434
436
|
|
|
435
437
|
### Upload an object
|
|
436
438
|
|
|
437
|
-
`PUT /api/projects/{project}/store/{key}` · role:
|
|
439
|
+
`PUT /api/projects/{project}/store/{key}` · role: critic
|
|
438
440
|
|
|
439
441
|
The write half of the `ObjectStore` interface. The server checks what it can from the bytes in hand and says no to what would corrupt the report for everyone:
|
|
440
442
|
|
|
@@ -446,6 +448,8 @@ The write half of the `ObjectStore` interface. The server checks what it can fro
|
|
|
446
448
|
|
|
447
449
|
Upload in dependency order: blobs, then the manifest, then the catalog entry. `atlas report publish`, `atlas report stream` and `httpStore` do. Needs a `Bearer` token of role `publish` or `admin`; a browser's sign-in box is refused.
|
|
448
450
|
|
|
451
|
+
A token of role `critic` may upload only what a critic publishes: blobs, and layers (and their catalog entries) whose `producer` is `critic.<name>` and whose parts are all of kind `critique`. Anything else is `403` with the rule it broke, and nothing is stored.
|
|
452
|
+
|
|
449
453
|
Scoped to one project. The same project may be selected on the unprefixed API using ?project= or X-Atlas-Project. Unknown projects return 404 on reads and are created by an authenticated upload.
|
|
450
454
|
|
|
451
455
|
- `project` (path, required): Project slug. Alternatively send X-Atlas-Project or use the project-prefixed route. Omit for the default project. Conflicting selectors are refused.
|
|
@@ -922,6 +926,52 @@ Scoped to one project. The same project may be selected on the unprefixed API us
|
|
|
922
926
|
|
|
923
927
|
Responses: `200` The matching results and the facets.; `304` Unchanged since the `If-None-Match` ETag.; `401` No token, or an unknown one. Send `Authorization: Bearer <token>`.; `503` The server is still reading its backends. Retry after the number of seconds in `Retry-After`.
|
|
924
928
|
|
|
929
|
+
### The UI catalog: captured UI states per profile, and what is still expected
|
|
930
|
+
|
|
931
|
+
`GET /api/reports/ui-catalog` · role: read
|
|
932
|
+
|
|
933
|
+
A browsable inventory of the product's user interface, computed from the winning results of the `ui-state` and `ui-plan` parts of every layer (see the UI catalog guide). Each state is a check id; each cell is one profile of it (the layer's environment and parameters) with its coverage: `captured` only when the winning capture passed, `failed`, `capturing`, `skipped`, or `missing` when a plan expects it and nothing has been captured. A picture is read from the cell's `capture.image.path` under the reports API root, or `uri`. What is expected is itself a layer, so a shard that never published leaves its states `missing`, never captured. The document equals what the viewer computes from the same report. Narrow it by `q` (words), `coverage`, `profile` (a profile `key`), `area`, `route`, `cohort`, `flag`, `surface` and `producer`: AND across parameters, OR within one; a state keeps only the cells that match `profile` and `coverage`. Page the states with `limit` and `offset`. Viewers need not poll: they hold the report over `/api/events` and read changes with `/api/reports/evidence/effective?since=`.
|
|
934
|
+
|
|
935
|
+
- `limit` (query): At most this many states (default 200).
|
|
936
|
+
- `offset` (query): Skip this many states (default 0).
|
|
937
|
+
- `q` (query): Words that must all appear in a state's id, title, route, fixture, cohort, area, flags, surfaces or producers.
|
|
938
|
+
- `coverage` (query): Coverage values: `captured`, `failed`, `capturing`, `skipped`, `missing`. Comma-separated.
|
|
939
|
+
- `profile` (query): Profile keys, as listed in `profiles[].key`.
|
|
940
|
+
- `area` (query): Areas.
|
|
941
|
+
- `route` (query): Routes.
|
|
942
|
+
- `cohort` (query): Cohorts.
|
|
943
|
+
- `flag` (query): Feature flags a state was captured with.
|
|
944
|
+
- `surface` (query): Surfaces a state covers.
|
|
945
|
+
- `producer` (query): Producers that captured or planned a state.
|
|
946
|
+
- `format` (query): `markdown` for Markdown instead of JSON.
|
|
947
|
+
- `project` (query): Project slug. Alternatively send X-Atlas-Project or use the project-prefixed route. Omit for the default project. Conflicting selectors are refused.
|
|
948
|
+
|
|
949
|
+
Responses: `200` The catalog.; `304` Unchanged since the `If-None-Match` ETag.; `401` No token, or an unknown one. Send `Authorization: Bearer <token>`.; `503` The server is still reading its backends. Retry after the number of seconds in `Retry-After`.
|
|
950
|
+
|
|
951
|
+
### The UI catalog: captured UI states per profile, and what is still expected
|
|
952
|
+
|
|
953
|
+
`GET /api/projects/{project}/reports/ui-catalog` · role: read
|
|
954
|
+
|
|
955
|
+
A browsable inventory of the product's user interface, computed from the winning results of the `ui-state` and `ui-plan` parts of every layer (see the UI catalog guide). Each state is a check id; each cell is one profile of it (the layer's environment and parameters) with its coverage: `captured` only when the winning capture passed, `failed`, `capturing`, `skipped`, or `missing` when a plan expects it and nothing has been captured. A picture is read from the cell's `capture.image.path` under the reports API root, or `uri`. What is expected is itself a layer, so a shard that never published leaves its states `missing`, never captured. The document equals what the viewer computes from the same report. Narrow it by `q` (words), `coverage`, `profile` (a profile `key`), `area`, `route`, `cohort`, `flag`, `surface` and `producer`: AND across parameters, OR within one; a state keeps only the cells that match `profile` and `coverage`. Page the states with `limit` and `offset`. Viewers need not poll: they hold the report over `/api/events` and read changes with `/api/reports/evidence/effective?since=`.
|
|
956
|
+
|
|
957
|
+
Scoped to one project. The same project may be selected on the unprefixed API using ?project= or X-Atlas-Project. Unknown projects return 404 on reads and are created by an authenticated upload.
|
|
958
|
+
|
|
959
|
+
- `project` (path, required): Project slug. Alternatively send X-Atlas-Project or use the project-prefixed route. Omit for the default project. Conflicting selectors are refused.
|
|
960
|
+
- `limit` (query): At most this many states (default 200).
|
|
961
|
+
- `offset` (query): Skip this many states (default 0).
|
|
962
|
+
- `q` (query): Words that must all appear in a state's id, title, route, fixture, cohort, area, flags, surfaces or producers.
|
|
963
|
+
- `coverage` (query): Coverage values: `captured`, `failed`, `capturing`, `skipped`, `missing`. Comma-separated.
|
|
964
|
+
- `profile` (query): Profile keys, as listed in `profiles[].key`.
|
|
965
|
+
- `area` (query): Areas.
|
|
966
|
+
- `route` (query): Routes.
|
|
967
|
+
- `cohort` (query): Cohorts.
|
|
968
|
+
- `flag` (query): Feature flags a state was captured with.
|
|
969
|
+
- `surface` (query): Surfaces a state covers.
|
|
970
|
+
- `producer` (query): Producers that captured or planned a state.
|
|
971
|
+
- `format` (query): `markdown` for Markdown instead of JSON.
|
|
972
|
+
|
|
973
|
+
Responses: `200` The catalog.; `304` Unchanged since the `If-None-Match` ETag.; `401` No token, or an unknown one. Send `Authorization: Bearer <token>`.; `503` The server is still reading its backends. Retry after the number of seconds in `Retry-After`.
|
|
974
|
+
|
|
925
975
|
### Every test one run recorded
|
|
926
976
|
|
|
927
977
|
`GET /api/reports/runs/detail` · role: read
|
|
@@ -104,7 +104,10 @@ recognise screens and tap or type (`screen`, `recognize`, `locate`, the signal c
|
|
|
104
104
|
the media tools (`captureScreen`, `ScreencastRecorder`, `ffmpeg`, `PHONE` and the pacing presets). Needs the
|
|
105
105
|
`playwright` package; clips and thumbnails need FFmpeg on the PATH.
|
|
106
106
|
|
|
107
|
+
Also `captureUiState`, the optional Playwright adapter of the UI catalog (it needs no Playwright import itself).
|
|
108
|
+
|
|
107
109
|
See docs/statecharts.md
|
|
110
|
+
See docs/ui-catalog.md
|
|
108
111
|
|
|
109
112
|
### Playwright screenshots, clips and paced actions
|
|
110
113
|
|
|
@@ -301,3 +304,63 @@ const states = {
|
|
|
301
304
|
'No todos': screen({ all: [text('Nothing to do yet')], none: [role('listitem')] })
|
|
302
305
|
};
|
|
303
306
|
```
|
|
307
|
+
|
|
308
|
+
### Playwright adapter for the UI catalog
|
|
309
|
+
|
|
310
|
+
Source: `packages/playwright/src/ui-catalog.ts`
|
|
311
|
+
|
|
312
|
+
The optional Playwright side of the UI catalog: take a page to a state, wait until it is ready, photograph it, and hand the
|
|
313
|
+
result to `uiStateLayer` (`@crvouga/atlas/reports/ui-catalog-publish`) as a capture. This is the only Playwright-specific code
|
|
314
|
+
of the feature; reading, composing and exploring the catalog know nothing about it, and any other program that writes the
|
|
315
|
+
same layer produces the same catalog. The page is typed by the three methods used, so no `playwright` import is needed here
|
|
316
|
+
and a Playwright Test `page`, a `Page` of the driver, or a stub all fit.
|
|
317
|
+
|
|
318
|
+
See docs/ui-catalog.md
|
|
319
|
+
|
|
320
|
+
#### `CapturePage`
|
|
321
|
+
|
|
322
|
+
```ts
|
|
323
|
+
export type CapturePage = {
|
|
324
|
+
screenshot(options?: { readonly fullPage?: boolean; readonly animations?: 'disabled' | 'allow'; readonly type?: 'png' }): Promise<Uint8Array>;
|
|
325
|
+
viewportSize(): { readonly width: number; readonly height: number } | null;
|
|
326
|
+
};
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
The part of a Playwright `Page` that capturing uses.
|
|
330
|
+
|
|
331
|
+
#### `CaptureUiStateOptions`
|
|
332
|
+
|
|
333
|
+
```ts
|
|
334
|
+
export type CaptureUiStateOptions = UiStateFacts & {
|
|
335
|
+
/** The state's stable id: how a later capture replaces this one. */
|
|
336
|
+
readonly id: string;
|
|
337
|
+
readonly title?: string;
|
|
338
|
+
/** Resolves when the page shows the state (a locator is visible, the network is idle). A rejection or a timeout makes the capture `failed`, with the reason as its detail. */
|
|
339
|
+
readonly ready?: (page: CapturePage) => Promise<void>;
|
|
340
|
+
/** How long to wait for `ready`; default 15 seconds. */
|
|
341
|
+
readonly readyTimeoutMs?: number;
|
|
342
|
+
/** Photograph the whole scrollable page rather than the viewport. */
|
|
343
|
+
readonly fullPage?: boolean;
|
|
344
|
+
/** Extra labels for the capture (`browser`, `os`), which never split it. */
|
|
345
|
+
readonly labels?: Readonly<Record<string, string>>;
|
|
346
|
+
};
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
What to capture and how to know the page is ready.
|
|
350
|
+
|
|
351
|
+
#### `captureUiState`
|
|
352
|
+
|
|
353
|
+
```ts
|
|
354
|
+
export async function captureUiState(page: CapturePage, options: CaptureUiStateOptions): Promise<UiStateCapture>
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
Wait for the page to be ready, photograph it, and return the capture to publish. Never throws for the page's sake: a page
|
|
358
|
+
that never became ready, or could not be photographed, is a `failed` capture that says why, so a broken state shows in the
|
|
359
|
+
catalog instead of ending the run. A readiness failure still attaches a picture of what the page showed, for diagnosis.
|
|
360
|
+
|
|
361
|
+
**Example**
|
|
362
|
+
|
|
363
|
+
```ts
|
|
364
|
+
const shot = await captureUiState(page, { id: 'checkout/payment', route: '/checkout', ready: p => expectVisible(p, '#total') });
|
|
365
|
+
await publishChecks(store, uiStateLayer({ producer: 'playwright', recordedAtMs: Date.now(), environment: 'desktop-web', profile: { viewport: '1280x800' }, states: [shot] }));
|
|
366
|
+
```
|