@ricsam/isolate-daemon 0.1.0 → 0.1.4
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 +38 -7
- package/bin/daemon.js +1 -1
- package/dist/cjs/connection.cjs +1058 -133
- package/dist/cjs/connection.cjs.map +3 -3
- package/dist/cjs/index.cjs +2 -2
- package/dist/cjs/index.cjs.map +3 -3
- package/dist/cjs/package.json +1 -1
- package/dist/mjs/connection.mjs +1057 -138
- package/dist/mjs/connection.mjs.map +3 -3
- package/dist/mjs/index.mjs +2 -2
- package/dist/mjs/index.mjs.map +3 -3
- package/dist/mjs/package.json +1 -1
- package/dist/types/types.d.ts +48 -11
- package/package.json +1 -1
package/dist/cjs/connection.cjs
CHANGED
|
@@ -1,8 +1,21 @@
|
|
|
1
1
|
// @bun @bun-cjs
|
|
2
|
-
(function(exports, require, module, __filename, __dirname) {var
|
|
2
|
+
(function(exports, require, module, __filename, __dirname) {var __create = Object.create;
|
|
3
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
3
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
6
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __toESM = (mod, isNodeMode, target) => {
|
|
9
|
+
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
10
|
+
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
11
|
+
for (let key of __getOwnPropNames(mod))
|
|
12
|
+
if (!__hasOwnProp.call(to, key))
|
|
13
|
+
__defProp(to, key, {
|
|
14
|
+
get: () => mod[key],
|
|
15
|
+
enumerable: true
|
|
16
|
+
});
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
6
19
|
var __moduleCache = /* @__PURE__ */ new WeakMap;
|
|
7
20
|
var __toCommonJS = (from) => {
|
|
8
21
|
var entry = __moduleCache.get(from), desc;
|
|
@@ -34,11 +47,11 @@ __export(exports_connection, {
|
|
|
34
47
|
});
|
|
35
48
|
module.exports = __toCommonJS(exports_connection);
|
|
36
49
|
var import_node_crypto = require("crypto");
|
|
50
|
+
var import_isolated_vm = __toESM(require("isolated-vm"));
|
|
37
51
|
var import_isolate_protocol = require("@ricsam/isolate-protocol");
|
|
38
52
|
var import_callback_fs_handler = require("./callback-fs-handler.cjs");
|
|
39
53
|
var import_isolate_test_environment = require("@ricsam/isolate-test-environment");
|
|
40
54
|
var import_isolate_playwright = require("@ricsam/isolate-playwright");
|
|
41
|
-
var import_playwright = require("playwright");
|
|
42
55
|
var import_isolate_runtime = require("@ricsam/isolate-runtime");
|
|
43
56
|
function handleConnection(socket, state) {
|
|
44
57
|
const connection = {
|
|
@@ -48,7 +61,9 @@ function handleConnection(socket, state) {
|
|
|
48
61
|
pendingCallbacks: new Map,
|
|
49
62
|
nextRequestId: 1,
|
|
50
63
|
nextCallbackId: 1,
|
|
51
|
-
nextStreamId: 1
|
|
64
|
+
nextStreamId: 1,
|
|
65
|
+
activeStreams: new Map,
|
|
66
|
+
streamReceivers: new Map
|
|
52
67
|
};
|
|
53
68
|
state.connections.set(socket, connection);
|
|
54
69
|
const parser = import_isolate_protocol.createFrameParser();
|
|
@@ -72,12 +87,6 @@ function handleConnection(socket, state) {
|
|
|
72
87
|
if (instance.playwrightHandle) {
|
|
73
88
|
instance.playwrightHandle.dispose();
|
|
74
89
|
}
|
|
75
|
-
if (instance.browserContext) {
|
|
76
|
-
instance.browserContext.close().catch(() => {});
|
|
77
|
-
}
|
|
78
|
-
if (instance.browser) {
|
|
79
|
-
instance.browser.close().catch(() => {});
|
|
80
|
-
}
|
|
81
90
|
instance.runtime.dispose();
|
|
82
91
|
} catch {}
|
|
83
92
|
state.isolates.delete(isolateId);
|
|
@@ -129,20 +138,56 @@ async function handleMessage(message, connection, state) {
|
|
|
129
138
|
case import_isolate_protocol.MessageType.DISPATCH_REQUEST:
|
|
130
139
|
await handleDispatchRequest(message, connection, state);
|
|
131
140
|
break;
|
|
132
|
-
case import_isolate_protocol.MessageType.TICK:
|
|
133
|
-
await handleTick(message, connection, state);
|
|
134
|
-
break;
|
|
135
141
|
case import_isolate_protocol.MessageType.CALLBACK_RESPONSE:
|
|
136
142
|
handleCallbackResponse(message, connection);
|
|
137
143
|
break;
|
|
138
|
-
case import_isolate_protocol.MessageType.
|
|
139
|
-
await
|
|
144
|
+
case import_isolate_protocol.MessageType.WS_OPEN:
|
|
145
|
+
await handleWsOpen(message, connection, state);
|
|
146
|
+
break;
|
|
147
|
+
case import_isolate_protocol.MessageType.WS_MESSAGE:
|
|
148
|
+
await handleWsMessage(message, connection, state);
|
|
149
|
+
break;
|
|
150
|
+
case import_isolate_protocol.MessageType.WS_CLOSE:
|
|
151
|
+
await handleWsClose(message, connection, state);
|
|
152
|
+
break;
|
|
153
|
+
case import_isolate_protocol.MessageType.FETCH_GET_UPGRADE_REQUEST:
|
|
154
|
+
await handleFetchGetUpgradeRequest(message, connection, state);
|
|
155
|
+
break;
|
|
156
|
+
case import_isolate_protocol.MessageType.FETCH_HAS_SERVE_HANDLER:
|
|
157
|
+
await handleFetchHasServeHandler(message, connection, state);
|
|
158
|
+
break;
|
|
159
|
+
case import_isolate_protocol.MessageType.FETCH_HAS_ACTIVE_CONNECTIONS:
|
|
160
|
+
await handleFetchHasActiveConnections(message, connection, state);
|
|
161
|
+
break;
|
|
162
|
+
case import_isolate_protocol.MessageType.FETCH_WS_ERROR:
|
|
163
|
+
await handleFetchWsError(message, connection, state);
|
|
164
|
+
break;
|
|
165
|
+
case import_isolate_protocol.MessageType.TIMERS_CLEAR_ALL:
|
|
166
|
+
await handleTimersClearAll(message, connection, state);
|
|
167
|
+
break;
|
|
168
|
+
case import_isolate_protocol.MessageType.CONSOLE_RESET:
|
|
169
|
+
await handleConsoleReset(message, connection, state);
|
|
170
|
+
break;
|
|
171
|
+
case import_isolate_protocol.MessageType.CONSOLE_GET_TIMERS:
|
|
172
|
+
await handleConsoleGetTimers(message, connection, state);
|
|
173
|
+
break;
|
|
174
|
+
case import_isolate_protocol.MessageType.CONSOLE_GET_COUNTERS:
|
|
175
|
+
await handleConsoleGetCounters(message, connection, state);
|
|
176
|
+
break;
|
|
177
|
+
case import_isolate_protocol.MessageType.CONSOLE_GET_GROUP_DEPTH:
|
|
178
|
+
await handleConsoleGetGroupDepth(message, connection, state);
|
|
140
179
|
break;
|
|
141
180
|
case import_isolate_protocol.MessageType.RUN_TESTS:
|
|
142
181
|
await handleRunTests(message, connection, state);
|
|
143
182
|
break;
|
|
144
|
-
case import_isolate_protocol.MessageType.
|
|
145
|
-
await
|
|
183
|
+
case import_isolate_protocol.MessageType.RESET_TEST_ENV:
|
|
184
|
+
await handleResetTestEnv(message, connection, state);
|
|
185
|
+
break;
|
|
186
|
+
case import_isolate_protocol.MessageType.HAS_TESTS:
|
|
187
|
+
await handleHasTests(message, connection, state);
|
|
188
|
+
break;
|
|
189
|
+
case import_isolate_protocol.MessageType.GET_TEST_COUNT:
|
|
190
|
+
await handleGetTestCount(message, connection, state);
|
|
146
191
|
break;
|
|
147
192
|
case import_isolate_protocol.MessageType.RUN_PLAYWRIGHT_TESTS:
|
|
148
193
|
await handleRunPlaywrightTests(message, connection, state);
|
|
@@ -153,9 +198,24 @@ async function handleMessage(message, connection, state) {
|
|
|
153
198
|
case import_isolate_protocol.MessageType.GET_COLLECTED_DATA:
|
|
154
199
|
await handleGetCollectedData(message, connection, state);
|
|
155
200
|
break;
|
|
201
|
+
case import_isolate_protocol.MessageType.CLEAR_COLLECTED_DATA:
|
|
202
|
+
await handleClearCollectedData(message, connection, state);
|
|
203
|
+
break;
|
|
156
204
|
case import_isolate_protocol.MessageType.PING:
|
|
157
205
|
sendMessage(connection.socket, { type: import_isolate_protocol.MessageType.PONG });
|
|
158
206
|
break;
|
|
207
|
+
case import_isolate_protocol.MessageType.STREAM_PUSH:
|
|
208
|
+
handleStreamPush(message, connection);
|
|
209
|
+
break;
|
|
210
|
+
case import_isolate_protocol.MessageType.STREAM_PULL:
|
|
211
|
+
handleStreamPull(message, connection);
|
|
212
|
+
break;
|
|
213
|
+
case import_isolate_protocol.MessageType.STREAM_CLOSE:
|
|
214
|
+
handleStreamClose(message, connection);
|
|
215
|
+
break;
|
|
216
|
+
case import_isolate_protocol.MessageType.STREAM_ERROR:
|
|
217
|
+
handleStreamError(message, connection);
|
|
218
|
+
break;
|
|
159
219
|
default:
|
|
160
220
|
sendError(connection.socket, message.requestId ?? 0, import_isolate_protocol.ErrorCode.UNKNOWN_MESSAGE_TYPE, `Unknown message type: ${message.type}`);
|
|
161
221
|
}
|
|
@@ -170,16 +230,16 @@ async function handleCreateRuntime(message, connection, state) {
|
|
|
170
230
|
const consoleCallbacks = message.options.callbacks?.console;
|
|
171
231
|
const fetchCallback = message.options.callbacks?.fetch;
|
|
172
232
|
const fsCallbacks = message.options.callbacks?.fs;
|
|
173
|
-
const
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
233
|
+
const moduleLoaderCallback = message.options.callbacks?.moduleLoader;
|
|
234
|
+
const customCallbacks = message.options.callbacks?.custom;
|
|
235
|
+
const pendingCallbacks = [];
|
|
236
|
+
const runtime = await import_isolate_runtime.createInternalRuntime({
|
|
237
|
+
memoryLimitMB: message.options.memoryLimitMB ?? state.options.defaultMemoryLimitMB,
|
|
238
|
+
cwd: message.options.cwd,
|
|
239
|
+
console: consoleCallbacks?.onEntry ? {
|
|
240
|
+
onEntry: (entry) => {
|
|
241
|
+
const promise = invokeClientCallback(connection, consoleCallbacks.onEntry.callbackId, [entry]).catch(() => {});
|
|
242
|
+
pendingCallbacks.push(promise);
|
|
183
243
|
}
|
|
184
244
|
} : undefined,
|
|
185
245
|
fetch: fetchCallback ? {
|
|
@@ -206,14 +266,25 @@ async function handleCreateRuntime(message, connection, state) {
|
|
|
206
266
|
ownerConnection: connection.socket,
|
|
207
267
|
callbacks: new Map,
|
|
208
268
|
createdAt: Date.now(),
|
|
209
|
-
lastActivity: Date.now()
|
|
269
|
+
lastActivity: Date.now(),
|
|
270
|
+
pendingCallbacks,
|
|
271
|
+
returnedCallbacks: new Map,
|
|
272
|
+
returnedPromises: new Map,
|
|
273
|
+
returnedIterators: new Map,
|
|
274
|
+
nextLocalCallbackId: 1e6
|
|
210
275
|
};
|
|
211
|
-
if (
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
276
|
+
if (moduleLoaderCallback) {
|
|
277
|
+
instance.moduleLoaderCallbackId = moduleLoaderCallback.callbackId;
|
|
278
|
+
instance.moduleCache = new Map;
|
|
279
|
+
}
|
|
280
|
+
if (customCallbacks) {
|
|
281
|
+
await setupCustomFunctions(runtime.context, customCallbacks, connection, instance);
|
|
282
|
+
}
|
|
283
|
+
if (consoleCallbacks?.onEntry) {
|
|
284
|
+
instance.callbacks.set(consoleCallbacks.onEntry.callbackId, {
|
|
285
|
+
...consoleCallbacks.onEntry,
|
|
286
|
+
name: "onEntry"
|
|
287
|
+
});
|
|
217
288
|
}
|
|
218
289
|
if (fetchCallback) {
|
|
219
290
|
instance.callbacks.set(fetchCallback.callbackId, fetchCallback);
|
|
@@ -225,9 +296,86 @@ async function handleCreateRuntime(message, connection, state) {
|
|
|
225
296
|
}
|
|
226
297
|
}
|
|
227
298
|
}
|
|
299
|
+
if (moduleLoaderCallback) {
|
|
300
|
+
instance.callbacks.set(moduleLoaderCallback.callbackId, moduleLoaderCallback);
|
|
301
|
+
}
|
|
302
|
+
if (customCallbacks) {
|
|
303
|
+
for (const [name, reg] of Object.entries(customCallbacks)) {
|
|
304
|
+
if (reg) {
|
|
305
|
+
instance.callbacks.set(reg.callbackId, { ...reg, name });
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
if (message.options.testEnvironment) {
|
|
310
|
+
const testEnvOption = message.options.testEnvironment;
|
|
311
|
+
const testEnvOptions = typeof testEnvOption === "object" ? testEnvOption : undefined;
|
|
312
|
+
const onEventCallback = testEnvOptions?.callbacks?.onEvent;
|
|
313
|
+
await import_isolate_test_environment.setupTestEnvironment(runtime.context, {
|
|
314
|
+
onEvent: onEventCallback ? (event) => {
|
|
315
|
+
const promise = invokeClientCallback(connection, onEventCallback.callbackId, [JSON.stringify(event)]).catch(() => {});
|
|
316
|
+
pendingCallbacks.push(promise);
|
|
317
|
+
} : undefined,
|
|
318
|
+
testTimeout: testEnvOptions?.testTimeout
|
|
319
|
+
});
|
|
320
|
+
instance.testEnvironmentEnabled = true;
|
|
321
|
+
if (onEventCallback) {
|
|
322
|
+
instance.callbacks.set(onEventCallback.callbackId, {
|
|
323
|
+
...onEventCallback,
|
|
324
|
+
name: "testEnvironment.onEvent"
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
const playwrightCallbacks = message.options.callbacks?.playwright;
|
|
329
|
+
if (playwrightCallbacks) {
|
|
330
|
+
const handler = async (op) => {
|
|
331
|
+
try {
|
|
332
|
+
const resultJson = await invokeClientCallback(connection, playwrightCallbacks.handlerCallbackId, [JSON.stringify(op)]);
|
|
333
|
+
return JSON.parse(resultJson);
|
|
334
|
+
} catch (err) {
|
|
335
|
+
const error = err;
|
|
336
|
+
return { ok: false, error: { name: error.name, message: error.message } };
|
|
337
|
+
}
|
|
338
|
+
};
|
|
339
|
+
instance.playwrightHandle = await import_isolate_playwright.setupPlaywright(runtime.context, {
|
|
340
|
+
handler,
|
|
341
|
+
console: playwrightCallbacks.console,
|
|
342
|
+
onEvent: (event) => {
|
|
343
|
+
if (event.type === "browserConsoleLog" && playwrightCallbacks.onBrowserConsoleLogCallbackId) {
|
|
344
|
+
const promise = invokeClientCallback(connection, playwrightCallbacks.onBrowserConsoleLogCallbackId, [{ level: event.level, args: event.args, timestamp: event.timestamp }]).catch(() => {});
|
|
345
|
+
pendingCallbacks.push(promise);
|
|
346
|
+
} else if (event.type === "networkRequest" && playwrightCallbacks.onNetworkRequestCallbackId) {
|
|
347
|
+
const promise = invokeClientCallback(connection, playwrightCallbacks.onNetworkRequestCallbackId, [event]).catch(() => {});
|
|
348
|
+
pendingCallbacks.push(promise);
|
|
349
|
+
} else if (event.type === "networkResponse" && playwrightCallbacks.onNetworkResponseCallbackId) {
|
|
350
|
+
const promise = invokeClientCallback(connection, playwrightCallbacks.onNetworkResponseCallbackId, [event]).catch(() => {});
|
|
351
|
+
pendingCallbacks.push(promise);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
});
|
|
355
|
+
}
|
|
228
356
|
state.isolates.set(isolateId, instance);
|
|
229
357
|
connection.isolates.add(isolateId);
|
|
230
358
|
state.stats.totalIsolatesCreated++;
|
|
359
|
+
instance.runtime.fetch.onWebSocketCommand((cmd) => {
|
|
360
|
+
let data;
|
|
361
|
+
if (cmd.data instanceof ArrayBuffer) {
|
|
362
|
+
data = new Uint8Array(cmd.data);
|
|
363
|
+
} else {
|
|
364
|
+
data = cmd.data;
|
|
365
|
+
}
|
|
366
|
+
const wsCommandMsg = {
|
|
367
|
+
type: import_isolate_protocol.MessageType.WS_COMMAND,
|
|
368
|
+
isolateId,
|
|
369
|
+
command: {
|
|
370
|
+
type: cmd.type,
|
|
371
|
+
connectionId: cmd.connectionId,
|
|
372
|
+
data,
|
|
373
|
+
code: cmd.code,
|
|
374
|
+
reason: cmd.reason
|
|
375
|
+
}
|
|
376
|
+
};
|
|
377
|
+
sendMessage(connection.socket, wsCommandMsg);
|
|
378
|
+
});
|
|
231
379
|
sendOk(connection.socket, message.requestId, { isolateId });
|
|
232
380
|
} catch (err) {
|
|
233
381
|
const error = err;
|
|
@@ -248,12 +396,6 @@ async function handleDisposeRuntime(message, connection, state) {
|
|
|
248
396
|
if (instance.playwrightHandle) {
|
|
249
397
|
instance.playwrightHandle.dispose();
|
|
250
398
|
}
|
|
251
|
-
if (instance.browserContext) {
|
|
252
|
-
await instance.browserContext.close();
|
|
253
|
-
}
|
|
254
|
-
if (instance.browser) {
|
|
255
|
-
await instance.browser.close();
|
|
256
|
-
}
|
|
257
399
|
instance.runtime.dispose();
|
|
258
400
|
state.isolates.delete(message.isolateId);
|
|
259
401
|
connection.isolates.delete(message.isolateId);
|
|
@@ -271,13 +413,26 @@ async function handleEval(message, connection, state) {
|
|
|
271
413
|
}
|
|
272
414
|
instance.lastActivity = Date.now();
|
|
273
415
|
try {
|
|
274
|
-
const
|
|
275
|
-
filename: message.filename
|
|
416
|
+
const mod = await instance.runtime.isolate.compileModule(message.code, {
|
|
417
|
+
filename: message.filename ?? "<eval>"
|
|
276
418
|
});
|
|
277
|
-
|
|
419
|
+
if (instance.moduleLoaderCallbackId) {
|
|
420
|
+
const resolver = createModuleResolver(instance, connection);
|
|
421
|
+
await mod.instantiate(instance.runtime.context, resolver);
|
|
422
|
+
} else {
|
|
423
|
+
await mod.instantiate(instance.runtime.context, (specifier) => {
|
|
424
|
+
throw new Error(`No module loader registered. Cannot import: ${specifier}`);
|
|
425
|
+
});
|
|
426
|
+
}
|
|
427
|
+
const timeout = message.maxExecutionMs;
|
|
428
|
+
await mod.evaluate(timeout ? { timeout } : undefined);
|
|
429
|
+
await Promise.all(instance.pendingCallbacks);
|
|
430
|
+
instance.pendingCallbacks.length = 0;
|
|
431
|
+
sendOk(connection.socket, message.requestId, { value: undefined });
|
|
278
432
|
} catch (err) {
|
|
279
433
|
const error = err;
|
|
280
|
-
|
|
434
|
+
const isTimeoutError = error.message?.includes("Script execution timed out");
|
|
435
|
+
sendError(connection.socket, message.requestId, isTimeoutError ? import_isolate_protocol.ErrorCode.ISOLATE_TIMEOUT : import_isolate_protocol.ErrorCode.SCRIPT_ERROR, error.message, { name: error.name, stack: error.stack });
|
|
281
436
|
}
|
|
282
437
|
}
|
|
283
438
|
async function handleDispatchRequest(message, connection, state) {
|
|
@@ -288,24 +443,135 @@ async function handleDispatchRequest(message, connection, state) {
|
|
|
288
443
|
}
|
|
289
444
|
instance.lastActivity = Date.now();
|
|
290
445
|
try {
|
|
446
|
+
let requestBody = null;
|
|
447
|
+
if (message.request.bodyStreamId !== undefined) {
|
|
448
|
+
requestBody = await receiveStreamedBody(connection, message.request.bodyStreamId);
|
|
449
|
+
} else if (message.request.body) {
|
|
450
|
+
requestBody = message.request.body;
|
|
451
|
+
}
|
|
291
452
|
const request = new Request(message.request.url, {
|
|
292
453
|
method: message.request.method,
|
|
293
454
|
headers: message.request.headers,
|
|
294
|
-
body:
|
|
455
|
+
body: requestBody
|
|
295
456
|
});
|
|
296
|
-
const response = await instance.runtime.fetch.dispatchRequest(request
|
|
297
|
-
|
|
298
|
-
|
|
457
|
+
const response = await instance.runtime.fetch.dispatchRequest(request);
|
|
458
|
+
const contentLength = response.headers.get("content-length");
|
|
459
|
+
const knownSize = contentLength ? parseInt(contentLength, 10) : null;
|
|
460
|
+
if (knownSize !== null && knownSize > import_isolate_protocol.STREAM_THRESHOLD) {
|
|
461
|
+
await sendStreamedResponse(connection, message.requestId, response);
|
|
462
|
+
} else {
|
|
463
|
+
const clonedResponse = response.clone();
|
|
464
|
+
try {
|
|
465
|
+
const serialized = await serializeResponse(response);
|
|
466
|
+
if (serialized.body && serialized.body.length > import_isolate_protocol.STREAM_THRESHOLD) {
|
|
467
|
+
await sendStreamedResponse(connection, message.requestId, clonedResponse);
|
|
468
|
+
} else {
|
|
469
|
+
sendOk(connection.socket, message.requestId, { response: serialized });
|
|
470
|
+
}
|
|
471
|
+
} catch {
|
|
472
|
+
await sendStreamedResponse(connection, message.requestId, clonedResponse);
|
|
299
473
|
}
|
|
474
|
+
}
|
|
475
|
+
} catch (err) {
|
|
476
|
+
const error = err;
|
|
477
|
+
sendError(connection.socket, message.requestId, import_isolate_protocol.ErrorCode.SCRIPT_ERROR, error.message, { name: error.name, stack: error.stack });
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
function receiveStreamedBody(connection, streamId) {
|
|
481
|
+
return new Promise((resolve, reject) => {
|
|
482
|
+
const receiver = {
|
|
483
|
+
streamId,
|
|
484
|
+
requestId: 0,
|
|
485
|
+
chunks: [],
|
|
486
|
+
totalBytes: 0,
|
|
487
|
+
resolve,
|
|
488
|
+
reject
|
|
489
|
+
};
|
|
490
|
+
connection.streamReceivers.set(streamId, receiver);
|
|
491
|
+
sendMessage(connection.socket, {
|
|
492
|
+
type: import_isolate_protocol.MessageType.STREAM_PULL,
|
|
493
|
+
streamId,
|
|
494
|
+
maxBytes: import_isolate_protocol.STREAM_DEFAULT_CREDIT
|
|
300
495
|
});
|
|
301
|
-
|
|
302
|
-
|
|
496
|
+
});
|
|
497
|
+
}
|
|
498
|
+
async function handleWsOpen(message, connection, state) {
|
|
499
|
+
const instance = state.isolates.get(message.isolateId);
|
|
500
|
+
if (!instance) {
|
|
501
|
+
sendError(connection.socket, message.requestId, import_isolate_protocol.ErrorCode.ISOLATE_NOT_FOUND, `Isolate not found: ${message.isolateId}`);
|
|
502
|
+
return;
|
|
503
|
+
}
|
|
504
|
+
instance.lastActivity = Date.now();
|
|
505
|
+
try {
|
|
506
|
+
instance.runtime.fetch.dispatchWebSocketOpen(message.connectionId);
|
|
507
|
+
sendOk(connection.socket, message.requestId);
|
|
508
|
+
} catch (err) {
|
|
509
|
+
const error = err;
|
|
510
|
+
sendError(connection.socket, message.requestId, import_isolate_protocol.ErrorCode.SCRIPT_ERROR, error.message, { name: error.name, stack: error.stack });
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
async function handleWsMessage(message, connection, state) {
|
|
514
|
+
const instance = state.isolates.get(message.isolateId);
|
|
515
|
+
if (!instance) {
|
|
516
|
+
sendError(connection.socket, message.requestId, import_isolate_protocol.ErrorCode.ISOLATE_NOT_FOUND, `Isolate not found: ${message.isolateId}`);
|
|
517
|
+
return;
|
|
518
|
+
}
|
|
519
|
+
instance.lastActivity = Date.now();
|
|
520
|
+
try {
|
|
521
|
+
const data = message.data instanceof Uint8Array ? message.data.buffer.slice(message.data.byteOffset, message.data.byteOffset + message.data.byteLength) : message.data;
|
|
522
|
+
instance.runtime.fetch.dispatchWebSocketMessage(message.connectionId, data);
|
|
523
|
+
sendOk(connection.socket, message.requestId);
|
|
524
|
+
} catch (err) {
|
|
525
|
+
const error = err;
|
|
526
|
+
sendError(connection.socket, message.requestId, import_isolate_protocol.ErrorCode.SCRIPT_ERROR, error.message, { name: error.name, stack: error.stack });
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
async function handleWsClose(message, connection, state) {
|
|
530
|
+
const instance = state.isolates.get(message.isolateId);
|
|
531
|
+
if (!instance) {
|
|
532
|
+
sendError(connection.socket, message.requestId, import_isolate_protocol.ErrorCode.ISOLATE_NOT_FOUND, `Isolate not found: ${message.isolateId}`);
|
|
533
|
+
return;
|
|
534
|
+
}
|
|
535
|
+
instance.lastActivity = Date.now();
|
|
536
|
+
try {
|
|
537
|
+
instance.runtime.fetch.dispatchWebSocketClose(message.connectionId, message.code, message.reason);
|
|
538
|
+
sendOk(connection.socket, message.requestId);
|
|
539
|
+
} catch (err) {
|
|
540
|
+
const error = err;
|
|
541
|
+
sendError(connection.socket, message.requestId, import_isolate_protocol.ErrorCode.SCRIPT_ERROR, error.message, { name: error.name, stack: error.stack });
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
async function handleFetchGetUpgradeRequest(message, connection, state) {
|
|
545
|
+
const instance = state.isolates.get(message.isolateId);
|
|
546
|
+
if (!instance) {
|
|
547
|
+
sendError(connection.socket, message.requestId, import_isolate_protocol.ErrorCode.ISOLATE_NOT_FOUND, `Isolate not found: ${message.isolateId}`);
|
|
548
|
+
return;
|
|
549
|
+
}
|
|
550
|
+
instance.lastActivity = Date.now();
|
|
551
|
+
try {
|
|
552
|
+
const upgradeRequest = instance.runtime.fetch.getUpgradeRequest();
|
|
553
|
+
sendOk(connection.socket, message.requestId, upgradeRequest);
|
|
554
|
+
} catch (err) {
|
|
555
|
+
const error = err;
|
|
556
|
+
sendError(connection.socket, message.requestId, import_isolate_protocol.ErrorCode.SCRIPT_ERROR, error.message, { name: error.name, stack: error.stack });
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
async function handleFetchHasServeHandler(message, connection, state) {
|
|
560
|
+
const instance = state.isolates.get(message.isolateId);
|
|
561
|
+
if (!instance) {
|
|
562
|
+
sendError(connection.socket, message.requestId, import_isolate_protocol.ErrorCode.ISOLATE_NOT_FOUND, `Isolate not found: ${message.isolateId}`);
|
|
563
|
+
return;
|
|
564
|
+
}
|
|
565
|
+
instance.lastActivity = Date.now();
|
|
566
|
+
try {
|
|
567
|
+
const hasHandler = instance.runtime.fetch.hasServeHandler();
|
|
568
|
+
sendOk(connection.socket, message.requestId, hasHandler);
|
|
303
569
|
} catch (err) {
|
|
304
570
|
const error = err;
|
|
305
571
|
sendError(connection.socket, message.requestId, import_isolate_protocol.ErrorCode.SCRIPT_ERROR, error.message, { name: error.name, stack: error.stack });
|
|
306
572
|
}
|
|
307
573
|
}
|
|
308
|
-
async function
|
|
574
|
+
async function handleFetchHasActiveConnections(message, connection, state) {
|
|
309
575
|
const instance = state.isolates.get(message.isolateId);
|
|
310
576
|
if (!instance) {
|
|
311
577
|
sendError(connection.socket, message.requestId, import_isolate_protocol.ErrorCode.ISOLATE_NOT_FOUND, `Isolate not found: ${message.isolateId}`);
|
|
@@ -313,13 +579,103 @@ async function handleTick(message, connection, state) {
|
|
|
313
579
|
}
|
|
314
580
|
instance.lastActivity = Date.now();
|
|
315
581
|
try {
|
|
316
|
-
instance.runtime.
|
|
582
|
+
const hasConnections = instance.runtime.fetch.hasActiveConnections();
|
|
583
|
+
sendOk(connection.socket, message.requestId, hasConnections);
|
|
584
|
+
} catch (err) {
|
|
585
|
+
const error = err;
|
|
586
|
+
sendError(connection.socket, message.requestId, import_isolate_protocol.ErrorCode.SCRIPT_ERROR, error.message, { name: error.name, stack: error.stack });
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
async function handleFetchWsError(message, connection, state) {
|
|
590
|
+
const instance = state.isolates.get(message.isolateId);
|
|
591
|
+
if (!instance) {
|
|
592
|
+
sendError(connection.socket, message.requestId, import_isolate_protocol.ErrorCode.ISOLATE_NOT_FOUND, `Isolate not found: ${message.isolateId}`);
|
|
593
|
+
return;
|
|
594
|
+
}
|
|
595
|
+
instance.lastActivity = Date.now();
|
|
596
|
+
try {
|
|
597
|
+
instance.runtime.fetch.dispatchWebSocketError(message.connectionId, new Error(message.error));
|
|
317
598
|
sendOk(connection.socket, message.requestId);
|
|
318
599
|
} catch (err) {
|
|
319
600
|
const error = err;
|
|
320
601
|
sendError(connection.socket, message.requestId, import_isolate_protocol.ErrorCode.SCRIPT_ERROR, error.message, { name: error.name, stack: error.stack });
|
|
321
602
|
}
|
|
322
603
|
}
|
|
604
|
+
async function handleTimersClearAll(message, connection, state) {
|
|
605
|
+
const instance = state.isolates.get(message.isolateId);
|
|
606
|
+
if (!instance) {
|
|
607
|
+
sendError(connection.socket, message.requestId, import_isolate_protocol.ErrorCode.ISOLATE_NOT_FOUND, `Isolate not found: ${message.isolateId}`);
|
|
608
|
+
return;
|
|
609
|
+
}
|
|
610
|
+
instance.lastActivity = Date.now();
|
|
611
|
+
try {
|
|
612
|
+
instance.runtime.timers.clearAll();
|
|
613
|
+
sendOk(connection.socket, message.requestId);
|
|
614
|
+
} catch (err) {
|
|
615
|
+
const error = err;
|
|
616
|
+
sendError(connection.socket, message.requestId, import_isolate_protocol.ErrorCode.SCRIPT_ERROR, error.message, { name: error.name, stack: error.stack });
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
async function handleConsoleReset(message, connection, state) {
|
|
620
|
+
const instance = state.isolates.get(message.isolateId);
|
|
621
|
+
if (!instance) {
|
|
622
|
+
sendError(connection.socket, message.requestId, import_isolate_protocol.ErrorCode.ISOLATE_NOT_FOUND, `Isolate not found: ${message.isolateId}`);
|
|
623
|
+
return;
|
|
624
|
+
}
|
|
625
|
+
instance.lastActivity = Date.now();
|
|
626
|
+
try {
|
|
627
|
+
instance.runtime.console.reset();
|
|
628
|
+
sendOk(connection.socket, message.requestId);
|
|
629
|
+
} catch (err) {
|
|
630
|
+
const error = err;
|
|
631
|
+
sendError(connection.socket, message.requestId, import_isolate_protocol.ErrorCode.SCRIPT_ERROR, error.message, { name: error.name, stack: error.stack });
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
async function handleConsoleGetTimers(message, connection, state) {
|
|
635
|
+
const instance = state.isolates.get(message.isolateId);
|
|
636
|
+
if (!instance) {
|
|
637
|
+
sendError(connection.socket, message.requestId, import_isolate_protocol.ErrorCode.ISOLATE_NOT_FOUND, `Isolate not found: ${message.isolateId}`);
|
|
638
|
+
return;
|
|
639
|
+
}
|
|
640
|
+
instance.lastActivity = Date.now();
|
|
641
|
+
try {
|
|
642
|
+
const timers = instance.runtime.console.getTimers();
|
|
643
|
+
sendOk(connection.socket, message.requestId, Object.fromEntries(timers));
|
|
644
|
+
} catch (err) {
|
|
645
|
+
const error = err;
|
|
646
|
+
sendError(connection.socket, message.requestId, import_isolate_protocol.ErrorCode.SCRIPT_ERROR, error.message, { name: error.name, stack: error.stack });
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
async function handleConsoleGetCounters(message, connection, state) {
|
|
650
|
+
const instance = state.isolates.get(message.isolateId);
|
|
651
|
+
if (!instance) {
|
|
652
|
+
sendError(connection.socket, message.requestId, import_isolate_protocol.ErrorCode.ISOLATE_NOT_FOUND, `Isolate not found: ${message.isolateId}`);
|
|
653
|
+
return;
|
|
654
|
+
}
|
|
655
|
+
instance.lastActivity = Date.now();
|
|
656
|
+
try {
|
|
657
|
+
const counters = instance.runtime.console.getCounters();
|
|
658
|
+
sendOk(connection.socket, message.requestId, Object.fromEntries(counters));
|
|
659
|
+
} catch (err) {
|
|
660
|
+
const error = err;
|
|
661
|
+
sendError(connection.socket, message.requestId, import_isolate_protocol.ErrorCode.SCRIPT_ERROR, error.message, { name: error.name, stack: error.stack });
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
async function handleConsoleGetGroupDepth(message, connection, state) {
|
|
665
|
+
const instance = state.isolates.get(message.isolateId);
|
|
666
|
+
if (!instance) {
|
|
667
|
+
sendError(connection.socket, message.requestId, import_isolate_protocol.ErrorCode.ISOLATE_NOT_FOUND, `Isolate not found: ${message.isolateId}`);
|
|
668
|
+
return;
|
|
669
|
+
}
|
|
670
|
+
instance.lastActivity = Date.now();
|
|
671
|
+
try {
|
|
672
|
+
const depth = instance.runtime.console.getGroupDepth();
|
|
673
|
+
sendOk(connection.socket, message.requestId, depth);
|
|
674
|
+
} catch (err) {
|
|
675
|
+
const error = err;
|
|
676
|
+
sendError(connection.socket, message.requestId, import_isolate_protocol.ErrorCode.SCRIPT_ERROR, error.message, { name: error.name, stack: error.stack });
|
|
677
|
+
}
|
|
678
|
+
}
|
|
323
679
|
function handleCallbackResponse(message, connection) {
|
|
324
680
|
const pending = connection.pendingCallbacks.get(message.requestId);
|
|
325
681
|
if (!pending) {
|
|
@@ -363,6 +719,477 @@ async function invokeClientCallback(connection, callbackId, args, timeout = 1e4)
|
|
|
363
719
|
sendMessage(connection.socket, invoke);
|
|
364
720
|
});
|
|
365
721
|
}
|
|
722
|
+
var ISOLATE_MARSHAL_CODE = `
|
|
723
|
+
(function() {
|
|
724
|
+
// Marshal a value (JavaScript \u2192 Ref)
|
|
725
|
+
function marshalForHost(value, depth = 0) {
|
|
726
|
+
if (depth > 100) throw new Error('Maximum marshalling depth exceeded');
|
|
727
|
+
|
|
728
|
+
if (value === null) return null;
|
|
729
|
+
if (value === undefined) return { __type: 'UndefinedRef' };
|
|
730
|
+
|
|
731
|
+
const type = typeof value;
|
|
732
|
+
if (type === 'string' || type === 'number' || type === 'boolean') return value;
|
|
733
|
+
if (type === 'bigint') return { __type: 'BigIntRef', value: value.toString() };
|
|
734
|
+
if (type === 'function') throw new Error('Cannot marshal functions from isolate');
|
|
735
|
+
if (type === 'symbol') throw new Error('Cannot marshal Symbol values');
|
|
736
|
+
|
|
737
|
+
if (type === 'object') {
|
|
738
|
+
if (value instanceof Date) {
|
|
739
|
+
return { __type: 'DateRef', timestamp: value.getTime() };
|
|
740
|
+
}
|
|
741
|
+
if (value instanceof RegExp) {
|
|
742
|
+
return { __type: 'RegExpRef', source: value.source, flags: value.flags };
|
|
743
|
+
}
|
|
744
|
+
if (value instanceof URL) {
|
|
745
|
+
return { __type: 'URLRef', href: value.href };
|
|
746
|
+
}
|
|
747
|
+
if (typeof Headers !== 'undefined' && value instanceof Headers) {
|
|
748
|
+
const pairs = [];
|
|
749
|
+
value.forEach((v, k) => pairs.push([k, v]));
|
|
750
|
+
return { __type: 'HeadersRef', pairs };
|
|
751
|
+
}
|
|
752
|
+
if (value instanceof Uint8Array) {
|
|
753
|
+
return { __type: 'Uint8ArrayRef', data: Array.from(value) };
|
|
754
|
+
}
|
|
755
|
+
if (value instanceof ArrayBuffer) {
|
|
756
|
+
return { __type: 'Uint8ArrayRef', data: Array.from(new Uint8Array(value)) };
|
|
757
|
+
}
|
|
758
|
+
if (typeof Request !== 'undefined' && value instanceof Request) {
|
|
759
|
+
throw new Error('Cannot marshal Request from isolate. Use fetch callback instead.');
|
|
760
|
+
}
|
|
761
|
+
if (typeof Response !== 'undefined' && value instanceof Response) {
|
|
762
|
+
throw new Error('Cannot marshal Response from isolate. Return plain objects instead.');
|
|
763
|
+
}
|
|
764
|
+
if (typeof File !== 'undefined' && value instanceof File) {
|
|
765
|
+
throw new Error('Cannot marshal File from isolate.');
|
|
766
|
+
}
|
|
767
|
+
if (typeof Blob !== 'undefined' && value instanceof Blob) {
|
|
768
|
+
throw new Error('Cannot marshal Blob from isolate.');
|
|
769
|
+
}
|
|
770
|
+
if (typeof FormData !== 'undefined' && value instanceof FormData) {
|
|
771
|
+
throw new Error('Cannot marshal FormData from isolate.');
|
|
772
|
+
}
|
|
773
|
+
if (Array.isArray(value)) {
|
|
774
|
+
return value.map(v => marshalForHost(v, depth + 1));
|
|
775
|
+
}
|
|
776
|
+
// Plain object
|
|
777
|
+
const result = {};
|
|
778
|
+
for (const key of Object.keys(value)) {
|
|
779
|
+
result[key] = marshalForHost(value[key], depth + 1);
|
|
780
|
+
}
|
|
781
|
+
return result;
|
|
782
|
+
}
|
|
783
|
+
return value;
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
// Unmarshal a value (Ref \u2192 JavaScript)
|
|
787
|
+
function unmarshalFromHost(value, depth = 0) {
|
|
788
|
+
if (depth > 100) throw new Error('Maximum unmarshalling depth exceeded');
|
|
789
|
+
|
|
790
|
+
if (value === null) return null;
|
|
791
|
+
if (typeof value !== 'object') return value;
|
|
792
|
+
|
|
793
|
+
if (value.__type) {
|
|
794
|
+
switch (value.__type) {
|
|
795
|
+
case 'UndefinedRef': return undefined;
|
|
796
|
+
case 'DateRef': return new Date(value.timestamp);
|
|
797
|
+
case 'RegExpRef': return new RegExp(value.source, value.flags);
|
|
798
|
+
case 'BigIntRef': return BigInt(value.value);
|
|
799
|
+
case 'URLRef': return new URL(value.href);
|
|
800
|
+
case 'HeadersRef': return new Headers(value.pairs);
|
|
801
|
+
case 'Uint8ArrayRef': return new Uint8Array(value.data);
|
|
802
|
+
case 'RequestRef': {
|
|
803
|
+
const init = {
|
|
804
|
+
method: value.method,
|
|
805
|
+
headers: value.headers,
|
|
806
|
+
body: value.body ? new Uint8Array(value.body) : null,
|
|
807
|
+
};
|
|
808
|
+
if (value.mode) init.mode = value.mode;
|
|
809
|
+
if (value.credentials) init.credentials = value.credentials;
|
|
810
|
+
if (value.cache) init.cache = value.cache;
|
|
811
|
+
if (value.redirect) init.redirect = value.redirect;
|
|
812
|
+
if (value.referrer) init.referrer = value.referrer;
|
|
813
|
+
if (value.referrerPolicy) init.referrerPolicy = value.referrerPolicy;
|
|
814
|
+
if (value.integrity) init.integrity = value.integrity;
|
|
815
|
+
return new Request(value.url, init);
|
|
816
|
+
}
|
|
817
|
+
case 'ResponseRef': {
|
|
818
|
+
return new Response(value.body ? new Uint8Array(value.body) : null, {
|
|
819
|
+
status: value.status,
|
|
820
|
+
statusText: value.statusText,
|
|
821
|
+
headers: value.headers,
|
|
822
|
+
});
|
|
823
|
+
}
|
|
824
|
+
case 'FileRef': {
|
|
825
|
+
if (!value.name) {
|
|
826
|
+
return new Blob([new Uint8Array(value.data)], { type: value.type });
|
|
827
|
+
}
|
|
828
|
+
return new File([new Uint8Array(value.data)], value.name, {
|
|
829
|
+
type: value.type,
|
|
830
|
+
lastModified: value.lastModified,
|
|
831
|
+
});
|
|
832
|
+
}
|
|
833
|
+
case 'FormDataRef': {
|
|
834
|
+
const fd = new FormData();
|
|
835
|
+
for (const [key, entry] of value.entries) {
|
|
836
|
+
if (typeof entry === 'string') {
|
|
837
|
+
fd.append(key, entry);
|
|
838
|
+
} else {
|
|
839
|
+
const file = unmarshalFromHost(entry, depth + 1);
|
|
840
|
+
fd.append(key, file);
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
return fd;
|
|
844
|
+
}
|
|
845
|
+
case 'CallbackRef': {
|
|
846
|
+
// Create a proxy function that invokes the callback
|
|
847
|
+
const callbackId = value.callbackId;
|
|
848
|
+
return function(...args) {
|
|
849
|
+
const argsJson = JSON.stringify(marshalForHost(args));
|
|
850
|
+
const resultJson = __customFn_invoke.applySyncPromise(undefined, [callbackId, argsJson]);
|
|
851
|
+
const result = JSON.parse(resultJson);
|
|
852
|
+
if (result.ok) {
|
|
853
|
+
return unmarshalFromHost(result.value);
|
|
854
|
+
} else {
|
|
855
|
+
const error = new Error(result.error.message);
|
|
856
|
+
error.name = result.error.name;
|
|
857
|
+
throw error;
|
|
858
|
+
}
|
|
859
|
+
};
|
|
860
|
+
}
|
|
861
|
+
case 'PromiseRef': {
|
|
862
|
+
// Create a proxy Promise that resolves via callback
|
|
863
|
+
const promiseId = value.promiseId;
|
|
864
|
+
return new Promise((resolve, reject) => {
|
|
865
|
+
try {
|
|
866
|
+
const argsJson = JSON.stringify([promiseId]);
|
|
867
|
+
const resultJson = __customFn_invoke.applySyncPromise(undefined, [value.__resolveCallbackId, argsJson]);
|
|
868
|
+
const result = JSON.parse(resultJson);
|
|
869
|
+
if (result.ok) {
|
|
870
|
+
resolve(unmarshalFromHost(result.value));
|
|
871
|
+
} else {
|
|
872
|
+
reject(new Error(result.error.message));
|
|
873
|
+
}
|
|
874
|
+
} catch (e) {
|
|
875
|
+
reject(e);
|
|
876
|
+
}
|
|
877
|
+
});
|
|
878
|
+
}
|
|
879
|
+
case 'AsyncIteratorRef': {
|
|
880
|
+
const iteratorId = value.iteratorId;
|
|
881
|
+
const nextCallbackId = value.__nextCallbackId;
|
|
882
|
+
const returnCallbackId = value.__returnCallbackId;
|
|
883
|
+
return {
|
|
884
|
+
[Symbol.asyncIterator]() { return this; },
|
|
885
|
+
async next() {
|
|
886
|
+
const argsJson = JSON.stringify([iteratorId]);
|
|
887
|
+
const resultJson = __customFn_invoke.applySyncPromise(undefined, [nextCallbackId, argsJson]);
|
|
888
|
+
const result = JSON.parse(resultJson);
|
|
889
|
+
if (!result.ok) {
|
|
890
|
+
const error = new Error(result.error.message);
|
|
891
|
+
error.name = result.error.name;
|
|
892
|
+
throw error;
|
|
893
|
+
}
|
|
894
|
+
return {
|
|
895
|
+
done: result.value.done,
|
|
896
|
+
value: unmarshalFromHost(result.value.value)
|
|
897
|
+
};
|
|
898
|
+
},
|
|
899
|
+
async return(v) {
|
|
900
|
+
const argsJson = JSON.stringify([iteratorId, marshalForHost(v)]);
|
|
901
|
+
const resultJson = __customFn_invoke.applySyncPromise(undefined, [returnCallbackId, argsJson]);
|
|
902
|
+
const result = JSON.parse(resultJson);
|
|
903
|
+
return { done: true, value: result.ok ? unmarshalFromHost(result.value) : undefined };
|
|
904
|
+
}
|
|
905
|
+
};
|
|
906
|
+
}
|
|
907
|
+
default:
|
|
908
|
+
// Unknown ref type, return as-is
|
|
909
|
+
break;
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
if (Array.isArray(value)) {
|
|
914
|
+
return value.map(v => unmarshalFromHost(v, depth + 1));
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
// Plain object - recursively unmarshal
|
|
918
|
+
const result = {};
|
|
919
|
+
for (const key of Object.keys(value)) {
|
|
920
|
+
result[key] = unmarshalFromHost(value[key], depth + 1);
|
|
921
|
+
}
|
|
922
|
+
return result;
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
globalThis.__marshalForHost = marshalForHost;
|
|
926
|
+
globalThis.__unmarshalFromHost = unmarshalFromHost;
|
|
927
|
+
})();
|
|
928
|
+
`;
|
|
929
|
+
var LOCAL_CALLBACK_THRESHOLD = 1e6;
|
|
930
|
+
function isPromiseRef(value) {
|
|
931
|
+
return typeof value === "object" && value !== null && value.__type === "PromiseRef";
|
|
932
|
+
}
|
|
933
|
+
function isAsyncIteratorRef(value) {
|
|
934
|
+
return typeof value === "object" && value !== null && value.__type === "AsyncIteratorRef";
|
|
935
|
+
}
|
|
936
|
+
function isLocalCallbackId(callbackId) {
|
|
937
|
+
return callbackId >= LOCAL_CALLBACK_THRESHOLD;
|
|
938
|
+
}
|
|
939
|
+
async function setupCustomFunctions(context, customCallbacks, connection, instance) {
|
|
940
|
+
const global = context.global;
|
|
941
|
+
function createMarshalContext() {
|
|
942
|
+
return {
|
|
943
|
+
registerCallback: (fn) => {
|
|
944
|
+
const callbackId = instance.nextLocalCallbackId++;
|
|
945
|
+
instance.returnedCallbacks.set(callbackId, fn);
|
|
946
|
+
return callbackId;
|
|
947
|
+
},
|
|
948
|
+
registerPromise: (promise) => {
|
|
949
|
+
const promiseId = instance.nextLocalCallbackId++;
|
|
950
|
+
instance.returnedPromises.set(promiseId, promise);
|
|
951
|
+
return promiseId;
|
|
952
|
+
},
|
|
953
|
+
registerIterator: (iterator) => {
|
|
954
|
+
const iteratorId = instance.nextLocalCallbackId++;
|
|
955
|
+
instance.returnedIterators.set(iteratorId, iterator);
|
|
956
|
+
return iteratorId;
|
|
957
|
+
}
|
|
958
|
+
};
|
|
959
|
+
}
|
|
960
|
+
function addCallbackIdsToRefs(value) {
|
|
961
|
+
if (value === null || typeof value !== "object") {
|
|
962
|
+
return value;
|
|
963
|
+
}
|
|
964
|
+
if (isPromiseRef(value)) {
|
|
965
|
+
if ("__resolveCallbackId" in value) {
|
|
966
|
+
return value;
|
|
967
|
+
}
|
|
968
|
+
const resolveCallbackId = instance.nextLocalCallbackId++;
|
|
969
|
+
instance.returnedCallbacks.set(resolveCallbackId, async (promiseId) => {
|
|
970
|
+
const promise = instance.returnedPromises.get(promiseId);
|
|
971
|
+
if (!promise) {
|
|
972
|
+
throw new Error(`Promise ${promiseId} not found`);
|
|
973
|
+
}
|
|
974
|
+
const result2 = await promise;
|
|
975
|
+
instance.returnedPromises.delete(promiseId);
|
|
976
|
+
const ctx = createMarshalContext();
|
|
977
|
+
const marshalled = await import_isolate_protocol.marshalValue(result2, ctx);
|
|
978
|
+
return addCallbackIdsToRefs(marshalled);
|
|
979
|
+
});
|
|
980
|
+
return {
|
|
981
|
+
...value,
|
|
982
|
+
__resolveCallbackId: resolveCallbackId
|
|
983
|
+
};
|
|
984
|
+
}
|
|
985
|
+
if (isAsyncIteratorRef(value)) {
|
|
986
|
+
if ("__nextCallbackId" in value) {
|
|
987
|
+
return value;
|
|
988
|
+
}
|
|
989
|
+
const nextCallbackId = instance.nextLocalCallbackId++;
|
|
990
|
+
instance.returnedCallbacks.set(nextCallbackId, async (iteratorId) => {
|
|
991
|
+
const iterator = instance.returnedIterators.get(iteratorId);
|
|
992
|
+
if (!iterator) {
|
|
993
|
+
throw new Error(`Iterator ${iteratorId} not found`);
|
|
994
|
+
}
|
|
995
|
+
const result2 = await iterator.next();
|
|
996
|
+
if (result2.done) {
|
|
997
|
+
instance.returnedIterators.delete(iteratorId);
|
|
998
|
+
}
|
|
999
|
+
const ctx = createMarshalContext();
|
|
1000
|
+
const marshalledValue = await import_isolate_protocol.marshalValue(result2.value, ctx);
|
|
1001
|
+
return {
|
|
1002
|
+
done: result2.done,
|
|
1003
|
+
value: addCallbackIdsToRefs(marshalledValue)
|
|
1004
|
+
};
|
|
1005
|
+
});
|
|
1006
|
+
const returnCallbackId = instance.nextLocalCallbackId++;
|
|
1007
|
+
instance.returnedCallbacks.set(returnCallbackId, async (iteratorId, returnValue) => {
|
|
1008
|
+
const iterator = instance.returnedIterators.get(iteratorId);
|
|
1009
|
+
instance.returnedIterators.delete(iteratorId);
|
|
1010
|
+
if (!iterator || !iterator.return) {
|
|
1011
|
+
return { done: true, value: undefined };
|
|
1012
|
+
}
|
|
1013
|
+
const result2 = await iterator.return(returnValue);
|
|
1014
|
+
const ctx = createMarshalContext();
|
|
1015
|
+
const marshalledValue = await import_isolate_protocol.marshalValue(result2.value, ctx);
|
|
1016
|
+
return {
|
|
1017
|
+
done: true,
|
|
1018
|
+
value: addCallbackIdsToRefs(marshalledValue)
|
|
1019
|
+
};
|
|
1020
|
+
});
|
|
1021
|
+
return {
|
|
1022
|
+
...value,
|
|
1023
|
+
__nextCallbackId: nextCallbackId,
|
|
1024
|
+
__returnCallbackId: returnCallbackId
|
|
1025
|
+
};
|
|
1026
|
+
}
|
|
1027
|
+
if (Array.isArray(value)) {
|
|
1028
|
+
return value.map((item) => addCallbackIdsToRefs(item));
|
|
1029
|
+
}
|
|
1030
|
+
const result = {};
|
|
1031
|
+
for (const key of Object.keys(value)) {
|
|
1032
|
+
result[key] = addCallbackIdsToRefs(value[key]);
|
|
1033
|
+
}
|
|
1034
|
+
return result;
|
|
1035
|
+
}
|
|
1036
|
+
const invokeCallbackRef = new import_isolated_vm.default.Reference(async (callbackId, argsJson) => {
|
|
1037
|
+
const marshalledArgs = JSON.parse(argsJson);
|
|
1038
|
+
const args = import_isolate_protocol.unmarshalValue(marshalledArgs);
|
|
1039
|
+
try {
|
|
1040
|
+
let result;
|
|
1041
|
+
if (isLocalCallbackId(callbackId)) {
|
|
1042
|
+
const callback = instance.returnedCallbacks.get(callbackId);
|
|
1043
|
+
if (!callback) {
|
|
1044
|
+
throw new Error(`Local callback ${callbackId} not found`);
|
|
1045
|
+
}
|
|
1046
|
+
result = await callback(...args);
|
|
1047
|
+
} else {
|
|
1048
|
+
result = await invokeClientCallback(connection, callbackId, args);
|
|
1049
|
+
}
|
|
1050
|
+
const ctx = createMarshalContext();
|
|
1051
|
+
const marshalledResult = await import_isolate_protocol.marshalValue({ ok: true, value: result }, ctx);
|
|
1052
|
+
const processedResult = addCallbackIdsToRefs(marshalledResult);
|
|
1053
|
+
return JSON.stringify(processedResult);
|
|
1054
|
+
} catch (error) {
|
|
1055
|
+
const err = error;
|
|
1056
|
+
return JSON.stringify({
|
|
1057
|
+
ok: false,
|
|
1058
|
+
error: { message: err.message, name: err.name }
|
|
1059
|
+
});
|
|
1060
|
+
}
|
|
1061
|
+
});
|
|
1062
|
+
global.setSync("__customFn_invoke", invokeCallbackRef);
|
|
1063
|
+
context.evalSync(ISOLATE_MARSHAL_CODE);
|
|
1064
|
+
for (const [name, registration] of Object.entries(customCallbacks)) {
|
|
1065
|
+
if (name.includes(":")) {
|
|
1066
|
+
continue;
|
|
1067
|
+
}
|
|
1068
|
+
if (registration.type === "sync") {
|
|
1069
|
+
context.evalSync(`
|
|
1070
|
+
globalThis.${name} = function(...args) {
|
|
1071
|
+
const argsJson = JSON.stringify(__marshalForHost(args));
|
|
1072
|
+
const resultJson = __customFn_invoke.applySyncPromise(
|
|
1073
|
+
undefined,
|
|
1074
|
+
[${registration.callbackId}, argsJson]
|
|
1075
|
+
);
|
|
1076
|
+
const result = JSON.parse(resultJson);
|
|
1077
|
+
if (result.ok) {
|
|
1078
|
+
return __unmarshalFromHost(result.value);
|
|
1079
|
+
} else {
|
|
1080
|
+
const error = new Error(result.error.message);
|
|
1081
|
+
error.name = result.error.name;
|
|
1082
|
+
throw error;
|
|
1083
|
+
}
|
|
1084
|
+
};
|
|
1085
|
+
`);
|
|
1086
|
+
} else if (registration.type === "asyncIterator") {
|
|
1087
|
+
const startReg = customCallbacks[`${name}:start`];
|
|
1088
|
+
const nextReg = customCallbacks[`${name}:next`];
|
|
1089
|
+
const returnReg = customCallbacks[`${name}:return`];
|
|
1090
|
+
const throwReg = customCallbacks[`${name}:throw`];
|
|
1091
|
+
if (!startReg || !nextReg || !returnReg || !throwReg) {
|
|
1092
|
+
throw new Error(`Missing companion callbacks for asyncIterator function "${name}"`);
|
|
1093
|
+
}
|
|
1094
|
+
context.evalSync(`
|
|
1095
|
+
globalThis.${name} = function(...args) {
|
|
1096
|
+
// Start the iterator and get the iteratorId
|
|
1097
|
+
const argsJson = JSON.stringify(__marshalForHost(args));
|
|
1098
|
+
const startResultJson = __customFn_invoke.applySyncPromise(
|
|
1099
|
+
undefined,
|
|
1100
|
+
[${startReg.callbackId}, argsJson]
|
|
1101
|
+
);
|
|
1102
|
+
const startResult = JSON.parse(startResultJson);
|
|
1103
|
+
if (!startResult.ok) {
|
|
1104
|
+
const error = new Error(startResult.error.message);
|
|
1105
|
+
error.name = startResult.error.name;
|
|
1106
|
+
throw error;
|
|
1107
|
+
}
|
|
1108
|
+
const iteratorId = __unmarshalFromHost(startResult.value).iteratorId;
|
|
1109
|
+
|
|
1110
|
+
return {
|
|
1111
|
+
[Symbol.asyncIterator]() { return this; },
|
|
1112
|
+
async next() {
|
|
1113
|
+
const argsJson = JSON.stringify(__marshalForHost([iteratorId]));
|
|
1114
|
+
const resultJson = __customFn_invoke.applySyncPromise(
|
|
1115
|
+
undefined,
|
|
1116
|
+
[${nextReg.callbackId}, argsJson]
|
|
1117
|
+
);
|
|
1118
|
+
const result = JSON.parse(resultJson);
|
|
1119
|
+
if (!result.ok) {
|
|
1120
|
+
const error = new Error(result.error.message);
|
|
1121
|
+
error.name = result.error.name;
|
|
1122
|
+
throw error;
|
|
1123
|
+
}
|
|
1124
|
+
const val = __unmarshalFromHost(result.value);
|
|
1125
|
+
return { done: val.done, value: val.value };
|
|
1126
|
+
},
|
|
1127
|
+
async return(v) {
|
|
1128
|
+
const argsJson = JSON.stringify(__marshalForHost([iteratorId, v]));
|
|
1129
|
+
const resultJson = __customFn_invoke.applySyncPromise(
|
|
1130
|
+
undefined,
|
|
1131
|
+
[${returnReg.callbackId}, argsJson]
|
|
1132
|
+
);
|
|
1133
|
+
const result = JSON.parse(resultJson);
|
|
1134
|
+
return { done: true, value: result.ok ? __unmarshalFromHost(result.value) : undefined };
|
|
1135
|
+
},
|
|
1136
|
+
async throw(e) {
|
|
1137
|
+
const argsJson = JSON.stringify(__marshalForHost([iteratorId, { message: e?.message, name: e?.name }]));
|
|
1138
|
+
const resultJson = __customFn_invoke.applySyncPromise(
|
|
1139
|
+
undefined,
|
|
1140
|
+
[${throwReg.callbackId}, argsJson]
|
|
1141
|
+
);
|
|
1142
|
+
const result = JSON.parse(resultJson);
|
|
1143
|
+
if (!result.ok) {
|
|
1144
|
+
const error = new Error(result.error.message);
|
|
1145
|
+
error.name = result.error.name;
|
|
1146
|
+
throw error;
|
|
1147
|
+
}
|
|
1148
|
+
const val = __unmarshalFromHost(result.value);
|
|
1149
|
+
return { done: val.done, value: val.value };
|
|
1150
|
+
}
|
|
1151
|
+
};
|
|
1152
|
+
};
|
|
1153
|
+
`);
|
|
1154
|
+
} else if (registration.type === "async") {
|
|
1155
|
+
context.evalSync(`
|
|
1156
|
+
globalThis.${name} = async function(...args) {
|
|
1157
|
+
const argsJson = JSON.stringify(__marshalForHost(args));
|
|
1158
|
+
const resultJson = __customFn_invoke.applySyncPromise(
|
|
1159
|
+
undefined,
|
|
1160
|
+
[${registration.callbackId}, argsJson]
|
|
1161
|
+
);
|
|
1162
|
+
const result = JSON.parse(resultJson);
|
|
1163
|
+
if (result.ok) {
|
|
1164
|
+
return __unmarshalFromHost(result.value);
|
|
1165
|
+
} else {
|
|
1166
|
+
const error = new Error(result.error.message);
|
|
1167
|
+
error.name = result.error.name;
|
|
1168
|
+
throw error;
|
|
1169
|
+
}
|
|
1170
|
+
};
|
|
1171
|
+
`);
|
|
1172
|
+
}
|
|
1173
|
+
}
|
|
1174
|
+
}
|
|
1175
|
+
function createModuleResolver(instance, connection) {
|
|
1176
|
+
return async (specifier, _referrer) => {
|
|
1177
|
+
const cached = instance.moduleCache?.get(specifier);
|
|
1178
|
+
if (cached)
|
|
1179
|
+
return cached;
|
|
1180
|
+
if (!instance.moduleLoaderCallbackId) {
|
|
1181
|
+
throw new Error(`Module not found: ${specifier}`);
|
|
1182
|
+
}
|
|
1183
|
+
const code = await invokeClientCallback(connection, instance.moduleLoaderCallbackId, [specifier]);
|
|
1184
|
+
const mod = await instance.runtime.isolate.compileModule(code, {
|
|
1185
|
+
filename: specifier
|
|
1186
|
+
});
|
|
1187
|
+
const resolver = createModuleResolver(instance, connection);
|
|
1188
|
+
await mod.instantiate(instance.runtime.context, resolver);
|
|
1189
|
+
instance.moduleCache?.set(specifier, mod);
|
|
1190
|
+
return mod;
|
|
1191
|
+
};
|
|
1192
|
+
}
|
|
366
1193
|
async function serializeRequest(request) {
|
|
367
1194
|
const headers = [];
|
|
368
1195
|
request.headers.forEach((value, key) => {
|
|
@@ -402,19 +1229,144 @@ function deserializeResponse(data) {
|
|
|
402
1229
|
headers: data.headers
|
|
403
1230
|
});
|
|
404
1231
|
}
|
|
405
|
-
|
|
406
|
-
const
|
|
407
|
-
if (!
|
|
408
|
-
|
|
1232
|
+
function handleStreamPush(message, connection) {
|
|
1233
|
+
const receiver = connection.streamReceivers.get(message.streamId);
|
|
1234
|
+
if (!receiver) {
|
|
1235
|
+
sendMessage(connection.socket, {
|
|
1236
|
+
type: import_isolate_protocol.MessageType.STREAM_ERROR,
|
|
1237
|
+
streamId: message.streamId,
|
|
1238
|
+
error: "Stream not found"
|
|
1239
|
+
});
|
|
409
1240
|
return;
|
|
410
1241
|
}
|
|
411
|
-
|
|
1242
|
+
receiver.chunks.push(message.chunk);
|
|
1243
|
+
receiver.totalBytes += message.chunk.length;
|
|
1244
|
+
sendMessage(connection.socket, {
|
|
1245
|
+
type: import_isolate_protocol.MessageType.STREAM_PULL,
|
|
1246
|
+
streamId: message.streamId,
|
|
1247
|
+
maxBytes: import_isolate_protocol.STREAM_DEFAULT_CREDIT
|
|
1248
|
+
});
|
|
1249
|
+
}
|
|
1250
|
+
function handleStreamPull(message, connection) {
|
|
1251
|
+
const session = connection.activeStreams.get(message.streamId);
|
|
1252
|
+
if (!session) {
|
|
1253
|
+
return;
|
|
1254
|
+
}
|
|
1255
|
+
session.credit += message.maxBytes;
|
|
1256
|
+
if (session.creditResolver) {
|
|
1257
|
+
session.creditResolver();
|
|
1258
|
+
session.creditResolver = undefined;
|
|
1259
|
+
}
|
|
1260
|
+
}
|
|
1261
|
+
function handleStreamClose(message, connection) {
|
|
1262
|
+
const receiver = connection.streamReceivers.get(message.streamId);
|
|
1263
|
+
if (!receiver) {
|
|
1264
|
+
return;
|
|
1265
|
+
}
|
|
1266
|
+
const totalLength = receiver.chunks.reduce((sum, chunk) => sum + chunk.length, 0);
|
|
1267
|
+
const body = new Uint8Array(totalLength);
|
|
1268
|
+
let offset = 0;
|
|
1269
|
+
for (const chunk of receiver.chunks) {
|
|
1270
|
+
body.set(chunk, offset);
|
|
1271
|
+
offset += chunk.length;
|
|
1272
|
+
}
|
|
1273
|
+
receiver.resolve(body);
|
|
1274
|
+
connection.streamReceivers.delete(message.streamId);
|
|
1275
|
+
}
|
|
1276
|
+
function handleStreamError(message, connection) {
|
|
1277
|
+
const receiver = connection.streamReceivers.get(message.streamId);
|
|
1278
|
+
if (receiver) {
|
|
1279
|
+
receiver.reject(new Error(message.error));
|
|
1280
|
+
connection.streamReceivers.delete(message.streamId);
|
|
1281
|
+
}
|
|
1282
|
+
const session = connection.activeStreams.get(message.streamId);
|
|
1283
|
+
if (session) {
|
|
1284
|
+
session.state = "closed";
|
|
1285
|
+
connection.activeStreams.delete(message.streamId);
|
|
1286
|
+
}
|
|
1287
|
+
}
|
|
1288
|
+
function waitForCredit(session) {
|
|
1289
|
+
return new Promise((resolve) => {
|
|
1290
|
+
session.creditResolver = resolve;
|
|
1291
|
+
});
|
|
1292
|
+
}
|
|
1293
|
+
async function sendStreamedResponse(connection, requestId, response) {
|
|
1294
|
+
const streamId = connection.nextStreamId++;
|
|
1295
|
+
const headers = [];
|
|
1296
|
+
response.headers.forEach((value, key) => {
|
|
1297
|
+
headers.push([key, value]);
|
|
1298
|
+
});
|
|
1299
|
+
const startMsg = {
|
|
1300
|
+
type: import_isolate_protocol.MessageType.RESPONSE_STREAM_START,
|
|
1301
|
+
requestId,
|
|
1302
|
+
streamId,
|
|
1303
|
+
metadata: {
|
|
1304
|
+
status: response.status,
|
|
1305
|
+
statusText: response.statusText,
|
|
1306
|
+
headers
|
|
1307
|
+
}
|
|
1308
|
+
};
|
|
1309
|
+
sendMessage(connection.socket, startMsg);
|
|
1310
|
+
if (!response.body) {
|
|
1311
|
+
const endMsg = {
|
|
1312
|
+
type: import_isolate_protocol.MessageType.RESPONSE_STREAM_END,
|
|
1313
|
+
requestId,
|
|
1314
|
+
streamId
|
|
1315
|
+
};
|
|
1316
|
+
sendMessage(connection.socket, endMsg);
|
|
1317
|
+
return;
|
|
1318
|
+
}
|
|
1319
|
+
const session = {
|
|
1320
|
+
streamId,
|
|
1321
|
+
direction: "download",
|
|
1322
|
+
requestId,
|
|
1323
|
+
state: "active",
|
|
1324
|
+
bytesTransferred: 0,
|
|
1325
|
+
credit: import_isolate_protocol.STREAM_DEFAULT_CREDIT
|
|
1326
|
+
};
|
|
1327
|
+
connection.activeStreams.set(streamId, session);
|
|
1328
|
+
const reader = response.body.getReader();
|
|
412
1329
|
try {
|
|
413
|
-
|
|
414
|
-
|
|
1330
|
+
while (true) {
|
|
1331
|
+
while (session.credit < import_isolate_protocol.STREAM_CHUNK_SIZE && session.state === "active") {
|
|
1332
|
+
await waitForCredit(session);
|
|
1333
|
+
}
|
|
1334
|
+
if (session.state !== "active") {
|
|
1335
|
+
throw new Error("Stream cancelled");
|
|
1336
|
+
}
|
|
1337
|
+
const { done, value } = await reader.read();
|
|
1338
|
+
if (done) {
|
|
1339
|
+
const endMsg = {
|
|
1340
|
+
type: import_isolate_protocol.MessageType.RESPONSE_STREAM_END,
|
|
1341
|
+
requestId,
|
|
1342
|
+
streamId
|
|
1343
|
+
};
|
|
1344
|
+
sendMessage(connection.socket, endMsg);
|
|
1345
|
+
break;
|
|
1346
|
+
}
|
|
1347
|
+
for (let offset = 0;offset < value.length; offset += import_isolate_protocol.STREAM_CHUNK_SIZE) {
|
|
1348
|
+
const chunk = value.slice(offset, offset + import_isolate_protocol.STREAM_CHUNK_SIZE);
|
|
1349
|
+
const chunkMsg = {
|
|
1350
|
+
type: import_isolate_protocol.MessageType.RESPONSE_STREAM_CHUNK,
|
|
1351
|
+
requestId,
|
|
1352
|
+
streamId,
|
|
1353
|
+
chunk
|
|
1354
|
+
};
|
|
1355
|
+
sendMessage(connection.socket, chunkMsg);
|
|
1356
|
+
session.credit -= chunk.length;
|
|
1357
|
+
session.bytesTransferred += chunk.length;
|
|
1358
|
+
}
|
|
1359
|
+
}
|
|
415
1360
|
} catch (err) {
|
|
416
|
-
const
|
|
417
|
-
|
|
1361
|
+
const errorMsg = {
|
|
1362
|
+
type: import_isolate_protocol.MessageType.STREAM_ERROR,
|
|
1363
|
+
streamId,
|
|
1364
|
+
error: err.message
|
|
1365
|
+
};
|
|
1366
|
+
sendMessage(connection.socket, errorMsg);
|
|
1367
|
+
} finally {
|
|
1368
|
+
reader.releaseLock();
|
|
1369
|
+
connection.activeStreams.delete(streamId);
|
|
418
1370
|
}
|
|
419
1371
|
}
|
|
420
1372
|
async function handleRunTests(message, connection, state) {
|
|
@@ -423,6 +1375,10 @@ async function handleRunTests(message, connection, state) {
|
|
|
423
1375
|
sendError(connection.socket, message.requestId, import_isolate_protocol.ErrorCode.ISOLATE_NOT_FOUND, `Isolate not found: ${message.isolateId}`);
|
|
424
1376
|
return;
|
|
425
1377
|
}
|
|
1378
|
+
if (!instance.testEnvironmentEnabled) {
|
|
1379
|
+
sendError(connection.socket, message.requestId, import_isolate_protocol.ErrorCode.SCRIPT_ERROR, "Test environment not enabled. Set testEnvironment: true in createRuntime options.");
|
|
1380
|
+
return;
|
|
1381
|
+
}
|
|
426
1382
|
instance.lastActivity = Date.now();
|
|
427
1383
|
try {
|
|
428
1384
|
const timeout = message.timeout ?? 30000;
|
|
@@ -439,119 +1395,69 @@ async function handleRunTests(message, connection, state) {
|
|
|
439
1395
|
sendError(connection.socket, message.requestId, import_isolate_protocol.ErrorCode.SCRIPT_ERROR, error.message, { name: error.name, stack: error.stack });
|
|
440
1396
|
}
|
|
441
1397
|
}
|
|
442
|
-
async function
|
|
1398
|
+
async function handleResetTestEnv(message, connection, state) {
|
|
443
1399
|
const instance = state.isolates.get(message.isolateId);
|
|
444
1400
|
if (!instance) {
|
|
445
1401
|
sendError(connection.socket, message.requestId, import_isolate_protocol.ErrorCode.ISOLATE_NOT_FOUND, `Isolate not found: ${message.isolateId}`);
|
|
446
1402
|
return;
|
|
447
1403
|
}
|
|
448
|
-
if (instance.
|
|
449
|
-
sendError(connection.socket, message.requestId, import_isolate_protocol.ErrorCode.SCRIPT_ERROR, "
|
|
1404
|
+
if (!instance.testEnvironmentEnabled) {
|
|
1405
|
+
sendError(connection.socket, message.requestId, import_isolate_protocol.ErrorCode.SCRIPT_ERROR, "Test environment not enabled. Set testEnvironment: true in createRuntime options.");
|
|
450
1406
|
return;
|
|
451
1407
|
}
|
|
452
1408
|
instance.lastActivity = Date.now();
|
|
453
1409
|
try {
|
|
454
|
-
|
|
455
|
-
const headless = message.options.headless ?? true;
|
|
456
|
-
let browser;
|
|
457
|
-
switch (browserType) {
|
|
458
|
-
case "firefox":
|
|
459
|
-
browser = await import_playwright.firefox.launch({ headless });
|
|
460
|
-
break;
|
|
461
|
-
case "webkit":
|
|
462
|
-
browser = await import_playwright.webkit.launch({ headless });
|
|
463
|
-
break;
|
|
464
|
-
default:
|
|
465
|
-
browser = await import_playwright.chromium.launch({ headless });
|
|
466
|
-
}
|
|
467
|
-
const browserContext = await browser.newContext();
|
|
468
|
-
const page = await browserContext.newPage();
|
|
469
|
-
const playwrightHandle = await import_isolate_playwright.setupPlaywright(instance.runtime.context, {
|
|
470
|
-
page,
|
|
471
|
-
baseUrl: message.options.baseURL,
|
|
472
|
-
onConsoleLog: (level, ...args) => {
|
|
473
|
-
const event = {
|
|
474
|
-
type: import_isolate_protocol.MessageType.PLAYWRIGHT_EVENT,
|
|
475
|
-
isolateId: message.isolateId,
|
|
476
|
-
eventType: "consoleLog",
|
|
477
|
-
payload: { level, args }
|
|
478
|
-
};
|
|
479
|
-
sendMessage(connection.socket, event);
|
|
480
|
-
},
|
|
481
|
-
onNetworkRequest: (info) => {
|
|
482
|
-
const event = {
|
|
483
|
-
type: import_isolate_protocol.MessageType.PLAYWRIGHT_EVENT,
|
|
484
|
-
isolateId: message.isolateId,
|
|
485
|
-
eventType: "networkRequest",
|
|
486
|
-
payload: info
|
|
487
|
-
};
|
|
488
|
-
sendMessage(connection.socket, event);
|
|
489
|
-
},
|
|
490
|
-
onNetworkResponse: (info) => {
|
|
491
|
-
const event = {
|
|
492
|
-
type: import_isolate_protocol.MessageType.PLAYWRIGHT_EVENT,
|
|
493
|
-
isolateId: message.isolateId,
|
|
494
|
-
eventType: "networkResponse",
|
|
495
|
-
payload: info
|
|
496
|
-
};
|
|
497
|
-
sendMessage(connection.socket, event);
|
|
498
|
-
}
|
|
499
|
-
});
|
|
500
|
-
instance.browser = browser;
|
|
501
|
-
instance.browserContext = browserContext;
|
|
502
|
-
instance.page = page;
|
|
503
|
-
instance.playwrightHandle = playwrightHandle;
|
|
1410
|
+
await instance.runtime.context.eval("__resetTestEnvironment()", { promise: true });
|
|
504
1411
|
sendOk(connection.socket, message.requestId);
|
|
505
1412
|
} catch (err) {
|
|
506
1413
|
const error = err;
|
|
507
1414
|
sendError(connection.socket, message.requestId, import_isolate_protocol.ErrorCode.SCRIPT_ERROR, error.message, { name: error.name, stack: error.stack });
|
|
508
1415
|
}
|
|
509
1416
|
}
|
|
510
|
-
async function
|
|
1417
|
+
async function handleHasTests(message, connection, state) {
|
|
511
1418
|
const instance = state.isolates.get(message.isolateId);
|
|
512
1419
|
if (!instance) {
|
|
513
1420
|
sendError(connection.socket, message.requestId, import_isolate_protocol.ErrorCode.ISOLATE_NOT_FOUND, `Isolate not found: ${message.isolateId}`);
|
|
514
1421
|
return;
|
|
515
1422
|
}
|
|
516
|
-
if (!instance.
|
|
517
|
-
sendError(connection.socket, message.requestId, import_isolate_protocol.ErrorCode.SCRIPT_ERROR, "
|
|
1423
|
+
if (!instance.testEnvironmentEnabled) {
|
|
1424
|
+
sendError(connection.socket, message.requestId, import_isolate_protocol.ErrorCode.SCRIPT_ERROR, "Test environment not enabled. Set testEnvironment: true in createRuntime options.");
|
|
518
1425
|
return;
|
|
519
1426
|
}
|
|
520
1427
|
instance.lastActivity = Date.now();
|
|
521
1428
|
try {
|
|
522
|
-
const
|
|
523
|
-
|
|
524
|
-
setTimeout(() => reject(new Error("Playwright test timeout")), timeout);
|
|
525
|
-
});
|
|
526
|
-
const results = await Promise.race([
|
|
527
|
-
import_isolate_playwright.runPlaywrightTests(instance.runtime.context),
|
|
528
|
-
timeoutPromise
|
|
529
|
-
]);
|
|
530
|
-
sendOk(connection.socket, message.requestId, results);
|
|
1429
|
+
const result = import_isolate_test_environment.hasTests(instance.runtime.context);
|
|
1430
|
+
sendOk(connection.socket, message.requestId, result);
|
|
531
1431
|
} catch (err) {
|
|
532
1432
|
const error = err;
|
|
533
1433
|
sendError(connection.socket, message.requestId, import_isolate_protocol.ErrorCode.SCRIPT_ERROR, error.message, { name: error.name, stack: error.stack });
|
|
534
1434
|
}
|
|
535
1435
|
}
|
|
536
|
-
async function
|
|
1436
|
+
async function handleGetTestCount(message, connection, state) {
|
|
537
1437
|
const instance = state.isolates.get(message.isolateId);
|
|
538
1438
|
if (!instance) {
|
|
539
1439
|
sendError(connection.socket, message.requestId, import_isolate_protocol.ErrorCode.ISOLATE_NOT_FOUND, `Isolate not found: ${message.isolateId}`);
|
|
540
1440
|
return;
|
|
541
1441
|
}
|
|
542
|
-
if (!instance.
|
|
543
|
-
sendError(connection.socket, message.requestId, import_isolate_protocol.ErrorCode.SCRIPT_ERROR, "
|
|
1442
|
+
if (!instance.testEnvironmentEnabled) {
|
|
1443
|
+
sendError(connection.socket, message.requestId, import_isolate_protocol.ErrorCode.SCRIPT_ERROR, "Test environment not enabled. Set testEnvironment: true in createRuntime options.");
|
|
544
1444
|
return;
|
|
545
1445
|
}
|
|
546
1446
|
instance.lastActivity = Date.now();
|
|
547
1447
|
try {
|
|
548
|
-
|
|
549
|
-
sendOk(connection.socket, message.requestId);
|
|
1448
|
+
const result = import_isolate_test_environment.getTestCount(instance.runtime.context);
|
|
1449
|
+
sendOk(connection.socket, message.requestId, result);
|
|
550
1450
|
} catch (err) {
|
|
551
1451
|
const error = err;
|
|
552
1452
|
sendError(connection.socket, message.requestId, import_isolate_protocol.ErrorCode.SCRIPT_ERROR, error.message, { name: error.name, stack: error.stack });
|
|
553
1453
|
}
|
|
554
1454
|
}
|
|
1455
|
+
async function handleRunPlaywrightTests(message, connection, _state) {
|
|
1456
|
+
sendError(connection.socket, message.requestId, import_isolate_protocol.ErrorCode.SCRIPT_ERROR, "playwright.runTests() has been removed. Use testEnvironment.runTests() instead.");
|
|
1457
|
+
}
|
|
1458
|
+
async function handleResetPlaywrightTests(message, connection, _state) {
|
|
1459
|
+
sendError(connection.socket, message.requestId, import_isolate_protocol.ErrorCode.SCRIPT_ERROR, "playwright.reset() has been removed. Use testEnvironment.reset() instead.");
|
|
1460
|
+
}
|
|
555
1461
|
async function handleGetCollectedData(message, connection, state) {
|
|
556
1462
|
const instance = state.isolates.get(message.isolateId);
|
|
557
1463
|
if (!instance) {
|
|
@@ -559,13 +1465,13 @@ async function handleGetCollectedData(message, connection, state) {
|
|
|
559
1465
|
return;
|
|
560
1466
|
}
|
|
561
1467
|
if (!instance.playwrightHandle) {
|
|
562
|
-
sendError(connection.socket, message.requestId, import_isolate_protocol.ErrorCode.SCRIPT_ERROR, "Playwright not
|
|
1468
|
+
sendError(connection.socket, message.requestId, import_isolate_protocol.ErrorCode.SCRIPT_ERROR, "Playwright not configured. Provide playwright.page in createRuntime options.");
|
|
563
1469
|
return;
|
|
564
1470
|
}
|
|
565
1471
|
instance.lastActivity = Date.now();
|
|
566
1472
|
try {
|
|
567
1473
|
const data = {
|
|
568
|
-
|
|
1474
|
+
browserConsoleLogs: instance.playwrightHandle.getBrowserConsoleLogs(),
|
|
569
1475
|
networkRequests: instance.playwrightHandle.getNetworkRequests(),
|
|
570
1476
|
networkResponses: instance.playwrightHandle.getNetworkResponses()
|
|
571
1477
|
};
|
|
@@ -575,6 +1481,25 @@ async function handleGetCollectedData(message, connection, state) {
|
|
|
575
1481
|
sendError(connection.socket, message.requestId, import_isolate_protocol.ErrorCode.SCRIPT_ERROR, error.message, { name: error.name, stack: error.stack });
|
|
576
1482
|
}
|
|
577
1483
|
}
|
|
1484
|
+
async function handleClearCollectedData(message, connection, state) {
|
|
1485
|
+
const instance = state.isolates.get(message.isolateId);
|
|
1486
|
+
if (!instance) {
|
|
1487
|
+
sendError(connection.socket, message.requestId, import_isolate_protocol.ErrorCode.ISOLATE_NOT_FOUND, `Isolate not found: ${message.isolateId}`);
|
|
1488
|
+
return;
|
|
1489
|
+
}
|
|
1490
|
+
if (!instance.playwrightHandle) {
|
|
1491
|
+
sendError(connection.socket, message.requestId, import_isolate_protocol.ErrorCode.SCRIPT_ERROR, "Playwright not configured. Provide playwright.page in createRuntime options.");
|
|
1492
|
+
return;
|
|
1493
|
+
}
|
|
1494
|
+
instance.lastActivity = Date.now();
|
|
1495
|
+
try {
|
|
1496
|
+
instance.playwrightHandle.clearCollected();
|
|
1497
|
+
sendOk(connection.socket, message.requestId);
|
|
1498
|
+
} catch (err) {
|
|
1499
|
+
const error = err;
|
|
1500
|
+
sendError(connection.socket, message.requestId, import_isolate_protocol.ErrorCode.SCRIPT_ERROR, error.message, { name: error.name, stack: error.stack });
|
|
1501
|
+
}
|
|
1502
|
+
}
|
|
578
1503
|
})
|
|
579
1504
|
|
|
580
|
-
//# debugId=
|
|
1505
|
+
//# debugId=A7BAC1A3990C35CA64756E2164756E21
|