@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.
- package/dist/agent/handoff.d.ts +19 -1
- package/dist/agent/handoff.js +54 -2
- package/dist/agent/handoff.js.map +1 -1
- package/dist/analysis/affected.d.ts +17 -0
- package/dist/analysis/affected.js +34 -0
- package/dist/analysis/affected.js.map +1 -1
- package/dist/browser/flags.d.ts +14 -0
- package/dist/browser/flags.js +15 -0
- package/dist/browser/flags.js.map +1 -1
- package/dist/browser/page.d.ts +19 -1
- package/dist/browser/page.js +16 -3
- package/dist/browser/page.js.map +1 -1
- package/dist/capture/capture.d.ts +10 -1
- package/dist/capture/capture.js +121 -16
- 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/discover.d.ts +28 -0
- package/dist/capture/discover.js +109 -0
- package/dist/capture/discover.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 +8 -0
- package/dist/capture/pool.js +2 -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 +44 -0
- package/dist/capture/variants.js +204 -0
- package/dist/capture/variants.js.map +1 -0
- package/dist/diff/structural/deltas.d.ts +2 -3
- package/dist/diff/structural/deltas.js +3 -3
- package/dist/diff/structural/deltas.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 +6 -3
- package/dist/index.js +9 -2
- package/dist/index.js.map +1 -1
- package/dist/platform.d.ts +20 -0
- package/dist/platforms/urls.js +5 -1
- package/dist/platforms/urls.js.map +1 -1
- package/dist/report/comment.js +19 -8
- package/dist/report/comment.js.map +1 -1
- package/dist/report/format.d.ts +17 -0
- package/dist/report/format.js +16 -0
- package/dist/report/format.js.map +1 -1
- package/dist/report/html.js +40 -6
- package/dist/report/html.js.map +1 -1
- package/dist/run/run.d.ts +6 -0
- package/dist/run/run.js +32 -0
- package/dist/run/run.js.map +1 -1
- package/dist/types.d.ts +107 -2
- 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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"}
|
|
@@ -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;
|