@ricsam/isolate-daemon 0.1.14 → 0.1.16
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 +1 -0
- package/dist/cjs/callback-fs-handler.cjs +34 -43
- package/dist/cjs/callback-fs-handler.cjs.map +3 -3
- package/dist/cjs/connection.cjs +264 -162
- package/dist/cjs/connection.cjs.map +3 -3
- package/dist/cjs/index.cjs +3 -2
- package/dist/cjs/index.cjs.map +3 -3
- package/dist/cjs/package.json +1 -1
- package/dist/mjs/callback-fs-handler.mjs +34 -43
- package/dist/mjs/callback-fs-handler.mjs.map +3 -3
- package/dist/mjs/connection.mjs +267 -160
- package/dist/mjs/connection.mjs.map +3 -3
- package/dist/mjs/index.mjs +3 -2
- package/dist/mjs/index.mjs.map +3 -3
- package/dist/mjs/package.json +1 -1
- package/dist/types/types.d.ts +15 -1
- package/package.json +1 -1
package/dist/cjs/connection.cjs
CHANGED
|
@@ -36,6 +36,28 @@ var import_node_crypto = require("node:crypto");
|
|
|
36
36
|
var import_isolate_protocol = require("@ricsam/isolate-protocol");
|
|
37
37
|
var import_callback_fs_handler = require("./callback-fs-handler.cjs");
|
|
38
38
|
var import_isolate_runtime = require("@ricsam/isolate-runtime");
|
|
39
|
+
var LINKER_CONFLICT_ERROR = "Module is currently being linked by another linker";
|
|
40
|
+
function getErrorText(error) {
|
|
41
|
+
if (error instanceof Error) {
|
|
42
|
+
const cause = error.cause;
|
|
43
|
+
const causeText = cause instanceof Error ? `${cause.name}: ${cause.message}
|
|
44
|
+
${cause.stack ?? ""}` : cause != null ? String(cause) : "";
|
|
45
|
+
return [error.name, error.message, error.stack, causeText].filter((part) => part != null && part !== "").join(`
|
|
46
|
+
`);
|
|
47
|
+
}
|
|
48
|
+
if (typeof error === "string") {
|
|
49
|
+
return error;
|
|
50
|
+
}
|
|
51
|
+
try {
|
|
52
|
+
return JSON.stringify(error);
|
|
53
|
+
} catch {
|
|
54
|
+
return String(error ?? "");
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
function isLinkerConflictError(error) {
|
|
58
|
+
const text = getErrorText(error).toLowerCase();
|
|
59
|
+
return text.includes(LINKER_CONFLICT_ERROR.toLowerCase());
|
|
60
|
+
}
|
|
39
61
|
function handleConnection(socket, state) {
|
|
40
62
|
const connection = {
|
|
41
63
|
socket,
|
|
@@ -68,17 +90,17 @@ function handleConnection(socket, state) {
|
|
|
68
90
|
const instance = state.isolates.get(isolateId);
|
|
69
91
|
if (instance) {
|
|
70
92
|
if (instance.namespaceId != null && !instance.isDisposed) {
|
|
71
|
-
|
|
93
|
+
if (instance.isPoisoned) {
|
|
94
|
+
hardDeleteRuntime(instance, state).catch(() => {});
|
|
95
|
+
} else {
|
|
96
|
+
softDeleteRuntime(instance, state);
|
|
97
|
+
}
|
|
72
98
|
} else if (!instance.isDisposed) {
|
|
73
|
-
instance
|
|
74
|
-
state.isolates.delete(isolateId);
|
|
99
|
+
hardDeleteRuntime(instance, state).catch(() => {});
|
|
75
100
|
}
|
|
76
101
|
}
|
|
77
102
|
}
|
|
78
103
|
for (const [, pending] of connection.pendingCallbacks) {
|
|
79
|
-
if (pending.timeoutId) {
|
|
80
|
-
clearTimeout(pending.timeoutId);
|
|
81
|
-
}
|
|
82
104
|
pending.reject(new Error("Connection closed"));
|
|
83
105
|
}
|
|
84
106
|
connection.pendingCallbacks.clear();
|
|
@@ -206,28 +228,64 @@ async function handleMessage(message, connection, state) {
|
|
|
206
228
|
case import_isolate_protocol.MessageType.CALLBACK_STREAM_END:
|
|
207
229
|
handleCallbackStreamEnd(message, connection);
|
|
208
230
|
break;
|
|
209
|
-
case import_isolate_protocol.MessageType.
|
|
210
|
-
|
|
211
|
-
break;
|
|
212
|
-
case import_isolate_protocol.MessageType.CLIENT_WS_MESSAGE:
|
|
213
|
-
handleClientWsMessage(message, connection, state);
|
|
214
|
-
break;
|
|
215
|
-
case import_isolate_protocol.MessageType.CLIENT_WS_CLOSED:
|
|
216
|
-
handleClientWsClosed(message, connection, state);
|
|
217
|
-
break;
|
|
218
|
-
case import_isolate_protocol.MessageType.CLIENT_WS_ERROR:
|
|
219
|
-
handleClientWsError(message, connection, state);
|
|
231
|
+
case import_isolate_protocol.MessageType.CLIENT_EVENT:
|
|
232
|
+
handleClientEvent(message, connection, state);
|
|
220
233
|
break;
|
|
221
234
|
default:
|
|
222
235
|
sendError(connection.socket, message.requestId ?? 0, import_isolate_protocol.ErrorCode.UNKNOWN_MESSAGE_TYPE, `Unknown message type: ${message.type}`);
|
|
223
236
|
}
|
|
224
237
|
}
|
|
238
|
+
async function hardDeleteRuntime(instance, state) {
|
|
239
|
+
try {
|
|
240
|
+
await instance.runtime.dispose();
|
|
241
|
+
} finally {
|
|
242
|
+
state.isolates.delete(instance.isolateId);
|
|
243
|
+
if (instance.namespaceId != null) {
|
|
244
|
+
const indexed = state.namespacedRuntimes.get(instance.namespaceId);
|
|
245
|
+
if (indexed?.isolateId === instance.isolateId) {
|
|
246
|
+
state.namespacedRuntimes.delete(instance.namespaceId);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
instance.isDisposed = true;
|
|
250
|
+
instance.disposedAt = undefined;
|
|
251
|
+
instance.ownerConnection = null;
|
|
252
|
+
if (instance.callbackContext) {
|
|
253
|
+
if (instance.callbackContext.reconnectionPromise) {
|
|
254
|
+
clearTimeout(instance.callbackContext.reconnectionPromise.timeoutId);
|
|
255
|
+
instance.callbackContext.reconnectionPromise.promise.catch(() => {});
|
|
256
|
+
instance.callbackContext.reconnectionPromise.reject(new Error("Runtime was permanently disposed"));
|
|
257
|
+
instance.callbackContext.reconnectionPromise = undefined;
|
|
258
|
+
}
|
|
259
|
+
instance.callbackContext.connection = null;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
var RECONNECTION_TIMEOUT_MS = 30000;
|
|
225
264
|
function softDeleteRuntime(instance, state) {
|
|
226
265
|
instance.isDisposed = true;
|
|
227
266
|
instance.disposedAt = Date.now();
|
|
228
267
|
instance.ownerConnection = null;
|
|
229
268
|
if (instance.callbackContext) {
|
|
230
269
|
instance.callbackContext.connection = null;
|
|
270
|
+
let resolve;
|
|
271
|
+
let reject;
|
|
272
|
+
const promise = new Promise((res, rej) => {
|
|
273
|
+
resolve = res;
|
|
274
|
+
reject = rej;
|
|
275
|
+
});
|
|
276
|
+
promise.catch(() => {});
|
|
277
|
+
const timeoutId = setTimeout(() => {
|
|
278
|
+
if (instance.callbackContext?.reconnectionPromise) {
|
|
279
|
+
instance.callbackContext.reconnectionPromise = undefined;
|
|
280
|
+
reject(new Error("Reconnection timeout: no client reconnected within timeout"));
|
|
281
|
+
}
|
|
282
|
+
}, RECONNECTION_TIMEOUT_MS);
|
|
283
|
+
instance.callbackContext.reconnectionPromise = {
|
|
284
|
+
promise,
|
|
285
|
+
resolve,
|
|
286
|
+
reject,
|
|
287
|
+
timeoutId
|
|
288
|
+
};
|
|
231
289
|
}
|
|
232
290
|
instance.callbacks.clear();
|
|
233
291
|
instance.runtime.timers.clearAll();
|
|
@@ -241,6 +299,7 @@ function softDeleteRuntime(instance, state) {
|
|
|
241
299
|
function reuseNamespacedRuntime(instance, connection, message, state) {
|
|
242
300
|
instance.ownerConnection = connection.socket;
|
|
243
301
|
instance.isDisposed = false;
|
|
302
|
+
instance.isPoisoned = false;
|
|
244
303
|
instance.disposedAt = undefined;
|
|
245
304
|
instance.lastActivity = Date.now();
|
|
246
305
|
connection.isolates.add(instance.isolateId);
|
|
@@ -248,6 +307,11 @@ function reuseNamespacedRuntime(instance, connection, message, state) {
|
|
|
248
307
|
const testEnvOptions = message.options.testEnvironment != null && typeof message.options.testEnvironment === "object" ? message.options.testEnvironment : undefined;
|
|
249
308
|
if (instance.callbackContext) {
|
|
250
309
|
instance.callbackContext.connection = connection;
|
|
310
|
+
if (instance.callbackContext.reconnectionPromise) {
|
|
311
|
+
clearTimeout(instance.callbackContext.reconnectionPromise.timeoutId);
|
|
312
|
+
instance.callbackContext.reconnectionPromise.resolve(connection);
|
|
313
|
+
instance.callbackContext.reconnectionPromise = undefined;
|
|
314
|
+
}
|
|
251
315
|
instance.callbackContext.consoleOnEntry = callbacks?.console?.onEntry?.callbackId;
|
|
252
316
|
instance.callbackContext.fetch = callbacks?.fetch?.callbackId;
|
|
253
317
|
instance.callbackContext.moduleLoader = callbacks?.moduleLoader?.callbackId;
|
|
@@ -342,6 +406,35 @@ function reuseNamespacedRuntime(instance, connection, message, state) {
|
|
|
342
406
|
instance.returnedIterators = new Map;
|
|
343
407
|
instance.nextLocalCallbackId = 1e6;
|
|
344
408
|
}
|
|
409
|
+
async function waitForConnection(callbackContext) {
|
|
410
|
+
if (callbackContext.connection) {
|
|
411
|
+
return callbackContext.connection;
|
|
412
|
+
}
|
|
413
|
+
if (callbackContext.reconnectionPromise) {
|
|
414
|
+
return callbackContext.reconnectionPromise.promise;
|
|
415
|
+
}
|
|
416
|
+
throw new Error("No connection available and no reconnection pending");
|
|
417
|
+
}
|
|
418
|
+
async function invokeCallbackWithReconnect(callbackContext, getCallbackId, args, label, invokeClientCallback) {
|
|
419
|
+
const conn = await waitForConnection(callbackContext);
|
|
420
|
+
const cbId = getCallbackId();
|
|
421
|
+
if (cbId === undefined) {
|
|
422
|
+
throw new Error(`${label} callback not available`);
|
|
423
|
+
}
|
|
424
|
+
try {
|
|
425
|
+
return await invokeClientCallback(conn, cbId, args);
|
|
426
|
+
} catch (err) {
|
|
427
|
+
if (callbackContext.reconnectionPromise && !callbackContext.connection) {
|
|
428
|
+
const newConn = await callbackContext.reconnectionPromise.promise;
|
|
429
|
+
const newCbId = getCallbackId();
|
|
430
|
+
if (newCbId === undefined) {
|
|
431
|
+
throw new Error(`${label} callback not available after reconnection`);
|
|
432
|
+
}
|
|
433
|
+
return invokeClientCallback(newConn, newCbId, args);
|
|
434
|
+
}
|
|
435
|
+
throw err;
|
|
436
|
+
}
|
|
437
|
+
}
|
|
345
438
|
async function evictOldestDisposedRuntime(state) {
|
|
346
439
|
let oldest = null;
|
|
347
440
|
let oldestTime = Infinity;
|
|
@@ -355,18 +448,15 @@ async function evictOldestDisposedRuntime(state) {
|
|
|
355
448
|
}
|
|
356
449
|
if (oldest) {
|
|
357
450
|
try {
|
|
358
|
-
await oldest
|
|
451
|
+
await hardDeleteRuntime(oldest, state);
|
|
359
452
|
} catch {}
|
|
360
|
-
state.isolates.delete(oldest.isolateId);
|
|
361
|
-
if (oldest.namespaceId != null) {
|
|
362
|
-
state.namespacedRuntimes.delete(oldest.namespaceId);
|
|
363
|
-
}
|
|
364
453
|
return true;
|
|
365
454
|
}
|
|
366
455
|
return false;
|
|
367
456
|
}
|
|
368
457
|
async function handleCreateRuntime(message, connection, state) {
|
|
369
458
|
const namespaceId = message.options.namespaceId;
|
|
459
|
+
let namespaceCreationLocked = false;
|
|
370
460
|
if (namespaceId != null) {
|
|
371
461
|
const existing = state.namespacedRuntimes.get(namespaceId);
|
|
372
462
|
if (existing) {
|
|
@@ -388,14 +478,20 @@ async function handleCreateRuntime(message, connection, state) {
|
|
|
388
478
|
});
|
|
389
479
|
return;
|
|
390
480
|
}
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
if (!await evictOldestDisposedRuntime(state)) {
|
|
394
|
-
sendError(connection.socket, message.requestId, import_isolate_protocol.ErrorCode.ISOLATE_MEMORY_LIMIT, `Maximum isolates (${state.options.maxIsolates}) reached`);
|
|
481
|
+
if (state.namespacedCreatesInFlight.has(namespaceId)) {
|
|
482
|
+
sendError(connection.socket, message.requestId, import_isolate_protocol.ErrorCode.SCRIPT_ERROR, `Namespace "${namespaceId}" creation already in progress`);
|
|
395
483
|
return;
|
|
396
484
|
}
|
|
485
|
+
state.namespacedCreatesInFlight.add(namespaceId);
|
|
486
|
+
namespaceCreationLocked = true;
|
|
397
487
|
}
|
|
398
488
|
try {
|
|
489
|
+
if (state.isolates.size >= state.options.maxIsolates) {
|
|
490
|
+
if (!await evictOldestDisposedRuntime(state)) {
|
|
491
|
+
sendError(connection.socket, message.requestId, import_isolate_protocol.ErrorCode.ISOLATE_MEMORY_LIMIT, `Maximum isolates (${state.options.maxIsolates}) reached`);
|
|
492
|
+
return;
|
|
493
|
+
}
|
|
494
|
+
}
|
|
399
495
|
const isolateId = import_node_crypto.randomUUID();
|
|
400
496
|
const consoleCallbacks = message.options.callbacks?.console;
|
|
401
497
|
const fetchCallback = message.options.callbacks?.fetch;
|
|
@@ -438,6 +534,7 @@ async function handleCreateRuntime(message, connection, state) {
|
|
|
438
534
|
nextLocalCallbackId: 1e6,
|
|
439
535
|
namespaceId,
|
|
440
536
|
isDisposed: false,
|
|
537
|
+
isPoisoned: false,
|
|
441
538
|
callbackContext
|
|
442
539
|
};
|
|
443
540
|
let bridgedCustomFunctions;
|
|
@@ -460,12 +557,10 @@ async function handleCreateRuntime(message, connection, state) {
|
|
|
460
557
|
return iteratorId;
|
|
461
558
|
}
|
|
462
559
|
});
|
|
463
|
-
const isPromiseRef = (value) => typeof value === "object" && value !== null && value.__type === "PromiseRef";
|
|
464
|
-
const isAsyncIteratorRef = (value) => typeof value === "object" && value !== null && value.__type === "AsyncIteratorRef";
|
|
465
560
|
const addCallbackIdsToRefs = (value) => {
|
|
466
561
|
if (value === null || typeof value !== "object")
|
|
467
562
|
return value;
|
|
468
|
-
if (isPromiseRef(value)) {
|
|
563
|
+
if (import_isolate_protocol.isPromiseRef(value)) {
|
|
469
564
|
if ("__resolveCallbackId" in value)
|
|
470
565
|
return value;
|
|
471
566
|
const resolveCallbackId = instance.nextLocalCallbackId++;
|
|
@@ -481,7 +576,7 @@ async function handleCreateRuntime(message, connection, state) {
|
|
|
481
576
|
});
|
|
482
577
|
return { ...value, __resolveCallbackId: resolveCallbackId };
|
|
483
578
|
}
|
|
484
|
-
if (isAsyncIteratorRef(value)) {
|
|
579
|
+
if (import_isolate_protocol.isAsyncIteratorRef(value)) {
|
|
485
580
|
if ("__nextCallbackId" in value)
|
|
486
581
|
return value;
|
|
487
582
|
const nextCallbackId = instance.nextLocalCallbackId++;
|
|
@@ -526,11 +621,16 @@ async function handleCreateRuntime(message, connection, state) {
|
|
|
526
621
|
}
|
|
527
622
|
return await callback(...args);
|
|
528
623
|
} else {
|
|
529
|
-
const conn = callbackContext
|
|
530
|
-
|
|
531
|
-
|
|
624
|
+
const conn = await waitForConnection(callbackContext);
|
|
625
|
+
try {
|
|
626
|
+
return await invokeClientCallback(conn, callbackId, args);
|
|
627
|
+
} catch (err) {
|
|
628
|
+
if (callbackContext.reconnectionPromise && !callbackContext.connection) {
|
|
629
|
+
const newConn = await callbackContext.reconnectionPromise.promise;
|
|
630
|
+
return invokeClientCallback(newConn, callbackId, args);
|
|
631
|
+
}
|
|
632
|
+
throw err;
|
|
532
633
|
}
|
|
533
|
-
return invokeClientCallback(conn, callbackId, args);
|
|
534
634
|
}
|
|
535
635
|
};
|
|
536
636
|
customFnMarshalOptions = { createMarshalContext, addCallbackIdsToRefs, invokeCallback };
|
|
@@ -547,27 +647,20 @@ async function handleCreateRuntime(message, connection, state) {
|
|
|
547
647
|
const nextCallbackId = callbackContext_.custom.get(`${name}:next`);
|
|
548
648
|
const returnCallbackId = callbackContext_.custom.get(`${name}:return`);
|
|
549
649
|
async function* bridgedIterator() {
|
|
550
|
-
const
|
|
551
|
-
if (!conn || startCallbackId === undefined) {
|
|
552
|
-
throw new Error(`AsyncIterator callback '${name}' not available`);
|
|
553
|
-
}
|
|
554
|
-
const startResult = await invokeClientCallback(conn, startCallbackId, args);
|
|
650
|
+
const startResult = await invokeCallbackWithReconnect(callbackContext_, () => callbackContext_.custom.get(`${name}:start`), args, `AsyncIterator '${name}' start`, invokeClientCallback);
|
|
555
651
|
const iteratorId = startResult.iteratorId;
|
|
556
652
|
try {
|
|
557
653
|
while (true) {
|
|
558
|
-
const
|
|
559
|
-
if (!nextConn || nextCallbackId === undefined) {
|
|
560
|
-
throw new Error(`AsyncIterator callback '${name}' not available`);
|
|
561
|
-
}
|
|
562
|
-
const nextResult = await invokeClientCallback(nextConn, nextCallbackId, [iteratorId]);
|
|
654
|
+
const nextResult = await invokeCallbackWithReconnect(callbackContext_, () => callbackContext_.custom.get(`${name}:next`), [iteratorId], `AsyncIterator '${name}' next`, invokeClientCallback);
|
|
563
655
|
if (nextResult.done)
|
|
564
656
|
return nextResult.value;
|
|
565
657
|
yield nextResult.value;
|
|
566
658
|
}
|
|
567
659
|
} finally {
|
|
568
660
|
const retConn = callbackContext_.connection;
|
|
569
|
-
|
|
570
|
-
|
|
661
|
+
const retCbId = callbackContext_.custom.get(`${name}:return`);
|
|
662
|
+
if (retConn && retCbId !== undefined) {
|
|
663
|
+
await invokeClientCallback(retConn, retCbId, [iteratorId]).catch(() => {});
|
|
571
664
|
}
|
|
572
665
|
}
|
|
573
666
|
}
|
|
@@ -578,12 +671,7 @@ async function handleCreateRuntime(message, connection, state) {
|
|
|
578
671
|
bridgedCustomFunctions[name] = {
|
|
579
672
|
type: registration.type,
|
|
580
673
|
fn: async (...args) => {
|
|
581
|
-
|
|
582
|
-
const cbId = callbackContext_.custom.get(name);
|
|
583
|
-
if (!conn || cbId === undefined) {
|
|
584
|
-
throw new Error(`Custom function callback '${name}' not available`);
|
|
585
|
-
}
|
|
586
|
-
return invokeClientCallback(conn, cbId, args);
|
|
674
|
+
return invokeCallbackWithReconnect(callbackContext_, () => callbackContext_.custom.get(name), args, `Custom function '${name}'`, invokeClientCallback);
|
|
587
675
|
}
|
|
588
676
|
};
|
|
589
677
|
}
|
|
@@ -592,12 +680,7 @@ async function handleCreateRuntime(message, connection, state) {
|
|
|
592
680
|
let moduleLoader;
|
|
593
681
|
if (moduleLoaderCallback) {
|
|
594
682
|
moduleLoader = async (specifier, importer) => {
|
|
595
|
-
|
|
596
|
-
const cbId = callbackContext.moduleLoader;
|
|
597
|
-
if (!conn || cbId === undefined) {
|
|
598
|
-
throw new Error("Module loader callback not available");
|
|
599
|
-
}
|
|
600
|
-
return invokeClientCallback(conn, cbId, [specifier, importer]);
|
|
683
|
+
return invokeCallbackWithReconnect(callbackContext, () => callbackContext.moduleLoader, [specifier, importer], "Module loader", invokeClientCallback);
|
|
601
684
|
};
|
|
602
685
|
}
|
|
603
686
|
let testEnvironment;
|
|
@@ -628,19 +711,8 @@ async function handleCreateRuntime(message, connection, state) {
|
|
|
628
711
|
if (playwrightCallbacks) {
|
|
629
712
|
playwrightOptions = {
|
|
630
713
|
handler: async (op) => {
|
|
631
|
-
const conn = callbackContext.connection;
|
|
632
|
-
const callbackId = callbackContext.playwright.handlerCallbackId;
|
|
633
|
-
if (!conn || callbackId === undefined) {
|
|
634
|
-
return {
|
|
635
|
-
ok: false,
|
|
636
|
-
error: {
|
|
637
|
-
name: "Error",
|
|
638
|
-
message: "Playwright handler callback not available"
|
|
639
|
-
}
|
|
640
|
-
};
|
|
641
|
-
}
|
|
642
714
|
try {
|
|
643
|
-
const resultJson = await
|
|
715
|
+
const resultJson = await invokeCallbackWithReconnect(callbackContext, () => callbackContext.playwright.handlerCallbackId, [JSON.stringify(op)], "Playwright handler", invokeClientCallback);
|
|
644
716
|
return JSON.parse(resultJson);
|
|
645
717
|
} catch (err) {
|
|
646
718
|
const error = err;
|
|
@@ -680,31 +752,23 @@ async function handleCreateRuntime(message, connection, state) {
|
|
|
680
752
|
}
|
|
681
753
|
},
|
|
682
754
|
fetch: async (url, init) => {
|
|
683
|
-
const conn = callbackContext.connection;
|
|
684
|
-
const callbackId = callbackContext.fetch;
|
|
685
|
-
if (!conn || callbackId === undefined) {
|
|
686
|
-
throw new Error("Fetch callback not available");
|
|
687
|
-
}
|
|
688
755
|
const serialized = {
|
|
689
756
|
url,
|
|
690
757
|
method: init.method,
|
|
691
758
|
headers: init.headers,
|
|
692
759
|
body: init.rawBody
|
|
693
760
|
};
|
|
694
|
-
const result = await
|
|
761
|
+
const result = await invokeCallbackWithReconnect(callbackContext, () => callbackContext.fetch, [serialized], "Fetch", invokeClientCallback);
|
|
695
762
|
if (result && typeof result === "object" && result.__streamingResponse) {
|
|
696
763
|
const response = result.response;
|
|
697
764
|
response.__isCallbackStream = true;
|
|
698
765
|
return response;
|
|
699
766
|
}
|
|
700
|
-
return deserializeResponse(result);
|
|
767
|
+
return import_isolate_protocol.deserializeResponse(result);
|
|
701
768
|
},
|
|
702
769
|
fs: {
|
|
703
770
|
getDirectory: async (dirPath) => {
|
|
704
|
-
const conn = callbackContext
|
|
705
|
-
if (!conn) {
|
|
706
|
-
throw new Error("FS callbacks not available");
|
|
707
|
-
}
|
|
771
|
+
const conn = await waitForConnection(callbackContext);
|
|
708
772
|
return import_callback_fs_handler.createCallbackFileSystemHandler({
|
|
709
773
|
connection: conn,
|
|
710
774
|
callbackContext,
|
|
@@ -791,18 +855,19 @@ async function handleCreateRuntime(message, connection, state) {
|
|
|
791
855
|
} else {
|
|
792
856
|
data = cmd.data;
|
|
793
857
|
}
|
|
794
|
-
const
|
|
795
|
-
type:
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
data,
|
|
801
|
-
code: cmd.code,
|
|
802
|
-
reason: cmd.reason
|
|
803
|
-
}
|
|
858
|
+
const payload = {
|
|
859
|
+
type: cmd.type,
|
|
860
|
+
connectionId: cmd.connectionId,
|
|
861
|
+
data,
|
|
862
|
+
code: cmd.code,
|
|
863
|
+
reason: cmd.reason
|
|
804
864
|
};
|
|
805
|
-
sendMessage(targetConnection.socket,
|
|
865
|
+
sendMessage(targetConnection.socket, {
|
|
866
|
+
type: import_isolate_protocol.MessageType.ISOLATE_EVENT,
|
|
867
|
+
isolateId,
|
|
868
|
+
event: import_isolate_protocol.IsolateEvents.WS_COMMAND,
|
|
869
|
+
payload
|
|
870
|
+
});
|
|
806
871
|
});
|
|
807
872
|
runtime.fetch.onClientWebSocketCommand((cmd) => {
|
|
808
873
|
const targetConnection = callbackContext.connection;
|
|
@@ -816,40 +881,62 @@ async function handleCreateRuntime(message, connection, state) {
|
|
|
816
881
|
data = cmd.data;
|
|
817
882
|
}
|
|
818
883
|
if (cmd.type === "connect") {
|
|
819
|
-
const
|
|
820
|
-
type: import_isolate_protocol.MessageType.CLIENT_WS_CONNECT,
|
|
821
|
-
requestId: 0,
|
|
822
|
-
isolateId,
|
|
884
|
+
const payload = {
|
|
823
885
|
socketId: cmd.socketId,
|
|
824
886
|
url: cmd.url,
|
|
825
887
|
protocols: cmd.protocols
|
|
826
888
|
};
|
|
827
|
-
sendMessage(targetConnection.socket,
|
|
828
|
-
|
|
829
|
-
const msg = {
|
|
830
|
-
type: import_isolate_protocol.MessageType.CLIENT_WS_SEND,
|
|
831
|
-
requestId: 0,
|
|
889
|
+
sendMessage(targetConnection.socket, {
|
|
890
|
+
type: import_isolate_protocol.MessageType.ISOLATE_EVENT,
|
|
832
891
|
isolateId,
|
|
892
|
+
event: import_isolate_protocol.IsolateEvents.WS_CLIENT_CONNECT,
|
|
893
|
+
payload
|
|
894
|
+
});
|
|
895
|
+
} else if (cmd.type === "send") {
|
|
896
|
+
const payload = {
|
|
833
897
|
socketId: cmd.socketId,
|
|
834
898
|
data
|
|
835
899
|
};
|
|
836
|
-
sendMessage(targetConnection.socket,
|
|
837
|
-
|
|
838
|
-
const msg = {
|
|
839
|
-
type: import_isolate_protocol.MessageType.CLIENT_WS_CLOSE,
|
|
840
|
-
requestId: 0,
|
|
900
|
+
sendMessage(targetConnection.socket, {
|
|
901
|
+
type: import_isolate_protocol.MessageType.ISOLATE_EVENT,
|
|
841
902
|
isolateId,
|
|
903
|
+
event: import_isolate_protocol.IsolateEvents.WS_CLIENT_SEND,
|
|
904
|
+
payload
|
|
905
|
+
});
|
|
906
|
+
} else if (cmd.type === "close") {
|
|
907
|
+
const payload = {
|
|
842
908
|
socketId: cmd.socketId,
|
|
843
909
|
code: cmd.code,
|
|
844
910
|
reason: cmd.reason
|
|
845
911
|
};
|
|
846
|
-
sendMessage(targetConnection.socket,
|
|
912
|
+
sendMessage(targetConnection.socket, {
|
|
913
|
+
type: import_isolate_protocol.MessageType.ISOLATE_EVENT,
|
|
914
|
+
isolateId,
|
|
915
|
+
event: import_isolate_protocol.IsolateEvents.WS_CLIENT_CLOSE,
|
|
916
|
+
payload
|
|
917
|
+
});
|
|
847
918
|
}
|
|
848
919
|
});
|
|
920
|
+
runtime.fetch.onEvent((eventName, payload) => {
|
|
921
|
+
const targetConnection = callbackContext.connection;
|
|
922
|
+
if (!targetConnection) {
|
|
923
|
+
return;
|
|
924
|
+
}
|
|
925
|
+
sendMessage(targetConnection.socket, {
|
|
926
|
+
type: import_isolate_protocol.MessageType.ISOLATE_EVENT,
|
|
927
|
+
isolateId,
|
|
928
|
+
event: eventName,
|
|
929
|
+
payload
|
|
930
|
+
});
|
|
931
|
+
});
|
|
849
932
|
sendOk(connection.socket, message.requestId, { isolateId, reused: false });
|
|
850
933
|
} catch (err) {
|
|
851
934
|
const error = err;
|
|
852
935
|
sendError(connection.socket, message.requestId, import_isolate_protocol.ErrorCode.SCRIPT_ERROR, error.message, { name: error.name, stack: error.stack });
|
|
936
|
+
} finally {
|
|
937
|
+
if (namespaceCreationLocked && namespaceId != null) {
|
|
938
|
+
state.namespacedCreatesInFlight.delete(namespaceId);
|
|
939
|
+
}
|
|
853
940
|
}
|
|
854
941
|
}
|
|
855
942
|
async function handleDisposeRuntime(message, connection, state) {
|
|
@@ -865,14 +952,20 @@ async function handleDisposeRuntime(message, connection, state) {
|
|
|
865
952
|
try {
|
|
866
953
|
connection.isolates.delete(message.isolateId);
|
|
867
954
|
if (instance.namespaceId != null) {
|
|
868
|
-
|
|
955
|
+
if (instance.isPoisoned) {
|
|
956
|
+
await hardDeleteRuntime(instance, state);
|
|
957
|
+
} else {
|
|
958
|
+
softDeleteRuntime(instance, state);
|
|
959
|
+
}
|
|
869
960
|
} else {
|
|
870
|
-
await instance
|
|
871
|
-
state.isolates.delete(message.isolateId);
|
|
961
|
+
await hardDeleteRuntime(instance, state);
|
|
872
962
|
}
|
|
873
963
|
sendOk(connection.socket, message.requestId);
|
|
874
964
|
} catch (err) {
|
|
875
965
|
const error = err;
|
|
966
|
+
if (instance.namespaceId != null && isLinkerConflictError(error)) {
|
|
967
|
+
instance.isPoisoned = true;
|
|
968
|
+
}
|
|
876
969
|
sendError(connection.socket, message.requestId, import_isolate_protocol.ErrorCode.SCRIPT_ERROR, error.message, { name: error.name, stack: error.stack });
|
|
877
970
|
}
|
|
878
971
|
}
|
|
@@ -885,14 +978,15 @@ async function handleEval(message, connection, state) {
|
|
|
885
978
|
instance.lastActivity = Date.now();
|
|
886
979
|
try {
|
|
887
980
|
await instance.runtime.eval(message.code, {
|
|
888
|
-
filename: message.filename
|
|
889
|
-
maxExecutionMs: message.maxExecutionMs
|
|
981
|
+
filename: message.filename
|
|
890
982
|
});
|
|
891
983
|
sendOk(connection.socket, message.requestId, { value: undefined });
|
|
892
984
|
} catch (err) {
|
|
893
985
|
const error = err;
|
|
894
|
-
|
|
895
|
-
|
|
986
|
+
if (instance.namespaceId != null && isLinkerConflictError(error)) {
|
|
987
|
+
instance.isPoisoned = true;
|
|
988
|
+
}
|
|
989
|
+
sendError(connection.socket, message.requestId, import_isolate_protocol.ErrorCode.SCRIPT_ERROR, error.message, { name: error.name, stack: error.stack });
|
|
896
990
|
}
|
|
897
991
|
}
|
|
898
992
|
async function handleDispatchRequest(message, connection, state) {
|
|
@@ -1000,34 +1094,38 @@ async function handleWsClose(message, connection, state) {
|
|
|
1000
1094
|
sendError(connection.socket, message.requestId, import_isolate_protocol.ErrorCode.SCRIPT_ERROR, error.message, { name: error.name, stack: error.stack });
|
|
1001
1095
|
}
|
|
1002
1096
|
}
|
|
1003
|
-
function
|
|
1004
|
-
const instance = state.isolates.get(message.isolateId);
|
|
1005
|
-
if (!instance)
|
|
1006
|
-
return;
|
|
1007
|
-
instance.lastActivity = Date.now();
|
|
1008
|
-
instance.runtime.fetch.dispatchClientWebSocketOpen(message.socketId, message.protocol, message.extensions);
|
|
1009
|
-
}
|
|
1010
|
-
function handleClientWsMessage(message, connection, state) {
|
|
1011
|
-
const instance = state.isolates.get(message.isolateId);
|
|
1012
|
-
if (!instance)
|
|
1013
|
-
return;
|
|
1014
|
-
instance.lastActivity = Date.now();
|
|
1015
|
-
const data = message.data instanceof Uint8Array ? message.data.buffer.slice(message.data.byteOffset, message.data.byteOffset + message.data.byteLength) : message.data;
|
|
1016
|
-
instance.runtime.fetch.dispatchClientWebSocketMessage(message.socketId, data);
|
|
1017
|
-
}
|
|
1018
|
-
function handleClientWsClosed(message, connection, state) {
|
|
1097
|
+
function handleClientEvent(message, connection, state) {
|
|
1019
1098
|
const instance = state.isolates.get(message.isolateId);
|
|
1020
1099
|
if (!instance)
|
|
1021
1100
|
return;
|
|
1022
1101
|
instance.lastActivity = Date.now();
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1102
|
+
switch (message.event) {
|
|
1103
|
+
case import_isolate_protocol.ClientEvents.WS_CLIENT_OPENED: {
|
|
1104
|
+
const payload = message.payload;
|
|
1105
|
+
instance.runtime.fetch.dispatchClientWebSocketOpen(payload.socketId, payload.protocol, payload.extensions);
|
|
1106
|
+
break;
|
|
1107
|
+
}
|
|
1108
|
+
case import_isolate_protocol.ClientEvents.WS_CLIENT_MESSAGE: {
|
|
1109
|
+
const payload = message.payload;
|
|
1110
|
+
const data = payload.data instanceof Uint8Array ? payload.data.buffer.slice(payload.data.byteOffset, payload.data.byteOffset + payload.data.byteLength) : payload.data;
|
|
1111
|
+
instance.runtime.fetch.dispatchClientWebSocketMessage(payload.socketId, data);
|
|
1112
|
+
break;
|
|
1113
|
+
}
|
|
1114
|
+
case import_isolate_protocol.ClientEvents.WS_CLIENT_CLOSED: {
|
|
1115
|
+
const payload = message.payload;
|
|
1116
|
+
instance.runtime.fetch.dispatchClientWebSocketClose(payload.socketId, payload.code, payload.reason, payload.wasClean);
|
|
1117
|
+
break;
|
|
1118
|
+
}
|
|
1119
|
+
case import_isolate_protocol.ClientEvents.WS_CLIENT_ERROR: {
|
|
1120
|
+
const payload = message.payload;
|
|
1121
|
+
instance.runtime.fetch.dispatchClientWebSocketError(payload.socketId);
|
|
1122
|
+
break;
|
|
1123
|
+
}
|
|
1124
|
+
default: {
|
|
1125
|
+
instance.runtime.fetch.dispatchEvent(message.event, message.payload);
|
|
1126
|
+
break;
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
1031
1129
|
}
|
|
1032
1130
|
async function handleFetchGetUpgradeRequest(message, connection, state) {
|
|
1033
1131
|
const instance = state.isolates.get(message.isolateId);
|
|
@@ -1171,9 +1269,6 @@ function handleCallbackResponse(message, connection) {
|
|
|
1171
1269
|
return;
|
|
1172
1270
|
}
|
|
1173
1271
|
connection.pendingCallbacks.delete(message.requestId);
|
|
1174
|
-
if (pending.timeoutId) {
|
|
1175
|
-
clearTimeout(pending.timeoutId);
|
|
1176
|
-
}
|
|
1177
1272
|
if (message.error) {
|
|
1178
1273
|
const error = new Error(message.error.message);
|
|
1179
1274
|
error.name = message.error.name;
|
|
@@ -1185,17 +1280,12 @@ function handleCallbackResponse(message, connection) {
|
|
|
1185
1280
|
pending.resolve(message.result);
|
|
1186
1281
|
}
|
|
1187
1282
|
}
|
|
1188
|
-
async function invokeClientCallback(connection, callbackId, args
|
|
1283
|
+
async function invokeClientCallback(connection, callbackId, args) {
|
|
1189
1284
|
const requestId = connection.nextCallbackId++;
|
|
1190
1285
|
return new Promise((resolve, reject) => {
|
|
1191
|
-
const timeoutId = setTimeout(() => {
|
|
1192
|
-
connection.pendingCallbacks.delete(requestId);
|
|
1193
|
-
reject(new Error("Callback timeout"));
|
|
1194
|
-
}, timeout);
|
|
1195
1286
|
const pending = {
|
|
1196
1287
|
resolve,
|
|
1197
|
-
reject
|
|
1198
|
-
timeoutId
|
|
1288
|
+
reject
|
|
1199
1289
|
};
|
|
1200
1290
|
connection.pendingCallbacks.set(requestId, pending);
|
|
1201
1291
|
const invoke = {
|
|
@@ -1207,13 +1297,6 @@ async function invokeClientCallback(connection, callbackId, args, timeout = 1e4)
|
|
|
1207
1297
|
sendMessage(connection.socket, invoke);
|
|
1208
1298
|
});
|
|
1209
1299
|
}
|
|
1210
|
-
function deserializeResponse(data) {
|
|
1211
|
-
return new Response(data.body, {
|
|
1212
|
-
status: data.status,
|
|
1213
|
-
statusText: data.statusText,
|
|
1214
|
-
headers: data.headers
|
|
1215
|
-
});
|
|
1216
|
-
}
|
|
1217
1300
|
function handleStreamPush(message, connection) {
|
|
1218
1301
|
const receiver = connection.streamReceivers.get(message.streamId);
|
|
1219
1302
|
if (!receiver) {
|
|
@@ -1341,9 +1424,6 @@ function handleCallbackStreamStart(message, connection) {
|
|
|
1341
1424
|
const pending = connection.pendingCallbacks.get(message.requestId);
|
|
1342
1425
|
if (pending) {
|
|
1343
1426
|
connection.pendingCallbacks.delete(message.requestId);
|
|
1344
|
-
if (pending.timeoutId) {
|
|
1345
|
-
clearTimeout(pending.timeoutId);
|
|
1346
|
-
}
|
|
1347
1427
|
const response = new Response(readableStream, {
|
|
1348
1428
|
status: message.metadata.status,
|
|
1349
1429
|
statusText: message.metadata.statusText,
|
|
@@ -1474,13 +1554,35 @@ async function handleRunTests(message, connection, state) {
|
|
|
1474
1554
|
return;
|
|
1475
1555
|
}
|
|
1476
1556
|
instance.lastActivity = Date.now();
|
|
1557
|
+
if (instance.pendingTestRun) {
|
|
1558
|
+
try {
|
|
1559
|
+
const results = await instance.pendingTestRun.promise;
|
|
1560
|
+
const currentConn = instance.callbackContext?.connection;
|
|
1561
|
+
if (currentConn) {
|
|
1562
|
+
sendOk(currentConn.socket, message.requestId, results);
|
|
1563
|
+
}
|
|
1564
|
+
} catch (err) {
|
|
1565
|
+
const error = err;
|
|
1566
|
+
const currentConn = instance.callbackContext?.connection;
|
|
1567
|
+
if (currentConn) {
|
|
1568
|
+
sendError(currentConn.socket, message.requestId, import_isolate_protocol.ErrorCode.SCRIPT_ERROR, error.message, { name: error.name, stack: error.stack });
|
|
1569
|
+
}
|
|
1570
|
+
}
|
|
1571
|
+
return;
|
|
1572
|
+
}
|
|
1477
1573
|
try {
|
|
1478
1574
|
const timeout = message.timeout ?? 30000;
|
|
1479
|
-
const
|
|
1480
|
-
|
|
1575
|
+
const runPromise = instance.runtime.testEnvironment.runTests(timeout);
|
|
1576
|
+
instance.pendingTestRun = { promise: runPromise };
|
|
1577
|
+
const results = await runPromise;
|
|
1578
|
+
instance.pendingTestRun = undefined;
|
|
1579
|
+
const currentConn = instance.callbackContext?.connection ?? connection;
|
|
1580
|
+
sendOk(currentConn.socket, message.requestId, results);
|
|
1481
1581
|
} catch (err) {
|
|
1582
|
+
instance.pendingTestRun = undefined;
|
|
1482
1583
|
const error = err;
|
|
1483
|
-
|
|
1584
|
+
const currentConn = instance.callbackContext?.connection ?? connection;
|
|
1585
|
+
sendError(currentConn.socket, message.requestId, import_isolate_protocol.ErrorCode.SCRIPT_ERROR, error.message, { name: error.name, stack: error.stack });
|
|
1484
1586
|
}
|
|
1485
1587
|
}
|
|
1486
1588
|
async function handleResetTestEnv(message, connection, state) {
|
|
@@ -1559,4 +1661,4 @@ async function handleClearCollectedData(message, connection, state) {
|
|
|
1559
1661
|
}
|
|
1560
1662
|
}
|
|
1561
1663
|
|
|
1562
|
-
//# debugId=
|
|
1664
|
+
//# debugId=D9712A3ECDBAF18B64756E2164756E21
|