tuffgal 0.1.0-alpha.9 → 0.2.0-alpha.2

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.
Files changed (89) hide show
  1. package/CHANGELOG.md +149 -2
  2. package/README.md +78 -3
  3. package/dist/cli.d.ts +50 -0
  4. package/dist/cli.d.ts.map +1 -1
  5. package/dist/cli.js +204 -18
  6. package/dist/cli.js.map +1 -1
  7. package/dist/commands/init.d.ts +1 -1
  8. package/dist/commands/init.d.ts.map +1 -1
  9. package/dist/commands/init.js +11 -1
  10. package/dist/commands/init.js.map +1 -1
  11. package/dist/config.d.ts +38 -1
  12. package/dist/config.d.ts.map +1 -1
  13. package/dist/config.js +20 -1
  14. package/dist/config.js.map +1 -1
  15. package/dist/coverage/screens.d.ts +11 -6
  16. package/dist/coverage/screens.d.ts.map +1 -1
  17. package/dist/coverage/screens.js +12 -7
  18. package/dist/coverage/screens.js.map +1 -1
  19. package/dist/reporter/assets/report.css +415 -51
  20. package/dist/reporter/assets/report.js +502 -68
  21. package/dist/reporter/template.d.ts +8 -1
  22. package/dist/reporter/template.d.ts.map +1 -1
  23. package/dist/reporter/template.js +517 -85
  24. package/dist/reporter/template.js.map +1 -1
  25. package/dist/reporter/writeReport.d.ts +2 -2
  26. package/dist/reporter/writeReport.d.ts.map +1 -1
  27. package/dist/reporter/writeReport.js +3 -3
  28. package/dist/reporter/writeReport.js.map +1 -1
  29. package/dist/runner/approve.d.ts +34 -9
  30. package/dist/runner/approve.d.ts.map +1 -1
  31. package/dist/runner/approve.js +54 -10
  32. package/dist/runner/approve.js.map +1 -1
  33. package/dist/runner/approveFrom.d.ts +58 -0
  34. package/dist/runner/approveFrom.d.ts.map +1 -0
  35. package/dist/runner/approveFrom.js +384 -0
  36. package/dist/runner/approveFrom.js.map +1 -0
  37. package/dist/runner/breakpointPasses.d.ts +39 -0
  38. package/dist/runner/breakpointPasses.d.ts.map +1 -0
  39. package/dist/runner/breakpointPasses.js +107 -0
  40. package/dist/runner/breakpointPasses.js.map +1 -0
  41. package/dist/runner/bridges/database.d.ts +4 -3
  42. package/dist/runner/bridges/database.d.ts.map +1 -1
  43. package/dist/runner/bridges/database.js +4 -3
  44. package/dist/runner/bridges/database.js.map +1 -1
  45. package/dist/runner/exitCode.d.ts +34 -0
  46. package/dist/runner/exitCode.d.ts.map +1 -0
  47. package/dist/runner/exitCode.js +41 -0
  48. package/dist/runner/exitCode.js.map +1 -0
  49. package/dist/runner/manifest.d.ts +149 -0
  50. package/dist/runner/manifest.d.ts.map +1 -0
  51. package/dist/runner/manifest.js +229 -0
  52. package/dist/runner/manifest.js.map +1 -0
  53. package/dist/runner/mode.d.ts +41 -0
  54. package/dist/runner/mode.d.ts.map +1 -0
  55. package/dist/runner/mode.js +43 -0
  56. package/dist/runner/mode.js.map +1 -0
  57. package/dist/runner/orphanScan.d.ts +68 -0
  58. package/dist/runner/orphanScan.d.ts.map +1 -0
  59. package/dist/runner/orphanScan.js +150 -0
  60. package/dist/runner/orphanScan.js.map +1 -0
  61. package/dist/runner/run.d.ts +85 -1
  62. package/dist/runner/run.d.ts.map +1 -1
  63. package/dist/runner/run.js +267 -41
  64. package/dist/runner/run.js.map +1 -1
  65. package/dist/runner/runAction.d.ts +16 -0
  66. package/dist/runner/runAction.d.ts.map +1 -1
  67. package/dist/runner/runAction.js +112 -13
  68. package/dist/runner/runAction.js.map +1 -1
  69. package/dist/runner/runStory.d.ts +15 -4
  70. package/dist/runner/runStory.d.ts.map +1 -1
  71. package/dist/runner/runStory.js +41 -19
  72. package/dist/runner/runStory.js.map +1 -1
  73. package/dist/runner/storyFilter.d.ts +8 -0
  74. package/dist/runner/storyFilter.d.ts.map +1 -1
  75. package/dist/runner/storyFilter.js +11 -0
  76. package/dist/runner/storyFilter.js.map +1 -1
  77. package/dist/schema/action.d.ts +11 -11
  78. package/dist/schema/result.d.ts +125 -6
  79. package/dist/schema/result.d.ts.map +1 -1
  80. package/dist/schema/result.js.map +1 -1
  81. package/dist/screenshots/baselineStore.d.ts +92 -10
  82. package/dist/screenshots/baselineStore.d.ts.map +1 -1
  83. package/dist/screenshots/baselineStore.js +102 -18
  84. package/dist/screenshots/baselineStore.js.map +1 -1
  85. package/dist/screenshots/capture.d.ts +10 -5
  86. package/dist/screenshots/capture.d.ts.map +1 -1
  87. package/dist/screenshots/capture.js +31 -18
  88. package/dist/screenshots/capture.js.map +1 -1
  89. package/package.json +1 -1
@@ -9,6 +9,7 @@ export declare const hintSchema: z.ZodObject<{
9
9
  text: z.ZodOptional<z.ZodString>;
10
10
  role: z.ZodOptional<z.ZodEnum<{
11
11
  link: "link";
12
+ status: "status";
12
13
  alert: "alert";
13
14
  banner: "banner";
14
15
  button: "button";
@@ -29,7 +30,6 @@ export declare const hintSchema: z.ZodObject<{
29
30
  region: "region";
30
31
  row: "row";
31
32
  searchbox: "searchbox";
32
- status: "status";
33
33
  switch: "switch";
34
34
  tab: "tab";
35
35
  table: "table";
@@ -59,6 +59,7 @@ export declare const stepSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
59
59
  text: z.ZodOptional<z.ZodString>;
60
60
  role: z.ZodOptional<z.ZodEnum<{
61
61
  link: "link";
62
+ status: "status";
62
63
  alert: "alert";
63
64
  banner: "banner";
64
65
  button: "button";
@@ -79,7 +80,6 @@ export declare const stepSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
79
80
  region: "region";
80
81
  row: "row";
81
82
  searchbox: "searchbox";
82
- status: "status";
83
83
  switch: "switch";
84
84
  tab: "tab";
85
85
  table: "table";
@@ -99,6 +99,7 @@ export declare const stepSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
99
99
  text: z.ZodOptional<z.ZodString>;
100
100
  role: z.ZodOptional<z.ZodEnum<{
101
101
  link: "link";
102
+ status: "status";
102
103
  alert: "alert";
103
104
  banner: "banner";
104
105
  button: "button";
@@ -119,7 +120,6 @@ export declare const stepSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
119
120
  region: "region";
120
121
  row: "row";
121
122
  searchbox: "searchbox";
122
- status: "status";
123
123
  switch: "switch";
124
124
  tab: "tab";
125
125
  table: "table";
@@ -161,6 +161,7 @@ export declare const stepSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
161
161
  text: z.ZodOptional<z.ZodString>;
162
162
  role: z.ZodOptional<z.ZodEnum<{
163
163
  link: "link";
164
+ status: "status";
164
165
  alert: "alert";
165
166
  banner: "banner";
166
167
  button: "button";
@@ -181,7 +182,6 @@ export declare const stepSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
181
182
  region: "region";
182
183
  row: "row";
183
184
  searchbox: "searchbox";
184
- status: "status";
185
185
  switch: "switch";
186
186
  tab: "tab";
187
187
  table: "table";
@@ -201,6 +201,7 @@ export declare const stepSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
201
201
  text: z.ZodOptional<z.ZodString>;
202
202
  role: z.ZodOptional<z.ZodEnum<{
203
203
  link: "link";
204
+ status: "status";
204
205
  alert: "alert";
205
206
  banner: "banner";
206
207
  button: "button";
@@ -221,7 +222,6 @@ export declare const stepSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
221
222
  region: "region";
222
223
  row: "row";
223
224
  searchbox: "searchbox";
224
- status: "status";
225
225
  switch: "switch";
226
226
  tab: "tab";
227
227
  table: "table";
@@ -261,6 +261,7 @@ export declare const actionSchema: z.ZodObject<{
261
261
  text: z.ZodOptional<z.ZodString>;
262
262
  role: z.ZodOptional<z.ZodEnum<{
263
263
  link: "link";
264
+ status: "status";
264
265
  alert: "alert";
265
266
  banner: "banner";
266
267
  button: "button";
@@ -281,7 +282,6 @@ export declare const actionSchema: z.ZodObject<{
281
282
  region: "region";
282
283
  row: "row";
283
284
  searchbox: "searchbox";
284
- status: "status";
285
285
  switch: "switch";
286
286
  tab: "tab";
287
287
  table: "table";
@@ -301,6 +301,7 @@ export declare const actionSchema: z.ZodObject<{
301
301
  text: z.ZodOptional<z.ZodString>;
302
302
  role: z.ZodOptional<z.ZodEnum<{
303
303
  link: "link";
304
+ status: "status";
304
305
  alert: "alert";
305
306
  banner: "banner";
306
307
  button: "button";
@@ -321,7 +322,6 @@ export declare const actionSchema: z.ZodObject<{
321
322
  region: "region";
322
323
  row: "row";
323
324
  searchbox: "searchbox";
324
- status: "status";
325
325
  switch: "switch";
326
326
  tab: "tab";
327
327
  table: "table";
@@ -363,6 +363,7 @@ export declare const actionSchema: z.ZodObject<{
363
363
  text: z.ZodOptional<z.ZodString>;
364
364
  role: z.ZodOptional<z.ZodEnum<{
365
365
  link: "link";
366
+ status: "status";
366
367
  alert: "alert";
367
368
  banner: "banner";
368
369
  button: "button";
@@ -383,7 +384,6 @@ export declare const actionSchema: z.ZodObject<{
383
384
  region: "region";
384
385
  row: "row";
385
386
  searchbox: "searchbox";
386
- status: "status";
387
387
  switch: "switch";
388
388
  tab: "tab";
389
389
  table: "table";
@@ -403,6 +403,7 @@ export declare const actionSchema: z.ZodObject<{
403
403
  text: z.ZodOptional<z.ZodString>;
404
404
  role: z.ZodOptional<z.ZodEnum<{
405
405
  link: "link";
406
+ status: "status";
406
407
  alert: "alert";
407
408
  banner: "banner";
408
409
  button: "button";
@@ -423,7 +424,6 @@ export declare const actionSchema: z.ZodObject<{
423
424
  region: "region";
424
425
  row: "row";
425
426
  searchbox: "searchbox";
426
- status: "status";
427
427
  switch: "switch";
428
428
  tab: "tab";
429
429
  table: "table";
@@ -449,6 +449,7 @@ export declare const actionSchema: z.ZodObject<{
449
449
  text: z.ZodOptional<z.ZodString>;
450
450
  role: z.ZodOptional<z.ZodEnum<{
451
451
  link: "link";
452
+ status: "status";
452
453
  alert: "alert";
453
454
  banner: "banner";
454
455
  button: "button";
@@ -469,7 +470,6 @@ export declare const actionSchema: z.ZodObject<{
469
470
  region: "region";
470
471
  row: "row";
471
472
  searchbox: "searchbox";
472
- status: "status";
473
473
  switch: "switch";
474
474
  tab: "tab";
475
475
  table: "table";
@@ -488,6 +488,7 @@ export declare const actionSchema: z.ZodObject<{
488
488
  text: z.ZodOptional<z.ZodString>;
489
489
  role: z.ZodOptional<z.ZodEnum<{
490
490
  link: "link";
491
+ status: "status";
491
492
  alert: "alert";
492
493
  banner: "banner";
493
494
  button: "button";
@@ -508,7 +509,6 @@ export declare const actionSchema: z.ZodObject<{
508
509
  region: "region";
509
510
  row: "row";
510
511
  searchbox: "searchbox";
511
- status: "status";
512
512
  switch: "switch";
513
513
  tab: "tab";
514
514
  table: "table";
@@ -1,12 +1,17 @@
1
+ import type { RunMode } from '../runner/mode.ts';
2
+ import type { EnvironmentManifest } from '../runner/manifest.ts';
1
3
  /**
2
4
  * Outcome model. The runner emits a `RunResult` per invocation; the reporter
3
5
  * consumes it to render the HTML report. Status values map onto the three
4
6
  * outcomes the framework distinguishes:
5
7
  *
6
8
  * - `pass` — action succeeded and screenshot matched baseline (or no baseline).
7
- * - `changed` — action succeeded but screenshot drifted past the threshold.
8
- * The story does not fail. The user reviews and either approves the new
9
- * baseline or files a bug.
9
+ * - `changed` — action succeeded but the baseline drifted: the screenshot moved
10
+ * past the threshold, or (CI mode) pixels matched while the accessibility-tree
11
+ * snapshot drifted. The story does not fail. The user reviews and either
12
+ * approves the new baseline or files a bug.
13
+ * - `new` — no baseline existed; one was written this run. Informational, not a
14
+ * regression — there is nothing to compare against yet.
10
15
  * - `failed` — a step threw. The story fails fast and skips any later actions.
11
16
  */
12
17
  export type ActionStatus = 'pass' | 'changed' | 'failed' | 'skipped' | 'new';
@@ -41,6 +46,25 @@ export interface ActionResult {
41
46
  */
42
47
  failedStepIndex?: number;
43
48
  failureMessage?: string;
49
+ /**
50
+ * Structured baseline/actual dimensions for the size-mismatch `changed` branch
51
+ * (a screenshot whose baseline and actual differ in size, so no pixel diff can
52
+ * be computed). Carried alongside `failureMessage` — which holds the same fact
53
+ * as prose — so the reporter can render the dimensions in an accessible split
54
+ * idiom (a spoken "W by H pixels" longhand) instead of string-parsing the
55
+ * message. Set ONLY by the size-mismatch branch of `runAction`; absent on every
56
+ * other result, in which case the reporter falls back to `failureMessage`.
57
+ */
58
+ sizeMismatch?: {
59
+ baseline: {
60
+ width: number;
61
+ height: number;
62
+ };
63
+ actual: {
64
+ width: number;
65
+ height: number;
66
+ };
67
+ };
44
68
  baselinePath?: string;
45
69
  actualPath?: string;
46
70
  diffPath?: string;
@@ -50,8 +74,21 @@ export interface ActionResult {
50
74
  ssimScore?: number;
51
75
  /**
52
76
  * `true` when the captured page accessibility tree differs from the
53
- * baseline tree. Informational only — does not gate pass/changed by
54
- * itself; pixel/SSIM still drives the status.
77
+ * baseline tree. Advisory in local mode (flagged, never gates status). In CI
78
+ * mode it DOES gate: when pixels pass but the aria snapshot drifts, the status
79
+ * becomes `changed` and a candidate pair is written, so a drifted committed
80
+ * `a11y.yaml` stays re-approvable under the sole-writer model.
81
+ *
82
+ * DISCRIMINATOR CONTRACT. A11y-only drift is identified POSITIVELY by
83
+ * `a11yChanged === true`, never by the absence of `diffPath` alone. Both
84
+ * pixel-drift branches (SSIM-fail and size-mismatch) carry a `diffPath` /
85
+ * `failureMessage` and deliberately leave `a11yChanged` unset, so the two
86
+ * classes never collide — but the size-mismatch branch's omission of
87
+ * `a11yChanged` is the load-bearing half of that guarantee and is locked by
88
+ * runAction's `never carries a11yChanged` test. Consumers MUST branch on
89
+ * `a11yChanged === true`; a `!diffPath` heuristic would misread a future
90
+ * result that carries neither, or a size-mismatch result if that omission
91
+ * ever regressed.
55
92
  */
56
93
  a11yChanged?: boolean;
57
94
  /**
@@ -63,7 +100,13 @@ export interface ActionResult {
63
100
  /** Path to the accessibility-tree snapshot captured during this run. */
64
101
  a11yActualPath?: string;
65
102
  }
66
- export type StoryStatus = 'pass' | 'changed' | 'failed';
103
+ /**
104
+ * Story-wide rollup of its action results. Precedence (worst wins):
105
+ * `failed` > `changed` > `new` > `pass`. A story is `new` only when it wrote at
106
+ * least one fresh baseline and had no `changed`/`failed` action — so a run of
107
+ * first-time stories no longer masquerades as `changed`.
108
+ */
109
+ export type StoryStatus = 'pass' | 'changed' | 'failed' | 'new';
67
110
  export interface StoryResult {
68
111
  story: string;
69
112
  file: string;
@@ -81,6 +124,26 @@ export interface CoverageMetric {
81
124
  ratio: number;
82
125
  missing: string[];
83
126
  }
127
+ /**
128
+ * One orphaned committed baseline: an entry under `paths.baselines` whose action
129
+ * ran no story this run, so nothing compared against it. Recorded (CI mode,
130
+ * unfiltered runs only) so the report can surface it and a later `approve
131
+ * --prune` can delete it — this wave detects, it never removes.
132
+ *
133
+ * Keyed per breakpoint rather than per action so a partially-orphaned action
134
+ * (some breakpoints retired, others still live) could in principle be expressed;
135
+ * today an orphan action is retired wholesale, one entry per baseline file group
136
+ * it left behind. `breakpoint` is the mode name (`mobile`/`desktop`/…) for the
137
+ * breakpoint-keyed `<action>/<breakpoint>.png` layout, or `'legacy'` for the
138
+ * pre-breakpoint `<action>/0.png` layout. `baselinePaths` are the absolute paths
139
+ * the prune step deletes — the PNG plus its a11y companion when one exists — so
140
+ * wave 7 prunes a flat path list without re-deriving layout.
141
+ */
142
+ export interface DeletedBaseline {
143
+ action: string;
144
+ breakpoint: string;
145
+ baselinePaths: string[];
146
+ }
84
147
  /**
85
148
  * Parses and shape-checks a `results.json` blob. `approve` re-reads the run's
86
149
  * own output, the one JSON re-entry point that skips the zod validation every
@@ -90,16 +153,72 @@ export interface CoverageMetric {
90
153
  * `stories[].actions[]` walk, not a full structural mirror.
91
154
  */
92
155
  export declare function parseRunResult(raw: string, sourcePath: string): RunResult;
156
+ /**
157
+ * The capture-environment block recorded on every run. Lets the report, the
158
+ * Action, and a human diagnose an environment drift between the committed
159
+ * baselines and the run that compared against them.
160
+ *
161
+ * - `expected` — the committed `<baselines>/manifest.json` when one exists and
162
+ * parses, else `null`. `null` covers both the bootstrap case (no manifest
163
+ * written yet — the first `approve --from` creates it) and local mode, which
164
+ * never reads `paths.baselines` at all.
165
+ * - `actual` — the environment this run actually captured under.
166
+ * - `mismatch` — `true` when a pixel-affecting key diverged (CI mode with a
167
+ * present manifest), or when the committed manifest was unreadable. Always
168
+ * `false` in local mode and on the bootstrap (missing-manifest) case.
169
+ * - `mismatchKeys` — the specific diverging keys (see `PIXEL_AFFECTING_KEYS`),
170
+ * or `['manifest']` when the committed manifest could not be parsed. Empty
171
+ * when `mismatch` is `false`.
172
+ */
173
+ export interface EnvironmentReport {
174
+ expected: EnvironmentManifest | null;
175
+ actual: EnvironmentManifest;
176
+ mismatch: boolean;
177
+ mismatchKeys: string[];
178
+ }
93
179
  export interface RunResult {
94
180
  startedAt: string;
95
181
  finishedAt: string;
96
182
  durationMs: number;
183
+ /**
184
+ * Comparison contract the run executed under (`'ci' | 'local'`). Recorded so
185
+ * the report, the Action, and `approve --from` can distinguish a committed-
186
+ * baseline gate run from an advisory local self-diff. Optional in the type
187
+ * only as a defensive parse guard for older `results.json` artifacts that
188
+ * predate this field; every result the current runner emits carries it.
189
+ */
190
+ mode?: RunMode;
97
191
  totals: {
98
192
  stories: number;
99
193
  passed: number;
100
194
  changed: number;
101
195
  failed: number;
196
+ /** Stories whose rollup status is `new` (wrote a fresh baseline, no drift). */
197
+ new: number;
198
+ /**
199
+ * Orphaned committed baselines detected this run — the length of `deleted`.
200
+ * CI mode + unfiltered runs only; `0` in local mode and on any filtered run
201
+ * (where unselected stories' baselines are not orphans, merely unvisited).
202
+ */
203
+ deleted: number;
102
204
  };
205
+ /**
206
+ * Orphaned committed baselines: entries under `paths.baselines` whose action
207
+ * ran no story this run. Populated only for an unfiltered CI run (see
208
+ * {@link DeletedBaseline}); empty in local mode and on filtered runs, where a
209
+ * baseline going unvisited says nothing about whether its story still exists.
210
+ * Detection only — pruning is a later wave.
211
+ */
212
+ deleted: DeletedBaseline[];
213
+ /**
214
+ * Capture-environment provenance for this run (see {@link EnvironmentReport}).
215
+ * `expected` is the committed manifest (null in local mode / bootstrap),
216
+ * `actual` is what this run captured under, and `mismatch`/`mismatchKeys`
217
+ * drive the report banner and the `3` exit code. Optional in the type only as
218
+ * a defensive parse guard for older `results.json` artifacts that predate this
219
+ * block; every result the current runner emits carries it.
220
+ */
221
+ environment?: EnvironmentReport;
103
222
  /**
104
223
  * Custom coverage metrics layered on top of V8 line coverage:
105
224
  * `screens` = baselined visit-* actions / declared screens,
@@ -1 +1 @@
1
- {"version":3,"file":"result.d.ts","sourceRoot":"","sources":["../../src/schema/result.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,SAAS,GAAG,QAAQ,GAAG,SAAS,GAAG,KAAK,CAAC;AAE7E,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC;;;;;;;OAOG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;;;OAMG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,MAAM,EAAE,YAAY,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uEAAuE;IACvE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,wEAAwE;IACxE,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,SAAS,GAAG,QAAQ,CAAC;AAExD,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,WAAW,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,uEAAuE;IACvE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,SAAS,CAmBzE;AAED,MAAM,WAAW,SAAS;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE;QACN,OAAO,EAAE,MAAM,CAAC;QAChB,MAAM,EAAE,MAAM,CAAC;QACf,OAAO,EAAE,MAAM,CAAC;QAChB,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC;IACF;;;;OAIG;IACH,cAAc,EAAE;QACd,OAAO,EAAE,cAAc,CAAC;QACxB,KAAK,EAAE,cAAc,CAAC;KACvB,CAAC;IACF,OAAO,EAAE,WAAW,EAAE,CAAC;CACxB"}
1
+ {"version":3,"file":"result.d.ts","sourceRoot":"","sources":["../../src/schema/result.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"result.js","sourceRoot":"","sources":["../../src/schema/result.ts"],"names":[],"mappings":"AAwFA;;;;;;;GAOG;AACH,MAAM,UAAU,cAAc,CAAC,GAAW,EAAE,UAAkB;IAC5D,IAAI,MAAe,CAAC;IACpB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC3B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,MAAM,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,cAAc,CAAC;QACvE,MAAM,IAAI,KAAK,CAAC,0BAA0B,UAAU,KAAK,MAAM,EAAE,CAAC,CAAC;IACrE,CAAC;IACD,IACE,OAAO,MAAM,KAAK,QAAQ;QAC1B,MAAM,KAAK,IAAI;QACf,CAAC,KAAK,CAAC,OAAO,CAAE,MAAgC,CAAC,OAAO,CAAC,EACzD,CAAC;QACD,MAAM,IAAI,KAAK,CACb,2BAA2B,UAAU,+BAA+B;YAClE,0CAA0C,CAC7C,CAAC;IACJ,CAAC;IACD,OAAO,MAAmB,CAAC;AAC7B,CAAC"}
1
+ {"version":3,"file":"result.js","sourceRoot":"","sources":["../../src/schema/result.ts"],"names":[],"mappings":"AAmJA;;;;;;;GAOG;AACH,MAAM,UAAU,cAAc,CAAC,GAAW,EAAE,UAAkB;IAC5D,IAAI,MAAe,CAAC;IACpB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC3B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,MAAM,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,cAAc,CAAC;QACvE,MAAM,IAAI,KAAK,CAAC,0BAA0B,UAAU,KAAK,MAAM,EAAE,CAAC,CAAC;IACrE,CAAC;IACD,IACE,OAAO,MAAM,KAAK,QAAQ;QAC1B,MAAM,KAAK,IAAI;QACf,CAAC,KAAK,CAAC,OAAO,CAAE,MAAgC,CAAC,OAAO,CAAC,EACzD,CAAC;QACD,MAAM,IAAI,KAAK,CACb,2BAA2B,UAAU,+BAA+B;YAClE,0CAA0C,CAC7C,CAAC;IACJ,CAAC;IACD,OAAO,MAAmB,CAAC;AAC7B,CAAC"}
@@ -15,9 +15,28 @@ export interface BaselinePaths {
15
15
  legacyBaseline: string;
16
16
  /** Pre-breakpoint a11y baseline (`<action>/a11y.yaml`); see `legacyBaseline`. */
17
17
  legacyA11yBaseline: string;
18
+ /**
19
+ * Proposed-new-baseline PNG for CI mode, under
20
+ * `<report>/candidates/<action>/<breakpoint>.png`. Mirrors the `baseline`
21
+ * layout exactly (action dir + breakpoint filename) so approving a candidate
22
+ * set is a plain tree copy into `paths.baselines`. Always report-rooted —
23
+ * never affected by the comparison root, which only moves the baseline side.
24
+ */
25
+ candidate: string;
26
+ /** Candidate a11y snapshot (`<report>/candidates/<action>/<breakpoint>.a11y.yaml`). */
27
+ a11yCandidate: string;
18
28
  }
19
29
  export interface StoreOptions {
20
30
  baselinesDir: string;
31
+ /**
32
+ * Root the baseline-side paths (`baseline`, `a11yBaseline`, and the two
33
+ * `legacy*` fallbacks) resolve under. Defaults to `baselinesDir`, so an
34
+ * omitted root reproduces the committed-baselines layout byte-for-byte — the
35
+ * zero-behaviour-change default. Local mode passes the per-machine cache dir
36
+ * here to self-diff against it instead. Report-side artifacts (actual, diff,
37
+ * candidate, a11yActual, a11yCandidate) ignore this and stay report-rooted.
38
+ */
39
+ comparisonRoot?: string;
21
40
  reportDir: string;
22
41
  storyFile: string;
23
42
  actionName: string;
@@ -29,26 +48,89 @@ export interface StoreOptions {
29
48
  breakpoint: string;
30
49
  }
31
50
  /**
32
- * Computes deterministic paths for the baseline (committed), actual
33
- * (regenerated each run), and diff (regenerated when a baseline existed and
34
- * the diff was non-zero) PNGs. Centralised so the runner and the CLI's
35
- * `approve` command agree on layout.
51
+ * The single owner of the `<report>/candidates/` path. The runner writes the
52
+ * candidate tree + a `results.json` copy here, and `approve --from` reads that
53
+ * same tree back. Centralised so the literal `'candidates'` segment has one
54
+ * definition instead of drifting across the runner, the store, and approve.
55
+ */
56
+ export declare function candidatesDir(reportDir: string): string;
57
+ /**
58
+ * The action-name shape the action schema enforces (`/^[a-z0-9-]+$/`,
59
+ * lowercase-kebab). Re-exported here so the trust boundary in `approve --from`
60
+ * can reject a directory name from an untrusted candidate artifact against the
61
+ * exact same invariant the runner produced its layout under, without importing
62
+ * the zod schema. Kept in lock-step with `schema/action.ts` by intent.
63
+ */
64
+ export declare const ACTION_NAME_PATTERN: RegExp;
65
+ /**
66
+ * The filesystem-safe breakpoint segment shape (`/^[a-z0-9_-]+$/`) that {@link
67
+ * breakpointSegment} emits. `approve --from` validates a candidate PNG's
68
+ * breakpoint stem against this so a name outside the runner's own output shape
69
+ * (path separators, dots, traversal) can never reach a write.
70
+ */
71
+ export declare const BREAKPOINT_SEGMENT_PATTERN: RegExp;
72
+ /**
73
+ * Computes deterministic paths for the baseline (the comparison target —
74
+ * committed baselines by default, or the local cache when `comparisonRoot` is
75
+ * supplied), actual (regenerated each run), diff (regenerated when a baseline
76
+ * existed and the diff was non-zero), and candidate (proposed-new-baseline for
77
+ * CI approval) PNGs. Centralised so the runner and the CLI's `approve` command
78
+ * agree on layout.
36
79
  *
37
80
  * Every path is keyed by both action name and breakpoint so per-breakpoint
38
- * captures of the same action stay isolated: baselines nest the breakpoint as a
39
- * filename under the action directory (`<action>/<breakpoint>.png`), while
40
- * report-side artifacts splice it into the filename
41
- * (`<action>.<breakpoint>.actual.png`) since they already share one per-story
42
- * directory.
81
+ * captures of the same action stay isolated: baseline- and candidate-side paths
82
+ * nest the breakpoint as a filename under the action directory
83
+ * (`<action>/<breakpoint>.png`), while report-side actual/diff artifacts splice
84
+ * it into the filename (`<action>.<breakpoint>.actual.png`) since they already
85
+ * share one per-story directory.
86
+ *
87
+ * The baseline side resolves under `comparisonRoot` (default `baselinesDir`), so
88
+ * omitting the root reproduces the committed-baselines layout unchanged; the
89
+ * candidate and report-side paths are always report-rooted and ignore it.
43
90
  */
44
91
  export declare function pathsFor(options: StoreOptions): BaselinePaths;
45
92
  export declare function withBaselineLock<T>(key: string, fn: () => Promise<T>): Promise<T>;
46
93
  export declare function readBaseline(path: string): Promise<Buffer | undefined>;
47
94
  export declare function readJsonBaseline(path: string): Promise<string | undefined>;
48
95
  export declare function writeText(path: string, content: string): Promise<void>;
96
+ /**
97
+ * Losslessly recompresses a PNG buffer. Playwright emits PNGs tuned for capture
98
+ * speed, not size; re-encoding through pngjs applies its default
99
+ * `deflateLevel: 9` plus adaptive per-scanline filtering (filter type chosen per
100
+ * row from the full `[0..4]` set), which typically shrinks the file with zero
101
+ * pixel change. The decode→re-encode round-trip is lossless: pngjs reads and
102
+ * writes 8-bit RGBA, so the recompressed buffer decodes to a byte-identical
103
+ * pixel array (`recompress.test.ts` asserts this).
104
+ *
105
+ * Two safety guarantees:
106
+ * - Never grows a file. A source that is already densely packed (e.g. a
107
+ * future oxipng-optimised baseline flowing through `approve --from`) can
108
+ * re-encode *larger*; in that case the original buffer is returned unchanged.
109
+ * - Never corrupts. Any decode/encode failure (a non-PNG buffer, a codec edge
110
+ * case) is swallowed and the original buffer is returned, so a recompress
111
+ * miss degrades to "write the bytes we were given" rather than a torn file.
112
+ *
113
+ * This is the single lossless-recompress seam for every PNG the tool writes:
114
+ * `writePng` routes through it, so baseline, candidate, cache, actual, and diff
115
+ * writes all inherit it without per-callsite wiring. Deterministic — pngjs's
116
+ * encoder is a pure function of the pixel data and the fixed options above.
117
+ *
118
+ * Deliberately NOT palette-quantised and never lossy: colour type and bit depth
119
+ * are preserved by the RGBA round-trip.
120
+ */
121
+ export declare function recompressPng(png: Buffer): Buffer;
49
122
  export declare function writePng(path: string, png: Buffer): Promise<void>;
50
123
  export declare function deleteIfExists(path: string): Promise<void>;
51
- export declare function copyToBaseline(actualPath: string, baselinePath: string): Promise<void>;
124
+ /**
125
+ * Copies an already-written PNG (the run's `actual`, or a `candidate`) to a new
126
+ * destination verbatim. No recompress here on purpose: the source was produced
127
+ * by `writePng`, which already ran `recompressPng`, so the bytes on disk are the
128
+ * recompressed ones. Copying them forward keeps the destination losslessly
129
+ * recompressed for free. The only non-test caller is `approve` refreshing the
130
+ * per-machine cache; `approve --from` does NOT use this — it reads each source
131
+ * once during validation and writes the retained buffer through {@link writePng}.
132
+ */
133
+ export declare function copyRecompressedPng(actualPath: string, destinationPath: string): Promise<void>;
52
134
  /**
53
135
  * Turns an absolute screenshot path into a path that is relative to the
54
136
  * generated report directory so the HTML report can reference it with a
@@ -1 +1 @@
1
- {"version":3,"file":"baselineStore.d.ts","sourceRoot":"","sources":["../../src/screenshots/baselineStore.ts"],"names":[],"mappings":"AAUA,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB;;;;;;;OAOG;IACH,cAAc,EAAE,MAAM,CAAC;IACvB,iFAAiF;IACjF,kBAAkB,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,YAAY;IAC3B,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,UAAU,EAAE,MAAM,CAAC;CACpB;AAcD;;;;;;;;;;;;GAYG;AACH,wBAAgB,QAAQ,CAAC,OAAO,EAAE,YAAY,GAAG,aAAa,CAuC7D;AAiBD,wBAAgB,gBAAgB,CAAC,CAAC,EAChC,GAAG,EAAE,MAAM,EACX,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GACnB,OAAO,CAAC,CAAC,CAAC,CAcZ;AAED,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAO5E;AAED,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAO7B;AAED,wBAAsB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAG5E;AAED,wBAAsB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAGvE;AAED,wBAAsB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAMhE;AAED,wBAAsB,cAAc,CAClC,UAAU,EAAE,MAAM,EAClB,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,IAAI,CAAC,CAGf;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAClC,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,GACf,MAAM,CAER"}
1
+ {"version":3,"file":"baselineStore.d.ts","sourceRoot":"","sources":["../../src/screenshots/baselineStore.ts"],"names":[],"mappings":"AAYA,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB;;;;;;;OAOG;IACH,cAAc,EAAE,MAAM,CAAC;IACvB,iFAAiF;IACjF,kBAAkB,EAAE,MAAM,CAAC;IAC3B;;;;;;OAMG;IACH,SAAS,EAAE,MAAM,CAAC;IAClB,uFAAuF;IACvF,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,YAAY;IAC3B,YAAY,EAAE,MAAM,CAAC;IACrB;;;;;;;OAOG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,UAAU,EAAE,MAAM,CAAC;CACpB;AAcD;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED;;;;;;GAMG;AACH,eAAO,MAAM,mBAAmB,QAAiB,CAAC;AAElD;;;;;GAKG;AACH,eAAO,MAAM,0BAA0B,QAAkB,CAAC;AAE1D;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,QAAQ,CAAC,OAAO,EAAE,YAAY,GAAG,aAAa,CA6C7D;AAiBD,wBAAgB,gBAAgB,CAAC,CAAC,EAChC,GAAG,EAAE,MAAM,EACX,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GACnB,OAAO,CAAC,CAAC,CAAC,CAcZ;AAED,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAO5E;AAED,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAO7B;AAED,wBAAsB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAG5E;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAWjD;AAED,wBAAsB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAGvE;AAED,wBAAsB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAMhE;AAED;;;;;;;;GAQG;AACH,wBAAsB,mBAAmB,CACvC,UAAU,EAAE,MAAM,EAClB,eAAe,EAAE,MAAM,GACtB,OAAO,CAAC,IAAI,CAAC,CAGf;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAClC,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,GACf,MAAM,CAER"}