@juxtapose/core 0.9.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.
- package/dist/agent/handoff.d.ts +3 -1
- package/dist/agent/handoff.js +17 -0
- package/dist/agent/handoff.js.map +1 -1
- package/dist/browser/page.d.ts +9 -1
- package/dist/browser/page.js +7 -0
- package/dist/browser/page.js.map +1 -1
- package/dist/capture/capture.d.ts +7 -0
- package/dist/capture/capture.js +40 -8
- package/dist/capture/capture.js.map +1 -1
- package/dist/capture/config-errors.d.ts +10 -0
- package/dist/capture/config-errors.js +20 -0
- package/dist/capture/config-errors.js.map +1 -0
- package/dist/capture/idle.d.ts +88 -0
- package/dist/capture/idle.js +545 -0
- package/dist/capture/idle.js.map +1 -0
- package/dist/capture/pool.d.ts +6 -0
- package/dist/capture/pool.js +1 -0
- package/dist/capture/pool.js.map +1 -1
- package/dist/capture/settle-options.d.ts +78 -0
- package/dist/capture/settle-options.js +234 -0
- package/dist/capture/settle-options.js.map +1 -0
- package/dist/capture/settle.d.ts +0 -18
- package/dist/capture/settle.js +7 -100
- package/dist/capture/settle.js.map +1 -1
- package/dist/capture/snap.d.ts +3 -0
- package/dist/capture/snap.js +7 -1
- package/dist/capture/snap.js.map +1 -1
- package/dist/capture/variants.d.ts +1 -4
- package/dist/capture/variants.js +22 -10
- package/dist/capture/variants.js.map +1 -1
- package/dist/diff/structural.d.ts +4 -1
- package/dist/diff/structural.js +3 -0
- package/dist/diff/structural.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/run/run.d.ts +4 -0
- package/dist/run/run.js +17 -0
- package/dist/run/run.js.map +1 -1
- package/dist/types.d.ts +60 -1
- 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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"}
|
package/dist/capture/settle.d.ts
CHANGED
|
@@ -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
|
*
|
package/dist/capture/settle.js
CHANGED
|
@@ -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
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
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":"AAAA,2EAA2E;AAC3E,+EAA+E;AAC/E,kDAAkD;AAClD,EAAE;AACF,
|
|
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"}
|
package/dist/capture/snap.d.ts
CHANGED
|
@@ -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
|
}
|
package/dist/capture/snap.js
CHANGED
|
@@ -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, {
|
|
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 });
|
package/dist/capture/snap.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"snap.js","sourceRoot":"","sources":["../../src/capture/snap.ts"],"names":[],"mappings":"AAAA,qEAAqE;AACrE,EAAE;AACF,2EAA2E;AAC3E,yEAAyE;AACzE,6EAA6E;AAC7E,0DAA0D;AAK1D,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"snap.js","sourceRoot":"","sources":["../../src/capture/snap.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,10 +2,7 @@ import type { StoryManifest, StoryParams, VariantSpec } from '../types.js';
|
|
|
2
2
|
/** Underscores are excluded so the `__` key separator can't occur in a name. */
|
|
3
3
|
export declare const VARIANT_NAME: RegExp;
|
|
4
4
|
export declare const VARIANT_SEPARATOR = "__";
|
|
5
|
-
|
|
6
|
-
export declare class VariantConfigError extends Error {
|
|
7
|
-
constructor(message: string);
|
|
8
|
-
}
|
|
5
|
+
export { VariantConfigError } from './config-errors.js';
|
|
9
6
|
export declare const captureKey: (baseStoryId: string, variant: string) => string;
|
|
10
7
|
/** The id every platform call receives: the platform's own, never the key. */
|
|
11
8
|
export declare const platformStoryId: (story: {
|
package/dist/capture/variants.js
CHANGED
|
@@ -7,16 +7,12 @@
|
|
|
7
7
|
// url entry, or a preview extract), so a platform can change how it discovers
|
|
8
8
|
// them without touching this.
|
|
9
9
|
import { compareCodeUnits } from '../diff/structural/deltas.js';
|
|
10
|
+
import { VariantConfigError } from './config-errors.js';
|
|
11
|
+
import { checkSettle, mergeSettle } from './settle-options.js';
|
|
10
12
|
/** Underscores are excluded so the `__` key separator can't occur in a name. */
|
|
11
13
|
export const VARIANT_NAME = /^[a-z0-9][a-z0-9-]{0,31}$/;
|
|
12
14
|
export const VARIANT_SEPARATOR = '__';
|
|
13
|
-
|
|
14
|
-
export class VariantConfigError extends Error {
|
|
15
|
-
constructor(message) {
|
|
16
|
-
super(`jux: ${message}`);
|
|
17
|
-
this.name = 'VariantConfigError';
|
|
18
|
-
}
|
|
19
|
-
}
|
|
15
|
+
export { VariantConfigError } from './config-errors.js';
|
|
20
16
|
export const captureKey = (baseStoryId, variant) => `${baseStoryId}${VARIANT_SEPARATOR}${variant}`;
|
|
21
17
|
/** The id every platform call receives: the platform's own, never the key. */
|
|
22
18
|
export const platformStoryId = (story) => story.baseStoryId ?? story.storyId;
|
|
@@ -35,6 +31,7 @@ export function expandVariants(stories, projectVariants) {
|
|
|
35
31
|
// `variants` is a declaration, not a capture setting, and `colorScheme`
|
|
36
32
|
// is variant-only: the default capture is always light.
|
|
37
33
|
const { variants: ownRaw, colorScheme: _ignored, ...baseParams } = story.params;
|
|
34
|
+
checkSettle(baseParams.settle, `story '${story.storyId}'`);
|
|
38
35
|
out.push({ ...story, params: baseParams });
|
|
39
36
|
// A skipped story skips every variant: there is nothing to vary.
|
|
40
37
|
if (baseParams.skip === true)
|
|
@@ -61,7 +58,7 @@ export function expandVariants(stories, projectVariants) {
|
|
|
61
58
|
variant: name,
|
|
62
59
|
variantParams: layer,
|
|
63
60
|
// Base params, then the extends chain, then the variant itself.
|
|
64
|
-
params: { ...baseParams, ...layer },
|
|
61
|
+
params: withSettle({ ...baseParams, ...layer }, baseParams.settle, layer.settle),
|
|
65
62
|
});
|
|
66
63
|
}
|
|
67
64
|
}
|
|
@@ -102,6 +99,7 @@ function checkDeclared(raw, where) {
|
|
|
102
99
|
if (scheme !== undefined && scheme !== 'light' && scheme !== 'dark') {
|
|
103
100
|
throw new VariantConfigError(`${where}: variant '${name}' colorScheme must be 'light' or 'dark'`);
|
|
104
101
|
}
|
|
102
|
+
checkSettle(spec['settle'], `${where}: variant '${name}'`);
|
|
105
103
|
const ext = spec['extends'];
|
|
106
104
|
if (ext !== undefined && typeof ext !== 'string') {
|
|
107
105
|
throw new VariantConfigError(`${where}: variant '${name}' extends must be a variant name`);
|
|
@@ -129,11 +127,22 @@ function resolveChain(storyId, name, merged) {
|
|
|
129
127
|
cur = spec.extends;
|
|
130
128
|
}
|
|
131
129
|
const layer = {};
|
|
132
|
-
for (const spec of chain)
|
|
130
|
+
for (const spec of chain) {
|
|
131
|
+
const settle = mergeSettle(layer.settle, spec.settle);
|
|
133
132
|
Object.assign(layer, spec);
|
|
133
|
+
if (settle)
|
|
134
|
+
layer.settle = settle;
|
|
135
|
+
}
|
|
134
136
|
delete layer.extends;
|
|
135
137
|
return layer;
|
|
136
138
|
}
|
|
139
|
+
/** `params` with `settle` merged field by field rather than replaced, so a
|
|
140
|
+
* variant's `settle.timeout` keeps the story's `settle.network`. Untouched
|
|
141
|
+
* (no `settle` key) when neither side sets one. */
|
|
142
|
+
function withSettle(params, under, over) {
|
|
143
|
+
const settle = mergeSettle(under, over);
|
|
144
|
+
return settle ? { ...params, settle } : params;
|
|
145
|
+
}
|
|
137
146
|
/**
|
|
138
147
|
* Merge live params (read from the rendered preview) for one capture.
|
|
139
148
|
*
|
|
@@ -149,7 +158,10 @@ export function mergeLiveParams(story, live) {
|
|
|
149
158
|
return params;
|
|
150
159
|
const { variants: _v, colorScheme: _c, ...settings } = live;
|
|
151
160
|
Object.assign(params, settings, story.variantParams ?? {});
|
|
152
|
-
|
|
161
|
+
const variantSettle = story.variantParams?.settle;
|
|
162
|
+
return variantSettle
|
|
163
|
+
? withSettle(params, settings.settle ?? story.params.settle, variantSettle)
|
|
164
|
+
: params;
|
|
153
165
|
}
|
|
154
166
|
/**
|
|
155
167
|
* Pick items by story id: a base id selects the story and every variant of
|