@ricsam/isolate-daemon 0.1.17 → 0.1.18

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.
@@ -2,27 +2,37 @@ var __defProp = Object.defineProperty;
2
2
  var __getOwnPropNames = Object.getOwnPropertyNames;
3
3
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
4
  var __hasOwnProp = Object.prototype.hasOwnProperty;
5
- var __moduleCache = /* @__PURE__ */ new WeakMap;
5
+ function __accessProp(key) {
6
+ return this[key];
7
+ }
6
8
  var __toCommonJS = (from) => {
7
- var entry = __moduleCache.get(from), desc;
9
+ var entry = (__moduleCache ??= new WeakMap).get(from), desc;
8
10
  if (entry)
9
11
  return entry;
10
12
  entry = __defProp({}, "__esModule", { value: true });
11
- if (from && typeof from === "object" || typeof from === "function")
12
- __getOwnPropNames(from).map((key) => !__hasOwnProp.call(entry, key) && __defProp(entry, key, {
13
- get: () => from[key],
14
- enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
15
- }));
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (var key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(entry, key))
16
+ __defProp(entry, key, {
17
+ get: __accessProp.bind(from, key),
18
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
19
+ });
20
+ }
16
21
  __moduleCache.set(from, entry);
17
22
  return entry;
18
23
  };
24
+ var __moduleCache;
25
+ var __returnValue = (v) => v;
26
+ function __exportSetter(name, newValue) {
27
+ this[name] = __returnValue.bind(null, newValue);
28
+ }
19
29
  var __export = (target, all) => {
20
30
  for (var name in all)
21
31
  __defProp(target, name, {
22
32
  get: all[name],
23
33
  enumerable: true,
24
34
  configurable: true,
25
- set: (newValue) => all[name] = () => newValue
35
+ set: __exportSetter.bind(all, name)
26
36
  });
27
37
  };
28
38
 
@@ -130,4 +140,4 @@ function updateStats(state) {
130
140
  state.stats.activeConnections = state.connections.size;
131
141
  }
132
142
 
133
- //# debugId=4E8854D8C09B9EE864756E2164756E21
143
+ //# debugId=08DBEA612F7F726864756E2164756E21
@@ -4,7 +4,7 @@
4
4
  "sourcesContent": [
5
5
  "/**\n * @ricsam/isolate-daemon\n *\n * Node.js daemon for running isolated-vm runtimes accessible via IPC.\n */\n\nimport { createServer, type Server } from \"node:net\";\nimport { unlink } from \"node:fs/promises\";\nimport { handleConnection } from \"./connection.cjs\";\nimport type {\n DaemonOptions,\n DaemonHandle,\n DaemonState,\n DaemonStats,\n} from \"./types.cjs\";\n\nexport type { DaemonOptions, DaemonHandle, DaemonStats };\n\nconst DEFAULT_OPTIONS: Required<DaemonOptions> = {\n socketPath: \"/tmp/isolate-daemon.sock\",\n host: \"127.0.0.1\",\n port: 47891,\n maxIsolates: 100,\n defaultMemoryLimitMB: 128,\n};\n\n/**\n * Start the isolate daemon.\n *\n * @param options - Daemon configuration options\n * @returns Handle to control the daemon\n */\nexport async function startDaemon(\n options: DaemonOptions = {}\n): Promise<DaemonHandle> {\n const resolvedOptions: Required<DaemonOptions> = {\n ...DEFAULT_OPTIONS,\n ...options,\n };\n\n const state: DaemonState = {\n isolates: new Map(),\n connections: new Map(),\n stats: {\n activeIsolates: 0,\n activeConnections: 0,\n totalIsolatesCreated: 0,\n totalRequestsProcessed: 0,\n },\n options: resolvedOptions,\n namespacedRuntimes: new Map(),\n namespacedCreatesInFlight: new Set(),\n };\n\n const server = createServer((socket) => {\n handleConnection(socket, state);\n updateStats(state);\n });\n\n // Try to remove existing socket file\n if (resolvedOptions.socketPath) {\n try {\n await unlink(resolvedOptions.socketPath);\n } catch {\n // Ignore if doesn't exist\n }\n }\n\n // Start listening\n await new Promise<void>((resolve, reject) => {\n server.on(\"error\", reject);\n\n if (resolvedOptions.socketPath) {\n server.listen(resolvedOptions.socketPath, () => {\n server.removeListener(\"error\", reject);\n resolve();\n });\n } else {\n server.listen(resolvedOptions.port, resolvedOptions.host, () => {\n server.removeListener(\"error\", reject);\n resolve();\n });\n }\n });\n\n const address = resolvedOptions.socketPath\n ? resolvedOptions.socketPath\n : `${resolvedOptions.host}:${resolvedOptions.port}`;\n\n console.log(`Isolate daemon listening on ${address}`);\n\n return {\n address,\n getStats: () => ({\n ...state.stats,\n activeIsolates: state.isolates.size,\n activeConnections: state.connections.size,\n }),\n close: async () => {\n // Close all connections\n for (const [socket] of state.connections) {\n socket.destroy();\n }\n\n // Dispose all isolates\n for (const [, instance] of state.isolates) {\n try {\n instance.runtime.dispose();\n } catch {\n // Ignore\n }\n }\n\n state.isolates.clear();\n state.connections.clear();\n state.namespacedRuntimes.clear();\n\n // Close server\n await closeServer(server);\n\n // Remove socket file\n if (resolvedOptions.socketPath) {\n try {\n await unlink(resolvedOptions.socketPath);\n } catch {\n // Ignore\n }\n }\n\n console.log(\"Isolate daemon stopped\");\n },\n };\n}\n\nfunction closeServer(server: Server): Promise<void> {\n return new Promise((resolve, reject) => {\n server.close((err) => {\n if (err) {\n reject(err);\n } else {\n resolve();\n }\n });\n });\n}\n\nfunction updateStats(state: DaemonState): void {\n state.stats.activeIsolates = state.isolates.size;\n state.stats.activeConnections = state.connections.size;\n}\n"
6
6
  ],
7
- "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAM0C,IAA1C;AACuB,IAAvB;AACiC,IAAjC;AAUA,IAAM,kBAA2C;AAAA,EAC/C,YAAY;AAAA,EACZ,MAAM;AAAA,EACN,MAAM;AAAA,EACN,aAAa;AAAA,EACb,sBAAsB;AACxB;AAQA,eAAsB,WAAW,CAC/B,UAAyB,CAAC,GACH;AAAA,EACvB,MAAM,kBAA2C;AAAA,OAC5C;AAAA,OACA;AAAA,EACL;AAAA,EAEA,MAAM,QAAqB;AAAA,IACzB,UAAU,IAAI;AAAA,IACd,aAAa,IAAI;AAAA,IACjB,OAAO;AAAA,MACL,gBAAgB;AAAA,MAChB,mBAAmB;AAAA,MACnB,sBAAsB;AAAA,MACtB,wBAAwB;AAAA,IAC1B;AAAA,IACA,SAAS;AAAA,IACT,oBAAoB,IAAI;AAAA,IACxB,2BAA2B,IAAI;AAAA,EACjC;AAAA,EAEA,MAAM,SAAS,6BAAa,CAAC,WAAW;AAAA,IACtC,mCAAiB,QAAQ,KAAK;AAAA,IAC9B,YAAY,KAAK;AAAA,GAClB;AAAA,EAGD,IAAI,gBAAgB,YAAY;AAAA,IAC9B,IAAI;AAAA,MACF,MAAM,uBAAO,gBAAgB,UAAU;AAAA,MACvC,MAAM;AAAA,EAGV;AAAA,EAGA,MAAM,IAAI,QAAc,CAAC,SAAS,WAAW;AAAA,IAC3C,OAAO,GAAG,SAAS,MAAM;AAAA,IAEzB,IAAI,gBAAgB,YAAY;AAAA,MAC9B,OAAO,OAAO,gBAAgB,YAAY,MAAM;AAAA,QAC9C,OAAO,eAAe,SAAS,MAAM;AAAA,QACrC,QAAQ;AAAA,OACT;AAAA,IACH,EAAO;AAAA,MACL,OAAO,OAAO,gBAAgB,MAAM,gBAAgB,MAAM,MAAM;AAAA,QAC9D,OAAO,eAAe,SAAS,MAAM;AAAA,QACrC,QAAQ;AAAA,OACT;AAAA;AAAA,GAEJ;AAAA,EAED,MAAM,UAAU,gBAAgB,aAC5B,gBAAgB,aAChB,GAAG,gBAAgB,QAAQ,gBAAgB;AAAA,EAE/C,QAAQ,IAAI,+BAA+B,SAAS;AAAA,EAEpD,OAAO;AAAA,IACL;AAAA,IACA,UAAU,OAAO;AAAA,SACZ,MAAM;AAAA,MACT,gBAAgB,MAAM,SAAS;AAAA,MAC/B,mBAAmB,MAAM,YAAY;AAAA,IACvC;AAAA,IACA,OAAO,YAAY;AAAA,MAEjB,YAAY,WAAW,MAAM,aAAa;AAAA,QACxC,OAAO,QAAQ;AAAA,MACjB;AAAA,MAGA,cAAc,aAAa,MAAM,UAAU;AAAA,QACzC,IAAI;AAAA,UACF,SAAS,QAAQ,QAAQ;AAAA,UACzB,MAAM;AAAA,MAGV;AAAA,MAEA,MAAM,SAAS,MAAM;AAAA,MACrB,MAAM,YAAY,MAAM;AAAA,MACxB,MAAM,mBAAmB,MAAM;AAAA,MAG/B,MAAM,YAAY,MAAM;AAAA,MAGxB,IAAI,gBAAgB,YAAY;AAAA,QAC9B,IAAI;AAAA,UACF,MAAM,uBAAO,gBAAgB,UAAU;AAAA,UACvC,MAAM;AAAA,MAGV;AAAA,MAEA,QAAQ,IAAI,wBAAwB;AAAA;AAAA,EAExC;AAAA;AAGF,SAAS,WAAW,CAAC,QAA+B;AAAA,EAClD,OAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AAAA,IACtC,OAAO,MAAM,CAAC,QAAQ;AAAA,MACpB,IAAI,KAAK;AAAA,QACP,OAAO,GAAG;AAAA,MACZ,EAAO;AAAA,QACL,QAAQ;AAAA;AAAA,KAEX;AAAA,GACF;AAAA;AAGH,SAAS,WAAW,CAAC,OAA0B;AAAA,EAC7C,MAAM,MAAM,iBAAiB,MAAM,SAAS;AAAA,EAC5C,MAAM,MAAM,oBAAoB,MAAM,YAAY;AAAA;",
8
- "debugId": "4E8854D8C09B9EE864756E2164756E21",
7
+ "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAM0C,IAA1C;AACuB,IAAvB;AACiC,IAAjC;AAUA,IAAM,kBAA2C;AAAA,EAC/C,YAAY;AAAA,EACZ,MAAM;AAAA,EACN,MAAM;AAAA,EACN,aAAa;AAAA,EACb,sBAAsB;AACxB;AAQA,eAAsB,WAAW,CAC/B,UAAyB,CAAC,GACH;AAAA,EACvB,MAAM,kBAA2C;AAAA,OAC5C;AAAA,OACA;AAAA,EACL;AAAA,EAEA,MAAM,QAAqB;AAAA,IACzB,UAAU,IAAI;AAAA,IACd,aAAa,IAAI;AAAA,IACjB,OAAO;AAAA,MACL,gBAAgB;AAAA,MAChB,mBAAmB;AAAA,MACnB,sBAAsB;AAAA,MACtB,wBAAwB;AAAA,IAC1B;AAAA,IACA,SAAS;AAAA,IACT,oBAAoB,IAAI;AAAA,IACxB,2BAA2B,IAAI;AAAA,EACjC;AAAA,EAEA,MAAM,SAAS,6BAAa,CAAC,WAAW;AAAA,IACtC,mCAAiB,QAAQ,KAAK;AAAA,IAC9B,YAAY,KAAK;AAAA,GAClB;AAAA,EAGD,IAAI,gBAAgB,YAAY;AAAA,IAC9B,IAAI;AAAA,MACF,MAAM,uBAAO,gBAAgB,UAAU;AAAA,MACvC,MAAM;AAAA,EAGV;AAAA,EAGA,MAAM,IAAI,QAAc,CAAC,SAAS,WAAW;AAAA,IAC3C,OAAO,GAAG,SAAS,MAAM;AAAA,IAEzB,IAAI,gBAAgB,YAAY;AAAA,MAC9B,OAAO,OAAO,gBAAgB,YAAY,MAAM;AAAA,QAC9C,OAAO,eAAe,SAAS,MAAM;AAAA,QACrC,QAAQ;AAAA,OACT;AAAA,IACH,EAAO;AAAA,MACL,OAAO,OAAO,gBAAgB,MAAM,gBAAgB,MAAM,MAAM;AAAA,QAC9D,OAAO,eAAe,SAAS,MAAM;AAAA,QACrC,QAAQ;AAAA,OACT;AAAA;AAAA,GAEJ;AAAA,EAED,MAAM,UAAU,gBAAgB,aAC5B,gBAAgB,aAChB,GAAG,gBAAgB,QAAQ,gBAAgB;AAAA,EAE/C,QAAQ,IAAI,+BAA+B,SAAS;AAAA,EAEpD,OAAO;AAAA,IACL;AAAA,IACA,UAAU,OAAO;AAAA,SACZ,MAAM;AAAA,MACT,gBAAgB,MAAM,SAAS;AAAA,MAC/B,mBAAmB,MAAM,YAAY;AAAA,IACvC;AAAA,IACA,OAAO,YAAY;AAAA,MAEjB,YAAY,WAAW,MAAM,aAAa;AAAA,QACxC,OAAO,QAAQ;AAAA,MACjB;AAAA,MAGA,cAAc,aAAa,MAAM,UAAU;AAAA,QACzC,IAAI;AAAA,UACF,SAAS,QAAQ,QAAQ;AAAA,UACzB,MAAM;AAAA,MAGV;AAAA,MAEA,MAAM,SAAS,MAAM;AAAA,MACrB,MAAM,YAAY,MAAM;AAAA,MACxB,MAAM,mBAAmB,MAAM;AAAA,MAG/B,MAAM,YAAY,MAAM;AAAA,MAGxB,IAAI,gBAAgB,YAAY;AAAA,QAC9B,IAAI;AAAA,UACF,MAAM,uBAAO,gBAAgB,UAAU;AAAA,UACvC,MAAM;AAAA,MAGV;AAAA,MAEA,QAAQ,IAAI,wBAAwB;AAAA;AAAA,EAExC;AAAA;AAGF,SAAS,WAAW,CAAC,QAA+B;AAAA,EAClD,OAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AAAA,IACtC,OAAO,MAAM,CAAC,QAAQ;AAAA,MACpB,IAAI,KAAK;AAAA,QACP,OAAO,GAAG;AAAA,MACZ,EAAO;AAAA,QACL,QAAQ;AAAA;AAAA,KAEX;AAAA,GACF;AAAA;AAGH,SAAS,WAAW,CAAC,OAA0B;AAAA,EAC7C,MAAM,MAAM,iBAAiB,MAAM,SAAS;AAAA,EAC5C,MAAM,MAAM,oBAAoB,MAAM,YAAY;AAAA;",
8
+ "debugId": "08DBEA612F7F726864756E2164756E21",
9
9
  "names": []
10
10
  }
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@ricsam/isolate-daemon",
3
- "version": "0.1.17",
3
+ "version": "0.1.18",
4
4
  "type": "commonjs"
5
5
  }
@@ -2,23 +2,29 @@ var __defProp = Object.defineProperty;
2
2
  var __getOwnPropNames = Object.getOwnPropertyNames;
3
3
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
4
  var __hasOwnProp = Object.prototype.hasOwnProperty;
5
- var __moduleCache = /* @__PURE__ */ new WeakMap;
5
+ function __accessProp(key) {
6
+ return this[key];
7
+ }
6
8
  var __toCommonJS = (from) => {
7
- var entry = __moduleCache.get(from), desc;
9
+ var entry = (__moduleCache ??= new WeakMap).get(from), desc;
8
10
  if (entry)
9
11
  return entry;
10
12
  entry = __defProp({}, "__esModule", { value: true });
11
- if (from && typeof from === "object" || typeof from === "function")
12
- __getOwnPropNames(from).map((key) => !__hasOwnProp.call(entry, key) && __defProp(entry, key, {
13
- get: () => from[key],
14
- enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
15
- }));
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (var key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(entry, key))
16
+ __defProp(entry, key, {
17
+ get: __accessProp.bind(from, key),
18
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
19
+ });
20
+ }
16
21
  __moduleCache.set(from, entry);
17
22
  return entry;
18
23
  };
24
+ var __moduleCache;
19
25
 
20
26
  // packages/isolate-daemon/src/types.ts
21
27
  var exports_types = {};
22
28
  module.exports = __toCommonJS(exports_types);
23
29
 
24
- //# debugId=A80F84CE523F92D564756E2164756E21
30
+ //# debugId=5C509FE4BE77B71164756E2164756E21
@@ -4,6 +4,6 @@
4
4
  "sourcesContent": [
5
5
  ],
6
6
  "mappings": "",
7
- "debugId": "A80F84CE523F92D564756E2164756E21",
7
+ "debugId": "5C509FE4BE77B71164756E2164756E21",
8
8
  "names": []
9
9
  }
@@ -70,6 +70,9 @@ function handleConnection(socket, state) {
70
70
  }
71
71
  });
72
72
  socket.on("close", () => {
73
+ for (const streamId of Array.from(connection.activeStreams.keys())) {
74
+ closeActiveDownloadSession(connection, streamId);
75
+ }
73
76
  for (const [, controller] of connection.dispatchAbortControllers) {
74
77
  controller.abort();
75
78
  }
@@ -103,6 +106,22 @@ function sendMessage(socket, message) {
103
106
  const frame = buildFrame(message);
104
107
  socket.write(frame);
105
108
  }
109
+ function closeActiveDownloadSession(connection, streamId) {
110
+ const session = connection.activeStreams.get(streamId);
111
+ if (!session) {
112
+ return;
113
+ }
114
+ session.state = "closed";
115
+ if (session.creditResolver) {
116
+ session.creditResolver();
117
+ session.creditResolver = undefined;
118
+ }
119
+ if (session.cancelReader) {
120
+ session.cancelReader();
121
+ session.cancelReader = undefined;
122
+ }
123
+ connection.activeStreams.delete(streamId);
124
+ }
106
125
  function sendError(socket, requestId, code, message, details) {
107
126
  const response = {
108
127
  type: MessageType.RESPONSE_ERROR,
@@ -1362,8 +1381,7 @@ function handleStreamError(message, connection) {
1362
1381
  }
1363
1382
  const session = connection.activeStreams.get(message.streamId);
1364
1383
  if (session) {
1365
- session.state = "closed";
1366
- connection.activeStreams.delete(message.streamId);
1384
+ closeActiveDownloadSession(connection, message.streamId);
1367
1385
  }
1368
1386
  const callbackReceiver = connection.callbackStreamReceivers.get(message.streamId);
1369
1387
  if (callbackReceiver && callbackReceiver.state === "active") {
@@ -1482,6 +1500,7 @@ function waitForCredit(session) {
1482
1500
  });
1483
1501
  }
1484
1502
  async function sendStreamedResponse(connection, requestId, response) {
1503
+ const SEND_LOOP_YIELD_BYTES = 64 * 1024;
1485
1504
  const streamId = connection.nextStreamId++;
1486
1505
  const headers = [];
1487
1506
  response.headers.forEach((value, key) => {
@@ -1517,38 +1536,61 @@ async function sendStreamedResponse(connection, requestId, response) {
1517
1536
  };
1518
1537
  connection.activeStreams.set(streamId, session);
1519
1538
  const reader = response.body.getReader();
1539
+ session.cancelReader = () => {
1540
+ reader.cancel("Stream cancelled by client").catch(() => {});
1541
+ };
1542
+ let cancelledByClient = false;
1543
+ let bytesSinceYield = 0;
1544
+ let chunksSinceYield = 0;
1520
1545
  try {
1521
- while (true) {
1522
- while (session.credit < STREAM_CHUNK_SIZE && session.state === "active") {
1523
- await waitForCredit(session);
1524
- }
1525
- if (session.state !== "active") {
1526
- throw new Error("Stream cancelled");
1527
- }
1528
- const { done, value } = await reader.read();
1529
- if (done) {
1530
- const endMsg = {
1531
- type: MessageType.RESPONSE_STREAM_END,
1532
- requestId,
1533
- streamId
1534
- };
1535
- sendMessage(connection.socket, endMsg);
1536
- break;
1537
- }
1538
- for (let offset = 0;offset < value.length; offset += STREAM_CHUNK_SIZE) {
1539
- const chunk = value.slice(offset, offset + STREAM_CHUNK_SIZE);
1540
- const chunkMsg = {
1541
- type: MessageType.RESPONSE_STREAM_CHUNK,
1542
- requestId,
1543
- streamId,
1544
- chunk
1545
- };
1546
- sendMessage(connection.socket, chunkMsg);
1547
- session.credit -= chunk.length;
1548
- session.bytesTransferred += chunk.length;
1546
+ streamLoop:
1547
+ while (true) {
1548
+ while (session.credit < STREAM_CHUNK_SIZE && session.state === "active") {
1549
+ await waitForCredit(session);
1550
+ }
1551
+ if (session.state !== "active") {
1552
+ cancelledByClient = true;
1553
+ break;
1554
+ }
1555
+ const { done, value } = await reader.read();
1556
+ if (done) {
1557
+ const endMsg = {
1558
+ type: MessageType.RESPONSE_STREAM_END,
1559
+ requestId,
1560
+ streamId
1561
+ };
1562
+ sendMessage(connection.socket, endMsg);
1563
+ break;
1564
+ }
1565
+ for (let offset = 0;offset < value.length; offset += STREAM_CHUNK_SIZE) {
1566
+ const chunk = value.slice(offset, offset + STREAM_CHUNK_SIZE);
1567
+ const chunkMsg = {
1568
+ type: MessageType.RESPONSE_STREAM_CHUNK,
1569
+ requestId,
1570
+ streamId,
1571
+ chunk
1572
+ };
1573
+ sendMessage(connection.socket, chunkMsg);
1574
+ const creditCost = Math.max(chunk.length, STREAM_CHUNK_SIZE);
1575
+ session.credit -= creditCost;
1576
+ session.bytesTransferred += chunk.length;
1577
+ bytesSinceYield += chunk.length;
1578
+ chunksSinceYield += 1;
1579
+ if (chunk.length < 1024 || bytesSinceYield >= SEND_LOOP_YIELD_BYTES || chunksSinceYield >= 256) {
1580
+ bytesSinceYield = 0;
1581
+ chunksSinceYield = 0;
1582
+ await new Promise((resolve) => setImmediate(resolve));
1583
+ if (session.state !== "active") {
1584
+ cancelledByClient = true;
1585
+ break streamLoop;
1586
+ }
1587
+ }
1588
+ }
1549
1589
  }
1550
- }
1551
1590
  } catch (err) {
1591
+ if (cancelledByClient || session.state !== "active") {
1592
+ return;
1593
+ }
1552
1594
  const errorMsg = {
1553
1595
  type: MessageType.STREAM_ERROR,
1554
1596
  streamId,
@@ -1556,8 +1598,15 @@ async function sendStreamedResponse(connection, requestId, response) {
1556
1598
  };
1557
1599
  sendMessage(connection.socket, errorMsg);
1558
1600
  } finally {
1601
+ if (cancelledByClient) {
1602
+ await Promise.race([
1603
+ reader.cancel("Stream cancelled by client").catch(() => {}),
1604
+ new Promise((resolve) => setTimeout(resolve, 50))
1605
+ ]);
1606
+ }
1607
+ session.cancelReader = undefined;
1559
1608
  reader.releaseLock();
1560
- connection.activeStreams.delete(streamId);
1609
+ closeActiveDownloadSession(connection, streamId);
1561
1610
  }
1562
1611
  }
1563
1612
  async function handleRunTests(message, connection, state) {
@@ -1677,4 +1726,4 @@ export {
1677
1726
  handleConnection
1678
1727
  };
1679
1728
 
1680
- //# debugId=23FC39B31009F9AA64756E2164756E21
1729
+ //# debugId=1FC256F121C44C4364756E2164756E21