@ricsam/isolate-daemon 0.1.20 → 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.
@@ -47,6 +47,26 @@ var import_isolate_protocol = require("@ricsam/isolate-protocol");
47
47
  var import_callback_fs_handler = require("./callback-fs-handler.cjs");
48
48
  var import_isolate_runtime = require("@ricsam/isolate-runtime");
49
49
  var LINKER_CONFLICT_ERROR = "Module is currently being linked by another linker";
50
+ var NULL_BODY_STATUSES = new Set([101, 103, 204, 205, 304]);
51
+ var DEFAULT_CALLBACK_TIMEOUT_MS = 120000;
52
+ var FETCH_CALLBACK_TIMEOUT_MS = 35000;
53
+ function createAbortError(reason = "The operation was aborted") {
54
+ if (typeof DOMException !== "undefined") {
55
+ return new DOMException(reason, "AbortError");
56
+ }
57
+ const error = new Error(reason);
58
+ error.name = "AbortError";
59
+ return error;
60
+ }
61
+ function sendCallbackAbortMessage(connection, targetRequestId, reason) {
62
+ const abortMessage = {
63
+ type: import_isolate_protocol.MessageType.CALLBACK_ABORT,
64
+ requestId: connection.nextRequestId++,
65
+ targetRequestId,
66
+ reason
67
+ };
68
+ sendMessage(connection.socket, abortMessage);
69
+ }
50
70
  function getErrorText(error) {
51
71
  if (error instanceof Error) {
52
72
  const cause = error.cause;
@@ -121,6 +141,7 @@ function handleConnection(socket, state) {
121
141
  }
122
142
  }
123
143
  for (const [, pending] of connection.pendingCallbacks) {
144
+ pending.cleanup?.();
124
145
  pending.reject(new Error("Connection closed"));
125
146
  }
126
147
  connection.pendingCallbacks.clear();
@@ -454,14 +475,14 @@ async function waitForConnection(callbackContext) {
454
475
  }
455
476
  throw new Error("No connection available and no reconnection pending");
456
477
  }
457
- async function invokeCallbackWithReconnect(callbackContext, getCallbackId, args, label, invokeClientCallback) {
478
+ async function invokeCallbackWithReconnect(callbackContext, getCallbackId, args, label, invokeClientCallback, options) {
458
479
  const conn = await waitForConnection(callbackContext);
459
480
  const cbId = getCallbackId();
460
481
  if (cbId === undefined) {
461
482
  throw new Error(`${label} callback not available`);
462
483
  }
463
484
  try {
464
- return await invokeClientCallback(conn, cbId, args);
485
+ return await invokeClientCallback(conn, cbId, args, options);
465
486
  } catch (err) {
466
487
  if (callbackContext.reconnectionPromise && !callbackContext.connection) {
467
488
  const newConn = await callbackContext.reconnectionPromise.promise;
@@ -469,7 +490,7 @@ async function invokeCallbackWithReconnect(callbackContext, getCallbackId, args,
469
490
  if (newCbId === undefined) {
470
491
  throw new Error(`${label} callback not available after reconnection`);
471
492
  }
472
- return invokeClientCallback(newConn, newCbId, args);
493
+ return invokeClientCallback(newConn, newCbId, args, options);
473
494
  }
474
495
  throw err;
475
496
  }
@@ -792,6 +813,9 @@ async function handleCreateRuntime(message, connection, state) {
792
813
  }
793
814
  },
794
815
  fetch: async (url, init) => {
816
+ if (init.signal.aborted) {
817
+ throw createAbortError();
818
+ }
795
819
  const serialized = {
796
820
  url,
797
821
  method: init.method,
@@ -799,7 +823,16 @@ async function handleCreateRuntime(message, connection, state) {
799
823
  body: init.rawBody,
800
824
  signalAborted: init.signal.aborted
801
825
  };
802
- const result = await invokeCallbackWithReconnect(callbackContext, () => callbackContext.fetch, [serialized], "Fetch", invokeClientCallback);
826
+ const callbackAbortController = new AbortController;
827
+ const onInitAbort = () => callbackAbortController.abort();
828
+ init.signal.addEventListener("abort", onInitAbort, { once: true });
829
+ const result = await invokeCallbackWithReconnect(callbackContext, () => callbackContext.fetch, [serialized], "Fetch", invokeClientCallback, {
830
+ signal: callbackAbortController.signal,
831
+ timeoutMs: FETCH_CALLBACK_TIMEOUT_MS,
832
+ timeoutLabel: "fetch callback"
833
+ }).finally(() => {
834
+ init.signal.removeEventListener("abort", onInitAbort);
835
+ });
803
836
  if (result && typeof result === "object" && result.__streamingResponse) {
804
837
  const response = result.response;
805
838
  response.__isCallbackStream = true;
@@ -1330,6 +1363,7 @@ function handleCallbackResponse(message, connection) {
1330
1363
  return;
1331
1364
  }
1332
1365
  connection.pendingCallbacks.delete(message.requestId);
1366
+ pending.cleanup?.();
1333
1367
  if (message.error) {
1334
1368
  const error = new Error(message.error.message);
1335
1369
  error.name = message.error.name;
@@ -1341,12 +1375,68 @@ function handleCallbackResponse(message, connection) {
1341
1375
  pending.resolve(message.result);
1342
1376
  }
1343
1377
  }
1344
- async function invokeClientCallback(connection, callbackId, args) {
1378
+ async function invokeClientCallback(connection, callbackId, args, options) {
1345
1379
  const requestId = connection.nextCallbackId++;
1380
+ const timeoutMs = options?.timeoutMs ?? DEFAULT_CALLBACK_TIMEOUT_MS;
1346
1381
  return new Promise((resolve, reject) => {
1382
+ let settled = false;
1383
+ let timeoutId;
1384
+ let onAbort;
1385
+ let callbackInvokeSent = false;
1386
+ const settle = (handler, value) => {
1387
+ if (settled)
1388
+ return;
1389
+ settled = true;
1390
+ connection.pendingCallbacks.delete(requestId);
1391
+ if (timeoutId) {
1392
+ clearTimeout(timeoutId);
1393
+ timeoutId = undefined;
1394
+ }
1395
+ if (options?.signal && onAbort) {
1396
+ options.signal.removeEventListener("abort", onAbort);
1397
+ }
1398
+ handler(value);
1399
+ };
1400
+ onAbort = () => {
1401
+ if (settled)
1402
+ return;
1403
+ if (callbackInvokeSent) {
1404
+ sendCallbackAbortMessage(connection, requestId, options?.timeoutLabel ? `${options.timeoutLabel} aborted` : "Callback aborted");
1405
+ }
1406
+ settle(reject, createAbortError());
1407
+ };
1408
+ if (options?.signal) {
1409
+ if (options.signal.aborted) {
1410
+ onAbort();
1411
+ return;
1412
+ }
1413
+ options.signal.addEventListener("abort", onAbort, { once: true });
1414
+ }
1415
+ if (timeoutMs > 0) {
1416
+ timeoutId = setTimeout(() => {
1417
+ if (settled)
1418
+ return;
1419
+ const label = options?.timeoutLabel ?? "callback";
1420
+ const timeoutError = new Error(`${label} timed out after ${timeoutMs}ms`);
1421
+ if (callbackInvokeSent) {
1422
+ sendCallbackAbortMessage(connection, requestId, timeoutError.message);
1423
+ }
1424
+ settle(reject, timeoutError);
1425
+ }, timeoutMs);
1426
+ }
1347
1427
  const pending = {
1348
- resolve,
1349
- reject
1428
+ callbackId,
1429
+ cleanup: () => {
1430
+ if (timeoutId) {
1431
+ clearTimeout(timeoutId);
1432
+ timeoutId = undefined;
1433
+ }
1434
+ if (options?.signal && onAbort) {
1435
+ options.signal.removeEventListener("abort", onAbort);
1436
+ }
1437
+ },
1438
+ resolve: (result) => settle(resolve, result),
1439
+ reject: (error) => settle(reject, error)
1350
1440
  };
1351
1441
  connection.pendingCallbacks.set(requestId, pending);
1352
1442
  const invoke = {
@@ -1356,6 +1446,7 @@ async function invokeClientCallback(connection, callbackId, args) {
1356
1446
  args
1357
1447
  };
1358
1448
  sendMessage(connection.socket, invoke);
1449
+ callbackInvokeSent = true;
1359
1450
  });
1360
1451
  }
1361
1452
  function handleStreamPush(message, connection) {
@@ -1480,16 +1571,36 @@ function handleCallbackStreamStart(message, connection) {
1480
1571
  return Promise.resolve();
1481
1572
  }
1482
1573
  });
1574
+ const cancelStream = () => {
1575
+ connection.callbackStreamReceivers.delete(message.streamId);
1576
+ sendMessage(connection.socket, {
1577
+ type: import_isolate_protocol.MessageType.CALLBACK_STREAM_CANCEL,
1578
+ streamId: message.streamId
1579
+ });
1580
+ };
1483
1581
  connection.callbackStreamReceivers.set(message.streamId, receiver);
1484
1582
  const pending = connection.pendingCallbacks.get(message.requestId);
1485
- if (pending) {
1486
- connection.pendingCallbacks.delete(message.requestId);
1487
- const response = new Response(readableStream, {
1583
+ if (!pending) {
1584
+ cancelStream();
1585
+ return;
1586
+ }
1587
+ try {
1588
+ const body = NULL_BODY_STATUSES.has(message.metadata.status) ? null : readableStream;
1589
+ const response = new Response(body, {
1488
1590
  status: message.metadata.status,
1489
1591
  statusText: message.metadata.statusText,
1490
1592
  headers: message.metadata.headers
1491
1593
  });
1594
+ connection.pendingCallbacks.delete(message.requestId);
1492
1595
  pending.resolve({ __streamingResponse: true, response });
1596
+ if (body === null) {
1597
+ cancelStream();
1598
+ }
1599
+ } catch (err) {
1600
+ connection.pendingCallbacks.delete(message.requestId);
1601
+ cancelStream();
1602
+ const error = err instanceof Error ? err : new Error(String(err));
1603
+ pending.reject(error);
1493
1604
  }
1494
1605
  }
1495
1606
  function handleCallbackStreamChunk(message, connection) {
@@ -1752,4 +1863,4 @@ async function handleClearCollectedData(message, connection, state) {
1752
1863
  }
1753
1864
  }
1754
1865
 
1755
- //# debugId=3783342607D7276564756E2164756E21
1866
+ //# debugId=8F51D70F9171DF6C64756E2164756E21