@ricsam/isolate-daemon 0.1.21 → 0.1.22

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.
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@ricsam/isolate-daemon",
3
- "version": "0.1.21",
3
+ "version": "0.1.22",
4
4
  "type": "commonjs"
5
5
  }
@@ -20,6 +20,25 @@ import {
20
20
  } from "@ricsam/isolate-runtime";
21
21
  var LINKER_CONFLICT_ERROR = "Module is currently being linked by another linker";
22
22
  var NULL_BODY_STATUSES = new Set([101, 103, 204, 205, 304]);
23
+ var DEFAULT_CALLBACK_TIMEOUT_MS = 120000;
24
+ var FETCH_CALLBACK_TIMEOUT_MS = 35000;
25
+ function createAbortError(reason = "The operation was aborted") {
26
+ if (typeof DOMException !== "undefined") {
27
+ return new DOMException(reason, "AbortError");
28
+ }
29
+ const error = new Error(reason);
30
+ error.name = "AbortError";
31
+ return error;
32
+ }
33
+ function sendCallbackAbortMessage(connection, targetRequestId, reason) {
34
+ const abortMessage = {
35
+ type: MessageType.CALLBACK_ABORT,
36
+ requestId: connection.nextRequestId++,
37
+ targetRequestId,
38
+ reason
39
+ };
40
+ sendMessage(connection.socket, abortMessage);
41
+ }
23
42
  function getErrorText(error) {
24
43
  if (error instanceof Error) {
25
44
  const cause = error.cause;
@@ -94,6 +113,7 @@ function handleConnection(socket, state) {
94
113
  }
95
114
  }
96
115
  for (const [, pending] of connection.pendingCallbacks) {
116
+ pending.cleanup?.();
97
117
  pending.reject(new Error("Connection closed"));
98
118
  }
99
119
  connection.pendingCallbacks.clear();
@@ -427,14 +447,14 @@ async function waitForConnection(callbackContext) {
427
447
  }
428
448
  throw new Error("No connection available and no reconnection pending");
429
449
  }
430
- async function invokeCallbackWithReconnect(callbackContext, getCallbackId, args, label, invokeClientCallback) {
450
+ async function invokeCallbackWithReconnect(callbackContext, getCallbackId, args, label, invokeClientCallback, options) {
431
451
  const conn = await waitForConnection(callbackContext);
432
452
  const cbId = getCallbackId();
433
453
  if (cbId === undefined) {
434
454
  throw new Error(`${label} callback not available`);
435
455
  }
436
456
  try {
437
- return await invokeClientCallback(conn, cbId, args);
457
+ return await invokeClientCallback(conn, cbId, args, options);
438
458
  } catch (err) {
439
459
  if (callbackContext.reconnectionPromise && !callbackContext.connection) {
440
460
  const newConn = await callbackContext.reconnectionPromise.promise;
@@ -442,7 +462,7 @@ async function invokeCallbackWithReconnect(callbackContext, getCallbackId, args,
442
462
  if (newCbId === undefined) {
443
463
  throw new Error(`${label} callback not available after reconnection`);
444
464
  }
445
- return invokeClientCallback(newConn, newCbId, args);
465
+ return invokeClientCallback(newConn, newCbId, args, options);
446
466
  }
447
467
  throw err;
448
468
  }
@@ -765,6 +785,9 @@ async function handleCreateRuntime(message, connection, state) {
765
785
  }
766
786
  },
767
787
  fetch: async (url, init) => {
788
+ if (init.signal.aborted) {
789
+ throw createAbortError();
790
+ }
768
791
  const serialized = {
769
792
  url,
770
793
  method: init.method,
@@ -772,7 +795,16 @@ async function handleCreateRuntime(message, connection, state) {
772
795
  body: init.rawBody,
773
796
  signalAborted: init.signal.aborted
774
797
  };
775
- const result = await invokeCallbackWithReconnect(callbackContext, () => callbackContext.fetch, [serialized], "Fetch", invokeClientCallback);
798
+ const callbackAbortController = new AbortController;
799
+ const onInitAbort = () => callbackAbortController.abort();
800
+ init.signal.addEventListener("abort", onInitAbort, { once: true });
801
+ const result = await invokeCallbackWithReconnect(callbackContext, () => callbackContext.fetch, [serialized], "Fetch", invokeClientCallback, {
802
+ signal: callbackAbortController.signal,
803
+ timeoutMs: FETCH_CALLBACK_TIMEOUT_MS,
804
+ timeoutLabel: "fetch callback"
805
+ }).finally(() => {
806
+ init.signal.removeEventListener("abort", onInitAbort);
807
+ });
776
808
  if (result && typeof result === "object" && result.__streamingResponse) {
777
809
  const response = result.response;
778
810
  response.__isCallbackStream = true;
@@ -1303,6 +1335,7 @@ function handleCallbackResponse(message, connection) {
1303
1335
  return;
1304
1336
  }
1305
1337
  connection.pendingCallbacks.delete(message.requestId);
1338
+ pending.cleanup?.();
1306
1339
  if (message.error) {
1307
1340
  const error = new Error(message.error.message);
1308
1341
  error.name = message.error.name;
@@ -1314,12 +1347,68 @@ function handleCallbackResponse(message, connection) {
1314
1347
  pending.resolve(message.result);
1315
1348
  }
1316
1349
  }
1317
- async function invokeClientCallback(connection, callbackId, args) {
1350
+ async function invokeClientCallback(connection, callbackId, args, options) {
1318
1351
  const requestId = connection.nextCallbackId++;
1352
+ const timeoutMs = options?.timeoutMs ?? DEFAULT_CALLBACK_TIMEOUT_MS;
1319
1353
  return new Promise((resolve, reject) => {
1354
+ let settled = false;
1355
+ let timeoutId;
1356
+ let onAbort;
1357
+ let callbackInvokeSent = false;
1358
+ const settle = (handler, value) => {
1359
+ if (settled)
1360
+ return;
1361
+ settled = true;
1362
+ connection.pendingCallbacks.delete(requestId);
1363
+ if (timeoutId) {
1364
+ clearTimeout(timeoutId);
1365
+ timeoutId = undefined;
1366
+ }
1367
+ if (options?.signal && onAbort) {
1368
+ options.signal.removeEventListener("abort", onAbort);
1369
+ }
1370
+ handler(value);
1371
+ };
1372
+ onAbort = () => {
1373
+ if (settled)
1374
+ return;
1375
+ if (callbackInvokeSent) {
1376
+ sendCallbackAbortMessage(connection, requestId, options?.timeoutLabel ? `${options.timeoutLabel} aborted` : "Callback aborted");
1377
+ }
1378
+ settle(reject, createAbortError());
1379
+ };
1380
+ if (options?.signal) {
1381
+ if (options.signal.aborted) {
1382
+ onAbort();
1383
+ return;
1384
+ }
1385
+ options.signal.addEventListener("abort", onAbort, { once: true });
1386
+ }
1387
+ if (timeoutMs > 0) {
1388
+ timeoutId = setTimeout(() => {
1389
+ if (settled)
1390
+ return;
1391
+ const label = options?.timeoutLabel ?? "callback";
1392
+ const timeoutError = new Error(`${label} timed out after ${timeoutMs}ms`);
1393
+ if (callbackInvokeSent) {
1394
+ sendCallbackAbortMessage(connection, requestId, timeoutError.message);
1395
+ }
1396
+ settle(reject, timeoutError);
1397
+ }, timeoutMs);
1398
+ }
1320
1399
  const pending = {
1321
- resolve,
1322
- reject
1400
+ callbackId,
1401
+ cleanup: () => {
1402
+ if (timeoutId) {
1403
+ clearTimeout(timeoutId);
1404
+ timeoutId = undefined;
1405
+ }
1406
+ if (options?.signal && onAbort) {
1407
+ options.signal.removeEventListener("abort", onAbort);
1408
+ }
1409
+ },
1410
+ resolve: (result) => settle(resolve, result),
1411
+ reject: (error) => settle(reject, error)
1323
1412
  };
1324
1413
  connection.pendingCallbacks.set(requestId, pending);
1325
1414
  const invoke = {
@@ -1329,6 +1418,7 @@ async function invokeClientCallback(connection, callbackId, args) {
1329
1418
  args
1330
1419
  };
1331
1420
  sendMessage(connection.socket, invoke);
1421
+ callbackInvokeSent = true;
1332
1422
  });
1333
1423
  }
1334
1424
  function handleStreamPush(message, connection) {
@@ -1748,4 +1838,4 @@ export {
1748
1838
  handleConnection
1749
1839
  };
1750
1840
 
1751
- //# debugId=D1C19AA670E1882A64756E2164756E21
1841
+ //# debugId=4B8E282F52FFC2D664756E2164756E21