browserscale-ts 1.7.1 → 1.9.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/README.md +137 -81
- package/dist/browser.d.ts +1 -1
- package/dist/browser.js +1 -1
- package/dist/browserscale.browser.js +533 -216
- package/dist/client.d.ts +266 -60
- package/dist/client.js +348 -81
- package/dist/defaults.d.ts +11 -9
- package/dist/defaults.js +15 -12
- package/dist/dom-mirror.d.ts +27 -0
- package/dist/dom-mirror.js +27 -0
- package/dist/errors.d.ts +43 -0
- package/dist/errors.js +37 -0
- package/dist/gen/wrc_pb.d.ts +466 -91
- package/dist/gen/wrc_pb.js +145 -135
- package/dist/index.d.ts +15 -9
- package/dist/index.js +17 -13
- package/dist/internal/convert.d.ts +4 -2
- package/dist/internal/convert.js +28 -0
- package/dist/locator.d.ts +12 -11
- package/dist/locator.js +14 -22
- package/dist/network-capture.d.ts +3 -2
- package/dist/network-capture.js +3 -2
- package/dist/options.d.ts +1 -1
- package/dist/scripts.d.ts +4 -3
- package/dist/scripts.js +4 -3
- package/dist/types.d.ts +52 -0
- package/package.json +1 -1
package/dist/client.js
CHANGED
|
@@ -2,20 +2,17 @@ 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, ReadCanvasRequestSchema, SetBlockListRequestSchema, SetStaticPathsRequestSchema, WaitForAnyRequestRequestSchema, WaitForAnyResponseRequestSchema, ModifyRequestRequestSchema, GetCookiesRequestSchema, SetCookiesRequestSchema, ClearCookiesRequestSchema, GetStorageRequestSchema, SetStorageRequestSchema, ClearStorageRequestSchema, GetAuthSessionRequestSchema, SetAuthSessionRequestSchema, InspectAtPositionRequestSchema, HighlightNodeRequestSchema, InsertTextRequestSchema, TypeRequestSchema, PressKeyRequestSchema, ReleaseKeyRequestSchema, GetSelectionRequestSchema, SolveCaptchaRequestSchema, GetStreamConfigRequestSchema, StartStreamRequestSchema, StopStreamRequestSchema, AddReactionRequestSchema, RemoveReactionRequestSchema, ListReactionsRequestSchema, StartNetworkCaptureRequestSchema, StopNetworkCaptureRequestSchema, StartDomMirrorRequestSchema, StopDomMirrorRequestSchema, GetDomChildrenRequestSchema, ReleaseDomSubtreeRequestSchema, RevealDomNodeRequestSchema, GetDomRevisionRequestSchema, StreamDomEventsRequestSchema, StreamNetworkExchangesRequestSchema, RunScriptRequestSchema, StartScriptRequestSchema, StopScriptsRequestSchema, ListScriptRunsRequestSchema, StreamScriptEventsRequestSchema, } from "./gen/wrc_pb.js";
|
|
6
|
-
import {
|
|
7
|
-
import { BrowserScaleError } from "./errors.js";
|
|
5
|
+
SetProxyRequestSchema, GetPagesRequestSchema, GetUsageRequestSchema, 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, GetAuthSessionRequestSchema, SetAuthSessionRequestSchema, InspectAtPositionRequestSchema, HighlightNodeRequestSchema, InsertTextRequestSchema, TypeRequestSchema, PressKeyRequestSchema, ReleaseKeyRequestSchema, GetSelectionRequestSchema, SolveCaptchaRequestSchema, GetStreamConfigRequestSchema, StartStreamRequestSchema, StopStreamRequestSchema, AddReactionRequestSchema, RemoveReactionRequestSchema, ListReactionsRequestSchema, StartNetworkCaptureRequestSchema, StopNetworkCaptureRequestSchema, StartDomMirrorRequestSchema, StopDomMirrorRequestSchema, GetDomChildrenRequestSchema, ReleaseDomSubtreeRequestSchema, RevealDomNodeRequestSchema, GetDomRevisionRequestSchema, StreamDomEventsRequestSchema, StreamNetworkExchangesRequestSchema, RunScriptRequestSchema, StartScriptRequestSchema, StopScriptsRequestSchema, ListScriptRunsRequestSchema, StreamScriptEventsRequestSchema, } from "./gen/wrc_pb.js";
|
|
6
|
+
import { BrowserScaleError, CommandError, throwCommandError } from "./errors.js";
|
|
8
7
|
import { NetworkCapture } from "./network-capture.js";
|
|
9
8
|
import { ScriptFollow, ScriptRun, scriptLogEntryFromProto, } from "./scripts.js";
|
|
10
9
|
import { DomMirror, } from "./dom-mirror.js";
|
|
11
|
-
import { authSessionFromProto, authSessionToProto, cookieParamFromProto, cookieParamsToProto, elementFields, headerModsToProto, headersToProto, interceptedRequestFromProto, interceptedResponseFromProto, pageInfoFromProto, rectFromProto, splitRequestPatterns, storageEntriesToProto, storageEntryFromProto, unwrapClick, unwrapDrag, unwrapFill, unwrapMove, unwrapScroll, unwrapSelect, unwrapWait, } from "./internal/convert.js";
|
|
10
|
+
import { authSessionFromProto, authSessionToProto, cookieParamFromProto, cookieParamsToProto, elementFields, headerModsToProto, headersToProto, interceptedRequestFromProto, interceptedResponseFromProto, pageInfoFromProto, rectFromProto, sessionUsageFromProto, splitRequestPatterns, storageEntriesToProto, storageEntryFromProto, unwrapClick, unwrapDrag, unwrapFill, unwrapMove, unwrapScroll, unwrapSelect, unwrapWait, } from "./internal/convert.js";
|
|
12
11
|
/**
|
|
13
12
|
* CloudBrowser is the SDK-side handle for an active browserscale browser session.
|
|
14
13
|
*
|
|
15
14
|
* One CloudBrowser corresponds to exactly one browser context, which
|
|
16
|
-
* always has at least one page
|
|
17
|
-
* primary page server-side — the proto's page_id field is currently
|
|
18
|
-
* ignored server-side, so the SDK never sets it.
|
|
15
|
+
* always has at least one page; its commands act on the primary page.
|
|
19
16
|
*
|
|
20
17
|
* Construct via rentBrowser() / createWebSocketBrowser() — never
|
|
21
18
|
* directly.
|
|
@@ -52,15 +49,25 @@ export class CloudBrowser {
|
|
|
52
49
|
* {@link createWebSocketBrowser} the rental stays untouched; only the
|
|
53
50
|
* transport is closed.
|
|
54
51
|
*
|
|
55
|
-
* @
|
|
52
|
+
* @returns SessionUsage what the session consumed over its whole life, read
|
|
53
|
+
* as it was torn down - no {@link CloudBrowser.getUsage} call is needed
|
|
54
|
+
* before stopping. Undefined when the server could not report it, and for
|
|
55
|
+
* a handle that owns no rental.
|
|
56
|
+
*
|
|
57
|
+
* Rejects only when the stop API or the transport close fails. The session is
|
|
58
|
+
* released either way; retrying a stop is safe.
|
|
56
59
|
*
|
|
57
60
|
* @example
|
|
58
|
-
* await browser.stopBrowser();
|
|
61
|
+
* const usage = await browser.stopBrowser();
|
|
62
|
+
* if (usage) {
|
|
63
|
+
* console.log(`ran ${usage.wallTime}s, ${usage.cpuTime}s CPU, peak ${usage.peakMemory} bytes`);
|
|
64
|
+
* }
|
|
59
65
|
*/
|
|
60
66
|
async stopBrowser() {
|
|
61
67
|
if (this._stopFn) {
|
|
62
|
-
await this._stopFn();
|
|
68
|
+
return await this._stopFn();
|
|
63
69
|
}
|
|
70
|
+
return undefined;
|
|
64
71
|
}
|
|
65
72
|
// ──────────────────────────────────────────────────────────────────
|
|
66
73
|
// Context-level
|
|
@@ -77,7 +84,9 @@ export class CloudBrowser {
|
|
|
77
84
|
* @param proxyUsername - proxy auth user; empty for unauthenticated proxies
|
|
78
85
|
* @param proxyPassword - proxy auth password; empty for unauthenticated proxies
|
|
79
86
|
*
|
|
80
|
-
*
|
|
87
|
+
* Rejects only on a transport failure - a dead session, a page that is gone,
|
|
88
|
+
* a broken connection. This call has no semantic failure of its own, so there
|
|
89
|
+
* are no error codes to branch on.
|
|
81
90
|
*
|
|
82
91
|
* @example
|
|
83
92
|
* await browser.setProxy("proxy.example.com", 8080, "user", "pass");
|
|
@@ -95,7 +104,8 @@ export class CloudBrowser {
|
|
|
95
104
|
if (proxyPassword)
|
|
96
105
|
req.proxyPassword = proxyPassword;
|
|
97
106
|
}
|
|
98
|
-
await this.client.setProxy(req);
|
|
107
|
+
const res = await this.client.setProxy(req);
|
|
108
|
+
throwCommandError("setProxy", res.error);
|
|
99
109
|
}
|
|
100
110
|
/**
|
|
101
111
|
* Returns all open pages (tabs and popups) for this session's browser
|
|
@@ -107,7 +117,9 @@ export class CloudBrowser {
|
|
|
107
117
|
*
|
|
108
118
|
* @returns PageInfo[] for every page currently open in the context
|
|
109
119
|
*
|
|
110
|
-
*
|
|
120
|
+
* Rejects only on a transport failure - a dead session, a page that is gone,
|
|
121
|
+
* a broken connection. This call has no semantic failure of its own, so there
|
|
122
|
+
* are no error codes to branch on.
|
|
111
123
|
*
|
|
112
124
|
* @example
|
|
113
125
|
* const pages = await browser.getPages();
|
|
@@ -118,8 +130,40 @@ export class CloudBrowser {
|
|
|
118
130
|
sessionId: this.sessionId,
|
|
119
131
|
apiKey: this.apiKey,
|
|
120
132
|
}));
|
|
133
|
+
throwCommandError("getPages", resp.error);
|
|
121
134
|
return resp.pages.map(pageInfoFromProto);
|
|
122
135
|
}
|
|
136
|
+
/**
|
|
137
|
+
* Reports what this session's browser has consumed so far.
|
|
138
|
+
*
|
|
139
|
+
* Everything but minMemory and averageMemory only ever grows, so polling and
|
|
140
|
+
* diffing two readings gives the cost of what ran in between. The final
|
|
141
|
+
* figures need no call of their own: {@link CloudBrowser.stopBrowser} resolves
|
|
142
|
+
* with them.
|
|
143
|
+
*
|
|
144
|
+
* @returns SessionUsage as of now
|
|
145
|
+
*
|
|
146
|
+
* Rejects only on a transport failure - a dead session, a broken connection.
|
|
147
|
+
* This call has no semantic failure of its own, so there are no error codes to
|
|
148
|
+
* branch on.
|
|
149
|
+
*
|
|
150
|
+
* @example
|
|
151
|
+
* const before = await browser.getUsage();
|
|
152
|
+
* await browser.navigate("https://example.com");
|
|
153
|
+
* const after = await browser.getUsage();
|
|
154
|
+
* console.log(`navigation cost ${after.cpuTime - before.cpuTime}s of CPU`);
|
|
155
|
+
*/
|
|
156
|
+
async getUsage() {
|
|
157
|
+
const resp = await this.client.getUsage(create(GetUsageRequestSchema, {
|
|
158
|
+
sessionId: this.sessionId,
|
|
159
|
+
apiKey: this.apiKey,
|
|
160
|
+
}));
|
|
161
|
+
throwCommandError("getUsage", resp.error);
|
|
162
|
+
if (!resp.usage) {
|
|
163
|
+
throw new BrowserScaleError("getUsage: the server sent no usage");
|
|
164
|
+
}
|
|
165
|
+
return sessionUsageFromProto(resp.usage);
|
|
166
|
+
}
|
|
123
167
|
// ──────────────────────────────────────────────────────────────────
|
|
124
168
|
// Page navigation / content
|
|
125
169
|
// ──────────────────────────────────────────────────────────────────
|
|
@@ -136,7 +180,16 @@ export class CloudBrowser {
|
|
|
136
180
|
* @returns NavigateResult with the final resolved URL and the frameId
|
|
137
181
|
* of the main frame after navigation
|
|
138
182
|
*
|
|
139
|
-
* @throws
|
|
183
|
+
* @throws timeout - nothing committed before the deadline; the page may still
|
|
184
|
+
* be loading, so a longer timeout can be the whole fix
|
|
185
|
+
* @throws net_error - the URL never loaded: DNS, TLS, a refused connection, or
|
|
186
|
+
* a proxy that could not reach it. The message carries the underlying net
|
|
187
|
+
* error name, which is what separates a bad proxy from a bad host - worth
|
|
188
|
+
* logging, since the two need different fixes
|
|
189
|
+
* @throws crashed - the renderer died mid-navigation; the page is unusable and
|
|
190
|
+
* has to be navigated again
|
|
191
|
+
*
|
|
192
|
+
* @see {@link CommandError} for reading the code off the rejection
|
|
140
193
|
*
|
|
141
194
|
* @example
|
|
142
195
|
* await browser.navigate("https://example.com");
|
|
@@ -150,6 +203,7 @@ export class CloudBrowser {
|
|
|
150
203
|
if (opts?.timeoutMs)
|
|
151
204
|
req.timeout = opts.timeoutMs;
|
|
152
205
|
const resp = await this.client.navigate(req);
|
|
206
|
+
throwCommandError("navigate", resp.error);
|
|
153
207
|
return { frameId: resp.frameId, url: resp.url };
|
|
154
208
|
}
|
|
155
209
|
/**
|
|
@@ -164,7 +218,11 @@ export class CloudBrowser {
|
|
|
164
218
|
* @param html - response body to serve
|
|
165
219
|
* @param opts - optional headers and statusCode (default 200)
|
|
166
220
|
*
|
|
167
|
-
* @throws
|
|
221
|
+
* @throws timeout - the page never requested the URL, so the prepared
|
|
222
|
+
* response had nobody to hand it to; usually the navigation was cancelled
|
|
223
|
+
* or redirected away before reaching it
|
|
224
|
+
*
|
|
225
|
+
* @see {@link CommandError} for reading the code off the rejection
|
|
168
226
|
*
|
|
169
227
|
* @example
|
|
170
228
|
* await browser.loadHTML("https://example.com", "<h1>hi</h1>");
|
|
@@ -180,7 +238,8 @@ export class CloudBrowser {
|
|
|
180
238
|
});
|
|
181
239
|
if (opts?.statusCode)
|
|
182
240
|
req.statusCode = opts.statusCode;
|
|
183
|
-
await this.client.loadHTML(req);
|
|
241
|
+
const res = await this.client.loadHTML(req);
|
|
242
|
+
throwCommandError("loadHTML", res.error);
|
|
184
243
|
}
|
|
185
244
|
// ──────────────────────────────────────────────────────────────────
|
|
186
245
|
// Evaluation
|
|
@@ -197,16 +256,34 @@ export class CloudBrowser {
|
|
|
197
256
|
* The generic T is a TypeScript hint only — there is no runtime
|
|
198
257
|
* validation that the JS expression actually returned that type.
|
|
199
258
|
*
|
|
259
|
+
* A falsy answer and a broken expression are different outcomes. Returning
|
|
260
|
+
* null, false or undefined is a successful evaluation and resolves normally;
|
|
261
|
+
* an expression that throws or will not compile rejects with a
|
|
262
|
+
* {@link CommandError}, so a typo can never read as "the page says null".
|
|
263
|
+
*
|
|
200
264
|
* @param expression - JavaScript expression evaluated in the main frame
|
|
201
265
|
*
|
|
202
266
|
* @returns EvaluateResult with either value (non-Element) or element
|
|
203
267
|
* metadata (Element)
|
|
204
268
|
*
|
|
205
|
-
* @throws
|
|
269
|
+
* @throws {@link CommandError} - code `"threw"` (the expression raised; the
|
|
270
|
+
* message carries the exception text), `"not_run"` (it could not be
|
|
271
|
+
* compiled, or execution never started), `"aborted"` (the browser stopped
|
|
272
|
+
* execution) or `"no_context"` (the frame had no live script context)
|
|
206
273
|
*
|
|
207
274
|
* @example
|
|
208
275
|
* const res = await browser.evaluate<string>("document.title");
|
|
209
276
|
* console.log(res.value);
|
|
277
|
+
*
|
|
278
|
+
* @example
|
|
279
|
+
* // Telling a false answer from a broken expression.
|
|
280
|
+
* try {
|
|
281
|
+
* const res = await browser.evaluate<boolean>("window.__ready === true");
|
|
282
|
+
* if (!res.value) { /* legitimately not ready yet *\/ }
|
|
283
|
+
* } catch (e) {
|
|
284
|
+
* if (e instanceof CommandError) throw new Error(`expression broken: ${e.message}`);
|
|
285
|
+
* throw e;
|
|
286
|
+
* }
|
|
210
287
|
*/
|
|
211
288
|
async evaluate(expression) {
|
|
212
289
|
return this._evaluate("", expression);
|
|
@@ -239,6 +316,15 @@ export class CloudBrowser {
|
|
|
239
316
|
if (frameId)
|
|
240
317
|
req.frameId = frameId;
|
|
241
318
|
const resp = await this.client.evaluate(req);
|
|
319
|
+
// An expression that threw arrives as success=false in the payload, not as a
|
|
320
|
+
// transport error, so it is raised here rather than by the call above.
|
|
321
|
+
if (resp.error) {
|
|
322
|
+
throw new CommandError({
|
|
323
|
+
command: "evaluate",
|
|
324
|
+
code: resp.error.code,
|
|
325
|
+
message: resp.error.message,
|
|
326
|
+
});
|
|
327
|
+
}
|
|
242
328
|
let value = null;
|
|
243
329
|
if (resp.result !== "") {
|
|
244
330
|
try {
|
|
@@ -249,6 +335,7 @@ export class CloudBrowser {
|
|
|
249
335
|
}
|
|
250
336
|
}
|
|
251
337
|
return {
|
|
338
|
+
success: resp.success,
|
|
252
339
|
value: value,
|
|
253
340
|
backendNodeId: resp.backendNodeId,
|
|
254
341
|
isVisible: resp.isVisible,
|
|
@@ -340,7 +427,8 @@ export class CloudBrowser {
|
|
|
340
427
|
sessionId: this.sessionId,
|
|
341
428
|
apiKey: this.apiKey,
|
|
342
429
|
conditions: pbConds,
|
|
343
|
-
|
|
430
|
+
// Left unset unless the caller passed one, so the API applies its default.
|
|
431
|
+
timeout: opts?.timeoutMs,
|
|
344
432
|
});
|
|
345
433
|
if (frameId)
|
|
346
434
|
req.frameId = frameId;
|
|
@@ -660,7 +748,9 @@ export class CloudBrowser {
|
|
|
660
748
|
* @returns DOMResult with the JSON string in `.dom` (the `.hash` field
|
|
661
749
|
* is populated by {@link getDOMHash}, not by this call)
|
|
662
750
|
*
|
|
663
|
-
*
|
|
751
|
+
* Rejects only on a transport failure - a dead session, a page that is gone,
|
|
752
|
+
* a broken connection. This call has no semantic failure of its own, so there
|
|
753
|
+
* are no error codes to branch on.
|
|
664
754
|
*
|
|
665
755
|
* @example
|
|
666
756
|
* const { dom } = await browser.getDOM();
|
|
@@ -675,6 +765,7 @@ export class CloudBrowser {
|
|
|
675
765
|
if (opts?.depth !== undefined)
|
|
676
766
|
req.depth = opts.depth;
|
|
677
767
|
const resp = await this.client.getDOM(req);
|
|
768
|
+
throwCommandError("getDOM", resp.error);
|
|
678
769
|
return { hash: "", dom: resp.dom };
|
|
679
770
|
}
|
|
680
771
|
/**
|
|
@@ -689,7 +780,9 @@ export class CloudBrowser {
|
|
|
689
780
|
*
|
|
690
781
|
* @returns 16-char hex string (the first 8 bytes of sha256 of the DOM JSON)
|
|
691
782
|
*
|
|
692
|
-
*
|
|
783
|
+
* Rejects only on a transport failure. The hash is computed from a serialized
|
|
784
|
+
* tree, so there is no semantic failure of its own and no error codes to branch
|
|
785
|
+
* on.
|
|
693
786
|
*
|
|
694
787
|
* @example
|
|
695
788
|
* const hash = await browser.getDOMHash();
|
|
@@ -738,7 +831,11 @@ export class CloudBrowser {
|
|
|
738
831
|
*
|
|
739
832
|
* @returns the observation in the requested format, ready to hand to a model
|
|
740
833
|
*
|
|
741
|
-
* @throws
|
|
834
|
+
* @throws not_found - the requested scope root is not on the page, so there
|
|
835
|
+
* was nothing to observe. Distinct from an observation that comes back
|
|
836
|
+
* empty, which means the scope exists and holds nothing worth reporting
|
|
837
|
+
*
|
|
838
|
+
* @see {@link CommandError} for reading the code off the rejection
|
|
742
839
|
*
|
|
743
840
|
* @example
|
|
744
841
|
* const obs = await browser.getObservation();
|
|
@@ -771,6 +868,7 @@ export class CloudBrowser {
|
|
|
771
868
|
if (opts?.frameId !== undefined)
|
|
772
869
|
req.frameId = opts.frameId;
|
|
773
870
|
const resp = await this.client.getObservation(req);
|
|
871
|
+
throwCommandError("getObservation", resp.error);
|
|
774
872
|
return resp.observation;
|
|
775
873
|
}
|
|
776
874
|
/**
|
|
@@ -787,7 +885,11 @@ export class CloudBrowser {
|
|
|
787
885
|
* @returns ScreenshotResult with the base64 image in `dataBase64` and the
|
|
788
886
|
* physical pixel `width`/`height`
|
|
789
887
|
*
|
|
790
|
-
* @throws
|
|
888
|
+
* @throws capture_failed - the page had no frame to copy. A page that has not
|
|
889
|
+
* produced one yet, or is not being composited at the moment, has nothing to
|
|
890
|
+
* hand over; retrying after it renders usually works
|
|
891
|
+
*
|
|
892
|
+
* @see {@link CommandError} for reading the code off the rejection
|
|
791
893
|
*
|
|
792
894
|
* @example
|
|
793
895
|
* const shot = await browser.screenshot({ format: "png" });
|
|
@@ -803,6 +905,7 @@ export class CloudBrowser {
|
|
|
803
905
|
if (opts?.quality !== undefined)
|
|
804
906
|
req.quality = opts.quality;
|
|
805
907
|
const resp = await this.client.screenshot(req);
|
|
908
|
+
throwCommandError("screenshot", resp.error);
|
|
806
909
|
return {
|
|
807
910
|
dataBase64: resp.dataBase64,
|
|
808
911
|
width: resp.width,
|
|
@@ -826,11 +929,15 @@ export class CloudBrowser {
|
|
|
826
929
|
* canvas `width`/`height`, resolved `frameId`/`backendNodeId` and the
|
|
827
930
|
* `originClean` flag
|
|
828
931
|
*
|
|
829
|
-
* @throws
|
|
830
|
-
* @throws
|
|
831
|
-
* @throws
|
|
832
|
-
*
|
|
833
|
-
*
|
|
932
|
+
* @throws not_found - no element matched the locator
|
|
933
|
+
* @throws not_element - the expression was truthy but did not yield an element
|
|
934
|
+
* @throws not_readable - the target was found but is not a readable canvas
|
|
935
|
+
*
|
|
936
|
+
* A target that is empty, uses `at(x, y)` or names several things at once is
|
|
937
|
+
* rejected before anything is sent. A closed page or a frame that is gone is a
|
|
938
|
+
* transport failure rather than a code.
|
|
939
|
+
*
|
|
940
|
+
* @see {@link CommandError} for reading the code off the rejection
|
|
834
941
|
*
|
|
835
942
|
* @example
|
|
836
943
|
* const res = await browser.readCanvas(css("#game canvas"));
|
|
@@ -862,6 +969,7 @@ export class CloudBrowser {
|
|
|
862
969
|
req.sh = opts.sh;
|
|
863
970
|
}
|
|
864
971
|
const resp = await this.client.readCanvas(req);
|
|
972
|
+
throwCommandError("readCanvas", resp.error);
|
|
865
973
|
return {
|
|
866
974
|
success: resp.success,
|
|
867
975
|
frameId: resp.frameId,
|
|
@@ -885,7 +993,9 @@ export class CloudBrowser {
|
|
|
885
993
|
*
|
|
886
994
|
* @param patterns - URL wildcards to block; empty array clears the list
|
|
887
995
|
*
|
|
888
|
-
*
|
|
996
|
+
* Rejects only on a transport failure - a dead session, a page that is gone,
|
|
997
|
+
* a broken connection. This call has no semantic failure of its own, so there
|
|
998
|
+
* are no error codes to branch on.
|
|
889
999
|
*
|
|
890
1000
|
* @example
|
|
891
1001
|
* await browser.setBlockList([
|
|
@@ -894,11 +1004,12 @@ export class CloudBrowser {
|
|
|
894
1004
|
* ]);
|
|
895
1005
|
*/
|
|
896
1006
|
async setBlockList(patterns) {
|
|
897
|
-
await this.client.setBlockList(create(SetBlockListRequestSchema, {
|
|
1007
|
+
const res = await this.client.setBlockList(create(SetBlockListRequestSchema, {
|
|
898
1008
|
sessionId: this.sessionId,
|
|
899
1009
|
apiKey: this.apiKey,
|
|
900
1010
|
patterns,
|
|
901
1011
|
}));
|
|
1012
|
+
throwCommandError("setBlockList", res.error);
|
|
902
1013
|
}
|
|
903
1014
|
/**
|
|
904
1015
|
* Configures the session to serve cached static responses for requests
|
|
@@ -912,18 +1023,21 @@ export class CloudBrowser {
|
|
|
912
1023
|
* @param blobName - server-side identifier of the snapshot to serve from
|
|
913
1024
|
* @param patterns - URL wildcards to redirect to the cache; empty disables
|
|
914
1025
|
*
|
|
915
|
-
*
|
|
1026
|
+
* Rejects only on a transport failure - a dead session, a page that is gone,
|
|
1027
|
+
* a broken connection. This call has no semantic failure of its own, so there
|
|
1028
|
+
* are no error codes to branch on.
|
|
916
1029
|
*
|
|
917
1030
|
* @example
|
|
918
1031
|
* await browser.setStaticPaths("snap-2026-05", ["*.example.com/*"]);
|
|
919
1032
|
*/
|
|
920
1033
|
async setStaticPaths(blobName, patterns) {
|
|
921
|
-
await this.client.setStaticPaths(create(SetStaticPathsRequestSchema, {
|
|
1034
|
+
const res = await this.client.setStaticPaths(create(SetStaticPathsRequestSchema, {
|
|
922
1035
|
sessionId: this.sessionId,
|
|
923
1036
|
apiKey: this.apiKey,
|
|
924
1037
|
blobName,
|
|
925
1038
|
patterns,
|
|
926
1039
|
}));
|
|
1040
|
+
throwCommandError("setStaticPaths", res.error);
|
|
927
1041
|
}
|
|
928
1042
|
/**
|
|
929
1043
|
* Blocks until the next request whose URL matches one of the supplied
|
|
@@ -940,7 +1054,12 @@ export class CloudBrowser {
|
|
|
940
1054
|
* (the captured method/URL/headers/body; null if intercepted with
|
|
941
1055
|
* no body)
|
|
942
1056
|
*
|
|
943
|
-
* @throws
|
|
1057
|
+
* @throws timeout - no request matched any pattern before the deadline.
|
|
1058
|
+
* Nothing occurring is an answer, and it stays distinguishable from a
|
|
1059
|
+
* connection that died on the way. Supplying no patterns is a caller mistake
|
|
1060
|
+
* rather than an outcome, and rejects separately
|
|
1061
|
+
*
|
|
1062
|
+
* @see {@link CommandError} for reading the code off the rejection
|
|
944
1063
|
*
|
|
945
1064
|
* @example
|
|
946
1065
|
* const { index, request } = await browser.waitForAnyRequest(
|
|
@@ -963,6 +1082,7 @@ export class CloudBrowser {
|
|
|
963
1082
|
if (opts?.timeoutMs)
|
|
964
1083
|
req.timeout = opts.timeoutMs;
|
|
965
1084
|
const resp = await this.client.waitForAnyRequest(req);
|
|
1085
|
+
throwCommandError("waitForAnyRequest", resp.error);
|
|
966
1086
|
return {
|
|
967
1087
|
index: resp.index,
|
|
968
1088
|
request: interceptedRequestFromProto(resp.request),
|
|
@@ -981,6 +1101,13 @@ export class CloudBrowser {
|
|
|
981
1101
|
* @returns object with `index` (matched pattern index) and `response`
|
|
982
1102
|
* (the captured status/headers/body; null if no body was returned)
|
|
983
1103
|
*
|
|
1104
|
+
* @throws timeout - no response matched any pattern before the deadline.
|
|
1105
|
+
* Nothing occurring is an answer, and it stays distinguishable from a
|
|
1106
|
+
* connection that died on the way. Supplying no patterns is a caller mistake
|
|
1107
|
+
* rather than an outcome, and rejects separately
|
|
1108
|
+
*
|
|
1109
|
+
* @see {@link CommandError} for reading the code off the rejection
|
|
1110
|
+
*
|
|
984
1111
|
* @example
|
|
985
1112
|
* const { index, response } = await browser.waitForAnyResponse(
|
|
986
1113
|
* [{ url: "*\/api/login" }],
|
|
@@ -1002,6 +1129,7 @@ export class CloudBrowser {
|
|
|
1002
1129
|
if (opts?.timeoutMs)
|
|
1003
1130
|
req.timeout = opts.timeoutMs;
|
|
1004
1131
|
const resp = await this.client.waitForAnyResponse(req);
|
|
1132
|
+
throwCommandError("waitForAnyResponse", resp.error);
|
|
1005
1133
|
return {
|
|
1006
1134
|
index: resp.index,
|
|
1007
1135
|
response: interceptedResponseFromProto(resp.response),
|
|
@@ -1023,7 +1151,10 @@ export class CloudBrowser {
|
|
|
1023
1151
|
* were actually sent on the wire after modifications were applied;
|
|
1024
1152
|
* null when no request payload was reported
|
|
1025
1153
|
*
|
|
1026
|
-
* @throws
|
|
1154
|
+
* @throws timeout - no matching request appeared before the deadline, so
|
|
1155
|
+
* nothing was modified
|
|
1156
|
+
*
|
|
1157
|
+
* @see {@link CommandError} for reading the code off the rejection
|
|
1027
1158
|
*
|
|
1028
1159
|
* @example
|
|
1029
1160
|
* const req = await browser.modifyRequest("*\/api/me", {
|
|
@@ -1047,6 +1178,7 @@ export class CloudBrowser {
|
|
|
1047
1178
|
if (opts?.timeoutMs)
|
|
1048
1179
|
req.timeout = opts.timeoutMs;
|
|
1049
1180
|
const resp = await this.client.modifyRequest(req);
|
|
1181
|
+
throwCommandError("modifyRequest", resp.error);
|
|
1050
1182
|
return interceptedRequestFromProto(resp.request);
|
|
1051
1183
|
}
|
|
1052
1184
|
// ──────────────────────────────────────────────────────────────────
|
|
@@ -1076,7 +1208,9 @@ export class CloudBrowser {
|
|
|
1076
1208
|
* @returns NetworkCapture handle for stopping the capture and inspecting how
|
|
1077
1209
|
* it ended
|
|
1078
1210
|
*
|
|
1079
|
-
*
|
|
1211
|
+
* Rejects only on a transport failure - a dead session, a page that is gone,
|
|
1212
|
+
* a broken connection. This call has no semantic failure of its own, so there
|
|
1213
|
+
* are no error codes to branch on.
|
|
1080
1214
|
*
|
|
1081
1215
|
* @example
|
|
1082
1216
|
* const capture = await browser.captureNetwork(
|
|
@@ -1112,29 +1246,34 @@ export class CloudBrowser {
|
|
|
1112
1246
|
*
|
|
1113
1247
|
* @param opts - which requests to capture and whether to keep bodies
|
|
1114
1248
|
*
|
|
1115
|
-
*
|
|
1249
|
+
* Rejects only on a transport failure - a dead session, a page that is gone,
|
|
1250
|
+
* a broken connection. This call has no semantic failure of its own, so there
|
|
1251
|
+
* are no error codes to branch on.
|
|
1116
1252
|
*/
|
|
1117
1253
|
async startNetworkCapture(opts) {
|
|
1118
|
-
await this.client.startNetworkCapture(create(StartNetworkCaptureRequestSchema, {
|
|
1254
|
+
const res = await this.client.startNetworkCapture(create(StartNetworkCaptureRequestSchema, {
|
|
1119
1255
|
sessionId: this.sessionId,
|
|
1120
1256
|
apiKey: this.apiKey,
|
|
1121
1257
|
patterns: opts.patterns ?? [],
|
|
1122
1258
|
bodies: opts.bodies ?? "none",
|
|
1123
1259
|
bodyPatterns: opts.bodyPatterns ?? [],
|
|
1124
1260
|
}));
|
|
1261
|
+
throwCommandError("startNetworkCapture", res.error);
|
|
1125
1262
|
}
|
|
1126
1263
|
/**
|
|
1127
1264
|
* Disarms the session's capture.
|
|
1128
1265
|
*
|
|
1129
1266
|
* @returns whether a capture was running
|
|
1130
1267
|
*
|
|
1131
|
-
*
|
|
1268
|
+
* Rejects only on a transport failure. Stopping a capture that is not running
|
|
1269
|
+
* is a no-op rather than a failure, so there are no error codes to branch on.
|
|
1132
1270
|
*/
|
|
1133
1271
|
async stopNetworkCapture() {
|
|
1134
1272
|
const resp = await this.client.stopNetworkCapture(create(StopNetworkCaptureRequestSchema, {
|
|
1135
1273
|
sessionId: this.sessionId,
|
|
1136
1274
|
apiKey: this.apiKey,
|
|
1137
1275
|
}));
|
|
1276
|
+
throwCommandError("stopNetworkCapture", resp.error);
|
|
1138
1277
|
return resp.stopped;
|
|
1139
1278
|
}
|
|
1140
1279
|
/**
|
|
@@ -1151,7 +1290,8 @@ export class CloudBrowser {
|
|
|
1151
1290
|
* @returns NetworkCapture attached to whatever capture is running; onExchange
|
|
1152
1291
|
* simply never fires when none is
|
|
1153
1292
|
*
|
|
1154
|
-
*
|
|
1293
|
+
* Rejects only on a transport failure: opening the subscription has no semantic
|
|
1294
|
+
* failure of its own.
|
|
1155
1295
|
*/
|
|
1156
1296
|
async streamNetworkExchanges(onExchange) {
|
|
1157
1297
|
return this.subscribeNetworkExchanges(onExchange);
|
|
@@ -1233,7 +1373,10 @@ export class CloudBrowser {
|
|
|
1233
1373
|
* @param onResync called when the copy had to be rebuilt, after the new tree
|
|
1234
1374
|
* is in place. Rebuilding is automatic; this is for telling the user why
|
|
1235
1375
|
* their expanded nodes collapsed.
|
|
1236
|
-
* @throws
|
|
1376
|
+
* @throws mirror_failed - the page could not be serialized, usually a document
|
|
1377
|
+
* that went away while the tree was being built. No mirror is left running
|
|
1378
|
+
*
|
|
1379
|
+
* @see {@link CommandError} for reading the code off the rejection
|
|
1237
1380
|
*/
|
|
1238
1381
|
async mirrorDom(opts, onChange, onResync) {
|
|
1239
1382
|
const abort = new AbortController();
|
|
@@ -1282,6 +1425,11 @@ export class CloudBrowser {
|
|
|
1282
1425
|
* Starts (or restarts) the page's mirror and returns the main document,
|
|
1283
1426
|
* without subscribing to changes. {@link CloudBrowser.mirrorDom} is what you
|
|
1284
1427
|
* normally want; this is the raw command.
|
|
1428
|
+
*
|
|
1429
|
+
* @throws mirror_failed - the page could not be serialized, usually a document
|
|
1430
|
+
* that went away while the tree was being built. No mirror is left running
|
|
1431
|
+
*
|
|
1432
|
+
* @see {@link CommandError} for reading the code off the rejection
|
|
1285
1433
|
*/
|
|
1286
1434
|
async startDomMirror(opts = {}) {
|
|
1287
1435
|
const req = create(StartDomMirrorRequestSchema, {
|
|
@@ -1293,6 +1441,7 @@ export class CloudBrowser {
|
|
|
1293
1441
|
if (opts.pierce !== undefined)
|
|
1294
1442
|
req.pierce = opts.pierce;
|
|
1295
1443
|
const resp = await this.client.startDomMirror(req);
|
|
1444
|
+
throwCommandError("startDomMirror", resp.error);
|
|
1296
1445
|
return {
|
|
1297
1446
|
root: resp.root,
|
|
1298
1447
|
frameId: resp.frameId,
|
|
@@ -1301,10 +1450,11 @@ export class CloudBrowser {
|
|
|
1301
1450
|
}
|
|
1302
1451
|
/** Stops the page's mirror, every frame of it. Idempotent. */
|
|
1303
1452
|
async stopDomMirror() {
|
|
1304
|
-
await this.client.stopDomMirror(create(StopDomMirrorRequestSchema, {
|
|
1453
|
+
const res = await this.client.stopDomMirror(create(StopDomMirrorRequestSchema, {
|
|
1305
1454
|
sessionId: this.sessionId,
|
|
1306
1455
|
apiKey: this.apiKey,
|
|
1307
1456
|
}));
|
|
1457
|
+
throwCommandError("stopDomMirror", res.error);
|
|
1308
1458
|
}
|
|
1309
1459
|
/**
|
|
1310
1460
|
* Fetches a node's children and starts reporting changes inside them.
|
|
@@ -1312,6 +1462,17 @@ export class CloudBrowser {
|
|
|
1312
1462
|
*
|
|
1313
1463
|
* On an `<iframe>` the one child is the document it hosts, and this call is
|
|
1314
1464
|
* what starts mirroring that frame.
|
|
1465
|
+
*
|
|
1466
|
+
* An id that is simply unknown is not a failure: the call resolves with an
|
|
1467
|
+
* empty result.
|
|
1468
|
+
*
|
|
1469
|
+
* @throws not_mirrored - the page has no mirror, or frameId is not part of the
|
|
1470
|
+
* one it has; start a mirror first, and after a resync fetch the current tree
|
|
1471
|
+
* before addressing nodes again
|
|
1472
|
+
* @throws mirror_failed - the subtree could not be serialized, usually a
|
|
1473
|
+
* document that went away mid-read
|
|
1474
|
+
*
|
|
1475
|
+
* @see {@link CommandError} for reading the code off the rejection
|
|
1315
1476
|
*/
|
|
1316
1477
|
async getDomChildren(backendNodeId, frameId = "", depth) {
|
|
1317
1478
|
const req = create(GetDomChildrenRequestSchema, {
|
|
@@ -1324,11 +1485,19 @@ export class CloudBrowser {
|
|
|
1324
1485
|
if (depth !== undefined)
|
|
1325
1486
|
req.depth = depth;
|
|
1326
1487
|
const resp = await this.client.getDomChildren(req);
|
|
1488
|
+
throwCommandError("getDomChildren", resp.error);
|
|
1327
1489
|
return { children: resp.children, seq: Number(resp.seq) };
|
|
1328
1490
|
}
|
|
1329
1491
|
/**
|
|
1330
1492
|
* Stops reporting changes inside a node, and inside any frame below it.
|
|
1331
1493
|
* {@link DomMirror.collapse} calls this.
|
|
1494
|
+
*
|
|
1495
|
+
* @throws not_mirrored - the page has no mirror, or frameId is not part of
|
|
1496
|
+
* the one it has; this is what replaying ids from a tree that has since
|
|
1497
|
+
* been resynced looks like, so fetch the current tree and address the node
|
|
1498
|
+
* again
|
|
1499
|
+
*
|
|
1500
|
+
* @see {@link CommandError} for reading the code off the rejection
|
|
1332
1501
|
*/
|
|
1333
1502
|
async releaseDomSubtree(backendNodeId, frameId = "") {
|
|
1334
1503
|
const req = create(ReleaseDomSubtreeRequestSchema, {
|
|
@@ -1338,13 +1507,24 @@ export class CloudBrowser {
|
|
|
1338
1507
|
});
|
|
1339
1508
|
if (frameId)
|
|
1340
1509
|
req.frameId = frameId;
|
|
1341
|
-
await this.client.releaseDomSubtree(req);
|
|
1510
|
+
const res = await this.client.releaseDomSubtree(req);
|
|
1511
|
+
throwCommandError("releaseDomSubtree", res.error);
|
|
1342
1512
|
}
|
|
1343
1513
|
/**
|
|
1344
1514
|
* Returns the chain from the main document down to a node, each ancestor
|
|
1345
1515
|
* with its own children, crossing into frames where it has to and starting
|
|
1346
1516
|
* the ones it passes through. {@link DomMirror.reveal} calls this and
|
|
1347
1517
|
* splices it in.
|
|
1518
|
+
*
|
|
1519
|
+
* An id that is simply unknown is not a failure: the call resolves with an
|
|
1520
|
+
* empty result.
|
|
1521
|
+
*
|
|
1522
|
+
* @throws not_mirrored - the page has no mirror, or frameId is not part of the
|
|
1523
|
+
* one it has
|
|
1524
|
+
* @throws mirror_failed - the path could not be serialized, usually a document
|
|
1525
|
+
* that went away mid-read
|
|
1526
|
+
*
|
|
1527
|
+
* @see {@link CommandError} for reading the code off the rejection
|
|
1348
1528
|
*/
|
|
1349
1529
|
async revealDomNode(backendNodeId, frameId = "") {
|
|
1350
1530
|
const req = create(RevealDomNodeRequestSchema, {
|
|
@@ -1355,6 +1535,7 @@ export class CloudBrowser {
|
|
|
1355
1535
|
if (frameId)
|
|
1356
1536
|
req.frameId = frameId;
|
|
1357
1537
|
const resp = await this.client.revealDomNode(req);
|
|
1538
|
+
throwCommandError("revealDomNode", resp.error);
|
|
1358
1539
|
return { path: resp.path, seq: Number(resp.seq) };
|
|
1359
1540
|
}
|
|
1360
1541
|
/**
|
|
@@ -1366,6 +1547,10 @@ export class CloudBrowser {
|
|
|
1366
1547
|
* whole tree just to hash it. The two answer different questions: a hash
|
|
1367
1548
|
* compares content, a revision only says whether this document moved since
|
|
1368
1549
|
* you last asked.
|
|
1550
|
+
*
|
|
1551
|
+
* Rejects only on a transport failure - a dead session, a page that is gone,
|
|
1552
|
+
* a broken connection. This call has no semantic failure of its own, so there
|
|
1553
|
+
* are no error codes to branch on.
|
|
1369
1554
|
*/
|
|
1370
1555
|
async getDomRevision(frameId = "") {
|
|
1371
1556
|
const req = create(GetDomRevisionRequestSchema, {
|
|
@@ -1375,6 +1560,7 @@ export class CloudBrowser {
|
|
|
1375
1560
|
if (frameId)
|
|
1376
1561
|
req.frameId = frameId;
|
|
1377
1562
|
const resp = await this.client.getDomRevision(req);
|
|
1563
|
+
throwCommandError("getDomRevision", resp.error);
|
|
1378
1564
|
return Number(resp.revision);
|
|
1379
1565
|
}
|
|
1380
1566
|
// ──────────────────────────────────────────────────────────────────
|
|
@@ -1385,7 +1571,9 @@ export class CloudBrowser {
|
|
|
1385
1571
|
*
|
|
1386
1572
|
* @returns CookieParam[], one per cookie in the context
|
|
1387
1573
|
*
|
|
1388
|
-
*
|
|
1574
|
+
* Rejects only on a transport failure - a dead session, a page that is gone,
|
|
1575
|
+
* a broken connection. This call has no semantic failure of its own, so there
|
|
1576
|
+
* are no error codes to branch on.
|
|
1389
1577
|
*
|
|
1390
1578
|
* @example
|
|
1391
1579
|
* const cookies = await browser.getCookies();
|
|
@@ -1396,6 +1584,7 @@ export class CloudBrowser {
|
|
|
1396
1584
|
sessionId: this.sessionId,
|
|
1397
1585
|
apiKey: this.apiKey,
|
|
1398
1586
|
}));
|
|
1587
|
+
throwCommandError("getCookies", resp.error);
|
|
1399
1588
|
return resp.cookies.map(cookieParamFromProto);
|
|
1400
1589
|
}
|
|
1401
1590
|
/**
|
|
@@ -1406,7 +1595,9 @@ export class CloudBrowser {
|
|
|
1406
1595
|
*
|
|
1407
1596
|
* @param cookies - cookies to write; empty array is a no-op
|
|
1408
1597
|
*
|
|
1409
|
-
*
|
|
1598
|
+
* Rejects only on a transport failure - a dead session, a page that is gone,
|
|
1599
|
+
* a broken connection. This call has no semantic failure of its own, so there
|
|
1600
|
+
* are no error codes to branch on.
|
|
1410
1601
|
*
|
|
1411
1602
|
* @example
|
|
1412
1603
|
* await browser.setCookies([
|
|
@@ -1414,25 +1605,29 @@ export class CloudBrowser {
|
|
|
1414
1605
|
* ]);
|
|
1415
1606
|
*/
|
|
1416
1607
|
async setCookies(cookies) {
|
|
1417
|
-
await this.client.setCookies(create(SetCookiesRequestSchema, {
|
|
1608
|
+
const res = await this.client.setCookies(create(SetCookiesRequestSchema, {
|
|
1418
1609
|
sessionId: this.sessionId,
|
|
1419
1610
|
apiKey: this.apiKey,
|
|
1420
1611
|
cookies: cookieParamsToProto(cookies),
|
|
1421
1612
|
}));
|
|
1613
|
+
throwCommandError("setCookies", res.error);
|
|
1422
1614
|
}
|
|
1423
1615
|
/**
|
|
1424
1616
|
* Deletes every cookie in the browser context.
|
|
1425
1617
|
*
|
|
1426
|
-
*
|
|
1618
|
+
* Rejects only on a transport failure - a dead session, a page that is gone,
|
|
1619
|
+
* a broken connection. This call has no semantic failure of its own, so there
|
|
1620
|
+
* are no error codes to branch on.
|
|
1427
1621
|
*
|
|
1428
1622
|
* @example
|
|
1429
1623
|
* await browser.clearCookies();
|
|
1430
1624
|
*/
|
|
1431
1625
|
async clearCookies() {
|
|
1432
|
-
await this.client.clearCookies(create(ClearCookiesRequestSchema, {
|
|
1626
|
+
const res = await this.client.clearCookies(create(ClearCookiesRequestSchema, {
|
|
1433
1627
|
sessionId: this.sessionId,
|
|
1434
1628
|
apiKey: this.apiKey,
|
|
1435
1629
|
}));
|
|
1630
|
+
throwCommandError("clearCookies", res.error);
|
|
1436
1631
|
}
|
|
1437
1632
|
// ──────────────────────────────────────────────────────────────────
|
|
1438
1633
|
// Storage (localStorage)
|
|
@@ -1450,7 +1645,9 @@ export class CloudBrowser {
|
|
|
1450
1645
|
*
|
|
1451
1646
|
* @returns StorageOriginEntry[], one per origin with localStorage data
|
|
1452
1647
|
*
|
|
1453
|
-
*
|
|
1648
|
+
* Rejects only on a transport failure - a dead session, a page that is gone,
|
|
1649
|
+
* a broken connection. This call has no semantic failure of its own, so there
|
|
1650
|
+
* are no error codes to branch on.
|
|
1454
1651
|
*
|
|
1455
1652
|
* @example
|
|
1456
1653
|
* const storage = await browser.getStorage();
|
|
@@ -1466,6 +1663,7 @@ export class CloudBrowser {
|
|
|
1466
1663
|
if (origin)
|
|
1467
1664
|
req.origin = origin;
|
|
1468
1665
|
const resp = await this.client.getStorage(req);
|
|
1666
|
+
throwCommandError("getStorage", resp.error);
|
|
1469
1667
|
return resp.storage.map(storageEntryFromProto);
|
|
1470
1668
|
}
|
|
1471
1669
|
/**
|
|
@@ -1479,7 +1677,9 @@ export class CloudBrowser {
|
|
|
1479
1677
|
*
|
|
1480
1678
|
* @param storage - entries to write, grouped by origin
|
|
1481
1679
|
*
|
|
1482
|
-
*
|
|
1680
|
+
* Rejects only on a transport failure - a dead session, a page that is gone,
|
|
1681
|
+
* a broken connection. This call has no semantic failure of its own, so there
|
|
1682
|
+
* are no error codes to branch on.
|
|
1483
1683
|
*
|
|
1484
1684
|
* @example
|
|
1485
1685
|
* await browser.setStorage([
|
|
@@ -1493,11 +1693,12 @@ export class CloudBrowser {
|
|
|
1493
1693
|
* ]);
|
|
1494
1694
|
*/
|
|
1495
1695
|
async setStorage(storage) {
|
|
1496
|
-
await this.client.setStorage(create(SetStorageRequestSchema, {
|
|
1696
|
+
const res = await this.client.setStorage(create(SetStorageRequestSchema, {
|
|
1497
1697
|
sessionId: this.sessionId,
|
|
1498
1698
|
apiKey: this.apiKey,
|
|
1499
1699
|
storage: storageEntriesToProto(storage),
|
|
1500
1700
|
}));
|
|
1701
|
+
throwCommandError("setStorage", res.error);
|
|
1501
1702
|
}
|
|
1502
1703
|
/**
|
|
1503
1704
|
* Deletes localStorage in the browser context.
|
|
@@ -1505,7 +1706,9 @@ export class CloudBrowser {
|
|
|
1505
1706
|
* @param origin - if set, only this origin's storage is deleted
|
|
1506
1707
|
* (e.g. "https://example.com"); omit to delete all origins
|
|
1507
1708
|
*
|
|
1508
|
-
*
|
|
1709
|
+
* Rejects only on a transport failure - a dead session, a page that is gone,
|
|
1710
|
+
* a broken connection. This call has no semantic failure of its own, so there
|
|
1711
|
+
* are no error codes to branch on.
|
|
1509
1712
|
*
|
|
1510
1713
|
* @example
|
|
1511
1714
|
* // Wipe one origin.
|
|
@@ -1521,7 +1724,8 @@ export class CloudBrowser {
|
|
|
1521
1724
|
});
|
|
1522
1725
|
if (origin)
|
|
1523
1726
|
req.origin = origin;
|
|
1524
|
-
await this.client.clearStorage(req);
|
|
1727
|
+
const res = await this.client.clearStorage(req);
|
|
1728
|
+
throwCommandError("clearStorage", res.error);
|
|
1525
1729
|
}
|
|
1526
1730
|
// ──────────────────────────────────────────────────────────────────
|
|
1527
1731
|
// Auth / DBSC (portable signed-in persona)
|
|
@@ -1536,7 +1740,9 @@ export class CloudBrowser {
|
|
|
1536
1740
|
*
|
|
1537
1741
|
* @returns AuthSession, or undefined when there is nothing to export
|
|
1538
1742
|
*
|
|
1539
|
-
*
|
|
1743
|
+
* Rejects only on a transport failure - a dead session, a page that is gone,
|
|
1744
|
+
* a broken connection. This call has no semantic failure of its own, so there
|
|
1745
|
+
* are no error codes to branch on.
|
|
1540
1746
|
*
|
|
1541
1747
|
* @example
|
|
1542
1748
|
* const auth = await browser.getAuthSession();
|
|
@@ -1547,6 +1753,7 @@ export class CloudBrowser {
|
|
|
1547
1753
|
sessionId: this.sessionId,
|
|
1548
1754
|
apiKey: this.apiKey,
|
|
1549
1755
|
}));
|
|
1756
|
+
throwCommandError("getAuthSession", resp.error);
|
|
1550
1757
|
return resp.session ? authSessionFromProto(resp.session) : undefined;
|
|
1551
1758
|
}
|
|
1552
1759
|
/**
|
|
@@ -1558,18 +1765,21 @@ export class CloudBrowser {
|
|
|
1558
1765
|
*
|
|
1559
1766
|
* @param session - session as returned by getAuthSession()
|
|
1560
1767
|
*
|
|
1561
|
-
*
|
|
1768
|
+
* Rejects only on a transport failure - a dead session, a page that is gone,
|
|
1769
|
+
* a broken connection. This call has no semantic failure of its own, so there
|
|
1770
|
+
* are no error codes to branch on.
|
|
1562
1771
|
*
|
|
1563
1772
|
* @example
|
|
1564
1773
|
* await browser.setAuthSession(saved);
|
|
1565
1774
|
* await browser.navigate("https://mail.google.com");
|
|
1566
1775
|
*/
|
|
1567
1776
|
async setAuthSession(session) {
|
|
1568
|
-
await this.client.setAuthSession(create(SetAuthSessionRequestSchema, {
|
|
1777
|
+
const res = await this.client.setAuthSession(create(SetAuthSessionRequestSchema, {
|
|
1569
1778
|
sessionId: this.sessionId,
|
|
1570
1779
|
apiKey: this.apiKey,
|
|
1571
1780
|
session: authSessionToProto(session),
|
|
1572
1781
|
}));
|
|
1782
|
+
throwCommandError("setAuthSession", res.error);
|
|
1573
1783
|
}
|
|
1574
1784
|
// ──────────────────────────────────────────────────────────────────
|
|
1575
1785
|
// Devtools / live-UI helpers
|
|
@@ -1589,7 +1799,9 @@ export class CloudBrowser {
|
|
|
1589
1799
|
* @returns InspectResult with the resolved backendNodeId, frameId, tag
|
|
1590
1800
|
* name, trimmed textContent, visibility and bounds
|
|
1591
1801
|
*
|
|
1592
|
-
*
|
|
1802
|
+
* Rejects only on a transport failure - a dead session, a page that is gone,
|
|
1803
|
+
* a broken connection. This call has no semantic failure of its own, so there
|
|
1804
|
+
* are no error codes to branch on.
|
|
1593
1805
|
*
|
|
1594
1806
|
* @example
|
|
1595
1807
|
* const r = await browser.inspectAtPosition(200, 300);
|
|
@@ -1602,6 +1814,7 @@ export class CloudBrowser {
|
|
|
1602
1814
|
x,
|
|
1603
1815
|
y,
|
|
1604
1816
|
}));
|
|
1817
|
+
throwCommandError("inspectAtPosition", resp.error);
|
|
1605
1818
|
return {
|
|
1606
1819
|
backendNodeId: resp.backendNodeId,
|
|
1607
1820
|
frameId: resp.frameId,
|
|
@@ -1621,7 +1834,9 @@ export class CloudBrowser {
|
|
|
1621
1834
|
* @param frameId - id of the frame the node lives in; empty string
|
|
1622
1835
|
* targets the main frame
|
|
1623
1836
|
*
|
|
1624
|
-
*
|
|
1837
|
+
* Rejects only on a transport failure - a dead session, a page that is gone,
|
|
1838
|
+
* a broken connection. This call has no semantic failure of its own, so there
|
|
1839
|
+
* are no error codes to branch on.
|
|
1625
1840
|
*
|
|
1626
1841
|
* @example
|
|
1627
1842
|
* await browser.highlightNode(res.backendNodeId, res.frameId);
|
|
@@ -1634,7 +1849,8 @@ export class CloudBrowser {
|
|
|
1634
1849
|
});
|
|
1635
1850
|
if (frameId)
|
|
1636
1851
|
req.frameId = frameId;
|
|
1637
|
-
await this.client.highlightNode(req);
|
|
1852
|
+
const res = await this.client.highlightNode(req);
|
|
1853
|
+
throwCommandError("highlight", res.error);
|
|
1638
1854
|
}
|
|
1639
1855
|
/**
|
|
1640
1856
|
* Pastes text at the current caret using IME-style input.
|
|
@@ -1646,17 +1862,22 @@ export class CloudBrowser {
|
|
|
1646
1862
|
*
|
|
1647
1863
|
* @param text - the text to insert at the caret
|
|
1648
1864
|
*
|
|
1649
|
-
* @throws
|
|
1865
|
+
* @throws no_focus - nothing in the page holds focus, so there is no caret to
|
|
1866
|
+
* insert at; click the field first
|
|
1867
|
+
* @throws busy - another action is already running on this page
|
|
1868
|
+
*
|
|
1869
|
+
* @see {@link CommandError} for reading the code off the rejection
|
|
1650
1870
|
*
|
|
1651
1871
|
* @example
|
|
1652
1872
|
* await browser.insertText("hello world");
|
|
1653
1873
|
*/
|
|
1654
1874
|
async insertText(text) {
|
|
1655
|
-
await this.client.insertText(create(InsertTextRequestSchema, {
|
|
1875
|
+
const res = await this.client.insertText(create(InsertTextRequestSchema, {
|
|
1656
1876
|
sessionId: this.sessionId,
|
|
1657
1877
|
apiKey: this.apiKey,
|
|
1658
1878
|
text,
|
|
1659
1879
|
}));
|
|
1880
|
+
throwCommandError("insertText", res.error);
|
|
1660
1881
|
}
|
|
1661
1882
|
/**
|
|
1662
1883
|
* Types `text` into the currently focused element as a per-key stream of real
|
|
@@ -1678,7 +1899,10 @@ export class CloudBrowser {
|
|
|
1678
1899
|
* @param opts - optional: `clearFirst` clears the focused field (Ctrl+A,
|
|
1679
1900
|
* Delete) before typing
|
|
1680
1901
|
*
|
|
1681
|
-
*
|
|
1902
|
+
* `type` has no semantic failure of its own: the keys land wherever focus
|
|
1903
|
+
* happens to be, so there is no target it can miss. Only the page or context
|
|
1904
|
+
* being torn down mid-stream surfaces, and that is a transport failure rather
|
|
1905
|
+
* than a code.
|
|
1682
1906
|
*
|
|
1683
1907
|
* @example
|
|
1684
1908
|
* // OTP field that auto-advances across boxes.
|
|
@@ -1693,7 +1917,7 @@ export class CloudBrowser {
|
|
|
1693
1917
|
});
|
|
1694
1918
|
if (opts?.clearFirst)
|
|
1695
1919
|
req.clearFirst = true;
|
|
1696
|
-
await this.client.type(req);
|
|
1920
|
+
throwCommandError("type", (await this.client.type(req)).error);
|
|
1697
1921
|
}
|
|
1698
1922
|
/**
|
|
1699
1923
|
* Fires a single key-down event.
|
|
@@ -1708,7 +1932,11 @@ export class CloudBrowser {
|
|
|
1708
1932
|
* `modifiers` (bit-flag: Alt=1, Ctrl=2, Meta=4, Shift=8),
|
|
1709
1933
|
* `location` (0=standard, 1=left, 2=right, 3=numpad)
|
|
1710
1934
|
*
|
|
1711
|
-
* @throws
|
|
1935
|
+
* @throws no_focus - nothing in the page holds focus, so the key has nowhere
|
|
1936
|
+
* to go; click the field first
|
|
1937
|
+
* @throws busy - another action is already running on this page
|
|
1938
|
+
*
|
|
1939
|
+
* @see {@link CommandError} for reading the code off the rejection
|
|
1712
1940
|
*
|
|
1713
1941
|
* @example
|
|
1714
1942
|
* // Ctrl+A
|
|
@@ -1727,7 +1955,8 @@ export class CloudBrowser {
|
|
|
1727
1955
|
req.modifiers = opts.modifiers;
|
|
1728
1956
|
if (opts?.location !== undefined)
|
|
1729
1957
|
req.location = opts.location;
|
|
1730
|
-
await this.client.pressKey(req);
|
|
1958
|
+
const res = await this.client.pressKey(req);
|
|
1959
|
+
throwCommandError("pressKey", res.error);
|
|
1731
1960
|
}
|
|
1732
1961
|
/**
|
|
1733
1962
|
* Fires a single key-up event.
|
|
@@ -1753,7 +1982,8 @@ export class CloudBrowser {
|
|
|
1753
1982
|
req.modifiers = opts.modifiers;
|
|
1754
1983
|
if (opts?.location !== undefined)
|
|
1755
1984
|
req.location = opts.location;
|
|
1756
|
-
await this.client.releaseKey(req);
|
|
1985
|
+
const res = await this.client.releaseKey(req);
|
|
1986
|
+
throwCommandError("releaseKey", res.error);
|
|
1757
1987
|
}
|
|
1758
1988
|
/**
|
|
1759
1989
|
* Returns the current text selection.
|
|
@@ -1764,7 +1994,9 @@ export class CloudBrowser {
|
|
|
1764
1994
|
*
|
|
1765
1995
|
* @returns the selected text, or `""` when nothing is selected
|
|
1766
1996
|
*
|
|
1767
|
-
*
|
|
1997
|
+
* Rejects only on a transport failure - a dead session, a page that is gone,
|
|
1998
|
+
* a broken connection. This call has no semantic failure of its own, so there
|
|
1999
|
+
* are no error codes to branch on.
|
|
1768
2000
|
*
|
|
1769
2001
|
* @example
|
|
1770
2002
|
* const sel = await browser.getSelection();
|
|
@@ -1775,6 +2007,7 @@ export class CloudBrowser {
|
|
|
1775
2007
|
sessionId: this.sessionId,
|
|
1776
2008
|
apiKey: this.apiKey,
|
|
1777
2009
|
}));
|
|
2010
|
+
throwCommandError("getSelection", resp.error);
|
|
1778
2011
|
return resp.text;
|
|
1779
2012
|
}
|
|
1780
2013
|
// ──────────────────────────────────────────────────────────────────
|
|
@@ -1795,8 +2028,10 @@ export class CloudBrowser {
|
|
|
1795
2028
|
*
|
|
1796
2029
|
* @returns empty string on success — the solution is applied server-side
|
|
1797
2030
|
*
|
|
1798
|
-
*
|
|
1799
|
-
*
|
|
2031
|
+
* Rejects when no captcha appeared within imeoutMs, or when the one that did
|
|
2032
|
+
* could not be solved within
|
|
2033
|
+
etryAmount attempts. Neither carries a code:
|
|
2034
|
+
* solving runs outside the page, so there is no per-command code set here.
|
|
1800
2035
|
*
|
|
1801
2036
|
* @example
|
|
1802
2037
|
* await browser.solveCaptcha({ retryAmount: 2 });
|
|
@@ -1824,7 +2059,8 @@ export class CloudBrowser {
|
|
|
1824
2059
|
*
|
|
1825
2060
|
* @returns the ICE servers for the client `RTCPeerConnection`
|
|
1826
2061
|
*
|
|
1827
|
-
*
|
|
2062
|
+
* Rejects when TURN is not configured on the server. That is a deployment
|
|
2063
|
+
* condition rather than a per-call outcome, so it carries no code.
|
|
1828
2064
|
*
|
|
1829
2065
|
* @example
|
|
1830
2066
|
* const ice = await browser.getStreamConfig();
|
|
@@ -1851,8 +2087,16 @@ export class CloudBrowser {
|
|
|
1851
2087
|
* @returns the SDP answer to apply as the remote description, plus the
|
|
1852
2088
|
* viewport to map input coordinates into
|
|
1853
2089
|
*
|
|
1854
|
-
* @throws
|
|
1855
|
-
*
|
|
2090
|
+
* @throws already_active - a stream is already running on this session; stop it
|
|
2091
|
+
* before starting another
|
|
2092
|
+
* @throws negotiation_failed - the browser could not agree on a connection. The
|
|
2093
|
+
* message carries the negotiator's own diagnostic, which is usually where the
|
|
2094
|
+
* actual cause is
|
|
2095
|
+
*
|
|
2096
|
+
* An empty offer or an unconfigured TURN setup is a caller mistake rather than
|
|
2097
|
+
* an outcome, and rejects separately.
|
|
2098
|
+
*
|
|
2099
|
+
* @see {@link CommandError} for reading the code off the rejection
|
|
1856
2100
|
*
|
|
1857
2101
|
* @example
|
|
1858
2102
|
* const { answerSdp, viewport } = await browser.startStream(offer.sdp);
|
|
@@ -1864,6 +2108,7 @@ export class CloudBrowser {
|
|
|
1864
2108
|
apiKey: this.apiKey,
|
|
1865
2109
|
offerSdp,
|
|
1866
2110
|
}));
|
|
2111
|
+
throwCommandError("startStream", resp.error);
|
|
1867
2112
|
return {
|
|
1868
2113
|
answerSdp: resp.answerSdp,
|
|
1869
2114
|
viewport: resp.viewport
|
|
@@ -1875,25 +2120,27 @@ export class CloudBrowser {
|
|
|
1875
2120
|
* Tears down the live video stream for the session's page. Safe to call even
|
|
1876
2121
|
* if no stream is running.
|
|
1877
2122
|
*
|
|
1878
|
-
*
|
|
2123
|
+
* Rejects only on a transport failure. Stopping a stream that is not running is
|
|
2124
|
+
* a no-op rather than a failure, so there are no error codes to branch on.
|
|
1879
2125
|
*
|
|
1880
2126
|
* @example
|
|
1881
2127
|
* await browser.stopStream();
|
|
1882
2128
|
*/
|
|
1883
2129
|
async stopStream() {
|
|
1884
|
-
await this.client.stopStream(create(StopStreamRequestSchema, {
|
|
2130
|
+
const res = await this.client.stopStream(create(StopStreamRequestSchema, {
|
|
1885
2131
|
sessionId: this.sessionId,
|
|
1886
2132
|
apiKey: this.apiKey,
|
|
1887
2133
|
}));
|
|
2134
|
+
throwCommandError("stopStream", res.error);
|
|
1888
2135
|
}
|
|
1889
2136
|
// ──────────────────────────────────────────────────────────────────
|
|
1890
2137
|
// Reactions
|
|
1891
2138
|
// ──────────────────────────────────────────────────────────────────
|
|
1892
2139
|
/**
|
|
1893
|
-
* Registers a one-shot "reaction":
|
|
1894
|
-
*
|
|
1895
|
-
*
|
|
1896
|
-
*
|
|
2140
|
+
* Registers a one-shot "reaction": the browser watches for the `match`
|
|
2141
|
+
* locator in the background and, as soon as it matches, clicks it the same
|
|
2142
|
+
* way {@link CloudBrowser.click} does (scroll, human path, occlusion check) —
|
|
2143
|
+
* then removes itself. The reaction yields to any in-flight input
|
|
1897
2144
|
* action and only fires while the pointer is idle, so a reaction naturally
|
|
1898
2145
|
* slots into the gaps of a retrying foreground action (e.g. it dismisses a
|
|
1899
2146
|
* newsletter modal blocking a {@link CloudBrowser.click}, after which the
|
|
@@ -1951,6 +2198,7 @@ export class CloudBrowser {
|
|
|
1951
2198
|
if (opts?.intervalMs)
|
|
1952
2199
|
req.interval = opts.intervalMs;
|
|
1953
2200
|
const resp = await this.client.addReaction(req);
|
|
2201
|
+
throwCommandError("addReaction", resp.error);
|
|
1954
2202
|
return resp.reactionId;
|
|
1955
2203
|
}
|
|
1956
2204
|
/**
|
|
@@ -1961,6 +2209,10 @@ export class CloudBrowser {
|
|
|
1961
2209
|
*
|
|
1962
2210
|
* @returns true if a pending reaction with this id existed and was removed
|
|
1963
2211
|
*
|
|
2212
|
+
* Removing an id that is not registered is a no-op rather than an error, so the
|
|
2213
|
+
* returned boolean - not a rejection - is what tells you whether anything was
|
|
2214
|
+
* there. Rejects only on a transport failure.
|
|
2215
|
+
*
|
|
1964
2216
|
* @example
|
|
1965
2217
|
* const removed = await browser.removeReaction(id);
|
|
1966
2218
|
*/
|
|
@@ -1970,6 +2222,7 @@ export class CloudBrowser {
|
|
|
1970
2222
|
apiKey: this.apiKey,
|
|
1971
2223
|
reactionId,
|
|
1972
2224
|
}));
|
|
2225
|
+
throwCommandError("removeReaction", resp.error);
|
|
1973
2226
|
return resp.removed;
|
|
1974
2227
|
}
|
|
1975
2228
|
/**
|
|
@@ -1978,6 +2231,10 @@ export class CloudBrowser {
|
|
|
1978
2231
|
*
|
|
1979
2232
|
* @returns the pending reactions for the page
|
|
1980
2233
|
*
|
|
2234
|
+
* Rejects only on a transport failure - a dead session, a page that is gone,
|
|
2235
|
+
* a broken connection. This call has no semantic failure of its own, so there
|
|
2236
|
+
* are no error codes to branch on.
|
|
2237
|
+
*
|
|
1981
2238
|
* @example
|
|
1982
2239
|
* const pending = await browser.listReactions();
|
|
1983
2240
|
* for (const r of pending) console.log(r.reactionId, r.matchSelector);
|
|
@@ -1987,6 +2244,7 @@ export class CloudBrowser {
|
|
|
1987
2244
|
sessionId: this.sessionId,
|
|
1988
2245
|
apiKey: this.apiKey,
|
|
1989
2246
|
}));
|
|
2247
|
+
throwCommandError("listReactions", resp.error);
|
|
1990
2248
|
return resp.reactions.map((r) => ({
|
|
1991
2249
|
reactionId: r.reactionId,
|
|
1992
2250
|
matchSelector: r.matchSelector ?? "",
|
|
@@ -2023,7 +2281,10 @@ export class CloudBrowser {
|
|
|
2023
2281
|
* @returns the return value and the script's whole console output. A script
|
|
2024
2282
|
* that threw is reported as `success: false`, not as a rejection
|
|
2025
2283
|
*
|
|
2026
|
-
*
|
|
2284
|
+
* Rejects only on a transport failure. A script that fails to compile or throws
|
|
2285
|
+
* is not a rejection: the returned result has success: false and
|
|
2286
|
+
esult holds
|
|
2287
|
+
* the message, so a broken script stays distinguishable from a broken connection.
|
|
2027
2288
|
*
|
|
2028
2289
|
* @example
|
|
2029
2290
|
* const result = await browser.runScript(`
|
|
@@ -2071,7 +2332,8 @@ export class CloudBrowser {
|
|
|
2071
2332
|
*
|
|
2072
2333
|
* @returns ScriptRun handle for awaiting or cancelling the run
|
|
2073
2334
|
*
|
|
2074
|
-
*
|
|
2335
|
+
* Rejects only on a transport failure: a script that fails to compile or throws
|
|
2336
|
+
* surfaces on the run itself rather than here.
|
|
2075
2337
|
*
|
|
2076
2338
|
* @example
|
|
2077
2339
|
* const run = await browser.startScript(source, (ev) => {
|
|
@@ -2156,7 +2418,8 @@ export class CloudBrowser {
|
|
|
2156
2418
|
*
|
|
2157
2419
|
* @returns ScriptFollow handle for stopping the subscription
|
|
2158
2420
|
*
|
|
2159
|
-
*
|
|
2421
|
+
* Rejects only on a transport failure: opening the subscription has no semantic
|
|
2422
|
+
* failure of its own.
|
|
2160
2423
|
*
|
|
2161
2424
|
* @example
|
|
2162
2425
|
* const follow = await browser.followScript(runId, (ev) => {
|
|
@@ -2189,7 +2452,9 @@ export class CloudBrowser {
|
|
|
2189
2452
|
* @returns how many runs were cancelled; 0 when the id named nothing in
|
|
2190
2453
|
* flight
|
|
2191
2454
|
*
|
|
2192
|
-
*
|
|
2455
|
+
* Rejects only on a transport failure. Cancelling runs that have already
|
|
2456
|
+
* finished, or none at all, is a no-op - read the returned count to learn how
|
|
2457
|
+
* many were actually stopped.
|
|
2193
2458
|
*
|
|
2194
2459
|
* @example
|
|
2195
2460
|
* await browser.stopScripts(""); // abandon everything running
|
|
@@ -2212,7 +2477,9 @@ export class CloudBrowser {
|
|
|
2212
2477
|
*
|
|
2213
2478
|
* @returns one entry per run still executing
|
|
2214
2479
|
*
|
|
2215
|
-
*
|
|
2480
|
+
* Rejects only on a transport failure - a dead session, a broken connection.
|
|
2481
|
+
* This call has no semantic failure of its own, so there are no error codes to
|
|
2482
|
+
* branch on.
|
|
2216
2483
|
*
|
|
2217
2484
|
* @example
|
|
2218
2485
|
* for (const run of await browser.listScriptRuns()) {
|