wrc-ts 0.1.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.
@@ -0,0 +1,840 @@
1
+ import type { Transport } from "@connectrpc/connect";
2
+ import type { Locator } from "./locator.ts";
3
+ import type { DOMResult, DragResult, ElementResult, EvaluateResult, InterceptedRequest, InterceptedResponse, InspectResult, NavigateResult, ObservationResult, PageInfo, SelectOptionResult, WaitResult } from "./types.ts";
4
+ import type { ClickOpts, FillOpts, GetDOMOpts, GetObservationOpts, LoadHTMLOpts, NavigateOpts, SelectOpts, WaitOpts } from "./options.ts";
5
+ import type { HeaderModification, RequestPattern } from "./network.ts";
6
+ import type { CookieParam } from "./cookies.ts";
7
+ import type { StorageOriginEntry } from "./storage.ts";
8
+ /**
9
+ * CloudBrowser is the SDK-side handle for an active WRC browser session.
10
+ *
11
+ * One CloudBrowser corresponds to exactly one browser context, which
12
+ * always has at least one page. The session is implicitly bound to its
13
+ * primary page server-side — the proto's page_id field is currently
14
+ * ignored server-side, so the SDK never sets it.
15
+ *
16
+ * Construct via rentBrowser() / createWebSocketBrowser() — never
17
+ * directly.
18
+ */
19
+ export declare class CloudBrowser {
20
+ private readonly client;
21
+ private readonly sessionId;
22
+ private readonly apiKey;
23
+ private readonly _transport;
24
+ private readonly _fingerprint;
25
+ private readonly _stopFn?;
26
+ constructor(transport: Transport, sessionId: string, apiKey: string, fingerprint: string, stopFn?: () => Promise<void>);
27
+ /** Returns the unique server-assigned id for this browser session. */
28
+ getSessionId(): string;
29
+ /** Returns the API key used to rent this session. */
30
+ getApiKey(): string;
31
+ /** Returns the browser fingerprint id in use for this session. */
32
+ getFingerprint(): string;
33
+ /**
34
+ * Releases the session and closes the underlying transport.
35
+ *
36
+ * For sessions created via {@link rentBrowser} this also calls the WRC
37
+ * stop endpoint to release the rental. For sessions attached with
38
+ * {@link createWebSocketBrowser} the rental stays untouched; only the
39
+ * transport is closed.
40
+ *
41
+ * @throws UNKNOWN_ERROR - the stop API or the transport close failed
42
+ *
43
+ * @example
44
+ * await browser.stopBrowser();
45
+ */
46
+ stopBrowser(): Promise<void>;
47
+ /**
48
+ * Changes the runtime proxy for this session.
49
+ *
50
+ * Takes effect for new requests immediately; in-flight requests keep
51
+ * their original routing. Pass an empty proxyHost to clear the proxy and
52
+ * route directly.
53
+ *
54
+ * @param proxyHost - upstream proxy host; empty disables the proxy
55
+ * @param proxyPort - upstream proxy port; ignored when proxyHost is empty
56
+ * @param proxyUsername - proxy auth user; empty for unauthenticated proxies
57
+ * @param proxyPassword - proxy auth password; empty for unauthenticated proxies
58
+ *
59
+ * @throws UNKNOWN_ERROR - the proxy could not be applied
60
+ *
61
+ * @example
62
+ * await browser.setProxy("proxy.example.com", 8080, "user", "pass");
63
+ */
64
+ setProxy(proxyHost: string, proxyPort: number, proxyUsername?: string, proxyPassword?: string): Promise<void>;
65
+ /**
66
+ * Returns all open pages (tabs and popups) for this session's browser
67
+ * context.
68
+ *
69
+ * Each PageInfo carries the page's URL, title, viewport and a full
70
+ * nested frame tree (out-of-process iframes are children of the page's
71
+ * main frame).
72
+ *
73
+ * @returns PageInfo[] for every page currently open in the context
74
+ *
75
+ * @throws UNKNOWN_ERROR - the pages could not be enumerated
76
+ *
77
+ * @example
78
+ * const pages = await browser.getPages();
79
+ * for (const p of pages) console.log(p.url, p.title);
80
+ */
81
+ getPages(): Promise<PageInfo[]>;
82
+ /**
83
+ * Navigates the page to url.
84
+ *
85
+ * Returns once the primary main-frame navigation commits (the response
86
+ * is received and a new document is selected), before DOMContentLoaded or
87
+ * load fire. Cross-origin redirects are followed.
88
+ *
89
+ * @param url - destination URL
90
+ * @param opts - optional navigation customization (e.g. timeoutMs)
91
+ *
92
+ * @returns NavigateResult with the final resolved URL and the frameId
93
+ * of the main frame after navigation
94
+ *
95
+ * @throws UNKNOWN_ERROR - the navigation failed or timed out
96
+ *
97
+ * @example
98
+ * await browser.navigate("https://example.com");
99
+ */
100
+ navigate(url: string, opts?: NavigateOpts): Promise<NavigateResult>;
101
+ /**
102
+ * Serves a synthetic response for the next navigation to url.
103
+ *
104
+ * Registers a one-shot interceptor that intercepts the next request to
105
+ * url and replies with the supplied html and headers instead of going to
106
+ * the network. Useful for snapshotted pages, test fixtures, and offline
107
+ * replays. Pair with {@link navigate} to trigger the load.
108
+ *
109
+ * @param url - URL pattern that, when navigated to, returns the html
110
+ * @param html - response body to serve
111
+ * @param opts - optional headers and statusCode (default 200)
112
+ *
113
+ * @throws UNKNOWN_ERROR - the interceptor could not be installed
114
+ *
115
+ * @example
116
+ * await browser.loadHTML("https://example.com", "<h1>hi</h1>");
117
+ * await browser.navigate("https://example.com");
118
+ */
119
+ loadHTML(url: string, html: string, opts?: LoadHTMLOpts): Promise<void>;
120
+ /**
121
+ * Runs a JavaScript expression in the page's main frame.
122
+ *
123
+ * The expression's return value is JSON-serialized server-side and
124
+ * parsed eagerly into `.value`. When the expression returns a DOM
125
+ * element the `.value` is null and the element metadata
126
+ * (backendNodeId, isVisible, bounds) is populated instead — use
127
+ * {@link node} in subsequent calls to act on it.
128
+ *
129
+ * The generic T is a TypeScript hint only — there is no runtime
130
+ * validation that the JS expression actually returned that type.
131
+ *
132
+ * @param expression - JavaScript expression evaluated in the main frame
133
+ *
134
+ * @returns EvaluateResult with either value (non-Element) or element
135
+ * metadata (Element)
136
+ *
137
+ * @throws UNKNOWN_ERROR - the expression threw or could not be compiled
138
+ *
139
+ * @example
140
+ * const res = await browser.evaluate<string>("document.title");
141
+ * console.log(res.value);
142
+ */
143
+ evaluate<T = unknown>(expression: string): Promise<EvaluateResult<T>>;
144
+ /**
145
+ * Runs a JavaScript expression in the given frame.
146
+ *
147
+ * Same semantics as {@link evaluate} but targets a specific frame
148
+ * instead of the main frame. Useful for evaluating inside OOPIFs (out-
149
+ * of-process iframes) found via {@link getPages}. ALL_FRAMES is not
150
+ * supported here.
151
+ *
152
+ * @inheritDoc CloudBrowser.evaluate
153
+ * @param frameId - id of the frame to evaluate in
154
+ *
155
+ * @example
156
+ * const pages = await browser.getPages();
157
+ * const iframeId = pages[0].frameTree.children[0].frameId;
158
+ * await browser.evaluateInFrame(iframeId, "location.href");
159
+ */
160
+ evaluateInFrame<T = unknown>(frameId: string, expression: string): Promise<EvaluateResult<T>>;
161
+ private _evaluate;
162
+ /**
163
+ * Blocks until the given locator matches.
164
+ *
165
+ * Shortcut for `waitAny([condition], opts)`. See {@link waitAny} for
166
+ * timeout handling, per-locator visible/steady defaults and the list of
167
+ * locators that are not valid wait conditions.
168
+ *
169
+ * @inheritDoc CloudBrowser.waitAny
170
+ * @param condition - the single locator to wait for
171
+ *
172
+ * @example
173
+ * await browser.wait(css(".success"));
174
+ */
175
+ wait(condition: Locator, opts?: WaitOpts): Promise<WaitResult>;
176
+ /**
177
+ * Blocks until any of the supplied locators matches.
178
+ *
179
+ * When several conditions are supplied, the first one to match wins;
180
+ * the others are abandoned. The returned WaitResult's `.index` points
181
+ * to the entry in `conditions` that matched.
182
+ *
183
+ * Defaults applied automatically:
184
+ * - timeout: 30000 ms — override via `opts.timeoutMs`
185
+ * - per-locator visible/steady: `true` / `500` for css() and js()
186
+ * locators. For js() expressions returning a non-Element value both
187
+ * flags are no-ops. Override with `.visible(false)` / `.steady(ms)`
188
+ * on individual locators.
189
+ *
190
+ * {@link node} and {@link at} are not valid wait conditions — they only
191
+ * make sense as action targets — and throw at send time.
192
+ *
193
+ * @param conditions - one or more {@link Locator}s to wait for; must be non-empty
194
+ * @param opts - optional wait customization (timeoutMs)
195
+ *
196
+ * @returns WaitResult for the first matching condition
197
+ *
198
+ * @throws UNKNOWN_ERROR - the wait timed out or a condition was invalid
199
+ *
200
+ * @example
201
+ * const r = await browser.waitAny(
202
+ * [css(".success"), js("window.__ready === true")],
203
+ * { timeoutMs: 5000 },
204
+ * );
205
+ * console.log("matched index:", r.index);
206
+ */
207
+ waitAny(conditions: Locator[], opts?: WaitOpts): Promise<WaitResult>;
208
+ /**
209
+ * Triggers a single left mouse click on the given target.
210
+ *
211
+ * The browser scrolls the element into view if needed, moves the
212
+ * cursor along a human-like path, then dispatches a full
213
+ * mouseDown+mouseUp at a randomized point inside the element's
214
+ * bounding rect.
215
+ *
216
+ * For right-click, double-click, press/release-only, or to override
217
+ * the target frame, pass a `ClickOpts` object as the second argument.
218
+ *
219
+ * @param target - locator describing what to click; {@link at} is also valid
220
+ * @param opts - optional click customization; see {@link ClickOpts}
221
+ *
222
+ * @returns ElementResult with success, resolved frameId, backendNodeId,
223
+ * post-scroll isVisible, element bounds, and the root-viewport
224
+ * (rootX, rootY) where the click landed
225
+ *
226
+ * @throws INVALID_LOCATOR - target is empty or has multiple targets set
227
+ * @throws ELEMENT_NOT_FOUND - no element matched the locator
228
+ * @throws FRAME_NOT_FOUND - the requested frame does not exist
229
+ * @throws CLICK_FAILED - the click could not be dispatched
230
+ * @throws TIMEOUT - the operation exceeded the server-side timeout
231
+ * @throws PAGE_NOT_ALIVE - the page has been closed
232
+ *
233
+ * @example
234
+ * await browser.click(css("button.submit"));
235
+ *
236
+ * @example
237
+ * // Right double-click on a context menu trigger.
238
+ * await browser.click(css("li.menu"), { button: "right", clickCount: 2 });
239
+ */
240
+ click(target: Locator, opts?: ClickOpts): Promise<ElementResult>;
241
+ /**
242
+ * Clicks the target and types text into it, appending to any existing
243
+ * content.
244
+ *
245
+ * The browser scrolls the element into view, moves the cursor along
246
+ * a human-like path, clicks to focus, then types the text character-
247
+ * by-character with QWERTZ keyboard simulation and human-like timing.
248
+ *
249
+ * To overwrite the field instead of appending, pass `{ clearFirst: true }`.
250
+ *
251
+ * {@link at} is not a valid target — fill requires an actual element.
252
+ *
253
+ * @param target - locator describing the input element
254
+ * @param text - text to type into the element
255
+ * @param opts - optional fill customization; see {@link FillOpts}
256
+ *
257
+ * @returns ElementResult with success, resolved frameId, backendNodeId
258
+ * and the root-viewport (rootX, rootY) where the element was clicked
259
+ *
260
+ * @throws INVALID_LOCATOR - target is empty or has multiple targets set
261
+ * @throws ELEMENT_NOT_FOUND - no element matched the locator
262
+ * @throws FRAME_NOT_FOUND - the requested frame does not exist
263
+ * @throws FILL_FAILED - the input could not be filled
264
+ * @throws TIMEOUT - the operation exceeded the server-side timeout
265
+ * @throws PAGE_NOT_ALIVE - the page has been closed
266
+ *
267
+ * @example
268
+ * await browser.fill(css("input[name=email]"), "user@example.com");
269
+ *
270
+ * @example
271
+ * // Wipe the field first, then type fresh content.
272
+ * await browser.fill(css("input[name=email]"), "user@example.com", { clearFirst: true });
273
+ */
274
+ fill(target: Locator, text: string, opts?: FillOpts): Promise<ElementResult>;
275
+ /**
276
+ * Moves the mouse cursor over the given target.
277
+ *
278
+ * The browser scrolls the target into view first if necessary, then
279
+ * animates the cursor along a human-like path to the element's center
280
+ * (or to the viewport coordinate when target is {@link at}).
281
+ *
282
+ * @param target - locator describing where to move; {@link at} is also valid
283
+ *
284
+ * @returns ElementResult with the resolved frameId, backendNodeId,
285
+ * post-scroll isVisible, element bounds and the root-viewport
286
+ * (rootX, rootY) where the cursor ended up
287
+ *
288
+ * @throws UNKNOWN_ERROR - the move could not be completed
289
+ *
290
+ * @example
291
+ * await browser.moveTo(css("nav .menu"));
292
+ */
293
+ moveTo(target: Locator): Promise<ElementResult>;
294
+ /**
295
+ * Scrolls the given element into view.
296
+ *
297
+ * Whatever scroll container is closest to the element does the
298
+ * scrolling — nested scroll containers and out-of-process iframe chains
299
+ * are walked automatically. {@link at} is not a valid target here;
300
+ * scrolling needs a real element.
301
+ *
302
+ * @param target - locator describing the element to bring into view;
303
+ * {@link at} is rejected
304
+ *
305
+ * @returns ElementResult with the resolved frameId, backendNodeId,
306
+ * post-scroll isVisible and the element's bounds after the scroll
307
+ *
308
+ * @throws UNKNOWN_ERROR - the element could not be scrolled into view
309
+ *
310
+ * @example
311
+ * await browser.scrollTo(css("#footer"));
312
+ */
313
+ scrollTo(target: Locator): Promise<ElementResult>;
314
+ /**
315
+ * Picks up the target and drops it at an offset relative to the pickup
316
+ * point.
317
+ *
318
+ * The browser presses the left mouse button at a pickup point inside
319
+ * the element, drags along a human-like path to (pickupX+offsetX,
320
+ * pickupY+offsetY), then releases. {@link at} is not a valid target —
321
+ * drag needs a real element.
322
+ *
323
+ * @param target - locator describing the element to pick up
324
+ * @param offsetX - horizontal distance to drag, in CSS pixels
325
+ * @param offsetY - vertical distance to drag, in CSS pixels
326
+ *
327
+ * @returns DragResult with the resolved frameId, backendNodeId and the
328
+ * final cursor position (rootX, rootY) where the drop happened
329
+ *
330
+ * @throws UNKNOWN_ERROR - the drag could not be performed
331
+ *
332
+ * @example
333
+ * await browser.dragBy(css(".slider .handle"), 120, 0);
334
+ */
335
+ dragBy(target: Locator, offsetX: number, offsetY: number): Promise<DragResult>;
336
+ /**
337
+ * Picks up the target and drops it at absolute root-viewport coordinates.
338
+ *
339
+ * Same gesture as {@link dragBy}, but the drop destination is in page
340
+ * coordinates rather than relative to the pickup point.
341
+ *
342
+ * @param target - locator describing the element to pick up
343
+ * @param absoluteX - horizontal drop coordinate in the root viewport
344
+ * @param absoluteY - vertical drop coordinate in the root viewport
345
+ *
346
+ * @returns DragResult with the resolved frameId, backendNodeId and the
347
+ * final cursor position (rootX, rootY) where the drop happened
348
+ *
349
+ * @throws UNKNOWN_ERROR - the drag could not be performed
350
+ *
351
+ * @example
352
+ * await browser.dragTo(css(".card"), 800, 400);
353
+ */
354
+ dragTo(target: Locator, absoluteX: number, absoluteY: number): Promise<DragResult>;
355
+ private _drag;
356
+ /**
357
+ * Picks the `<option>` at the zero-based index inside the targeted
358
+ * `<select>` element.
359
+ *
360
+ * Sets the option as selected on the targeted `<select>`, then fires
361
+ * the standard input + change events (unless suppressed via
362
+ * `opts.fireEvents = false`).
363
+ *
364
+ * {@link at} is not a valid target — select requires an actual
365
+ * `<select>` element.
366
+ *
367
+ * @param target - locator describing the `<select>` element
368
+ * @param index - zero-based option index
369
+ * @param opts - optional select customization; see {@link SelectOpts}
370
+ *
371
+ * @returns SelectOptionResult with the resolved selectedIndex,
372
+ * selectedValue and selectedText after the change
373
+ *
374
+ * @throws INVALID_LOCATOR - target is empty or has multiple targets set
375
+ * @throws ELEMENT_NOT_FOUND - no element matched the locator
376
+ * @throws FRAME_NOT_FOUND - the requested frame does not exist
377
+ * @throws SELECT_FAILED - the option could not be selected
378
+ * (out of range, or element is not a `<select>`)
379
+ * @throws TIMEOUT - the operation exceeded the server-side timeout
380
+ * @throws PAGE_NOT_ALIVE - the page has been closed
381
+ *
382
+ * @example
383
+ * await browser.selectByIndex(css("select#country"), 2);
384
+ *
385
+ * @example
386
+ * // Pick the option silently, no input/change events.
387
+ * await browser.selectByIndex(css("select#hidden"), 0, { fireEvents: false });
388
+ */
389
+ selectByIndex(target: Locator, index: number, opts?: SelectOpts): Promise<SelectOptionResult>;
390
+ /**
391
+ * Picks the `<option>` whose `value` attribute matches the given
392
+ * string exactly.
393
+ *
394
+ * @inheritDoc {@link CloudBrowser.selectByIndex}
395
+ * @param value - the `value` attribute to match
396
+ *
397
+ * @example
398
+ * await browser.selectByValue(css("select#country"), "DE");
399
+ */
400
+ selectByValue(target: Locator, value: string, opts?: SelectOpts): Promise<SelectOptionResult>;
401
+ /**
402
+ * Picks the `<option>` whose visible (trimmed) text matches the given
403
+ * string exactly.
404
+ *
405
+ * @inheritDoc {@link CloudBrowser.selectByIndex}
406
+ * @param text - the visible option text to match
407
+ *
408
+ * @example
409
+ * await browser.selectByText(css("select#country"), "Germany");
410
+ */
411
+ selectByText(target: Locator, text: string, opts?: SelectOpts): Promise<SelectOptionResult>;
412
+ private _select;
413
+ /**
414
+ * Returns the DOM in CDP DOM.Node shape for the requested frame.
415
+ *
416
+ * The shape matches Chrome DevTools' Protocol DOM.Node — useful for
417
+ * piping into agent loops or visualizers that already speak CDP. For a
418
+ * much smaller agent-oriented payload, prefer {@link getObservation}
419
+ * instead. The cheap polling endpoint is {@link getDOMHash}.
420
+ *
421
+ * @param frameId - id of the frame to dump; empty string targets the main frame
422
+ * @param opts - optional `depth`: -1 full tree (default), 0 root only,
423
+ * N root + N descendant levels
424
+ *
425
+ * @returns DOMResult with the JSON string in `.dom` (the `.hash` field
426
+ * is populated by {@link getDOMHash}, not by this call)
427
+ *
428
+ * @throws UNKNOWN_ERROR - the DOM could not be retrieved
429
+ *
430
+ * @example
431
+ * const { dom } = await browser.getDOM();
432
+ */
433
+ getDOM(frameId?: string, opts?: GetDOMOpts): Promise<DOMResult>;
434
+ /**
435
+ * Returns sha256[:8] of the full-tree DOM JSON for cheap polling-based
436
+ * change detection.
437
+ *
438
+ * Computing a hash is much cheaper than transferring the full tree —
439
+ * pair this with {@link getDOM} only when the hash differs from your
440
+ * last snapshot.
441
+ *
442
+ * @param frameId - id of the frame to hash; empty string targets the main frame
443
+ *
444
+ * @returns 16-char hex string (the first 8 bytes of sha256 of the DOM JSON)
445
+ *
446
+ * @throws UNKNOWN_ERROR - the hash could not be computed
447
+ *
448
+ * @example
449
+ * const hash = await browser.getDOMHash();
450
+ * if (hash !== lastHash) {
451
+ * // DOM changed → re-fetch
452
+ * }
453
+ */
454
+ getDOMHash(frameId?: string): Promise<string>;
455
+ /**
456
+ * Returns a compact, agent-friendly description of every interactable
457
+ * element currently visible on the page, together with a truncated view
458
+ * of the surrounding text.
459
+ *
460
+ * Intended as input for LLM/agent loops where a full DOM dump would be
461
+ * too large; the server filters down to elements that are actually
462
+ * visible and interactable.
463
+ *
464
+ * @param opts - optional caps: `maxElementsPerFrame`, `maxTextLength`;
465
+ * omit either to use the server default
466
+ *
467
+ * @returns ObservationResult with both a human-readable `text` rendering
468
+ * and a `json` payload of the structured observation
469
+ *
470
+ * @throws UNKNOWN_ERROR - the observation could not be produced
471
+ *
472
+ * @example
473
+ * const obs = await browser.getObservation({ maxElementsPerFrame: 200 });
474
+ * console.log(obs.text);
475
+ */
476
+ getObservation(opts?: GetObservationOpts): Promise<ObservationResult>;
477
+ /**
478
+ * Replaces the session's URL blocklist.
479
+ *
480
+ * Any request whose URL matches one of the supplied patterns is blocked
481
+ * before it leaves the browser. Patterns are simple URL wildcards (`*`
482
+ * matches any character span). Pass an empty array to clear the
483
+ * blocklist and let everything through.
484
+ *
485
+ * @param patterns - URL wildcards to block; empty array clears the list
486
+ *
487
+ * @throws UNKNOWN_ERROR - the blocklist could not be applied
488
+ *
489
+ * @example
490
+ * await browser.setBlockList([
491
+ * "*.doubleclick.net/*",
492
+ * "*googletagmanager.com*",
493
+ * ]);
494
+ */
495
+ setBlockList(patterns: string[]): Promise<void>;
496
+ /**
497
+ * Configures the session to serve cached static responses for requests
498
+ * matching the given patterns from blobName.
499
+ *
500
+ * Useful for replaying frozen page assets (HTML/JS/CSS/images) without
501
+ * hitting the origin every time. The cache backend itself is configured
502
+ * server-side. Pass an empty patterns array to disable caching for this
503
+ * session.
504
+ *
505
+ * @param blobName - server-side identifier of the snapshot to serve from
506
+ * @param patterns - URL wildcards to redirect to the cache; empty disables
507
+ *
508
+ * @throws UNKNOWN_ERROR - the static paths could not be configured
509
+ *
510
+ * @example
511
+ * await browser.setStaticPaths("snap-2026-05", ["*.example.com/*"]);
512
+ */
513
+ setStaticPaths(blobName: string, patterns: string[]): Promise<void>;
514
+ /**
515
+ * Blocks until the next request whose URL matches one of the supplied
516
+ * patterns is observed.
517
+ *
518
+ * Returns the matched pattern's index and the captured request. When
519
+ * `patterns[i].abort` is true the request is dropped with an empty 200
520
+ * response instead of being sent to the network.
521
+ *
522
+ * @param patterns - one or more URL patterns (with optional abort flags)
523
+ * @param opts - optional `timeoutMs`; omit to use the server default
524
+ *
525
+ * @returns object with `index` (matched pattern index) and `request`
526
+ * (the captured method/URL/headers/body; null if intercepted with
527
+ * no body)
528
+ *
529
+ * @throws UNKNOWN_ERROR - the wait timed out or no patterns were supplied
530
+ *
531
+ * @example
532
+ * const { index, request } = await browser.waitForAnyRequest(
533
+ * [{ url: "*\/api/login" }],
534
+ * { timeoutMs: 5000 },
535
+ * );
536
+ * console.log(index, request?.method, request?.url);
537
+ */
538
+ waitForAnyRequest(patterns: RequestPattern[], opts?: {
539
+ timeoutMs?: number;
540
+ }): Promise<{
541
+ index: number;
542
+ request: InterceptedRequest | null;
543
+ }>;
544
+ /**
545
+ * Blocks until the next response whose URL matches one of the supplied
546
+ * patterns is observed.
547
+ *
548
+ * Same shape as {@link waitForAnyRequest} but on the response phase.
549
+ * When `patterns[i].abort` is true the page receives an empty 200
550
+ * instead of the real response.
551
+ *
552
+ * @inheritDoc CloudBrowser.waitForAnyRequest
553
+ *
554
+ * @returns object with `index` (matched pattern index) and `response`
555
+ * (the captured status/headers/body; null if no body was returned)
556
+ *
557
+ * @example
558
+ * const { index, response } = await browser.waitForAnyResponse(
559
+ * [{ url: "*\/api/login" }],
560
+ * { timeoutMs: 5000 },
561
+ * );
562
+ * console.log(index, response?.statusCode);
563
+ */
564
+ waitForAnyResponse(patterns: RequestPattern[], opts?: {
565
+ timeoutMs?: number;
566
+ }): Promise<{
567
+ index: number;
568
+ response: InterceptedResponse | null;
569
+ }>;
570
+ /**
571
+ * Waits for the next request whose URL matches urlPattern, applies the
572
+ * supplied header modifications (and optional body replacement), then
573
+ * forwards the modified request.
574
+ *
575
+ * One-shot: consumes the first matching request. Each modification is a
576
+ * plain {@link HeaderModification} object literal.
577
+ *
578
+ * @param urlPattern - URL wildcard to wait for
579
+ * @param opts - optional `body` (replacement request body), `modifications`
580
+ * (header changes), and `timeoutMs` (per-call timeout)
581
+ *
582
+ * @returns InterceptedRequest carrying the method/URL/headers/body that
583
+ * were actually sent on the wire after modifications were applied;
584
+ * null when no request payload was reported
585
+ *
586
+ * @throws UNKNOWN_ERROR - no matching request appeared within the timeout
587
+ *
588
+ * @example
589
+ * const req = await browser.modifyRequest("*\/api/me", {
590
+ * modifications: [
591
+ * { action: "add", name: "X-Trace", value: "abc123" },
592
+ * { action: "remove", name: "Cookie" },
593
+ * ],
594
+ * timeoutMs: 5000,
595
+ * });
596
+ * console.log("forwarded headers:", req?.headers);
597
+ */
598
+ modifyRequest(urlPattern: string, opts?: {
599
+ body?: string;
600
+ modifications?: HeaderModification[];
601
+ timeoutMs?: number;
602
+ }): Promise<InterceptedRequest | null>;
603
+ /**
604
+ * Returns all cookies currently stored in this session's browser context.
605
+ *
606
+ * @returns CookieParam[], one per cookie in the context
607
+ *
608
+ * @throws UNKNOWN_ERROR - the cookies could not be read
609
+ *
610
+ * @example
611
+ * const cookies = await browser.getCookies();
612
+ * for (const c of cookies) console.log(c.name, "=", c.value);
613
+ */
614
+ getCookies(): Promise<CookieParam[]>;
615
+ /**
616
+ * Writes the supplied cookies into the browser context.
617
+ *
618
+ * Existing cookies with the same (name, domain, path) tuple are
619
+ * overwritten. Pass an empty array for a no-op.
620
+ *
621
+ * @param cookies - cookies to write; empty array is a no-op
622
+ *
623
+ * @throws UNKNOWN_ERROR - the cookies could not be written
624
+ *
625
+ * @example
626
+ * await browser.setCookies([
627
+ * { name: "auth", value: "tok", domain: "example.com", path: "/" },
628
+ * ]);
629
+ */
630
+ setCookies(cookies: CookieParam[]): Promise<void>;
631
+ /**
632
+ * Deletes every cookie in the browser context.
633
+ *
634
+ * @throws UNKNOWN_ERROR - the cookies could not be cleared
635
+ *
636
+ * @example
637
+ * await browser.clearCookies();
638
+ */
639
+ clearCookies(): Promise<void>;
640
+ /**
641
+ * Returns the localStorage contents of this session's browser context,
642
+ * grouped by origin.
643
+ *
644
+ * The storage database is read directly in the browser process, so no
645
+ * page needs to be open. Only first-party localStorage is included —
646
+ * sessionStorage is per-tab and not covered.
647
+ *
648
+ * @param origin - if set, only this origin is returned
649
+ * (e.g. "https://example.com"); omit to get all origins
650
+ *
651
+ * @returns StorageOriginEntry[], one per origin with localStorage data
652
+ *
653
+ * @throws UNKNOWN_ERROR - the storage could not be read
654
+ *
655
+ * @example
656
+ * const storage = await browser.getStorage();
657
+ * for (const e of storage) {
658
+ * for (const { key, value } of e.items) console.log(e.origin, key, "=", value);
659
+ * }
660
+ */
661
+ getStorage(origin?: string): Promise<StorageOriginEntry[]>;
662
+ /**
663
+ * Writes localStorage entries into the browser context, grouped by
664
+ * origin.
665
+ *
666
+ * Accepts the same structure getStorage() returns, so a dump can be
667
+ * fed back verbatim. Existing keys are overwritten. Works without any
668
+ * open page; pages that are already open will not observe the writes
669
+ * until they reload.
670
+ *
671
+ * @param storage - entries to write, grouped by origin
672
+ *
673
+ * @throws UNKNOWN_ERROR - the storage could not be written
674
+ *
675
+ * @example
676
+ * await browser.setStorage([
677
+ * {
678
+ * origin: "https://example.com",
679
+ * items: [
680
+ * { key: "token", value: "abc123" },
681
+ * { key: "theme", value: "dark" },
682
+ * ],
683
+ * },
684
+ * ]);
685
+ */
686
+ setStorage(storage: StorageOriginEntry[]): Promise<void>;
687
+ /**
688
+ * Deletes localStorage in the browser context.
689
+ *
690
+ * @param origin - if set, only this origin's storage is deleted
691
+ * (e.g. "https://example.com"); omit to delete all origins
692
+ *
693
+ * @throws UNKNOWN_ERROR - the storage could not be cleared
694
+ *
695
+ * @example
696
+ * // Wipe one origin.
697
+ * await browser.clearStorage("https://example.com");
698
+ *
699
+ * // Wipe everything.
700
+ * await browser.clearStorage();
701
+ */
702
+ clearStorage(origin?: string): Promise<void>;
703
+ /**
704
+ * Hit-tests at the viewport-relative (x, y) and returns the topmost
705
+ * element under that point.
706
+ *
707
+ * Mirrors what the live-UI overlay does on hover. Elements with
708
+ * pointer-events:none are skipped — the result is the actual click
709
+ * target, not the visually-topmost node. A `backendNodeId === 0` in
710
+ * the result means nothing was found.
711
+ *
712
+ * @param x - viewport-relative x in CSS pixels
713
+ * @param y - viewport-relative y in CSS pixels
714
+ *
715
+ * @returns InspectResult with the resolved backendNodeId, frameId, tag
716
+ * name, trimmed textContent, visibility and bounds
717
+ *
718
+ * @throws UNKNOWN_ERROR - the hit-test failed
719
+ *
720
+ * @example
721
+ * const r = await browser.inspectAtPosition(200, 300);
722
+ * console.log(r.tagName, r.textContent);
723
+ */
724
+ inspectAtPosition(x: number, y: number): Promise<InspectResult>;
725
+ /**
726
+ * Paints a debug overlay over the node identified by backendNodeId.
727
+ *
728
+ * Useful for visual debugging of agent flows — the overlay stays until
729
+ * the next call. Pass backendNodeId <= 0 to clear any current highlights.
730
+ *
731
+ * @param backendNodeId - id of the node to highlight, or <= 0 to clear
732
+ * @param frameId - id of the frame the node lives in; empty string
733
+ * targets the main frame
734
+ *
735
+ * @throws UNKNOWN_ERROR - the highlight could not be applied
736
+ *
737
+ * @example
738
+ * await browser.highlightNode(res.backendNodeId, res.frameId);
739
+ */
740
+ highlightNode(backendNodeId: number, frameId?: string): Promise<void>;
741
+ /**
742
+ * Pastes text at the current caret using IME-style input.
743
+ *
744
+ * No individual key events are dispatched; the entire string is
745
+ * committed at once via Input.insertText. Whatever element currently has
746
+ * focus receives the text. Use {@link click} or {@link fill} first if
747
+ * you need a specific element to be focused.
748
+ *
749
+ * @param text - the text to insert at the caret
750
+ *
751
+ * @throws UNKNOWN_ERROR - the text could not be inserted
752
+ *
753
+ * @example
754
+ * await browser.insertText("hello world");
755
+ */
756
+ insertText(text: string): Promise<void>;
757
+ /**
758
+ * Fires a single key-down event.
759
+ *
760
+ * Only the keydown half is dispatched — pair with {@link releaseKey} for
761
+ * a full press cycle. The event targets whichever element currently has
762
+ * focus.
763
+ *
764
+ * @param key - DOM KeyboardEvent.key value (e.g. `"Enter"`, `"a"`, `"ArrowLeft"`)
765
+ * @param opts - optional key customization:
766
+ * `code` (DOM KeyboardEvent.code, e.g. `"KeyA"`),
767
+ * `modifiers` (bit-flag: Alt=1, Ctrl=2, Meta=4, Shift=8),
768
+ * `location` (0=standard, 1=left, 2=right, 3=numpad)
769
+ *
770
+ * @throws UNKNOWN_ERROR - the event could not be dispatched
771
+ *
772
+ * @example
773
+ * // Ctrl+A
774
+ * await browser.pressKey("a", { code: "KeyA", modifiers: 2 });
775
+ * await browser.releaseKey("a", { code: "KeyA", modifiers: 2 });
776
+ */
777
+ pressKey(key: string, opts?: {
778
+ code?: string;
779
+ modifiers?: number;
780
+ location?: number;
781
+ }): Promise<void>;
782
+ /**
783
+ * Fires a single key-up event.
784
+ *
785
+ * Mirror of {@link pressKey}. Same parameter semantics; use this to
786
+ * close a press cycle that was started with pressKey.
787
+ *
788
+ * @inheritDoc CloudBrowser.pressKey
789
+ *
790
+ * @example
791
+ * await browser.pressKey("Shift", { code: "ShiftLeft", location: 1 });
792
+ * await browser.releaseKey("Shift", { code: "ShiftLeft", location: 1 });
793
+ */
794
+ releaseKey(key: string, opts?: {
795
+ code?: string;
796
+ modifiers?: number;
797
+ location?: number;
798
+ }): Promise<void>;
799
+ /**
800
+ * Returns the current text selection.
801
+ *
802
+ * Walks every frame and returns the first non-empty selection found —
803
+ * useful for "copy what the user highlighted" flows. Returns an empty
804
+ * string when nothing is selected anywhere.
805
+ *
806
+ * @returns the selected text, or `""` when nothing is selected
807
+ *
808
+ * @throws UNKNOWN_ERROR - the selection could not be read
809
+ *
810
+ * @example
811
+ * const sel = await browser.getSelection();
812
+ * console.log("user selected:", sel);
813
+ */
814
+ getSelection(): Promise<string>;
815
+ /**
816
+ * Detects and solves the first supported bot-challenge it finds
817
+ * anywhere on the page.
818
+ *
819
+ * Detection covers the common challenge types you run into in the
820
+ * wild. The challenge is completed in-page server-side (the resulting
821
+ * token / bypass cookies are wired into the page automatically), so
822
+ * callers can ignore the returned string.
823
+ *
824
+ * @param opts - optional `timeoutMs` (how long to wait for a captcha to
825
+ * appear; omit for server default 60s) and `retryAmount` (failures
826
+ * tolerated before giving up)
827
+ *
828
+ * @returns empty string on success — the solution is applied server-side
829
+ *
830
+ * @throws UNKNOWN_ERROR - no captcha appeared within timeoutMs, or the
831
+ * detected captcha could not be solved within retryAmount attempts
832
+ *
833
+ * @example
834
+ * await browser.solveCaptcha({ retryAmount: 2 });
835
+ */
836
+ solveCaptcha(opts?: {
837
+ timeoutMs?: number;
838
+ retryAmount?: number;
839
+ }): Promise<string>;
840
+ }