@ricsam/isolate-client 0.1.16 → 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.
@@ -37,44 +37,142 @@ async function connect(options = {}) {
37
37
  streamResponses: new Map,
38
38
  uploadStreams: new Map,
39
39
  moduleSourceCache: new Map,
40
- callbackStreamReaders: new Map
40
+ callbackStreamReaders: new Map,
41
+ closing: false,
42
+ namespacedRuntimes: new Map
41
43
  };
42
- const parser = createFrameParser();
43
- socket.on("data", (data) => {
44
- try {
45
- for (const frame of parser.feed(new Uint8Array(data))) {
46
- handleMessage(frame.message, state);
44
+ function setupSocket(sock) {
45
+ const parser = createFrameParser();
46
+ sock.on("data", (data) => {
47
+ try {
48
+ for (const frame of parser.feed(new Uint8Array(data))) {
49
+ handleMessage(frame.message, state);
50
+ }
51
+ } catch (err) {
52
+ console.error("Error parsing frame:", err);
47
53
  }
48
- } catch (err) {
49
- console.error("Error parsing frame:", err);
50
- }
51
- });
52
- socket.on("close", () => {
53
- state.connected = false;
54
- for (const [, pending] of state.pendingRequests) {
55
- pending.reject(new Error("Connection closed"));
56
- }
57
- state.pendingRequests.clear();
58
- for (const [, receiver] of state.streamResponses) {
59
- receiver.state = "errored";
60
- receiver.error = new Error("Connection closed");
61
- const resolvers = receiver.pullResolvers.splice(0);
62
- for (const resolver of resolvers) {
63
- resolver();
54
+ });
55
+ sock.on("close", () => {
56
+ state.connected = false;
57
+ for (const [, pending] of state.pendingRequests) {
58
+ pending.reject(new Error("Connection closed"));
64
59
  }
65
- }
66
- state.streamResponses.clear();
67
- for (const [, session] of state.uploadStreams) {
68
- session.state = "closed";
69
- if (session.creditResolver) {
70
- session.creditResolver();
60
+ state.pendingRequests.clear();
61
+ for (const [, receiver] of state.streamResponses) {
62
+ receiver.state = "errored";
63
+ receiver.error = new Error("Connection closed");
64
+ const resolvers = receiver.pullResolvers.splice(0);
65
+ for (const resolver of resolvers) {
66
+ resolver();
67
+ }
68
+ }
69
+ state.streamResponses.clear();
70
+ for (const [, session] of state.uploadStreams) {
71
+ session.state = "closed";
72
+ if (session.creditResolver) {
73
+ session.creditResolver();
74
+ }
75
+ }
76
+ state.uploadStreams.clear();
77
+ if (!state.closing && state.namespacedRuntimes.size > 0) {
78
+ state.reconnecting = reconnect(state, options).catch(() => {
79
+ state.namespacedRuntimes.clear();
80
+ state.reconnecting = undefined;
81
+ });
82
+ }
83
+ });
84
+ sock.on("error", (err) => {
85
+ if (!state.closing && state.namespacedRuntimes.size > 0)
86
+ return;
87
+ console.error("Socket error:", err);
88
+ });
89
+ }
90
+ setupSocket(socket);
91
+ async function reconnect(st, opts) {
92
+ try {
93
+ const newSocket = await createSocket(opts);
94
+ st.socket = newSocket;
95
+ st.connected = true;
96
+ setupSocket(newSocket);
97
+ for (const [namespaceId, descriptor] of st.namespacedRuntimes) {
98
+ const runtimeOptions = descriptor.runtimeOptions;
99
+ const callbacks = {};
100
+ if (runtimeOptions.console) {
101
+ callbacks.console = registerConsoleCallbacks(st, runtimeOptions.console);
102
+ }
103
+ if (runtimeOptions.fetch) {
104
+ callbacks.fetch = registerFetchCallback(st, runtimeOptions.fetch);
105
+ }
106
+ if (runtimeOptions.fs) {
107
+ callbacks.fs = registerFsCallbacks(st, runtimeOptions.fs);
108
+ }
109
+ if (runtimeOptions.moduleLoader) {
110
+ callbacks.moduleLoader = registerModuleLoaderCallback(st, runtimeOptions.moduleLoader);
111
+ }
112
+ if (runtimeOptions.customFunctions) {
113
+ callbacks.custom = registerCustomFunctions(st, runtimeOptions.customFunctions);
114
+ }
115
+ if (runtimeOptions.playwright) {
116
+ const playwrightHandler = runtimeOptions.playwright.handler;
117
+ if (playwrightHandler) {
118
+ const handlerCallbackId = st.nextCallbackId++;
119
+ st.callbacks.set(handlerCallbackId, async (opJson) => {
120
+ const op = JSON.parse(opJson);
121
+ const result2 = await playwrightHandler(op);
122
+ return JSON.stringify(result2);
123
+ });
124
+ callbacks.playwright = {
125
+ handlerCallbackId,
126
+ console: runtimeOptions.playwright.console && !runtimeOptions.console?.onEntry
127
+ };
128
+ }
129
+ }
130
+ let testEnvironmentOption;
131
+ if (runtimeOptions.testEnvironment) {
132
+ if (typeof runtimeOptions.testEnvironment === "object") {
133
+ const testEnvOptions = runtimeOptions.testEnvironment;
134
+ const testEnvCallbacks = {};
135
+ if (testEnvOptions.onEvent) {
136
+ const userOnEvent = testEnvOptions.onEvent;
137
+ const onEventCallbackId = registerEventCallback(st, (eventJson) => {
138
+ const event = JSON.parse(eventJson);
139
+ userOnEvent(event);
140
+ });
141
+ testEnvCallbacks.onEvent = {
142
+ callbackId: onEventCallbackId,
143
+ name: "testEnvironment.onEvent",
144
+ type: "sync"
145
+ };
146
+ }
147
+ testEnvironmentOption = {
148
+ callbacks: testEnvCallbacks,
149
+ testTimeout: testEnvOptions.testTimeout
150
+ };
151
+ } else {
152
+ testEnvironmentOption = true;
153
+ }
154
+ }
155
+ const requestId = st.nextRequestId++;
156
+ const request = {
157
+ type: MessageType.CREATE_RUNTIME,
158
+ requestId,
159
+ options: {
160
+ memoryLimitMB: runtimeOptions.memoryLimitMB,
161
+ cwd: runtimeOptions.cwd,
162
+ callbacks,
163
+ testEnvironment: testEnvironmentOption,
164
+ namespaceId
165
+ }
166
+ };
167
+ const result = await sendRequest(st, request);
168
+ descriptor.isolateId = result.isolateId;
71
169
  }
170
+ st.reconnecting = undefined;
171
+ } catch {
172
+ st.reconnecting = undefined;
173
+ throw new Error("Failed to reconnect to daemon");
72
174
  }
73
- state.uploadStreams.clear();
74
- });
75
- socket.on("error", (err) => {
76
- console.error("Socket error:", err);
77
- });
175
+ }
78
176
  return {
79
177
  createRuntime: (runtimeOptions) => createRuntime(state, runtimeOptions),
80
178
  createNamespace: (id) => ({
@@ -82,8 +180,9 @@ async function connect(options = {}) {
82
180
  createRuntime: (runtimeOptions) => createRuntime(state, runtimeOptions, id)
83
181
  }),
84
182
  close: async () => {
183
+ state.closing = true;
85
184
  state.connected = false;
86
- socket.destroy();
185
+ state.socket.destroy();
87
186
  },
88
187
  isConnected: () => state.connected
89
188
  };
@@ -510,6 +609,12 @@ async function createRuntime(state, options = {}, namespaceId) {
510
609
  const result = await sendRequest(state, request);
511
610
  const isolateId = result.isolateId;
512
611
  const reused = result.reused ?? false;
612
+ if (namespaceId != null) {
613
+ state.namespacedRuntimes.set(namespaceId, {
614
+ isolateId,
615
+ runtimeOptions: options
616
+ });
617
+ }
513
618
  const wsCommandCallbacks = new Set;
514
619
  isolateWsCallbacks.set(isolateId, wsCommandCallbacks);
515
620
  if (options.onWebSocketCommand) {
@@ -524,6 +629,8 @@ async function createRuntime(state, options = {}, namespaceId) {
524
629
  if (signal?.aborted) {
525
630
  throw new DOMException("The operation was aborted", "AbortError");
526
631
  }
632
+ const requestSignal = req.signal;
633
+ const requestSignalInitiallyAborted = requestSignal?.aborted ?? false;
527
634
  const reqId = state.nextRequestId++;
528
635
  const serialized = await serializeRequestWithStreaming(state, req);
529
636
  const { bodyStream, ...serializableRequest } = serialized;
@@ -550,6 +657,23 @@ async function createRuntime(state, options = {}, namespaceId) {
550
657
  };
551
658
  signal.addEventListener("abort", onAbort, { once: true });
552
659
  }
660
+ let onRequestAbort;
661
+ if (requestSignal && !requestSignalInitiallyAborted) {
662
+ onRequestAbort = () => {
663
+ const abortMessage = {
664
+ type: MessageType.DISPATCH_REQUEST_ABORT,
665
+ isolateId,
666
+ targetRequestId: reqId
667
+ };
668
+ if (state.connected) {
669
+ sendMessage(state.socket, abortMessage);
670
+ }
671
+ };
672
+ requestSignal.addEventListener("abort", onRequestAbort, { once: true });
673
+ if (requestSignal.aborted) {
674
+ onRequestAbort();
675
+ }
676
+ }
553
677
  try {
554
678
  if (serialized.bodyStreamId !== undefined && bodyStream) {
555
679
  const streamId = serialized.bodyStreamId;
@@ -565,6 +689,9 @@ async function createRuntime(state, options = {}, namespaceId) {
565
689
  if (signal && onAbort) {
566
690
  signal.removeEventListener("abort", onAbort);
567
691
  }
692
+ if (requestSignal && onRequestAbort) {
693
+ requestSignal.removeEventListener("abort", onRequestAbort);
694
+ }
568
695
  }
569
696
  },
570
697
  async getUpgradeRequest() {
@@ -711,7 +838,22 @@ async function createRuntime(state, options = {}, namespaceId) {
711
838
  isolateId,
712
839
  timeout
713
840
  };
714
- return sendRequest(state, req);
841
+ try {
842
+ return await sendRequest(state, req);
843
+ } catch (err) {
844
+ if (err instanceof Error && /connection closed|not connected/i.test(err.message) && state.reconnecting) {
845
+ await state.reconnecting;
846
+ const retryReqId = state.nextRequestId++;
847
+ const retryReq = {
848
+ type: MessageType.RUN_TESTS,
849
+ requestId: retryReqId,
850
+ isolateId,
851
+ timeout
852
+ };
853
+ return sendRequest(state, retryReq);
854
+ }
855
+ throw err;
856
+ }
715
857
  },
716
858
  async hasTests() {
717
859
  if (!testEnvironmentEnabled) {
@@ -836,6 +978,9 @@ async function createRuntime(state, options = {}, namespaceId) {
836
978
  }
837
979
  isolateClientWebSockets.delete(isolateId);
838
980
  }
981
+ if (namespaceId != null) {
982
+ state.namespacedRuntimes.delete(namespaceId);
983
+ }
839
984
  const reqId = state.nextRequestId++;
840
985
  const req = {
841
986
  type: MessageType.DISPOSE_RUNTIME,
@@ -877,12 +1022,16 @@ function registerFetchCallback(state, callback) {
877
1022
  state.callbacksNeedingRequestId.add(callbackId);
878
1023
  state.callbacks.set(callbackId, async (serialized, requestId) => {
879
1024
  const data = serialized;
1025
+ const signalController = new AbortController;
1026
+ if (data.signalAborted) {
1027
+ signalController.abort();
1028
+ }
880
1029
  const init = {
881
1030
  method: data.method,
882
1031
  headers: data.headers,
883
1032
  rawBody: data.body ?? null,
884
1033
  body: data.body ?? null,
885
- signal: new AbortController().signal
1034
+ signal: signalController.signal
886
1035
  };
887
1036
  const response = await callback(data.url, init);
888
1037
  const contentLength = response.headers.get("content-length");
@@ -1027,7 +1176,7 @@ function registerModuleLoaderCallback(state, callback) {
1027
1176
  const specifier = moduleName;
1028
1177
  const importerInfo = importer;
1029
1178
  const result = await callback(specifier, importerInfo);
1030
- const resolvedPath = path.posix.join(result.resolveDir, path.posix.basename(specifier));
1179
+ const resolvedPath = path.posix.join(result.resolveDir, result.filename);
1031
1180
  state.moduleSourceCache.set(resolvedPath, result.code);
1032
1181
  return result;
1033
1182
  });
@@ -1253,7 +1402,8 @@ async function serializeRequestWithStreaming(state, request) {
1253
1402
  method: request.method,
1254
1403
  url: request.url,
1255
1404
  headers,
1256
- body
1405
+ body,
1406
+ signalAborted: request.signal?.aborted ?? false
1257
1407
  };
1258
1408
  if (bodyStreamId !== undefined) {
1259
1409
  result.bodyStreamId = bodyStreamId;
@@ -1493,7 +1643,7 @@ function handleClientWsSend(isolateId, payload, state) {
1493
1643
  const binary = Buffer.from(base64, "base64");
1494
1644
  ws.send(binary);
1495
1645
  } else if (data instanceof Uint8Array) {
1496
- ws.send(data);
1646
+ ws.send(Buffer.from(data));
1497
1647
  } else {
1498
1648
  ws.send(data);
1499
1649
  }
@@ -1514,4 +1664,4 @@ export {
1514
1664
  connect
1515
1665
  };
1516
1666
 
1517
- //# debugId=A84790A2E8D5115464756E2164756E21
1667
+ //# debugId=7EA890CF5E7314ED64756E2164756E21