@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/mjs/connection.mjs
CHANGED
|
@@ -10,9 +10,14 @@ import {
|
|
|
10
10
|
STREAM_DEFAULT_CREDIT,
|
|
11
11
|
marshalValue
|
|
12
12
|
} from "@ricsam/isolate-protocol";
|
|
13
|
-
import {
|
|
13
|
+
import {
|
|
14
|
+
getDefaultPlaywrightHandlerMetadata
|
|
15
|
+
} from "@ricsam/isolate-playwright/client";
|
|
14
16
|
var DEFAULT_TIMEOUT = 30000;
|
|
17
|
+
var TEST_REQUEST_TIMEOUT_BUFFER_MS = 1000;
|
|
15
18
|
var isolateWsCallbacks = new Map;
|
|
19
|
+
var isolateClientWebSockets = new Map;
|
|
20
|
+
var isolateWebSocketCallbacks = new Map;
|
|
16
21
|
async function connect(options = {}) {
|
|
17
22
|
const socket = await createSocket(options);
|
|
18
23
|
const state = {
|
|
@@ -176,6 +181,21 @@ function handleMessage(message, state) {
|
|
|
176
181
|
}
|
|
177
182
|
break;
|
|
178
183
|
}
|
|
184
|
+
case MessageType.CLIENT_WS_CONNECT: {
|
|
185
|
+
const msg = message;
|
|
186
|
+
handleClientWsConnect(msg, state);
|
|
187
|
+
break;
|
|
188
|
+
}
|
|
189
|
+
case MessageType.CLIENT_WS_SEND: {
|
|
190
|
+
const msg = message;
|
|
191
|
+
handleClientWsSend(msg, state);
|
|
192
|
+
break;
|
|
193
|
+
}
|
|
194
|
+
case MessageType.CLIENT_WS_CLOSE: {
|
|
195
|
+
const msg = message;
|
|
196
|
+
handleClientWsClose(msg, state);
|
|
197
|
+
break;
|
|
198
|
+
}
|
|
179
199
|
case MessageType.RESPONSE_STREAM_START: {
|
|
180
200
|
const msg = message;
|
|
181
201
|
const receiver = {
|
|
@@ -411,74 +431,76 @@ async function createRuntime(state, options = {}, namespaceId) {
|
|
|
411
431
|
const networkResponses = [];
|
|
412
432
|
const pageListenerCleanups = [];
|
|
413
433
|
if (options.playwright) {
|
|
414
|
-
playwrightHandler =
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
}
|
|
434
|
+
playwrightHandler = options.playwright.handler;
|
|
435
|
+
if (!playwrightHandler) {
|
|
436
|
+
throw new Error("playwright.handler is required when using playwright options");
|
|
437
|
+
}
|
|
438
|
+
const page = getDefaultPlaywrightHandlerMetadata(playwrightHandler)?.page;
|
|
418
439
|
const handlerCallbackId = state.nextCallbackId++;
|
|
419
440
|
state.callbacks.set(handlerCallbackId, async (opJson) => {
|
|
420
441
|
const op = JSON.parse(opJson);
|
|
421
442
|
const result2 = await playwrightHandler(op);
|
|
422
443
|
return JSON.stringify(result2);
|
|
423
444
|
});
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
445
|
+
if (page) {
|
|
446
|
+
const onConsole = (msg) => {
|
|
447
|
+
const entry = {
|
|
448
|
+
level: msg.type(),
|
|
449
|
+
stdout: msg.text(),
|
|
450
|
+
timestamp: Date.now()
|
|
451
|
+
};
|
|
452
|
+
browserConsoleLogs.push(entry);
|
|
453
|
+
if (options.playwright.onEvent) {
|
|
454
|
+
options.playwright.onEvent({
|
|
455
|
+
type: "browserConsoleLog",
|
|
456
|
+
...entry
|
|
457
|
+
});
|
|
458
|
+
}
|
|
459
|
+
if (options.playwright.console && options.console?.onEntry) {
|
|
460
|
+
options.console.onEntry({
|
|
461
|
+
type: "browserOutput",
|
|
462
|
+
...entry
|
|
463
|
+
});
|
|
464
|
+
} else if (options.playwright.console) {
|
|
465
|
+
const prefix = entry.level === "error" ? "[browser:error]" : "[browser]";
|
|
466
|
+
console.log(prefix, entry.stdout);
|
|
467
|
+
}
|
|
430
468
|
};
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
options.
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
console.log(prefix, entry.stdout);
|
|
446
|
-
}
|
|
447
|
-
};
|
|
448
|
-
const onRequest = (request2) => {
|
|
449
|
-
const info = {
|
|
450
|
-
url: request2.url(),
|
|
451
|
-
method: request2.method(),
|
|
452
|
-
headers: request2.headers(),
|
|
453
|
-
timestamp: Date.now()
|
|
469
|
+
const onRequest = (request2) => {
|
|
470
|
+
const info = {
|
|
471
|
+
url: request2.url(),
|
|
472
|
+
method: request2.method(),
|
|
473
|
+
headers: request2.headers(),
|
|
474
|
+
timestamp: Date.now()
|
|
475
|
+
};
|
|
476
|
+
networkRequests.push(info);
|
|
477
|
+
if (options.playwright.onEvent) {
|
|
478
|
+
options.playwright.onEvent({
|
|
479
|
+
type: "networkRequest",
|
|
480
|
+
...info
|
|
481
|
+
});
|
|
482
|
+
}
|
|
454
483
|
};
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
484
|
+
const onResponse = (response) => {
|
|
485
|
+
const info = {
|
|
486
|
+
url: response.url(),
|
|
487
|
+
status: response.status(),
|
|
488
|
+
headers: response.headers(),
|
|
489
|
+
timestamp: Date.now()
|
|
490
|
+
};
|
|
491
|
+
networkResponses.push(info);
|
|
492
|
+
if (options.playwright.onEvent) {
|
|
493
|
+
options.playwright.onEvent({
|
|
494
|
+
type: "networkResponse",
|
|
495
|
+
...info
|
|
496
|
+
});
|
|
497
|
+
}
|
|
469
498
|
};
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
});
|
|
476
|
-
}
|
|
477
|
-
};
|
|
478
|
-
page.on("console", onConsole);
|
|
479
|
-
page.on("request", onRequest);
|
|
480
|
-
page.on("response", onResponse);
|
|
481
|
-
pageListenerCleanups.push(() => page.removeListener("console", onConsole), () => page.removeListener("request", onRequest), () => page.removeListener("response", onResponse));
|
|
499
|
+
page.on("console", onConsole);
|
|
500
|
+
page.on("request", onRequest);
|
|
501
|
+
page.on("response", onResponse);
|
|
502
|
+
pageListenerCleanups.push(() => page.removeListener("console", onConsole), () => page.removeListener("request", onRequest), () => page.removeListener("response", onResponse));
|
|
503
|
+
}
|
|
482
504
|
callbacks.playwright = {
|
|
483
505
|
handlerCallbackId,
|
|
484
506
|
console: options.playwright.console && !options.console?.onEntry
|
|
@@ -526,6 +548,9 @@ async function createRuntime(state, options = {}, namespaceId) {
|
|
|
526
548
|
const reused = result.reused ?? false;
|
|
527
549
|
const wsCommandCallbacks = new Set;
|
|
528
550
|
isolateWsCallbacks.set(isolateId, wsCommandCallbacks);
|
|
551
|
+
if (options.webSocket) {
|
|
552
|
+
isolateWebSocketCallbacks.set(isolateId, options.webSocket);
|
|
553
|
+
}
|
|
529
554
|
const fetchHandle = {
|
|
530
555
|
async dispatchRequest(req, opts) {
|
|
531
556
|
const reqId = state.nextRequestId++;
|
|
@@ -699,7 +724,8 @@ async function createRuntime(state, options = {}, namespaceId) {
|
|
|
699
724
|
isolateId,
|
|
700
725
|
timeout
|
|
701
726
|
};
|
|
702
|
-
|
|
727
|
+
const requestTimeout = timeout === undefined ? DEFAULT_TIMEOUT : Math.max(timeout + TEST_REQUEST_TIMEOUT_BUFFER_MS, DEFAULT_TIMEOUT);
|
|
728
|
+
return sendRequest(state, req, requestTimeout);
|
|
703
729
|
},
|
|
704
730
|
async hasTests() {
|
|
705
731
|
if (!testEnvironmentEnabled) {
|
|
@@ -741,7 +767,7 @@ async function createRuntime(state, options = {}, namespaceId) {
|
|
|
741
767
|
const playwrightHandle = {
|
|
742
768
|
getCollectedData() {
|
|
743
769
|
if (!playwrightEnabled) {
|
|
744
|
-
throw new Error("Playwright not configured. Provide playwright.
|
|
770
|
+
throw new Error("Playwright not configured. Provide playwright.handler in createRuntime options.");
|
|
745
771
|
}
|
|
746
772
|
return {
|
|
747
773
|
browserConsoleLogs: [...browserConsoleLogs],
|
|
@@ -751,7 +777,7 @@ async function createRuntime(state, options = {}, namespaceId) {
|
|
|
751
777
|
},
|
|
752
778
|
clearCollectedData() {
|
|
753
779
|
if (!playwrightEnabled) {
|
|
754
|
-
throw new Error("Playwright not configured. Provide playwright.
|
|
780
|
+
throw new Error("Playwright not configured. Provide playwright.handler in createRuntime options.");
|
|
755
781
|
}
|
|
756
782
|
browserConsoleLogs.length = 0;
|
|
757
783
|
networkRequests.length = 0;
|
|
@@ -760,7 +786,6 @@ async function createRuntime(state, options = {}, namespaceId) {
|
|
|
760
786
|
};
|
|
761
787
|
return {
|
|
762
788
|
id: isolateId,
|
|
763
|
-
isolateId,
|
|
764
789
|
reused,
|
|
765
790
|
fetch: fetchHandle,
|
|
766
791
|
timers: timersHandle,
|
|
@@ -776,8 +801,7 @@ async function createRuntime(state, options = {}, namespaceId) {
|
|
|
776
801
|
isolateId,
|
|
777
802
|
code,
|
|
778
803
|
filename: options2?.filename,
|
|
779
|
-
maxExecutionMs: options2?.maxExecutionMs
|
|
780
|
-
module: true
|
|
804
|
+
maxExecutionMs: options2?.maxExecutionMs
|
|
781
805
|
};
|
|
782
806
|
await sendRequest(state, req);
|
|
783
807
|
},
|
|
@@ -786,6 +810,16 @@ async function createRuntime(state, options = {}, namespaceId) {
|
|
|
786
810
|
cleanup();
|
|
787
811
|
}
|
|
788
812
|
isolateWsCallbacks.delete(isolateId);
|
|
813
|
+
isolateWebSocketCallbacks.delete(isolateId);
|
|
814
|
+
const clientSockets = isolateClientWebSockets.get(isolateId);
|
|
815
|
+
if (clientSockets) {
|
|
816
|
+
for (const ws of clientSockets.values()) {
|
|
817
|
+
if (ws.readyState === WebSocket.OPEN || ws.readyState === WebSocket.CONNECTING) {
|
|
818
|
+
ws.close(1000, "Isolate disposed");
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
isolateClientWebSockets.delete(isolateId);
|
|
822
|
+
}
|
|
789
823
|
const reqId = state.nextRequestId++;
|
|
790
824
|
const req = {
|
|
791
825
|
type: MessageType.DISPOSE_RUNTIME,
|
|
@@ -820,8 +854,15 @@ function registerFetchCallback(state, callback) {
|
|
|
820
854
|
const callbackId = state.nextCallbackId++;
|
|
821
855
|
state.callbacksNeedingRequestId.add(callbackId);
|
|
822
856
|
state.callbacks.set(callbackId, async (serialized, requestId) => {
|
|
823
|
-
const
|
|
824
|
-
const
|
|
857
|
+
const data = serialized;
|
|
858
|
+
const init = {
|
|
859
|
+
method: data.method,
|
|
860
|
+
headers: data.headers,
|
|
861
|
+
rawBody: data.body,
|
|
862
|
+
body: data.body,
|
|
863
|
+
signal: new AbortController().signal
|
|
864
|
+
};
|
|
865
|
+
const response = await callback(data.url, init);
|
|
825
866
|
const contentLength = response.headers.get("content-length");
|
|
826
867
|
const knownSize = contentLength ? parseInt(contentLength, 10) : null;
|
|
827
868
|
const isNetworkResponse = response.url && (response.url.startsWith("http://") || response.url.startsWith("https://"));
|
|
@@ -1179,13 +1220,6 @@ async function serializeResponse(response) {
|
|
|
1179
1220
|
body
|
|
1180
1221
|
};
|
|
1181
1222
|
}
|
|
1182
|
-
function deserializeRequest(data) {
|
|
1183
|
-
return new Request(data.url, {
|
|
1184
|
-
method: data.method,
|
|
1185
|
-
headers: data.headers,
|
|
1186
|
-
body: data.body
|
|
1187
|
-
});
|
|
1188
|
-
}
|
|
1189
1223
|
function deserializeResponse(data) {
|
|
1190
1224
|
return new Response(data.body, {
|
|
1191
1225
|
status: data.status,
|
|
@@ -1291,8 +1325,155 @@ async function sendBodyStream(state, streamId, body) {
|
|
|
1291
1325
|
state.uploadStreams.delete(streamId);
|
|
1292
1326
|
}
|
|
1293
1327
|
}
|
|
1328
|
+
function handleClientWsConnect(message, state) {
|
|
1329
|
+
const { isolateId, socketId, url, protocols } = message;
|
|
1330
|
+
let sockets = isolateClientWebSockets.get(isolateId);
|
|
1331
|
+
if (!sockets) {
|
|
1332
|
+
sockets = new Map;
|
|
1333
|
+
isolateClientWebSockets.set(isolateId, sockets);
|
|
1334
|
+
}
|
|
1335
|
+
const setupWebSocket = (ws) => {
|
|
1336
|
+
sockets.set(socketId, ws);
|
|
1337
|
+
ws.onopen = () => {
|
|
1338
|
+
const msg = {
|
|
1339
|
+
type: MessageType.CLIENT_WS_OPENED,
|
|
1340
|
+
isolateId,
|
|
1341
|
+
socketId,
|
|
1342
|
+
protocol: ws.protocol || "",
|
|
1343
|
+
extensions: ws.extensions || ""
|
|
1344
|
+
};
|
|
1345
|
+
sendMessage(state.socket, msg);
|
|
1346
|
+
};
|
|
1347
|
+
ws.onmessage = (event) => {
|
|
1348
|
+
let data;
|
|
1349
|
+
if (typeof event.data === "string") {
|
|
1350
|
+
data = event.data;
|
|
1351
|
+
} else if (event.data instanceof ArrayBuffer) {
|
|
1352
|
+
data = new Uint8Array(event.data);
|
|
1353
|
+
} else if (event.data instanceof Blob) {
|
|
1354
|
+
event.data.arrayBuffer().then((buffer) => {
|
|
1355
|
+
const msg2 = {
|
|
1356
|
+
type: MessageType.CLIENT_WS_MESSAGE,
|
|
1357
|
+
isolateId,
|
|
1358
|
+
socketId,
|
|
1359
|
+
data: new Uint8Array(buffer)
|
|
1360
|
+
};
|
|
1361
|
+
sendMessage(state.socket, msg2);
|
|
1362
|
+
});
|
|
1363
|
+
return;
|
|
1364
|
+
} else {
|
|
1365
|
+
data = String(event.data);
|
|
1366
|
+
}
|
|
1367
|
+
const msg = {
|
|
1368
|
+
type: MessageType.CLIENT_WS_MESSAGE,
|
|
1369
|
+
isolateId,
|
|
1370
|
+
socketId,
|
|
1371
|
+
data
|
|
1372
|
+
};
|
|
1373
|
+
sendMessage(state.socket, msg);
|
|
1374
|
+
};
|
|
1375
|
+
ws.onerror = () => {
|
|
1376
|
+
const msg = {
|
|
1377
|
+
type: MessageType.CLIENT_WS_ERROR,
|
|
1378
|
+
isolateId,
|
|
1379
|
+
socketId
|
|
1380
|
+
};
|
|
1381
|
+
sendMessage(state.socket, msg);
|
|
1382
|
+
};
|
|
1383
|
+
ws.onclose = (event) => {
|
|
1384
|
+
const msg = {
|
|
1385
|
+
type: MessageType.CLIENT_WS_CLOSED,
|
|
1386
|
+
isolateId,
|
|
1387
|
+
socketId,
|
|
1388
|
+
code: event.code,
|
|
1389
|
+
reason: event.reason,
|
|
1390
|
+
wasClean: event.wasClean
|
|
1391
|
+
};
|
|
1392
|
+
sendMessage(state.socket, msg);
|
|
1393
|
+
sockets?.delete(socketId);
|
|
1394
|
+
if (sockets?.size === 0) {
|
|
1395
|
+
isolateClientWebSockets.delete(isolateId);
|
|
1396
|
+
}
|
|
1397
|
+
};
|
|
1398
|
+
};
|
|
1399
|
+
const sendConnectionFailed = (reason) => {
|
|
1400
|
+
const errorMsg = {
|
|
1401
|
+
type: MessageType.CLIENT_WS_ERROR,
|
|
1402
|
+
isolateId,
|
|
1403
|
+
socketId
|
|
1404
|
+
};
|
|
1405
|
+
sendMessage(state.socket, errorMsg);
|
|
1406
|
+
const closeMsg = {
|
|
1407
|
+
type: MessageType.CLIENT_WS_CLOSED,
|
|
1408
|
+
isolateId,
|
|
1409
|
+
socketId,
|
|
1410
|
+
code: 1006,
|
|
1411
|
+
reason,
|
|
1412
|
+
wasClean: false
|
|
1413
|
+
};
|
|
1414
|
+
sendMessage(state.socket, closeMsg);
|
|
1415
|
+
};
|
|
1416
|
+
const callback = isolateWebSocketCallbacks.get(isolateId);
|
|
1417
|
+
if (callback) {
|
|
1418
|
+
try {
|
|
1419
|
+
const result = callback(url, protocols || []);
|
|
1420
|
+
if (result instanceof Promise) {
|
|
1421
|
+
result.then((ws) => {
|
|
1422
|
+
if (ws === null) {
|
|
1423
|
+
sendConnectionFailed("Connection blocked");
|
|
1424
|
+
} else {
|
|
1425
|
+
setupWebSocket(ws);
|
|
1426
|
+
}
|
|
1427
|
+
}).catch(() => {
|
|
1428
|
+
sendConnectionFailed("Callback error");
|
|
1429
|
+
});
|
|
1430
|
+
} else if (result === null) {
|
|
1431
|
+
sendConnectionFailed("Connection blocked");
|
|
1432
|
+
} else {
|
|
1433
|
+
setupWebSocket(result);
|
|
1434
|
+
}
|
|
1435
|
+
} catch {
|
|
1436
|
+
sendConnectionFailed("Callback error");
|
|
1437
|
+
}
|
|
1438
|
+
} else {
|
|
1439
|
+
try {
|
|
1440
|
+
const ws = protocols && protocols.length > 0 ? new WebSocket(url, protocols) : new WebSocket(url);
|
|
1441
|
+
setupWebSocket(ws);
|
|
1442
|
+
} catch {
|
|
1443
|
+
sendConnectionFailed("Connection failed");
|
|
1444
|
+
}
|
|
1445
|
+
}
|
|
1446
|
+
}
|
|
1447
|
+
function handleClientWsSend(message, state) {
|
|
1448
|
+
const { isolateId, socketId, data } = message;
|
|
1449
|
+
const sockets = isolateClientWebSockets.get(isolateId);
|
|
1450
|
+
const ws = sockets?.get(socketId);
|
|
1451
|
+
if (!ws || ws.readyState !== WebSocket.OPEN) {
|
|
1452
|
+
return;
|
|
1453
|
+
}
|
|
1454
|
+
if (typeof data === "string" && data.startsWith("__BINARY__")) {
|
|
1455
|
+
const base64 = data.slice(10);
|
|
1456
|
+
const binary = Buffer.from(base64, "base64");
|
|
1457
|
+
ws.send(binary);
|
|
1458
|
+
} else if (data instanceof Uint8Array) {
|
|
1459
|
+
ws.send(data);
|
|
1460
|
+
} else {
|
|
1461
|
+
ws.send(data);
|
|
1462
|
+
}
|
|
1463
|
+
}
|
|
1464
|
+
function handleClientWsClose(message, state) {
|
|
1465
|
+
const { isolateId, socketId, code, reason } = message;
|
|
1466
|
+
const sockets = isolateClientWebSockets.get(isolateId);
|
|
1467
|
+
const ws = sockets?.get(socketId);
|
|
1468
|
+
if (!ws) {
|
|
1469
|
+
return;
|
|
1470
|
+
}
|
|
1471
|
+
if (ws.readyState === WebSocket.OPEN || ws.readyState === WebSocket.CONNECTING) {
|
|
1472
|
+
ws.close(code ?? 1000, reason ?? "");
|
|
1473
|
+
}
|
|
1474
|
+
}
|
|
1294
1475
|
export {
|
|
1295
1476
|
connect
|
|
1296
1477
|
};
|
|
1297
1478
|
|
|
1298
|
-
//# debugId=
|
|
1479
|
+
//# debugId=D4986DB48143DFE264756E2164756E21
|