tuffgal 0.2.0-alpha.2 → 0.2.0-alpha.4

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 (110) hide show
  1. package/CHANGELOG.md +41 -19
  2. package/README.md +6 -6
  3. package/dist/cli.d.ts +5 -5
  4. package/dist/cli.d.ts.map +1 -1
  5. package/dist/cli.js +12 -5
  6. package/dist/cli.js.map +1 -1
  7. package/dist/commands/init.d.ts +1 -1
  8. package/dist/commands/init.js +6 -6
  9. package/dist/commands/supervise.d.ts +3 -3
  10. package/dist/commands/supervise.js +8 -4
  11. package/dist/commands/supervise.js.map +1 -1
  12. package/dist/config.d.ts +29 -13
  13. package/dist/config.d.ts.map +1 -1
  14. package/dist/config.js +5 -2
  15. package/dist/config.js.map +1 -1
  16. package/dist/coverage/screens.d.ts +10 -5
  17. package/dist/coverage/screens.d.ts.map +1 -1
  18. package/dist/coverage/screens.js +18 -8
  19. package/dist/coverage/screens.js.map +1 -1
  20. package/dist/reporter/assets/report.css +11 -11
  21. package/dist/reporter/assets/report.js +81 -21
  22. package/dist/reporter/template.js +56 -45
  23. package/dist/reporter/template.js.map +1 -1
  24. package/dist/runner/approve.d.ts +5 -5
  25. package/dist/runner/approve.js +5 -5
  26. package/dist/runner/approveFrom.d.ts +8 -8
  27. package/dist/runner/approveFrom.js +32 -31
  28. package/dist/runner/approveFrom.js.map +1 -1
  29. package/dist/runner/breakpointPasses.d.ts +16 -9
  30. package/dist/runner/breakpointPasses.d.ts.map +1 -1
  31. package/dist/runner/breakpointPasses.js +17 -9
  32. package/dist/runner/breakpointPasses.js.map +1 -1
  33. package/dist/runner/bridges/devServers.d.ts +10 -1
  34. package/dist/runner/bridges/devServers.d.ts.map +1 -1
  35. package/dist/runner/bridges/devServers.js +16 -8
  36. package/dist/runner/bridges/devServers.js.map +1 -1
  37. package/dist/runner/coverage.d.ts +3 -3
  38. package/dist/runner/coverage.js +5 -5
  39. package/dist/runner/coverage.js.map +1 -1
  40. package/dist/runner/exitCode.d.ts +7 -7
  41. package/dist/runner/exitCode.js +7 -7
  42. package/dist/runner/interpolate.js +1 -1
  43. package/dist/runner/interpolate.js.map +1 -1
  44. package/dist/runner/manifest.d.ts +16 -16
  45. package/dist/runner/manifest.js +12 -12
  46. package/dist/runner/mode.d.ts +2 -2
  47. package/dist/runner/mode.js +4 -4
  48. package/dist/runner/orphanScan.d.ts +3 -3
  49. package/dist/runner/orphanScan.js +5 -5
  50. package/dist/runner/orphanScan.js.map +1 -1
  51. package/dist/runner/resolveLocator.d.ts +5 -5
  52. package/dist/runner/resolveLocator.js +6 -6
  53. package/dist/runner/resolveLocator.js.map +1 -1
  54. package/dist/runner/run.d.ts +55 -18
  55. package/dist/runner/run.d.ts.map +1 -1
  56. package/dist/runner/run.js +118 -67
  57. package/dist/runner/run.js.map +1 -1
  58. package/dist/runner/runAction.d.ts +4 -4
  59. package/dist/runner/runAction.d.ts.map +1 -1
  60. package/dist/runner/runAction.js +72 -34
  61. package/dist/runner/runAction.js.map +1 -1
  62. package/dist/runner/runStory.d.ts +29 -18
  63. package/dist/runner/runStory.d.ts.map +1 -1
  64. package/dist/runner/runStory.js +38 -42
  65. package/dist/runner/runStory.js.map +1 -1
  66. package/dist/runner/scheduler.d.ts +11 -0
  67. package/dist/runner/scheduler.d.ts.map +1 -1
  68. package/dist/runner/scheduler.js +1 -1
  69. package/dist/runner/scheduler.js.map +1 -1
  70. package/dist/runner/steps/intercept.d.ts +6 -4
  71. package/dist/runner/steps/intercept.d.ts.map +1 -1
  72. package/dist/runner/steps/intercept.js.map +1 -1
  73. package/dist/runner/steps/navigate.d.ts +5 -2
  74. package/dist/runner/steps/navigate.d.ts.map +1 -1
  75. package/dist/runner/steps/navigate.js +17 -3
  76. package/dist/runner/steps/navigate.js.map +1 -1
  77. package/dist/runner/steps/read.d.ts +1 -1
  78. package/dist/runner/steps/read.js +1 -1
  79. package/dist/runner/steps/scroll.d.ts +1 -1
  80. package/dist/runner/steps/scroll.d.ts.map +1 -1
  81. package/dist/runner/steps/scroll.js +3 -2
  82. package/dist/runner/steps/scroll.js.map +1 -1
  83. package/dist/schema/action.d.ts +2 -2
  84. package/dist/schema/action.d.ts.map +1 -1
  85. package/dist/schema/action.js +58 -13
  86. package/dist/schema/action.js.map +1 -1
  87. package/dist/schema/load.js +1 -1
  88. package/dist/schema/load.js.map +1 -1
  89. package/dist/schema/result.d.ts +22 -22
  90. package/dist/schema/result.d.ts.map +1 -1
  91. package/dist/schema/result.js +1 -1
  92. package/dist/schema/story.d.ts +1 -1
  93. package/dist/schema/story.js +5 -5
  94. package/dist/screenshots/baselineStore.d.ts +44 -21
  95. package/dist/screenshots/baselineStore.d.ts.map +1 -1
  96. package/dist/screenshots/baselineStore.js +59 -28
  97. package/dist/screenshots/baselineStore.js.map +1 -1
  98. package/dist/screenshots/capture.d.ts +35 -4
  99. package/dist/screenshots/capture.d.ts.map +1 -1
  100. package/dist/screenshots/capture.js +61 -9
  101. package/dist/screenshots/capture.js.map +1 -1
  102. package/dist/screenshots/diff.d.ts +57 -8
  103. package/dist/screenshots/diff.d.ts.map +1 -1
  104. package/dist/screenshots/diff.js +78 -28
  105. package/dist/screenshots/diff.js.map +1 -1
  106. package/dist/util.d.ts +23 -2
  107. package/dist/util.d.ts.map +1 -1
  108. package/dist/util.js +42 -2
  109. package/dist/util.js.map +1 -1
  110. package/package.json +4 -8
@@ -1 +1 @@
1
- {"version":3,"file":"interpolate.js","sourceRoot":"","sources":["../../src/runner/interpolate.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,MAAM,UAAU,WAAW,CACzB,QAAgB,EAChB,UAAkC;IAElC,yEAAyE;IACzE,8EAA8E;IAC9E,6EAA6E;IAC7E,OAAO,QAAQ,CAAC,OAAO,CAAC,kBAAkB,EAAE,CAAC,MAAM,EAAE,IAAY,EAAE,EAAE;QACnE,MAAM,KAAK,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC;QAC/B,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxB,MAAM,IAAI,KAAK,CAAC,sBAAsB,IAAI,mBAAmB,QAAQ,GAAG,CAAC,CAAC;QAC5E,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe,CAC7B,IAAU,EACV,UAAkC;IAElC,OAAO;QACL,GAAG,IAAI;QACP,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI;QAChE,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACrB,CAAC,CAAC,WAAW,CAAC,IAAI,CAAC,QAAQ,EAAE,UAAU,CAAC;YACxC,CAAC,CAAC,IAAI,CAAC,QAAQ;KAClB,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"interpolate.js","sourceRoot":"","sources":["../../src/runner/interpolate.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,MAAM,UAAU,WAAW,CACzB,QAAgB,EAChB,UAAkC;IAElC,yEAAyE;IACzE,6EAA6E;IAC7E,6EAA6E;IAC7E,OAAO,QAAQ,CAAC,OAAO,CAAC,kBAAkB,EAAE,CAAC,MAAM,EAAE,IAAY,EAAE,EAAE;QACnE,MAAM,KAAK,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC;QAC/B,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxB,MAAM,IAAI,KAAK,CAAC,sBAAsB,IAAI,mBAAmB,QAAQ,GAAG,CAAC,CAAC;QAC5E,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe,CAC7B,IAAU,EACV,UAAkC;IAElC,OAAO;QACL,GAAG,IAAI;QACP,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI;QAChE,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACrB,CAAC,CAAC,WAAW,CAAC,IAAI,CAAC,QAAQ,EAAE,UAAU,CAAC;YACxC,CAAC,CAAC,IAAI,CAAC,QAAQ;KAClB,CAAC;AACJ,CAAC"}
@@ -1,7 +1,7 @@
1
1
  import type { ResolvedConfig } from '../config.ts';
2
2
  /**
3
3
  * Bump when the capture pipeline changes behaviour in a way that shifts pixels
4
- * for the same input UI — a new default screenshot option, a change to how the
4
+ * for the same input UI: a new default screenshot option, a change to how the
5
5
  * clock is frozen, a mask-application tweak, etc. A committed baseline set was
6
6
  * rendered under one `CAPTURE_SCHEMA`; a run under a higher value is a pixel-
7
7
  * affecting mismatch (see {@link PIXEL_AFFECTING_KEYS}) that prompts re-approve.
@@ -26,7 +26,7 @@ export interface ManifestBreakpoint {
26
26
  * to detect when the current capture environment has drifted from the one the
27
27
  * committed baselines were rendered under.
28
28
  *
29
- * `tuffgalVersion` and `playwrightVersion` are informational only — they are
29
+ * `tuffgalVersion` and `playwrightVersion` are informational only. They are
30
30
  * recorded for debugging but never contribute to a mismatch (a tuffgal patch
31
31
  * release or a Playwright bump that does not move the bundled browser leaves
32
32
  * pixels untouched). Every other field is pixel-affecting; see
@@ -54,7 +54,7 @@ export interface EnvironmentManifest {
54
54
  * shape, not the pixels); `tuffgalVersion`/`playwrightVersion` are excluded as
55
55
  * informational. `browser` is excluded because a browser-*name* change is
56
56
  * already implied by `browserVersion` (which carries the vendor+version string)
57
- * and the runner only launches chromium today — comparing the name would add a
57
+ * and the runner only launches chromium today. Comparing the name would add a
58
58
  * redundant key with no independent signal.
59
59
  */
60
60
  export declare const PIXEL_AFFECTING_KEYS: readonly ["captureSchema", "browserVersion", "platform", "captureMode", "breakpoints", "deviceScaleFactor", "frozenTime"];
@@ -62,17 +62,17 @@ export type PixelAffectingKey = (typeof PIXEL_AFFECTING_KEYS)[number];
62
62
  /**
63
63
  * Result of reading `<baselines>/manifest.json`. Three outcomes the caller must
64
64
  * distinguish, because they gate differently:
65
- * - `ok` — a well-formed manifest was read; compare it against the run.
66
- * - `missing` — no file on disk. The bootstrap case: the first `approve
65
+ * - `ok`: a well-formed manifest was read; compare it against the run.
66
+ * - `missing`: no file on disk. The bootstrap case: the first `approve
67
67
  * --from` has not written a manifest yet, so `run --ci` treats it as "no
68
- * expectation to violate" — `environment.expected: null`, no mismatch, no
68
+ * expectation to violate": `environment.expected: null`, no mismatch, no
69
69
  * exit 3. Never gate a project that has simply not been approved once.
70
- * - `malformed` — a file exists but is not valid JSON or fails shape checks.
70
+ * - `malformed`: a file exists but is not valid JSON or fails shape checks.
71
71
  * A committed manifest that cannot be parsed is a real problem the run
72
72
  * should surface, but crashing the whole comparison is too blunt. Treated
73
73
  * as a mismatch-with-note: `expected: null`, `mismatch: true`, and
74
74
  * `mismatchKeys: ['manifest']` so the report/comment can say "the committed
75
- * manifest is unreadable — re-approve to rewrite it" without aborting.
75
+ * manifest is unreadable. Re-approve to rewrite it" without aborting.
76
76
  */
77
77
  export type ManifestReadResult = {
78
78
  status: 'ok';
@@ -91,7 +91,7 @@ export declare const MANIFEST_FILENAME = "manifest.json";
91
91
  * Reads and shape-checks `<baselinesDir>/manifest.json`. Tolerant by design
92
92
  * (see {@link ManifestReadResult}): a missing file is the bootstrap case, a
93
93
  * malformed one is surfaced as a note rather than a crash. Shape validation is
94
- * intentionally shallow — enough to trust the field reads in {@link
94
+ * intentionally shallow, enough to trust the field reads in {@link
95
95
  * compareEnvironment}, not a full structural mirror.
96
96
  */
97
97
  export declare function readManifest(baselinesDir: string): Promise<ManifestReadResult>;
@@ -103,7 +103,7 @@ export declare function readManifest(baselinesDir: string): Promise<ManifestRead
103
103
  *
104
104
  * Exported so `approve --from` can run the same shape check against a candidate
105
105
  * artifact's `environment.actual` before promoting it into the committed
106
- * manifest — a well-formed-but-wrong-shaped environment block must fail closed,
106
+ * manifest. A well-formed-but-wrong-shaped environment block must fail closed,
107
107
  * not slip into `manifest.json` and silently defeat the drift gate.
108
108
  */
109
109
  export declare function validateManifestShape(value: unknown): string | undefined;
@@ -116,13 +116,13 @@ export interface EnvironmentComparison {
116
116
  * Compares a committed manifest read result against the environment captured
117
117
  * this run, returning which pixel-affecting keys diverged.
118
118
  *
119
- * - `missing` — bootstrap: nothing to violate. `{ mismatch: false }`.
120
- * - `malformed` — unreadable committed manifest: mismatch-with-note,
119
+ * - `missing`: bootstrap: nothing to violate. `{ mismatch: false }`.
120
+ * - `malformed`: unreadable committed manifest: mismatch-with-note,
121
121
  * `mismatchKeys: ['manifest']` (see {@link MALFORMED_MANIFEST_KEY}).
122
- * - `ok` — diff each {@link PIXEL_AFFECTING_KEYS}. `breakpoints` compares deep
122
+ * - `ok`: diff each {@link PIXEL_AFFECTING_KEYS}. `breakpoints` compares deep
123
123
  * and order-sensitively: the manifest records an ordered list, and a
124
124
  * reorder (even at identical dimensions) is treated as a change worth a
125
- * re-approve prompt rather than silently equal — order-sensitivity is the
125
+ * re-approve prompt rather than silently equal. Order-sensitivity is the
126
126
  * conservative pixel-safety choice. Informational keys
127
127
  * (`tuffgalVersion`/`playwrightVersion`) are never compared.
128
128
  */
@@ -133,7 +133,7 @@ export interface CapturedBrowser {
133
133
  version: string;
134
134
  }
135
135
  /**
136
- * Builds the environment manifest describing THIS run — the "actual" side of
136
+ * Builds the environment manifest describing THIS run, the "actual" side of
137
137
  * the comparison and the block a later `approve --from` promotes into the
138
138
  * committed manifest. Everything except the live browser version is derived
139
139
  * from the resolved config and the host, so the caller only supplies the
@@ -142,7 +142,7 @@ export interface CapturedBrowser {
142
142
  * `deviceScaleFactor` is hardcoded `1`: the runner never sets `deviceScaleFactor`
143
143
  * on `browser.newContext` (see `runStory.ts`), so Playwright's default of `1`
144
144
  * applies. If a future wave makes the scale factor configurable, thread it here
145
- * and this constant must track it — the manifest's whole job is to notice a DSF
145
+ * and this constant must track it. The manifest's whole job is to notice a DSF
146
146
  * change, so a stale hardcode here would silently defeat that.
147
147
  */
148
148
  export declare function captureEnvironment(config: ResolvedConfig, browser: CapturedBrowser): EnvironmentManifest;
@@ -3,7 +3,7 @@ import { createRequire } from 'node:module';
3
3
  import { join } from 'node:path';
4
4
  /**
5
5
  * Bump when the capture pipeline changes behaviour in a way that shifts pixels
6
- * for the same input UI — a new default screenshot option, a change to how the
6
+ * for the same input UI: a new default screenshot option, a change to how the
7
7
  * clock is frozen, a mask-application tweak, etc. A committed baseline set was
8
8
  * rendered under one `CAPTURE_SCHEMA`; a run under a higher value is a pixel-
9
9
  * affecting mismatch (see {@link PIXEL_AFFECTING_KEYS}) that prompts re-approve.
@@ -22,7 +22,7 @@ export const SCHEMA_VERSION = 1;
22
22
  * shape, not the pixels); `tuffgalVersion`/`playwrightVersion` are excluded as
23
23
  * informational. `browser` is excluded because a browser-*name* change is
24
24
  * already implied by `browserVersion` (which carries the vendor+version string)
25
- * and the runner only launches chromium today — comparing the name would add a
25
+ * and the runner only launches chromium today. Comparing the name would add a
26
26
  * redundant key with no independent signal.
27
27
  */
28
28
  export const PIXEL_AFFECTING_KEYS = [
@@ -42,7 +42,7 @@ export const MANIFEST_FILENAME = 'manifest.json';
42
42
  * Reads and shape-checks `<baselinesDir>/manifest.json`. Tolerant by design
43
43
  * (see {@link ManifestReadResult}): a missing file is the bootstrap case, a
44
44
  * malformed one is surfaced as a note rather than a crash. Shape validation is
45
- * intentionally shallow — enough to trust the field reads in {@link
45
+ * intentionally shallow, enough to trust the field reads in {@link
46
46
  * compareEnvironment}, not a full structural mirror.
47
47
  */
48
48
  export async function readManifest(baselinesDir) {
@@ -75,7 +75,7 @@ export async function readManifest(baselinesDir) {
75
75
  *
76
76
  * Exported so `approve --from` can run the same shape check against a candidate
77
77
  * artifact's `environment.actual` before promoting it into the committed
78
- * manifest — a well-formed-but-wrong-shaped environment block must fail closed,
78
+ * manifest. A well-formed-but-wrong-shaped environment block must fail closed,
79
79
  * not slip into `manifest.json` and silently defeat the drift gate.
80
80
  */
81
81
  export function validateManifestShape(value) {
@@ -127,13 +127,13 @@ export function validateManifestShape(value) {
127
127
  * Compares a committed manifest read result against the environment captured
128
128
  * this run, returning which pixel-affecting keys diverged.
129
129
  *
130
- * - `missing` — bootstrap: nothing to violate. `{ mismatch: false }`.
131
- * - `malformed` — unreadable committed manifest: mismatch-with-note,
130
+ * - `missing`: bootstrap: nothing to violate. `{ mismatch: false }`.
131
+ * - `malformed`: unreadable committed manifest: mismatch-with-note,
132
132
  * `mismatchKeys: ['manifest']` (see {@link MALFORMED_MANIFEST_KEY}).
133
- * - `ok` — diff each {@link PIXEL_AFFECTING_KEYS}. `breakpoints` compares deep
133
+ * - `ok`: diff each {@link PIXEL_AFFECTING_KEYS}. `breakpoints` compares deep
134
134
  * and order-sensitively: the manifest records an ordered list, and a
135
135
  * reorder (even at identical dimensions) is treated as a change worth a
136
- * re-approve prompt rather than silently equal — order-sensitivity is the
136
+ * re-approve prompt rather than silently equal. Order-sensitivity is the
137
137
  * conservative pixel-safety choice. Informational keys
138
138
  * (`tuffgalVersion`/`playwrightVersion`) are never compared.
139
139
  */
@@ -161,7 +161,7 @@ export function compareEnvironment(expected, actual) {
161
161
  }
162
162
  /**
163
163
  * Deep, order-sensitive equality of two breakpoint lists: same length, and each
164
- * position matches on `name`/`width`/`height`. Order-sensitive on purpose — see
164
+ * position matches on `name`/`width`/`height`. Order-sensitive on purpose. See
165
165
  * {@link compareEnvironment}.
166
166
  */
167
167
  function breakpointsEqual(a, b) {
@@ -178,7 +178,7 @@ function breakpointsEqual(a, b) {
178
178
  const requirePackage = createRequire(import.meta.url);
179
179
  /**
180
180
  * Reads tuffgal's own version from its `package.json`. Resolved relative to this
181
- * module so it works from both `src/` (dev/test) and the built `dist/` tree —
181
+ * module so it works from both `src/` (dev/test) and the built `dist/` tree.
182
182
  * `../../package.json` is the package root in both layouts. Informational only.
183
183
  */
184
184
  function readTuffgalVersion() {
@@ -195,7 +195,7 @@ function readPlaywrightVersion() {
195
195
  return pkg.version;
196
196
  }
197
197
  /**
198
- * Builds the environment manifest describing THIS run — the "actual" side of
198
+ * Builds the environment manifest describing THIS run, the "actual" side of
199
199
  * the comparison and the block a later `approve --from` promotes into the
200
200
  * committed manifest. Everything except the live browser version is derived
201
201
  * from the resolved config and the host, so the caller only supplies the
@@ -204,7 +204,7 @@ function readPlaywrightVersion() {
204
204
  * `deviceScaleFactor` is hardcoded `1`: the runner never sets `deviceScaleFactor`
205
205
  * on `browser.newContext` (see `runStory.ts`), so Playwright's default of `1`
206
206
  * applies. If a future wave makes the scale factor configurable, thread it here
207
- * and this constant must track it — the manifest's whole job is to notice a DSF
207
+ * and this constant must track it. The manifest's whole job is to notice a DSF
208
208
  * change, so a stale hardcode here would silently defeat that.
209
209
  */
210
210
  export function captureEnvironment(config, browser) {
@@ -26,13 +26,13 @@ export interface RunModeInputs {
26
26
  * - `--ci` and `--local` are mutually exclusive; passing both throws.
27
27
  * - An explicit flag always wins over the environment.
28
28
  * - With no flag, default to `ci` when `CI` is truthy (non-empty and not an
29
- * explicit negation — see {@link FALSY_CI}), else `local`.
29
+ * explicit negation, see {@link FALSY_CI}), else `local`.
30
30
  */
31
31
  export declare function resolveRunMode(inputs: RunModeInputs): RunMode;
32
32
  /**
33
33
  * The baseline root a run compares against for the given mode: the committed
34
34
  * `paths.baselines` set in CI mode, the per-machine gitignored `paths.localCache`
35
- * in local mode. This is the single source of the mode→root mapping — both the
35
+ * in local mode. This is the single source of the mode→root mapping, both the
36
36
  * per-action comparison in `runAction` and the screen-coverage metric in `run`
37
37
  * select their root through here, so the two can never drift. Encodes the PRD
38
38
  * invariant that a local run never reads `paths.baselines`.
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * Strings a CI system might export to explicitly opt OUT of CI behaviour. Most
3
3
  * CI providers (GitHub Actions, Travis, CircleCI, GitLab, …) set `CI=true`, but
4
- * the de-facto convention — as codified by the widely-used `ci-info`/`is-ci`
5
- * packages — is that any non-empty `CI` value counts as "in CI" unless it is one
4
+ * the de-facto convention (as codified by the widely-used `ci-info`/`is-ci`
5
+ * packages) is that any non-empty `CI` value counts as "in CI" unless it is one
6
6
  * of these explicit negations. We mirror that so a developer who exports
7
7
  * `CI=false` locally still gets local mode.
8
8
  */
@@ -13,7 +13,7 @@ const FALSY_CI = new Set(['', '0', 'false']);
13
13
  * - `--ci` and `--local` are mutually exclusive; passing both throws.
14
14
  * - An explicit flag always wins over the environment.
15
15
  * - With no flag, default to `ci` when `CI` is truthy (non-empty and not an
16
- * explicit negation — see {@link FALSY_CI}), else `local`.
16
+ * explicit negation, see {@link FALSY_CI}), else `local`.
17
17
  */
18
18
  export function resolveRunMode(inputs) {
19
19
  if (inputs.ci && inputs.local) {
@@ -32,7 +32,7 @@ export function resolveRunMode(inputs) {
32
32
  /**
33
33
  * The baseline root a run compares against for the given mode: the committed
34
34
  * `paths.baselines` set in CI mode, the per-machine gitignored `paths.localCache`
35
- * in local mode. This is the single source of the mode→root mapping — both the
35
+ * in local mode. This is the single source of the mode→root mapping, both the
36
36
  * per-action comparison in `runAction` and the screen-coverage metric in `run`
37
37
  * select their root through here, so the two can never drift. Encodes the PRD
38
38
  * invariant that a local run never reads `paths.baselines`.
@@ -34,16 +34,16 @@ export declare function shouldScanForOrphans(mode: RunMode, storyFilter: string
34
34
  /**
35
35
  * The distinct set of action names that ran at least one result this run. An
36
36
  * action directory under `paths.baselines` whose name is absent from this set
37
- * had no story compare against it — it is a candidate orphan. Derived from the
37
+ * had no story compare against it. It is a candidate orphan. Derived from the
38
38
  * flat `stories[].actions[]` walk so `skipped` entries (an earlier step failed)
39
39
  * still count the action as executed: the baseline was reachable this run, the
40
- * story simply broke before reaching it, which is a failure to surface — not a
40
+ * story simply broke before reaching it, which is a failure to surface, not a
41
41
  * baseline to retire.
42
42
  */
43
43
  export declare function executedActionNames(stories: RunResult['stories']): Set<string>;
44
44
  /**
45
45
  * Scans `baselinesDir` for orphaned committed baselines: action directories no
46
- * story executed this run. Detection only — the returned entries carry the
46
+ * story executed this run. Detection only. The returned entries carry the
47
47
  * absolute paths a later `approve --prune` deletes; this function never touches
48
48
  * the filesystem beyond reading directory listings.
49
49
  *
@@ -22,7 +22,7 @@ export const LEGACY_BREAKPOINT = 'legacy';
22
22
  * Non-action files that may sit at the baselines root and must never be treated
23
23
  * as orphan candidates. `manifest.json` (the environment manifest, wave 6) is
24
24
  * the known one; it is a file, not an action directory, so the readdir walk
25
- * already skips it by virtue of only descending into directories — this set is a
25
+ * already skips it by virtue of only descending into directories. This set is a
26
26
  * belt-and-suspenders guard for any future flat file a sibling wave drops here.
27
27
  */
28
28
  const NON_ACTION_ENTRIES = new Set(['manifest.json']);
@@ -44,10 +44,10 @@ export function shouldScanForOrphans(mode, storyFilter) {
44
44
  /**
45
45
  * The distinct set of action names that ran at least one result this run. An
46
46
  * action directory under `paths.baselines` whose name is absent from this set
47
- * had no story compare against it — it is a candidate orphan. Derived from the
47
+ * had no story compare against it. It is a candidate orphan. Derived from the
48
48
  * flat `stories[].actions[]` walk so `skipped` entries (an earlier step failed)
49
49
  * still count the action as executed: the baseline was reachable this run, the
50
- * story simply broke before reaching it, which is a failure to surface — not a
50
+ * story simply broke before reaching it, which is a failure to surface, not a
51
51
  * baseline to retire.
52
52
  */
53
53
  export function executedActionNames(stories) {
@@ -61,7 +61,7 @@ export function executedActionNames(stories) {
61
61
  }
62
62
  /**
63
63
  * Scans `baselinesDir` for orphaned committed baselines: action directories no
64
- * story executed this run. Detection only — the returned entries carry the
64
+ * story executed this run. Detection only. The returned entries carry the
65
65
  * absolute paths a later `approve --prune` deletes; this function never touches
66
66
  * the filesystem beyond reading directory listings.
67
67
  *
@@ -92,7 +92,7 @@ export async function scanOrphanedBaselines(baselinesDir, executed) {
92
92
  }));
93
93
  }
94
94
  catch {
95
- // No baselines directory yet (a never-approved project) — nothing to orphan.
95
+ // No baselines directory yet (a never-approved project), nothing to orphan.
96
96
  return [];
97
97
  }
98
98
  const orphans = [];
@@ -1 +1 @@
1
- {"version":3,"file":"orphanScan.js","sourceRoot":"","sources":["../../src/runner/orphanScan.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"orphanScan.js","sourceRoot":"","sources":["../../src/runner/orphanScan.ts"],"names":[],"mappings":"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"}
@@ -2,14 +2,14 @@ import type { Locator, Page } from 'playwright';
2
2
  import type { Hint } from '../schema/action.ts';
3
3
  /**
4
4
  * Resolves a hint to a Playwright Locator using a precedence chain. The MVP
5
- * intentionally stays literal — no LLM fallback. When this throws,
5
+ * intentionally stays literal, no LLM fallback. When this throws,
6
6
  * `runAction` records `LocatorNotFound` and the parent story fails fast.
7
7
  *
8
8
  * Order:
9
- * 1. `role + text` — strongest ARIA contract.
10
- * 2. `role` alone — when no name is supplied.
11
- * 3. `selector` — explicit escape hatch / cached resolution.
12
- * 4. `text` alone — last resort because text-only locators are noisy.
9
+ * 1. `role + text`: strongest ARIA contract.
10
+ * 2. `role` alone: when no name is supplied.
11
+ * 3. `selector`: explicit escape hatch / cached resolution.
12
+ * 4. `text` alone: last resort because text-only locators are noisy.
13
13
  */
14
14
  export declare function resolveLocator(page: Page, hint: Hint): Locator;
15
15
  export declare class LocatorHintError extends Error {
@@ -1,13 +1,13 @@
1
1
  /**
2
2
  * Resolves a hint to a Playwright Locator using a precedence chain. The MVP
3
- * intentionally stays literal — no LLM fallback. When this throws,
3
+ * intentionally stays literal, no LLM fallback. When this throws,
4
4
  * `runAction` records `LocatorNotFound` and the parent story fails fast.
5
5
  *
6
6
  * Order:
7
- * 1. `role + text` — strongest ARIA contract.
8
- * 2. `role` alone — when no name is supplied.
9
- * 3. `selector` — explicit escape hatch / cached resolution.
10
- * 4. `text` alone — last resort because text-only locators are noisy.
7
+ * 1. `role + text`: strongest ARIA contract.
8
+ * 2. `role` alone: when no name is supplied.
9
+ * 3. `selector`: explicit escape hatch / cached resolution.
10
+ * 4. `text` alone: last resort because text-only locators are noisy.
11
11
  */
12
12
  export function resolveLocator(page, hint) {
13
13
  if (hint.role && hint.text) {
@@ -22,7 +22,7 @@ export function resolveLocator(page, hint) {
22
22
  if (hint.text) {
23
23
  return page.getByText(hint.text, { exact: false });
24
24
  }
25
- throw new LocatorHintError('hint has no role, selector, or text — cannot resolve');
25
+ throw new LocatorHintError('hint has no role, selector, or text; cannot resolve');
26
26
  }
27
27
  export class LocatorHintError extends Error {
28
28
  constructor(message) {
@@ -1 +1 @@
1
- {"version":3,"file":"resolveLocator.js","sourceRoot":"","sources":["../../src/runner/resolveLocator.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;GAUG;AACH,MAAM,UAAU,cAAc,CAAC,IAAU,EAAE,IAAU;IACnD,IAAI,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;QAC3B,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;IACtE,CAAC;IACD,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;QACd,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACnC,CAAC;IACD,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;QAClB,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACrC,CAAC;IACD,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;QACd,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;IACrD,CAAC;IACD,MAAM,IAAI,gBAAgB,CACxB,sDAAsD,CACvD,CAAC;AACJ,CAAC;AAED,MAAM,OAAO,gBAAiB,SAAQ,KAAK;IACzC,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,kBAAkB,CAAC;IACjC,CAAC;CACF;AAED,MAAM,OAAO,oBAAqB,SAAQ,KAAK;IAC3B,KAAK,CAAW;IAClC,YAAY,IAAU,EAAE,KAAe;QACrC,KAAK,CAAC,2BAA2B,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACzD,IAAI,CAAC,IAAI,GAAG,sBAAsB,CAAC;QACnC,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IACrB,CAAC;CACF"}
1
+ {"version":3,"file":"resolveLocator.js","sourceRoot":"","sources":["../../src/runner/resolveLocator.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;GAUG;AACH,MAAM,UAAU,cAAc,CAAC,IAAU,EAAE,IAAU;IACnD,IAAI,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;QAC3B,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;IACtE,CAAC;IACD,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;QACd,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACnC,CAAC;IACD,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;QAClB,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACrC,CAAC;IACD,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;QACd,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;IACrD,CAAC;IACD,MAAM,IAAI,gBAAgB,CACxB,qDAAqD,CACtD,CAAC;AACJ,CAAC;AAED,MAAM,OAAO,gBAAiB,SAAQ,KAAK;IACzC,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,kBAAkB,CAAC;IACjC,CAAC;CACF;AAED,MAAM,OAAO,oBAAqB,SAAQ,KAAK;IAC3B,KAAK,CAAW;IAClC,YAAY,IAAU,EAAE,KAAe;QACrC,KAAK,CAAC,2BAA2B,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACzD,IAAI,CAAC,IAAI,GAAG,sBAAsB,CAAC;QACnC,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IACrB,CAAC;CACF"}
@@ -1,5 +1,7 @@
1
+ import { type Browser } from 'playwright';
1
2
  import type { ResolvedConfig } from '../config.ts';
2
3
  import type { EnvironmentReport, RunResult, StoryResult, StoryStatus } from '../schema/result.ts';
4
+ import { type ManagedDevServers } from './bridges/devServers.ts';
3
5
  import { type RunMode } from './mode.ts';
4
6
  import { type CapturedBrowser } from './manifest.ts';
5
7
  export interface RunCliOptions {
@@ -21,16 +23,49 @@ export interface RunCliOptions {
21
23
  * Loads every action and story from the configured paths, resets the
22
24
  * consumer-supplied test database, schedules stories according to their
23
25
  * needs/produces DAG, and drives execution across a fixed worker pool.
24
- * Returns the aggregate `RunResult` so the CLI can set the process exit
25
- * code.
26
+ * Returns the aggregate `RunResult` so the CLI can set the process exit code.
27
+ *
28
+ * Owns the run's shared lifecycle: it launches ONE browser (injectable via
29
+ * `launchBrowser` for tests) that every story reuses (each opening its own
30
+ * isolated context per breakpoint) and registers a SIGINT/SIGTERM handler that
31
+ * closes that browser and stops any `--manage-servers` dev-servers before
32
+ * exiting non-zero, so an interrupted run (Ctrl-C, CI cancel) orphans neither.
33
+ * Both the normal finally path and the signal handler tear down through the
34
+ * same idempotent {@link createRunTeardown}, so nothing double-closes.
35
+ */
36
+ export declare function runAll(config: ResolvedConfig, options: RunCliOptions, launchBrowser?: () => Promise<Browser>): Promise<RunResult>;
37
+ /** The run's teardownable handles: the shared browser and any managed servers. */
38
+ interface RunHandles {
39
+ browser?: Pick<Browser, 'close'>;
40
+ managedServers?: ManagedDevServers;
41
+ }
42
+ /**
43
+ * Builds the run's single teardown: closes the shared browser and stops any
44
+ * managed dev-servers, exactly once. The `done` latch makes it idempotent so
45
+ * the normal finally path and a racing signal handler cannot double-close.
46
+ * Whichever calls first tears down, the other is a no-op. Reads the handles off
47
+ * the shared object at CALL time (not capture time), so a signal that arrives
48
+ * mid-setup still tears down whatever has been launched so far. `allSettled`
49
+ * closes both independently: a browser that throws on close must not prevent
50
+ * the dev-servers from being stopped.
51
+ */
52
+ export declare function createRunTeardown(handles: RunHandles): () => Promise<void>;
53
+ /**
54
+ * Wraps a {@link createRunTeardown} teardown in a SIGINT/SIGTERM handler: tear
55
+ * the run's handles down, then exit non-zero (an interrupted run is a failure,
56
+ * not a clean pass). `exit` is injected (defaulting to `process.exit`) so a
57
+ * test can drive the handler directly, asserting it closes the browser, stops
58
+ * the servers, and requests a non-zero exit, without raising a real signal in
59
+ * the test runner. Idempotent by construction: it delegates to the shared
60
+ * teardown, so a second signal cannot double-close.
26
61
  */
27
- export declare function runAll(config: ResolvedConfig, options: RunCliOptions): Promise<RunResult>;
62
+ export declare function createSignalTeardownHandler(teardown: () => Promise<void>, exit?: (code: number) => void): () => void;
28
63
  /**
29
64
  * Copies the just-written `results.json` into `<report>/candidates/` so the
30
65
  * candidate tree is a self-contained approval artifact. `writeReport` has
31
66
  * already written `<report>/results.json`; this places a sibling copy under
32
67
  * candidates. The candidates dir may not exist yet (a run where every action
33
- * passed writes no candidate renders), so it is created first — an empty
68
+ * passed writes no candidate renders), so it is created first. An empty
34
69
  * `candidates/` carrying only `results.json` still correctly describes "nothing
35
70
  * to approve".
36
71
  */
@@ -40,21 +75,21 @@ export declare function copyResultsIntoCandidates(reportDir: string): Promise<vo
40
75
  * `paths.baselines` in CI mode, per-machine `paths.localCache` in local mode.
41
76
  * A thin, coverage-named alias over the shared {@link comparisonRootFor} so the
42
77
  * metric counts screens against the SAME set the run (via `runAction`) diffed
43
- * against — the mode→root mapping now lives once, in `mode.ts`. Preserved as a
78
+ * against. The mode→root mapping now lives once, in `mode.ts`. Preserved as a
44
79
  * named export because the coverage metric's PRD invariant (a local run never
45
80
  * reads `paths.baselines`) reads most clearly against a coverage-specific name.
46
81
  */
47
82
  export declare function coverageComparisonRoot(config: ResolvedConfig, mode: RunMode): string;
48
83
  /**
49
84
  * Builds the run's {@link EnvironmentReport}: the environment this run captured
50
- * under (`actual`), and — in CI mode — the committed `<baselines>/manifest.json`
85
+ * under (`actual`), and (in CI mode) the committed `<baselines>/manifest.json`
51
86
  * (`expected`) plus whether their pixel-affecting keys diverge.
52
87
  *
53
- * The live browser-version probe ({@link captureBrowserIdentity}) runs in CI
54
- * mode ONLY. Local mode never reads `paths.baselines`, so `expected` is always
55
- * `null` and `browserVersion` is never compared against anything — launching a
56
- * throwaway chromium just to stamp a value nothing reads is pure cost, so local
57
- * mode fills the browser identity with an empty sentinel and skips the probe.
88
+ * The live browser-version probe runs in CI mode ONLY. Local mode never reads
89
+ * `paths.baselines`, so `expected` is always `null` and `browserVersion` is
90
+ * never compared against anything. Reading a browser version just to stamp a
91
+ * value nothing reads is pure cost, so local mode fills the browser identity
92
+ * with an empty sentinel and skips the probe entirely.
58
93
  *
59
94
  * In CI mode a missing manifest is the bootstrap case (no expectation yet, no
60
95
  * mismatch); a malformed one surfaces as a mismatch note (see
@@ -62,16 +97,17 @@ export declare function coverageComparisonRoot(config: ResolvedConfig, mode: Run
62
97
  * it read cleanly, so a malformed file reports `null` here while still driving a
63
98
  * mismatch.
64
99
  *
65
- * `probe` is the live browser-identity reader, injected (defaulting to the real
66
- * {@link captureBrowserIdentity}) so a test can assert it is invoked in CI mode
67
- * and NEVER invoked in local mode without launching a real chromium.
100
+ * `probe` is the live browser-identity reader. `runAll` passes one that reads
101
+ * the run's SHARED browser handle (`browser.version()`), so no throwaway
102
+ * Chromium is launched just to stamp the version; a test injects its own probe
103
+ * to assert it is invoked in CI mode and NEVER invoked in local mode.
68
104
  */
69
- export declare function resolveEnvironmentReport(config: ResolvedConfig, mode: RunMode, probe?: () => Promise<CapturedBrowser>): Promise<EnvironmentReport>;
105
+ export declare function resolveEnvironmentReport(config: ResolvedConfig, mode: RunMode, probe: () => Promise<CapturedBrowser>): Promise<EnvironmentReport>;
70
106
  /**
71
107
  * Rolls a set of story results into the outcome counts. `deleted` counts
72
108
  * orphaned baselines, which are a run-level detection (not a per-story outcome),
73
109
  * so it always seeds `0` here; `runAll` overwrites the run-total `deleted` from
74
- * the orphan scan. Per-breakpoint pass summaries keep `0`, which is correct —
110
+ * the orphan scan. Per-breakpoint pass summaries keep `0`, which is correct.
75
111
  * an orphan is not attributable to a single breakpoint pass.
76
112
  */
77
113
  export declare function summarise(results: StoryResult[]): RunResult['totals'];
@@ -85,13 +121,13 @@ export declare function formatResultLine(status: StoryStatus, actionCount: numbe
85
121
  /**
86
122
  * One Summary bullet for a breakpoint pass: `• <parts> on "<name>" breakpoint`,
87
123
  * where `<parts>` joins only the nonzero outcome categories in a fixed order
88
- * (passed, new, changed, failed) — e.g. `2 passed, 1 changed`. Falls back to
124
+ * (passed, new, changed, failed), e.g. `2 passed, 1 changed`. Falls back to
89
125
  * `0 passed` for the degenerate all-zero pass the run loop never actually
90
126
  * emits (it `continue`s past a pass with no participating stories).
91
127
  */
92
128
  export declare function formatSummaryBullet(name: string, counts: RunResult['totals']): string;
93
129
  /**
94
- * The breakpoint names that drove a merged story to its status — the modes the
130
+ * The breakpoint names that drove a merged story to its status, the modes the
95
131
  * reader actually needs to inspect. A `changed` story that drifted only at
96
132
  * `desktop` tags `[desktop]`, not the `mobile` pass that stayed clean. Returns
97
133
  * empty (no tag) when the story spans a single breakpoint, since there is
@@ -99,4 +135,5 @@ export declare function formatSummaryBullet(name: string, counts: RunResult['tot
99
135
  * tag, so this is derived, not separately tracked.
100
136
  */
101
137
  export declare function drivingBreakpoints(result: StoryResult): string[];
138
+ export {};
102
139
  //# sourceMappingURL=run.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../src/runner/run.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAsB,cAAc,EAAE,MAAM,cAAc,CAAC;AAEvE,OAAO,KAAK,EACV,iBAAiB,EACjB,SAAS,EACT,WAAW,EACX,WAAW,EACZ,MAAM,qBAAqB,CAAC;AA8B7B,OAAO,EAAqB,KAAK,OAAO,EAAE,MAAM,WAAW,CAAC;AAC5D,OAAO,EAIL,KAAK,eAAe,EAErB,MAAM,eAAe,CAAC;AAGvB,MAAM,WAAW,aAAa;IAC5B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;;;;OAMG;IACH,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAUD;;;;;;GAMG;AACH,wBAAsB,MAAM,CAC1B,MAAM,EAAE,cAAc,EACtB,OAAO,EAAE,aAAa,GACrB,OAAO,CAAC,SAAS,CAAC,CA0KpB;AA4DD;;;;;;;;GAQG;AACH,wBAAsB,yBAAyB,CAC7C,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC,CAIf;AAED;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,cAAc,EACtB,IAAI,EAAE,OAAO,GACZ,MAAM,CAER;AA6BD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAsB,wBAAwB,CAC5C,MAAM,EAAE,cAAc,EACtB,IAAI,EAAE,OAAO,EACb,KAAK,GAAE,MAAM,OAAO,CAAC,eAAe,CAA0B,GAC7D,OAAO,CAAC,iBAAiB,CAAC,CAe5B;AAED;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,WAAW,EAAE,GAAG,SAAS,CAAC,QAAQ,CAAC,CASrE;AAUD;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,WAAW,EACnB,WAAW,EAAE,MAAM,EACnB,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,MAAM,GACX,MAAM,CAIR;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,SAAS,CAAC,QAAQ,CAAC,GAC1B,MAAM,CAQR;AAoBD;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,EAAE,CAuBhE"}
1
+ {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../src/runner/run.ts"],"names":[],"mappings":"AAIA,OAAO,EAAY,KAAK,OAAO,EAAE,MAAM,YAAY,CAAC;AACpD,OAAO,KAAK,EAAsB,cAAc,EAAE,MAAM,cAAc,CAAC;AAEvE,OAAO,KAAK,EACV,iBAAiB,EACjB,SAAS,EACT,WAAW,EACX,WAAW,EACZ,MAAM,qBAAqB,CAAC;AAM7B,OAAO,EAEL,KAAK,iBAAiB,EACvB,MAAM,yBAAyB,CAAC;AAqBjC,OAAO,EAAqB,KAAK,OAAO,EAAE,MAAM,WAAW,CAAC;AAC5D,OAAO,EAIL,KAAK,eAAe,EAErB,MAAM,eAAe,CAAC;AAGvB,MAAM,WAAW,aAAa;IAC5B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;;;;OAMG;IACH,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAUD;;;;;;;;;;;;;GAaG;AACH,wBAAsB,MAAM,CAC1B,MAAM,EAAE,cAAc,EACtB,OAAO,EAAE,aAAa,EACtB,aAAa,GAAE,MAAM,OAAO,CAAC,OAAO,CACY,GAC/C,OAAO,CAAC,SAAS,CAAC,CAkMpB;AAED,kFAAkF;AAClF,UAAU,UAAU;IAClB,OAAO,CAAC,EAAE,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IACjC,cAAc,CAAC,EAAE,iBAAiB,CAAC;CACpC;AAED;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,UAAU,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,CAU1E;AAED;;;;;;;;GAQG;AACH,wBAAgB,2BAA2B,CACzC,QAAQ,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,EAC7B,IAAI,GAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAmC,GAC1D,MAAM,IAAI,CAIZ;AAoED;;;;;;;;GAQG;AACH,wBAAsB,yBAAyB,CAC7C,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC,CAIf;AAED;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,cAAc,EACtB,IAAI,EAAE,OAAO,GACZ,MAAM,CAER;AAYD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAsB,wBAAwB,CAC5C,MAAM,EAAE,cAAc,EACtB,IAAI,EAAE,OAAO,EACb,KAAK,EAAE,MAAM,OAAO,CAAC,eAAe,CAAC,GACpC,OAAO,CAAC,iBAAiB,CAAC,CAe5B;AAED;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,WAAW,EAAE,GAAG,SAAS,CAAC,QAAQ,CAAC,CASrE;AAUD;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,WAAW,EACnB,WAAW,EAAE,MAAM,EACnB,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,MAAM,GACX,MAAM,CAIR;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,SAAS,CAAC,QAAQ,CAAC,GAC1B,MAAM,CAQR;AAoBD;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,EAAE,CAuBhE"}