@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.
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@ricsam/isolate-daemon",
3
- "version": "0.1.20",
3
+ "version": "0.1.22",
4
4
  "type": "commonjs"
5
5
  }
@@ -19,6 +19,26 @@ import {
19
19
  createRuntime
20
20
  } from "@ricsam/isolate-runtime";
21
21
  var LINKER_CONFLICT_ERROR = "Module is currently being linked by another linker";
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
+ }
22
42
  function getErrorText(error) {
23
43
  if (error instanceof Error) {
24
44
  const cause = error.cause;
@@ -93,6 +113,7 @@ function handleConnection(socket, state) {
93
113
  }
94
114
  }
95
115
  for (const [, pending] of connection.pendingCallbacks) {
116
+ pending.cleanup?.();
96
117
  pending.reject(new Error("Connection closed"));
97
118
  }
98
119
  connection.pendingCallbacks.clear();
@@ -426,14 +447,14 @@ async function waitForConnection(callbackContext) {
426
447
  }
427
448
  throw new Error("No connection available and no reconnection pending");
428
449
  }
429
- async function invokeCallbackWithReconnect(callbackContext, getCallbackId, args, label, invokeClientCallback) {
450
+ async function invokeCallbackWithReconnect(callbackContext, getCallbackId, args, label, invokeClientCallback, options) {
430
451
  const conn = await waitForConnection(callbackContext);
431
452
  const cbId = getCallbackId();
432
453
  if (cbId === undefined) {
433
454
  throw new Error(`${label} callback not available`);
434
455
  }
435
456
  try {
436
- return await invokeClientCallback(conn, cbId, args);
457
+ return await invokeClientCallback(conn, cbId, args, options);
437
458
  } catch (err) {
438
459
  if (callbackContext.reconnectionPromise && !callbackContext.connection) {
439
460
  const newConn = await callbackContext.reconnectionPromise.promise;
@@ -441,7 +462,7 @@ async function invokeCallbackWithReconnect(callbackContext, getCallbackId, args,
441
462
  if (newCbId === undefined) {
442
463
  throw new Error(`${label} callback not available after reconnection`);
443
464
  }
444
- return invokeClientCallback(newConn, newCbId, args);
465
+ return invokeClientCallback(newConn, newCbId, args, options);
445
466
  }
446
467
  throw err;
447
468
  }
@@ -764,6 +785,9 @@ async function handleCreateRuntime(message, connection, state) {
764
785
  }
765
786
  },
766
787
  fetch: async (url, init) => {
788
+ if (init.signal.aborted) {
789
+ throw createAbortError();
790
+ }
767
791
  const serialized = {
768
792
  url,
769
793
  method: init.method,
@@ -771,7 +795,16 @@ async function handleCreateRuntime(message, connection, state) {
771
795
  body: init.rawBody,
772
796
  signalAborted: init.signal.aborted
773
797
  };
774
- 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
+ });
775
808
  if (result && typeof result === "object" && result.__streamingResponse) {
776
809
  const response = result.response;
777
810
  response.__isCallbackStream = true;
@@ -1302,6 +1335,7 @@ function handleCallbackResponse(message, connection) {
1302
1335
  return;
1303
1336
  }
1304
1337
  connection.pendingCallbacks.delete(message.requestId);
1338
+ pending.cleanup?.();
1305
1339
  if (message.error) {
1306
1340
  const error = new Error(message.error.message);
1307
1341
  error.name = message.error.name;
@@ -1313,12 +1347,68 @@ function handleCallbackResponse(message, connection) {
1313
1347
  pending.resolve(message.result);
1314
1348
  }
1315
1349
  }
1316
- async function invokeClientCallback(connection, callbackId, args) {
1350
+ async function invokeClientCallback(connection, callbackId, args, options) {
1317
1351
  const requestId = connection.nextCallbackId++;
1352
+ const timeoutMs = options?.timeoutMs ?? DEFAULT_CALLBACK_TIMEOUT_MS;
1318
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
+ }
1319
1399
  const pending = {
1320
- resolve,
1321
- 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)
1322
1412
  };
1323
1413
  connection.pendingCallbacks.set(requestId, pending);
1324
1414
  const invoke = {
@@ -1328,6 +1418,7 @@ async function invokeClientCallback(connection, callbackId, args) {
1328
1418
  args
1329
1419
  };
1330
1420
  sendMessage(connection.socket, invoke);
1421
+ callbackInvokeSent = true;
1331
1422
  });
1332
1423
  }
1333
1424
  function handleStreamPush(message, connection) {
@@ -1452,16 +1543,36 @@ function handleCallbackStreamStart(message, connection) {
1452
1543
  return Promise.resolve();
1453
1544
  }
1454
1545
  });
1546
+ const cancelStream = () => {
1547
+ connection.callbackStreamReceivers.delete(message.streamId);
1548
+ sendMessage(connection.socket, {
1549
+ type: MessageType.CALLBACK_STREAM_CANCEL,
1550
+ streamId: message.streamId
1551
+ });
1552
+ };
1455
1553
  connection.callbackStreamReceivers.set(message.streamId, receiver);
1456
1554
  const pending = connection.pendingCallbacks.get(message.requestId);
1457
- if (pending) {
1458
- connection.pendingCallbacks.delete(message.requestId);
1459
- const response = new Response(readableStream, {
1555
+ if (!pending) {
1556
+ cancelStream();
1557
+ return;
1558
+ }
1559
+ try {
1560
+ const body = NULL_BODY_STATUSES.has(message.metadata.status) ? null : readableStream;
1561
+ const response = new Response(body, {
1460
1562
  status: message.metadata.status,
1461
1563
  statusText: message.metadata.statusText,
1462
1564
  headers: message.metadata.headers
1463
1565
  });
1566
+ connection.pendingCallbacks.delete(message.requestId);
1464
1567
  pending.resolve({ __streamingResponse: true, response });
1568
+ if (body === null) {
1569
+ cancelStream();
1570
+ }
1571
+ } catch (err) {
1572
+ connection.pendingCallbacks.delete(message.requestId);
1573
+ cancelStream();
1574
+ const error = err instanceof Error ? err : new Error(String(err));
1575
+ pending.reject(error);
1465
1576
  }
1466
1577
  }
1467
1578
  function handleCallbackStreamChunk(message, connection) {
@@ -1727,4 +1838,4 @@ export {
1727
1838
  handleConnection
1728
1839
  };
1729
1840
 
1730
- //# debugId=6A80A8A9D63693D864756E2164756E21
1841
+ //# debugId=4B8E282F52FFC2D664756E2164756E21