@ricsam/isolate-client 0.1.13 → 0.1.15
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 +237 -8
- package/dist/cjs/connection.cjs +254 -75
- package/dist/cjs/connection.cjs.map +3 -3
- package/dist/cjs/package.json +1 -1
- package/dist/mjs/connection.mjs +257 -76
- package/dist/mjs/connection.mjs.map +3 -3
- package/dist/mjs/package.json +1 -1
- package/dist/types/types.d.ts +4 -12
- package/package.json +1 -1
package/dist/cjs/connection.cjs
CHANGED
|
@@ -50,7 +50,10 @@ var import_node_path = __toESM(require("node:path"));
|
|
|
50
50
|
var import_isolate_protocol = require("@ricsam/isolate-protocol");
|
|
51
51
|
var import_client = require("@ricsam/isolate-playwright/client");
|
|
52
52
|
var DEFAULT_TIMEOUT = 30000;
|
|
53
|
+
var TEST_REQUEST_TIMEOUT_BUFFER_MS = 1000;
|
|
53
54
|
var isolateWsCallbacks = new Map;
|
|
55
|
+
var isolateClientWebSockets = new Map;
|
|
56
|
+
var isolateWebSocketCallbacks = new Map;
|
|
54
57
|
async function connect(options = {}) {
|
|
55
58
|
const socket = await createSocket(options);
|
|
56
59
|
const state = {
|
|
@@ -214,6 +217,21 @@ function handleMessage(message, state) {
|
|
|
214
217
|
}
|
|
215
218
|
break;
|
|
216
219
|
}
|
|
220
|
+
case import_isolate_protocol.MessageType.CLIENT_WS_CONNECT: {
|
|
221
|
+
const msg = message;
|
|
222
|
+
handleClientWsConnect(msg, state);
|
|
223
|
+
break;
|
|
224
|
+
}
|
|
225
|
+
case import_isolate_protocol.MessageType.CLIENT_WS_SEND: {
|
|
226
|
+
const msg = message;
|
|
227
|
+
handleClientWsSend(msg, state);
|
|
228
|
+
break;
|
|
229
|
+
}
|
|
230
|
+
case import_isolate_protocol.MessageType.CLIENT_WS_CLOSE: {
|
|
231
|
+
const msg = message;
|
|
232
|
+
handleClientWsClose(msg, state);
|
|
233
|
+
break;
|
|
234
|
+
}
|
|
217
235
|
case import_isolate_protocol.MessageType.RESPONSE_STREAM_START: {
|
|
218
236
|
const msg = message;
|
|
219
237
|
const receiver = {
|
|
@@ -449,74 +467,76 @@ async function createRuntime(state, options = {}, namespaceId) {
|
|
|
449
467
|
const networkResponses = [];
|
|
450
468
|
const pageListenerCleanups = [];
|
|
451
469
|
if (options.playwright) {
|
|
452
|
-
playwrightHandler =
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
}
|
|
470
|
+
playwrightHandler = options.playwright.handler;
|
|
471
|
+
if (!playwrightHandler) {
|
|
472
|
+
throw new Error("playwright.handler is required when using playwright options");
|
|
473
|
+
}
|
|
474
|
+
const page = import_client.getDefaultPlaywrightHandlerMetadata(playwrightHandler)?.page;
|
|
456
475
|
const handlerCallbackId = state.nextCallbackId++;
|
|
457
476
|
state.callbacks.set(handlerCallbackId, async (opJson) => {
|
|
458
477
|
const op = JSON.parse(opJson);
|
|
459
478
|
const result2 = await playwrightHandler(op);
|
|
460
479
|
return JSON.stringify(result2);
|
|
461
480
|
});
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
481
|
+
if (page) {
|
|
482
|
+
const onConsole = (msg) => {
|
|
483
|
+
const entry = {
|
|
484
|
+
level: msg.type(),
|
|
485
|
+
stdout: msg.text(),
|
|
486
|
+
timestamp: Date.now()
|
|
487
|
+
};
|
|
488
|
+
browserConsoleLogs.push(entry);
|
|
489
|
+
if (options.playwright.onEvent) {
|
|
490
|
+
options.playwright.onEvent({
|
|
491
|
+
type: "browserConsoleLog",
|
|
492
|
+
...entry
|
|
493
|
+
});
|
|
494
|
+
}
|
|
495
|
+
if (options.playwright.console && options.console?.onEntry) {
|
|
496
|
+
options.console.onEntry({
|
|
497
|
+
type: "browserOutput",
|
|
498
|
+
...entry
|
|
499
|
+
});
|
|
500
|
+
} else if (options.playwright.console) {
|
|
501
|
+
const prefix = entry.level === "error" ? "[browser:error]" : "[browser]";
|
|
502
|
+
console.log(prefix, entry.stdout);
|
|
503
|
+
}
|
|
468
504
|
};
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
options.
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
console.log(prefix, entry.stdout);
|
|
484
|
-
}
|
|
485
|
-
};
|
|
486
|
-
const onRequest = (request2) => {
|
|
487
|
-
const info = {
|
|
488
|
-
url: request2.url(),
|
|
489
|
-
method: request2.method(),
|
|
490
|
-
headers: request2.headers(),
|
|
491
|
-
timestamp: Date.now()
|
|
505
|
+
const onRequest = (request2) => {
|
|
506
|
+
const info = {
|
|
507
|
+
url: request2.url(),
|
|
508
|
+
method: request2.method(),
|
|
509
|
+
headers: request2.headers(),
|
|
510
|
+
timestamp: Date.now()
|
|
511
|
+
};
|
|
512
|
+
networkRequests.push(info);
|
|
513
|
+
if (options.playwright.onEvent) {
|
|
514
|
+
options.playwright.onEvent({
|
|
515
|
+
type: "networkRequest",
|
|
516
|
+
...info
|
|
517
|
+
});
|
|
518
|
+
}
|
|
492
519
|
};
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
520
|
+
const onResponse = (response) => {
|
|
521
|
+
const info = {
|
|
522
|
+
url: response.url(),
|
|
523
|
+
status: response.status(),
|
|
524
|
+
headers: response.headers(),
|
|
525
|
+
timestamp: Date.now()
|
|
526
|
+
};
|
|
527
|
+
networkResponses.push(info);
|
|
528
|
+
if (options.playwright.onEvent) {
|
|
529
|
+
options.playwright.onEvent({
|
|
530
|
+
type: "networkResponse",
|
|
531
|
+
...info
|
|
532
|
+
});
|
|
533
|
+
}
|
|
507
534
|
};
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
});
|
|
514
|
-
}
|
|
515
|
-
};
|
|
516
|
-
page.on("console", onConsole);
|
|
517
|
-
page.on("request", onRequest);
|
|
518
|
-
page.on("response", onResponse);
|
|
519
|
-
pageListenerCleanups.push(() => page.removeListener("console", onConsole), () => page.removeListener("request", onRequest), () => page.removeListener("response", onResponse));
|
|
535
|
+
page.on("console", onConsole);
|
|
536
|
+
page.on("request", onRequest);
|
|
537
|
+
page.on("response", onResponse);
|
|
538
|
+
pageListenerCleanups.push(() => page.removeListener("console", onConsole), () => page.removeListener("request", onRequest), () => page.removeListener("response", onResponse));
|
|
539
|
+
}
|
|
520
540
|
callbacks.playwright = {
|
|
521
541
|
handlerCallbackId,
|
|
522
542
|
console: options.playwright.console && !options.console?.onEntry
|
|
@@ -564,6 +584,9 @@ async function createRuntime(state, options = {}, namespaceId) {
|
|
|
564
584
|
const reused = result.reused ?? false;
|
|
565
585
|
const wsCommandCallbacks = new Set;
|
|
566
586
|
isolateWsCallbacks.set(isolateId, wsCommandCallbacks);
|
|
587
|
+
if (options.webSocket) {
|
|
588
|
+
isolateWebSocketCallbacks.set(isolateId, options.webSocket);
|
|
589
|
+
}
|
|
567
590
|
const fetchHandle = {
|
|
568
591
|
async dispatchRequest(req, opts) {
|
|
569
592
|
const reqId = state.nextRequestId++;
|
|
@@ -737,7 +760,8 @@ async function createRuntime(state, options = {}, namespaceId) {
|
|
|
737
760
|
isolateId,
|
|
738
761
|
timeout
|
|
739
762
|
};
|
|
740
|
-
|
|
763
|
+
const requestTimeout = timeout === undefined ? DEFAULT_TIMEOUT : Math.max(timeout + TEST_REQUEST_TIMEOUT_BUFFER_MS, DEFAULT_TIMEOUT);
|
|
764
|
+
return sendRequest(state, req, requestTimeout);
|
|
741
765
|
},
|
|
742
766
|
async hasTests() {
|
|
743
767
|
if (!testEnvironmentEnabled) {
|
|
@@ -779,7 +803,7 @@ async function createRuntime(state, options = {}, namespaceId) {
|
|
|
779
803
|
const playwrightHandle = {
|
|
780
804
|
getCollectedData() {
|
|
781
805
|
if (!playwrightEnabled) {
|
|
782
|
-
throw new Error("Playwright not configured. Provide playwright.
|
|
806
|
+
throw new Error("Playwright not configured. Provide playwright.handler in createRuntime options.");
|
|
783
807
|
}
|
|
784
808
|
return {
|
|
785
809
|
browserConsoleLogs: [...browserConsoleLogs],
|
|
@@ -789,7 +813,7 @@ async function createRuntime(state, options = {}, namespaceId) {
|
|
|
789
813
|
},
|
|
790
814
|
clearCollectedData() {
|
|
791
815
|
if (!playwrightEnabled) {
|
|
792
|
-
throw new Error("Playwright not configured. Provide playwright.
|
|
816
|
+
throw new Error("Playwright not configured. Provide playwright.handler in createRuntime options.");
|
|
793
817
|
}
|
|
794
818
|
browserConsoleLogs.length = 0;
|
|
795
819
|
networkRequests.length = 0;
|
|
@@ -798,7 +822,6 @@ async function createRuntime(state, options = {}, namespaceId) {
|
|
|
798
822
|
};
|
|
799
823
|
return {
|
|
800
824
|
id: isolateId,
|
|
801
|
-
isolateId,
|
|
802
825
|
reused,
|
|
803
826
|
fetch: fetchHandle,
|
|
804
827
|
timers: timersHandle,
|
|
@@ -814,8 +837,7 @@ async function createRuntime(state, options = {}, namespaceId) {
|
|
|
814
837
|
isolateId,
|
|
815
838
|
code,
|
|
816
839
|
filename: options2?.filename,
|
|
817
|
-
maxExecutionMs: options2?.maxExecutionMs
|
|
818
|
-
module: true
|
|
840
|
+
maxExecutionMs: options2?.maxExecutionMs
|
|
819
841
|
};
|
|
820
842
|
await sendRequest(state, req);
|
|
821
843
|
},
|
|
@@ -824,6 +846,16 @@ async function createRuntime(state, options = {}, namespaceId) {
|
|
|
824
846
|
cleanup();
|
|
825
847
|
}
|
|
826
848
|
isolateWsCallbacks.delete(isolateId);
|
|
849
|
+
isolateWebSocketCallbacks.delete(isolateId);
|
|
850
|
+
const clientSockets = isolateClientWebSockets.get(isolateId);
|
|
851
|
+
if (clientSockets) {
|
|
852
|
+
for (const ws of clientSockets.values()) {
|
|
853
|
+
if (ws.readyState === WebSocket.OPEN || ws.readyState === WebSocket.CONNECTING) {
|
|
854
|
+
ws.close(1000, "Isolate disposed");
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
isolateClientWebSockets.delete(isolateId);
|
|
858
|
+
}
|
|
827
859
|
const reqId = state.nextRequestId++;
|
|
828
860
|
const req = {
|
|
829
861
|
type: import_isolate_protocol.MessageType.DISPOSE_RUNTIME,
|
|
@@ -858,8 +890,15 @@ function registerFetchCallback(state, callback) {
|
|
|
858
890
|
const callbackId = state.nextCallbackId++;
|
|
859
891
|
state.callbacksNeedingRequestId.add(callbackId);
|
|
860
892
|
state.callbacks.set(callbackId, async (serialized, requestId) => {
|
|
861
|
-
const
|
|
862
|
-
const
|
|
893
|
+
const data = serialized;
|
|
894
|
+
const init = {
|
|
895
|
+
method: data.method,
|
|
896
|
+
headers: data.headers,
|
|
897
|
+
rawBody: data.body,
|
|
898
|
+
body: data.body,
|
|
899
|
+
signal: new AbortController().signal
|
|
900
|
+
};
|
|
901
|
+
const response = await callback(data.url, init);
|
|
863
902
|
const contentLength = response.headers.get("content-length");
|
|
864
903
|
const knownSize = contentLength ? parseInt(contentLength, 10) : null;
|
|
865
904
|
const isNetworkResponse = response.url && (response.url.startsWith("http://") || response.url.startsWith("https://"));
|
|
@@ -1217,13 +1256,6 @@ async function serializeResponse(response) {
|
|
|
1217
1256
|
body
|
|
1218
1257
|
};
|
|
1219
1258
|
}
|
|
1220
|
-
function deserializeRequest(data) {
|
|
1221
|
-
return new Request(data.url, {
|
|
1222
|
-
method: data.method,
|
|
1223
|
-
headers: data.headers,
|
|
1224
|
-
body: data.body
|
|
1225
|
-
});
|
|
1226
|
-
}
|
|
1227
1259
|
function deserializeResponse(data) {
|
|
1228
1260
|
return new Response(data.body, {
|
|
1229
1261
|
status: data.status,
|
|
@@ -1329,5 +1361,152 @@ async function sendBodyStream(state, streamId, body) {
|
|
|
1329
1361
|
state.uploadStreams.delete(streamId);
|
|
1330
1362
|
}
|
|
1331
1363
|
}
|
|
1364
|
+
function handleClientWsConnect(message, state) {
|
|
1365
|
+
const { isolateId, socketId, url, protocols } = message;
|
|
1366
|
+
let sockets = isolateClientWebSockets.get(isolateId);
|
|
1367
|
+
if (!sockets) {
|
|
1368
|
+
sockets = new Map;
|
|
1369
|
+
isolateClientWebSockets.set(isolateId, sockets);
|
|
1370
|
+
}
|
|
1371
|
+
const setupWebSocket = (ws) => {
|
|
1372
|
+
sockets.set(socketId, ws);
|
|
1373
|
+
ws.onopen = () => {
|
|
1374
|
+
const msg = {
|
|
1375
|
+
type: import_isolate_protocol.MessageType.CLIENT_WS_OPENED,
|
|
1376
|
+
isolateId,
|
|
1377
|
+
socketId,
|
|
1378
|
+
protocol: ws.protocol || "",
|
|
1379
|
+
extensions: ws.extensions || ""
|
|
1380
|
+
};
|
|
1381
|
+
sendMessage(state.socket, msg);
|
|
1382
|
+
};
|
|
1383
|
+
ws.onmessage = (event) => {
|
|
1384
|
+
let data;
|
|
1385
|
+
if (typeof event.data === "string") {
|
|
1386
|
+
data = event.data;
|
|
1387
|
+
} else if (event.data instanceof ArrayBuffer) {
|
|
1388
|
+
data = new Uint8Array(event.data);
|
|
1389
|
+
} else if (event.data instanceof Blob) {
|
|
1390
|
+
event.data.arrayBuffer().then((buffer) => {
|
|
1391
|
+
const msg2 = {
|
|
1392
|
+
type: import_isolate_protocol.MessageType.CLIENT_WS_MESSAGE,
|
|
1393
|
+
isolateId,
|
|
1394
|
+
socketId,
|
|
1395
|
+
data: new Uint8Array(buffer)
|
|
1396
|
+
};
|
|
1397
|
+
sendMessage(state.socket, msg2);
|
|
1398
|
+
});
|
|
1399
|
+
return;
|
|
1400
|
+
} else {
|
|
1401
|
+
data = String(event.data);
|
|
1402
|
+
}
|
|
1403
|
+
const msg = {
|
|
1404
|
+
type: import_isolate_protocol.MessageType.CLIENT_WS_MESSAGE,
|
|
1405
|
+
isolateId,
|
|
1406
|
+
socketId,
|
|
1407
|
+
data
|
|
1408
|
+
};
|
|
1409
|
+
sendMessage(state.socket, msg);
|
|
1410
|
+
};
|
|
1411
|
+
ws.onerror = () => {
|
|
1412
|
+
const msg = {
|
|
1413
|
+
type: import_isolate_protocol.MessageType.CLIENT_WS_ERROR,
|
|
1414
|
+
isolateId,
|
|
1415
|
+
socketId
|
|
1416
|
+
};
|
|
1417
|
+
sendMessage(state.socket, msg);
|
|
1418
|
+
};
|
|
1419
|
+
ws.onclose = (event) => {
|
|
1420
|
+
const msg = {
|
|
1421
|
+
type: import_isolate_protocol.MessageType.CLIENT_WS_CLOSED,
|
|
1422
|
+
isolateId,
|
|
1423
|
+
socketId,
|
|
1424
|
+
code: event.code,
|
|
1425
|
+
reason: event.reason,
|
|
1426
|
+
wasClean: event.wasClean
|
|
1427
|
+
};
|
|
1428
|
+
sendMessage(state.socket, msg);
|
|
1429
|
+
sockets?.delete(socketId);
|
|
1430
|
+
if (sockets?.size === 0) {
|
|
1431
|
+
isolateClientWebSockets.delete(isolateId);
|
|
1432
|
+
}
|
|
1433
|
+
};
|
|
1434
|
+
};
|
|
1435
|
+
const sendConnectionFailed = (reason) => {
|
|
1436
|
+
const errorMsg = {
|
|
1437
|
+
type: import_isolate_protocol.MessageType.CLIENT_WS_ERROR,
|
|
1438
|
+
isolateId,
|
|
1439
|
+
socketId
|
|
1440
|
+
};
|
|
1441
|
+
sendMessage(state.socket, errorMsg);
|
|
1442
|
+
const closeMsg = {
|
|
1443
|
+
type: import_isolate_protocol.MessageType.CLIENT_WS_CLOSED,
|
|
1444
|
+
isolateId,
|
|
1445
|
+
socketId,
|
|
1446
|
+
code: 1006,
|
|
1447
|
+
reason,
|
|
1448
|
+
wasClean: false
|
|
1449
|
+
};
|
|
1450
|
+
sendMessage(state.socket, closeMsg);
|
|
1451
|
+
};
|
|
1452
|
+
const callback = isolateWebSocketCallbacks.get(isolateId);
|
|
1453
|
+
if (callback) {
|
|
1454
|
+
try {
|
|
1455
|
+
const result = callback(url, protocols || []);
|
|
1456
|
+
if (result instanceof Promise) {
|
|
1457
|
+
result.then((ws) => {
|
|
1458
|
+
if (ws === null) {
|
|
1459
|
+
sendConnectionFailed("Connection blocked");
|
|
1460
|
+
} else {
|
|
1461
|
+
setupWebSocket(ws);
|
|
1462
|
+
}
|
|
1463
|
+
}).catch(() => {
|
|
1464
|
+
sendConnectionFailed("Callback error");
|
|
1465
|
+
});
|
|
1466
|
+
} else if (result === null) {
|
|
1467
|
+
sendConnectionFailed("Connection blocked");
|
|
1468
|
+
} else {
|
|
1469
|
+
setupWebSocket(result);
|
|
1470
|
+
}
|
|
1471
|
+
} catch {
|
|
1472
|
+
sendConnectionFailed("Callback error");
|
|
1473
|
+
}
|
|
1474
|
+
} else {
|
|
1475
|
+
try {
|
|
1476
|
+
const ws = protocols && protocols.length > 0 ? new WebSocket(url, protocols) : new WebSocket(url);
|
|
1477
|
+
setupWebSocket(ws);
|
|
1478
|
+
} catch {
|
|
1479
|
+
sendConnectionFailed("Connection failed");
|
|
1480
|
+
}
|
|
1481
|
+
}
|
|
1482
|
+
}
|
|
1483
|
+
function handleClientWsSend(message, state) {
|
|
1484
|
+
const { isolateId, socketId, data } = message;
|
|
1485
|
+
const sockets = isolateClientWebSockets.get(isolateId);
|
|
1486
|
+
const ws = sockets?.get(socketId);
|
|
1487
|
+
if (!ws || ws.readyState !== WebSocket.OPEN) {
|
|
1488
|
+
return;
|
|
1489
|
+
}
|
|
1490
|
+
if (typeof data === "string" && data.startsWith("__BINARY__")) {
|
|
1491
|
+
const base64 = data.slice(10);
|
|
1492
|
+
const binary = Buffer.from(base64, "base64");
|
|
1493
|
+
ws.send(binary);
|
|
1494
|
+
} else if (data instanceof Uint8Array) {
|
|
1495
|
+
ws.send(data);
|
|
1496
|
+
} else {
|
|
1497
|
+
ws.send(data);
|
|
1498
|
+
}
|
|
1499
|
+
}
|
|
1500
|
+
function handleClientWsClose(message, state) {
|
|
1501
|
+
const { isolateId, socketId, code, reason } = message;
|
|
1502
|
+
const sockets = isolateClientWebSockets.get(isolateId);
|
|
1503
|
+
const ws = sockets?.get(socketId);
|
|
1504
|
+
if (!ws) {
|
|
1505
|
+
return;
|
|
1506
|
+
}
|
|
1507
|
+
if (ws.readyState === WebSocket.OPEN || ws.readyState === WebSocket.CONNECTING) {
|
|
1508
|
+
ws.close(code ?? 1000, reason ?? "");
|
|
1509
|
+
}
|
|
1510
|
+
}
|
|
1332
1511
|
|
|
1333
|
-
//# debugId=
|
|
1512
|
+
//# debugId=7A85E05BA8CAD49764756E2164756E21
|