@juxtapose/core 0.9.0 → 0.11.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 +20 -4
- package/dist/browser/page.js +15 -6
- package/dist/browser/page.js.map +1 -1
- package/dist/capture/capture.d.ts +7 -0
- package/dist/capture/capture.js +60 -9
- 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 +558 -0
- package/dist/capture/idle.js.map +1 -0
- package/dist/capture/network-policy.d.ts +21 -0
- package/dist/capture/network-policy.js +114 -0
- package/dist/capture/network-policy.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 +80 -0
- package/dist/capture/settle-options.js +236 -0
- package/dist/capture/settle-options.js.map +1 -0
- package/dist/capture/settle.d.ts +60 -18
- package/dist/capture/settle.js +157 -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 +25 -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 +3 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/run/run.d.ts +4 -0
- package/dist/run/run.js +20 -1
- package/dist/run/run.js.map +1 -1
- package/dist/types.d.ts +77 -1
- package/package.json +1 -1
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
// `network.external: 'block'` (issue #147): requests to origins other than
|
|
2
|
+
// the page's own fail at once, through CDP Fetch, instead of fetching bytes
|
|
3
|
+
// that change between runs (a random photo, a CDN) or holding the page to the
|
|
4
|
+
// settle cap (a broken link that answers after 20 s, issue #125).
|
|
5
|
+
//
|
|
6
|
+
// Blocked requests fail with BlockedByClient, which arrives as
|
|
7
|
+
// Network.loadingFailed: the settle tracker drops them like any failed
|
|
8
|
+
// request, an <img> becomes `complete`, and nothing is pending or flagged.
|
|
9
|
+
//
|
|
10
|
+
// Limits: WebSockets are not intercepted, and neither is anything running on
|
|
11
|
+
// a session this page does not hold (an out-of-process iframe; measured:
|
|
12
|
+
// headless-shell ran a cross-site iframe in-process, and dedicated-worker
|
|
13
|
+
// fetches were blocked, but a real Chrome with site isolation may differ).
|
|
14
|
+
// data:, blob:, about: and file: URLs have no remote origin and always pass; so does the top-level
|
|
15
|
+
// document, so a story URL that redirects elsewhere still loads.
|
|
16
|
+
import { SettleConfigError } from './config-errors.js';
|
|
17
|
+
/** Validate a `network` value where it is declared; `where` names the place to edit. */
|
|
18
|
+
export function checkNetwork(raw, where) {
|
|
19
|
+
if (raw === undefined)
|
|
20
|
+
return undefined;
|
|
21
|
+
const fail = (m) => new SettleConfigError(`${where}: ${m}`);
|
|
22
|
+
if (typeof raw !== 'object' || raw === null || Array.isArray(raw))
|
|
23
|
+
throw fail(`'network' must be an object`);
|
|
24
|
+
for (const k of Object.keys(raw))
|
|
25
|
+
if (k !== 'external')
|
|
26
|
+
throw fail(`unknown network option '${k}' (expected external)`);
|
|
27
|
+
const ext = raw.external;
|
|
28
|
+
if (ext !== undefined && ext !== 'allow' && ext !== 'block')
|
|
29
|
+
throw fail(`network.external must be 'allow' or 'block', got ${JSON.stringify(ext)}`);
|
|
30
|
+
return ext === undefined ? {} : { external: ext };
|
|
31
|
+
}
|
|
32
|
+
/** call > story > project, default 'allow'. */
|
|
33
|
+
export function resolveExternal(project, story, call) {
|
|
34
|
+
return (call?.network?.external ??
|
|
35
|
+
story?.network?.external ??
|
|
36
|
+
project?.network?.external ??
|
|
37
|
+
'allow');
|
|
38
|
+
}
|
|
39
|
+
/** Whether `url` is a remote request to a different origin than `origin`. */
|
|
40
|
+
export function isExternal(url, origin) {
|
|
41
|
+
let u;
|
|
42
|
+
try {
|
|
43
|
+
u = new URL(url);
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
if (u.protocol !== 'http:' && u.protocol !== 'https:')
|
|
49
|
+
return false;
|
|
50
|
+
return u.origin !== origin;
|
|
51
|
+
}
|
|
52
|
+
const applied = new WeakMap();
|
|
53
|
+
/**
|
|
54
|
+
* Make the page's request policy match `mode` for the origin of `baseUrl`.
|
|
55
|
+
* Called before every navigation; a page keeps its policy until the next call,
|
|
56
|
+
* so a pooled page never carries one capture's block into the next.
|
|
57
|
+
*/
|
|
58
|
+
export async function applyNetworkPolicy(page, mode, baseUrl) {
|
|
59
|
+
const current = applied.get(page);
|
|
60
|
+
let origin;
|
|
61
|
+
try {
|
|
62
|
+
origin = new URL(baseUrl).origin;
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
origin = 'null';
|
|
66
|
+
}
|
|
67
|
+
if (mode === 'allow') {
|
|
68
|
+
if (!current)
|
|
69
|
+
return;
|
|
70
|
+
applied.delete(page);
|
|
71
|
+
current.off();
|
|
72
|
+
await page.send('Fetch.disable').catch(() => { });
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
if (current) {
|
|
76
|
+
// In place, never off() then on(): a request paused in between would
|
|
77
|
+
// never be answered and would hang the page.
|
|
78
|
+
current.origin = origin;
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
if (!page.on)
|
|
82
|
+
return;
|
|
83
|
+
const tree = await page.send('Page.getFrameTree').catch(() => undefined);
|
|
84
|
+
const mainFrameId = tree?.frameTree?.frame?.id;
|
|
85
|
+
const state = { origin, off: () => { } };
|
|
86
|
+
state.off = page.on('Fetch.requestPaused', (e) => {
|
|
87
|
+
const top = e.resourceType === 'Document' && e.frameId === mainFrameId;
|
|
88
|
+
// The page's own origin is the committed main document's: a top-level
|
|
89
|
+
// redirect (127.0.0.1 -> localhost) moves it, and the new document's own
|
|
90
|
+
// CSS, scripts and images must not count as external.
|
|
91
|
+
if (top)
|
|
92
|
+
state.origin = originOf(e.request.url) ?? state.origin;
|
|
93
|
+
const block = !top && isExternal(e.request.url, state.origin);
|
|
94
|
+
const done = block
|
|
95
|
+
? page.send('Fetch.failRequest', {
|
|
96
|
+
requestId: e.requestId,
|
|
97
|
+
errorReason: 'BlockedByClient',
|
|
98
|
+
})
|
|
99
|
+
: page.send('Fetch.continueRequest', { requestId: e.requestId });
|
|
100
|
+
done.catch(() => { });
|
|
101
|
+
});
|
|
102
|
+
applied.set(page, state);
|
|
103
|
+
await page.send('Fetch.enable', { patterns: [{ urlPattern: '*' }] });
|
|
104
|
+
}
|
|
105
|
+
function originOf(url) {
|
|
106
|
+
try {
|
|
107
|
+
const u = new URL(url);
|
|
108
|
+
return u.protocol === 'http:' || u.protocol === 'https:' ? u.origin : undefined;
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
return undefined;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=network-policy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"network-policy.js","sourceRoot":"","sources":["../../src/capture/network-policy.ts"],"names":[],"mappings":"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"}
|
package/dist/capture/pool.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type Browser } from '../browser/browser.js';
|
|
2
2
|
import type { Platform } from '../platform.js';
|
|
3
3
|
import type { Snapshot, StoryManifest, Viewport } from '../types.js';
|
|
4
|
+
import type { SettleLayer } from './settle-options.js';
|
|
4
5
|
/**
|
|
5
6
|
* Default parallelism.
|
|
6
7
|
*
|
|
@@ -25,6 +26,11 @@ export interface PoolOptions {
|
|
|
25
26
|
warmSwitch?: boolean;
|
|
26
27
|
/** Non-fatal notices from capture, e.g. an ignored live variant override. */
|
|
27
28
|
onWarning?: (message: string) => void;
|
|
29
|
+
/** Settle options around each story's own; see CaptureDeps. */
|
|
30
|
+
settleLayers?: {
|
|
31
|
+
project?: SettleLayer;
|
|
32
|
+
call?: SettleLayer;
|
|
33
|
+
};
|
|
28
34
|
}
|
|
29
35
|
export interface CaptureOutcome {
|
|
30
36
|
story: StoryManifest;
|
package/dist/capture/pool.js
CHANGED
|
@@ -63,6 +63,7 @@ export async function* capturePool(stories, opts) {
|
|
|
63
63
|
baseUrl: opts.baseUrl,
|
|
64
64
|
warmSwitch: opts.warmSwitch,
|
|
65
65
|
...(opts.onWarning ? { onWarning: opts.onWarning } : {}),
|
|
66
|
+
...(opts.settleLayers ? { settleLayers: opts.settleLayers } : {}),
|
|
66
67
|
};
|
|
67
68
|
try {
|
|
68
69
|
for (;;) {
|
package/dist/capture/pool.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pool.js","sourceRoot":"","sources":["../../src/capture/pool.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,6EAA6E;AAC7E,6CAA6C;AAE7C,OAAO,EACL,gBAAgB,GAGjB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAAa,MAAM,oBAAoB,CAAC;AAGjE,OAAO,EAAE,YAAY,EAAoB,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"pool.js","sourceRoot":"","sources":["../../src/capture/pool.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,6EAA6E;AAC7E,6CAA6C;AAE7C,OAAO,EACL,gBAAgB,GAGjB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAAa,MAAM,oBAAoB,CAAC;AAGjE,OAAO,EAAE,YAAY,EAAoB,MAAM,cAAc,CAAC;AAG9D;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,CAAC;AA6BrC;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,SAAS,CAAC,CAAC,WAAW,CAChC,OAAwB,EACxB,IAAiB;IAEjB,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;IAC5D,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO;IAE/B,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,WAAW,IAAI,mBAAmB,CAAC,CAAC;IACzE,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAExD,2EAA2E;IAC3E,4EAA4E;IAC5E,qEAAqE;IACrE,MAAM,IAAI,GAAG,WAAW,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;IAEtD,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,MAAM,MAAM,GAAqB,EAAE,CAAC;IACpC,IAAI,MAAM,GAAwB,IAAI,CAAC;IAEvC,MAAM,IAAI,GAAG,CAAC,GAAmB,EAAE,EAAE;QACnC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACjB,MAAM,EAAE,EAAE,CAAC;QACX,MAAM,GAAG,IAAI,CAAC;IAChB,CAAC,CAAC;IAEF,4EAA4E;IAC5E,uEAAuE;IACvE,IAAI,IAAI,GAA4B,IAAI,CAAC;IAEzC,MAAM,IAAI,GAAG,KAAK,IAAsD,EAAE;QACxE,6EAA6E;QAC7E,oEAAoE;QACpE,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;QAC3E,IAAI,CAAC;YACH,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC;QACpD,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;YACtC,MAAM,GAAG,CAAC;QACZ,CAAC;IACH,CAAC,CAAC;IAEF,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE,EAAE,KAAK,IAAI,EAAE;QAC7D,IAAI,OAAO,GAAmD,MAAM,IAAI,EAAE,CAAC;QAC3E,MAAM,IAAI,GAAgB;YACxB,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,UAAU,EAAE,IAAI,CAAC,UAAU;YAC3B,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACxD,GAAG,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,IAAI,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAClE,CAAC;QAEF,IAAI,CAAC;YACH,SAAS,CAAC;gBACR,IAAI,IAAI,CAAC,MAAM,EAAE,OAAO;oBAAE,MAAM;gBAChC,MAAM,CAAC,GAAG,MAAM,EAAE,CAAC;gBACnB,IAAI,CAAC,IAAI,IAAI,CAAC,MAAM;oBAAE,MAAM;gBAC5B,MAAM,KAAK,GAAG,IAAI,CAAC,CAAC,CAAE,CAAC,CAAC,iCAAiC;gBAEzD,IAAI,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;oBACrB,IAAI,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,IAAI,IAAI,gBAAgB,EAAE,EAAE,CAAC,CAAC;oBACvD,IAAI,CAAC,UAAU,EAAE,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;oBACtD,SAAS;gBACX,CAAC;gBAED,IAAI,CAAC;oBACH,MAAM,QAAQ,GAAG,MAAM,YAAY,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;oBAC9E,mEAAmE;oBACnE,2DAA2D;oBAC3D,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;gBAClE,CAAC;gBAAC,OAAO,GAAG,EAAE,CAAC;oBACb,qEAAqE;oBACrE,4CAA4C;oBAC5C,IAAI,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC;oBAC5E,oEAAoE;oBACpE,gEAAgE;oBAChE,iEAAiE;oBACjE,kEAAkE;oBAClE,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC;wBAChC,IAAI,KAAK,IAAI,gBAAgB,CAAC,GAAG,CAAC,CAAC;oBACrC,CAAC;yBAAM,IACL,GAAG,YAAY,gBAAgB;wBAC/B,GAAG,YAAY,iBAAiB,EAChC,CAAC;wBACD,MAAM,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;wBAC9C,OAAO,GAAG,MAAM,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,CAAU,EAAE,EAAE;4BAC1C,IAAI,KAAK,IAAI,gBAAgB,CAAC,CAAC,CAAC,CAAC;4BACjC,OAAO,IAAI,CAAC;wBACd,CAAC,CAAC,CAAC;oBACL,CAAC;gBACH,CAAC;gBAED,IAAI,CAAC,UAAU,EAAE,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;YACxD,CAAC;QACH,CAAC;gBAAS,CAAC;YACT,MAAM,OAAO,EAAE,OAAO,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QACjD,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IACjC,IAAI,QAAQ,GAAG,KAAK,CAAC;IACrB,KAAK,GAAG,CAAC,IAAI,CACX,GAAG,EAAE;QACH,QAAQ,GAAG,IAAI,CAAC;QAChB,MAAM,EAAE,EAAE,CAAC;QACX,MAAM,GAAG,IAAI,CAAC;IAChB,CAAC,EACD,GAAG,EAAE;QACH,QAAQ,GAAG,IAAI,CAAC;QAChB,MAAM,EAAE,EAAE,CAAC;QACX,MAAM,GAAG,IAAI,CAAC;IAChB,CAAC,CACF,CAAC;IAEF,OAAO,CAAC,QAAQ,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACtC,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACxB,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;gBAClC,MAAM,GAAG,OAAO,CAAC;YACnB,CAAC,CAAC,CAAC;YACH,SAAS;QACX,CAAC;QACD,MAAM,MAAM,CAAC,KAAK,EAAG,CAAC;IACxB,CAAC;IAED,MAAM,GAAG,CAAC;AACZ,CAAC;AAED,SAAS,OAAO,CAAI,KAAU;IAC5B,MAAM,GAAG,GAAG,KAAK,CAAC,KAAK,EAAE,CAAC;IAC1B,KAAK,IAAI,CAAC,GAAG,GAAG,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QACxC,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QAC9C,kDAAkD;QAClD,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAE,EAAE,GAAG,CAAC,CAAC,CAAE,CAAC,CAAC;IACxC,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import type { CaptureFlag, NetworkParams, 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
|
+
network?: NetworkParams;
|
|
43
|
+
waitForSelector?: string;
|
|
44
|
+
delay?: number;
|
|
45
|
+
}
|
|
46
|
+
export interface ResolvedSettle {
|
|
47
|
+
timeoutMs: number;
|
|
48
|
+
horizonMs: number;
|
|
49
|
+
ready: boolean;
|
|
50
|
+
ignore: UrlPattern[];
|
|
51
|
+
require: UrlPattern[];
|
|
52
|
+
}
|
|
53
|
+
/** Merge two `settle` objects: scalars from `over`, lists concatenated
|
|
54
|
+
* without duplicates. Keys absent from both stay absent. */
|
|
55
|
+
export declare function mergeSettle(under: SettleParams | undefined, over: SettleParams | undefined): SettleParams | undefined;
|
|
56
|
+
/** The settle wait for one capture, from its layers, lowest first. Assumes
|
|
57
|
+
* each layer passed checkSettle. */
|
|
58
|
+
export declare function resolveSettle(...layers: Array<SettleParams | undefined>): ResolvedSettle;
|
|
59
|
+
/** waitForSelector and delay: call > story > project. */
|
|
60
|
+
export declare function layerScalars(project: SettleLayer | undefined, params: StoryParams, call: SettleLayer | undefined): Pick<StoryParams, 'waitForSelector' | 'delay'>;
|
|
61
|
+
/** The flag a settle report raises. */
|
|
62
|
+
export declare const settleFlag: (s: Pick<SettleReport, "status">) => CaptureFlag;
|
|
63
|
+
/** A settle cap: a whole number of ms, 1 to MAX_SETTLE_TIMEOUT_MS. The CLI
|
|
64
|
+
* and the MCP server check per-call values with this too. */
|
|
65
|
+
export declare function isSettleTimeout(v: unknown): v is number;
|
|
66
|
+
/**
|
|
67
|
+
* Validate a `settle` value where it is declared, so the error names the
|
|
68
|
+
* place to edit. `where` is e.g. `project config` or `story 'button--primary'`.
|
|
69
|
+
* Throws SettleConfigError; returns the value typed.
|
|
70
|
+
*/
|
|
71
|
+
export declare function checkSettle(raw: unknown, where: string): SettleParams | undefined;
|
|
72
|
+
/** Validate a pattern list; `fail` makes the error. */
|
|
73
|
+
export declare function checkPatterns(list: unknown, name: string, fail: (m: string) => Error): string[];
|
|
74
|
+
/** The project config's settle options: `settle`, `waitForSelector`, `delay`. */
|
|
75
|
+
export declare function checkProjectSettle(config: {
|
|
76
|
+
settle?: unknown;
|
|
77
|
+
network?: unknown;
|
|
78
|
+
waitForSelector?: unknown;
|
|
79
|
+
delay?: unknown;
|
|
80
|
+
}): void;
|
|
@@ -0,0 +1,236 @@
|
|
|
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
|
+
import { checkNetwork } from './network-policy.js';
|
|
13
|
+
export { SettleConfigError };
|
|
14
|
+
/** Default cap on the settle wait. Measured on the examples (29 stories, 5
|
|
15
|
+
* runs each): the event-driven wait settles in 34-39 ms p50, 43 ms max, so
|
|
16
|
+
* the cap only matters for a page that really has outstanding work; 1500 ms
|
|
17
|
+
* lets a 1000 ms timer finish with room to paint. */
|
|
18
|
+
export const DEFAULT_SETTLE_TIMEOUT_MS = 1500;
|
|
19
|
+
/** A cap above this is a typo, not a slow page: it would be paid per story. */
|
|
20
|
+
export const MAX_SETTLE_TIMEOUT_MS = 60_000;
|
|
21
|
+
/** Timers longer than this are never waited for, at any cap. The spike
|
|
22
|
+
* measured horizon 1000 / cap 1500; the horizon must stay below the cap or a
|
|
23
|
+
* long timer is tracked and then abandoned at the cap, so it shrinks with a
|
|
24
|
+
* smaller cap (two thirds of it). */
|
|
25
|
+
export const MAX_TIMER_HORIZON_MS = 1000;
|
|
26
|
+
/** Request types that never finish while the page is open, or never affect
|
|
27
|
+
* pixels. A WebSocket handshake emits no request events today; it is listed
|
|
28
|
+
* so it stays ignored if Chrome starts reporting it. */
|
|
29
|
+
export const DEFAULT_NETWORK_IGNORE = [
|
|
30
|
+
'type:WebSocket',
|
|
31
|
+
'type:EventSource',
|
|
32
|
+
'type:Ping',
|
|
33
|
+
'type:Prefetch',
|
|
34
|
+
];
|
|
35
|
+
/** CDP Network.ResourceType values, for `type:` validation. */
|
|
36
|
+
const RESOURCE_TYPES = [
|
|
37
|
+
'Document',
|
|
38
|
+
'Stylesheet',
|
|
39
|
+
'Image',
|
|
40
|
+
'Media',
|
|
41
|
+
'Font',
|
|
42
|
+
'Script',
|
|
43
|
+
'TextTrack',
|
|
44
|
+
'XHR',
|
|
45
|
+
'Fetch',
|
|
46
|
+
'Prefetch',
|
|
47
|
+
'EventSource',
|
|
48
|
+
'WebSocket',
|
|
49
|
+
'Manifest',
|
|
50
|
+
'SignedExchange',
|
|
51
|
+
'Ping',
|
|
52
|
+
'CSPViolationReport',
|
|
53
|
+
'Preflight',
|
|
54
|
+
'FedCM',
|
|
55
|
+
'Other',
|
|
56
|
+
];
|
|
57
|
+
/**
|
|
58
|
+
* `[type:<ResourceType>] [<glob>]`, at least one of the two. In the glob `**`
|
|
59
|
+
* matches anything, `*` anything but `/`, `?` one character but `/`. A glob
|
|
60
|
+
* starting with `/` matches the path (`/api/feed`), anything else the whole
|
|
61
|
+
* URL (`https://cdn.example.com/**`). Throws with the reason; the caller
|
|
62
|
+
* prefixes where the pattern came from.
|
|
63
|
+
*/
|
|
64
|
+
export function parsePattern(source) {
|
|
65
|
+
const tokens = source.trim().split(/\s+/).filter(Boolean);
|
|
66
|
+
let type;
|
|
67
|
+
let glob;
|
|
68
|
+
for (const t of tokens) {
|
|
69
|
+
if (/^type:/i.test(t)) {
|
|
70
|
+
if (type !== undefined)
|
|
71
|
+
throw new Error(`pattern '${source}' has two types`);
|
|
72
|
+
const name = t.slice(5);
|
|
73
|
+
const known = RESOURCE_TYPES.find((r) => r.toLowerCase() === name.toLowerCase());
|
|
74
|
+
if (!known)
|
|
75
|
+
throw new Error(`pattern '${source}': unknown resource type '${name}' (one of ${RESOURCE_TYPES.join(', ')})`);
|
|
76
|
+
type = known;
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
if (glob !== undefined)
|
|
80
|
+
throw new Error(`pattern '${source}' has two URL globs; write one, or one pattern per glob`);
|
|
81
|
+
glob = t;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
if (type === undefined && glob === undefined)
|
|
85
|
+
throw new Error('a pattern must not be empty');
|
|
86
|
+
return {
|
|
87
|
+
source,
|
|
88
|
+
...(type !== undefined ? { type } : {}),
|
|
89
|
+
...(glob !== undefined ? { glob: globToRegExp(glob) } : {}),
|
|
90
|
+
pathOnly: glob?.startsWith('/') === true,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
function globToRegExp(glob) {
|
|
94
|
+
let re = '';
|
|
95
|
+
for (let i = 0; i < glob.length; i++) {
|
|
96
|
+
const c = glob[i];
|
|
97
|
+
if (c === '*') {
|
|
98
|
+
if (glob[i + 1] === '*') {
|
|
99
|
+
re += '.*';
|
|
100
|
+
i++;
|
|
101
|
+
}
|
|
102
|
+
else
|
|
103
|
+
re += '[^/]*';
|
|
104
|
+
}
|
|
105
|
+
else if (c === '?')
|
|
106
|
+
re += '[^/]';
|
|
107
|
+
else
|
|
108
|
+
re += c.replace(/[.+^${}()|[\]\\]/g, '\\$&');
|
|
109
|
+
}
|
|
110
|
+
return new RegExp(`^${re}$`);
|
|
111
|
+
}
|
|
112
|
+
export function matchPattern(p, url, type) {
|
|
113
|
+
if (p.type !== undefined && p.type !== (type ?? 'Other'))
|
|
114
|
+
return false;
|
|
115
|
+
if (!p.glob)
|
|
116
|
+
return true;
|
|
117
|
+
if (!p.pathOnly)
|
|
118
|
+
return p.glob.test(url);
|
|
119
|
+
try {
|
|
120
|
+
return p.glob.test(new URL(url).pathname);
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
/** Merge two `settle` objects: scalars from `over`, lists concatenated
|
|
127
|
+
* without duplicates. Keys absent from both stay absent. */
|
|
128
|
+
export function mergeSettle(under, over) {
|
|
129
|
+
if (!under)
|
|
130
|
+
return over;
|
|
131
|
+
if (!over)
|
|
132
|
+
return under;
|
|
133
|
+
const list = (a, b) => a || b ? [...new Set([...(a ?? []), ...(b ?? [])])] : undefined;
|
|
134
|
+
const ignore = list(under.network?.ignore, over.network?.ignore);
|
|
135
|
+
const require = list(under.network?.require, over.network?.require);
|
|
136
|
+
const timeout = over.timeout ?? under.timeout;
|
|
137
|
+
const ready = over.ready ?? under.ready;
|
|
138
|
+
return {
|
|
139
|
+
...(timeout !== undefined ? { timeout } : {}),
|
|
140
|
+
...(ready !== undefined ? { ready } : {}),
|
|
141
|
+
...(ignore || require
|
|
142
|
+
? { network: { ...(ignore ? { ignore } : {}), ...(require ? { require } : {}) } }
|
|
143
|
+
: {}),
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
/** The settle wait for one capture, from its layers, lowest first. Assumes
|
|
147
|
+
* each layer passed checkSettle. */
|
|
148
|
+
export function resolveSettle(...layers) {
|
|
149
|
+
let merged;
|
|
150
|
+
for (const l of layers)
|
|
151
|
+
merged = mergeSettle(merged, l);
|
|
152
|
+
const timeoutMs = merged?.timeout ?? DEFAULT_SETTLE_TIMEOUT_MS;
|
|
153
|
+
return {
|
|
154
|
+
timeoutMs,
|
|
155
|
+
horizonMs: Math.min(MAX_TIMER_HORIZON_MS, Math.floor((timeoutMs * 2) / 3)),
|
|
156
|
+
ready: merged?.ready === true,
|
|
157
|
+
ignore: [...DEFAULT_NETWORK_IGNORE, ...(merged?.network?.ignore ?? [])].map(parsePattern),
|
|
158
|
+
require: (merged?.network?.require ?? []).map(parsePattern),
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
/** waitForSelector and delay: call > story > project. */
|
|
162
|
+
export function layerScalars(project, params, call) {
|
|
163
|
+
const waitForSelector = call?.waitForSelector ?? params.waitForSelector ?? project?.waitForSelector;
|
|
164
|
+
const delay = call?.delay ?? params.delay ?? project?.delay;
|
|
165
|
+
return {
|
|
166
|
+
...(waitForSelector !== undefined ? { waitForSelector } : {}),
|
|
167
|
+
...(delay !== undefined ? { delay } : {}),
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
const isRecord = (v) => typeof v === 'object' && v !== null && !Array.isArray(v);
|
|
171
|
+
/** The flag a settle report raises. */
|
|
172
|
+
export const settleFlag = (s) => s.status === 'timeout' ? 'settle-timeout' : 'settle-fallback';
|
|
173
|
+
/** A settle cap: a whole number of ms, 1 to MAX_SETTLE_TIMEOUT_MS. The CLI
|
|
174
|
+
* and the MCP server check per-call values with this too. */
|
|
175
|
+
export function isSettleTimeout(v) {
|
|
176
|
+
return (Number.isInteger(v) && v >= 1 && v <= MAX_SETTLE_TIMEOUT_MS);
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Validate a `settle` value where it is declared, so the error names the
|
|
180
|
+
* place to edit. `where` is e.g. `project config` or `story 'button--primary'`.
|
|
181
|
+
* Throws SettleConfigError; returns the value typed.
|
|
182
|
+
*/
|
|
183
|
+
export function checkSettle(raw, where) {
|
|
184
|
+
if (raw === undefined)
|
|
185
|
+
return undefined;
|
|
186
|
+
const fail = (m) => new SettleConfigError(`${where}: ${m}`);
|
|
187
|
+
if (!isRecord(raw))
|
|
188
|
+
throw fail(`'settle' must be an object`);
|
|
189
|
+
for (const k of Object.keys(raw)) {
|
|
190
|
+
if (k !== 'timeout' && k !== 'ready' && k !== 'network')
|
|
191
|
+
throw fail(`unknown settle option '${k}' (expected timeout, ready, network)`);
|
|
192
|
+
}
|
|
193
|
+
if (raw['timeout'] !== undefined && !isSettleTimeout(raw['timeout']))
|
|
194
|
+
throw fail(`settle.timeout must be a whole number of ms from 1 to ${MAX_SETTLE_TIMEOUT_MS}, got ${JSON.stringify(raw['timeout'])}`);
|
|
195
|
+
if (raw['ready'] !== undefined && typeof raw['ready'] !== 'boolean')
|
|
196
|
+
throw fail(`settle.ready must be true or false, got ${JSON.stringify(raw['ready'])}`);
|
|
197
|
+
const net = raw['network'];
|
|
198
|
+
if (net !== undefined) {
|
|
199
|
+
if (!isRecord(net))
|
|
200
|
+
throw fail(`settle.network must be an object`);
|
|
201
|
+
for (const k of Object.keys(net)) {
|
|
202
|
+
if (k !== 'ignore' && k !== 'require')
|
|
203
|
+
throw fail(`unknown settle.network option '${k}' (expected ignore, require)`);
|
|
204
|
+
checkPatterns(net[k], `settle.network.${k}`, fail);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
return raw;
|
|
208
|
+
}
|
|
209
|
+
/** Validate a pattern list; `fail` makes the error. */
|
|
210
|
+
export function checkPatterns(list, name, fail) {
|
|
211
|
+
if (!Array.isArray(list) || !list.every((p) => typeof p === 'string'))
|
|
212
|
+
throw fail(`${name} must be an array of URL patterns`);
|
|
213
|
+
for (const p of list) {
|
|
214
|
+
try {
|
|
215
|
+
parsePattern(p);
|
|
216
|
+
}
|
|
217
|
+
catch (e) {
|
|
218
|
+
throw fail(`${name}: ${e instanceof Error ? e.message : String(e)}`);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
return list;
|
|
222
|
+
}
|
|
223
|
+
/** The project config's settle options: `settle`, `waitForSelector`, `delay`. */
|
|
224
|
+
export function checkProjectSettle(config) {
|
|
225
|
+
const where = 'project config';
|
|
226
|
+
checkSettle(config.settle, where);
|
|
227
|
+
checkNetwork(config.network, where);
|
|
228
|
+
if (config.waitForSelector !== undefined && typeof config.waitForSelector !== 'string')
|
|
229
|
+
throw new SettleConfigError(`${where}: waitForSelector must be a CSS selector`);
|
|
230
|
+
if (config.delay !== undefined &&
|
|
231
|
+
(typeof config.delay !== 'number' ||
|
|
232
|
+
!Number.isFinite(config.delay) ||
|
|
233
|
+
config.delay < 0))
|
|
234
|
+
throw new SettleConfigError(`${where}: delay must be a number of ms, 0 or more`);
|
|
235
|
+
}
|
|
236
|
+
//# sourceMappingURL=settle-options.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"settle-options.js","sourceRoot":"","sources":["../../src/capture/settle-options.ts"],"names":[],"mappings":"AAAA,mEAAmE;AACnE,sDAAsD;AACtD,EAAE;AACF,0EAA0E;AAC1E,0EAA0E;AAC1E,8EAA8E;AAC9E,4EAA4E;AAC5E,yDAAyD;AACzD,EAAE;AACF,+EAA+E;AAS/E,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAEnD,OAAO,EAAE,iBAAiB,EAAE,CAAC;AAE7B;;;sDAGsD;AACtD,MAAM,CAAC,MAAM,yBAAyB,GAAG,IAAI,CAAC;AAC9C,+EAA+E;AAC/E,MAAM,CAAC,MAAM,qBAAqB,GAAG,MAAM,CAAC;AAC5C;;;sCAGsC;AACtC,MAAM,CAAC,MAAM,oBAAoB,GAAG,IAAI,CAAC;AAEzC;;yDAEyD;AACzD,MAAM,CAAC,MAAM,sBAAsB,GAAsB;IACvD,gBAAgB;IAChB,kBAAkB;IAClB,WAAW;IACX,eAAe;CAChB,CAAC;AAEF,+DAA+D;AAC/D,MAAM,cAAc,GAAG;IACrB,UAAU;IACV,YAAY;IACZ,OAAO;IACP,OAAO;IACP,MAAM;IACN,QAAQ;IACR,WAAW;IACX,KAAK;IACL,OAAO;IACP,UAAU;IACV,aAAa;IACb,WAAW;IACX,UAAU;IACV,gBAAgB;IAChB,MAAM;IACN,oBAAoB;IACpB,WAAW;IACX,OAAO;IACP,OAAO;CACC,CAAC;AAaX;;;;;;GAMG;AACH,MAAM,UAAU,YAAY,CAAC,MAAc;IACzC,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAC1D,IAAI,IAAwB,CAAC;IAC7B,IAAI,IAAwB,CAAC;IAC7B,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;QACvB,IAAI,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;YACtB,IAAI,IAAI,KAAK,SAAS;gBAAE,MAAM,IAAI,KAAK,CAAC,YAAY,MAAM,iBAAiB,CAAC,CAAC;YAC7E,MAAM,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YACxB,MAAM,KAAK,GAAG,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,KAAK,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC;YACjF,IAAI,CAAC,KAAK;gBACR,MAAM,IAAI,KAAK,CACb,YAAY,MAAM,6BAA6B,IAAI,aAAa,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAC7F,CAAC;YACJ,IAAI,GAAG,KAAK,CAAC;QACf,CAAC;aAAM,CAAC;YACN,IAAI,IAAI,KAAK,SAAS;gBACpB,MAAM,IAAI,KAAK,CACb,YAAY,MAAM,yDAAyD,CAC5E,CAAC;YACJ,IAAI,GAAG,CAAC,CAAC;QACX,CAAC;IACH,CAAC;IACD,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,SAAS;QAC1C,MAAM,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAC;IACjD,OAAO;QACL,MAAM;QACN,GAAG,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACvC,GAAG,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,YAAY,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC3D,QAAQ,EAAE,IAAI,EAAE,UAAU,CAAC,GAAG,CAAC,KAAK,IAAI;KACzC,CAAC;AACJ,CAAC;AAED,SAAS,YAAY,CAAC,IAAY;IAChC,IAAI,EAAE,GAAG,EAAE,CAAC;IACZ,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACrC,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC,CAAE,CAAC;QACnB,IAAI,CAAC,KAAK,GAAG,EAAE,CAAC;YACd,IAAI,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC;gBACxB,EAAE,IAAI,IAAI,CAAC;gBACX,CAAC,EAAE,CAAC;YACN,CAAC;;gBAAM,EAAE,IAAI,OAAO,CAAC;QACvB,CAAC;aAAM,IAAI,CAAC,KAAK,GAAG;YAAE,EAAE,IAAI,MAAM,CAAC;;YAC9B,EAAE,IAAI,CAAC,CAAC,OAAO,CAAC,mBAAmB,EAAE,MAAM,CAAC,CAAC;IACpD,CAAC;IACD,OAAO,IAAI,MAAM,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;AAC/B,CAAC;AAED,MAAM,UAAU,YAAY,CAC1B,CAAa,EACb,GAAW,EACX,IAAwB;IAExB,IAAI,CAAC,CAAC,IAAI,KAAK,SAAS,IAAI,CAAC,CAAC,IAAI,KAAK,CAAC,IAAI,IAAI,OAAO,CAAC;QAAE,OAAO,KAAK,CAAC;IACvE,IAAI,CAAC,CAAC,CAAC,IAAI;QAAE,OAAO,IAAI,CAAC;IACzB,IAAI,CAAC,CAAC,CAAC,QAAQ;QAAE,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACzC,IAAI,CAAC;QACH,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAC;IAC5C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAkBD;6DAC6D;AAC7D,MAAM,UAAU,WAAW,CACzB,KAA+B,EAC/B,IAA8B;IAE9B,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IACxB,IAAI,CAAC,IAAI;QAAE,OAAO,KAAK,CAAC;IACxB,MAAM,IAAI,GAAG,CAAC,CAAY,EAAE,CAAY,EAAE,EAAE,CAC1C,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAClE,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IACjE,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IACpE,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,IAAI,KAAK,CAAC,OAAO,CAAC;IAC9C,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,KAAK,CAAC;IACxC,OAAO;QACL,GAAG,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC7C,GAAG,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACzC,GAAG,CAAC,MAAM,IAAI,OAAO;YACnB,CAAC,CAAC,EAAE,OAAO,EAAE,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;YACjF,CAAC,CAAC,EAAE,CAAC;KACR,CAAC;AACJ,CAAC;AAED;qCACqC;AACrC,MAAM,UAAU,aAAa,CAC3B,GAAG,MAAuC;IAE1C,IAAI,MAAgC,CAAC;IACrC,KAAK,MAAM,CAAC,IAAI,MAAM;QAAE,MAAM,GAAG,WAAW,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IACxD,MAAM,SAAS,GAAG,MAAM,EAAE,OAAO,IAAI,yBAAyB,CAAC;IAC/D,OAAO;QACL,SAAS;QACT,SAAS,EAAE,IAAI,CAAC,GAAG,CAAC,oBAAoB,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,SAAS,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QAC1E,KAAK,EAAE,MAAM,EAAE,KAAK,KAAK,IAAI;QAC7B,MAAM,EAAE,CAAC,GAAG,sBAAsB,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,IAAI,EAAE,CAAC,CAAC,CAAC,GAAG,CACzE,YAAY,CACb;QACD,OAAO,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,YAAY,CAAC;KAC5D,CAAC;AACJ,CAAC;AAED,yDAAyD;AACzD,MAAM,UAAU,YAAY,CAC1B,OAAgC,EAChC,MAAmB,EACnB,IAA6B;IAE7B,MAAM,eAAe,GACnB,IAAI,EAAE,eAAe,IAAI,MAAM,CAAC,eAAe,IAAI,OAAO,EAAE,eAAe,CAAC;IAC9E,MAAM,KAAK,GAAG,IAAI,EAAE,KAAK,IAAI,MAAM,CAAC,KAAK,IAAI,OAAO,EAAE,KAAK,CAAC;IAC5D,OAAO;QACL,GAAG,CAAC,eAAe,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,eAAe,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC7D,GAAG,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC1C,CAAC;AACJ,CAAC;AAED,MAAM,QAAQ,GAAG,CAAC,CAAU,EAAgC,EAAE,CAC5D,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;AAE3D,uCAAuC;AACvC,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,CAA+B,EAAe,EAAE,CACzE,CAAC,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,iBAAiB,CAAC;AAEhE;8DAC8D;AAC9D,MAAM,UAAU,eAAe,CAAC,CAAU;IACxC,OAAO,CACL,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,IAAK,CAAY,IAAI,CAAC,IAAK,CAAY,IAAI,qBAAqB,CACpF,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,WAAW,CAAC,GAAY,EAAE,KAAa;IACrD,IAAI,GAAG,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IACxC,MAAM,IAAI,GAAG,CAAC,CAAS,EAAE,EAAE,CAAC,IAAI,iBAAiB,CAAC,GAAG,KAAK,KAAK,CAAC,EAAE,CAAC,CAAC;IACpE,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC;QAAE,MAAM,IAAI,CAAC,4BAA4B,CAAC,CAAC;IAC7D,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QACjC,IAAI,CAAC,KAAK,SAAS,IAAI,CAAC,KAAK,OAAO,IAAI,CAAC,KAAK,SAAS;YACrD,MAAM,IAAI,CAAC,0BAA0B,CAAC,sCAAsC,CAAC,CAAC;IAClF,CAAC;IACD,IAAI,GAAG,CAAC,SAAS,CAAC,KAAK,SAAS,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QAClE,MAAM,IAAI,CACR,yDAAyD,qBAAqB,SAAS,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,EAAE,CACxH,CAAC;IACJ,IAAI,GAAG,CAAC,OAAO,CAAC,KAAK,SAAS,IAAI,OAAO,GAAG,CAAC,OAAO,CAAC,KAAK,SAAS;QACjE,MAAM,IAAI,CAAC,2CAA2C,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC;IACxF,MAAM,GAAG,GAAG,GAAG,CAAC,SAAS,CAAC,CAAC;IAC3B,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;QACtB,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC;YAAE,MAAM,IAAI,CAAC,kCAAkC,CAAC,CAAC;QACnE,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;YACjC,IAAI,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,SAAS;gBACnC,MAAM,IAAI,CAAC,kCAAkC,CAAC,8BAA8B,CAAC,CAAC;YAChF,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,kBAAkB,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;QACrD,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,uDAAuD;AACvD,MAAM,UAAU,aAAa,CAC3B,IAAa,EACb,IAAY,EACZ,IAA0B;IAE1B,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAe,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC;QAChF,MAAM,IAAI,CAAC,GAAG,IAAI,mCAAmC,CAAC,CAAC;IACzD,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;QACrB,IAAI,CAAC;YACH,YAAY,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,MAAM,IAAI,CAAC,GAAG,IAAI,KAAK,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QACvE,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,iFAAiF;AACjF,MAAM,UAAU,kBAAkB,CAAC,MAKlC;IACC,MAAM,KAAK,GAAG,gBAAgB,CAAC;IAC/B,WAAW,CAAC,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IAClC,YAAY,CAAC,MAAM,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IACpC,IAAI,MAAM,CAAC,eAAe,KAAK,SAAS,IAAI,OAAO,MAAM,CAAC,eAAe,KAAK,QAAQ;QACpF,MAAM,IAAI,iBAAiB,CAAC,GAAG,KAAK,0CAA0C,CAAC,CAAC;IAClF,IACE,MAAM,CAAC,KAAK,KAAK,SAAS;QAC1B,CAAC,OAAO,MAAM,CAAC,KAAK,KAAK,QAAQ;YAC/B,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC;YAC9B,MAAM,CAAC,KAAK,GAAG,CAAC,CAAC;QAEnB,MAAM,IAAI,iBAAiB,CAAC,GAAG,KAAK,2CAA2C,CAAC,CAAC;AACrF,CAAC"}
|
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
|
*
|
|
@@ -29,6 +11,66 @@ export declare const FREEZE_ANIMATIONS_CSS = "\n*, *::before, *::after {\n anim
|
|
|
29
11
|
/** Wait for an explicit selector. Escape hatch for async content the network
|
|
30
12
|
* drain can't observe — SDK init, canvas paint, third-party widgets. */
|
|
31
13
|
export declare function waitForSelector(page: Page, selector: string, timeoutMs: number): Promise<void>;
|
|
14
|
+
/**
|
|
15
|
+
* Drop and rebuild every `will-change` compositor layer, so each is rastered
|
|
16
|
+
* once, from the page as it stands now.
|
|
17
|
+
*
|
|
18
|
+
* Chrome picks a layer's raster translation (the subpixel offset its content
|
|
19
|
+
* is rasterized at) when it first rasterizes the layer, and keeps it on a
|
|
20
|
+
* `will-change: transform` layer so a moving transform never re-rasters. When
|
|
21
|
+
* the transform changes after that first raster (an animation that ran a
|
|
22
|
+
* frame before the freeze, or a `translateX(-100%)` whose width grew when a
|
|
23
|
+
* font loaded) the layer keeps the old offset, and its glyph antialiasing
|
|
24
|
+
* records the page's history instead of its final state. That history is
|
|
25
|
+
* timing: one Chakra marquee captured 3 different hashes depending on run
|
|
26
|
+
* context (issue #136), with the same frozen transform in all of them.
|
|
27
|
+
*
|
|
28
|
+
* A frame with `will-change: auto` composites the element into its parent;
|
|
29
|
+
* removing the override makes a new layer, rastered at its current
|
|
30
|
+
* transform. Measured on a translateX marquee fixture through snapPages, 40
|
|
31
|
+
* captures per arm: animated 39/40 and load-delayed 40/40 differed from the
|
|
32
|
+
* same page with a static transform; with this, 0/80. On the Chakra story
|
|
33
|
+
* the three contexts now capture one hash, the same one a page frozen from
|
|
34
|
+
* its first frame and then rebuilt this way gives. A device-scale round trip
|
|
35
|
+
* and `--disable-threaded-animation` leave the stale raster; freezing
|
|
36
|
+
* animations from the first frame (an init script) fixes the fixture but not
|
|
37
|
+
* Chakra, whose marquee widens when its font loads.
|
|
38
|
+
*
|
|
39
|
+
* Only the elements whose computed `will-change` is not `auto` are touched,
|
|
40
|
+
* through an inline `!important` declaration, in the document and in every
|
|
41
|
+
* open shadow root. The first version adopted a `* { will-change: auto
|
|
42
|
+
* !important }` sheet instead, and that went wrong in four measured ways:
|
|
43
|
+
* - `will-change: transform` (or filter, perspective) gives an element a
|
|
44
|
+
* containing block for fixed descendants and a stacking context. Dropping
|
|
45
|
+
* it moved those descendants to the viewport for two frames, so a latching
|
|
46
|
+
* IntersectionObserver captured "seen" 6/6, a ResizeObserver reacting
|
|
47
|
+
* through a 30 ms timer captured the wrong layout 3-4/6, and every such
|
|
48
|
+
* page took the recheck. Where the computed transform, translate and
|
|
49
|
+
* filter are all `none`, the element also gets `translate: 0px
|
|
50
|
+
* !important` for the two frames: an identity 2D transform, with no layer
|
|
51
|
+
* of its own and the same containing block. 0/6 after, and no recheck.
|
|
52
|
+
* - An adopted sheet does not reach shadow roots: a marquee in one stayed
|
|
53
|
+
* stale 4/4. 0/4 after.
|
|
54
|
+
* - An inline `will-change: transform !important` beats an author
|
|
55
|
+
* `!important` sheet: stale 4/4. 0/4 after.
|
|
56
|
+
* - Adopting any `*` sheet forces a whole-document style recalc, which by
|
|
57
|
+
* itself re-rastered a frozen fill-forwards transform animation with no
|
|
58
|
+
* will-change (Chakra button--custom-spinner) to a hash a static page
|
|
59
|
+
* never gives. Touching only will-change elements leaves those alone.
|
|
60
|
+
* The writes run through `__juxIdle.untracked` (idle.ts), so the mutation
|
|
61
|
+
* counter, and with it the recheck in captureStory, never sees them. Each
|
|
62
|
+
* declaration is put back as it was, in the second frame or on timeout.
|
|
63
|
+
* A page with no will-change element, most stories, skips the two frames:
|
|
64
|
+
* 16-18 ms became the scan, 0.7 / 1.9 / 6.9 ms on 200 / 2k / 10k elements.
|
|
65
|
+
*
|
|
66
|
+
* Scope: this rebuilds `will-change: transform` layers. A layer promoted
|
|
67
|
+
* another way (`translateZ(0)`, `translate3d`, `backdrop-filter`) or by
|
|
68
|
+
* `will-change: opacity` or `filter` keeps its first raster: stale 3-4/4 when
|
|
69
|
+
* it grows after that raster, before and after this. Will-change on a
|
|
70
|
+
* pseudo-element is not reached either, since it takes no inline style.
|
|
71
|
+
* Those need a different mechanism than a style toggle (see #145).
|
|
72
|
+
*/
|
|
73
|
+
export declare function rerasterLayers(page: Page, timeoutMs?: number): Promise<void>;
|
|
32
74
|
/**
|
|
33
75
|
* Block until the compositor has actually presented a frame.
|
|
34
76
|
*
|