@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,280 @@
1
+ /**
2
+ * Semantic tree capture for one observation: the in-page walk, nested-document
3
+ * stitching, and element-handle bookkeeping the surface keeps per generation.
4
+ */
5
+ import { EngineError, sameSite, OBSERVED_NAME_LIMIT, OBSERVED_TEXT_LIMIT, } from 'e2e/engine';
6
+ import { readDocumentSemanticsFunction, SECURE_FIELD_SELECTOR, } from './read-node.js';
7
+ import { CaptureScope } from './capture-scope.js';
8
+ /**
9
+ * The id of every observation's root: the main document's element under one
10
+ * id the engine mints, stable across observations and navigations, so the
11
+ * harness can address the viewport (`perform(root, swipe)`) from an earlier
12
+ * observation. Outside the `n<number>` space stamped ids and located ids share.
13
+ */
14
+ export const ROOT_NODE_ID = 'root';
15
+ /** Nested iframe capture depth; deeper frames stay boundary nodes. */
16
+ const MAX_FRAME_DEPTH = 4;
17
+ /** Budget for capturing the main document, still capped by the observation deadline. */
18
+ const DOCUMENT_CAPTURE_TIMEOUT_MS = 15_000;
19
+ /**
20
+ * Budget for capturing one child document. A stalled frame (ads, trackers)
21
+ * must cost an observation a moment, not the context default timeout.
22
+ */
23
+ const FRAME_CAPTURE_TIMEOUT_MS = 3_000;
24
+ /**
25
+ * Captures one document's semantic tree, then descends into each observed
26
+ * iframe boundary node via its content frame and stitches the child
27
+ * document under it. Frame capture is best-effort: a detached or unloaded
28
+ * frame leaves its boundary node childless rather than failing the
29
+ * observation. The node budget is shared across all documents.
30
+ */
31
+ export async function captureDocument(deps, host, options) {
32
+ const { framePath, budget, signal } = options;
33
+ const cap = framePath.length === 0 ? DOCUMENT_CAPTURE_TIMEOUT_MS : FRAME_CAPTURE_TIMEOUT_MS;
34
+ const deadline = Math.min(options.deadline, Date.now() + cap);
35
+ const scope = new CaptureScope(deadline, signal);
36
+ // A child document's handles are published only once it captured whole; a
37
+ // frame that fails midway leaves no reachable ids behind.
38
+ const staged = [];
39
+ const stage = (id, element) => {
40
+ if (scope.own(element))
41
+ staged.push([id, element]);
42
+ };
43
+ return scope.run(async () => {
44
+ const captured = await captureInto(deps, scope, stage, host, framePath, budget, signal);
45
+ scope.check();
46
+ for (const [id, element] of staged) {
47
+ deps.commit(id, element);
48
+ scope.release(element);
49
+ }
50
+ return captured;
51
+ });
52
+ }
53
+ /** Reads one document and stages its child documents under the same capture owner. */
54
+ async function captureInto(deps, scope, stage, host, framePath, budget, signal) {
55
+ const captured = await scope.read(() => host.evaluateHandle(readDocumentSemanticsFunction, {
56
+ testIdAttribute: deps.testIdAttribute,
57
+ secureFieldSelector: SECURE_FIELD_SELECTOR,
58
+ mode: {
59
+ kind: 'tree',
60
+ maxNodes: budget,
61
+ idSeed: deps.reserveIds(budget),
62
+ nameLimit: OBSERVED_NAME_LIMIT,
63
+ textLimit: OBSERVED_TEXT_LIMIT,
64
+ },
65
+ }), (handle) => scope.own(handle));
66
+ // Property handles would keep earlier captures alive after their parent is disposed.
67
+ // The metadata is JSON data. Sending one string avoids Playwright recursively
68
+ // encoding every field of every node across the protocol.
69
+ const metadata = await scope.read(() => captured.evaluate((observation) => {
70
+ const data = { nodes: observation.nodes, ids: observation.ids, truncated: observation.truncated };
71
+ // Application serializers may transform valid metadata as well as throw.
72
+ // Keep Playwright's structured transfer when the page changes JSON behavior.
73
+ try {
74
+ if (typeof JSON.stringify === 'function' &&
75
+ /^function stringify\(\)\s*\{\s*\[native code\]\s*\}$/.test(Function.prototype.toString.call(JSON.stringify)) &&
76
+ !('toJSON' in Object.prototype) &&
77
+ !('toJSON' in Array.prototype))
78
+ return JSON.stringify(data);
79
+ }
80
+ catch {
81
+ // A page can also replace the intrinsics used to detect its serializer.
82
+ }
83
+ return data;
84
+ }));
85
+ const decoded = typeof metadata === 'string'
86
+ ? JSON.parse(metadata)
87
+ : metadata;
88
+ const { nodes, ids, truncated: walkTruncated } = decoded;
89
+ if (!Array.isArray(ids) || ids.length !== nodes.length) {
90
+ throw new EngineError('ENGINE_FAILURE', 'observation ids do not align with its nodes', {
91
+ retryable: false,
92
+ });
93
+ }
94
+ // The main document's element is the observation root, under the minted
95
+ // id rather than the one stamped on it: a new document stamps a new
96
+ // number, the root's id must not change with it.
97
+ if (framePath.length === 0 && ids.length > 0)
98
+ ids[0] = ROOT_NODE_ID;
99
+ const elementsHandle = await scope.read(() => captured.getProperty('elements'), (handle) => scope.own(handle));
100
+ const elements = await collectElementHandles(scope, elementsHandle, nodes.length);
101
+ elements.forEach((element, index) => stage(ids[index], element));
102
+ let nodeCount = nodes.length;
103
+ let truncated = walkTruncated === true;
104
+ const frameChildren = new Map();
105
+ for (let index = 0; index < nodes.length; index += 1) {
106
+ const selector = nodes[index].frameSelector;
107
+ if (selector === undefined)
108
+ continue;
109
+ // A frame with no document (detached, never loaded) shows nothing, so
110
+ // nothing is missing from the tree.
111
+ const frame = await scope.read(() => elements[index].contentFrame().catch(() => null));
112
+ if (frame === null)
113
+ continue;
114
+ // Only frames on the app's site enter observations. Third-party
115
+ // frames (ads, trackers, embeds) are not the agent's to read or act
116
+ // on - and a stalled ad frame must not tax the capture. They stay
117
+ // boundary nodes by design, so leaving them out is not truncation.
118
+ if (!isOnSiteFrame(frame.url(), deps.site))
119
+ continue;
120
+ // From here the frame's document is content the model should see: any
121
+ // reason it is not read (depth, budget, deadline, a failed or timed-out
122
+ // capture) leaves the tree incomplete, and the snapshot must say so.
123
+ if (framePath.length >= MAX_FRAME_DEPTH) {
124
+ truncated = true;
125
+ continue;
126
+ }
127
+ const remaining = budget - nodeCount;
128
+ if (remaining <= 0 || Date.now() >= scope.deadline) {
129
+ truncated = true;
130
+ continue;
131
+ }
132
+ const child = await scope.read(() => captureDocument({ ...deps, commit: stage }, frame, {
133
+ framePath: [...framePath, selector],
134
+ budget: remaining,
135
+ deadline: scope.deadline,
136
+ signal,
137
+ }).catch(() => undefined));
138
+ if (child === undefined) {
139
+ truncated = true;
140
+ continue;
141
+ }
142
+ frameChildren.set(index, child.tree);
143
+ nodeCount += child.nodeCount;
144
+ truncated ||= child.truncated;
145
+ }
146
+ return { tree: assembleTree(nodes, ids, framePath, frameChildren), nodeCount, truncated };
147
+ }
148
+ /**
149
+ * True when a frame document is on the app's site.
150
+ *
151
+ * `about:blank` and `srcdoc` documents inherit their parent's origin, so they
152
+ * are the app's own content (consent managers, editors) and always allowed; the
153
+ * parent frame was already admitted to be captured at all.
154
+ *
155
+ * A `data:` document is *not* admitted, even though its bytes are written by the
156
+ * page that embeds it. It has an opaque origin rather than an inherited one, and
157
+ * navigation denies the scheme by name alongside `file:` and `javascript:`.
158
+ */
159
+ function isOnSiteFrame(url, site) {
160
+ if (url === '' || url === 'about:blank' || url === 'about:srcdoc')
161
+ return true;
162
+ if (site === undefined)
163
+ return false;
164
+ const protocol = URL.canParse(url) ? new URL(url).protocol : undefined;
165
+ return protocol !== 'data:' && sameSite(url, site);
166
+ }
167
+ /**
168
+ * Reads one element handle per observed node from the in-page element array.
169
+ * `asElement` types handles as `ElementHandle<Node>`, but the observation walk
170
+ * records `Element` nodes only, so the narrowing is safe by construction.
171
+ */
172
+ async function collectElementHandles(scope, elementsHandle, count) {
173
+ const properties = await scope.read(() => elementsHandle.getProperties(), (handles) => {
174
+ for (const handle of handles.values())
175
+ scope.own(handle);
176
+ });
177
+ const elements = [];
178
+ for (let index = 0; index < count; index += 1) {
179
+ const property = properties.get(String(index));
180
+ const element = (property?.asElement() ?? null);
181
+ if (element === null) {
182
+ // The in-page array outlived its document (a navigation committed while
183
+ // the handles were being read back). The capture is repeatable.
184
+ throw new EngineError('NODE_STALE', `observation node ${index} lost its element`, {
185
+ retryable: true,
186
+ });
187
+ }
188
+ elements.push(element);
189
+ }
190
+ return elements;
191
+ }
192
+ /**
193
+ * Rebuilds the observation tree from the depth-first node list. Descendants
194
+ * always follow their parent, so children are complete before a parent is
195
+ * built. Captured child documents attach under their iframe boundary nodes.
196
+ */
197
+ function assembleTree(nodes, ids, framePath, frameChildren) {
198
+ if (nodes.length === 0 || ids.length === 0) {
199
+ // An empty document is what a navigation in flight looks like; a real page
200
+ // always has nodes, so the capture is worth repeating.
201
+ throw new EngineError('NODE_STALE', 'observation produced no nodes', { retryable: true });
202
+ }
203
+ const childLists = nodes.map(() => []);
204
+ const built = [];
205
+ for (let index = nodes.length - 1; index >= 0; index -= 1) {
206
+ const raw = nodes[index];
207
+ const embedded = frameChildren.get(index);
208
+ // A child document measured its nodes against its own viewport. Shifted by
209
+ // the boundary element's box and clipped to it, every box in the tree is
210
+ // in the top-level viewport's CSS pixels, the space the screenshot and a
211
+ // point tap share, and a child that overflows its frame cannot claim a
212
+ // point over the page around it. The shift is the frame's border box; a
213
+ // bordered iframe is off by its border width, which is within a tap
214
+ // target. A frame under a CSS transform is not unwound: its boxes are
215
+ // where the untransformed frame would put them.
216
+ if (embedded !== undefined)
217
+ childLists[index].unshift(placeInFrame(embedded, raw.rect));
218
+ const node = toSemanticNode({ id: ids[index], revision: '' }, raw, childLists[index], framePath);
219
+ built[index] = node;
220
+ if (raw.parent >= 0)
221
+ childLists[raw.parent].unshift(node);
222
+ }
223
+ return built[0];
224
+ }
225
+ /** The tree with every box translated into the frame's space and clipped to its box; a box left empty by the clip is dropped. */
226
+ function placeInFrame(node, frame) {
227
+ const { rect, ...rest } = node;
228
+ const placed = rect === undefined ? undefined : clipRect(offsetRect(rect, frame), frame);
229
+ return {
230
+ ...rest,
231
+ ...(placed === undefined ? {} : { rect: placed }),
232
+ ...(node.children === undefined ? {} : { children: node.children.map((child) => placeInFrame(child, frame)) }),
233
+ };
234
+ }
235
+ function offsetRect(rect, by) {
236
+ return { x: rect.x + by.x, y: rect.y + by.y, width: rect.width, height: rect.height };
237
+ }
238
+ function clipRect(rect, bounds) {
239
+ const x = Math.max(rect.x, bounds.x);
240
+ const y = Math.max(rect.y, bounds.y);
241
+ const width = Math.min(rect.x + rect.width, bounds.x + bounds.width) - x;
242
+ const height = Math.min(rect.y + rect.height, bounds.y + bounds.height) - y;
243
+ return width > 0 && height > 0 ? { x, y, width, height } : undefined;
244
+ }
245
+ export function toSemanticNode(ref, raw, children = [], framePath = []) {
246
+ const states = {};
247
+ if (raw.states.checked !== null)
248
+ states['checked'] = raw.states.checked;
249
+ if (raw.states.disabled)
250
+ states['disabled'] = true;
251
+ if (raw.states.selected !== null)
252
+ states['selected'] = raw.states.selected;
253
+ if (raw.states.expanded !== null)
254
+ states['expanded'] = raw.states.expanded;
255
+ if (raw.states.pressed !== null)
256
+ states['pressed'] = raw.states.pressed;
257
+ if (raw.states.focused)
258
+ states['focused'] = true;
259
+ if (raw.states.hidden)
260
+ states['hidden'] = true;
261
+ if (raw.states.secure)
262
+ states['secure'] = true;
263
+ return {
264
+ ref,
265
+ ...(raw.role !== null ? { role: raw.role } : {}),
266
+ ...(raw.name !== null ? { name: raw.name } : {}),
267
+ ...(raw.text !== null ? { text: raw.text } : {}),
268
+ ...(raw.value !== null ? { value: raw.value } : {}),
269
+ ...(raw.selection !== null ? { selection: raw.selection } : {}),
270
+ ...(raw.testId !== null ? { testId: raw.testId } : {}),
271
+ inputPurpose: raw.inputPurpose,
272
+ states,
273
+ ...(raw.level !== null ? { level: raw.level } : {}),
274
+ attributes: raw.attributes,
275
+ rect: raw.rect,
276
+ ...(framePath.length > 0 ? { framePath } : {}),
277
+ ...(children.length > 0 ? { children } : {}),
278
+ };
279
+ }
280
+ //# sourceMappingURL=observation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"observation.js","sourceRoot":"","sources":["../src/observation.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EACL,WAAW,EACX,QAAQ,EACR,mBAAmB,EACnB,mBAAmB,GAGpB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,6BAA6B,EAC7B,qBAAqB,GAGtB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD;;;;;GAKG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,MAAM,CAAC;AAEnC,sEAAsE;AACtE,MAAM,eAAe,GAAG,CAAC,CAAC;AAE1B,wFAAwF;AACxF,MAAM,2BAA2B,GAAG,MAAM,CAAC;AAE3C;;;GAGG;AACH,MAAM,wBAAwB,GAAG,KAAK,CAAC;AAgDvC;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,IAAiB,EACjB,IAAkB,EAClB,OAAuB;IAEvB,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;IAC9C,MAAM,GAAG,GAAG,SAAS,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,2BAA2B,CAAC,CAAC,CAAC,wBAAwB,CAAC;IAC5F,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,QAAQ,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,GAAG,CAAC,CAAC;IAC9D,MAAM,KAAK,GAAG,IAAI,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IACjD,0EAA0E;IAC1E,0DAA0D;IAC1D,MAAM,MAAM,GAAgB,EAAE,CAAC;IAC/B,MAAM,KAAK,GAAG,CAAC,EAAU,EAAE,OAA+B,EAAQ,EAAE;QAClE,IAAI,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC;YAAE,MAAM,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC,CAAC;IACrD,CAAC,CAAC;IACF,OAAO,KAAK,CAAC,GAAG,CAAC,KAAK,IAAI,EAAE;QAC1B,MAAM,QAAQ,GAAG,MAAM,WAAW,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;QACxF,KAAK,CAAC,KAAK,EAAE,CAAC;QACd,KAAK,MAAM,CAAC,EAAE,EAAE,OAAO,CAAC,IAAI,MAAM,EAAE,CAAC;YACnC,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;YACzB,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QACzB,CAAC;QACD,OAAO,QAAQ,CAAC;IAClB,CAAC,CAAC,CAAC;AACL,CAAC;AAED,sFAAsF;AACtF,KAAK,UAAU,WAAW,CACxB,IAAiB,EACjB,KAAmB,EACnB,KAA4D,EAC5D,IAAkB,EAClB,SAA4B,EAC5B,MAAc,EACd,MAAmB;IAEnB,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,cAAc,CAAC,6BAA6B,EAAE;QACzF,eAAe,EAAE,IAAI,CAAC,eAAe;QACrC,mBAAmB,EAAE,qBAAqB;QAC1C,IAAI,EAAE;YACJ,IAAI,EAAE,MAAe;YACrB,QAAQ,EAAE,MAAM;YAChB,MAAM,EAAE,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC;YAC/B,SAAS,EAAE,mBAAmB;YAC9B,SAAS,EAAE,mBAAmB;SAC/B;KACF,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;IACnC,qFAAqF;IACrF,8EAA8E;IAC9E,0DAA0D;IAC1D,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,WAAW,EAAE,EAAE;QACxE,MAAM,IAAI,GAAG,EAAE,KAAK,EAAE,WAAW,CAAC,KAAK,EAAE,GAAG,EAAE,WAAW,CAAC,GAAG,EAAE,SAAS,EAAE,WAAW,CAAC,SAAS,EAAE,CAAC;QAClG,yEAAyE;QACzE,6EAA6E;QAC7E,IAAI,CAAC;YACH,IACE,OAAO,IAAI,CAAC,SAAS,KAAK,UAAU;gBACpC,sDAAsD,CAAC,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;gBAC7G,CAAC,CAAC,QAAQ,IAAI,MAAM,CAAC,SAAS,CAAC;gBAC/B,CAAC,CAAC,QAAQ,IAAI,KAAK,CAAC,SAAS,CAAC;gBAC9B,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;QAChC,CAAC;QAAC,MAAM,CAAC;YACP,wEAAwE;QAC1E,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC,CAAC,CAAC,CAAC;IACJ,MAAM,OAAO,GAAG,OAAO,QAAQ,KAAK,QAAQ;QAC1C,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAuE;QAC5F,CAAC,CAAC,QAAQ,CAAC;IACb,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,SAAS,EAAE,aAAa,EAAE,GAAG,OAAO,CAAC;IACzD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,MAAM,KAAK,KAAK,CAAC,MAAM,EAAE,CAAC;QACvD,MAAM,IAAI,WAAW,CAAC,gBAAgB,EAAE,6CAA6C,EAAE;YACrF,SAAS,EAAE,KAAK;SACjB,CAAC,CAAC;IACL,CAAC;IACD,wEAAwE;IACxE,oEAAoE;IACpE,iDAAiD;IACjD,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC;QAAE,GAAG,CAAC,CAAC,CAAC,GAAG,YAAY,CAAC;IACpE,MAAM,cAAc,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,WAAW,CAAC,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;IAC/G,MAAM,QAAQ,GAAG,MAAM,qBAAqB,CAAC,KAAK,EAAE,cAAc,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAClF,QAAQ,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAW,EAAE,OAAO,CAAC,CAAC,CAAC;IAC3E,IAAI,SAAS,GAAG,KAAK,CAAC,MAAM,CAAC;IAC7B,IAAI,SAAS,GAAG,aAAa,KAAK,IAAI,CAAC;IACvC,MAAM,aAAa,GAAG,IAAI,GAAG,EAAwB,CAAC;IACtD,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,KAAK,CAAC,MAAM,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;QACrD,MAAM,QAAQ,GAAG,KAAK,CAAC,KAAK,CAAE,CAAC,aAAa,CAAC;QAC7C,IAAI,QAAQ,KAAK,SAAS;YAAE,SAAS;QACrC,sEAAsE;QACtE,oCAAoC;QACpC,MAAM,KAAK,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAE,CAAC,YAAY,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;QACxF,IAAI,KAAK,KAAK,IAAI;YAAE,SAAS;QAC7B,gEAAgE;QAChE,oEAAoE;QACpE,kEAAkE;QAClE,mEAAmE;QACnE,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,GAAG,EAAE,EAAE,IAAI,CAAC,IAAI,CAAC;YAAE,SAAS;QACrD,sEAAsE;QACtE,wEAAwE;QACxE,qEAAqE;QACrE,IAAI,SAAS,CAAC,MAAM,IAAI,eAAe,EAAE,CAAC;YACxC,SAAS,GAAG,IAAI,CAAC;YACjB,SAAS;QACX,CAAC;QACD,MAAM,SAAS,GAAG,MAAM,GAAG,SAAS,CAAC;QACrC,IAAI,SAAS,IAAI,CAAC,IAAI,IAAI,CAAC,GAAG,EAAE,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;YACnD,SAAS,GAAG,IAAI,CAAC;YACjB,SAAS;QACX,CAAC;QACD,MAAM,KAAK,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,eAAe,CAAC,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE;YACtF,SAAS,EAAE,CAAC,GAAG,SAAS,EAAE,QAAQ,CAAC;YACnC,MAAM,EAAE,SAAS;YACjB,QAAQ,EAAE,KAAK,CAAC,QAAQ;YACxB,MAAM;SACP,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC;QAC3B,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxB,SAAS,GAAG,IAAI,CAAC;YACjB,SAAS;QACX,CAAC;QACD,aAAa,CAAC,GAAG,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QACrC,SAAS,IAAI,KAAK,CAAC,SAAS,CAAC;QAC7B,SAAS,KAAK,KAAK,CAAC,SAAS,CAAC;IAChC,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,YAAY,CAAC,KAAK,EAAE,GAAG,EAAE,SAAS,EAAE,aAAa,CAAC,EAAE,SAAS,EAAE,SAAS,EAAE,CAAC;AAC5F,CAAC;AAED;;;;;;;;;;GAUG;AACH,SAAS,aAAa,CAAC,GAAW,EAAE,IAAwB;IAC1D,IAAI,GAAG,KAAK,EAAE,IAAI,GAAG,KAAK,aAAa,IAAI,GAAG,KAAK,cAAc;QAAE,OAAO,IAAI,CAAC;IAC/E,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC;IACrC,MAAM,QAAQ,GAAG,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC;IACvE,OAAO,QAAQ,KAAK,OAAO,IAAI,QAAQ,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;AACrD,CAAC;AAED;;;;GAIG;AACH,KAAK,UAAU,qBAAqB,CAClC,KAAmB,EACnB,cAAwB,EACxB,KAAa;IAEb,MAAM,UAAU,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,cAAc,CAAC,aAAa,EAAE,EAAE,CAAC,OAAO,EAAE,EAAE;QACpF,KAAK,MAAM,MAAM,IAAI,OAAO,CAAC,MAAM,EAAE;YAAE,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAC3D,CAAC,CAAC,CAAC;IACH,MAAM,QAAQ,GAA6B,EAAE,CAAC;IAC9C,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,KAAK,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;QAC9C,MAAM,QAAQ,GAAG,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;QAC/C,MAAM,OAAO,GAAG,CAAC,QAAQ,EAAE,SAAS,EAAE,IAAI,IAAI,CAAkC,CAAC;QACjF,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;YACrB,wEAAwE;YACxE,gEAAgE;YAChE,MAAM,IAAI,WAAW,CAAC,YAAY,EAAE,oBAAoB,KAAK,mBAAmB,EAAE;gBAChF,SAAS,EAAE,IAAI;aAChB,CAAC,CAAC;QACL,CAAC;QACD,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACzB,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;;;GAIG;AACH,SAAS,YAAY,CACnB,KAAiC,EACjC,GAAsB,EACtB,SAA4B,EAC5B,aAAgD;IAEhD,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC3C,2EAA2E;QAC3E,uDAAuD;QACvD,MAAM,IAAI,WAAW,CAAC,YAAY,EAAE,+BAA+B,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC5F,CAAC;IACD,MAAM,UAAU,GAAqB,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;IACzD,MAAM,KAAK,GAAmB,EAAE,CAAC;IACjC,KAAK,IAAI,KAAK,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,KAAK,IAAI,CAAC,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;QAC1D,MAAM,GAAG,GAAG,KAAK,CAAC,KAAK,CAAE,CAAC;QAC1B,MAAM,QAAQ,GAAG,aAAa,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAC1C,2EAA2E;QAC3E,yEAAyE;QACzE,yEAAyE;QACzE,uEAAuE;QACvE,wEAAwE;QACxE,oEAAoE;QACpE,sEAAsE;QACtE,gDAAgD;QAChD,IAAI,QAAQ,KAAK,SAAS;YAAE,UAAU,CAAC,KAAK,CAAE,CAAC,OAAO,CAAC,YAAY,CAAC,QAAQ,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC;QACzF,MAAM,IAAI,GAAG,cAAc,CAAC,EAAE,EAAE,EAAE,GAAG,CAAC,KAAK,CAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,UAAU,CAAC,KAAK,CAAE,EAAE,SAAS,CAAC,CAAC;QACnG,KAAK,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC;QACpB,IAAI,GAAG,CAAC,MAAM,IAAI,CAAC;YAAE,UAAU,CAAC,GAAG,CAAC,MAAM,CAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7D,CAAC;IACD,OAAO,KAAK,CAAC,CAAC,CAAE,CAAC;AACnB,CAAC;AAID,iIAAiI;AACjI,SAAS,YAAY,CAAC,IAAkB,EAAE,KAAW;IACnD,MAAM,EAAE,IAAI,EAAE,GAAG,IAAI,EAAE,GAAG,IAAI,CAAC;IAC/B,MAAM,MAAM,GAAG,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,KAAK,CAAC,CAAC;IACzF,OAAO;QACL,GAAG,IAAI;QACP,GAAG,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;QACjD,GAAG,CAAC,IAAI,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,YAAY,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC;KAC/G,CAAC;AACJ,CAAC;AAED,SAAS,UAAU,CAAC,IAAU,EAAE,EAAQ;IACtC,OAAO,EAAE,CAAC,EAAE,IAAI,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC;AACxF,CAAC;AAED,SAAS,QAAQ,CAAC,IAAU,EAAE,MAAY;IACxC,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC;IACrC,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC;IACrC,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACzE,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAC5E,OAAO,KAAK,GAAG,CAAC,IAAI,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;AACvE,CAAC;AAED,MAAM,UAAU,cAAc,CAC5B,GAAY,EACZ,GAAgB,EAChB,QAAQ,GAA4B,EAAE,EACtC,SAAS,GAAsB,EAAE;IAEjC,MAAM,MAAM,GAA4B,EAAE,CAAC;IAC3C,IAAI,GAAG,CAAC,MAAM,CAAC,OAAO,KAAK,IAAI;QAAE,MAAM,CAAC,SAAS,CAAC,GAAG,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC;IACxE,IAAI,GAAG,CAAC,MAAM,CAAC,QAAQ;QAAE,MAAM,CAAC,UAAU,CAAC,GAAG,IAAI,CAAC;IACnD,IAAI,GAAG,CAAC,MAAM,CAAC,QAAQ,KAAK,IAAI;QAAE,MAAM,CAAC,UAAU,CAAC,GAAG,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC;IAC3E,IAAI,GAAG,CAAC,MAAM,CAAC,QAAQ,KAAK,IAAI;QAAE,MAAM,CAAC,UAAU,CAAC,GAAG,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC;IAC3E,IAAI,GAAG,CAAC,MAAM,CAAC,OAAO,KAAK,IAAI;QAAE,MAAM,CAAC,SAAS,CAAC,GAAG,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC;IACxE,IAAI,GAAG,CAAC,MAAM,CAAC,OAAO;QAAE,MAAM,CAAC,SAAS,CAAC,GAAG,IAAI,CAAC;IACjD,IAAI,GAAG,CAAC,MAAM,CAAC,MAAM;QAAE,MAAM,CAAC,QAAQ,CAAC,GAAG,IAAI,CAAC;IAC/C,IAAI,GAAG,CAAC,MAAM,CAAC,MAAM;QAAE,MAAM,CAAC,QAAQ,CAAC,GAAG,IAAI,CAAC;IAC/C,OAAO;QACL,GAAG;QACH,GAAG,CAAC,GAAG,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAChD,GAAG,CAAC,GAAG,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAChD,GAAG,CAAC,GAAG,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAChD,GAAG,CAAC,GAAG,CAAC,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACnD,GAAG,CAAC,GAAG,CAAC,SAAS,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,GAAG,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC/D,GAAG,CAAC,GAAG,CAAC,MAAM,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACtD,YAAY,EAAE,GAAG,CAAC,YAAY;QAC9B,MAAM;QACN,GAAG,CAAC,GAAG,CAAC,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACnD,UAAU,EAAE,GAAG,CAAC,UAAU;QAC1B,IAAI,EAAE,GAAG,CAAC,IAAI;QACd,GAAG,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC9C,GAAG,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC7C,CAAC;AACJ,CAAC"}
@@ -0,0 +1,51 @@
1
+ /** Masked pixel capture for an observation. */
2
+ import type { Locator, Page } from 'playwright';
3
+ import type { ObservationPixels, OperationContext, ViewportSize } from 'e2e/engine';
4
+ /**
5
+ * Two mask locators per frame Playwright can reach, covering every secure
6
+ * field: one through light DOM and open shadow roots, one through the closed
7
+ * roots the reader also walks. Sweeping every frame rather than only the
8
+ * frames an observation walked makes the masked set a superset of the observed
9
+ * secure set - the direction that is safe. Shared by the observation pixels
10
+ * and the artifact screenshot so both are redacted at the source by the same
11
+ * rule.
12
+ */
13
+ export declare function secureFieldMasks(page: Page): Locator[];
14
+ /** Screenshot options that apply the secure-field masks; empty when there is no frame to mask. */
15
+ export declare function maskOptions(masks: readonly Locator[]): Partial<{
16
+ mask: Locator[];
17
+ maskColor: string;
18
+ }>;
19
+ export interface PixelCapture {
20
+ readonly pixels: ObservationPixels;
21
+ readonly maskedRegionCount: number;
22
+ /** Every secure-field probe completed, independently of semantic capture. */
23
+ readonly maskingProven: boolean;
24
+ }
25
+ /**
26
+ * Captures masked viewport pixels.
27
+ *
28
+ * Secure fields are covered before the image leaves the engine and the covered
29
+ * regions are counted, so the runner can prove the image is at least as redacted
30
+ * as the tree (the runner's clearance check requires the masked set to be a
31
+ * superset of the observed secure set, which `secureFieldMasks` guarantees).
32
+ *
33
+ * `scale: 'css'` keeps the image in the CSS pixel space every observed node rect
34
+ * already uses, which is what lets the runner hit-test a point against the same
35
+ * tree.
36
+ */
37
+ export declare function capturePixels(page: Page, operation: OperationContext, viewport: ViewportSize): Promise<PixelCapture>;
38
+ /**
39
+ * Reads pixel dimensions out of PNG bytes.
40
+ *
41
+ * The dimensions reported with a screenshot must be the dimensions of the bytes
42
+ * the model actually receives: every coordinate it returns is relative to them,
43
+ * so a viewport value that disagrees with the capture displaces every point
44
+ * silently. PNG puts IHDR first, so width and height are at fixed offsets and no
45
+ * decoder is needed.
46
+ */
47
+ export declare function readPngSize(data: Uint8Array): {
48
+ width: number;
49
+ height: number;
50
+ } | null;
51
+ //# sourceMappingURL=observe.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"observe.d.ts","sourceRoot":"","sources":["../src/observe.ts"],"names":[],"mappings":"AAAA,+CAA+C;AAE/C,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,KAAK,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAgBpF;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,EAAE,CAOtD;AAED,kGAAkG;AAClG,wBAAgB,WAAW,CAAC,KAAK,EAAE,SAAS,OAAO,EAAE,GAAG,OAAO,CAAC;IAAE,IAAI,EAAE,OAAO,EAAE,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC,CAEtG;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,MAAM,EAAE,iBAAiB,CAAC;IACnC,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;IACnC,6EAA6E;IAC7E,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;CACjC;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,aAAa,CACjC,IAAI,EAAE,IAAI,EACV,SAAS,EAAE,gBAAgB,EAC3B,QAAQ,EAAE,YAAY,GACrB,OAAO,CAAC,YAAY,CAAC,CAkCvB;AAED;;;;;;;;GAQG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,UAAU,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAStF"}
@@ -0,0 +1,105 @@
1
+ /** Masked pixel capture for an observation. */
2
+ import { CaptureScope } from './capture-scope.js';
3
+ import { CLOSED_SHADOW_SELECTOR_ENGINE } from './closed-shadow.js';
4
+ import { SECURE_FIELD_SELECTOR } from './read-node.js';
5
+ /**
6
+ * Budget for one masked screenshot, still capped by the operation timeout. A
7
+ * heavy page that cannot produce a frame promptly must cost the observation a
8
+ * moment, not the whole step: the caller degrades to tree-only input rather
9
+ * than failing.
10
+ */
11
+ const PIXEL_CAPTURE_TIMEOUT_MS = 10_000;
12
+ /** Opaque fill covering every masked region. */
13
+ const MASK_COLOR = '#000000';
14
+ /**
15
+ * Two mask locators per frame Playwright can reach, covering every secure
16
+ * field: one through light DOM and open shadow roots, one through the closed
17
+ * roots the reader also walks. Sweeping every frame rather than only the
18
+ * frames an observation walked makes the masked set a superset of the observed
19
+ * secure set - the direction that is safe. Shared by the observation pixels
20
+ * and the artifact screenshot so both are redacted at the source by the same
21
+ * rule.
22
+ */
23
+ export function secureFieldMasks(page) {
24
+ return page
25
+ .frames()
26
+ .flatMap((frame) => [
27
+ frame.locator(SECURE_FIELD_SELECTOR),
28
+ frame.locator(`${CLOSED_SHADOW_SELECTOR_ENGINE}=${SECURE_FIELD_SELECTOR}`),
29
+ ]);
30
+ }
31
+ /** Screenshot options that apply the secure-field masks; empty when there is no frame to mask. */
32
+ export function maskOptions(masks) {
33
+ return masks.length === 0 ? {} : { mask: [...masks], maskColor: MASK_COLOR };
34
+ }
35
+ /**
36
+ * Captures masked viewport pixels.
37
+ *
38
+ * Secure fields are covered before the image leaves the engine and the covered
39
+ * regions are counted, so the runner can prove the image is at least as redacted
40
+ * as the tree (the runner's clearance check requires the masked set to be a
41
+ * superset of the observed secure set, which `secureFieldMasks` guarantees).
42
+ *
43
+ * `scale: 'css'` keeps the image in the CSS pixel space every observed node rect
44
+ * already uses, which is what lets the runner hit-test a point against the same
45
+ * tree.
46
+ */
47
+ export async function capturePixels(page, operation, viewport) {
48
+ const deadline = Date.now() + operation.timeoutMs;
49
+ const scope = new CaptureScope(deadline, operation.signal);
50
+ return scope.run(async () => {
51
+ const masks = secureFieldMasks(page);
52
+ // A frame that detaches mid-sweep contributes nothing to the count, which can
53
+ // only push the observation toward withholding the image.
54
+ const counts = await scope.read(() => Promise.allSettled(masks.map((mask) => mask.count())));
55
+ const image = await scope.read(() => page.screenshot({
56
+ type: 'png',
57
+ scale: 'css',
58
+ animations: 'disabled',
59
+ caret: 'hide',
60
+ timeout: Math.max(1, Math.min(deadline - Date.now(), PIXEL_CAPTURE_TIMEOUT_MS)),
61
+ ...maskOptions(masks),
62
+ }));
63
+ const data = new Uint8Array(image);
64
+ // The bytes are the authority on their own size. `scale: 'css'` is asked for
65
+ // precisely so one image pixel is one CSS pixel, but the ratio is measured
66
+ // rather than assumed: if a capture ever comes back at device scale, reporting
67
+ // the viewport instead would displace every coordinate the model reads off it.
68
+ const size = readPngSize(data) ?? { width: viewport.width, height: viewport.height };
69
+ return {
70
+ pixels: {
71
+ data,
72
+ mediaType: 'image/png',
73
+ width: size.width,
74
+ height: size.height,
75
+ scale: viewport.width > 0 ? size.width / viewport.width : 1,
76
+ },
77
+ maskedRegionCount: counts.reduce((total, count) => total + (count.status === 'fulfilled' ? count.value : 0), 0),
78
+ maskingProven: masks.length > 0 && counts.every((count) => count.status === 'fulfilled'),
79
+ };
80
+ });
81
+ }
82
+ /**
83
+ * Reads pixel dimensions out of PNG bytes.
84
+ *
85
+ * The dimensions reported with a screenshot must be the dimensions of the bytes
86
+ * the model actually receives: every coordinate it returns is relative to them,
87
+ * so a viewport value that disagrees with the capture displaces every point
88
+ * silently. PNG puts IHDR first, so width and height are at fixed offsets and no
89
+ * decoder is needed.
90
+ */
91
+ export function readPngSize(data) {
92
+ if (data.byteLength < 24)
93
+ return null;
94
+ const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
95
+ if (view.getUint32(0) !== 0x89504e47 || view.getUint32(4) !== 0x0d0a1a0a)
96
+ return null;
97
+ if (view.getUint32(12) !== 0x49484452)
98
+ return null;
99
+ const width = view.getUint32(16);
100
+ const height = view.getUint32(20);
101
+ if (width === 0 || height === 0)
102
+ return null;
103
+ return { width, height };
104
+ }
105
+ //# sourceMappingURL=observe.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"observe.js","sourceRoot":"","sources":["../src/observe.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ /** One deadline for connection recovery and the operation it precedes. */
2
+ import { EngineError } from 'e2e/engine';
3
+ export interface ConnectionBudget {
4
+ readonly timeoutMs: number;
5
+ readonly signal: AbortSignal;
6
+ }
7
+ /** Preserves the reason for an engine-owned deadline without relabeling caller cancellation. */
8
+ export declare function connectionAbort(signal: AbortSignal, label: string): EngineError;
9
+ /** Bounds the entire transition, aborts abandoned work, and never grants a fresh dispatch budget. */
10
+ export declare function withConnectionBudget<T>(budget: ConnectionBudget, label: string, work: (remaining: () => ConnectionBudget) => Promise<T>): Promise<T>;
11
+ //# sourceMappingURL=operation-budget.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"operation-budget.d.ts","sourceRoot":"","sources":["../src/operation-budget.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAC1E,OAAO,EAAE,WAAW,EAAa,MAAM,YAAY,CAAC;AAEpD,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;CAC9B;AAED,gGAAgG;AAChG,wBAAgB,eAAe,CAAC,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,GAAG,WAAW,CAI/E;AAED,qGAAqG;AACrG,wBAAsB,oBAAoB,CAAC,CAAC,EAC1C,MAAM,EAAE,gBAAgB,EACxB,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,CAAC,SAAS,EAAE,MAAM,gBAAgB,KAAK,OAAO,CAAC,CAAC,CAAC,GACtD,OAAO,CAAC,CAAC,CAAC,CAwBZ"}
@@ -0,0 +1,40 @@
1
+ /** One deadline for connection recovery and the operation it precedes. */
2
+ import { EngineError, raceAbort } from 'e2e/engine';
3
+ /** Preserves the reason for an engine-owned deadline without relabeling caller cancellation. */
4
+ export function connectionAbort(signal, label) {
5
+ return signal.reason instanceof EngineError && signal.reason.code === 'OPERATION_TIMEOUT'
6
+ ? signal.reason
7
+ : new EngineError('CANCELLED', `${label} cancelled`, { retryable: false });
8
+ }
9
+ /** Bounds the entire transition, aborts abandoned work, and never grants a fresh dispatch budget. */
10
+ export async function withConnectionBudget(budget, label, work) {
11
+ const controller = new AbortController();
12
+ const signal = AbortSignal.any([budget.signal, controller.signal]);
13
+ const endsAt = Date.now() + budget.timeoutMs;
14
+ const timeout = () => new EngineError('OPERATION_TIMEOUT', `${label} timed out`, { retryable: false });
15
+ const timer = setTimeout(() => controller.abort(timeout()), Math.max(0, budget.timeoutMs));
16
+ const remaining = () => {
17
+ if (signal.aborted)
18
+ throw connectionAbort(signal, label);
19
+ const timeoutMs = endsAt - Date.now();
20
+ if (timeoutMs <= 0) {
21
+ controller.abort(timeout());
22
+ throw connectionAbort(signal, label);
23
+ }
24
+ return { signal, timeoutMs };
25
+ };
26
+ try {
27
+ return await raceAbort(() => work(remaining), signal, label);
28
+ }
29
+ catch (cause) {
30
+ if (budget.signal.aborted)
31
+ throw connectionAbort(budget.signal, label);
32
+ if (signal.aborted)
33
+ throw connectionAbort(signal, label);
34
+ throw cause;
35
+ }
36
+ finally {
37
+ clearTimeout(timer);
38
+ }
39
+ }
40
+ //# sourceMappingURL=operation-budget.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"operation-budget.js","sourceRoot":"","sources":["../src/operation-budget.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAC1E,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAOpD,gGAAgG;AAChG,MAAM,UAAU,eAAe,CAAC,MAAmB,EAAE,KAAa;IAChE,OAAO,MAAM,CAAC,MAAM,YAAY,WAAW,IAAI,MAAM,CAAC,MAAM,CAAC,IAAI,KAAK,mBAAmB;QACvF,CAAC,CAAC,MAAM,CAAC,MAAM;QACf,CAAC,CAAC,IAAI,WAAW,CAAC,WAAW,EAAE,GAAG,KAAK,YAAY,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC;AAC/E,CAAC;AAED,qGAAqG;AACrG,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,MAAwB,EACxB,KAAa,EACb,IAAuD;IAEvD,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;IACzC,MAAM,MAAM,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC;IACnE,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,MAAM,CAAC,SAAS,CAAC;IAC7C,MAAM,OAAO,GAAG,GAAG,EAAE,CAAC,IAAI,WAAW,CAAC,mBAAmB,EAAE,GAAG,KAAK,YAAY,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC;IACvG,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC;IAC3F,MAAM,SAAS,GAAG,GAAqB,EAAE;QACvC,IAAI,MAAM,CAAC,OAAO;YAAE,MAAM,eAAe,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;QACzD,MAAM,SAAS,GAAG,MAAM,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACtC,IAAI,SAAS,IAAI,CAAC,EAAE,CAAC;YACnB,UAAU,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;YAC5B,MAAM,eAAe,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;QACvC,CAAC;QACD,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;IAC/B,CAAC,CAAC;IACF,IAAI,CAAC;QACH,OAAO,MAAM,SAAS,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC;IAC/D,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,MAAM,CAAC,MAAM,CAAC,OAAO;YAAE,MAAM,eAAe,CAAC,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;QACvE,IAAI,MAAM,CAAC,OAAO;YAAE,MAAM,eAAe,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;QACzD,MAAM,KAAK,CAAC;IACd,CAAC;YAAS,CAAC;QACT,YAAY,CAAC,KAAK,CAAC,CAAC;IACtB,CAAC;AACH,CAAC"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Protected-preview access, scoped to the app's site: the configured headers
3
+ * ride only requests bound for the site, and basic-auth credentials answer
4
+ * a challenge wherever one is issued. The surface calls these on every
5
+ * context it creates; nothing here holds state.
6
+ */
7
+ import type { BrowserContext } from 'playwright';
8
+ import type { WebBasicAuth } from './surface.ts';
9
+ /** The credentials Playwright answers an HTTP authentication challenge with. */
10
+ export interface PlaywrightHttpCredential {
11
+ readonly username: string;
12
+ readonly password: string;
13
+ }
14
+ /** Header names lowercased, as Playwright reports a request's own headers. */
15
+ export declare function lowercaseNames(headers: Readonly<Record<string, string>>): Readonly<Record<string, string>>;
16
+ /**
17
+ * Adds the configured headers to every request bound for the app's site,
18
+ * via a context route that falls back to the network. A request for any
19
+ * other site is not routed at all; without a site there is no policy and
20
+ * nothing is routed. Nothing awaits a route handler, so a fallback that
21
+ * fails (the page closed under the request) is dropped rather than left to
22
+ * surface as an unhandled rejection.
23
+ */
24
+ export declare function installSiteHeaders(context: BrowserContext, site: string | undefined, headers: Readonly<Record<string, string>> | undefined): Promise<void>;
25
+ /**
26
+ * The basic-auth credentials a context answers a challenge with, as
27
+ * Playwright's own `httpCredentials` does: on a 401 from any origin. A site
28
+ * cannot be enumerated into the exact origins Playwright scopes by, and a
29
+ * challenge is answered only where one is issued.
30
+ */
31
+ export declare function httpCredentials(basicAuth: WebBasicAuth | undefined): PlaywrightHttpCredential | undefined;
32
+ //# sourceMappingURL=protected-app.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"protected-app.d.ts","sourceRoot":"","sources":["../src/protected-app.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAEjD,gFAAgF;AAChF,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED,8EAA8E;AAC9E,wBAAgB,cAAc,CAAC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAE1G;AAED;;;;;;;GAOG;AACH,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,cAAc,EACvB,IAAI,EAAE,MAAM,GAAG,SAAS,EACxB,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,GAAG,SAAS,GACpD,OAAO,CAAC,IAAI,CAAC,CAQf;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,YAAY,GAAG,SAAS,GAAG,wBAAwB,GAAG,SAAS,CAIzG"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Protected-preview access, scoped to the app's site: the configured headers
3
+ * ride only requests bound for the site, and basic-auth credentials answer
4
+ * a challenge wherever one is issued. The surface calls these on every
5
+ * context it creates; nothing here holds state.
6
+ */
7
+ import { sameSite } from 'e2e/engine';
8
+ /** Header names lowercased, as Playwright reports a request's own headers. */
9
+ export function lowercaseNames(headers) {
10
+ return Object.fromEntries(Object.entries(headers).map(([name, value]) => [name.toLowerCase(), value]));
11
+ }
12
+ /**
13
+ * Adds the configured headers to every request bound for the app's site,
14
+ * via a context route that falls back to the network. A request for any
15
+ * other site is not routed at all; without a site there is no policy and
16
+ * nothing is routed. Nothing awaits a route handler, so a fallback that
17
+ * fails (the page closed under the request) is dropped rather than left to
18
+ * surface as an unhandled rejection.
19
+ */
20
+ export async function installSiteHeaders(context, site, headers) {
21
+ if (headers === undefined || site === undefined)
22
+ return;
23
+ await context.route((url) => sameSite(url, site), async (route) => {
24
+ await route.fallback({ headers: { ...route.request().headers(), ...headers } }).catch(() => undefined);
25
+ });
26
+ }
27
+ /**
28
+ * The basic-auth credentials a context answers a challenge with, as
29
+ * Playwright's own `httpCredentials` does: on a 401 from any origin. A site
30
+ * cannot be enumerated into the exact origins Playwright scopes by, and a
31
+ * challenge is answered only where one is issued.
32
+ */
33
+ export function httpCredentials(basicAuth) {
34
+ if (basicAuth === undefined)
35
+ return undefined;
36
+ const { username, password } = basicAuth;
37
+ return { username, password };
38
+ }
39
+ //# sourceMappingURL=protected-app.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"protected-app.js","sourceRoot":"","sources":["../src/protected-app.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAStC,8EAA8E;AAC9E,MAAM,UAAU,cAAc,CAAC,OAAyC;IACtE,OAAO,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC;AACzG,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,OAAuB,EACvB,IAAwB,EACxB,OAAqD;IAErD,IAAI,OAAO,KAAK,SAAS,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO;IACxD,MAAM,OAAO,CAAC,KAAK,CACjB,CAAC,GAAG,EAAE,EAAE,CAAC,QAAQ,CAAC,GAAG,EAAE,IAAI,CAAC,EAC5B,KAAK,EAAE,KAAK,EAAE,EAAE;QACd,MAAM,KAAK,CAAC,QAAQ,CAAC,EAAE,OAAO,EAAE,EAAE,GAAG,KAAK,CAAC,OAAO,EAAE,CAAC,OAAO,EAAE,EAAE,GAAG,OAAO,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;IACzG,CAAC,CACF,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAAC,SAAmC;IACjE,IAAI,SAAS,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IAC9C,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,GAAG,SAAS,CAAC;IACzC,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC;AAChC,CAAC"}