@e2e-dev/web 0.11.0-canary-20260928184528

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (121) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +5 -0
  3. package/README.md +138 -0
  4. package/dist/.build.json +1 -0
  5. package/dist/actions.d.ts +23 -0
  6. package/dist/actions.d.ts.map +1 -0
  7. package/dist/actions.js +174 -0
  8. package/dist/actions.js.map +1 -0
  9. package/dist/attempt-session.d.ts +77 -0
  10. package/dist/attempt-session.d.ts.map +1 -0
  11. package/dist/attempt-session.js +293 -0
  12. package/dist/attempt-session.js.map +1 -0
  13. package/dist/browser-connection.d.ts +34 -0
  14. package/dist/browser-connection.d.ts.map +1 -0
  15. package/dist/browser-connection.js +86 -0
  16. package/dist/browser-connection.js.map +1 -0
  17. package/dist/capture-scope.d.ts +21 -0
  18. package/dist/capture-scope.d.ts.map +1 -0
  19. package/dist/capture-scope.js +61 -0
  20. package/dist/capture-scope.js.map +1 -0
  21. package/dist/cdp-recovery.d.ts +29 -0
  22. package/dist/cdp-recovery.d.ts.map +1 -0
  23. package/dist/cdp-recovery.js +84 -0
  24. package/dist/cdp-recovery.js.map +1 -0
  25. package/dist/cdp-selectors.d.ts +15 -0
  26. package/dist/cdp-selectors.d.ts.map +1 -0
  27. package/dist/cdp-selectors.js +52 -0
  28. package/dist/cdp-selectors.js.map +1 -0
  29. package/dist/closed-shadow.d.ts +64 -0
  30. package/dist/closed-shadow.d.ts.map +1 -0
  31. package/dist/closed-shadow.js +142 -0
  32. package/dist/closed-shadow.js.map +1 -0
  33. package/dist/dialogs.d.ts +38 -0
  34. package/dist/dialogs.d.ts.map +1 -0
  35. package/dist/dialogs.js +87 -0
  36. package/dist/dialogs.js.map +1 -0
  37. package/dist/engine.d.ts +26 -0
  38. package/dist/engine.d.ts.map +1 -0
  39. package/dist/engine.js +176 -0
  40. package/dist/engine.js.map +1 -0
  41. package/dist/evaluation.d.ts +18 -0
  42. package/dist/evaluation.d.ts.map +1 -0
  43. package/dist/evaluation.js +35 -0
  44. package/dist/evaluation.js.map +1 -0
  45. package/dist/in-page/read-semantics.d.ts +28 -0
  46. package/dist/in-page/read-semantics.d.ts.map +1 -0
  47. package/dist/in-page/read-semantics.js +1178 -0
  48. package/dist/in-page/read-semantics.js.map +1 -0
  49. package/dist/index.d.ts +22 -0
  50. package/dist/index.d.ts.map +1 -0
  51. package/dist/index.js +14 -0
  52. package/dist/index.js.map +1 -0
  53. package/dist/install.d.ts +40 -0
  54. package/dist/install.d.ts.map +1 -0
  55. package/dist/install.js +117 -0
  56. package/dist/install.js.map +1 -0
  57. package/dist/label-selector.d.ts +37 -0
  58. package/dist/label-selector.d.ts.map +1 -0
  59. package/dist/label-selector.js +53 -0
  60. package/dist/label-selector.js.map +1 -0
  61. package/dist/locators.d.ts +76 -0
  62. package/dist/locators.d.ts.map +1 -0
  63. package/dist/locators.js +289 -0
  64. package/dist/locators.js.map +1 -0
  65. package/dist/observation-capture.d.ts +12 -0
  66. package/dist/observation-capture.d.ts.map +1 -0
  67. package/dist/observation-capture.js +93 -0
  68. package/dist/observation-capture.js.map +1 -0
  69. package/dist/observation.d.ts +63 -0
  70. package/dist/observation.d.ts.map +1 -0
  71. package/dist/observation.js +280 -0
  72. package/dist/observation.js.map +1 -0
  73. package/dist/observe.d.ts +51 -0
  74. package/dist/observe.d.ts.map +1 -0
  75. package/dist/observe.js +105 -0
  76. package/dist/observe.js.map +1 -0
  77. package/dist/operation-budget.d.ts +11 -0
  78. package/dist/operation-budget.d.ts.map +1 -0
  79. package/dist/operation-budget.js +40 -0
  80. package/dist/operation-budget.js.map +1 -0
  81. package/dist/protected-app.d.ts +32 -0
  82. package/dist/protected-app.d.ts.map +1 -0
  83. package/dist/protected-app.js +39 -0
  84. package/dist/protected-app.js.map +1 -0
  85. package/dist/provider.d.ts +158 -0
  86. package/dist/provider.d.ts.map +1 -0
  87. package/dist/provider.js +311 -0
  88. package/dist/provider.js.map +1 -0
  89. package/dist/read-node.d.ts +152 -0
  90. package/dist/read-node.d.ts.map +1 -0
  91. package/dist/read-node.js +42 -0
  92. package/dist/read-node.js.map +1 -0
  93. package/dist/refs.d.ts +43 -0
  94. package/dist/refs.d.ts.map +1 -0
  95. package/dist/refs.js +85 -0
  96. package/dist/refs.js.map +1 -0
  97. package/dist/route-pattern.d.ts +13 -0
  98. package/dist/route-pattern.d.ts.map +1 -0
  99. package/dist/route-pattern.js +55 -0
  100. package/dist/route-pattern.js.map +1 -0
  101. package/dist/selector-engines.d.ts +6 -0
  102. package/dist/selector-engines.d.ts.map +1 -0
  103. package/dist/selector-engines.js +8 -0
  104. package/dist/selector-engines.js.map +1 -0
  105. package/dist/support.d.ts +147 -0
  106. package/dist/support.d.ts.map +1 -0
  107. package/dist/support.js +366 -0
  108. package/dist/support.js.map +1 -0
  109. package/dist/surface.d.ts +313 -0
  110. package/dist/surface.d.ts.map +1 -0
  111. package/dist/surface.js +691 -0
  112. package/dist/surface.js.map +1 -0
  113. package/dist/video.d.ts +59 -0
  114. package/dist/video.d.ts.map +1 -0
  115. package/dist/video.js +116 -0
  116. package/dist/video.js.map +1 -0
  117. package/dist/web.d.ts +191 -0
  118. package/dist/web.d.ts.map +1 -0
  119. package/dist/web.js +382 -0
  120. package/dist/web.js.map +1 -0
  121. package/package.json +67 -0
@@ -0,0 +1,84 @@
1
+ import { EngineError } from 'e2e/engine';
2
+ import { connectCdp } from './browser-connection.js';
3
+ import { registerCdpSelectors, requireCdpShadowTracking } from './cdp-selectors.js';
4
+ import { connectionAbort } from './operation-budget.js';
5
+ /** A failed reconnect must never quietly provision another session. */
6
+ export function recoveryFailed(detail) {
7
+ return new EngineError('ENGINE_FAILURE', `CDP recovery failed: ${detail}`, { retryable: false });
8
+ }
9
+ /** Reads protocol identity, independent of URL, document contents, and tab order. */
10
+ export async function targetIdentity(page) {
11
+ const session = await page.context().newCDPSession(page);
12
+ try {
13
+ return (await session.send('Target.getTargetInfo')).targetInfo;
14
+ }
15
+ finally {
16
+ await session.detach().catch(() => undefined);
17
+ }
18
+ }
19
+ /** Owns an uncommitted connection until its identity is proven; aborted and late connections detach. */
20
+ export async function attachPersistent(resolve, budget, previous) {
21
+ const { signal } = budget;
22
+ const deadline = Date.now() + budget.timeoutMs;
23
+ const check = () => { if (signal.aborted)
24
+ throw connectionAbort(signal, 'CDP connection'); };
25
+ check();
26
+ const endpoint = await resolve(signal);
27
+ check();
28
+ if (typeof endpoint !== 'string' || endpoint.trim() === '')
29
+ throw recoveryFailed('the endpoint is empty');
30
+ const remaining = deadline - Date.now();
31
+ if (remaining <= 0)
32
+ throw new EngineError('OPERATION_TIMEOUT', 'CDP connection timed out', { retryable: false });
33
+ const browser = await connectCdp(endpoint, remaining);
34
+ const detach = () => { void browser.close().catch(() => undefined); };
35
+ signal.addEventListener('abort', detach, { once: true });
36
+ try {
37
+ check();
38
+ const session = await browser.newBrowserCDPSession();
39
+ let contextId;
40
+ try {
41
+ const contexts = await session.send('Target.getBrowserContexts');
42
+ if (contexts.browserContextIds.length > 0) {
43
+ throw recoveryFailed('the remote browser must contain only its dedicated default context');
44
+ }
45
+ contextId = contexts.defaultBrowserContextId;
46
+ }
47
+ finally {
48
+ await session.detach().catch(() => undefined);
49
+ }
50
+ check();
51
+ const context = browser.contexts()[0];
52
+ if (context === undefined)
53
+ throw recoveryFailed('the default context is missing');
54
+ if (contextId === undefined || contextId === '')
55
+ throw recoveryFailed('the default context identity is unavailable');
56
+ if (previous !== undefined && contextId !== previous.contextId) {
57
+ throw recoveryFailed('reconnectEndpoint returned a different browser');
58
+ }
59
+ await registerCdpSelectors(context, { signal, timeoutMs: deadline - Date.now() });
60
+ check();
61
+ if (previous?.target === undefined)
62
+ return { browser, context, page: null, identity: { contextId } };
63
+ for (const page of context.pages()) {
64
+ const target = await targetIdentity(page);
65
+ check();
66
+ if (target.targetId !== previous.target.targetId)
67
+ continue;
68
+ if (target.browserContextId !== previous.target.browserContextId) {
69
+ throw recoveryFailed('the original page belongs to a different context');
70
+ }
71
+ await requireCdpShadowTracking(page, { signal, timeoutMs: deadline - Date.now() });
72
+ return { browser, context, page, identity: { contextId, target } };
73
+ }
74
+ throw recoveryFailed('the original page no longer exists');
75
+ }
76
+ catch (cause) {
77
+ await browser.close().catch(() => undefined);
78
+ throw cause;
79
+ }
80
+ finally {
81
+ signal.removeEventListener('abort', detach);
82
+ }
83
+ }
84
+ //# sourceMappingURL=cdp-recovery.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cdp-recovery.js","sourceRoot":"","sources":["../src/cdp-recovery.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ /** Registers the engine's selector engines in a CDP default context; every locator and secure-field mask needs them. */
2
+ import type { BrowserContext, Page } from 'playwright';
3
+ import { type ConnectionBudget } from './operation-budget.ts';
4
+ /**
5
+ * Playwright 1.63 seeds custom selectors only in newly created contexts, not
6
+ * the persistent context returned by connectOverCDP. Its public register API
7
+ * rejects an already registered name before reaching that context. Use the
8
+ * same context channel as selectors.register, without allocating global names
9
+ * on every reconnect. Missing channel support must fail the connection: every
10
+ * locator resolves through these engines and every masked capture relies on them.
11
+ */
12
+ export declare function registerCdpSelectors(context: BrowserContext, budget: ConnectionBudget): Promise<void>;
13
+ /** Existing closed roots cannot be recovered if their document loaded without the tracking hook. */
14
+ export declare function requireCdpShadowTracking(page: Page, budget: ConnectionBudget): Promise<void>;
15
+ //# sourceMappingURL=cdp-selectors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cdp-selectors.d.ts","sourceRoot":"","sources":["../src/cdp-selectors.ts"],"names":[],"mappings":"AAAA,wHAAwH;AAExH,OAAO,KAAK,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAEvD,OAAO,EAAmB,KAAK,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAW/E;;;;;;;GAOG;AACH,wBAAsB,oBAAoB,CAAC,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAuB3G;AAED,oGAAoG;AACpG,wBAAsB,wBAAwB,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CASlG"}
@@ -0,0 +1,52 @@
1
+ /** Registers the engine's selector engines in a CDP default context; every locator and secure-field mask needs them. */
2
+ import { EngineError } from 'e2e/engine';
3
+ import { connectionAbort } from './operation-budget.js';
4
+ import { CLOSED_SHADOW_ROOTS_KEY } from './closed-shadow.js';
5
+ import { SELECTOR_ENGINES } from './selector-engines.js';
6
+ /**
7
+ * Playwright 1.63 seeds custom selectors only in newly created contexts, not
8
+ * the persistent context returned by connectOverCDP. Its public register API
9
+ * rejects an already registered name before reaching that context. Use the
10
+ * same context channel as selectors.register, without allocating global names
11
+ * on every reconnect. Missing channel support must fail the connection: every
12
+ * locator resolves through these engines and every masked capture relies on them.
13
+ */
14
+ export async function registerCdpSelectors(context, budget) {
15
+ if (budget.signal.aborted)
16
+ throw connectionAbort(budget.signal, 'CDP selector registration');
17
+ if (budget.timeoutMs <= 0) {
18
+ throw new EngineError('OPERATION_TIMEOUT', 'CDP selector registration timed out', { retryable: false });
19
+ }
20
+ const channel = Reflect.get(context, '_channel');
21
+ const register = channel !== null && typeof channel === 'object' ? Reflect.get(channel, 'registerSelectorEngine') : undefined;
22
+ if (typeof register !== 'function') {
23
+ throw new EngineError('UNSUPPORTED_CAPABILITY', 'this Playwright version cannot register selector engines in the CDP context; locators and secure-field masks need them', { retryable: false });
24
+ }
25
+ for (const { name, source } of SELECTOR_ENGINES) {
26
+ try {
27
+ await channel.registerSelectorEngine({ selectorEngine: {
28
+ name,
29
+ source: `(${source})(undefined)`,
30
+ contentScript: false,
31
+ } }, { timeout: budget.timeoutMs, signal: budget.signal });
32
+ }
33
+ catch (cause) {
34
+ // A version that seeds CDP contexts already has our process-wide registration.
35
+ const duplicate = `"${name}" selector engine has been already registered`;
36
+ if (!(cause instanceof Error) || !cause.message.endsWith(duplicate))
37
+ throw cause;
38
+ }
39
+ }
40
+ }
41
+ /** Existing closed roots cannot be recovered if their document loaded without the tracking hook. */
42
+ export async function requireCdpShadowTracking(page, budget) {
43
+ if (budget.signal.aborted)
44
+ throw connectionAbort(budget.signal, 'CDP tracking verification');
45
+ const tracked = await Promise.all(page.frames().map((frame) => frame.evaluate((key) => Reflect.get(globalThis, Symbol.for(key)) instanceof WeakMap, CLOSED_SHADOW_ROOTS_KEY)));
46
+ if (budget.signal.aborted)
47
+ throw connectionAbort(budget.signal, 'CDP tracking verification');
48
+ if (tracked.some((present) => !present)) {
49
+ throw new EngineError('ENGINE_FAILURE', 'CDP recovery failed: closed shadow root tracking is unavailable after a document changed while disconnected; locators and secure-field masks need it', { retryable: false });
50
+ }
51
+ }
52
+ //# sourceMappingURL=cdp-selectors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cdp-selectors.js","sourceRoot":"","sources":["../src/cdp-selectors.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Closed shadow roots, kept reachable for the reader, the locators, and the
3
+ * screenshot masks: the context init script that records them, and the two
4
+ * Playwright selector engines that search them. Both engines are page code as
5
+ * source strings; the engine list is what the browser connection and the CDP
6
+ * context register.
7
+ */
8
+ /**
9
+ * Where the init script below records closed shadow roots, keyed by host, and
10
+ * where the in-page reader looks for them. The reader is serialized into the
11
+ * page and cannot import this constant, so the literal is repeated inside it;
12
+ * the two must agree, like `SECURE_FIELD_SELECTOR` on both sides of masking.
13
+ *
14
+ * The record is a `WeakMap` from host to root with one own property beside
15
+ * the map: `count`, the number of closed roots the document has attached. It
16
+ * lets a selector engine answer for a document without closed roots without
17
+ * walking it.
18
+ */
19
+ export declare const CLOSED_SHADOW_ROOTS_KEY = "e2e.closedShadowRoots";
20
+ /**
21
+ * Context init script that keeps every closed shadow root reachable for the
22
+ * reader. A closed root hides its tree from `element.shadowRoot`, so a checkout
23
+ * button a third-party widget renders that way is on screen for a person yet
24
+ * absent from the walk. `attachShadow` is the one way a script creates such a
25
+ * root; wrapping it before any page script runs records host and root in a
26
+ * WeakMap under a well-known symbol, which the reader consults where it reads
27
+ * `shadowRoot`, and counts the roots attached so far. The map is keyed by the
28
+ * host element and never enumerated, so it holds nothing alive and changes
29
+ * nothing the page can observe about the root itself. Declarative
30
+ * `<template shadowrootmode="closed">` roots are parsed rather than attached
31
+ * and stay out of reach.
32
+ */
33
+ export declare const CLOSED_SHADOW_ROOTS_INIT_SCRIPT: string;
34
+ /** Playwright selector engine name; `e2e-closed=<css>` matches inside recorded closed shadow roots. */
35
+ export declare const CLOSED_SHADOW_SELECTOR_ENGINE = "e2e-closed";
36
+ /**
37
+ * Playwright selector engine name for the roots a semantic query searches.
38
+ * `e2e-roots=` (an empty body) resolves to the query root followed by every
39
+ * closed shadow root recorded under it, so the Playwright query part that
40
+ * follows (`internal:role`, `internal:text`, `internal:label`, ...) runs its
41
+ * own matching in each of them; `e2e-roots=<css>` resolves to the elements the
42
+ * CSS selector matches in the query root and in every closed root under it.
43
+ */
44
+ export declare const SEARCH_ROOTS_SELECTOR_ENGINE = "e2e-roots";
45
+ /**
46
+ * In-page helpers the selector engines below and the label engine
47
+ * (`label-selector.ts`) are built on, as source: the
48
+ * closed roots the init script recorded under a query root (the root's own,
49
+ * when it is a closed host, and every one below through open and closed
50
+ * roots alike), and the elements a CSS selector matches in one tree and the
51
+ * open roots below it. A closed root is searched only as an entry of the
52
+ * first list, never by recursion from the tree above it, so each element is
53
+ * reached from exactly one root. A document that has attached no closed root
54
+ * (the record's `count` is zero) has none to find, and the walk over every
55
+ * element is skipped; every locator resolution runs this, so a page without
56
+ * closed roots must pay nothing for the reach.
57
+ */
58
+ export declare const CLOSED_SHADOW_HELPERS_SOURCE: string;
59
+ /** The two closed-root selector engines; `selector-engines.ts` lists them with the label engine for registration. */
60
+ export declare const CLOSED_SHADOW_SELECTOR_ENGINES: readonly {
61
+ readonly name: string;
62
+ readonly source: string;
63
+ }[];
64
+ //# sourceMappingURL=closed-shadow.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"closed-shadow.d.ts","sourceRoot":"","sources":["../src/closed-shadow.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;;;;;;;;GAUG;AACH,eAAO,MAAM,uBAAuB,0BAA0B,CAAC;AAE/D;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,+BAA+B,QAetC,CAAC;AAEP,uGAAuG;AACvG,eAAO,MAAM,6BAA6B,eAAe,CAAC;AAE1D;;;;;;;GAOG;AACH,eAAO,MAAM,4BAA4B,cAAc,CAAC;AAExD;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,4BAA4B,QAsBpC,CAAC;AA6CN,qHAAqH;AACrH,eAAO,MAAM,8BAA8B,EAAE,SAAS;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,EAGvG,CAAC"}
@@ -0,0 +1,142 @@
1
+ /**
2
+ * Closed shadow roots, kept reachable for the reader, the locators, and the
3
+ * screenshot masks: the context init script that records them, and the two
4
+ * Playwright selector engines that search them. Both engines are page code as
5
+ * source strings; the engine list is what the browser connection and the CDP
6
+ * context register.
7
+ */
8
+ /**
9
+ * Where the init script below records closed shadow roots, keyed by host, and
10
+ * where the in-page reader looks for them. The reader is serialized into the
11
+ * page and cannot import this constant, so the literal is repeated inside it;
12
+ * the two must agree, like `SECURE_FIELD_SELECTOR` on both sides of masking.
13
+ *
14
+ * The record is a `WeakMap` from host to root with one own property beside
15
+ * the map: `count`, the number of closed roots the document has attached. It
16
+ * lets a selector engine answer for a document without closed roots without
17
+ * walking it.
18
+ */
19
+ export const CLOSED_SHADOW_ROOTS_KEY = 'e2e.closedShadowRoots';
20
+ /**
21
+ * Context init script that keeps every closed shadow root reachable for the
22
+ * reader. A closed root hides its tree from `element.shadowRoot`, so a checkout
23
+ * button a third-party widget renders that way is on screen for a person yet
24
+ * absent from the walk. `attachShadow` is the one way a script creates such a
25
+ * root; wrapping it before any page script runs records host and root in a
26
+ * WeakMap under a well-known symbol, which the reader consults where it reads
27
+ * `shadowRoot`, and counts the roots attached so far. The map is keyed by the
28
+ * host element and never enumerated, so it holds nothing alive and changes
29
+ * nothing the page can observe about the root itself. Declarative
30
+ * `<template shadowrootmode="closed">` roots are parsed rather than attached
31
+ * and stay out of reach.
32
+ */
33
+ export const CLOSED_SHADOW_ROOTS_INIT_SCRIPT = `(() => {
34
+ const key = Symbol.for(${JSON.stringify(CLOSED_SHADOW_ROOTS_KEY)});
35
+ if (Object.prototype.hasOwnProperty.call(globalThis, key)) return;
36
+ const roots = new WeakMap();
37
+ Object.defineProperty(roots, 'count', { value: 0, enumerable: false, configurable: false, writable: true });
38
+ Object.defineProperty(globalThis, key, { value: roots, enumerable: false, configurable: false, writable: false });
39
+ const attachShadow = Element.prototype.attachShadow;
40
+ Element.prototype.attachShadow = function (init) {
41
+ const root = attachShadow.call(this, init);
42
+ if (root.mode === 'closed') {
43
+ roots.set(this, root);
44
+ roots.count += 1;
45
+ }
46
+ return root;
47
+ };
48
+ })();`;
49
+ /** Playwright selector engine name; `e2e-closed=<css>` matches inside recorded closed shadow roots. */
50
+ export const CLOSED_SHADOW_SELECTOR_ENGINE = 'e2e-closed';
51
+ /**
52
+ * Playwright selector engine name for the roots a semantic query searches.
53
+ * `e2e-roots=` (an empty body) resolves to the query root followed by every
54
+ * closed shadow root recorded under it, so the Playwright query part that
55
+ * follows (`internal:role`, `internal:text`, `internal:label`, ...) runs its
56
+ * own matching in each of them; `e2e-roots=<css>` resolves to the elements the
57
+ * CSS selector matches in the query root and in every closed root under it.
58
+ */
59
+ export const SEARCH_ROOTS_SELECTOR_ENGINE = 'e2e-roots';
60
+ /**
61
+ * In-page helpers the selector engines below and the label engine
62
+ * (`label-selector.ts`) are built on, as source: the
63
+ * closed roots the init script recorded under a query root (the root's own,
64
+ * when it is a closed host, and every one below through open and closed
65
+ * roots alike), and the elements a CSS selector matches in one tree and the
66
+ * open roots below it. A closed root is searched only as an entry of the
67
+ * first list, never by recursion from the tree above it, so each element is
68
+ * reached from exactly one root. A document that has attached no closed root
69
+ * (the record's `count` is zero) has none to find, and the walk over every
70
+ * element is skipped; every locator resolution runs this, so a page without
71
+ * closed roots must pay nothing for the reach.
72
+ */
73
+ export const CLOSED_SHADOW_HELPERS_SOURCE = `
74
+ const roots = globalThis[Symbol.for(${JSON.stringify(CLOSED_SHADOW_ROOTS_KEY)})];
75
+ const tracked = roots instanceof WeakMap;
76
+ const closedRootsUnder = (root, out) => {
77
+ if (!tracked || roots.count === 0) return out;
78
+ const scan = (el) => {
79
+ const closed = roots.get(el);
80
+ if (closed !== undefined) out.push(closed);
81
+ const nested = el.shadowRoot ?? closed;
82
+ if (nested !== undefined && nested !== null) closedRootsUnder(nested, out);
83
+ };
84
+ if (root.nodeType === 1) scan(root);
85
+ for (const el of root.querySelectorAll('*')) scan(el);
86
+ return out;
87
+ };
88
+ const matchesIn = (root, selector, out) => {
89
+ for (const el of root.querySelectorAll(selector)) out.push(el);
90
+ if (root.nodeType === 1 && root.shadowRoot !== null) matchesIn(root.shadowRoot, selector, out);
91
+ for (const el of root.querySelectorAll('*')) {
92
+ if (el.shadowRoot !== null) matchesIn(el.shadowRoot, selector, out);
93
+ }
94
+ return out;
95
+ };`;
96
+ /**
97
+ * The engine behind `e2e-closed=<css>`: every element matching the CSS
98
+ * selector inside a closed shadow root the init script recorded, reached from
99
+ * the query root through light DOM and open roots, and through roots of either
100
+ * kind nested below a closed one. Playwright's own selectors stop at a closed
101
+ * root, so this is what lets a screenshot mask cover a secure field the reader
102
+ * now reports there. It runs in the page's main world (not as a content
103
+ * script) because that is where the record lives. A document without the
104
+ * record is refused: a mask that cannot prove its coverage must fail closed.
105
+ */
106
+ const CLOSED_SHADOW_SELECTOR_ENGINE_SOURCE = `() => {${CLOSED_SHADOW_HELPERS_SOURCE}
107
+ const queryAll = (root, selector) => {
108
+ if (!tracked) throw new Error('closed-shadow root tracking is unavailable for this document');
109
+ const out = [];
110
+ for (const closed of closedRootsUnder(root, [])) matchesIn(closed, selector, out);
111
+ return out;
112
+ };
113
+ return { queryAll, query: (root, selector) => queryAll(root, selector)[0] ?? null };
114
+ }`;
115
+ /**
116
+ * The engine behind `e2e-roots=`: the roots a semantic query searches, or the
117
+ * elements a CSS selector matches across them. Playwright's own query engines
118
+ * accept a shadow root as their root and never cross a closed boundary
119
+ * downward, so listing every closed root as a root of its own lets them match
120
+ * inside with the same role, name, text, label, and attribute rules they apply
121
+ * outside, and finds each element exactly once. A CSS selector list is matched
122
+ * here rather than by Playwright's CSS engine, which sorts a list's matches in
123
+ * DOM order through `shadowRoot` and so drops the direct children of a closed
124
+ * root; the matches of one root keep their document order and roots follow in
125
+ * the order they were found. A document without the record answers as
126
+ * Playwright alone would, so a locator never fails where it used to work.
127
+ */
128
+ const SEARCH_ROOTS_SELECTOR_ENGINE_SOURCE = `() => {${CLOSED_SHADOW_HELPERS_SOURCE}
129
+ const queryAll = (root, selector) => {
130
+ if (selector === '') return [root, ...closedRootsUnder(root, [])];
131
+ const out = matchesIn(root, selector, []);
132
+ for (const closed of closedRootsUnder(root, [])) matchesIn(closed, selector, out);
133
+ return out;
134
+ };
135
+ return { queryAll, query: (root, selector) => queryAll(root, selector)[0] ?? null };
136
+ }`;
137
+ /** The two closed-root selector engines; `selector-engines.ts` lists them with the label engine for registration. */
138
+ export const CLOSED_SHADOW_SELECTOR_ENGINES = [
139
+ { name: CLOSED_SHADOW_SELECTOR_ENGINE, source: CLOSED_SHADOW_SELECTOR_ENGINE_SOURCE },
140
+ { name: SEARCH_ROOTS_SELECTOR_ENGINE, source: SEARCH_ROOTS_SELECTOR_ENGINE_SOURCE },
141
+ ];
142
+ //# sourceMappingURL=closed-shadow.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"closed-shadow.js","sourceRoot":"","sources":["../src/closed-shadow.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,uBAAuB,CAAC;AAE/D;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,+BAA+B,GAAG;2BACpB,IAAI,CAAC,SAAS,CAAC,uBAAuB,CAAC;;;;;;;;;;;;;;MAc5D,CAAC;AAEP,uGAAuG;AACvG,MAAM,CAAC,MAAM,6BAA6B,GAAG,YAAY,CAAC;AAE1D;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,4BAA4B,GAAG,WAAW,CAAC;AAExD;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,4BAA4B,GAAG;wCACJ,IAAI,CAAC,SAAS,CAAC,uBAAuB,CAAC;;;;;;;;;;;;;;;;;;;;;KAqB1E,CAAC;AAEN;;;;;;;;;GASG;AACH,MAAM,oCAAoC,GAAG,UAAU,4BAA4B;;;;;;;;EAQjF,CAAC;AAEH;;;;;;;;;;;;GAYG;AACH,MAAM,mCAAmC,GAAG,UAAU,4BAA4B;;;;;;;;EAQhF,CAAC;AAEH,qHAAqH;AACrH,MAAM,CAAC,MAAM,8BAA8B,GAAkE;IAC3G,EAAE,IAAI,EAAE,6BAA6B,EAAE,MAAM,EAAE,oCAAoC,EAAE;IACrF,EAAE,IAAI,EAAE,4BAA4B,EAAE,MAAM,EAAE,mCAAmC,EAAE;CACpF,CAAC"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Native dialog routing for one attempt. Handlers are registered by the `web`
3
+ * fixture; the surface owns the router so a dialog that fires before a test
4
+ * ever touches `web` is still accounted for instead of silently dismissed.
5
+ */
6
+ import type { Dialog as PwDialog } from 'playwright';
7
+ import { ErrorLatch } from './support.ts';
8
+ /** A native dialog as a test's handler sees it. */
9
+ export interface Dialog {
10
+ /** The dialog's text. */
11
+ readonly message: string;
12
+ /** Accepts the dialog once. */
13
+ accept(text?: string): Promise<void>;
14
+ /** Dismisses the dialog once. */
15
+ dismiss(): Promise<void>;
16
+ }
17
+ export type DialogHandler = 'accept' | 'dismiss' | ((dialog: Dialog) => void | Promise<void>);
18
+ export declare class DialogRouter {
19
+ private readonly latch;
20
+ private registrations;
21
+ /**
22
+ * `latch` is shared with the surface: a dialog failure and a route-handler
23
+ * failure surface through one check at the next step.
24
+ */
25
+ constructor(latch?: ErrorLatch);
26
+ /**
27
+ * Registers a handler (the newest wins) and returns its idempotent
28
+ * unsubscribe. Registrations are tracked by identity, so registering the
29
+ * same handler twice and unsubscribing once leaves one in place.
30
+ */
31
+ add(handler: DialogHandler): () => void;
32
+ /** Rethrows an error latched by an unhandled or failing dialog handler. */
33
+ throwPending(): void;
34
+ /** Routes one native dialog to the newest registered handler; the attempt end waits for it. */
35
+ dispatch(dialog: PwDialog): Promise<void>;
36
+ private route;
37
+ }
38
+ //# sourceMappingURL=dialogs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dialogs.d.ts","sourceRoot":"","sources":["../src/dialogs.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,MAAM,IAAI,QAAQ,EAAE,MAAM,YAAY,CAAC;AAErD,OAAO,EAAE,UAAU,EAAyB,MAAM,cAAc,CAAC;AAEjE,mDAAmD;AACnD,MAAM,WAAW,MAAM;IACrB,yBAAyB;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,+BAA+B;IAC/B,MAAM,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACrC,iCAAiC;IACjC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1B;AAED,MAAM,MAAM,aAAa,GAAG,QAAQ,GAAG,SAAS,GAAG,CAAC,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;AAM9F,qBAAa,YAAY;IAOX,OAAO,CAAC,QAAQ,CAAC,KAAK;IANlC,OAAO,CAAC,aAAa,CAAsB;IAE3C;;;OAGG;IACH,YAA6B,KAAK,GAAE,UAA6B,EAAI;IAErE;;;;OAIG;IACH,GAAG,CAAC,OAAO,EAAE,aAAa,GAAG,MAAM,IAAI,CAMtC;IAED,2EAA2E;IAC3E,YAAY,IAAI,IAAI,CAEnB;IAED,+FAA+F;IAC/F,QAAQ,CAAC,MAAM,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAIxC;YAEa,KAAK;CAyDpB"}
@@ -0,0 +1,87 @@
1
+ /**
2
+ * Native dialog routing for one attempt. Handlers are registered by the `web`
3
+ * fixture; the surface owns the router so a dialog that fires before a test
4
+ * ever touches `web` is still accounted for instead of silently dismissed.
5
+ */
6
+ import { EngineError } from 'e2e/engine';
7
+ import { ErrorLatch, isClassified, message } from './support.js';
8
+ export class DialogRouter {
9
+ latch;
10
+ registrations = [];
11
+ /**
12
+ * `latch` is shared with the surface: a dialog failure and a route-handler
13
+ * failure surface through one check at the next step.
14
+ */
15
+ constructor(latch = new ErrorLatch()) {
16
+ this.latch = latch;
17
+ }
18
+ /**
19
+ * Registers a handler (the newest wins) and returns its idempotent
20
+ * unsubscribe. Registrations are tracked by identity, so registering the
21
+ * same handler twice and unsubscribing once leaves one in place.
22
+ */
23
+ add(handler) {
24
+ const registration = { handler };
25
+ this.registrations.push(registration);
26
+ return () => {
27
+ this.registrations = this.registrations.filter((entry) => entry !== registration);
28
+ };
29
+ }
30
+ /** Rethrows an error latched by an unhandled or failing dialog handler. */
31
+ throwPending() {
32
+ this.latch.throwPending();
33
+ }
34
+ /** Routes one native dialog to the newest registered handler; the attempt end waits for it. */
35
+ dispatch(dialog) {
36
+ const work = this.route(dialog);
37
+ this.latch.track('dialog', work);
38
+ return work;
39
+ }
40
+ async route(dialog) {
41
+ const handler = this.registrations.at(-1)?.handler;
42
+ if (handler === undefined) {
43
+ this.latch.latch(new EngineError('INVALID_STATE', `unhandled ${dialog.type()} dialog: ${dialog.message()}`, { retryable: false }));
44
+ await dialog.dismiss().catch(() => undefined);
45
+ return;
46
+ }
47
+ let decided = false;
48
+ const publicDialog = {
49
+ message: dialog.message(),
50
+ accept: async (text) => {
51
+ decided = true;
52
+ await dialog.accept(text);
53
+ },
54
+ dismiss: async () => {
55
+ decided = true;
56
+ await dialog.dismiss();
57
+ },
58
+ };
59
+ try {
60
+ if (handler === 'accept')
61
+ await dialog.accept();
62
+ else if (handler === 'dismiss')
63
+ await dialog.dismiss();
64
+ else {
65
+ await handler(publicDialog);
66
+ // A dialog left open blocks the page, and the failure would surface
67
+ // later as an unrelated action timeout. The handler contract is the
68
+ // same as a route handler's: decide, or the step fails naming you.
69
+ if (!decided) {
70
+ await dialog.dismiss().catch(() => undefined);
71
+ this.latch.latch(new EngineError('INVALID_STATE', `dialog handler returned without calling accept or dismiss for ${dialog.type()} dialog: ${dialog.message()}`, { retryable: false }));
72
+ }
73
+ }
74
+ }
75
+ catch (cause) {
76
+ // A failed assertion or a policy refusal keeps its code; only an
77
+ // unclassified throw is the engine's to name.
78
+ this.latch.latch(isClassified(cause)
79
+ ? cause
80
+ : new EngineError('ENGINE_FAILURE', `dialog handler failed: ${message(cause)}`, {
81
+ retryable: false,
82
+ cause,
83
+ }));
84
+ }
85
+ }
86
+ }
87
+ //# sourceMappingURL=dialogs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dialogs.js","sourceRoot":"","sources":["../src/dialogs.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * The Playwright engine for e2e: a browser body built with the
3
+ * public `defineEngine`, validated by the same rules and graded by the same
4
+ * capabilities as any other engine. Core imports nothing from here; this
5
+ * package imports the contract from `e2e/engine` and contributes the `web`
6
+ * fixture the way a device engine contributes `device`.
7
+ */
8
+ import type { BrowserContext, Page } from 'playwright';
9
+ import { type EngineHandle } from 'e2e/engine';
10
+ import { type WebOptions } from './surface.ts';
11
+ /**
12
+ * The live browser objects behind a `web()` handle, for agent-side code
13
+ * that replaces the toolset wholesale (spec chapter 16) and drives the page with
14
+ * its own Playwright tooling. Both accessors read the current attempt: the
15
+ * context exists from `startAttempt`, the page from the first `app.open()` or
16
+ * `web.goto()`, and either throws `INVALID_STATE` before that. The harness
17
+ * remains the notary for what it witnesses; a caller here acts out of band.
18
+ */
19
+ export interface PlaywrightLiveSurface {
20
+ readonly page: () => Page;
21
+ readonly context: () => BrowserContext;
22
+ }
23
+ /** The live surface of a handle this module created, or undefined for any other engine. */
24
+ export declare function surfaceOf(engine: EngineHandle): PlaywrightLiveSurface | undefined;
25
+ export declare function web(options?: WebOptions): EngineHandle;
26
+ //# sourceMappingURL=engine.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AACvD,OAAO,EAOL,KAAK,YAAY,EAClB,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAqB,KAAK,UAAU,EAAE,MAAM,cAAc,CAAC;AAMlE;;;;;;;GAOG;AACH,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,IAAI,EAAE,MAAM,IAAI,CAAC;IAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,cAAc,CAAC;CACxC;AAED,2FAA2F;AAC3F,wBAAgB,SAAS,CAAC,MAAM,EAAE,YAAY,GAAG,qBAAqB,GAAG,SAAS,CAIjF;AAED,wBAAgB,GAAG,CAAC,OAAO,GAAE,UAAe,GAAG,YAAY,CAyF1D"}