@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.
- package/dist/cjs/connection.cjs +190 -40
- package/dist/cjs/connection.cjs.map +3 -3
- package/dist/cjs/package.json +1 -1
- package/dist/mjs/connection.mjs +190 -40
- package/dist/mjs/connection.mjs.map +3 -3
- package/dist/mjs/package.json +1 -1
- package/package.json +1 -1
package/dist/mjs/connection.mjs
CHANGED
|
@@ -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
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
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
|
-
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
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
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
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,
|
|
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=
|
|
1667
|
+
//# debugId=7EA890CF5E7314ED64756E2164756E21
|