browserscale-ts 1.1.0 → 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/client.d.ts CHANGED
@@ -195,7 +195,10 @@ export declare class CloudBrowser {
195
195
  *
196
196
  * @returns WaitResult for the first matching condition
197
197
  *
198
- * @throws UNKNOWN_ERROR - the wait timed out or a condition was invalid
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 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
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
- * await browser.click(css("button.submit"));
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 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
+ * @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 UNKNOWN_ERROR - the move could not be completed
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 UNKNOWN_ERROR - the element could not be scrolled into view
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 UNKNOWN_ERROR - the drag could not be performed
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 UNKNOWN_ERROR - the drag could not be performed
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 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
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);
package/dist/client.js CHANGED
@@ -5,7 +5,7 @@ import { Browser,
5
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, dragResultFromProto, elementFields, elementResultFromProto, headerModsToProto, headersToProto, interceptedRequestFromProto, interceptedResponseFromProto, pageInfoFromProto, rectFromProto, splitRequestPatterns, storageEntriesToProto, storageEntryFromProto, waitResultFromProto, } from "./internal/convert.js";
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 UNKNOWN_ERROR - the wait timed out or a condition was invalid
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
- const resp = await this.client.waitForAny(req);
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 INVALID_LOCATOR - target is empty or has multiple targets set
366
- * @throws ELEMENT_NOT_FOUND - no element matched the locator
367
- * @throws FRAME_NOT_FOUND - the requested frame does not exist
368
- * @throws CLICK_FAILED - the click could not be dispatched
369
- * @throws TIMEOUT - the operation exceeded the server-side timeout
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
- * await browser.click(css("button.submit"));
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
- const resp = await this.client.click(req);
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 INVALID_LOCATOR - target is empty or has multiple targets set
415
- * @throws ELEMENT_NOT_FOUND - no element matched the locator
416
- * @throws FRAME_NOT_FOUND - the requested frame does not exist
417
- * @throws FILL_FAILED - the input could not be filled
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
- const resp = await this.client.fill(req);
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 UNKNOWN_ERROR - the move could not be completed
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
- const resp = await this.client.moveTo(req);
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 UNKNOWN_ERROR - the element could not be scrolled into view
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
- const resp = await this.client.scrollTo(req);
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 UNKNOWN_ERROR - the drag could not be performed
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 UNKNOWN_ERROR - the drag could not be performed
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
- const resp = await this.client.drag(req);
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 INVALID_LOCATOR - target is empty or has multiple targets set
580
- * @throws ELEMENT_NOT_FOUND - no element matched the locator
581
- * @throws FRAME_NOT_FOUND - the requested frame does not exist
582
- * @throws SELECT_FAILED - the option could not be selected
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
- const resp = await this.client.selectOption(req);
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
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 single error type thrown by all SDK methods. It wraps
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
- * Catch it with `instanceof BrowserScaleError`:
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 BrowserScaleError) { ... }
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
+ }
package/dist/errors.js CHANGED
@@ -1,15 +1,22 @@
1
1
  /**
2
- * BrowserScaleError is the single error type thrown by all SDK methods. It wraps
3
- * either:
2
+ * BrowserScaleError is the base class for every error thrown by the SDK. It
3
+ * wraps either:
4
4
  * - a client-side validation failure (bad locator, missing patterns, …)
5
5
  * - a server-side gRPC error (Connect's ConnectError, available as `cause`)
6
6
  *
7
- * Catch it with `instanceof BrowserScaleError`:
7
+ * Semantic action failures (an occluded click, a wait timeout, an option that
8
+ * did not exist, …) are thrown as the typed subclasses below, each carrying the
9
+ * same structured detail the Go SDK exposes via `errors.As` — plus the partial
10
+ * result of the attempted action on `.result`, so a single `catch` gives you
11
+ * both the diagnostics and the resolved coordinates.
12
+ *
13
+ * Catch the base for anything, or narrow to a subclass for the detail:
8
14
  *
9
15
  * try {
10
16
  * await browser.click(css("#btn"));
11
17
  * } catch (e) {
12
- * if (e instanceof BrowserScaleError) { ... }
18
+ * if (e instanceof ClickError) console.log(e.code, e.occluder?.tagName);
19
+ * else if (e instanceof BrowserScaleError) { ... }
13
20
  * }
14
21
  */
15
22
  export class BrowserScaleError extends Error {
@@ -19,3 +26,112 @@ export class BrowserScaleError extends Error {
19
26
  this.name = "BrowserScaleError";
20
27
  }
21
28
  }
29
+ /**
30
+ * ClickError is thrown by {@link CloudBrowser.click} when the click did not
31
+ * land — the target was found but another element covered the intended point.
32
+ * `occluder` describes the blocker; `result` carries the resolved element and
33
+ * coordinates (success is false).
34
+ *
35
+ * It is also nested under {@link FillError} / {@link DragError} as the
36
+ * underlying click-core failure; in that nested form `result` is undefined
37
+ * (the partial result lives on the outer error).
38
+ */
39
+ export class ClickError extends BrowserScaleError {
40
+ constructor(init) {
41
+ super(formatMessage("click", init.code, init.message));
42
+ this.name = "ClickError";
43
+ this.code = init.code;
44
+ this.occluder = init.occluder;
45
+ this.evadeAttempted = init.evadeAttempted ?? false;
46
+ this.result = init.result;
47
+ }
48
+ }
49
+ /**
50
+ * FillError is thrown by {@link CloudBrowser.fill} when the field could not be
51
+ * focused/typed. Fill focuses with the exact same smart click as
52
+ * {@link CloudBrowser.click}, so a pre-typing failure is a click failure:
53
+ * `code` mirrors it and the full click diagnostics live under `clickError`.
54
+ */
55
+ export class FillError extends BrowserScaleError {
56
+ constructor(init) {
57
+ super(formatMessage("fill", init.code, init.message));
58
+ this.name = "FillError";
59
+ this.code = init.code;
60
+ this.clickError = init.clickError;
61
+ this.result = init.result;
62
+ }
63
+ }
64
+ /**
65
+ * DragError is thrown by {@link CloudBrowser.dragBy} / {@link CloudBrowser.dragTo}
66
+ * when the source element could not be acquired/pressed. Drag picks up the
67
+ * source with the same smart click as {@link CloudBrowser.click}, so a pre-drag
68
+ * failure is a click failure: `code` mirrors it and the full click diagnostics
69
+ * live under `clickError`.
70
+ */
71
+ export class DragError extends BrowserScaleError {
72
+ constructor(init) {
73
+ super(formatMessage("drag", init.code, init.message));
74
+ this.name = "DragError";
75
+ this.code = init.code;
76
+ this.clickError = init.clickError;
77
+ this.result = init.result;
78
+ }
79
+ }
80
+ /**
81
+ * ScrollError is thrown by {@link CloudBrowser.scrollTo} when the target could
82
+ * not be located/scrolled.
83
+ */
84
+ export class ScrollError extends BrowserScaleError {
85
+ constructor(init) {
86
+ super(formatMessage("scrollTo", init.code, init.message));
87
+ this.name = "ScrollError";
88
+ this.code = init.code;
89
+ this.result = init.result;
90
+ }
91
+ }
92
+ /**
93
+ * MoveError is thrown by {@link CloudBrowser.moveTo} when the target could not
94
+ * be located. A move has no occlusion notion, so this is the only semantic
95
+ * failure.
96
+ */
97
+ export class MoveError extends BrowserScaleError {
98
+ constructor(init) {
99
+ super(formatMessage("moveTo", init.code, init.message));
100
+ this.name = "MoveError";
101
+ this.code = init.code;
102
+ this.result = init.result;
103
+ }
104
+ }
105
+ /**
106
+ * SelectOptionError is thrown by the {@link CloudBrowser.selectByIndex} /
107
+ * `selectByValue` / `selectByText` calls when the option could not be selected.
108
+ * selectOption is programmatic (no pointer gate), so it only reports semantic
109
+ * failures.
110
+ */
111
+ export class SelectOptionError extends BrowserScaleError {
112
+ constructor(init) {
113
+ super(formatMessage("selectOption", init.code, init.message));
114
+ this.name = "SelectOptionError";
115
+ this.code = init.code;
116
+ this.result = init.result;
117
+ }
118
+ }
119
+ /**
120
+ * WaitError is thrown by {@link CloudBrowser.wait} / {@link CloudBrowser.waitAny}
121
+ * when no condition matched before the deadline. `conditions` holds the
122
+ * per-condition breakdown (same order/length as the conditions passed in)
123
+ * explaining why each one never matched.
124
+ */
125
+ export class WaitError extends BrowserScaleError {
126
+ constructor(init) {
127
+ super(formatMessage("wait", init.code, init.message));
128
+ this.name = "WaitError";
129
+ this.code = init.code;
130
+ this.conditions = init.conditions;
131
+ this.result = init.result;
132
+ }
133
+ }
134
+ function formatMessage(action, code, message) {
135
+ const base = `${action} failed: ${code}`;
136
+ return message ? `${base}: ${message}` : base;
137
+ }