@ricsam/isolate-client 0.1.5 → 0.1.7

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,6 +1,5 @@
1
- // @bun
2
1
  // packages/isolate-client/src/connection.ts
3
- import { connect as netConnect } from "net";
2
+ import { connect as netConnect } from "node:net";
4
3
  import {
5
4
  createFrameParser,
6
5
  buildFrame,
@@ -24,7 +23,8 @@ async function connect(options = {}) {
24
23
  nextStreamId: 1,
25
24
  connected: true,
26
25
  streamResponses: new Map,
27
- uploadStreams: new Map
26
+ uploadStreams: new Map,
27
+ moduleSourceCache: new Map
28
28
  };
29
29
  const parser = createFrameParser();
30
30
  socket.on("data", (data) => {
@@ -39,15 +39,38 @@ async function connect(options = {}) {
39
39
  socket.on("close", () => {
40
40
  state.connected = false;
41
41
  for (const [, pending] of state.pendingRequests) {
42
+ if (pending.timeoutId) {
43
+ clearTimeout(pending.timeoutId);
44
+ }
42
45
  pending.reject(new Error("Connection closed"));
43
46
  }
44
47
  state.pendingRequests.clear();
48
+ for (const [, receiver] of state.streamResponses) {
49
+ receiver.state = "errored";
50
+ receiver.error = new Error("Connection closed");
51
+ const resolvers = receiver.pullResolvers.splice(0);
52
+ for (const resolver of resolvers) {
53
+ resolver();
54
+ }
55
+ }
56
+ state.streamResponses.clear();
57
+ for (const [, session] of state.uploadStreams) {
58
+ session.state = "closed";
59
+ if (session.creditResolver) {
60
+ session.creditResolver();
61
+ }
62
+ }
63
+ state.uploadStreams.clear();
45
64
  });
46
65
  socket.on("error", (err) => {
47
66
  console.error("Socket error:", err);
48
67
  });
49
68
  return {
50
69
  createRuntime: (runtimeOptions) => createRuntime(state, runtimeOptions),
70
+ createNamespace: (id) => ({
71
+ id,
72
+ createRuntime: (runtimeOptions) => createRuntime(state, runtimeOptions, id)
73
+ }),
51
74
  close: async () => {
52
75
  state.connected = false;
53
76
  socket.destroy();
@@ -150,23 +173,35 @@ function handleMessage(message, state) {
150
173
  metadata: msg.metadata,
151
174
  controller: null,
152
175
  state: "active",
153
- pendingChunks: []
176
+ pendingChunks: [],
177
+ pullResolvers: [],
178
+ controllerFinalized: false
154
179
  };
155
180
  const readableStream = new ReadableStream({
156
181
  start(controller) {
157
182
  receiver.controller = controller;
158
183
  },
159
184
  pull(_controller) {
185
+ if (receiver.controllerFinalized) {
186
+ return;
187
+ }
160
188
  while (receiver.pendingChunks.length > 0) {
161
189
  const chunk = receiver.pendingChunks.shift();
162
190
  receiver.controller.enqueue(chunk);
163
191
  }
164
192
  if (receiver.state === "closed") {
165
- receiver.controller.close();
166
- return;
193
+ if (!receiver.controllerFinalized) {
194
+ receiver.controllerFinalized = true;
195
+ receiver.controller.close();
196
+ }
197
+ return Promise.resolve();
167
198
  }
168
199
  if (receiver.state === "errored") {
169
- return;
200
+ if (!receiver.controllerFinalized && receiver.error) {
201
+ receiver.controllerFinalized = true;
202
+ receiver.controller.error(receiver.error);
203
+ }
204
+ return Promise.resolve();
170
205
  }
171
206
  sendMessage(state.socket, {
172
207
  type: MessageType.STREAM_PULL,
@@ -174,17 +209,23 @@ function handleMessage(message, state) {
174
209
  maxBytes: STREAM_DEFAULT_CREDIT
175
210
  });
176
211
  return new Promise((resolve) => {
177
- receiver.pullResolver = resolve;
212
+ receiver.pullResolvers.push(resolve);
178
213
  });
179
214
  },
180
215
  cancel(_reason) {
181
- receiver.state = "errored";
216
+ receiver.state = "closed";
217
+ receiver.controllerFinalized = true;
218
+ const resolvers = receiver.pullResolvers.splice(0);
219
+ for (const resolver of resolvers) {
220
+ resolver();
221
+ }
182
222
  sendMessage(state.socket, {
183
223
  type: MessageType.STREAM_ERROR,
184
224
  streamId: msg.streamId,
185
225
  error: "Stream cancelled by consumer"
186
226
  });
187
227
  state.streamResponses.delete(msg.streamId);
228
+ return new Promise((resolve) => setTimeout(resolve, 0));
188
229
  }
189
230
  });
190
231
  state.streamResponses.set(msg.streamId, receiver);
@@ -211,10 +252,9 @@ function handleMessage(message, state) {
211
252
  const msg = message;
212
253
  const receiver = state.streamResponses.get(msg.streamId);
213
254
  if (receiver && receiver.state === "active") {
214
- if (receiver.pullResolver) {
255
+ if (receiver.pullResolvers.length > 0) {
215
256
  receiver.controller.enqueue(msg.chunk);
216
- const resolver = receiver.pullResolver;
217
- receiver.pullResolver = undefined;
257
+ const resolver = receiver.pullResolvers.shift();
218
258
  resolver();
219
259
  } else {
220
260
  receiver.pendingChunks.push(msg.chunk);
@@ -231,10 +271,12 @@ function handleMessage(message, state) {
231
271
  const chunk = receiver.pendingChunks.shift();
232
272
  receiver.controller.enqueue(chunk);
233
273
  }
234
- receiver.controller.close();
235
- if (receiver.pullResolver) {
236
- const resolver = receiver.pullResolver;
237
- receiver.pullResolver = undefined;
274
+ if (!receiver.controllerFinalized) {
275
+ receiver.controllerFinalized = true;
276
+ receiver.controller.close();
277
+ }
278
+ const resolvers = receiver.pullResolvers.splice(0);
279
+ for (const resolver of resolvers) {
238
280
  resolver();
239
281
  }
240
282
  state.streamResponses.delete(msg.streamId);
@@ -263,10 +305,13 @@ function handleMessage(message, state) {
263
305
  const receiver = state.streamResponses.get(msg.streamId);
264
306
  if (receiver) {
265
307
  receiver.state = "errored";
266
- receiver.controller.error(new Error(msg.error));
267
- if (receiver.pullResolver) {
268
- const resolver = receiver.pullResolver;
269
- receiver.pullResolver = undefined;
308
+ receiver.error = new Error(msg.error);
309
+ while (receiver.pendingChunks.length > 0) {
310
+ const chunk = receiver.pendingChunks.shift();
311
+ receiver.controller.enqueue(chunk);
312
+ }
313
+ const resolvers = receiver.pullResolvers.splice(0);
314
+ for (const resolver of resolvers) {
270
315
  resolver();
271
316
  }
272
317
  state.streamResponses.delete(msg.streamId);
@@ -326,7 +371,7 @@ function sendRequest(state, message, timeout = DEFAULT_TIMEOUT) {
326
371
  sendMessage(state.socket, message);
327
372
  });
328
373
  }
329
- async function createRuntime(state, options = {}) {
374
+ async function createRuntime(state, options = {}, namespaceId) {
330
375
  const callbacks = {};
331
376
  if (options.console) {
332
377
  callbacks.console = registerConsoleCallbacks(state, options.console);
@@ -449,11 +494,13 @@ async function createRuntime(state, options = {}) {
449
494
  memoryLimitMB: options.memoryLimitMB,
450
495
  cwd: options.cwd,
451
496
  callbacks,
452
- testEnvironment: testEnvironmentOption
497
+ testEnvironment: testEnvironmentOption,
498
+ namespaceId
453
499
  }
454
500
  };
455
501
  const result = await sendRequest(state, request);
456
502
  const isolateId = result.isolateId;
503
+ const reused = result.reused ?? false;
457
504
  const wsCommandCallbacks = new Set;
458
505
  isolateWsCallbacks.set(isolateId, wsCommandCallbacks);
459
506
  const fetchHandle = {
@@ -697,6 +744,7 @@ async function createRuntime(state, options = {}) {
697
744
  return {
698
745
  id: isolateId,
699
746
  isolateId,
747
+ reused,
700
748
  fetch: fetchHandle,
701
749
  timers: timersHandle,
702
750
  console: consoleHandle,
@@ -830,7 +878,14 @@ function registerFsCallbacks(state, callbacks) {
830
878
  function registerModuleLoaderCallback(state, callback) {
831
879
  const callbackId = state.nextCallbackId++;
832
880
  state.callbacks.set(callbackId, async (moduleName) => {
833
- return callback(moduleName);
881
+ const specifier = moduleName;
882
+ const cached = state.moduleSourceCache.get(specifier);
883
+ if (cached !== undefined) {
884
+ return cached;
885
+ }
886
+ const source = await callback(specifier);
887
+ state.moduleSourceCache.set(specifier, source);
888
+ return source;
834
889
  });
835
890
  return { callbackId, name: "moduleLoader", type: "async" };
836
891
  }
@@ -1159,4 +1214,4 @@ export {
1159
1214
  connect
1160
1215
  };
1161
1216
 
1162
- //# debugId=2BAB1C12EE66984164756E2164756E21
1217
+ //# debugId=CF03E0FFB61C218564756E2164756E21