@juxtapose/core 0.8.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/dist/agent/handoff.d.ts +19 -1
  2. package/dist/agent/handoff.js +54 -2
  3. package/dist/agent/handoff.js.map +1 -1
  4. package/dist/analysis/affected.d.ts +17 -0
  5. package/dist/analysis/affected.js +34 -0
  6. package/dist/analysis/affected.js.map +1 -1
  7. package/dist/browser/flags.d.ts +14 -0
  8. package/dist/browser/flags.js +15 -0
  9. package/dist/browser/flags.js.map +1 -1
  10. package/dist/browser/page.d.ts +19 -1
  11. package/dist/browser/page.js +16 -3
  12. package/dist/browser/page.js.map +1 -1
  13. package/dist/capture/capture.d.ts +10 -1
  14. package/dist/capture/capture.js +121 -16
  15. package/dist/capture/capture.js.map +1 -1
  16. package/dist/capture/config-errors.d.ts +10 -0
  17. package/dist/capture/config-errors.js +20 -0
  18. package/dist/capture/config-errors.js.map +1 -0
  19. package/dist/capture/discover.d.ts +28 -0
  20. package/dist/capture/discover.js +109 -0
  21. package/dist/capture/discover.js.map +1 -0
  22. package/dist/capture/idle.d.ts +88 -0
  23. package/dist/capture/idle.js +545 -0
  24. package/dist/capture/idle.js.map +1 -0
  25. package/dist/capture/pool.d.ts +8 -0
  26. package/dist/capture/pool.js +2 -0
  27. package/dist/capture/pool.js.map +1 -1
  28. package/dist/capture/settle-options.d.ts +78 -0
  29. package/dist/capture/settle-options.js +234 -0
  30. package/dist/capture/settle-options.js.map +1 -0
  31. package/dist/capture/settle.d.ts +0 -18
  32. package/dist/capture/settle.js +7 -100
  33. package/dist/capture/settle.js.map +1 -1
  34. package/dist/capture/snap.d.ts +3 -0
  35. package/dist/capture/snap.js +7 -1
  36. package/dist/capture/snap.js.map +1 -1
  37. package/dist/capture/variants.d.ts +44 -0
  38. package/dist/capture/variants.js +204 -0
  39. package/dist/capture/variants.js.map +1 -0
  40. package/dist/diff/structural/deltas.d.ts +2 -3
  41. package/dist/diff/structural/deltas.js +3 -3
  42. package/dist/diff/structural/deltas.js.map +1 -1
  43. package/dist/diff/structural.d.ts +4 -1
  44. package/dist/diff/structural.js +3 -0
  45. package/dist/diff/structural.js.map +1 -1
  46. package/dist/index.d.ts +6 -3
  47. package/dist/index.js +9 -2
  48. package/dist/index.js.map +1 -1
  49. package/dist/platform.d.ts +20 -0
  50. package/dist/platforms/urls.js +5 -1
  51. package/dist/platforms/urls.js.map +1 -1
  52. package/dist/report/comment.js +19 -8
  53. package/dist/report/comment.js.map +1 -1
  54. package/dist/report/format.d.ts +17 -0
  55. package/dist/report/format.js +16 -0
  56. package/dist/report/format.js.map +1 -1
  57. package/dist/report/html.js +40 -6
  58. package/dist/report/html.js.map +1 -1
  59. package/dist/run/run.d.ts +6 -0
  60. package/dist/run/run.js +32 -0
  61. package/dist/run/run.js.map +1 -1
  62. package/dist/types.d.ts +107 -2
  63. package/package.json +1 -1
@@ -0,0 +1,78 @@
1
+ import type { CaptureFlag, SettleParams, SettleReport, StoryParams } from '../types.js';
2
+ import { SettleConfigError } from './config-errors.js';
3
+ export { SettleConfigError };
4
+ /** Default cap on the settle wait. Measured on the examples (29 stories, 5
5
+ * runs each): the event-driven wait settles in 34-39 ms p50, 43 ms max, so
6
+ * the cap only matters for a page that really has outstanding work; 1500 ms
7
+ * lets a 1000 ms timer finish with room to paint. */
8
+ export declare const DEFAULT_SETTLE_TIMEOUT_MS = 1500;
9
+ /** A cap above this is a typo, not a slow page: it would be paid per story. */
10
+ export declare const MAX_SETTLE_TIMEOUT_MS = 60000;
11
+ /** Timers longer than this are never waited for, at any cap. The spike
12
+ * measured horizon 1000 / cap 1500; the horizon must stay below the cap or a
13
+ * long timer is tracked and then abandoned at the cap, so it shrinks with a
14
+ * smaller cap (two thirds of it). */
15
+ export declare const MAX_TIMER_HORIZON_MS = 1000;
16
+ /** Request types that never finish while the page is open, or never affect
17
+ * pixels. A WebSocket handshake emits no request events today; it is listed
18
+ * so it stays ignored if Chrome starts reporting it. */
19
+ export declare const DEFAULT_NETWORK_IGNORE: readonly string[];
20
+ export interface UrlPattern {
21
+ /** As written, for messages. */
22
+ source: string;
23
+ /** Canonical resource type, when the pattern has `type:`. */
24
+ type?: string;
25
+ /** Absent when the pattern is a type alone. */
26
+ glob?: RegExp;
27
+ /** A glob starting with `/` matches the URL's path, not the whole URL. */
28
+ pathOnly: boolean;
29
+ }
30
+ /**
31
+ * `[type:<ResourceType>] [<glob>]`, at least one of the two. In the glob `**`
32
+ * matches anything, `*` anything but `/`, `?` one character but `/`. A glob
33
+ * starting with `/` matches the path (`/api/feed`), anything else the whole
34
+ * URL (`https://cdn.example.com/**`). Throws with the reason; the caller
35
+ * prefixes where the pattern came from.
36
+ */
37
+ export declare function parsePattern(source: string): UrlPattern;
38
+ export declare function matchPattern(p: UrlPattern, url: string, type: string | undefined): boolean;
39
+ /** One layer: the options a story, the project or a call can set. */
40
+ export interface SettleLayer {
41
+ settle?: SettleParams;
42
+ waitForSelector?: string;
43
+ delay?: number;
44
+ }
45
+ export interface ResolvedSettle {
46
+ timeoutMs: number;
47
+ horizonMs: number;
48
+ ready: boolean;
49
+ ignore: UrlPattern[];
50
+ require: UrlPattern[];
51
+ }
52
+ /** Merge two `settle` objects: scalars from `over`, lists concatenated
53
+ * without duplicates. Keys absent from both stay absent. */
54
+ export declare function mergeSettle(under: SettleParams | undefined, over: SettleParams | undefined): SettleParams | undefined;
55
+ /** The settle wait for one capture, from its layers, lowest first. Assumes
56
+ * each layer passed checkSettle. */
57
+ export declare function resolveSettle(...layers: Array<SettleParams | undefined>): ResolvedSettle;
58
+ /** waitForSelector and delay: call > story > project. */
59
+ export declare function layerScalars(project: SettleLayer | undefined, params: StoryParams, call: SettleLayer | undefined): Pick<StoryParams, 'waitForSelector' | 'delay'>;
60
+ /** The flag a settle report raises. */
61
+ export declare const settleFlag: (s: Pick<SettleReport, "status">) => CaptureFlag;
62
+ /** A settle cap: a whole number of ms, 1 to MAX_SETTLE_TIMEOUT_MS. The CLI
63
+ * and the MCP server check per-call values with this too. */
64
+ export declare function isSettleTimeout(v: unknown): v is number;
65
+ /**
66
+ * Validate a `settle` value where it is declared, so the error names the
67
+ * place to edit. `where` is e.g. `project config` or `story 'button--primary'`.
68
+ * Throws SettleConfigError; returns the value typed.
69
+ */
70
+ export declare function checkSettle(raw: unknown, where: string): SettleParams | undefined;
71
+ /** Validate a pattern list; `fail` makes the error. */
72
+ export declare function checkPatterns(list: unknown, name: string, fail: (m: string) => Error): string[];
73
+ /** The project config's settle options: `settle`, `waitForSelector`, `delay`. */
74
+ export declare function checkProjectSettle(config: {
75
+ settle?: unknown;
76
+ waitForSelector?: unknown;
77
+ delay?: unknown;
78
+ }): void;
@@ -0,0 +1,234 @@
1
+ // Settle options: validation, layering and the URL patterns behind
2
+ // `settle.network.ignore` / `settle.network.require`.
3
+ //
4
+ // Layers, lowest first: built-in defaults < project config < story params
5
+ // (variants included) < per call (CLI flags, MCP tool arguments). Scalars
6
+ // override; pattern lists accumulate, so a story adds to the project's ignore
7
+ // list rather than replacing it, and `require` is the way to take a request
8
+ // back out of an ignore list (both match: require wins).
9
+ //
10
+ // Pure on purpose: nothing here touches a page, so every rule has a unit test.
11
+ import { SettleConfigError } from './config-errors.js';
12
+ export { SettleConfigError };
13
+ /** Default cap on the settle wait. Measured on the examples (29 stories, 5
14
+ * runs each): the event-driven wait settles in 34-39 ms p50, 43 ms max, so
15
+ * the cap only matters for a page that really has outstanding work; 1500 ms
16
+ * lets a 1000 ms timer finish with room to paint. */
17
+ export const DEFAULT_SETTLE_TIMEOUT_MS = 1500;
18
+ /** A cap above this is a typo, not a slow page: it would be paid per story. */
19
+ export const MAX_SETTLE_TIMEOUT_MS = 60_000;
20
+ /** Timers longer than this are never waited for, at any cap. The spike
21
+ * measured horizon 1000 / cap 1500; the horizon must stay below the cap or a
22
+ * long timer is tracked and then abandoned at the cap, so it shrinks with a
23
+ * smaller cap (two thirds of it). */
24
+ export const MAX_TIMER_HORIZON_MS = 1000;
25
+ /** Request types that never finish while the page is open, or never affect
26
+ * pixels. A WebSocket handshake emits no request events today; it is listed
27
+ * so it stays ignored if Chrome starts reporting it. */
28
+ export const DEFAULT_NETWORK_IGNORE = [
29
+ 'type:WebSocket',
30
+ 'type:EventSource',
31
+ 'type:Ping',
32
+ 'type:Prefetch',
33
+ ];
34
+ /** CDP Network.ResourceType values, for `type:` validation. */
35
+ const RESOURCE_TYPES = [
36
+ 'Document',
37
+ 'Stylesheet',
38
+ 'Image',
39
+ 'Media',
40
+ 'Font',
41
+ 'Script',
42
+ 'TextTrack',
43
+ 'XHR',
44
+ 'Fetch',
45
+ 'Prefetch',
46
+ 'EventSource',
47
+ 'WebSocket',
48
+ 'Manifest',
49
+ 'SignedExchange',
50
+ 'Ping',
51
+ 'CSPViolationReport',
52
+ 'Preflight',
53
+ 'FedCM',
54
+ 'Other',
55
+ ];
56
+ /**
57
+ * `[type:<ResourceType>] [<glob>]`, at least one of the two. In the glob `**`
58
+ * matches anything, `*` anything but `/`, `?` one character but `/`. A glob
59
+ * starting with `/` matches the path (`/api/feed`), anything else the whole
60
+ * URL (`https://cdn.example.com/**`). Throws with the reason; the caller
61
+ * prefixes where the pattern came from.
62
+ */
63
+ export function parsePattern(source) {
64
+ const tokens = source.trim().split(/\s+/).filter(Boolean);
65
+ let type;
66
+ let glob;
67
+ for (const t of tokens) {
68
+ if (/^type:/i.test(t)) {
69
+ if (type !== undefined)
70
+ throw new Error(`pattern '${source}' has two types`);
71
+ const name = t.slice(5);
72
+ const known = RESOURCE_TYPES.find((r) => r.toLowerCase() === name.toLowerCase());
73
+ if (!known)
74
+ throw new Error(`pattern '${source}': unknown resource type '${name}' (one of ${RESOURCE_TYPES.join(', ')})`);
75
+ type = known;
76
+ }
77
+ else {
78
+ if (glob !== undefined)
79
+ throw new Error(`pattern '${source}' has two URL globs; write one, or one pattern per glob`);
80
+ glob = t;
81
+ }
82
+ }
83
+ if (type === undefined && glob === undefined)
84
+ throw new Error('a pattern must not be empty');
85
+ return {
86
+ source,
87
+ ...(type !== undefined ? { type } : {}),
88
+ ...(glob !== undefined ? { glob: globToRegExp(glob) } : {}),
89
+ pathOnly: glob?.startsWith('/') === true,
90
+ };
91
+ }
92
+ function globToRegExp(glob) {
93
+ let re = '';
94
+ for (let i = 0; i < glob.length; i++) {
95
+ const c = glob[i];
96
+ if (c === '*') {
97
+ if (glob[i + 1] === '*') {
98
+ re += '.*';
99
+ i++;
100
+ }
101
+ else
102
+ re += '[^/]*';
103
+ }
104
+ else if (c === '?')
105
+ re += '[^/]';
106
+ else
107
+ re += c.replace(/[.+^${}()|[\]\\]/g, '\\$&');
108
+ }
109
+ return new RegExp(`^${re}$`);
110
+ }
111
+ export function matchPattern(p, url, type) {
112
+ if (p.type !== undefined && p.type !== (type ?? 'Other'))
113
+ return false;
114
+ if (!p.glob)
115
+ return true;
116
+ if (!p.pathOnly)
117
+ return p.glob.test(url);
118
+ try {
119
+ return p.glob.test(new URL(url).pathname);
120
+ }
121
+ catch {
122
+ return false;
123
+ }
124
+ }
125
+ /** Merge two `settle` objects: scalars from `over`, lists concatenated
126
+ * without duplicates. Keys absent from both stay absent. */
127
+ export function mergeSettle(under, over) {
128
+ if (!under)
129
+ return over;
130
+ if (!over)
131
+ return under;
132
+ const list = (a, b) => a || b ? [...new Set([...(a ?? []), ...(b ?? [])])] : undefined;
133
+ const ignore = list(under.network?.ignore, over.network?.ignore);
134
+ const require = list(under.network?.require, over.network?.require);
135
+ const timeout = over.timeout ?? under.timeout;
136
+ const ready = over.ready ?? under.ready;
137
+ return {
138
+ ...(timeout !== undefined ? { timeout } : {}),
139
+ ...(ready !== undefined ? { ready } : {}),
140
+ ...(ignore || require
141
+ ? { network: { ...(ignore ? { ignore } : {}), ...(require ? { require } : {}) } }
142
+ : {}),
143
+ };
144
+ }
145
+ /** The settle wait for one capture, from its layers, lowest first. Assumes
146
+ * each layer passed checkSettle. */
147
+ export function resolveSettle(...layers) {
148
+ let merged;
149
+ for (const l of layers)
150
+ merged = mergeSettle(merged, l);
151
+ const timeoutMs = merged?.timeout ?? DEFAULT_SETTLE_TIMEOUT_MS;
152
+ return {
153
+ timeoutMs,
154
+ horizonMs: Math.min(MAX_TIMER_HORIZON_MS, Math.floor((timeoutMs * 2) / 3)),
155
+ ready: merged?.ready === true,
156
+ ignore: [...DEFAULT_NETWORK_IGNORE, ...(merged?.network?.ignore ?? [])].map(parsePattern),
157
+ require: (merged?.network?.require ?? []).map(parsePattern),
158
+ };
159
+ }
160
+ /** waitForSelector and delay: call > story > project. */
161
+ export function layerScalars(project, params, call) {
162
+ const waitForSelector = call?.waitForSelector ?? params.waitForSelector ?? project?.waitForSelector;
163
+ const delay = call?.delay ?? params.delay ?? project?.delay;
164
+ return {
165
+ ...(waitForSelector !== undefined ? { waitForSelector } : {}),
166
+ ...(delay !== undefined ? { delay } : {}),
167
+ };
168
+ }
169
+ const isRecord = (v) => typeof v === 'object' && v !== null && !Array.isArray(v);
170
+ /** The flag a settle report raises. */
171
+ export const settleFlag = (s) => s.status === 'timeout' ? 'settle-timeout' : 'settle-fallback';
172
+ /** A settle cap: a whole number of ms, 1 to MAX_SETTLE_TIMEOUT_MS. The CLI
173
+ * and the MCP server check per-call values with this too. */
174
+ export function isSettleTimeout(v) {
175
+ return (Number.isInteger(v) && v >= 1 && v <= MAX_SETTLE_TIMEOUT_MS);
176
+ }
177
+ /**
178
+ * Validate a `settle` value where it is declared, so the error names the
179
+ * place to edit. `where` is e.g. `project config` or `story 'button--primary'`.
180
+ * Throws SettleConfigError; returns the value typed.
181
+ */
182
+ export function checkSettle(raw, where) {
183
+ if (raw === undefined)
184
+ return undefined;
185
+ const fail = (m) => new SettleConfigError(`${where}: ${m}`);
186
+ if (!isRecord(raw))
187
+ throw fail(`'settle' must be an object`);
188
+ for (const k of Object.keys(raw)) {
189
+ if (k !== 'timeout' && k !== 'ready' && k !== 'network')
190
+ throw fail(`unknown settle option '${k}' (expected timeout, ready, network)`);
191
+ }
192
+ if (raw['timeout'] !== undefined && !isSettleTimeout(raw['timeout']))
193
+ throw fail(`settle.timeout must be a whole number of ms from 1 to ${MAX_SETTLE_TIMEOUT_MS}, got ${JSON.stringify(raw['timeout'])}`);
194
+ if (raw['ready'] !== undefined && typeof raw['ready'] !== 'boolean')
195
+ throw fail(`settle.ready must be true or false, got ${JSON.stringify(raw['ready'])}`);
196
+ const net = raw['network'];
197
+ if (net !== undefined) {
198
+ if (!isRecord(net))
199
+ throw fail(`settle.network must be an object`);
200
+ for (const k of Object.keys(net)) {
201
+ if (k !== 'ignore' && k !== 'require')
202
+ throw fail(`unknown settle.network option '${k}' (expected ignore, require)`);
203
+ checkPatterns(net[k], `settle.network.${k}`, fail);
204
+ }
205
+ }
206
+ return raw;
207
+ }
208
+ /** Validate a pattern list; `fail` makes the error. */
209
+ export function checkPatterns(list, name, fail) {
210
+ if (!Array.isArray(list) || !list.every((p) => typeof p === 'string'))
211
+ throw fail(`${name} must be an array of URL patterns`);
212
+ for (const p of list) {
213
+ try {
214
+ parsePattern(p);
215
+ }
216
+ catch (e) {
217
+ throw fail(`${name}: ${e instanceof Error ? e.message : String(e)}`);
218
+ }
219
+ }
220
+ return list;
221
+ }
222
+ /** The project config's settle options: `settle`, `waitForSelector`, `delay`. */
223
+ export function checkProjectSettle(config) {
224
+ const where = 'project config';
225
+ checkSettle(config.settle, where);
226
+ if (config.waitForSelector !== undefined && typeof config.waitForSelector !== 'string')
227
+ throw new SettleConfigError(`${where}: waitForSelector must be a CSS selector`);
228
+ if (config.delay !== undefined &&
229
+ (typeof config.delay !== 'number' ||
230
+ !Number.isFinite(config.delay) ||
231
+ config.delay < 0))
232
+ throw new SettleConfigError(`${where}: delay must be a number of ms, 0 or more`);
233
+ }
234
+ //# sourceMappingURL=settle-options.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"settle-options.js","sourceRoot":"","sources":["../../src/capture/settle-options.ts"],"names":[],"mappings":"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"}
@@ -1,22 +1,4 @@
1
1
  import type { Page } from '../browser/page.js';
2
- /** Quiet period with no DOM mutations and no in-flight fetches before we
3
- * declare the page settled. */
4
- export declare const DOM_IDLE_MS = 150;
5
- /** Hard cap on waiting for quiescence. A story that never goes idle — a
6
- * spinner that survived the CSS freeze, a polling widget — still gets
7
- * captured rather than hanging the run. Kept tight: this cost is paid on
8
- * every such story, times the whole suite. */
9
- export declare const DOM_IDLE_TIMEOUT_MS = 500;
10
- /** Arm the quiescence observer. Call before triggering a story render. */
11
- export declare function armSettle(page: Page): Promise<void>;
12
- /**
13
- * Await quiescence, webfonts and image decode in a single round trip.
14
- *
15
- * Deliberately one `evaluate` rather than three. At ~1.2s per story and 8
16
- * parallel pages, CDP round-trips are a measurable share of wall clock; the
17
- * original tool cut per-story time meaningfully by collapsing these.
18
- */
19
- export declare function drain(page: Page): Promise<void>;
20
2
  /**
21
3
  * Zero out animations and transitions.
22
4
  *
@@ -2,106 +2,13 @@
2
2
  // (Platform.settle) has resolved. Everything here applies to any DOM, which is
3
3
  // why it lives in core rather than in an adapter.
4
4
  //
5
- // The ordering matters and is not arbitrary:
6
- // arm → platform render signal → DOM/network quiescence → fonts → images
7
- // Arming must happen before the render is triggered, or the observer misses
8
- // the mutations it is supposed to be watching.
9
- /** Quiet period with no DOM mutations and no in-flight fetches before we
10
- * declare the page settled. */
11
- export const DOM_IDLE_MS = 150;
12
- /** Hard cap on waiting for quiescence. A story that never goes idle — a
13
- * spinner that survived the CSS freeze, a polling widget — still gets
14
- * captured rather than hanging the run. Kept tight: this cost is paid on
15
- * every such story, times the whole suite. */
16
- export const DOM_IDLE_TIMEOUT_MS = 500;
17
- /**
18
- * Installed via evaluate before each story render. Resolves `window.__juxWait`
19
- * once the DOM has been mutation-free for `idleMs`, or `timeoutMs` elapses.
20
- *
21
- * Counts in-flight fetches too: a story that fires a request on mount would
22
- * otherwise report "idle" in the gap between mount and response, and we'd
23
- * screenshot the loading state — intermittently.
24
- */
25
- const SETTLE_SCRIPT = `
26
- var done = false;
27
- window.__juxWait = new Promise(function (resolve) {
28
- var pending = 0;
29
- var idleTimer = null;
30
-
31
- function finish() {
32
- if (done) return;
33
- done = true;
34
- observer.disconnect();
35
- clearTimeout(idleTimer);
36
- clearTimeout(capTimer);
37
- if (window.__juxFetchOrig) {
38
- window.fetch = window.__juxFetchOrig;
39
- delete window.__juxFetchOrig;
40
- }
41
- resolve();
42
- }
43
-
44
- var capTimer = setTimeout(finish, timeoutMs);
45
-
46
- function scheduleIdle() {
47
- clearTimeout(idleTimer);
48
- if (pending === 0) idleTimer = setTimeout(finish, idleMs);
49
- }
50
-
51
- if (!window.__juxFetchOrig) {
52
- window.__juxFetchOrig = window.fetch;
53
- window.fetch = function () {
54
- pending++;
55
- clearTimeout(idleTimer);
56
- return window.__juxFetchOrig.apply(this, arguments).finally(function () {
57
- pending--;
58
- scheduleIdle();
59
- });
60
- };
61
- }
62
-
63
- var observer = new MutationObserver(scheduleIdle);
64
- observer.observe(document.documentElement, {
65
- childList: true, subtree: true, attributes: true, characterData: true,
66
- });
67
- scheduleIdle();
68
- });
69
- `;
70
- /** Arm the quiescence observer. Call before triggering a story render. */
71
- export async function armSettle(page) {
72
- await page
73
- .evaluate(`(function (idleMs, timeoutMs) { ${SETTLE_SCRIPT} })(${DOM_IDLE_MS}, ${DOM_IDLE_TIMEOUT_MS})`)
74
- .catch(() => { });
75
- }
76
- /**
77
- * Await quiescence, webfonts and image decode in a single round trip.
78
- *
79
- * Deliberately one `evaluate` rather than three. At ~1.2s per story and 8
80
- * parallel pages, CDP round-trips are a measurable share of wall clock; the
81
- * original tool cut per-story time meaningfully by collapsing these.
82
- */
83
- export async function drain(page) {
84
- // The window cast inside is a cast, not a global Window augmentation: the
85
- // callback runs in the page, and declaring the hook globally would leak it
86
- // into core's public types. (Comments stay out here: TypeScript keeps them
87
- // in the callback's source, which is what the page evaluates.)
88
- await page
89
- .evaluate(async () => {
90
- const w = window;
91
- await (w.__juxWait ?? Promise.resolve());
92
- // Webfonts: a story captured mid-swap diffs against one captured after,
93
- // for every glyph on the page.
94
- await document.fonts?.ready?.catch?.(() => { });
95
- // Decode, not just load — an undecoded image can still paint late.
96
- await Promise.all(Array.from(document.images).map((img) => img.complete
97
- ? Promise.resolve()
98
- : new Promise((r) => {
99
- img.addEventListener('load', () => r(), { once: true });
100
- img.addEventListener('error', () => r(), { once: true });
101
- })));
102
- })
103
- .catch(() => { });
104
- }
5
+ // The order, in captureStory:
6
+ // freeze animations → platform render signal → waitForSelector →
7
+ // event-driven idle wait (idle.ts) → delay → paint barrier → recheck
8
+ //
9
+ // The idle wait replaced a 150 ms DOM-quiet window plus a `drain` that awaited
10
+ // fonts and every incomplete image with no cap; see idle.ts for what was
11
+ // measured and why.
105
12
  /**
106
13
  * Zero out animations and transitions.
107
14
  *
@@ -1 +1 @@
1
- {"version":3,"file":"settle.js","sourceRoot":"","sources":["../../src/capture/settle.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"settle.js","sourceRoot":"","sources":["../../src/capture/settle.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,+EAA+E;AAC/E,kDAAkD;AAClD,EAAE;AACF,8BAA8B;AAC9B,mEAAmE;AACnE,uEAAuE;AACvE,EAAE;AACF,+EAA+E;AAC/E,yEAAyE;AACzE,oBAAoB;AAIpB;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG;;;;;;;;;CASpC,CAAC;AAEF;yEACyE;AACzE,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,IAAU,EACV,QAAgB,EAChB,SAAiB;IAEjB,2EAA2E;IAC3E,8EAA8E;IAC9E,MAAM,IAAI,CAAC,eAAe,CAAC,QAAQ,EAAE,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;AAC/E,CAAC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,IAAU,EAAE,SAAS,GAAG,IAAI;IAC7D,MAAM,IAAI;SACP,QAAQ,CACP,CAAC,GAAG,EAAE,EAAE,CACN,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;QAC5B,MAAM,IAAI,GAAG,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC;QAC7B,MAAM,KAAK,GAAG,UAAU,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;QACpC,qBAAqB,CAAC,GAAG,EAAE,CACzB,qBAAqB,CAAC,GAAG,EAAE;YACzB,MAAM,MAAM,GAAG,GAAG,EAAE;gBAClB,YAAY,CAAC,KAAK,CAAC,CAAC;gBACpB,IAAI,EAAE,CAAC;YACT,CAAC,CAAC;YACF,IAAI,OAAO,mBAAmB,KAAK,UAAU,EAAE,CAAC;gBAC9C,mBAAmB,CAAC,MAAM,EAAE,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,CAAC;YAChD,CAAC;iBAAM,CAAC;gBACN,MAAM,EAAE,CAAC;YACX,CAAC;QACH,CAAC,CAAC,CACH,CAAC;IACJ,CAAC,CAAC,EACJ,SAAS,CACV;SACA,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;AACrB,CAAC"}
@@ -1,5 +1,6 @@
1
1
  import type { Browser } from '../browser/browser.js';
2
2
  import type { CaptureEnv, Snapshot, StoryParams, Viewport } from '../types.js';
3
+ import type { SettleLayer } from './settle-options.js';
3
4
  export interface SnapOptions {
4
5
  url: string;
5
6
  viewport?: Viewport;
@@ -17,6 +18,8 @@ export interface SnapPagesOptions {
17
18
  urls: string[];
18
19
  viewport?: Viewport;
19
20
  params?: StoryParams;
21
+ /** The project config's settle options, below `params`. */
22
+ projectSettle?: SettleLayer;
20
23
  /** Acquired by the caller (acquireBrowser); not closed here. */
21
24
  browser: Browser;
22
25
  }
@@ -34,7 +34,13 @@ export async function snapPages(opts) {
34
34
  try {
35
35
  // A url platform enumerates exactly the one entry it was given.
36
36
  const [story] = await platform.enumerate({ projectRoot: process.cwd() });
37
- const snapshot = await captureStory(page, story, { platform, baseUrl: url }, viewport);
37
+ const snapshot = await captureStory(page, story, {
38
+ platform,
39
+ baseUrl: url,
40
+ ...(opts.projectSettle
41
+ ? { settleLayers: { project: opts.projectSettle } }
42
+ : {}),
43
+ }, viewport);
38
44
  if (!snapshot)
39
45
  throw new Error('page declared skip');
40
46
  pages.push({ url, snapshot, status });
@@ -1 +1 @@
1
- {"version":3,"file":"snap.js","sourceRoot":"","sources":["../../src/capture/snap.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"snap.js","sourceRoot":"","sources":["../../src/capture/snap.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,44 @@
1
+ import type { StoryManifest, StoryParams, VariantSpec } from '../types.js';
2
+ /** Underscores are excluded so the `__` key separator can't occur in a name. */
3
+ export declare const VARIANT_NAME: RegExp;
4
+ export declare const VARIANT_SEPARATOR = "__";
5
+ export { VariantConfigError } from './config-errors.js';
6
+ export declare const captureKey: (baseStoryId: string, variant: string) => string;
7
+ /** The id every platform call receives: the platform's own, never the key. */
8
+ export declare const platformStoryId: (story: {
9
+ storyId: string;
10
+ baseStoryId?: string;
11
+ }) => string;
12
+ /**
13
+ * Expand each story into its default capture plus one entry per variant,
14
+ * sorted by capture key in code-unit order.
15
+ *
16
+ * Throws VariantConfigError on a bad name, a malformed spec, an unknown or
17
+ * cyclic `extends`, or two captures that would share a key.
18
+ */
19
+ export declare function expandVariants(stories: readonly StoryManifest[], projectVariants?: Record<string, VariantSpec | false>): StoryManifest[];
20
+ /**
21
+ * Merge live params (read from the rendered preview) for one capture.
22
+ *
23
+ * Live params override the manifest — the preview is the story's own
24
+ * declaration, the index a lossy projection of it — but they merge UNDER a
25
+ * variant's layer: a story's own live `viewport` must not undo the mobile
26
+ * variant's. `variants` and `colorScheme` in live params are declarations,
27
+ * not settings for this capture, so they are dropped.
28
+ */
29
+ export declare function mergeLiveParams(story: Pick<StoryManifest, 'params' | 'variantParams'>, live: StoryParams | undefined): StoryParams;
30
+ /**
31
+ * Pick items by story id: a base id selects the story and every variant of
32
+ * it, a capture key selects exactly that capture. Ids that match nothing are
33
+ * returned so the caller can report them.
34
+ */
35
+ export declare function selectByStoryId<T extends {
36
+ storyId: string;
37
+ baseStoryId?: string;
38
+ }>(items: readonly T[], ids: readonly string[]): {
39
+ selected: T[];
40
+ unknown: string[];
41
+ };
42
+ /** Structural equality for JSON-shaped values: key order doesn't matter,
43
+ * array order does. */
44
+ export declare function deepEqual(a: unknown, b: unknown): boolean;