@keboola/validate-ui 0.6.0 → 0.6.1

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 CHANGED
@@ -1,5 +1,14 @@
1
1
  # @keboola/validate-ui
2
2
 
3
+ ## 0.6.1
4
+
5
+ ### Patch Changes
6
+
7
+ - Runtime health no longer fails a page because an error string such as "something went wrong" appears only inside an inline `<script>`, `<style>`, `<template>`, `<noscript>` or comment. Error-boundary copy is matched against the rendered text, and dev-server overlay names are matched against the DOM with those bodies removed.
8
+
9
+ - Updated dependencies:
10
+ - @keboola/brand-registry@1.10.0
11
+
3
12
  ## 0.6.0
4
13
 
5
14
  ### Minor Changes
package/dist/cli.cjs CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- const require_runner = require("./runner-Dhb1FmeB.cjs");
2
+ const require_runner = require("./runner-BwEjSueH.cjs");
3
3
  let node_fs_promises = require("node:fs/promises");
4
4
  let node_path = require("node:path");
5
5
  let node_process = require("node:process");
package/dist/cli.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { k as parseViewport, m as CompareConfig, o as validate, t as compareRoutes, w as serveStatic } from "./runner-D5dD5L7c.js";
2
+ import { k as parseViewport, m as CompareConfig, o as validate, t as compareRoutes, w as serveStatic } from "./runner-DH04MitR.js";
3
3
  import { readFile } from "node:fs/promises";
4
4
  import { resolve } from "node:path";
5
5
  import process from "node:process";
package/dist/index.cjs CHANGED
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_runner = require("./runner-Dhb1FmeB.cjs");
2
+ const require_runner = require("./runner-BwEjSueH.cjs");
3
3
  //#region src/types.ts
4
4
  const SEVERITIES = [
5
5
  "critical",
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":[],"sources":["../src/types.ts"],"sourcesContent":["import type { Browser, Page } from 'playwright';\n\nimport type { CompareConfig, CompareSnapshot } from './compare/types';\n\n/** A named viewport the harness renders and captures at. */\nexport type Viewport = {\n label: string;\n width: number;\n height: number;\n};\n\n/** A single console / page-error message emitted while the page loaded. */\nexport type ConsoleEntry = {\n type: string;\n text: string;\n location?: string;\n};\n\n/** A network response or failed request observed during load. */\nexport type NetworkEntry = {\n url: string;\n method: string;\n /** HTTP status, or 0 for a request that failed before a response. */\n status: number;\n failure?: string;\n};\n\n/**\n * Ground truth captured from a rendered page — the input every verdict axis\n * reasons over. Produced by {@link capture}; never contains live browser handles\n * so it is safe to serialize and pass to a VLM.\n */\nexport type CaptureArtifact = {\n url: string;\n viewport: Viewport;\n /** Full-page PNG bytes. */\n screenshot: Uint8Array;\n /** Serialized outer HTML of the document after load. */\n dom: string;\n /**\n * Trimmed `body.innerText` read from the live DOM when available. An empty\n * string is a reliable white-screen signal — more robust than parsing\n * {@link dom}. Absent only when an artifact is built without a live page.\n */\n renderedText?: string;\n console: ConsoleEntry[];\n network: NetworkEntry[];\n};\n\nexport const SEVERITIES = ['critical', 'serious', 'moderate', 'minor'] as const;\nexport type Severity = (typeof SEVERITIES)[number];\n\n/** One problem an axis found. */\nexport type Finding = {\n message: string;\n severity: Severity;\n /** Optional extra context — a stack, a WCAG id, a selector, a diff ratio. */\n detail?: string;\n /** Optional pointer — WCAG ref, DOM selector, request URL, etc. */\n ref?: string;\n};\n\n/** The result of running one axis against one page. */\nexport type Verdict = {\n axis: string;\n pass: boolean;\n findings: Finding[];\n};\n\n/** The combined result of running every axis against one page. */\nexport type AggregateVerdict = {\n url: string;\n pass: boolean;\n verdicts: Verdict[];\n};\n\n/**\n * Everything an axis may need. Different axes use different fields — a11y and\n * visual axes need the live {@link Page}; brief-conformance needs the brief;\n * the visual axis re-renders under an alternate brand via\n * {@link AxisContext.captureUnderBrand}. All optional fields may be absent, and\n * an axis must degrade to a clear finding rather than throw when its input is\n * missing.\n */\nexport type AxisContext = {\n artifact: CaptureArtifact;\n /** Live page for the primary capture, when the run keeps a browser open. */\n page?: Page;\n /** Shared browser, for axes that need to render additional pages. */\n browser?: Browser;\n /** The natural-language brief the UI was generated from. */\n brief?: string;\n /** A prior screenshot to diff the current one against. */\n baseline?: Uint8Array;\n /** Re-render the same route under a different brand id and capture it. */\n captureUnderBrand?: (brand: string) => Promise<CaptureArtifact>;\n /** The \"old\" UI reduced to regions — enables the compare axis when present. */\n comparison?: CompareSnapshot;\n /** Allowlist + state markers for the compare axis. */\n compareConfig?: CompareConfig;\n};\n\n/**\n * A verdict axis. Each of the axes (runtime-health, accessibility,\n * visual-brand, brief-conformance, compare) implements this contract in its own\n * file so they can be built independently. `run` must resolve to a {@link Verdict} and\n * should not throw for expected-missing input — the aggregator converts a throw\n * into a failing verdict, but a clear finding is better.\n */\nexport type Axis = {\n name: string;\n run: (context: AxisContext) => Promise<Verdict>;\n};\n"],"mappings":";;;AAiDA,MAAa,aAAa;CAAC;CAAY;CAAW;CAAY;AAAO"}
1
+ {"version":3,"file":"index.cjs","names":[],"sources":["../src/types.ts"],"sourcesContent":["import type { Browser, Page } from 'playwright';\n\nimport type { CompareConfig, CompareSnapshot } from './compare/types';\n\nexport type Viewport = {\n label: string;\n width: number;\n height: number;\n};\n\n/** A console or page-error message emitted while the page loaded. */\nexport type ConsoleEntry = {\n type: string;\n text: string;\n location?: string;\n};\n\n/** A network response or failed request observed during load. */\nexport type NetworkEntry = {\n url: string;\n method: string;\n /** HTTP status, or 0 for a request that failed before a response. */\n status: number;\n failure?: string;\n};\n\n/**\n * Ground truth every verdict axis reasons over, produced by {@link capture}.\n *\n * - Holds no live browser handles, so it is safe to serialize and pass to a VLM.\n */\nexport type CaptureArtifact = {\n url: string;\n viewport: Viewport;\n /** Full-page PNG. */\n screenshot: Uint8Array;\n /** Outer HTML after load. */\n dom: string;\n /** Trimmed `body.innerText`; empty is a reliable white-screen signal. Absent without a live page. */\n renderedText?: string;\n console: ConsoleEntry[];\n network: NetworkEntry[];\n};\n\nexport const SEVERITIES = ['critical', 'serious', 'moderate', 'minor'] as const;\nexport type Severity = (typeof SEVERITIES)[number];\n\n/** One problem an axis found. */\nexport type Finding = {\n message: string;\n severity: Severity;\n /** Extra context — a stack, a WCAG id, a selector, a diff ratio. */\n detail?: string;\n /** Pointer — WCAG ref, DOM selector, request URL. */\n ref?: string;\n};\n\n/** The result of running one axis against one page. */\nexport type Verdict = {\n axis: string;\n pass: boolean;\n findings: Finding[];\n};\n\n/** The combined result of running every axis against one page. */\nexport type AggregateVerdict = {\n url: string;\n pass: boolean;\n verdicts: Verdict[];\n};\n\n/**\n * Everything an axis may need.\n *\n * - Any optional field may be absent; an axis then reports a clear finding instead of throwing.\n */\nexport type AxisContext = {\n artifact: CaptureArtifact;\n /** Live page for the primary capture, when the run keeps a browser open. */\n page?: Page;\n /** Shared browser for rendering additional pages. */\n browser?: Browser;\n /** Natural-language brief the UI was generated from. */\n brief?: string;\n /** Prior screenshot to diff against. */\n baseline?: Uint8Array;\n /** Re-render and capture the same route under another brand id. */\n captureUnderBrand?: (brand: string) => Promise<CaptureArtifact>;\n /** The \"old\" UI reduced to regions — enables the compare axis when present. */\n comparison?: CompareSnapshot;\n compareConfig?: CompareConfig;\n};\n\n/**\n * A verdict axis.\n *\n * - `run` should not throw on missing input; the aggregator turns a throw into a failing verdict.\n */\nexport type Axis = {\n name: string;\n run: (context: AxisContext) => Promise<Verdict>;\n};\n"],"mappings":";;;AA4CA,MAAa,aAAa;CAAC;CAAY;CAAW;CAAY;AAAO"}
package/dist/index.d.cts CHANGED
@@ -79,13 +79,12 @@ declare const CompareConfig: z.ZodObject<{
79
79
  type CompareConfig = z.infer<typeof CompareConfig>;
80
80
  //#endregion
81
81
  //#region src/types.d.ts
82
- /** A named viewport the harness renders and captures at. */
83
82
  type Viewport = {
84
83
  label: string;
85
84
  width: number;
86
85
  height: number;
87
86
  };
88
- /** A single console / page-error message emitted while the page loaded. */
87
+ /** A console or page-error message emitted while the page loaded. */
89
88
  type ConsoleEntry = {
90
89
  type: string;
91
90
  text: string;
@@ -100,22 +99,18 @@ type NetworkEntry = {
100
99
  failure?: string;
101
100
  };
102
101
  /**
103
- * Ground truth captured from a rendered page — the input every verdict axis
104
- * reasons over. Produced by {@link capture}; never contains live browser handles
105
- * so it is safe to serialize and pass to a VLM.
102
+ * Ground truth every verdict axis reasons over, produced by {@link capture}.
103
+ *
104
+ * - Holds no live browser handles, so it is safe to serialize and pass to a VLM.
106
105
  */
107
106
  type CaptureArtifact = {
108
107
  url: string;
109
108
  viewport: Viewport;
110
- /** Full-page PNG bytes. */
109
+ /** Full-page PNG. */
111
110
  screenshot: Uint8Array;
112
- /** Serialized outer HTML of the document after load. */
111
+ /** Outer HTML after load. */
113
112
  dom: string;
114
- /**
115
- * Trimmed `body.innerText` read from the live DOM when available. An empty
116
- * string is a reliable white-screen signal — more robust than parsing
117
- * {@link dom}. Absent only when an artifact is built without a live page.
118
- */
113
+ /** Trimmed `body.innerText`; empty is a reliable white-screen signal. Absent without a live page. */
119
114
  renderedText?: string;
120
115
  console: ConsoleEntry[];
121
116
  network: NetworkEntry[];
@@ -126,9 +121,9 @@ type Severity = (typeof SEVERITIES)[number];
126
121
  type Finding = {
127
122
  message: string;
128
123
  severity: Severity;
129
- /** Optional extra context — a stack, a WCAG id, a selector, a diff ratio. */
124
+ /** Extra context — a stack, a WCAG id, a selector, a diff ratio. */
130
125
  detail?: string;
131
- /** Optional pointer — WCAG ref, DOM selector, request URL, etc. */
126
+ /** Pointer — WCAG ref, DOM selector, request URL. */
132
127
  ref?: string;
133
128
  };
134
129
  /** The result of running one axis against one page. */
@@ -144,36 +139,30 @@ type AggregateVerdict = {
144
139
  verdicts: Verdict[];
145
140
  };
146
141
  /**
147
- * Everything an axis may need. Different axes use different fields — a11y and
148
- * visual axes need the live {@link Page}; brief-conformance needs the brief;
149
- * the visual axis re-renders under an alternate brand via
150
- * {@link AxisContext.captureUnderBrand}. All optional fields may be absent, and
151
- * an axis must degrade to a clear finding rather than throw when its input is
152
- * missing.
142
+ * Everything an axis may need.
143
+ *
144
+ * - Any optional field may be absent; an axis then reports a clear finding instead of throwing.
153
145
  */
154
146
  type AxisContext = {
155
147
  artifact: CaptureArtifact;
156
148
  /** Live page for the primary capture, when the run keeps a browser open. */
157
149
  page?: Page;
158
- /** Shared browser, for axes that need to render additional pages. */
150
+ /** Shared browser for rendering additional pages. */
159
151
  browser?: Browser;
160
- /** The natural-language brief the UI was generated from. */
152
+ /** Natural-language brief the UI was generated from. */
161
153
  brief?: string;
162
- /** A prior screenshot to diff the current one against. */
154
+ /** Prior screenshot to diff against. */
163
155
  baseline?: Uint8Array;
164
- /** Re-render the same route under a different brand id and capture it. */
156
+ /** Re-render and capture the same route under another brand id. */
165
157
  captureUnderBrand?: (brand: string) => Promise<CaptureArtifact>;
166
158
  /** The "old" UI reduced to regions — enables the compare axis when present. */
167
159
  comparison?: CompareSnapshot;
168
- /** Allowlist + state markers for the compare axis. */
169
160
  compareConfig?: CompareConfig;
170
161
  };
171
162
  /**
172
- * A verdict axis. Each of the axes (runtime-health, accessibility,
173
- * visual-brand, brief-conformance, compare) implements this contract in its own
174
- * file so they can be built independently. `run` must resolve to a {@link Verdict} and
175
- * should not throw for expected-missing input — the aggregator converts a throw
176
- * into a failing verdict, but a clear finding is better.
163
+ * A verdict axis.
164
+ *
165
+ * - `run` should not throw on missing input; the aggregator turns a throw into a failing verdict.
177
166
  */
178
167
  type Axis = {
179
168
  name: string;
@@ -302,14 +291,10 @@ declare const briefConformanceAxis: Axis;
302
291
  //#endregion
303
292
  //#region src/axes/compare.d.ts
304
293
  /**
305
- * A5 · Semantic old-vs-new compare verdict — UT-4572.
294
+ * A5 · Semantic old-vs-new compare verdict.
306
295
  *
307
- * Activates only when `context.comparison` (an "old" {@link CompareSnapshot}) is
308
- * supplied; otherwise it degrades to a passing skip, so ordinary single-page runs
309
- * are unaffected. Extracts the "new" snapshot from the live `context.page`, then
310
- * runs the pure {@link diffSnapshots}: deltas are localized per region, declared
311
- * expected-absent deltas are suppressed (not dropped), and a region that degraded
312
- * to an empty/error state is reported informationally rather than as value loss.
296
+ * - Passing skip unless `context.comparison` (an "old" {@link CompareSnapshot}) is supplied.
297
+ * - Diffs it against a snapshot of the live `context.page` via {@link diffSnapshots}.
313
298
  */
314
299
  declare const compareAxis: Axis;
315
300
  //#endregion
@@ -325,15 +310,10 @@ declare const runtimeHealthAxis: Axis;
325
310
  //#endregion
326
311
  //#region src/axes/visual-brand.d.ts
327
312
  /**
328
- * A4 · Visual & brand-correctness verdict — UT-4495.
313
+ * A4 · Visual & brand-correctness verdict.
329
314
  *
330
- * Baseline diff of `context.artifact.screenshot` vs `context.baseline`
331
- * (pixelmatch + pngjs), plus the alt-brand fitness function via
332
- * `context.captureUnderBrand`: the chrome must visibly re-skin under an
333
- * alternate brand. Flags layout overflow, blocking at phone widths. Each check
334
- * degrades to no finding when its input is absent. (Asserting categorical colors
335
- * — Badge/Alert/ModalIcon — stay fixed pixel-wise is future work; see
336
- * `altBrandFinding`.)
315
+ * - Baseline pixel diff; alt brand must visibly re-skin the chrome; layout overflow.
316
+ * - Each check yields no finding when its input is absent.
337
317
  */
338
318
  declare const visualBrandAxis: Axis;
339
319
  //#endregion
@@ -381,14 +361,10 @@ declare const dedupeSharedChrome: (perRoute: RouteFindings[]) => DedupResult;
381
361
  //#endregion
382
362
  //#region src/compare/regions.d.ts
383
363
  /**
384
- * Reduce the live page to its diffable regions, in the browser. An explicit
385
- * `[data-region]` is an authoritative boundary (its subtree is one region);
386
- * elsewhere boundaries are the outermost-empty ("leaf") landmarks/sections, so
387
- * regions never overlap or double-count values. Each region's key is resolved by
388
- * precedence
389
- * (`data-region` → `data-testid` → role → `aria-label` → nearest heading), its
390
- * state from `data-state`/text markers, and its values as normalized numeric
391
- * tokens. Symmetric across old and new so the two sides diff cleanly.
364
+ * Reduce the live page to its diffable regions, in the browser.
365
+ *
366
+ * - Regions never overlap, so no value is counted twice.
367
+ * - Must stay symmetric across old and new so the two sides diff cleanly.
392
368
  */
393
369
  declare const extractRegions: (page: Page, config?: CompareConfig) => Promise<Region[]>;
394
370
  //#endregion
package/dist/index.d.ts CHANGED
@@ -79,13 +79,12 @@ declare const CompareConfig: z.ZodObject<{
79
79
  type CompareConfig = z.infer<typeof CompareConfig>;
80
80
  //#endregion
81
81
  //#region src/types.d.ts
82
- /** A named viewport the harness renders and captures at. */
83
82
  type Viewport = {
84
83
  label: string;
85
84
  width: number;
86
85
  height: number;
87
86
  };
88
- /** A single console / page-error message emitted while the page loaded. */
87
+ /** A console or page-error message emitted while the page loaded. */
89
88
  type ConsoleEntry = {
90
89
  type: string;
91
90
  text: string;
@@ -100,22 +99,18 @@ type NetworkEntry = {
100
99
  failure?: string;
101
100
  };
102
101
  /**
103
- * Ground truth captured from a rendered page — the input every verdict axis
104
- * reasons over. Produced by {@link capture}; never contains live browser handles
105
- * so it is safe to serialize and pass to a VLM.
102
+ * Ground truth every verdict axis reasons over, produced by {@link capture}.
103
+ *
104
+ * - Holds no live browser handles, so it is safe to serialize and pass to a VLM.
106
105
  */
107
106
  type CaptureArtifact = {
108
107
  url: string;
109
108
  viewport: Viewport;
110
- /** Full-page PNG bytes. */
109
+ /** Full-page PNG. */
111
110
  screenshot: Uint8Array;
112
- /** Serialized outer HTML of the document after load. */
111
+ /** Outer HTML after load. */
113
112
  dom: string;
114
- /**
115
- * Trimmed `body.innerText` read from the live DOM when available. An empty
116
- * string is a reliable white-screen signal — more robust than parsing
117
- * {@link dom}. Absent only when an artifact is built without a live page.
118
- */
113
+ /** Trimmed `body.innerText`; empty is a reliable white-screen signal. Absent without a live page. */
119
114
  renderedText?: string;
120
115
  console: ConsoleEntry[];
121
116
  network: NetworkEntry[];
@@ -126,9 +121,9 @@ type Severity = (typeof SEVERITIES)[number];
126
121
  type Finding = {
127
122
  message: string;
128
123
  severity: Severity;
129
- /** Optional extra context — a stack, a WCAG id, a selector, a diff ratio. */
124
+ /** Extra context — a stack, a WCAG id, a selector, a diff ratio. */
130
125
  detail?: string;
131
- /** Optional pointer — WCAG ref, DOM selector, request URL, etc. */
126
+ /** Pointer — WCAG ref, DOM selector, request URL. */
132
127
  ref?: string;
133
128
  };
134
129
  /** The result of running one axis against one page. */
@@ -144,36 +139,30 @@ type AggregateVerdict = {
144
139
  verdicts: Verdict[];
145
140
  };
146
141
  /**
147
- * Everything an axis may need. Different axes use different fields — a11y and
148
- * visual axes need the live {@link Page}; brief-conformance needs the brief;
149
- * the visual axis re-renders under an alternate brand via
150
- * {@link AxisContext.captureUnderBrand}. All optional fields may be absent, and
151
- * an axis must degrade to a clear finding rather than throw when its input is
152
- * missing.
142
+ * Everything an axis may need.
143
+ *
144
+ * - Any optional field may be absent; an axis then reports a clear finding instead of throwing.
153
145
  */
154
146
  type AxisContext = {
155
147
  artifact: CaptureArtifact;
156
148
  /** Live page for the primary capture, when the run keeps a browser open. */
157
149
  page?: Page;
158
- /** Shared browser, for axes that need to render additional pages. */
150
+ /** Shared browser for rendering additional pages. */
159
151
  browser?: Browser;
160
- /** The natural-language brief the UI was generated from. */
152
+ /** Natural-language brief the UI was generated from. */
161
153
  brief?: string;
162
- /** A prior screenshot to diff the current one against. */
154
+ /** Prior screenshot to diff against. */
163
155
  baseline?: Uint8Array;
164
- /** Re-render the same route under a different brand id and capture it. */
156
+ /** Re-render and capture the same route under another brand id. */
165
157
  captureUnderBrand?: (brand: string) => Promise<CaptureArtifact>;
166
158
  /** The "old" UI reduced to regions — enables the compare axis when present. */
167
159
  comparison?: CompareSnapshot;
168
- /** Allowlist + state markers for the compare axis. */
169
160
  compareConfig?: CompareConfig;
170
161
  };
171
162
  /**
172
- * A verdict axis. Each of the axes (runtime-health, accessibility,
173
- * visual-brand, brief-conformance, compare) implements this contract in its own
174
- * file so they can be built independently. `run` must resolve to a {@link Verdict} and
175
- * should not throw for expected-missing input — the aggregator converts a throw
176
- * into a failing verdict, but a clear finding is better.
163
+ * A verdict axis.
164
+ *
165
+ * - `run` should not throw on missing input; the aggregator turns a throw into a failing verdict.
177
166
  */
178
167
  type Axis = {
179
168
  name: string;
@@ -302,14 +291,10 @@ declare const briefConformanceAxis: Axis;
302
291
  //#endregion
303
292
  //#region src/axes/compare.d.ts
304
293
  /**
305
- * A5 · Semantic old-vs-new compare verdict — UT-4572.
294
+ * A5 · Semantic old-vs-new compare verdict.
306
295
  *
307
- * Activates only when `context.comparison` (an "old" {@link CompareSnapshot}) is
308
- * supplied; otherwise it degrades to a passing skip, so ordinary single-page runs
309
- * are unaffected. Extracts the "new" snapshot from the live `context.page`, then
310
- * runs the pure {@link diffSnapshots}: deltas are localized per region, declared
311
- * expected-absent deltas are suppressed (not dropped), and a region that degraded
312
- * to an empty/error state is reported informationally rather than as value loss.
296
+ * - Passing skip unless `context.comparison` (an "old" {@link CompareSnapshot}) is supplied.
297
+ * - Diffs it against a snapshot of the live `context.page` via {@link diffSnapshots}.
313
298
  */
314
299
  declare const compareAxis: Axis;
315
300
  //#endregion
@@ -325,15 +310,10 @@ declare const runtimeHealthAxis: Axis;
325
310
  //#endregion
326
311
  //#region src/axes/visual-brand.d.ts
327
312
  /**
328
- * A4 · Visual & brand-correctness verdict — UT-4495.
313
+ * A4 · Visual & brand-correctness verdict.
329
314
  *
330
- * Baseline diff of `context.artifact.screenshot` vs `context.baseline`
331
- * (pixelmatch + pngjs), plus the alt-brand fitness function via
332
- * `context.captureUnderBrand`: the chrome must visibly re-skin under an
333
- * alternate brand. Flags layout overflow, blocking at phone widths. Each check
334
- * degrades to no finding when its input is absent. (Asserting categorical colors
335
- * — Badge/Alert/ModalIcon — stay fixed pixel-wise is future work; see
336
- * `altBrandFinding`.)
315
+ * - Baseline pixel diff; alt brand must visibly re-skin the chrome; layout overflow.
316
+ * - Each check yields no finding when its input is absent.
337
317
  */
338
318
  declare const visualBrandAxis: Axis;
339
319
  //#endregion
@@ -381,14 +361,10 @@ declare const dedupeSharedChrome: (perRoute: RouteFindings[]) => DedupResult;
381
361
  //#endregion
382
362
  //#region src/compare/regions.d.ts
383
363
  /**
384
- * Reduce the live page to its diffable regions, in the browser. An explicit
385
- * `[data-region]` is an authoritative boundary (its subtree is one region);
386
- * elsewhere boundaries are the outermost-empty ("leaf") landmarks/sections, so
387
- * regions never overlap or double-count values. Each region's key is resolved by
388
- * precedence
389
- * (`data-region` → `data-testid` → role → `aria-label` → nearest heading), its
390
- * state from `data-state`/text markers, and its values as normalized numeric
391
- * tokens. Symmetric across old and new so the two sides diff cleanly.
364
+ * Reduce the live page to its diffable regions, in the browser.
365
+ *
366
+ * - Regions never overlap, so no value is counted twice.
367
+ * - Must stay symmetric across old and new so the two sides diff cleanly.
392
368
  */
393
369
  declare const extractRegions: (page: Page, config?: CompareConfig) => Promise<Region[]>;
394
370
  //#endregion
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { A as DEFAULT_VIEWPORTS, C as accessibilityAxis, D as VIEWPORT_PRESETS, E as PHONE_VIEWPORT, F as prepareForScreenshot, M as MOBILE_VIEWPORT, N as capture, O as isPhoneViewport, P as capturePage, S as briefConformanceAxis, T as PHONE_MAX_WIDTH, _ as REGION_STATES, a as runAxes, b as comparePass, c as visualBrandAxis, d as captureSnapshot, f as loadSnapshot, g as ExpectedAbsentRule, h as CompareSnapshot, i as aggregatePass, j as DESKTOP_VIEWPORT, k as parseViewport, l as runtimeHealthAxis, m as CompareConfig, n as routeSnapshotFile, o as validate, p as saveSnapshot, r as dedupeSharedChrome, s as ALL_AXES, t as compareRoutes, u as compareAxis, v as Region, w as serveStatic, x as diffSnapshots, y as extractRegions } from "./runner-D5dD5L7c.js";
1
+ import { A as DEFAULT_VIEWPORTS, C as accessibilityAxis, D as VIEWPORT_PRESETS, E as PHONE_VIEWPORT, F as prepareForScreenshot, M as MOBILE_VIEWPORT, N as capture, O as isPhoneViewport, P as capturePage, S as briefConformanceAxis, T as PHONE_MAX_WIDTH, _ as REGION_STATES, a as runAxes, b as comparePass, c as visualBrandAxis, d as captureSnapshot, f as loadSnapshot, g as ExpectedAbsentRule, h as CompareSnapshot, i as aggregatePass, j as DESKTOP_VIEWPORT, k as parseViewport, l as runtimeHealthAxis, m as CompareConfig, n as routeSnapshotFile, o as validate, p as saveSnapshot, r as dedupeSharedChrome, s as ALL_AXES, t as compareRoutes, u as compareAxis, v as Region, w as serveStatic, x as diffSnapshots, y as extractRegions } from "./runner-DH04MitR.js";
2
2
  //#region src/types.ts
3
3
  const SEVERITIES = [
4
4
  "critical",
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/types.ts"],"sourcesContent":["import type { Browser, Page } from 'playwright';\n\nimport type { CompareConfig, CompareSnapshot } from './compare/types';\n\n/** A named viewport the harness renders and captures at. */\nexport type Viewport = {\n label: string;\n width: number;\n height: number;\n};\n\n/** A single console / page-error message emitted while the page loaded. */\nexport type ConsoleEntry = {\n type: string;\n text: string;\n location?: string;\n};\n\n/** A network response or failed request observed during load. */\nexport type NetworkEntry = {\n url: string;\n method: string;\n /** HTTP status, or 0 for a request that failed before a response. */\n status: number;\n failure?: string;\n};\n\n/**\n * Ground truth captured from a rendered page — the input every verdict axis\n * reasons over. Produced by {@link capture}; never contains live browser handles\n * so it is safe to serialize and pass to a VLM.\n */\nexport type CaptureArtifact = {\n url: string;\n viewport: Viewport;\n /** Full-page PNG bytes. */\n screenshot: Uint8Array;\n /** Serialized outer HTML of the document after load. */\n dom: string;\n /**\n * Trimmed `body.innerText` read from the live DOM when available. An empty\n * string is a reliable white-screen signal — more robust than parsing\n * {@link dom}. Absent only when an artifact is built without a live page.\n */\n renderedText?: string;\n console: ConsoleEntry[];\n network: NetworkEntry[];\n};\n\nexport const SEVERITIES = ['critical', 'serious', 'moderate', 'minor'] as const;\nexport type Severity = (typeof SEVERITIES)[number];\n\n/** One problem an axis found. */\nexport type Finding = {\n message: string;\n severity: Severity;\n /** Optional extra context — a stack, a WCAG id, a selector, a diff ratio. */\n detail?: string;\n /** Optional pointer — WCAG ref, DOM selector, request URL, etc. */\n ref?: string;\n};\n\n/** The result of running one axis against one page. */\nexport type Verdict = {\n axis: string;\n pass: boolean;\n findings: Finding[];\n};\n\n/** The combined result of running every axis against one page. */\nexport type AggregateVerdict = {\n url: string;\n pass: boolean;\n verdicts: Verdict[];\n};\n\n/**\n * Everything an axis may need. Different axes use different fields — a11y and\n * visual axes need the live {@link Page}; brief-conformance needs the brief;\n * the visual axis re-renders under an alternate brand via\n * {@link AxisContext.captureUnderBrand}. All optional fields may be absent, and\n * an axis must degrade to a clear finding rather than throw when its input is\n * missing.\n */\nexport type AxisContext = {\n artifact: CaptureArtifact;\n /** Live page for the primary capture, when the run keeps a browser open. */\n page?: Page;\n /** Shared browser, for axes that need to render additional pages. */\n browser?: Browser;\n /** The natural-language brief the UI was generated from. */\n brief?: string;\n /** A prior screenshot to diff the current one against. */\n baseline?: Uint8Array;\n /** Re-render the same route under a different brand id and capture it. */\n captureUnderBrand?: (brand: string) => Promise<CaptureArtifact>;\n /** The \"old\" UI reduced to regions — enables the compare axis when present. */\n comparison?: CompareSnapshot;\n /** Allowlist + state markers for the compare axis. */\n compareConfig?: CompareConfig;\n};\n\n/**\n * A verdict axis. Each of the axes (runtime-health, accessibility,\n * visual-brand, brief-conformance, compare) implements this contract in its own\n * file so they can be built independently. `run` must resolve to a {@link Verdict} and\n * should not throw for expected-missing input — the aggregator converts a throw\n * into a failing verdict, but a clear finding is better.\n */\nexport type Axis = {\n name: string;\n run: (context: AxisContext) => Promise<Verdict>;\n};\n"],"mappings":";;AAiDA,MAAa,aAAa;CAAC;CAAY;CAAW;CAAY;AAAO"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/types.ts"],"sourcesContent":["import type { Browser, Page } from 'playwright';\n\nimport type { CompareConfig, CompareSnapshot } from './compare/types';\n\nexport type Viewport = {\n label: string;\n width: number;\n height: number;\n};\n\n/** A console or page-error message emitted while the page loaded. */\nexport type ConsoleEntry = {\n type: string;\n text: string;\n location?: string;\n};\n\n/** A network response or failed request observed during load. */\nexport type NetworkEntry = {\n url: string;\n method: string;\n /** HTTP status, or 0 for a request that failed before a response. */\n status: number;\n failure?: string;\n};\n\n/**\n * Ground truth every verdict axis reasons over, produced by {@link capture}.\n *\n * - Holds no live browser handles, so it is safe to serialize and pass to a VLM.\n */\nexport type CaptureArtifact = {\n url: string;\n viewport: Viewport;\n /** Full-page PNG. */\n screenshot: Uint8Array;\n /** Outer HTML after load. */\n dom: string;\n /** Trimmed `body.innerText`; empty is a reliable white-screen signal. Absent without a live page. */\n renderedText?: string;\n console: ConsoleEntry[];\n network: NetworkEntry[];\n};\n\nexport const SEVERITIES = ['critical', 'serious', 'moderate', 'minor'] as const;\nexport type Severity = (typeof SEVERITIES)[number];\n\n/** One problem an axis found. */\nexport type Finding = {\n message: string;\n severity: Severity;\n /** Extra context — a stack, a WCAG id, a selector, a diff ratio. */\n detail?: string;\n /** Pointer — WCAG ref, DOM selector, request URL. */\n ref?: string;\n};\n\n/** The result of running one axis against one page. */\nexport type Verdict = {\n axis: string;\n pass: boolean;\n findings: Finding[];\n};\n\n/** The combined result of running every axis against one page. */\nexport type AggregateVerdict = {\n url: string;\n pass: boolean;\n verdicts: Verdict[];\n};\n\n/**\n * Everything an axis may need.\n *\n * - Any optional field may be absent; an axis then reports a clear finding instead of throwing.\n */\nexport type AxisContext = {\n artifact: CaptureArtifact;\n /** Live page for the primary capture, when the run keeps a browser open. */\n page?: Page;\n /** Shared browser for rendering additional pages. */\n browser?: Browser;\n /** Natural-language brief the UI was generated from. */\n brief?: string;\n /** Prior screenshot to diff against. */\n baseline?: Uint8Array;\n /** Re-render and capture the same route under another brand id. */\n captureUnderBrand?: (brand: string) => Promise<CaptureArtifact>;\n /** The \"old\" UI reduced to regions — enables the compare axis when present. */\n comparison?: CompareSnapshot;\n compareConfig?: CompareConfig;\n};\n\n/**\n * A verdict axis.\n *\n * - `run` should not throw on missing input; the aggregator turns a throw into a failing verdict.\n */\nexport type Axis = {\n name: string;\n run: (context: AxisContext) => Promise<Verdict>;\n};\n"],"mappings":";;AA4CA,MAAa,aAAa;CAAC;CAAY;CAAW;CAAY;AAAO"}