browserscale-ts 1.0.1 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser.d.ts +3 -3
- package/dist/browser.js +2 -2
- package/dist/browserscale.browser.js +322 -87
- package/dist/client.d.ts +70 -27
- package/dist/client.js +106 -45
- package/dist/errors.d.ts +153 -4
- package/dist/errors.js +120 -4
- package/dist/gen/wrc_pb.d.ts +716 -4
- package/dist/gen/wrc_pb.js +140 -55
- package/dist/index.d.ts +27 -3
- package/dist/index.js +31 -2
- package/dist/internal/convert.d.ts +16 -5
- package/dist/internal/convert.js +126 -10
- package/dist/options.d.ts +25 -0
- package/dist/types.d.ts +77 -0
- package/package.json +1 -1
package/dist/client.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Transport } from "@connectrpc/connect";
|
|
2
2
|
import type { Locator } from "./locator.ts";
|
|
3
|
-
import type { DOMResult, DragResult, ElementResult, EvaluateResult, InterceptedRequest, InterceptedResponse, InspectResult, NavigateResult, ObservationResult, PageInfo, ScreenshotResult, SelectOptionResult, WaitResult } from "./types.ts";
|
|
4
|
-
import type { ClickOpts, FillOpts, GetDOMOpts, GetObservationOpts, LoadHTMLOpts, NavigateOpts, ScreenshotOpts, SelectOpts, WaitOpts } from "./options.ts";
|
|
3
|
+
import type { DOMResult, DragResult, ElementResult, EvaluateResult, InterceptedRequest, InterceptedResponse, InspectResult, NavigateResult, ObservationResult, PageInfo, ScreenshotResult, ReadCanvasResult, SelectOptionResult, WaitResult } from "./types.ts";
|
|
4
|
+
import type { ClickOpts, FillOpts, GetDOMOpts, GetObservationOpts, LoadHTMLOpts, NavigateOpts, ScreenshotOpts, ReadCanvasOpts, SelectOpts, WaitOpts } from "./options.ts";
|
|
5
5
|
import type { HeaderModification, RequestPattern } from "./network.ts";
|
|
6
6
|
import type { CookieParam } from "./cookies.ts";
|
|
7
7
|
import type { StorageOriginEntry } from "./storage.ts";
|
|
@@ -195,7 +195,10 @@ export declare class CloudBrowser {
|
|
|
195
195
|
*
|
|
196
196
|
* @returns WaitResult for the first matching condition
|
|
197
197
|
*
|
|
198
|
-
* @throws
|
|
198
|
+
* @throws {@link WaitError} - no condition matched before the deadline;
|
|
199
|
+
* `.conditions` holds the per-condition breakdown of why each never matched
|
|
200
|
+
* @throws {@link BrowserScaleError} - a condition was invalid, or a
|
|
201
|
+
* server/transport error occurred
|
|
199
202
|
*
|
|
200
203
|
* @example
|
|
201
204
|
* const r = await browser.waitAny(
|
|
@@ -223,15 +226,18 @@ export declare class CloudBrowser {
|
|
|
223
226
|
* post-scroll isVisible, element bounds, and the root-viewport
|
|
224
227
|
* (rootX, rootY) where the click landed
|
|
225
228
|
*
|
|
226
|
-
* @throws
|
|
227
|
-
*
|
|
228
|
-
*
|
|
229
|
-
* @throws
|
|
230
|
-
*
|
|
231
|
-
* @throws PAGE_NOT_ALIVE - the page has been closed
|
|
229
|
+
* @throws {@link ClickError} - the target was found but the click could not
|
|
230
|
+
* land because another element covered it; `.code`, `.occluder` and
|
|
231
|
+
* `.result` describe the blocker and the resolved coordinates
|
|
232
|
+
* @throws {@link BrowserScaleError} - invalid locator, or a server/transport
|
|
233
|
+
* error (element not found, frame not found, timeout, page closed)
|
|
232
234
|
*
|
|
233
235
|
* @example
|
|
234
|
-
*
|
|
236
|
+
* try {
|
|
237
|
+
* await browser.click(css("button.submit"));
|
|
238
|
+
* } catch (e) {
|
|
239
|
+
* if (e instanceof ClickError) console.log(e.code, e.occluder?.tagName);
|
|
240
|
+
* }
|
|
235
241
|
*
|
|
236
242
|
* @example
|
|
237
243
|
* // Right double-click on a context menu trigger.
|
|
@@ -257,12 +263,10 @@ export declare class CloudBrowser {
|
|
|
257
263
|
* @returns ElementResult with success, resolved frameId, backendNodeId
|
|
258
264
|
* and the root-viewport (rootX, rootY) where the element was clicked
|
|
259
265
|
*
|
|
260
|
-
* @throws
|
|
261
|
-
*
|
|
262
|
-
* @throws
|
|
263
|
-
*
|
|
264
|
-
* @throws TIMEOUT - the operation exceeded the server-side timeout
|
|
265
|
-
* @throws PAGE_NOT_ALIVE - the page has been closed
|
|
266
|
+
* @throws {@link FillError} - the field could not be focused/typed; `.code`
|
|
267
|
+
* and `.clickError` (the underlying click-core failure) describe why
|
|
268
|
+
* @throws {@link BrowserScaleError} - invalid locator, or a server/transport
|
|
269
|
+
* error (element not found, frame not found, timeout, page closed)
|
|
266
270
|
*
|
|
267
271
|
* @example
|
|
268
272
|
* await browser.fill(css("input[name=email]"), "user@example.com");
|
|
@@ -285,7 +289,9 @@ export declare class CloudBrowser {
|
|
|
285
289
|
* post-scroll isVisible, element bounds and the root-viewport
|
|
286
290
|
* (rootX, rootY) where the cursor ended up
|
|
287
291
|
*
|
|
288
|
-
* @throws
|
|
292
|
+
* @throws {@link MoveError} - the target could not be located (`.code` is
|
|
293
|
+
* `"not_found"`); `.result` carries the resolved payload
|
|
294
|
+
* @throws {@link BrowserScaleError} - invalid locator or a server/transport error
|
|
289
295
|
*
|
|
290
296
|
* @example
|
|
291
297
|
* await browser.moveTo(css("nav .menu"));
|
|
@@ -305,7 +311,9 @@ export declare class CloudBrowser {
|
|
|
305
311
|
* @returns ElementResult with the resolved frameId, backendNodeId,
|
|
306
312
|
* post-scroll isVisible and the element's bounds after the scroll
|
|
307
313
|
*
|
|
308
|
-
* @throws
|
|
314
|
+
* @throws {@link ScrollError} - the target could not be located/scrolled
|
|
315
|
+
* (`.code` is `"not_found"`); `.result` carries the resolved payload
|
|
316
|
+
* @throws {@link BrowserScaleError} - invalid locator or a server/transport error
|
|
309
317
|
*
|
|
310
318
|
* @example
|
|
311
319
|
* await browser.scrollTo(css("#footer"));
|
|
@@ -327,7 +335,9 @@ export declare class CloudBrowser {
|
|
|
327
335
|
* @returns DragResult with the resolved frameId, backendNodeId and the
|
|
328
336
|
* final cursor position (rootX, rootY) where the drop happened
|
|
329
337
|
*
|
|
330
|
-
* @throws
|
|
338
|
+
* @throws {@link DragError} - the source could not be acquired/pressed;
|
|
339
|
+
* `.code` and `.clickError` describe the underlying click-core failure
|
|
340
|
+
* @throws {@link BrowserScaleError} - invalid locator or a server/transport error
|
|
331
341
|
*
|
|
332
342
|
* @example
|
|
333
343
|
* await browser.dragBy(css(".slider .handle"), 120, 0);
|
|
@@ -346,7 +356,9 @@ export declare class CloudBrowser {
|
|
|
346
356
|
* @returns DragResult with the resolved frameId, backendNodeId and the
|
|
347
357
|
* final cursor position (rootX, rootY) where the drop happened
|
|
348
358
|
*
|
|
349
|
-
* @throws
|
|
359
|
+
* @throws {@link DragError} - the source could not be acquired/pressed;
|
|
360
|
+
* `.code` and `.clickError` describe the underlying click-core failure
|
|
361
|
+
* @throws {@link BrowserScaleError} - invalid locator or a server/transport error
|
|
350
362
|
*
|
|
351
363
|
* @example
|
|
352
364
|
* await browser.dragTo(css(".card"), 800, 400);
|
|
@@ -371,13 +383,10 @@ export declare class CloudBrowser {
|
|
|
371
383
|
* @returns SelectOptionResult with the resolved selectedIndex,
|
|
372
384
|
* selectedValue and selectedText after the change
|
|
373
385
|
*
|
|
374
|
-
* @throws
|
|
375
|
-
*
|
|
376
|
-
* @throws
|
|
377
|
-
*
|
|
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
|
|
386
|
+
* @throws {@link SelectOptionError} - the option could not be selected;
|
|
387
|
+
* `.code` is `"not_found"` (no `<select>`) or `"option_not_found"`
|
|
388
|
+
* @throws {@link BrowserScaleError} - invalid locator, or a server/transport
|
|
389
|
+
* error (frame not found, timeout, page closed)
|
|
381
390
|
*
|
|
382
391
|
* @example
|
|
383
392
|
* await browser.selectByIndex(css("select#country"), 2);
|
|
@@ -495,6 +504,40 @@ export declare class CloudBrowser {
|
|
|
495
504
|
* await fs.writeFile("page.png", Buffer.from(shot.dataBase64, "base64"));
|
|
496
505
|
*/
|
|
497
506
|
screenshot(opts?: ScreenshotOpts): Promise<ScreenshotResult>;
|
|
507
|
+
/**
|
|
508
|
+
* Reads the pixels of a <canvas> element directly in the renderer,
|
|
509
|
+
* bypassing the origin-clean (tainted) security check and without executing
|
|
510
|
+
* any page JavaScript — so cross-origin/tainted canvases (common in
|
|
511
|
+
* captchas) read fine where a normal `toDataURL` / `getImageData` would
|
|
512
|
+
* throw a SecurityError.
|
|
513
|
+
*
|
|
514
|
+
* {@link at} is not a valid target — a real <canvas> element is required.
|
|
515
|
+
*
|
|
516
|
+
* @param target - locator for the <canvas>; {@link css}, {@link js} or {@link node}
|
|
517
|
+
* @param opts - optional `format`, `quality`, sub-rectangle and frame
|
|
518
|
+
* override; see {@link ReadCanvasOpts}
|
|
519
|
+
*
|
|
520
|
+
* @returns ReadCanvasResult with the base64 image in `dataBase64`, the
|
|
521
|
+
* canvas `width`/`height`, resolved `frameId`/`backendNodeId` and the
|
|
522
|
+
* `originClean` flag
|
|
523
|
+
*
|
|
524
|
+
* @throws INVALID_LOCATOR - target is empty, uses at(x,y), or has multiple targets
|
|
525
|
+
* @throws ELEMENT_NOT_FOUND - no element matched the locator
|
|
526
|
+
* @throws FRAME_NOT_FOUND - the requested frame does not exist
|
|
527
|
+
* @throws TIMEOUT - the operation exceeded the server-side timeout
|
|
528
|
+
* @throws PAGE_NOT_ALIVE - the page has been closed
|
|
529
|
+
*
|
|
530
|
+
* @example
|
|
531
|
+
* const res = await browser.readCanvas(css("#game canvas"));
|
|
532
|
+
* await fs.writeFile("canvas.png", Buffer.from(res.dataBase64, "base64"));
|
|
533
|
+
*
|
|
534
|
+
* @example
|
|
535
|
+
* // Read the left half as JPEG at quality 80.
|
|
536
|
+
* const res = await browser.readCanvas(css("canvas"), {
|
|
537
|
+
* format: "jpeg", quality: 80, sw: 150, sh: 300,
|
|
538
|
+
* });
|
|
539
|
+
*/
|
|
540
|
+
readCanvas(target: Locator, opts?: ReadCanvasOpts): Promise<ReadCanvasResult>;
|
|
498
541
|
/**
|
|
499
542
|
* Replaces the session's URL blocklist.
|
|
500
543
|
*
|
package/dist/client.js
CHANGED
|
@@ -2,10 +2,10 @@ import { createClient } from "@connectrpc/connect";
|
|
|
2
2
|
import { create } from "@bufbuild/protobuf";
|
|
3
3
|
import { Browser,
|
|
4
4
|
// request / response schemas
|
|
5
|
-
SetProxyRequestSchema, GetPagesRequestSchema, NavigateRequestSchema, LoadHTMLRequestSchema, EvaluateRequestSchema, WaitForAnyParamsSchema, WaitConditionSchema, ClickRequestSchema, FillRequestSchema, MoveToRequestSchema, ScrollToRequestSchema, DragRequestSchema, SelectOptionRequestSchema, GetDOMRequestSchema, GetDOMHashRequestSchema, GetObservationRequestSchema, ScreenshotRequestSchema, SetBlockListRequestSchema, SetStaticPathsRequestSchema, WaitForAnyRequestRequestSchema, WaitForAnyResponseRequestSchema, ModifyRequestRequestSchema, GetCookiesRequestSchema, SetCookiesRequestSchema, ClearCookiesRequestSchema, GetStorageRequestSchema, SetStorageRequestSchema, ClearStorageRequestSchema, InspectAtPositionRequestSchema, HighlightNodeRequestSchema, InsertTextRequestSchema, PressKeyRequestSchema, ReleaseKeyRequestSchema, GetSelectionRequestSchema, SolveCaptchaRequestSchema, } from "./gen/wrc_pb.js";
|
|
5
|
+
SetProxyRequestSchema, GetPagesRequestSchema, NavigateRequestSchema, LoadHTMLRequestSchema, EvaluateRequestSchema, WaitForAnyParamsSchema, WaitConditionSchema, ClickRequestSchema, FillRequestSchema, MoveToRequestSchema, ScrollToRequestSchema, DragRequestSchema, SelectOptionRequestSchema, GetDOMRequestSchema, GetDOMHashRequestSchema, GetObservationRequestSchema, ScreenshotRequestSchema, ReadCanvasRequestSchema, SetBlockListRequestSchema, SetStaticPathsRequestSchema, WaitForAnyRequestRequestSchema, WaitForAnyResponseRequestSchema, ModifyRequestRequestSchema, GetCookiesRequestSchema, SetCookiesRequestSchema, ClearCookiesRequestSchema, GetStorageRequestSchema, SetStorageRequestSchema, ClearStorageRequestSchema, InspectAtPositionRequestSchema, HighlightNodeRequestSchema, InsertTextRequestSchema, PressKeyRequestSchema, ReleaseKeyRequestSchema, GetSelectionRequestSchema, SolveCaptchaRequestSchema, } from "./gen/wrc_pb.js";
|
|
6
6
|
import { DefaultWaitTimeoutMs } from "./defaults.js";
|
|
7
7
|
import { BrowserScaleError } from "./errors.js";
|
|
8
|
-
import { cookieParamFromProto, cookieParamsToProto,
|
|
8
|
+
import { cookieParamFromProto, cookieParamsToProto, elementFields, headerModsToProto, headersToProto, interceptedRequestFromProto, interceptedResponseFromProto, pageInfoFromProto, rectFromProto, splitRequestPatterns, storageEntriesToProto, storageEntryFromProto, unwrapClick, unwrapDrag, unwrapFill, unwrapMove, unwrapScroll, unwrapSelect, unwrapWait, } from "./internal/convert.js";
|
|
9
9
|
/**
|
|
10
10
|
* CloudBrowser is the SDK-side handle for an active browserscale browser session.
|
|
11
11
|
*
|
|
@@ -293,7 +293,10 @@ export class CloudBrowser {
|
|
|
293
293
|
*
|
|
294
294
|
* @returns WaitResult for the first matching condition
|
|
295
295
|
*
|
|
296
|
-
* @throws
|
|
296
|
+
* @throws {@link WaitError} - no condition matched before the deadline;
|
|
297
|
+
* `.conditions` holds the per-condition breakdown of why each never matched
|
|
298
|
+
* @throws {@link BrowserScaleError} - a condition was invalid, or a
|
|
299
|
+
* server/transport error occurred
|
|
297
300
|
*
|
|
298
301
|
* @example
|
|
299
302
|
* const r = await browser.waitAny(
|
|
@@ -338,8 +341,7 @@ export class CloudBrowser {
|
|
|
338
341
|
});
|
|
339
342
|
if (frameId)
|
|
340
343
|
req.frameId = frameId;
|
|
341
|
-
|
|
342
|
-
return waitResultFromProto(resp);
|
|
344
|
+
return unwrapWait(await this.client.waitForAny(req));
|
|
343
345
|
}
|
|
344
346
|
// ──────────────────────────────────────────────────────────────────
|
|
345
347
|
// Element actions
|
|
@@ -362,15 +364,18 @@ export class CloudBrowser {
|
|
|
362
364
|
* post-scroll isVisible, element bounds, and the root-viewport
|
|
363
365
|
* (rootX, rootY) where the click landed
|
|
364
366
|
*
|
|
365
|
-
* @throws
|
|
366
|
-
*
|
|
367
|
-
*
|
|
368
|
-
* @throws
|
|
369
|
-
*
|
|
370
|
-
* @throws PAGE_NOT_ALIVE - the page has been closed
|
|
367
|
+
* @throws {@link ClickError} - the target was found but the click could not
|
|
368
|
+
* land because another element covered it; `.code`, `.occluder` and
|
|
369
|
+
* `.result` describe the blocker and the resolved coordinates
|
|
370
|
+
* @throws {@link BrowserScaleError} - invalid locator, or a server/transport
|
|
371
|
+
* error (element not found, frame not found, timeout, page closed)
|
|
371
372
|
*
|
|
372
373
|
* @example
|
|
373
|
-
*
|
|
374
|
+
* try {
|
|
375
|
+
* await browser.click(css("button.submit"));
|
|
376
|
+
* } catch (e) {
|
|
377
|
+
* if (e instanceof ClickError) console.log(e.code, e.occluder?.tagName);
|
|
378
|
+
* }
|
|
374
379
|
*
|
|
375
380
|
* @example
|
|
376
381
|
* // Right double-click on a context menu trigger.
|
|
@@ -389,8 +394,7 @@ export class CloudBrowser {
|
|
|
389
394
|
req.clickCount = opts.clickCount;
|
|
390
395
|
if (opts?.action)
|
|
391
396
|
req.action = opts.action;
|
|
392
|
-
|
|
393
|
-
return elementResultFromProto(resp);
|
|
397
|
+
return unwrapClick(await this.client.click(req));
|
|
394
398
|
}
|
|
395
399
|
/**
|
|
396
400
|
* Clicks the target and types text into it, appending to any existing
|
|
@@ -411,12 +415,10 @@ export class CloudBrowser {
|
|
|
411
415
|
* @returns ElementResult with success, resolved frameId, backendNodeId
|
|
412
416
|
* and the root-viewport (rootX, rootY) where the element was clicked
|
|
413
417
|
*
|
|
414
|
-
* @throws
|
|
415
|
-
*
|
|
416
|
-
* @throws
|
|
417
|
-
*
|
|
418
|
-
* @throws TIMEOUT - the operation exceeded the server-side timeout
|
|
419
|
-
* @throws PAGE_NOT_ALIVE - the page has been closed
|
|
418
|
+
* @throws {@link FillError} - the field could not be focused/typed; `.code`
|
|
419
|
+
* and `.clickError` (the underlying click-core failure) describe why
|
|
420
|
+
* @throws {@link BrowserScaleError} - invalid locator, or a server/transport
|
|
421
|
+
* error (element not found, frame not found, timeout, page closed)
|
|
420
422
|
*
|
|
421
423
|
* @example
|
|
422
424
|
* await browser.fill(css("input[name=email]"), "user@example.com");
|
|
@@ -435,8 +437,7 @@ export class CloudBrowser {
|
|
|
435
437
|
});
|
|
436
438
|
if (opts?.clearFirst)
|
|
437
439
|
req.clearFirst = true;
|
|
438
|
-
|
|
439
|
-
return elementResultFromProto(resp);
|
|
440
|
+
return unwrapFill(await this.client.fill(req));
|
|
440
441
|
}
|
|
441
442
|
/**
|
|
442
443
|
* Moves the mouse cursor over the given target.
|
|
@@ -451,7 +452,9 @@ export class CloudBrowser {
|
|
|
451
452
|
* post-scroll isVisible, element bounds and the root-viewport
|
|
452
453
|
* (rootX, rootY) where the cursor ended up
|
|
453
454
|
*
|
|
454
|
-
* @throws
|
|
455
|
+
* @throws {@link MoveError} - the target could not be located (`.code` is
|
|
456
|
+
* `"not_found"`); `.result` carries the resolved payload
|
|
457
|
+
* @throws {@link BrowserScaleError} - invalid locator or a server/transport error
|
|
455
458
|
*
|
|
456
459
|
* @example
|
|
457
460
|
* await browser.moveTo(css("nav .menu"));
|
|
@@ -463,8 +466,7 @@ export class CloudBrowser {
|
|
|
463
466
|
apiKey: this.apiKey,
|
|
464
467
|
...elementFields(target),
|
|
465
468
|
});
|
|
466
|
-
|
|
467
|
-
return elementResultFromProto(resp);
|
|
469
|
+
return unwrapMove(await this.client.moveTo(req));
|
|
468
470
|
}
|
|
469
471
|
/**
|
|
470
472
|
* Scrolls the given element into view.
|
|
@@ -480,7 +482,9 @@ export class CloudBrowser {
|
|
|
480
482
|
* @returns ElementResult with the resolved frameId, backendNodeId,
|
|
481
483
|
* post-scroll isVisible and the element's bounds after the scroll
|
|
482
484
|
*
|
|
483
|
-
* @throws
|
|
485
|
+
* @throws {@link ScrollError} - the target could not be located/scrolled
|
|
486
|
+
* (`.code` is `"not_found"`); `.result` carries the resolved payload
|
|
487
|
+
* @throws {@link BrowserScaleError} - invalid locator or a server/transport error
|
|
484
488
|
*
|
|
485
489
|
* @example
|
|
486
490
|
* await browser.scrollTo(css("#footer"));
|
|
@@ -492,8 +496,7 @@ export class CloudBrowser {
|
|
|
492
496
|
apiKey: this.apiKey,
|
|
493
497
|
...elementFields(target),
|
|
494
498
|
});
|
|
495
|
-
|
|
496
|
-
return elementResultFromProto(resp);
|
|
499
|
+
return unwrapScroll(await this.client.scrollTo(req));
|
|
497
500
|
}
|
|
498
501
|
/**
|
|
499
502
|
* Picks up the target and drops it at an offset relative to the pickup
|
|
@@ -511,7 +514,9 @@ export class CloudBrowser {
|
|
|
511
514
|
* @returns DragResult with the resolved frameId, backendNodeId and the
|
|
512
515
|
* final cursor position (rootX, rootY) where the drop happened
|
|
513
516
|
*
|
|
514
|
-
* @throws
|
|
517
|
+
* @throws {@link DragError} - the source could not be acquired/pressed;
|
|
518
|
+
* `.code` and `.clickError` describe the underlying click-core failure
|
|
519
|
+
* @throws {@link BrowserScaleError} - invalid locator or a server/transport error
|
|
515
520
|
*
|
|
516
521
|
* @example
|
|
517
522
|
* await browser.dragBy(css(".slider .handle"), 120, 0);
|
|
@@ -532,7 +537,9 @@ export class CloudBrowser {
|
|
|
532
537
|
* @returns DragResult with the resolved frameId, backendNodeId and the
|
|
533
538
|
* final cursor position (rootX, rootY) where the drop happened
|
|
534
539
|
*
|
|
535
|
-
* @throws
|
|
540
|
+
* @throws {@link DragError} - the source could not be acquired/pressed;
|
|
541
|
+
* `.code` and `.clickError` describe the underlying click-core failure
|
|
542
|
+
* @throws {@link BrowserScaleError} - invalid locator or a server/transport error
|
|
536
543
|
*
|
|
537
544
|
* @example
|
|
538
545
|
* await browser.dragTo(css(".card"), 800, 400);
|
|
@@ -555,8 +562,7 @@ export class CloudBrowser {
|
|
|
555
562
|
req.absoluteX = spec.absoluteX;
|
|
556
563
|
if (spec.absoluteY !== undefined)
|
|
557
564
|
req.absoluteY = spec.absoluteY;
|
|
558
|
-
|
|
559
|
-
return dragResultFromProto(resp);
|
|
565
|
+
return unwrapDrag(await this.client.drag(req));
|
|
560
566
|
}
|
|
561
567
|
/**
|
|
562
568
|
* Picks the `<option>` at the zero-based index inside the targeted
|
|
@@ -576,13 +582,10 @@ export class CloudBrowser {
|
|
|
576
582
|
* @returns SelectOptionResult with the resolved selectedIndex,
|
|
577
583
|
* selectedValue and selectedText after the change
|
|
578
584
|
*
|
|
579
|
-
* @throws
|
|
580
|
-
*
|
|
581
|
-
* @throws
|
|
582
|
-
*
|
|
583
|
-
* (out of range, or element is not a `<select>`)
|
|
584
|
-
* @throws TIMEOUT - the operation exceeded the server-side timeout
|
|
585
|
-
* @throws PAGE_NOT_ALIVE - the page has been closed
|
|
585
|
+
* @throws {@link SelectOptionError} - the option could not be selected;
|
|
586
|
+
* `.code` is `"not_found"` (no `<select>`) or `"option_not_found"`
|
|
587
|
+
* @throws {@link BrowserScaleError} - invalid locator, or a server/transport
|
|
588
|
+
* error (frame not found, timeout, page closed)
|
|
586
589
|
*
|
|
587
590
|
* @example
|
|
588
591
|
* await browser.selectByIndex(css("select#country"), 2);
|
|
@@ -630,12 +633,7 @@ export class CloudBrowser {
|
|
|
630
633
|
withKey(req);
|
|
631
634
|
if (opts?.fireEvents === false)
|
|
632
635
|
req.fireEvents = false;
|
|
633
|
-
|
|
634
|
-
return {
|
|
635
|
-
selectedIndex: resp.selectedIndex,
|
|
636
|
-
selectedValue: resp.selectedValue,
|
|
637
|
-
selectedText: resp.selectedText,
|
|
638
|
-
};
|
|
636
|
+
return unwrapSelect(await this.client.selectOption(req));
|
|
639
637
|
}
|
|
640
638
|
// ──────────────────────────────────────────────────────────────────
|
|
641
639
|
// DOM / observation
|
|
@@ -772,6 +770,69 @@ export class CloudBrowser {
|
|
|
772
770
|
height: resp.height,
|
|
773
771
|
};
|
|
774
772
|
}
|
|
773
|
+
/**
|
|
774
|
+
* Reads the pixels of a <canvas> element directly in the renderer,
|
|
775
|
+
* bypassing the origin-clean (tainted) security check and without executing
|
|
776
|
+
* any page JavaScript — so cross-origin/tainted canvases (common in
|
|
777
|
+
* captchas) read fine where a normal `toDataURL` / `getImageData` would
|
|
778
|
+
* throw a SecurityError.
|
|
779
|
+
*
|
|
780
|
+
* {@link at} is not a valid target — a real <canvas> element is required.
|
|
781
|
+
*
|
|
782
|
+
* @param target - locator for the <canvas>; {@link css}, {@link js} or {@link node}
|
|
783
|
+
* @param opts - optional `format`, `quality`, sub-rectangle and frame
|
|
784
|
+
* override; see {@link ReadCanvasOpts}
|
|
785
|
+
*
|
|
786
|
+
* @returns ReadCanvasResult with the base64 image in `dataBase64`, the
|
|
787
|
+
* canvas `width`/`height`, resolved `frameId`/`backendNodeId` and the
|
|
788
|
+
* `originClean` flag
|
|
789
|
+
*
|
|
790
|
+
* @throws INVALID_LOCATOR - target is empty, uses at(x,y), or has multiple targets
|
|
791
|
+
* @throws ELEMENT_NOT_FOUND - no element matched the locator
|
|
792
|
+
* @throws FRAME_NOT_FOUND - the requested frame does not exist
|
|
793
|
+
* @throws TIMEOUT - the operation exceeded the server-side timeout
|
|
794
|
+
* @throws PAGE_NOT_ALIVE - the page has been closed
|
|
795
|
+
*
|
|
796
|
+
* @example
|
|
797
|
+
* const res = await browser.readCanvas(css("#game canvas"));
|
|
798
|
+
* await fs.writeFile("canvas.png", Buffer.from(res.dataBase64, "base64"));
|
|
799
|
+
*
|
|
800
|
+
* @example
|
|
801
|
+
* // Read the left half as JPEG at quality 80.
|
|
802
|
+
* const res = await browser.readCanvas(css("canvas"), {
|
|
803
|
+
* format: "jpeg", quality: 80, sw: 150, sh: 300,
|
|
804
|
+
* });
|
|
805
|
+
*/
|
|
806
|
+
async readCanvas(target, opts) {
|
|
807
|
+
// A real <canvas> is required — at(x,y) coordinates are not valid here.
|
|
808
|
+
target.validateTarget("readCanvas", false);
|
|
809
|
+
const req = create(ReadCanvasRequestSchema, {
|
|
810
|
+
sessionId: this.sessionId,
|
|
811
|
+
apiKey: this.apiKey,
|
|
812
|
+
...elementFields(target, opts?.inFrame),
|
|
813
|
+
});
|
|
814
|
+
if (opts?.format !== undefined)
|
|
815
|
+
req.format = opts.format;
|
|
816
|
+
if (opts?.quality !== undefined)
|
|
817
|
+
req.quality = opts.quality;
|
|
818
|
+
if (opts?.sw !== undefined && opts?.sh !== undefined &&
|
|
819
|
+
opts.sw > 0 && opts.sh > 0) {
|
|
820
|
+
req.sx = opts.sx ?? 0;
|
|
821
|
+
req.sy = opts.sy ?? 0;
|
|
822
|
+
req.sw = opts.sw;
|
|
823
|
+
req.sh = opts.sh;
|
|
824
|
+
}
|
|
825
|
+
const resp = await this.client.readCanvas(req);
|
|
826
|
+
return {
|
|
827
|
+
success: resp.success,
|
|
828
|
+
frameId: resp.frameId,
|
|
829
|
+
backendNodeId: resp.backendNodeId,
|
|
830
|
+
dataBase64: resp.dataBase64,
|
|
831
|
+
width: resp.width,
|
|
832
|
+
height: resp.height,
|
|
833
|
+
originClean: resp.originClean,
|
|
834
|
+
};
|
|
835
|
+
}
|
|
775
836
|
// ──────────────────────────────────────────────────────────────────
|
|
776
837
|
// Network interception
|
|
777
838
|
// ──────────────────────────────────────────────────────────────────
|
package/dist/errors.d.ts
CHANGED
|
@@ -1,18 +1,167 @@
|
|
|
1
|
+
import type { DragResult, ElementResult, OccluderInfo, SelectOptionResult, WaitConditionStatus, WaitResult } from "./types.ts";
|
|
1
2
|
/**
|
|
2
|
-
* BrowserScaleError is the
|
|
3
|
-
* either:
|
|
3
|
+
* BrowserScaleError is the base class for every error thrown by the SDK. It
|
|
4
|
+
* wraps either:
|
|
4
5
|
* - a client-side validation failure (bad locator, missing patterns, …)
|
|
5
6
|
* - a server-side gRPC error (Connect's ConnectError, available as `cause`)
|
|
6
7
|
*
|
|
7
|
-
*
|
|
8
|
+
* Semantic action failures (an occluded click, a wait timeout, an option that
|
|
9
|
+
* did not exist, …) are thrown as the typed subclasses below, each carrying the
|
|
10
|
+
* same structured detail the Go SDK exposes via `errors.As` — plus the partial
|
|
11
|
+
* result of the attempted action on `.result`, so a single `catch` gives you
|
|
12
|
+
* both the diagnostics and the resolved coordinates.
|
|
13
|
+
*
|
|
14
|
+
* Catch the base for anything, or narrow to a subclass for the detail:
|
|
8
15
|
*
|
|
9
16
|
* try {
|
|
10
17
|
* await browser.click(css("#btn"));
|
|
11
18
|
* } catch (e) {
|
|
12
|
-
* if (e instanceof
|
|
19
|
+
* if (e instanceof ClickError) console.log(e.code, e.occluder?.tagName);
|
|
20
|
+
* else if (e instanceof BrowserScaleError) { ... }
|
|
13
21
|
* }
|
|
14
22
|
*/
|
|
15
23
|
export declare class BrowserScaleError extends Error {
|
|
16
24
|
readonly cause?: unknown | undefined;
|
|
17
25
|
constructor(message: string, cause?: unknown | undefined);
|
|
18
26
|
}
|
|
27
|
+
/**
|
|
28
|
+
* ClickError is thrown by {@link CloudBrowser.click} when the click did not
|
|
29
|
+
* land — the target was found but another element covered the intended point.
|
|
30
|
+
* `occluder` describes the blocker; `result` carries the resolved element and
|
|
31
|
+
* coordinates (success is false).
|
|
32
|
+
*
|
|
33
|
+
* It is also nested under {@link FillError} / {@link DragError} as the
|
|
34
|
+
* underlying click-core failure; in that nested form `result` is undefined
|
|
35
|
+
* (the partial result lives on the outer error).
|
|
36
|
+
*/
|
|
37
|
+
export declare class ClickError extends BrowserScaleError {
|
|
38
|
+
/**
|
|
39
|
+
* Machine-stable failure code, e.g. `"occluded_no_reachable_point"` (target
|
|
40
|
+
* fully covered, no exposed part reachable), `"occluded_after_evade"` (a
|
|
41
|
+
* reposition was tried but the target was still covered) or `"not_found"`.
|
|
42
|
+
*/
|
|
43
|
+
readonly code: string;
|
|
44
|
+
/** The intercepting element (present for occlusion codes). */
|
|
45
|
+
readonly occluder?: OccluderInfo;
|
|
46
|
+
/** Whether a pointer reposition was tried before giving up. */
|
|
47
|
+
readonly evadeAttempted: boolean;
|
|
48
|
+
/**
|
|
49
|
+
* Resolved element + coordinates at the failed action. Present when this is
|
|
50
|
+
* the thrown top-level error; undefined when nested inside another error.
|
|
51
|
+
*/
|
|
52
|
+
readonly result?: ElementResult;
|
|
53
|
+
constructor(init: {
|
|
54
|
+
code: string;
|
|
55
|
+
message: string;
|
|
56
|
+
occluder?: OccluderInfo;
|
|
57
|
+
evadeAttempted?: boolean;
|
|
58
|
+
result?: ElementResult;
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* FillError is thrown by {@link CloudBrowser.fill} when the field could not be
|
|
63
|
+
* focused/typed. Fill focuses with the exact same smart click as
|
|
64
|
+
* {@link CloudBrowser.click}, so a pre-typing failure is a click failure:
|
|
65
|
+
* `code` mirrors it and the full click diagnostics live under `clickError`.
|
|
66
|
+
*/
|
|
67
|
+
export declare class FillError extends BrowserScaleError {
|
|
68
|
+
/** Mirrored from the underlying click failure. */
|
|
69
|
+
readonly code: string;
|
|
70
|
+
/** The underlying click-core failure that prevented focusing/typing. */
|
|
71
|
+
readonly clickError?: ClickError;
|
|
72
|
+
/** Resolved element + coordinates at the failed action (success is false). */
|
|
73
|
+
readonly result: ElementResult;
|
|
74
|
+
constructor(init: {
|
|
75
|
+
code: string;
|
|
76
|
+
message: string;
|
|
77
|
+
clickError?: ClickError;
|
|
78
|
+
result: ElementResult;
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* DragError is thrown by {@link CloudBrowser.dragBy} / {@link CloudBrowser.dragTo}
|
|
83
|
+
* when the source element could not be acquired/pressed. Drag picks up the
|
|
84
|
+
* source with the same smart click as {@link CloudBrowser.click}, so a pre-drag
|
|
85
|
+
* failure is a click failure: `code` mirrors it and the full click diagnostics
|
|
86
|
+
* live under `clickError`.
|
|
87
|
+
*/
|
|
88
|
+
export declare class DragError extends BrowserScaleError {
|
|
89
|
+
readonly code: string;
|
|
90
|
+
readonly clickError?: ClickError;
|
|
91
|
+
/** Resolved source + coordinates at the failed drag (success is false). */
|
|
92
|
+
readonly result: DragResult;
|
|
93
|
+
constructor(init: {
|
|
94
|
+
code: string;
|
|
95
|
+
message: string;
|
|
96
|
+
clickError?: ClickError;
|
|
97
|
+
result: DragResult;
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* ScrollError is thrown by {@link CloudBrowser.scrollTo} when the target could
|
|
102
|
+
* not be located/scrolled.
|
|
103
|
+
*/
|
|
104
|
+
export declare class ScrollError extends BrowserScaleError {
|
|
105
|
+
/** Currently always `"not_found"`. */
|
|
106
|
+
readonly code: string;
|
|
107
|
+
readonly result: ElementResult;
|
|
108
|
+
constructor(init: {
|
|
109
|
+
code: string;
|
|
110
|
+
message: string;
|
|
111
|
+
result: ElementResult;
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* MoveError is thrown by {@link CloudBrowser.moveTo} when the target could not
|
|
116
|
+
* be located. A move has no occlusion notion, so this is the only semantic
|
|
117
|
+
* failure.
|
|
118
|
+
*/
|
|
119
|
+
export declare class MoveError extends BrowserScaleError {
|
|
120
|
+
/** Currently always `"not_found"`. */
|
|
121
|
+
readonly code: string;
|
|
122
|
+
readonly result: ElementResult;
|
|
123
|
+
constructor(init: {
|
|
124
|
+
code: string;
|
|
125
|
+
message: string;
|
|
126
|
+
result: ElementResult;
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* SelectOptionError is thrown by the {@link CloudBrowser.selectByIndex} /
|
|
131
|
+
* `selectByValue` / `selectByText` calls when the option could not be selected.
|
|
132
|
+
* selectOption is programmatic (no pointer gate), so it only reports semantic
|
|
133
|
+
* failures.
|
|
134
|
+
*/
|
|
135
|
+
export declare class SelectOptionError extends BrowserScaleError {
|
|
136
|
+
/**
|
|
137
|
+
* `"not_found"` (the `<select>` was not located) or `"option_not_found"` (no
|
|
138
|
+
* option matched the requested index/value/text).
|
|
139
|
+
*/
|
|
140
|
+
readonly code: string;
|
|
141
|
+
readonly result: SelectOptionResult;
|
|
142
|
+
constructor(init: {
|
|
143
|
+
code: string;
|
|
144
|
+
message: string;
|
|
145
|
+
result: SelectOptionResult;
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* WaitError is thrown by {@link CloudBrowser.wait} / {@link CloudBrowser.waitAny}
|
|
150
|
+
* when no condition matched before the deadline. `conditions` holds the
|
|
151
|
+
* per-condition breakdown (same order/length as the conditions passed in)
|
|
152
|
+
* explaining why each one never matched.
|
|
153
|
+
*/
|
|
154
|
+
export declare class WaitError extends BrowserScaleError {
|
|
155
|
+
/** Machine-stable failure code, currently always `"timeout"`. */
|
|
156
|
+
readonly code: string;
|
|
157
|
+
/** Per-condition status, same order/length as the conditions passed to wait. */
|
|
158
|
+
readonly conditions: WaitConditionStatus[];
|
|
159
|
+
/** The partial wait result (index is -1 on timeout). */
|
|
160
|
+
readonly result: WaitResult;
|
|
161
|
+
constructor(init: {
|
|
162
|
+
code: string;
|
|
163
|
+
message: string;
|
|
164
|
+
conditions: WaitConditionStatus[];
|
|
165
|
+
result: WaitResult;
|
|
166
|
+
});
|
|
167
|
+
}
|