single-file-cli 2.3.0 → 2.3.1
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/build.sh +1 -1
- package/deno.json +1 -1
- package/lib/cdp-client.js +123 -41
- package/lib/single-file-archive.js +4 -4
- package/lib/single-file-bundle.js +1 -1
- package/lib/version.js +1 -1
- package/options.js +1 -1
- package/package.json +1 -1
- package/single-file-cli-api.js +6 -0
- package/test/e2e/oopif.test.js +86 -0
package/build.sh
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
mv package.json package.json.tmp
|
|
4
4
|
mv deno.json deno.json.tmp
|
|
5
5
|
mv deno.lock deno.lock.tmp
|
|
6
|
-
deno install --vendor --quiet --minimum-dependency-age=0 "npm:single-file-core@1.5.
|
|
6
|
+
deno install --vendor --quiet --minimum-dependency-age=0 "npm:single-file-core@1.5.98"
|
|
7
7
|
mv package.json.tmp package.json
|
|
8
8
|
mv deno.json.tmp deno.json
|
|
9
9
|
mv deno.lock.tmp deno.lock
|
package/deno.json
CHANGED
package/lib/cdp-client.js
CHANGED
|
@@ -102,7 +102,10 @@ function relaunchBrowser() {
|
|
|
102
102
|
|
|
103
103
|
async function getPageData(options) {
|
|
104
104
|
const EMPTY_PAGE_URL = "about:blank";
|
|
105
|
-
|
|
105
|
+
// compiled here so that an invalid pattern is reported before the page is
|
|
106
|
+
// loaded, instead of throwing for every request that is intercepted
|
|
107
|
+
const blockedURLPatterns = (options.blockedURLPatterns || []).map(pattern => new RegExp(pattern));
|
|
108
|
+
const pageContext = { options, consoleMessages: [], debugMessages: [], httpInfo: {}, blockedURLPatterns, fetchAbortController: new AbortController() };
|
|
106
109
|
let targetInfo, cdp;
|
|
107
110
|
try {
|
|
108
111
|
logData(["Loading page", EMPTY_PAGE_URL], pageContext);
|
|
@@ -205,25 +208,28 @@ async function setupBrowserWindow({ Browser }, targetId, { options, debugMessage
|
|
|
205
208
|
}
|
|
206
209
|
}
|
|
207
210
|
|
|
208
|
-
async function setupSecurity({ Security }, { options, debugMessages }) {
|
|
211
|
+
async function setupSecurity({ Security }, { options, debugMessages }, sessionId) {
|
|
209
212
|
if (options.browserIgnoreHTTPSErrors !== undefined && options.browserIgnoreHTTPSErrors) {
|
|
210
213
|
logData(["Ignoring HTTPS errors"], { options, debugMessages });
|
|
211
|
-
await Security.setIgnoreCertificateErrors({ ignore: true });
|
|
214
|
+
await Security.setIgnoreCertificateErrors({ ignore: true }, sessionId);
|
|
212
215
|
}
|
|
213
216
|
}
|
|
214
217
|
|
|
215
218
|
async function setupDeviceEmulation({ Browser, Emulation, Runtime }, { options, debugMessages }) {
|
|
216
219
|
const needsDeviceMetrics = options.browserMobileEmulation || options.browserDeviceWidth ||
|
|
217
220
|
options.browserDeviceHeight || options.browserDeviceScaleFactor;
|
|
218
|
-
const needsUserAgent = options.browserMobileEmulation || options.platform || options.acceptLanguage;
|
|
219
221
|
if (needsDeviceMetrics) {
|
|
220
222
|
await setupDeviceMetrics({ Emulation, Runtime }, { options, debugMessages });
|
|
221
223
|
}
|
|
222
|
-
if (
|
|
224
|
+
if (needsUserAgentOverride(options)) {
|
|
223
225
|
await setupUserAgent({ Browser, Emulation }, { options, debugMessages });
|
|
224
226
|
}
|
|
225
227
|
}
|
|
226
228
|
|
|
229
|
+
function needsUserAgentOverride(options) {
|
|
230
|
+
return Boolean(options.browserMobileEmulation || options.platform || options.acceptLanguage);
|
|
231
|
+
}
|
|
232
|
+
|
|
227
233
|
async function setupDeviceMetrics({ Emulation, Runtime }, { options, debugMessages }) {
|
|
228
234
|
const INNER_WIDTH_PROPERTY = "window.innerWidth";
|
|
229
235
|
const INNER_HEIGHT_PROPERTY = "window.innerHeight";
|
|
@@ -244,7 +250,7 @@ async function setupDeviceMetrics({ Emulation, Runtime }, { options, debugMessag
|
|
|
244
250
|
await Emulation.setDeviceMetricsOverride(deviceMetricsOptions);
|
|
245
251
|
}
|
|
246
252
|
|
|
247
|
-
async function setupUserAgent({ Browser, Emulation }, { options, debugMessages }) {
|
|
253
|
+
async function setupUserAgent({ Browser, Emulation }, { options, debugMessages }, sessionId) {
|
|
248
254
|
const ANDROID_PLATFORM = "Android";
|
|
249
255
|
const { userAgent, product } = await Browser.getVersion();
|
|
250
256
|
const defaultMobileUA = `Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) ${product} Mobile Safari/537.36`;
|
|
@@ -258,22 +264,17 @@ async function setupUserAgent({ Browser, Emulation }, { options, debugMessages }
|
|
|
258
264
|
agentOptions.platform = options.platform || ANDROID_PLATFORM;
|
|
259
265
|
}
|
|
260
266
|
logData(["Emulating user agent", JSON.stringify(agentOptions)], { options, debugMessages });
|
|
261
|
-
await Emulation.setUserAgentOverride(agentOptions);
|
|
267
|
+
await Emulation.setUserAgentOverride(agentOptions, sessionId);
|
|
262
268
|
}
|
|
263
269
|
|
|
264
|
-
async function setupNetworkInterception({ Browser, Emulation, Fetch, Network }, { options, debugMessages, httpInfo }) {
|
|
265
|
-
const REQUEST_STAGE = "Request";
|
|
266
|
-
const RESPONSE_STAGE = "Response";
|
|
270
|
+
async function setupNetworkInterception({ Browser, Emulation, Fetch, Network }, { options, debugMessages, httpInfo, blockedURLPatterns }) {
|
|
267
271
|
const DENY_BEHAVIOR = "deny";
|
|
268
|
-
const handleAuthRequests =
|
|
269
|
-
const patterns = handleAuthRequests ?
|
|
270
|
-
[{ requestStage: REQUEST_STAGE }, { requestStage: RESPONSE_STAGE }] :
|
|
271
|
-
[{ requestStage: RESPONSE_STAGE }];
|
|
272
|
+
const { handleAuthRequests, patterns } = getInterceptionOptions(options);
|
|
272
273
|
await Fetch.enable({ handleAuthRequests, patterns });
|
|
273
274
|
if (handleAuthRequests) {
|
|
274
275
|
setupProxyAuth({ Fetch }, { options, debugMessages });
|
|
275
276
|
}
|
|
276
|
-
setupRequestInterception({ Fetch }, { options, debugMessages, httpInfo });
|
|
277
|
+
setupRequestInterception({ Fetch }, { options, debugMessages, httpInfo, blockedURLPatterns });
|
|
277
278
|
if (options.httpHeaders) {
|
|
278
279
|
await setupHttpHeaders({ Network }, { options, debugMessages });
|
|
279
280
|
}
|
|
@@ -286,10 +287,20 @@ async function setupNetworkInterception({ Browser, Emulation, Fetch, Network },
|
|
|
286
287
|
await Browser.setDownloadBehavior({ behavior: DENY_BEHAVIOR });
|
|
287
288
|
}
|
|
288
289
|
|
|
290
|
+
function getInterceptionOptions(options) {
|
|
291
|
+
const REQUEST_STAGE = "Request";
|
|
292
|
+
const RESPONSE_STAGE = "Response";
|
|
293
|
+
const handleAuthRequests = Boolean(options.httpProxyUsername);
|
|
294
|
+
const patterns = handleAuthRequests ?
|
|
295
|
+
[{ requestStage: REQUEST_STAGE }, { requestStage: RESPONSE_STAGE }] :
|
|
296
|
+
[{ requestStage: RESPONSE_STAGE }];
|
|
297
|
+
return { handleAuthRequests, patterns };
|
|
298
|
+
}
|
|
299
|
+
|
|
289
300
|
function setupProxyAuth({ Fetch }, { options, debugMessages }) {
|
|
290
301
|
const AUTH_REQUIRED_EVENT_TYPE = "authRequired";
|
|
291
302
|
const PROVIDE_CREDENTIALS_RESPONSE = "ProvideCredentials";
|
|
292
|
-
Fetch.addEventListener(AUTH_REQUIRED_EVENT_TYPE, ignoringErrors(async ({ params }) => {
|
|
303
|
+
Fetch.addEventListener(AUTH_REQUIRED_EVENT_TYPE, ignoringErrors(async ({ params, sessionId }) => {
|
|
293
304
|
logData(["Authenticating"], { options, debugMessages });
|
|
294
305
|
await Fetch.continueWithAuth({
|
|
295
306
|
requestId: params.requestId,
|
|
@@ -298,38 +309,37 @@ function setupProxyAuth({ Fetch }, { options, debugMessages }) {
|
|
|
298
309
|
username: options.httpProxyUsername,
|
|
299
310
|
password: options.httpProxyPassword
|
|
300
311
|
}
|
|
301
|
-
});
|
|
312
|
+
}, sessionId);
|
|
302
313
|
}, { options, debugMessages }));
|
|
303
314
|
}
|
|
304
315
|
|
|
305
|
-
function setupRequestInterception({ Fetch }, { options, debugMessages, httpInfo }) {
|
|
316
|
+
function setupRequestInterception({ Fetch }, { options, debugMessages, httpInfo, blockedURLPatterns }) {
|
|
306
317
|
const REQUEST_PAUSED_EVENT_TYPE = "requestPaused";
|
|
307
318
|
const ABORTED_ERROR_REASON = "Aborted";
|
|
308
319
|
const urlState = { url: options.url, alternativeUrl: getAlternativeUrl(options.url) };
|
|
309
|
-
|
|
310
|
-
// loaded, instead of throwing for every request that is intercepted
|
|
311
|
-
const blockedURLPatterns = (options.blockedURLPatterns || []).map(pattern => new RegExp(pattern));
|
|
312
|
-
Fetch.addEventListener(REQUEST_PAUSED_EVENT_TYPE, ignoringErrors(async ({ params }) => {
|
|
320
|
+
Fetch.addEventListener(REQUEST_PAUSED_EVENT_TYPE, ignoringErrors(async ({ params, sessionId }) => {
|
|
313
321
|
const { requestId, request } = params;
|
|
314
322
|
// the request is always resumed below, otherwise the page waits for it
|
|
315
323
|
// until the load timeout expires
|
|
316
324
|
let blocked = false;
|
|
317
325
|
try {
|
|
318
|
-
|
|
326
|
+
if (!sessionId) {
|
|
327
|
+
captureHttpInfo(params, urlState, { options, debugMessages, httpInfo });
|
|
328
|
+
}
|
|
319
329
|
blocked = shouldBlockRequest(request.url);
|
|
320
330
|
} catch (error) {
|
|
321
331
|
logData(["Ignoring request interception error", error.message], { options, debugMessages });
|
|
322
332
|
}
|
|
323
333
|
if (blocked) {
|
|
324
334
|
try {
|
|
325
|
-
await Fetch.failRequest({ requestId, errorReason: ABORTED_ERROR_REASON });
|
|
335
|
+
await Fetch.failRequest({ requestId, errorReason: ABORTED_ERROR_REASON }, sessionId);
|
|
326
336
|
return;
|
|
327
337
|
} catch {
|
|
328
338
|
// ignored
|
|
329
339
|
}
|
|
330
340
|
}
|
|
331
341
|
try {
|
|
332
|
-
await Fetch.continueRequest({ requestId });
|
|
342
|
+
await Fetch.continueRequest({ requestId }, sessionId);
|
|
333
343
|
} catch {
|
|
334
344
|
// ignored
|
|
335
345
|
}
|
|
@@ -381,13 +391,13 @@ function captureHttpInfo(params, urlState, { options, debugMessages, httpInfo })
|
|
|
381
391
|
}
|
|
382
392
|
}
|
|
383
393
|
|
|
384
|
-
async function setupHttpHeaders({ Network }, { options, debugMessages }) {
|
|
394
|
+
async function setupHttpHeaders({ Network }, { options, debugMessages }, sessionId) {
|
|
385
395
|
logData(["Setting HTTP headers", JSON.stringify(options.httpHeaders)], { options, debugMessages });
|
|
386
|
-
await Network.enable();
|
|
387
|
-
await Network.setExtraHTTPHeaders({ headers: options.httpHeaders });
|
|
396
|
+
await Network.enable({}, sessionId);
|
|
397
|
+
await Network.setExtraHTTPHeaders({ headers: options.httpHeaders }, sessionId);
|
|
388
398
|
}
|
|
389
399
|
|
|
390
|
-
async function setupMediaFeatures({ Emulation }, { options, debugMessages }) {
|
|
400
|
+
async function setupMediaFeatures({ Emulation }, { options, debugMessages }, sessionId) {
|
|
391
401
|
const features = [];
|
|
392
402
|
for (const mediaFeature of options.emulateMediaFeatures) {
|
|
393
403
|
logData(["Emulating media feature", mediaFeature.name, mediaFeature.value], { options, debugMessages });
|
|
@@ -395,7 +405,7 @@ async function setupMediaFeatures({ Emulation }, { options, debugMessages }) {
|
|
|
395
405
|
features.push({ name: mediaFeature.name, value: value.trim() });
|
|
396
406
|
}
|
|
397
407
|
}
|
|
398
|
-
await Emulation.setEmulatedMedia({ features });
|
|
408
|
+
await Emulation.setEmulatedMedia({ features }, sessionId);
|
|
399
409
|
}
|
|
400
410
|
|
|
401
411
|
async function setupCookies({ Network }, { options, debugMessages }) {
|
|
@@ -403,16 +413,77 @@ async function setupCookies({ Network }, { options, debugMessages }) {
|
|
|
403
413
|
await Network.setCookies({ cookies: options.browserCookies });
|
|
404
414
|
}
|
|
405
415
|
|
|
406
|
-
async function setupScriptInjection(
|
|
416
|
+
async function setupScriptInjection(cdp, { options, debugMessages }) {
|
|
417
|
+
const { Page } = cdp;
|
|
418
|
+
const scriptSource = await getScriptSource(options);
|
|
407
419
|
await Page.addScriptToEvaluateOnNewDocument({
|
|
408
420
|
source: getHookScriptSource(),
|
|
409
421
|
runImmediately: true
|
|
410
422
|
});
|
|
411
423
|
await Page.addScriptToEvaluateOnNewDocument({
|
|
412
|
-
source:
|
|
424
|
+
source: scriptSource,
|
|
413
425
|
runImmediately: true,
|
|
414
426
|
worldName: SINGLE_FILE_WORLD_NAME
|
|
415
427
|
});
|
|
428
|
+
await setupFrameScriptInjection(cdp, scriptSource, { options, debugMessages });
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
// out-of-process frames are separate targets, so the scripts registered above
|
|
432
|
+
// do not reach them; they are attached paused, injected and resumed instead
|
|
433
|
+
async function setupFrameScriptInjection(cdp, scriptSource, { options, debugMessages }) {
|
|
434
|
+
const { Page, Runtime, Target } = cdp;
|
|
435
|
+
const ATTACHED_TO_TARGET_EVENT_TYPE = "attachedToTarget";
|
|
436
|
+
const IFRAME_TARGET_TYPE = "iframe";
|
|
437
|
+
const autoAttachOptions = { autoAttach: true, waitForDebuggerOnStart: true, flatten: true };
|
|
438
|
+
Target.addEventListener(ATTACHED_TO_TARGET_EVENT_TYPE, ignoringErrors(async ({ params }) => {
|
|
439
|
+
const { sessionId, targetInfo, waitingForDebugger } = params;
|
|
440
|
+
try {
|
|
441
|
+
if (targetInfo.type === IFRAME_TARGET_TYPE) {
|
|
442
|
+
logData(["Injecting scripts into frame", targetInfo.url], { options, debugMessages });
|
|
443
|
+
// the scripts registered below only run when the Page domain is
|
|
444
|
+
// enabled on the session
|
|
445
|
+
await Page.enable({}, sessionId);
|
|
446
|
+
await Target.setAutoAttach(autoAttachOptions, sessionId);
|
|
447
|
+
await Page.addScriptToEvaluateOnNewDocument({
|
|
448
|
+
source: getHookScriptSource(),
|
|
449
|
+
runImmediately: true
|
|
450
|
+
}, sessionId);
|
|
451
|
+
await Page.addScriptToEvaluateOnNewDocument({
|
|
452
|
+
source: scriptSource,
|
|
453
|
+
runImmediately: true,
|
|
454
|
+
worldName: SINGLE_FILE_WORLD_NAME
|
|
455
|
+
}, sessionId);
|
|
456
|
+
await Runtime.addBinding({ name: FETCH_FUNCTION_NAME, executionContextName: SINGLE_FILE_WORLD_NAME }, sessionId);
|
|
457
|
+
await setupFrameSession(cdp, sessionId, { options, debugMessages });
|
|
458
|
+
}
|
|
459
|
+
} finally {
|
|
460
|
+
// the target is always resumed, even when a setup command fails,
|
|
461
|
+
// otherwise it would stay paused until the load timeout expires
|
|
462
|
+
if (waitingForDebugger) {
|
|
463
|
+
await Runtime.runIfWaitingForDebugger({}, sessionId);
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
}, { options, debugMessages }));
|
|
467
|
+
await Target.setAutoAttach(autoAttachOptions);
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
async function setupFrameSession({ Browser, Emulation, Fetch, Network, Security }, sessionId, { options, debugMessages }) {
|
|
471
|
+
const { handleAuthRequests, patterns } = getInterceptionOptions(options);
|
|
472
|
+
if (handleAuthRequests || (options.blockedURLPatterns && options.blockedURLPatterns.length)) {
|
|
473
|
+
// the requestPaused and authRequired listeners are registered once on
|
|
474
|
+
// the connection and receive the events of every session
|
|
475
|
+
await Fetch.enable({ handleAuthRequests, patterns }, sessionId);
|
|
476
|
+
}
|
|
477
|
+
await setupSecurity({ Security }, { options, debugMessages }, sessionId);
|
|
478
|
+
if (options.httpHeaders) {
|
|
479
|
+
await setupHttpHeaders({ Network }, { options, debugMessages }, sessionId);
|
|
480
|
+
}
|
|
481
|
+
if (options.emulateMediaFeatures) {
|
|
482
|
+
await setupMediaFeatures({ Emulation }, { options, debugMessages }, sessionId);
|
|
483
|
+
}
|
|
484
|
+
if (needsUserAgentOverride(options)) {
|
|
485
|
+
await setupUserAgent({ Browser, Emulation }, { options, debugMessages }, sessionId);
|
|
486
|
+
}
|
|
416
487
|
}
|
|
417
488
|
|
|
418
489
|
async function getContextId({ Debugger, Page, Runtime }, { options, debugMessages }) {
|
|
@@ -578,7 +649,7 @@ function setupPageDataCapture({ Runtime }, { options, debugMessages }) {
|
|
|
578
649
|
});
|
|
579
650
|
}
|
|
580
651
|
|
|
581
|
-
async function setupBindings({ Page, Runtime }, { options, debugMessages, fetchAbortController }) {
|
|
652
|
+
async function setupBindings({ Page, Runtime }, { options, debugMessages, blockedURLPatterns, fetchAbortController }) {
|
|
582
653
|
await Runtime.addBinding({ name: SET_PAGE_DATA_FUNCTION_NAME, executionContextName: SINGLE_FILE_WORLD_NAME });
|
|
583
654
|
if (options.embedScreenshot && options.compressContent) {
|
|
584
655
|
await setupScreenshotCapture({ Page, Runtime }, { options, debugMessages });
|
|
@@ -587,9 +658,9 @@ async function setupBindings({ Page, Runtime }, { options, debugMessages, fetchA
|
|
|
587
658
|
await setupPdfCapture({ Page, Runtime }, { options, debugMessages });
|
|
588
659
|
}
|
|
589
660
|
await Runtime.addBinding({ name: FETCH_FUNCTION_NAME, executionContextName: SINGLE_FILE_WORLD_NAME });
|
|
590
|
-
Runtime.addEventListener(BINDING_CALLED_EVENT_TYPE, ignoringErrors(async ({ params }) => {
|
|
661
|
+
Runtime.addEventListener(BINDING_CALLED_EVENT_TYPE, ignoringErrors(async ({ params, sessionId }) => {
|
|
591
662
|
if (params.name === FETCH_FUNCTION_NAME) {
|
|
592
|
-
await handleFetchRequest({ Runtime }, params, { options, debugMessages, fetchAbortController });
|
|
663
|
+
await handleFetchRequest({ Runtime }, params, { options, debugMessages, blockedURLPatterns, fetchAbortController }, sessionId);
|
|
593
664
|
}
|
|
594
665
|
}, { options, debugMessages }));
|
|
595
666
|
}
|
|
@@ -652,12 +723,23 @@ async function setupPdfCapture({ Page, Runtime }, { options, debugMessages }) {
|
|
|
652
723
|
}
|
|
653
724
|
}
|
|
654
725
|
|
|
655
|
-
async function handleFetchRequest({ Runtime }, params, { options, debugMessages, fetchAbortController }) {
|
|
726
|
+
async function handleFetchRequest({ Runtime }, params, { options, debugMessages, blockedURLPatterns, fetchAbortController }, sessionId) {
|
|
727
|
+
const BLOCKED_URL_ERROR_MESSAGE = "Blocked URL";
|
|
656
728
|
const { executionContextId: contextId, payload } = params;
|
|
657
729
|
const { requestId, url, options: fetchOptions } = JSON.parse(payload);
|
|
658
730
|
logData(["Fetching URL", url], { options, debugMessages });
|
|
659
731
|
try {
|
|
660
|
-
|
|
732
|
+
// this fetch does not go through the browser, so the URL blocking and
|
|
733
|
+
// the extra HTTP headers must be applied here too
|
|
734
|
+
if (blockedURLPatterns.some(pattern => pattern.test(url))) {
|
|
735
|
+
logData(["Blocking request", url], { options, debugMessages });
|
|
736
|
+
throw new Error(BLOCKED_URL_ERROR_MESSAGE);
|
|
737
|
+
}
|
|
738
|
+
const headers = Object.assign({}, fetchOptions.headers, options.httpHeaders);
|
|
739
|
+
if (options.userAgent) {
|
|
740
|
+
headers["user-agent"] = options.userAgent;
|
|
741
|
+
}
|
|
742
|
+
const response = await fetch(url, Object.assign({}, fetchOptions, { headers, signal: fetchAbortController.signal }));
|
|
661
743
|
const arrayBuffer = await response.arrayBuffer();
|
|
662
744
|
const base64Data = arrayBufferToBase64(arrayBuffer);
|
|
663
745
|
const result = {
|
|
@@ -665,22 +747,22 @@ async function handleFetchRequest({ Runtime }, params, { options, debugMessages,
|
|
|
665
747
|
headers: Object.fromEntries(response.headers.entries()),
|
|
666
748
|
data: base64Data
|
|
667
749
|
};
|
|
668
|
-
await callBrowserFunction({ Runtime }, contextId, RESOLVE_FETCH_FUNCTION_NAME, [requestId, result]);
|
|
750
|
+
await callBrowserFunction({ Runtime }, contextId, RESOLVE_FETCH_FUNCTION_NAME, [requestId, result], sessionId);
|
|
669
751
|
} catch (error) {
|
|
670
752
|
const errorResult = {
|
|
671
753
|
error: error.message,
|
|
672
754
|
code: error.code
|
|
673
755
|
};
|
|
674
|
-
await callBrowserFunction({ Runtime }, contextId, REJECT_FETCH_FUNCTION_NAME, [requestId, errorResult]);
|
|
756
|
+
await callBrowserFunction({ Runtime }, contextId, REJECT_FETCH_FUNCTION_NAME, [requestId, errorResult], sessionId);
|
|
675
757
|
}
|
|
676
758
|
}
|
|
677
759
|
|
|
678
|
-
async function callBrowserFunction({ Runtime }, contextId, functionName, args) {
|
|
760
|
+
async function callBrowserFunction({ Runtime }, contextId, functionName, args, sessionId) {
|
|
679
761
|
const serializedArgs = args.map(arg => JSON.stringify(arg)).join(", ");
|
|
680
762
|
await Runtime.evaluate({
|
|
681
763
|
expression: `globalThis.${functionName}(${serializedArgs})`,
|
|
682
764
|
contextId
|
|
683
|
-
});
|
|
765
|
+
}, sessionId);
|
|
684
766
|
}
|
|
685
767
|
|
|
686
768
|
async function capturePageData({ Runtime }, contextId, { options, debugMessages }) {
|