@ricsam/isolate-daemon 0.1.15 → 0.1.17
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/callback-fs-handler.cjs +17 -11
- package/dist/cjs/callback-fs-handler.cjs.map +3 -3
- package/dist/cjs/connection.cjs +140 -58
- package/dist/cjs/connection.cjs.map +3 -3
- package/dist/cjs/daemon.cjs +1 -5
- package/dist/cjs/daemon.cjs.map +3 -3
- package/dist/cjs/package.json +1 -1
- package/dist/mjs/callback-fs-handler.mjs +17 -11
- package/dist/mjs/callback-fs-handler.mjs.map +3 -3
- package/dist/mjs/connection.mjs +140 -58
- package/dist/mjs/connection.mjs.map +3 -3
- package/dist/mjs/daemon.mjs +1 -5
- package/dist/mjs/daemon.mjs.map +3 -3
- package/dist/mjs/package.json +1 -1
- package/dist/types/types.d.ts +15 -0
- package/package.json +1 -1
package/dist/cjs/daemon.cjs
CHANGED
|
@@ -89,14 +89,10 @@ Shutting down...`);
|
|
|
89
89
|
};
|
|
90
90
|
process.on("SIGINT", shutdown);
|
|
91
91
|
process.on("SIGTERM", shutdown);
|
|
92
|
-
setInterval(() => {
|
|
93
|
-
const stats = daemon.getStats();
|
|
94
|
-
console.log(`[stats] connections: ${stats.activeConnections}, isolates: ${stats.activeIsolates}, total requests: ${stats.totalRequestsProcessed}`);
|
|
95
|
-
}, 60000);
|
|
96
92
|
}
|
|
97
93
|
main().catch((err) => {
|
|
98
94
|
console.error("Failed to start daemon:", err);
|
|
99
95
|
process.exit(1);
|
|
100
96
|
});
|
|
101
97
|
|
|
102
|
-
//# debugId=
|
|
98
|
+
//# debugId=D96ECFE02F2246F464756E2164756E21
|
package/dist/cjs/daemon.cjs.map
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/daemon.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"#!/usr/bin/env node\n\n// Suppress the ExperimentalWarning for stripTypeScriptTypes\nprocess.removeAllListeners(\"warning\");\nprocess.on(\"warning\", (warning) => {\n if (\n warning.name === \"ExperimentalWarning\" &&\n warning.message.includes(\"stripTypeScriptTypes\")\n ) {\n return; // Suppress this specific warning\n }\n console.warn(warning);\n});\n\n/**\n * CLI entry point for the isolate daemon.\n *\n * Usage:\n * isolate-daemon [options]\n *\n * Options:\n * --socket <path> Unix socket path (default: /tmp/isolate-daemon.sock)\n * --host <host> TCP host (default: 127.0.0.1)\n * --port <port> TCP port (default: 47891)\n * --max-isolates <n> Maximum isolates (default: 100)\n * --memory-limit <mb> Default memory limit (default: 128)\n */\n\nimport { startDaemon, type DaemonOptions } from \"./index.cjs\";\n\nfunction parseArgs(args: string[]): Partial<DaemonOptions> {\n const options: Partial<DaemonOptions> = {};\n\n for (let i = 0; i < args.length; i++) {\n const arg = args[i];\n\n switch (arg) {\n case \"--socket\":\n i++;\n if (args[i]) {\n options.socketPath = args[i];\n }\n break;\n case \"--host\":\n i++;\n if (args[i]) {\n options.host = args[i];\n options.socketPath = undefined; // Use TCP instead\n }\n break;\n case \"--port\": {\n i++;\n const value = args[i];\n if (value !== undefined) {\n options.port = parseInt(value, 10);\n options.socketPath = undefined; // Use TCP instead\n }\n break;\n }\n case \"--max-isolates\": {\n i++;\n const value = args[i];\n if (value !== undefined) {\n options.maxIsolates = parseInt(value, 10);\n }\n break;\n }\n case \"--memory-limit\": {\n i++;\n const value = args[i];\n if (value !== undefined) {\n options.defaultMemoryLimitMB = parseInt(value, 10);\n }\n break;\n }\n case \"--help\":\n case \"-h\":\n console.log(`\nIsolate Daemon - Run isolated-vm runtimes accessible via IPC\n\nUsage:\n isolate-daemon [options]\n\nOptions:\n --socket <path> Unix socket path (default: /tmp/isolate-daemon.sock)\n --host <host> TCP host (default: 127.0.0.1, disables Unix socket)\n --port <port> TCP port (default: 47891, disables Unix socket)\n --max-isolates <n> Maximum isolates (default: 100)\n --memory-limit <mb> Default memory limit in MB (default: 128)\n --help, -h Show this help message\n`);\n process.exit(0);\n default:\n if (arg !== undefined && arg.startsWith(\"-\")) {\n console.error(`Unknown option: ${arg}`);\n process.exit(1);\n }\n }\n }\n\n return options;\n}\n\nasync function main() {\n const options = parseArgs(process.argv.slice(2));\n\n const daemon = await startDaemon(options);\n\n // Handle shutdown signals\n const shutdown = async () => {\n console.log(\"\\nShutting down...\");\n await daemon.close();\n process.exit(0);\n };\n\n process.on(\"SIGINT\", shutdown);\n process.on(\"SIGTERM\", shutdown);\n
|
|
5
|
+
"#!/usr/bin/env node\n\n// Suppress the ExperimentalWarning for stripTypeScriptTypes\nprocess.removeAllListeners(\"warning\");\nprocess.on(\"warning\", (warning) => {\n if (\n warning.name === \"ExperimentalWarning\" &&\n warning.message.includes(\"stripTypeScriptTypes\")\n ) {\n return; // Suppress this specific warning\n }\n console.warn(warning);\n});\n\n/**\n * CLI entry point for the isolate daemon.\n *\n * Usage:\n * isolate-daemon [options]\n *\n * Options:\n * --socket <path> Unix socket path (default: /tmp/isolate-daemon.sock)\n * --host <host> TCP host (default: 127.0.0.1)\n * --port <port> TCP port (default: 47891)\n * --max-isolates <n> Maximum isolates (default: 100)\n * --memory-limit <mb> Default memory limit (default: 128)\n */\n\nimport { startDaemon, type DaemonOptions } from \"./index.cjs\";\n\nfunction parseArgs(args: string[]): Partial<DaemonOptions> {\n const options: Partial<DaemonOptions> = {};\n\n for (let i = 0; i < args.length; i++) {\n const arg = args[i];\n\n switch (arg) {\n case \"--socket\":\n i++;\n if (args[i]) {\n options.socketPath = args[i];\n }\n break;\n case \"--host\":\n i++;\n if (args[i]) {\n options.host = args[i];\n options.socketPath = undefined; // Use TCP instead\n }\n break;\n case \"--port\": {\n i++;\n const value = args[i];\n if (value !== undefined) {\n options.port = parseInt(value, 10);\n options.socketPath = undefined; // Use TCP instead\n }\n break;\n }\n case \"--max-isolates\": {\n i++;\n const value = args[i];\n if (value !== undefined) {\n options.maxIsolates = parseInt(value, 10);\n }\n break;\n }\n case \"--memory-limit\": {\n i++;\n const value = args[i];\n if (value !== undefined) {\n options.defaultMemoryLimitMB = parseInt(value, 10);\n }\n break;\n }\n case \"--help\":\n case \"-h\":\n console.log(`\nIsolate Daemon - Run isolated-vm runtimes accessible via IPC\n\nUsage:\n isolate-daemon [options]\n\nOptions:\n --socket <path> Unix socket path (default: /tmp/isolate-daemon.sock)\n --host <host> TCP host (default: 127.0.0.1, disables Unix socket)\n --port <port> TCP port (default: 47891, disables Unix socket)\n --max-isolates <n> Maximum isolates (default: 100)\n --memory-limit <mb> Default memory limit in MB (default: 128)\n --help, -h Show this help message\n`);\n process.exit(0);\n default:\n if (arg !== undefined && arg.startsWith(\"-\")) {\n console.error(`Unknown option: ${arg}`);\n process.exit(1);\n }\n }\n }\n\n return options;\n}\n\nasync function main() {\n const options = parseArgs(process.argv.slice(2));\n\n const daemon = await startDaemon(options);\n\n // Handle shutdown signals\n const shutdown = async () => {\n console.log(\"\\nShutting down...\");\n await daemon.close();\n process.exit(0);\n };\n\n process.on(\"SIGINT\", shutdown);\n process.on(\"SIGTERM\", shutdown);\n}\n\nmain().catch((err) => {\n console.error(\"Failed to start daemon:\", err);\n process.exit(1);\n});\n"
|
|
6
6
|
],
|
|
7
|
-
"mappings": ";;;AA4BgD,IAAhD;AAzBA,QAAQ,mBAAmB,SAAS;AACpC,QAAQ,GAAG,WAAW,CAAC,YAAY;AAAA,EACjC,IACE,QAAQ,SAAS,yBACjB,QAAQ,QAAQ,SAAS,sBAAsB,GAC/C;AAAA,IACA;AAAA,EACF;AAAA,EACA,QAAQ,KAAK,OAAO;AAAA,CACrB;AAkBD,SAAS,SAAS,CAAC,MAAwC;AAAA,EACzD,MAAM,UAAkC,CAAC;AAAA,EAEzC,SAAS,IAAI,EAAG,IAAI,KAAK,QAAQ,KAAK;AAAA,IACpC,MAAM,MAAM,KAAK;AAAA,IAEjB,QAAQ;AAAA,WACD;AAAA,QACH;AAAA,QACA,IAAI,KAAK,IAAI;AAAA,UACX,QAAQ,aAAa,KAAK;AAAA,QAC5B;AAAA,QACA;AAAA,WACG;AAAA,QACH;AAAA,QACA,IAAI,KAAK,IAAI;AAAA,UACX,QAAQ,OAAO,KAAK;AAAA,UACpB,QAAQ,aAAa;AAAA,QACvB;AAAA,QACA;AAAA,WACG,UAAU;AAAA,QACb;AAAA,QACA,MAAM,QAAQ,KAAK;AAAA,QACnB,IAAI,UAAU,WAAW;AAAA,UACvB,QAAQ,OAAO,SAAS,OAAO,EAAE;AAAA,UACjC,QAAQ,aAAa;AAAA,QACvB;AAAA,QACA;AAAA,MACF;AAAA,WACK,kBAAkB;AAAA,QACrB;AAAA,QACA,MAAM,QAAQ,KAAK;AAAA,QACnB,IAAI,UAAU,WAAW;AAAA,UACvB,QAAQ,cAAc,SAAS,OAAO,EAAE;AAAA,QAC1C;AAAA,QACA;AAAA,MACF;AAAA,WACK,kBAAkB;AAAA,QACrB;AAAA,QACA,MAAM,QAAQ,KAAK;AAAA,QACnB,IAAI,UAAU,WAAW;AAAA,UACvB,QAAQ,uBAAuB,SAAS,OAAO,EAAE;AAAA,QACnD;AAAA,QACA;AAAA,MACF;AAAA,WACK;AAAA,WACA;AAAA,QACH,QAAQ,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAanB;AAAA,QACO,QAAQ,KAAK,CAAC;AAAA;AAAA,QAEd,IAAI,QAAQ,aAAa,IAAI,WAAW,GAAG,GAAG;AAAA,UAC5C,QAAQ,MAAM,mBAAmB,KAAK;AAAA,UACtC,QAAQ,KAAK,CAAC;AAAA,QAChB;AAAA;AAAA,EAEN;AAAA,EAEA,OAAO;AAAA;AAGT,eAAe,IAAI,GAAG;AAAA,EACpB,MAAM,UAAU,UAAU,QAAQ,KAAK,MAAM,CAAC,CAAC;AAAA,EAE/C,MAAM,SAAS,MAAM,qBAAY,OAAO;AAAA,EAGxC,MAAM,WAAW,YAAY;AAAA,IAC3B,QAAQ,IAAI;AAAA,iBAAoB;AAAA,IAChC,MAAM,OAAO,MAAM;AAAA,IACnB,QAAQ,KAAK,CAAC;AAAA;AAAA,EAGhB,QAAQ,GAAG,UAAU,QAAQ;AAAA,EAC7B,QAAQ,GAAG,WAAW,QAAQ;AAAA
|
|
8
|
-
"debugId": "
|
|
7
|
+
"mappings": ";;;AA4BgD,IAAhD;AAzBA,QAAQ,mBAAmB,SAAS;AACpC,QAAQ,GAAG,WAAW,CAAC,YAAY;AAAA,EACjC,IACE,QAAQ,SAAS,yBACjB,QAAQ,QAAQ,SAAS,sBAAsB,GAC/C;AAAA,IACA;AAAA,EACF;AAAA,EACA,QAAQ,KAAK,OAAO;AAAA,CACrB;AAkBD,SAAS,SAAS,CAAC,MAAwC;AAAA,EACzD,MAAM,UAAkC,CAAC;AAAA,EAEzC,SAAS,IAAI,EAAG,IAAI,KAAK,QAAQ,KAAK;AAAA,IACpC,MAAM,MAAM,KAAK;AAAA,IAEjB,QAAQ;AAAA,WACD;AAAA,QACH;AAAA,QACA,IAAI,KAAK,IAAI;AAAA,UACX,QAAQ,aAAa,KAAK;AAAA,QAC5B;AAAA,QACA;AAAA,WACG;AAAA,QACH;AAAA,QACA,IAAI,KAAK,IAAI;AAAA,UACX,QAAQ,OAAO,KAAK;AAAA,UACpB,QAAQ,aAAa;AAAA,QACvB;AAAA,QACA;AAAA,WACG,UAAU;AAAA,QACb;AAAA,QACA,MAAM,QAAQ,KAAK;AAAA,QACnB,IAAI,UAAU,WAAW;AAAA,UACvB,QAAQ,OAAO,SAAS,OAAO,EAAE;AAAA,UACjC,QAAQ,aAAa;AAAA,QACvB;AAAA,QACA;AAAA,MACF;AAAA,WACK,kBAAkB;AAAA,QACrB;AAAA,QACA,MAAM,QAAQ,KAAK;AAAA,QACnB,IAAI,UAAU,WAAW;AAAA,UACvB,QAAQ,cAAc,SAAS,OAAO,EAAE;AAAA,QAC1C;AAAA,QACA;AAAA,MACF;AAAA,WACK,kBAAkB;AAAA,QACrB;AAAA,QACA,MAAM,QAAQ,KAAK;AAAA,QACnB,IAAI,UAAU,WAAW;AAAA,UACvB,QAAQ,uBAAuB,SAAS,OAAO,EAAE;AAAA,QACnD;AAAA,QACA;AAAA,MACF;AAAA,WACK;AAAA,WACA;AAAA,QACH,QAAQ,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAanB;AAAA,QACO,QAAQ,KAAK,CAAC;AAAA;AAAA,QAEd,IAAI,QAAQ,aAAa,IAAI,WAAW,GAAG,GAAG;AAAA,UAC5C,QAAQ,MAAM,mBAAmB,KAAK;AAAA,UACtC,QAAQ,KAAK,CAAC;AAAA,QAChB;AAAA;AAAA,EAEN;AAAA,EAEA,OAAO;AAAA;AAGT,eAAe,IAAI,GAAG;AAAA,EACpB,MAAM,UAAU,UAAU,QAAQ,KAAK,MAAM,CAAC,CAAC;AAAA,EAE/C,MAAM,SAAS,MAAM,qBAAY,OAAO;AAAA,EAGxC,MAAM,WAAW,YAAY;AAAA,IAC3B,QAAQ,IAAI;AAAA,iBAAoB;AAAA,IAChC,MAAM,OAAO,MAAM;AAAA,IACnB,QAAQ,KAAK,CAAC;AAAA;AAAA,EAGhB,QAAQ,GAAG,UAAU,QAAQ;AAAA,EAC7B,QAAQ,GAAG,WAAW,QAAQ;AAAA;AAGhC,KAAK,EAAE,MAAM,CAAC,QAAQ;AAAA,EACpB,QAAQ,MAAM,2BAA2B,GAAG;AAAA,EAC5C,QAAQ,KAAK,CAAC;AAAA,CACf;",
|
|
8
|
+
"debugId": "D96ECFE02F2246F464756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
package/dist/cjs/package.json
CHANGED
|
@@ -27,13 +27,19 @@ function createCallbackFileSystemHandler(options) {
|
|
|
27
27
|
const getCallbackId = (name) => {
|
|
28
28
|
return callbackContext.fs[name];
|
|
29
29
|
};
|
|
30
|
-
const getConnection = () => {
|
|
31
|
-
|
|
30
|
+
const getConnection = async () => {
|
|
31
|
+
if (callbackContext.connection) {
|
|
32
|
+
return callbackContext.connection;
|
|
33
|
+
}
|
|
34
|
+
if (callbackContext.reconnectionPromise) {
|
|
35
|
+
return callbackContext.reconnectionPromise.promise;
|
|
36
|
+
}
|
|
37
|
+
return connection;
|
|
32
38
|
};
|
|
33
39
|
return {
|
|
34
40
|
async getFileHandle(path, opts) {
|
|
35
41
|
const fullPath = resolvePath(path);
|
|
36
|
-
const conn = getConnection();
|
|
42
|
+
const conn = await getConnection();
|
|
37
43
|
if (opts?.create) {
|
|
38
44
|
const writeFileId = getCallbackId("writeFile");
|
|
39
45
|
if (writeFileId !== undefined) {
|
|
@@ -75,7 +81,7 @@ function createCallbackFileSystemHandler(options) {
|
|
|
75
81
|
},
|
|
76
82
|
async getDirectoryHandle(path, opts) {
|
|
77
83
|
const fullPath = resolvePath(path);
|
|
78
|
-
const conn = getConnection();
|
|
84
|
+
const conn = await getConnection();
|
|
79
85
|
if (opts?.create) {
|
|
80
86
|
const mkdirId = getCallbackId("mkdir");
|
|
81
87
|
if (mkdirId !== undefined) {
|
|
@@ -107,7 +113,7 @@ function createCallbackFileSystemHandler(options) {
|
|
|
107
113
|
},
|
|
108
114
|
async removeEntry(path, opts) {
|
|
109
115
|
const fullPath = resolvePath(path);
|
|
110
|
-
const conn = getConnection();
|
|
116
|
+
const conn = await getConnection();
|
|
111
117
|
let isFile = true;
|
|
112
118
|
const statId = getCallbackId("stat");
|
|
113
119
|
if (statId !== undefined) {
|
|
@@ -136,7 +142,7 @@ function createCallbackFileSystemHandler(options) {
|
|
|
136
142
|
},
|
|
137
143
|
async readDirectory(path) {
|
|
138
144
|
const fullPath = resolvePath(path);
|
|
139
|
-
const conn = getConnection();
|
|
145
|
+
const conn = await getConnection();
|
|
140
146
|
const readdirId = getCallbackId("readdir");
|
|
141
147
|
if (readdirId === undefined) {
|
|
142
148
|
throw new Error(`[NotAllowedError]Directory reading not supported`);
|
|
@@ -163,7 +169,7 @@ function createCallbackFileSystemHandler(options) {
|
|
|
163
169
|
},
|
|
164
170
|
async readFile(path) {
|
|
165
171
|
const fullPath = resolvePath(path);
|
|
166
|
-
const conn = getConnection();
|
|
172
|
+
const conn = await getConnection();
|
|
167
173
|
const readFileId = getCallbackId("readFile");
|
|
168
174
|
if (readFileId === undefined) {
|
|
169
175
|
throw new Error(`[NotAllowedError]File reading not supported`);
|
|
@@ -201,7 +207,7 @@ function createCallbackFileSystemHandler(options) {
|
|
|
201
207
|
},
|
|
202
208
|
async writeFile(path, data, position) {
|
|
203
209
|
const fullPath = resolvePath(path);
|
|
204
|
-
const conn = getConnection();
|
|
210
|
+
const conn = await getConnection();
|
|
205
211
|
const writeFileId = getCallbackId("writeFile");
|
|
206
212
|
if (writeFileId === undefined) {
|
|
207
213
|
throw new Error(`[NotAllowedError]File writing not supported`);
|
|
@@ -245,7 +251,7 @@ function createCallbackFileSystemHandler(options) {
|
|
|
245
251
|
},
|
|
246
252
|
async truncateFile(path, size) {
|
|
247
253
|
const fullPath = resolvePath(path);
|
|
248
|
-
const conn = getConnection();
|
|
254
|
+
const conn = await getConnection();
|
|
249
255
|
const readFileId = getCallbackId("readFile");
|
|
250
256
|
const writeFileId = getCallbackId("writeFile");
|
|
251
257
|
if (readFileId === undefined || writeFileId === undefined) {
|
|
@@ -270,7 +276,7 @@ function createCallbackFileSystemHandler(options) {
|
|
|
270
276
|
},
|
|
271
277
|
async getFileMetadata(path) {
|
|
272
278
|
const fullPath = resolvePath(path);
|
|
273
|
-
const conn = getConnection();
|
|
279
|
+
const conn = await getConnection();
|
|
274
280
|
const statId = getCallbackId("stat");
|
|
275
281
|
if (statId === undefined) {
|
|
276
282
|
throw new Error(`[NotAllowedError]File stat not supported`);
|
|
@@ -295,4 +301,4 @@ export {
|
|
|
295
301
|
createCallbackFileSystemHandler
|
|
296
302
|
};
|
|
297
303
|
|
|
298
|
-
//# debugId=
|
|
304
|
+
//# debugId=629A15509C1B27EB64756E2164756E21
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/callback-fs-handler.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"/**\n * Callback-based FileSystemHandler adapter.\n *\n * Adapts simple client callbacks (readFile, writeFile, etc.) to the\n * FileSystemHandler interface used by @ricsam/isolate-fs.\n */\n\nimport type { FileSystemHandler } from \"@ricsam/isolate-fs\";\nimport type { ConnectionState, CallbackContext } from \"./types.mjs\";\n\n/** Common MIME type mappings by file extension. */\nconst MIME_TYPES: Record<string, string> = {\n txt: \"text/plain\",\n html: \"text/html\",\n htm: \"text/html\",\n css: \"text/css\",\n js: \"text/javascript\",\n json: \"application/json\",\n xml: \"application/xml\",\n png: \"image/png\",\n jpg: \"image/jpeg\",\n jpeg: \"image/jpeg\",\n gif: \"image/gif\",\n svg: \"image/svg+xml\",\n pdf: \"application/pdf\",\n};\n\ninterface InvokeClientCallback {\n (connection: ConnectionState, callbackId: number, args: unknown[]): Promise<unknown>;\n}\n\ninterface CallbackFsHandlerOptions {\n connection: ConnectionState;\n callbackContext: CallbackContext;\n invokeClientCallback: InvokeClientCallback;\n basePath?: string;\n}\n\n/**\n * Create a FileSystemHandler that invokes client callbacks.\n *\n * Maps WHATWG FileSystem API operations to simple POSIX-like callbacks.\n * Uses callbackContext for dynamic callback ID lookup to support runtime reuse.\n */\nexport function createCallbackFileSystemHandler(\n options: CallbackFsHandlerOptions\n): FileSystemHandler {\n const { connection, callbackContext, invokeClientCallback, basePath = \"\" } = options;\n\n const resolvePath = (path: string): string => {\n // Remove leading slash from the path\n const cleanPath = path.startsWith(\"/\") ? path.slice(1) : path;\n // Handle root case\n if (!basePath || basePath === \"/\") {\n return `/${cleanPath}`;\n }\n // Remove trailing slash from basePath\n const cleanBase = basePath.endsWith(\"/\") ? basePath.slice(0, -1) : basePath;\n return `${cleanBase}/${cleanPath}`;\n };\n\n // Helper to get current callback ID (supports runtime reuse)\n const getCallbackId = (name: keyof CallbackContext[\"fs\"]): number | undefined => {\n return callbackContext.fs[name];\n };\n\n // Helper to get current connection (supports runtime reuse)\n const getConnection = (): ConnectionState => {\n return callbackContext.connection || connection;\n };\n\n return {\n async getFileHandle(path: string, opts?: { create?: boolean }): Promise<void> {\n const fullPath = resolvePath(path);\n const conn = getConnection();\n\n if (opts?.create) {\n // Ensure file exists by writing empty content if it doesn't exist\n const writeFileId = getCallbackId(\"writeFile\");\n if (writeFileId !== undefined) {\n try {\n // Check if file exists first\n const statId = getCallbackId(\"stat\");\n if (statId !== undefined) {\n try {\n await invokeClientCallback(conn, statId, [fullPath]);\n // File exists, nothing to do\n return;\n } catch {\n // File doesn't exist, create it\n }\n }\n // Create empty file\n await invokeClientCallback(conn, writeFileId, [\n fullPath,\n new Uint8Array(0),\n ]);\n } catch (err) {\n const error = err as Error;\n throw new Error(`[NotFoundError]${error.message}`);\n }\n }\n return;\n }\n\n // Check file exists\n const statId = getCallbackId(\"stat\");\n if (statId !== undefined) {\n try {\n const result = (await invokeClientCallback(conn, statId, [\n fullPath,\n ])) as { isFile: boolean };\n if (!result.isFile) {\n throw new Error(`[TypeMismatchError]Not a file: ${fullPath}`);\n }\n } catch (err) {\n const error = err as Error;\n if (error.message.includes(\"TypeMismatchError\")) throw error;\n throw new Error(`[NotFoundError]File not found: ${fullPath}`);\n }\n }\n },\n\n async getDirectoryHandle(path: string, opts?: { create?: boolean }): Promise<void> {\n const fullPath = resolvePath(path);\n const conn = getConnection();\n\n if (opts?.create) {\n const mkdirId = getCallbackId(\"mkdir\");\n if (mkdirId !== undefined) {\n try {\n await invokeClientCallback(conn, mkdirId, [\n fullPath,\n { recursive: true },\n ]);\n } catch {\n // Ignore error if directory already exists\n }\n }\n return;\n }\n\n // Check directory exists\n const statId = getCallbackId(\"stat\");\n if (statId !== undefined) {\n try {\n const result = (await invokeClientCallback(conn, statId, [\n fullPath,\n ])) as { isDirectory: boolean };\n if (!result.isDirectory) {\n throw new Error(`[TypeMismatchError]Not a directory: ${fullPath}`);\n }\n } catch (err) {\n const error = err as Error;\n if (error.message.includes(\"TypeMismatchError\")) throw error;\n throw new Error(`[NotFoundError]Directory not found: ${fullPath}`);\n }\n }\n },\n\n async removeEntry(path: string, opts?: { recursive?: boolean }): Promise<void> {\n const fullPath = resolvePath(path);\n const conn = getConnection();\n\n // Check if it's a file or directory\n let isFile = true;\n const statId = getCallbackId(\"stat\");\n if (statId !== undefined) {\n try {\n const result = (await invokeClientCallback(conn, statId, [\n fullPath,\n ])) as { isFile: boolean; isDirectory: boolean };\n isFile = result.isFile;\n } catch {\n throw new Error(`[NotFoundError]Entry not found: ${fullPath}`);\n }\n }\n\n if (isFile) {\n const unlinkId = getCallbackId(\"unlink\");\n if (unlinkId === undefined) {\n throw new Error(`[NotAllowedError]File deletion not supported`);\n }\n await invokeClientCallback(conn, unlinkId, [fullPath]);\n } else {\n const rmdirId = getCallbackId(\"rmdir\");\n if (rmdirId === undefined) {\n throw new Error(`[NotAllowedError]Directory deletion not supported`);\n }\n // Note: recursive option may need special handling\n await invokeClientCallback(conn, rmdirId, [fullPath]);\n }\n },\n\n async readDirectory(\n path: string\n ): Promise<Array<{ name: string; kind: \"file\" | \"directory\" }>> {\n const fullPath = resolvePath(path);\n const conn = getConnection();\n\n const readdirId = getCallbackId(\"readdir\");\n if (readdirId === undefined) {\n throw new Error(`[NotAllowedError]Directory reading not supported`);\n }\n\n const entries = (await invokeClientCallback(conn, readdirId, [\n fullPath,\n ])) as string[];\n\n // We need to stat each entry to determine if it's a file or directory\n const result: Array<{ name: string; kind: \"file\" | \"directory\" }> = [];\n\n const statId = getCallbackId(\"stat\");\n for (const name of entries) {\n const entryPath = fullPath ? `${fullPath}/${name}` : name;\n let kind: \"file\" | \"directory\" = \"file\";\n\n if (statId !== undefined) {\n try {\n const stat = (await invokeClientCallback(conn, statId, [\n entryPath,\n ])) as { isFile: boolean; isDirectory: boolean };\n kind = stat.isDirectory ? \"directory\" : \"file\";\n } catch {\n // Default to file if stat fails\n }\n }\n\n result.push({ name, kind });\n }\n\n return result;\n },\n\n async readFile(\n path: string\n ): Promise<{ data: Uint8Array; size: number; lastModified: number; type: string }> {\n const fullPath = resolvePath(path);\n const conn = getConnection();\n\n const readFileId = getCallbackId(\"readFile\");\n if (readFileId === undefined) {\n throw new Error(`[NotAllowedError]File reading not supported`);\n }\n\n const data = (await invokeClientCallback(conn, readFileId, [\n fullPath,\n ])) as Uint8Array | ArrayBuffer | number[];\n\n // Convert to Uint8Array if needed\n let bytes: Uint8Array;\n if (data instanceof Uint8Array) {\n bytes = data;\n } else if (Array.isArray(data)) {\n bytes = new Uint8Array(data);\n } else if (data instanceof ArrayBuffer) {\n bytes = new Uint8Array(data);\n } else {\n bytes = new Uint8Array(0);\n }\n\n // Get metadata if stat is available\n let size = bytes.length;\n let lastModified = Date.now();\n\n const statId = getCallbackId(\"stat\");\n if (statId !== undefined) {\n try {\n const stat = (await invokeClientCallback(conn, statId, [\n fullPath,\n ])) as { size: number; lastModified?: number };\n size = stat.size;\n if (stat.lastModified) {\n lastModified = stat.lastModified;\n }\n } catch {\n // Use byte length as fallback\n }\n }\n\n // Determine MIME type from extension\n const ext = path.split(\".\").pop()?.toLowerCase() || \"\";\n const type = MIME_TYPES[ext] || \"application/octet-stream\";\n\n return { data: bytes, size, lastModified, type };\n },\n\n async writeFile(path: string, data: Uint8Array, position?: number): Promise<void> {\n const fullPath = resolvePath(path);\n const conn = getConnection();\n\n const writeFileId = getCallbackId(\"writeFile\");\n if (writeFileId === undefined) {\n throw new Error(`[NotAllowedError]File writing not supported`);\n }\n\n // Note: position parameter for partial writes may need special handling\n // Simple implementation overwrites entire file\n if (position !== undefined && position > 0) {\n // For positional writes, we need to read existing content and merge\n const readFileId = getCallbackId(\"readFile\");\n if (readFileId !== undefined) {\n try {\n const existing = (await invokeClientCallback(\n conn,\n readFileId,\n [fullPath]\n )) as Uint8Array | ArrayBuffer | number[];\n\n let existingBytes: Uint8Array;\n if (existing instanceof Uint8Array) {\n existingBytes = existing;\n } else if (Array.isArray(existing)) {\n existingBytes = new Uint8Array(existing);\n } else if (existing instanceof ArrayBuffer) {\n existingBytes = new Uint8Array(existing);\n } else {\n existingBytes = new Uint8Array(0);\n }\n\n // Create merged buffer\n const newSize = Math.max(existingBytes.length, position + data.length);\n const merged = new Uint8Array(newSize);\n merged.set(existingBytes);\n merged.set(data, position);\n\n await invokeClientCallback(conn, writeFileId, [\n fullPath,\n merged,\n ]);\n return;\n } catch {\n // File doesn't exist, create new one at position\n const newData = new Uint8Array(position + data.length);\n newData.set(data, position);\n await invokeClientCallback(conn, writeFileId, [\n fullPath,\n newData,\n ]);\n return;\n }\n }\n }\n\n await invokeClientCallback(conn, writeFileId, [fullPath, data]);\n },\n\n async truncateFile(path: string, size: number): Promise<void> {\n const fullPath = resolvePath(path);\n const conn = getConnection();\n\n const readFileId = getCallbackId(\"readFile\");\n const writeFileId = getCallbackId(\"writeFile\");\n if (readFileId === undefined || writeFileId === undefined) {\n throw new Error(`[NotAllowedError]File truncation not supported`);\n }\n\n // Read existing content\n const existing = (await invokeClientCallback(conn, readFileId, [\n fullPath,\n ])) as Uint8Array | ArrayBuffer | number[];\n\n let existingBytes: Uint8Array;\n if (existing instanceof Uint8Array) {\n existingBytes = existing;\n } else if (Array.isArray(existing)) {\n existingBytes = new Uint8Array(existing);\n } else if (existing instanceof ArrayBuffer) {\n existingBytes = new Uint8Array(existing);\n } else {\n existingBytes = new Uint8Array(0);\n }\n\n // Create truncated buffer\n const truncated = new Uint8Array(size);\n truncated.set(existingBytes.slice(0, size));\n\n await invokeClientCallback(conn, writeFileId, [fullPath, truncated]);\n },\n\n async getFileMetadata(\n path: string\n ): Promise<{ size: number; lastModified: number; type: string }> {\n const fullPath = resolvePath(path);\n const conn = getConnection();\n\n const statId = getCallbackId(\"stat\");\n if (statId === undefined) {\n throw new Error(`[NotAllowedError]File stat not supported`);\n }\n\n const stat = (await invokeClientCallback(conn, statId, [\n fullPath,\n ])) as { size: number; lastModified?: number; isFile: boolean };\n\n if (!stat.isFile) {\n throw new Error(`[TypeMismatchError]Not a file: ${fullPath}`);\n }\n\n // Determine MIME type from extension\n const ext = path.split(\".\").pop()?.toLowerCase() || \"\";\n const type = MIME_TYPES[ext] || \"application/octet-stream\";\n\n return {\n size: stat.size,\n lastModified: stat.lastModified ?? Date.now(),\n type,\n };\n },\n };\n}\n"
|
|
5
|
+
"/**\n * Callback-based FileSystemHandler adapter.\n *\n * Adapts simple client callbacks (readFile, writeFile, etc.) to the\n * FileSystemHandler interface used by @ricsam/isolate-fs.\n */\n\nimport type { FileSystemHandler } from \"@ricsam/isolate-fs\";\nimport type { ConnectionState, CallbackContext } from \"./types.mjs\";\n\n/** Common MIME type mappings by file extension. */\nconst MIME_TYPES: Record<string, string> = {\n txt: \"text/plain\",\n html: \"text/html\",\n htm: \"text/html\",\n css: \"text/css\",\n js: \"text/javascript\",\n json: \"application/json\",\n xml: \"application/xml\",\n png: \"image/png\",\n jpg: \"image/jpeg\",\n jpeg: \"image/jpeg\",\n gif: \"image/gif\",\n svg: \"image/svg+xml\",\n pdf: \"application/pdf\",\n};\n\ninterface InvokeClientCallback {\n (connection: ConnectionState, callbackId: number, args: unknown[]): Promise<unknown>;\n}\n\ninterface CallbackFsHandlerOptions {\n connection: ConnectionState;\n callbackContext: CallbackContext;\n invokeClientCallback: InvokeClientCallback;\n basePath?: string;\n}\n\n/**\n * Create a FileSystemHandler that invokes client callbacks.\n *\n * Maps WHATWG FileSystem API operations to simple POSIX-like callbacks.\n * Uses callbackContext for dynamic callback ID lookup to support runtime reuse.\n */\nexport function createCallbackFileSystemHandler(\n options: CallbackFsHandlerOptions\n): FileSystemHandler {\n const { connection, callbackContext, invokeClientCallback, basePath = \"\" } = options;\n\n const resolvePath = (path: string): string => {\n // Remove leading slash from the path\n const cleanPath = path.startsWith(\"/\") ? path.slice(1) : path;\n // Handle root case\n if (!basePath || basePath === \"/\") {\n return `/${cleanPath}`;\n }\n // Remove trailing slash from basePath\n const cleanBase = basePath.endsWith(\"/\") ? basePath.slice(0, -1) : basePath;\n return `${cleanBase}/${cleanPath}`;\n };\n\n // Helper to get current callback ID (supports runtime reuse)\n const getCallbackId = (name: keyof CallbackContext[\"fs\"]): number | undefined => {\n return callbackContext.fs[name];\n };\n\n // Helper to get current connection (supports runtime reuse and reconnection)\n const getConnection = async (): Promise<ConnectionState> => {\n if (callbackContext.connection) {\n return callbackContext.connection;\n }\n if (callbackContext.reconnectionPromise) {\n return callbackContext.reconnectionPromise.promise;\n }\n // Fall back to the originally captured connection\n return connection;\n };\n\n return {\n async getFileHandle(path: string, opts?: { create?: boolean }): Promise<void> {\n const fullPath = resolvePath(path);\n const conn = await getConnection();\n\n if (opts?.create) {\n // Ensure file exists by writing empty content if it doesn't exist\n const writeFileId = getCallbackId(\"writeFile\");\n if (writeFileId !== undefined) {\n try {\n // Check if file exists first\n const statId = getCallbackId(\"stat\");\n if (statId !== undefined) {\n try {\n await invokeClientCallback(conn, statId, [fullPath]);\n // File exists, nothing to do\n return;\n } catch {\n // File doesn't exist, create it\n }\n }\n // Create empty file\n await invokeClientCallback(conn, writeFileId, [\n fullPath,\n new Uint8Array(0),\n ]);\n } catch (err) {\n const error = err as Error;\n throw new Error(`[NotFoundError]${error.message}`);\n }\n }\n return;\n }\n\n // Check file exists\n const statId = getCallbackId(\"stat\");\n if (statId !== undefined) {\n try {\n const result = (await invokeClientCallback(conn, statId, [\n fullPath,\n ])) as { isFile: boolean };\n if (!result.isFile) {\n throw new Error(`[TypeMismatchError]Not a file: ${fullPath}`);\n }\n } catch (err) {\n const error = err as Error;\n if (error.message.includes(\"TypeMismatchError\")) throw error;\n throw new Error(`[NotFoundError]File not found: ${fullPath}`);\n }\n }\n },\n\n async getDirectoryHandle(path: string, opts?: { create?: boolean }): Promise<void> {\n const fullPath = resolvePath(path);\n const conn = await getConnection();\n\n if (opts?.create) {\n const mkdirId = getCallbackId(\"mkdir\");\n if (mkdirId !== undefined) {\n try {\n await invokeClientCallback(conn, mkdirId, [\n fullPath,\n { recursive: true },\n ]);\n } catch {\n // Ignore error if directory already exists\n }\n }\n return;\n }\n\n // Check directory exists\n const statId = getCallbackId(\"stat\");\n if (statId !== undefined) {\n try {\n const result = (await invokeClientCallback(conn, statId, [\n fullPath,\n ])) as { isDirectory: boolean };\n if (!result.isDirectory) {\n throw new Error(`[TypeMismatchError]Not a directory: ${fullPath}`);\n }\n } catch (err) {\n const error = err as Error;\n if (error.message.includes(\"TypeMismatchError\")) throw error;\n throw new Error(`[NotFoundError]Directory not found: ${fullPath}`);\n }\n }\n },\n\n async removeEntry(path: string, opts?: { recursive?: boolean }): Promise<void> {\n const fullPath = resolvePath(path);\n const conn = await getConnection();\n\n // Check if it's a file or directory\n let isFile = true;\n const statId = getCallbackId(\"stat\");\n if (statId !== undefined) {\n try {\n const result = (await invokeClientCallback(conn, statId, [\n fullPath,\n ])) as { isFile: boolean; isDirectory: boolean };\n isFile = result.isFile;\n } catch {\n throw new Error(`[NotFoundError]Entry not found: ${fullPath}`);\n }\n }\n\n if (isFile) {\n const unlinkId = getCallbackId(\"unlink\");\n if (unlinkId === undefined) {\n throw new Error(`[NotAllowedError]File deletion not supported`);\n }\n await invokeClientCallback(conn, unlinkId, [fullPath]);\n } else {\n const rmdirId = getCallbackId(\"rmdir\");\n if (rmdirId === undefined) {\n throw new Error(`[NotAllowedError]Directory deletion not supported`);\n }\n // Note: recursive option may need special handling\n await invokeClientCallback(conn, rmdirId, [fullPath]);\n }\n },\n\n async readDirectory(\n path: string\n ): Promise<Array<{ name: string; kind: \"file\" | \"directory\" }>> {\n const fullPath = resolvePath(path);\n const conn = await getConnection();\n\n const readdirId = getCallbackId(\"readdir\");\n if (readdirId === undefined) {\n throw new Error(`[NotAllowedError]Directory reading not supported`);\n }\n\n const entries = (await invokeClientCallback(conn, readdirId, [\n fullPath,\n ])) as string[];\n\n // We need to stat each entry to determine if it's a file or directory\n const result: Array<{ name: string; kind: \"file\" | \"directory\" }> = [];\n\n const statId = getCallbackId(\"stat\");\n for (const name of entries) {\n const entryPath = fullPath ? `${fullPath}/${name}` : name;\n let kind: \"file\" | \"directory\" = \"file\";\n\n if (statId !== undefined) {\n try {\n const stat = (await invokeClientCallback(conn, statId, [\n entryPath,\n ])) as { isFile: boolean; isDirectory: boolean };\n kind = stat.isDirectory ? \"directory\" : \"file\";\n } catch {\n // Default to file if stat fails\n }\n }\n\n result.push({ name, kind });\n }\n\n return result;\n },\n\n async readFile(\n path: string\n ): Promise<{ data: Uint8Array; size: number; lastModified: number; type: string }> {\n const fullPath = resolvePath(path);\n const conn = await getConnection();\n\n const readFileId = getCallbackId(\"readFile\");\n if (readFileId === undefined) {\n throw new Error(`[NotAllowedError]File reading not supported`);\n }\n\n const data = (await invokeClientCallback(conn, readFileId, [\n fullPath,\n ])) as Uint8Array | ArrayBuffer | number[];\n\n // Convert to Uint8Array if needed\n let bytes: Uint8Array;\n if (data instanceof Uint8Array) {\n bytes = data;\n } else if (Array.isArray(data)) {\n bytes = new Uint8Array(data);\n } else if (data instanceof ArrayBuffer) {\n bytes = new Uint8Array(data);\n } else {\n bytes = new Uint8Array(0);\n }\n\n // Get metadata if stat is available\n let size = bytes.length;\n let lastModified = Date.now();\n\n const statId = getCallbackId(\"stat\");\n if (statId !== undefined) {\n try {\n const stat = (await invokeClientCallback(conn, statId, [\n fullPath,\n ])) as { size: number; lastModified?: number };\n size = stat.size;\n if (stat.lastModified) {\n lastModified = stat.lastModified;\n }\n } catch {\n // Use byte length as fallback\n }\n }\n\n // Determine MIME type from extension\n const ext = path.split(\".\").pop()?.toLowerCase() || \"\";\n const type = MIME_TYPES[ext] || \"application/octet-stream\";\n\n return { data: bytes, size, lastModified, type };\n },\n\n async writeFile(path: string, data: Uint8Array, position?: number): Promise<void> {\n const fullPath = resolvePath(path);\n const conn = await getConnection();\n\n const writeFileId = getCallbackId(\"writeFile\");\n if (writeFileId === undefined) {\n throw new Error(`[NotAllowedError]File writing not supported`);\n }\n\n // Note: position parameter for partial writes may need special handling\n // Simple implementation overwrites entire file\n if (position !== undefined && position > 0) {\n // For positional writes, we need to read existing content and merge\n const readFileId = getCallbackId(\"readFile\");\n if (readFileId !== undefined) {\n try {\n const existing = (await invokeClientCallback(\n conn,\n readFileId,\n [fullPath]\n )) as Uint8Array | ArrayBuffer | number[];\n\n let existingBytes: Uint8Array;\n if (existing instanceof Uint8Array) {\n existingBytes = existing;\n } else if (Array.isArray(existing)) {\n existingBytes = new Uint8Array(existing);\n } else if (existing instanceof ArrayBuffer) {\n existingBytes = new Uint8Array(existing);\n } else {\n existingBytes = new Uint8Array(0);\n }\n\n // Create merged buffer\n const newSize = Math.max(existingBytes.length, position + data.length);\n const merged = new Uint8Array(newSize);\n merged.set(existingBytes);\n merged.set(data, position);\n\n await invokeClientCallback(conn, writeFileId, [\n fullPath,\n merged,\n ]);\n return;\n } catch {\n // File doesn't exist, create new one at position\n const newData = new Uint8Array(position + data.length);\n newData.set(data, position);\n await invokeClientCallback(conn, writeFileId, [\n fullPath,\n newData,\n ]);\n return;\n }\n }\n }\n\n await invokeClientCallback(conn, writeFileId, [fullPath, data]);\n },\n\n async truncateFile(path: string, size: number): Promise<void> {\n const fullPath = resolvePath(path);\n const conn = await getConnection();\n\n const readFileId = getCallbackId(\"readFile\");\n const writeFileId = getCallbackId(\"writeFile\");\n if (readFileId === undefined || writeFileId === undefined) {\n throw new Error(`[NotAllowedError]File truncation not supported`);\n }\n\n // Read existing content\n const existing = (await invokeClientCallback(conn, readFileId, [\n fullPath,\n ])) as Uint8Array | ArrayBuffer | number[];\n\n let existingBytes: Uint8Array;\n if (existing instanceof Uint8Array) {\n existingBytes = existing;\n } else if (Array.isArray(existing)) {\n existingBytes = new Uint8Array(existing);\n } else if (existing instanceof ArrayBuffer) {\n existingBytes = new Uint8Array(existing);\n } else {\n existingBytes = new Uint8Array(0);\n }\n\n // Create truncated buffer\n const truncated = new Uint8Array(size);\n truncated.set(existingBytes.slice(0, size));\n\n await invokeClientCallback(conn, writeFileId, [fullPath, truncated]);\n },\n\n async getFileMetadata(\n path: string\n ): Promise<{ size: number; lastModified: number; type: string }> {\n const fullPath = resolvePath(path);\n const conn = await getConnection();\n\n const statId = getCallbackId(\"stat\");\n if (statId === undefined) {\n throw new Error(`[NotAllowedError]File stat not supported`);\n }\n\n const stat = (await invokeClientCallback(conn, statId, [\n fullPath,\n ])) as { size: number; lastModified?: number; isFile: boolean };\n\n if (!stat.isFile) {\n throw new Error(`[TypeMismatchError]Not a file: ${fullPath}`);\n }\n\n // Determine MIME type from extension\n const ext = path.split(\".\").pop()?.toLowerCase() || \"\";\n const type = MIME_TYPES[ext] || \"application/octet-stream\";\n\n return {\n size: stat.size,\n lastModified: stat.lastModified ?? Date.now(),\n type,\n };\n },\n };\n}\n"
|
|
6
6
|
],
|
|
7
|
-
"mappings": ";AAWA,IAAM,aAAqC;AAAA,EACzC,KAAK;AAAA,EACL,MAAM;AAAA,EACN,KAAK;AAAA,EACL,KAAK;AAAA,EACL,IAAI;AAAA,EACJ,MAAM;AAAA,EACN,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,MAAM;AAAA,EACN,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AACP;AAmBO,SAAS,+BAA+B,CAC7C,SACmB;AAAA,EACnB,QAAQ,YAAY,iBAAiB,sBAAsB,WAAW,OAAO;AAAA,EAE7E,MAAM,cAAc,CAAC,SAAyB;AAAA,IAE5C,MAAM,YAAY,KAAK,WAAW,GAAG,IAAI,KAAK,MAAM,CAAC,IAAI;AAAA,IAEzD,IAAI,CAAC,YAAY,aAAa,KAAK;AAAA,MACjC,OAAO,IAAI;AAAA,IACb;AAAA,IAEA,MAAM,YAAY,SAAS,SAAS,GAAG,IAAI,SAAS,MAAM,GAAG,EAAE,IAAI;AAAA,IACnE,OAAO,GAAG,aAAa;AAAA;AAAA,EAIzB,MAAM,gBAAgB,CAAC,SAA0D;AAAA,IAC/E,OAAO,gBAAgB,GAAG;AAAA;AAAA,EAI5B,MAAM,gBAAgB,
|
|
8
|
-
"debugId": "
|
|
7
|
+
"mappings": "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",
|
|
8
|
+
"debugId": "629A15509C1B27EB64756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
package/dist/mjs/connection.mjs
CHANGED
|
@@ -51,7 +51,9 @@ function handleConnection(socket, state) {
|
|
|
51
51
|
nextStreamId: 1,
|
|
52
52
|
activeStreams: new Map,
|
|
53
53
|
streamReceivers: new Map,
|
|
54
|
-
callbackStreamReceivers: new Map
|
|
54
|
+
callbackStreamReceivers: new Map,
|
|
55
|
+
dispatchAbortControllers: new Map,
|
|
56
|
+
earlyAbortedDispatches: new Set
|
|
55
57
|
};
|
|
56
58
|
state.connections.set(socket, connection);
|
|
57
59
|
const parser = createFrameParser();
|
|
@@ -68,6 +70,11 @@ function handleConnection(socket, state) {
|
|
|
68
70
|
}
|
|
69
71
|
});
|
|
70
72
|
socket.on("close", () => {
|
|
73
|
+
for (const [, controller] of connection.dispatchAbortControllers) {
|
|
74
|
+
controller.abort();
|
|
75
|
+
}
|
|
76
|
+
connection.dispatchAbortControllers.clear();
|
|
77
|
+
connection.earlyAbortedDispatches.clear();
|
|
71
78
|
for (const isolateId of connection.isolates) {
|
|
72
79
|
const instance = state.isolates.get(isolateId);
|
|
73
80
|
if (instance) {
|
|
@@ -129,6 +136,9 @@ async function handleMessage(message, connection, state) {
|
|
|
129
136
|
case MessageType.DISPATCH_REQUEST:
|
|
130
137
|
await handleDispatchRequest(message, connection, state);
|
|
131
138
|
break;
|
|
139
|
+
case MessageType.DISPATCH_REQUEST_ABORT:
|
|
140
|
+
handleDispatchRequestAbort(message, connection);
|
|
141
|
+
break;
|
|
132
142
|
case MessageType.CALLBACK_RESPONSE:
|
|
133
143
|
handleCallbackResponse(message, connection);
|
|
134
144
|
break;
|
|
@@ -232,16 +242,42 @@ async function hardDeleteRuntime(instance, state) {
|
|
|
232
242
|
instance.disposedAt = undefined;
|
|
233
243
|
instance.ownerConnection = null;
|
|
234
244
|
if (instance.callbackContext) {
|
|
245
|
+
if (instance.callbackContext.reconnectionPromise) {
|
|
246
|
+
clearTimeout(instance.callbackContext.reconnectionPromise.timeoutId);
|
|
247
|
+
instance.callbackContext.reconnectionPromise.promise.catch(() => {});
|
|
248
|
+
instance.callbackContext.reconnectionPromise.reject(new Error("Runtime was permanently disposed"));
|
|
249
|
+
instance.callbackContext.reconnectionPromise = undefined;
|
|
250
|
+
}
|
|
235
251
|
instance.callbackContext.connection = null;
|
|
236
252
|
}
|
|
237
253
|
}
|
|
238
254
|
}
|
|
255
|
+
var RECONNECTION_TIMEOUT_MS = 30000;
|
|
239
256
|
function softDeleteRuntime(instance, state) {
|
|
240
257
|
instance.isDisposed = true;
|
|
241
258
|
instance.disposedAt = Date.now();
|
|
242
259
|
instance.ownerConnection = null;
|
|
243
260
|
if (instance.callbackContext) {
|
|
244
261
|
instance.callbackContext.connection = null;
|
|
262
|
+
let resolve;
|
|
263
|
+
let reject;
|
|
264
|
+
const promise = new Promise((res, rej) => {
|
|
265
|
+
resolve = res;
|
|
266
|
+
reject = rej;
|
|
267
|
+
});
|
|
268
|
+
promise.catch(() => {});
|
|
269
|
+
const timeoutId = setTimeout(() => {
|
|
270
|
+
if (instance.callbackContext?.reconnectionPromise) {
|
|
271
|
+
instance.callbackContext.reconnectionPromise = undefined;
|
|
272
|
+
reject(new Error("Reconnection timeout: no client reconnected within timeout"));
|
|
273
|
+
}
|
|
274
|
+
}, RECONNECTION_TIMEOUT_MS);
|
|
275
|
+
instance.callbackContext.reconnectionPromise = {
|
|
276
|
+
promise,
|
|
277
|
+
resolve,
|
|
278
|
+
reject,
|
|
279
|
+
timeoutId
|
|
280
|
+
};
|
|
245
281
|
}
|
|
246
282
|
instance.callbacks.clear();
|
|
247
283
|
instance.runtime.timers.clearAll();
|
|
@@ -263,6 +299,11 @@ function reuseNamespacedRuntime(instance, connection, message, state) {
|
|
|
263
299
|
const testEnvOptions = message.options.testEnvironment != null && typeof message.options.testEnvironment === "object" ? message.options.testEnvironment : undefined;
|
|
264
300
|
if (instance.callbackContext) {
|
|
265
301
|
instance.callbackContext.connection = connection;
|
|
302
|
+
if (instance.callbackContext.reconnectionPromise) {
|
|
303
|
+
clearTimeout(instance.callbackContext.reconnectionPromise.timeoutId);
|
|
304
|
+
instance.callbackContext.reconnectionPromise.resolve(connection);
|
|
305
|
+
instance.callbackContext.reconnectionPromise = undefined;
|
|
306
|
+
}
|
|
266
307
|
instance.callbackContext.consoleOnEntry = callbacks?.console?.onEntry?.callbackId;
|
|
267
308
|
instance.callbackContext.fetch = callbacks?.fetch?.callbackId;
|
|
268
309
|
instance.callbackContext.moduleLoader = callbacks?.moduleLoader?.callbackId;
|
|
@@ -357,6 +398,35 @@ function reuseNamespacedRuntime(instance, connection, message, state) {
|
|
|
357
398
|
instance.returnedIterators = new Map;
|
|
358
399
|
instance.nextLocalCallbackId = 1e6;
|
|
359
400
|
}
|
|
401
|
+
async function waitForConnection(callbackContext) {
|
|
402
|
+
if (callbackContext.connection) {
|
|
403
|
+
return callbackContext.connection;
|
|
404
|
+
}
|
|
405
|
+
if (callbackContext.reconnectionPromise) {
|
|
406
|
+
return callbackContext.reconnectionPromise.promise;
|
|
407
|
+
}
|
|
408
|
+
throw new Error("No connection available and no reconnection pending");
|
|
409
|
+
}
|
|
410
|
+
async function invokeCallbackWithReconnect(callbackContext, getCallbackId, args, label, invokeClientCallback) {
|
|
411
|
+
const conn = await waitForConnection(callbackContext);
|
|
412
|
+
const cbId = getCallbackId();
|
|
413
|
+
if (cbId === undefined) {
|
|
414
|
+
throw new Error(`${label} callback not available`);
|
|
415
|
+
}
|
|
416
|
+
try {
|
|
417
|
+
return await invokeClientCallback(conn, cbId, args);
|
|
418
|
+
} catch (err) {
|
|
419
|
+
if (callbackContext.reconnectionPromise && !callbackContext.connection) {
|
|
420
|
+
const newConn = await callbackContext.reconnectionPromise.promise;
|
|
421
|
+
const newCbId = getCallbackId();
|
|
422
|
+
if (newCbId === undefined) {
|
|
423
|
+
throw new Error(`${label} callback not available after reconnection`);
|
|
424
|
+
}
|
|
425
|
+
return invokeClientCallback(newConn, newCbId, args);
|
|
426
|
+
}
|
|
427
|
+
throw err;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
360
430
|
async function evictOldestDisposedRuntime(state) {
|
|
361
431
|
let oldest = null;
|
|
362
432
|
let oldestTime = Infinity;
|
|
@@ -543,11 +613,16 @@ async function handleCreateRuntime(message, connection, state) {
|
|
|
543
613
|
}
|
|
544
614
|
return await callback(...args);
|
|
545
615
|
} else {
|
|
546
|
-
const conn = callbackContext
|
|
547
|
-
|
|
548
|
-
|
|
616
|
+
const conn = await waitForConnection(callbackContext);
|
|
617
|
+
try {
|
|
618
|
+
return await invokeClientCallback(conn, callbackId, args);
|
|
619
|
+
} catch (err) {
|
|
620
|
+
if (callbackContext.reconnectionPromise && !callbackContext.connection) {
|
|
621
|
+
const newConn = await callbackContext.reconnectionPromise.promise;
|
|
622
|
+
return invokeClientCallback(newConn, callbackId, args);
|
|
623
|
+
}
|
|
624
|
+
throw err;
|
|
549
625
|
}
|
|
550
|
-
return invokeClientCallback(conn, callbackId, args);
|
|
551
626
|
}
|
|
552
627
|
};
|
|
553
628
|
customFnMarshalOptions = { createMarshalContext, addCallbackIdsToRefs, invokeCallback };
|
|
@@ -564,27 +639,20 @@ async function handleCreateRuntime(message, connection, state) {
|
|
|
564
639
|
const nextCallbackId = callbackContext_.custom.get(`${name}:next`);
|
|
565
640
|
const returnCallbackId = callbackContext_.custom.get(`${name}:return`);
|
|
566
641
|
async function* bridgedIterator() {
|
|
567
|
-
const
|
|
568
|
-
if (!conn || startCallbackId === undefined) {
|
|
569
|
-
throw new Error(`AsyncIterator callback '${name}' not available`);
|
|
570
|
-
}
|
|
571
|
-
const startResult = await invokeClientCallback(conn, startCallbackId, args);
|
|
642
|
+
const startResult = await invokeCallbackWithReconnect(callbackContext_, () => callbackContext_.custom.get(`${name}:start`), args, `AsyncIterator '${name}' start`, invokeClientCallback);
|
|
572
643
|
const iteratorId = startResult.iteratorId;
|
|
573
644
|
try {
|
|
574
645
|
while (true) {
|
|
575
|
-
const
|
|
576
|
-
if (!nextConn || nextCallbackId === undefined) {
|
|
577
|
-
throw new Error(`AsyncIterator callback '${name}' not available`);
|
|
578
|
-
}
|
|
579
|
-
const nextResult = await invokeClientCallback(nextConn, nextCallbackId, [iteratorId]);
|
|
646
|
+
const nextResult = await invokeCallbackWithReconnect(callbackContext_, () => callbackContext_.custom.get(`${name}:next`), [iteratorId], `AsyncIterator '${name}' next`, invokeClientCallback);
|
|
580
647
|
if (nextResult.done)
|
|
581
648
|
return nextResult.value;
|
|
582
649
|
yield nextResult.value;
|
|
583
650
|
}
|
|
584
651
|
} finally {
|
|
585
652
|
const retConn = callbackContext_.connection;
|
|
586
|
-
|
|
587
|
-
|
|
653
|
+
const retCbId = callbackContext_.custom.get(`${name}:return`);
|
|
654
|
+
if (retConn && retCbId !== undefined) {
|
|
655
|
+
await invokeClientCallback(retConn, retCbId, [iteratorId]).catch(() => {});
|
|
588
656
|
}
|
|
589
657
|
}
|
|
590
658
|
}
|
|
@@ -595,12 +663,7 @@ async function handleCreateRuntime(message, connection, state) {
|
|
|
595
663
|
bridgedCustomFunctions[name] = {
|
|
596
664
|
type: registration.type,
|
|
597
665
|
fn: async (...args) => {
|
|
598
|
-
|
|
599
|
-
const cbId = callbackContext_.custom.get(name);
|
|
600
|
-
if (!conn || cbId === undefined) {
|
|
601
|
-
throw new Error(`Custom function callback '${name}' not available`);
|
|
602
|
-
}
|
|
603
|
-
return invokeClientCallback(conn, cbId, args);
|
|
666
|
+
return invokeCallbackWithReconnect(callbackContext_, () => callbackContext_.custom.get(name), args, `Custom function '${name}'`, invokeClientCallback);
|
|
604
667
|
}
|
|
605
668
|
};
|
|
606
669
|
}
|
|
@@ -609,12 +672,7 @@ async function handleCreateRuntime(message, connection, state) {
|
|
|
609
672
|
let moduleLoader;
|
|
610
673
|
if (moduleLoaderCallback) {
|
|
611
674
|
moduleLoader = async (specifier, importer) => {
|
|
612
|
-
|
|
613
|
-
const cbId = callbackContext.moduleLoader;
|
|
614
|
-
if (!conn || cbId === undefined) {
|
|
615
|
-
throw new Error("Module loader callback not available");
|
|
616
|
-
}
|
|
617
|
-
return invokeClientCallback(conn, cbId, [specifier, importer]);
|
|
675
|
+
return invokeCallbackWithReconnect(callbackContext, () => callbackContext.moduleLoader, [specifier, importer], "Module loader", invokeClientCallback);
|
|
618
676
|
};
|
|
619
677
|
}
|
|
620
678
|
let testEnvironment;
|
|
@@ -645,19 +703,8 @@ async function handleCreateRuntime(message, connection, state) {
|
|
|
645
703
|
if (playwrightCallbacks) {
|
|
646
704
|
playwrightOptions = {
|
|
647
705
|
handler: async (op) => {
|
|
648
|
-
const conn = callbackContext.connection;
|
|
649
|
-
const callbackId = callbackContext.playwright.handlerCallbackId;
|
|
650
|
-
if (!conn || callbackId === undefined) {
|
|
651
|
-
return {
|
|
652
|
-
ok: false,
|
|
653
|
-
error: {
|
|
654
|
-
name: "Error",
|
|
655
|
-
message: "Playwright handler callback not available"
|
|
656
|
-
}
|
|
657
|
-
};
|
|
658
|
-
}
|
|
659
706
|
try {
|
|
660
|
-
const resultJson = await
|
|
707
|
+
const resultJson = await invokeCallbackWithReconnect(callbackContext, () => callbackContext.playwright.handlerCallbackId, [JSON.stringify(op)], "Playwright handler", invokeClientCallback);
|
|
661
708
|
return JSON.parse(resultJson);
|
|
662
709
|
} catch (err) {
|
|
663
710
|
const error = err;
|
|
@@ -697,18 +744,14 @@ async function handleCreateRuntime(message, connection, state) {
|
|
|
697
744
|
}
|
|
698
745
|
},
|
|
699
746
|
fetch: async (url, init) => {
|
|
700
|
-
const conn = callbackContext.connection;
|
|
701
|
-
const callbackId = callbackContext.fetch;
|
|
702
|
-
if (!conn || callbackId === undefined) {
|
|
703
|
-
throw new Error("Fetch callback not available");
|
|
704
|
-
}
|
|
705
747
|
const serialized = {
|
|
706
748
|
url,
|
|
707
749
|
method: init.method,
|
|
708
750
|
headers: init.headers,
|
|
709
|
-
body: init.rawBody
|
|
751
|
+
body: init.rawBody,
|
|
752
|
+
signalAborted: init.signal.aborted
|
|
710
753
|
};
|
|
711
|
-
const result = await
|
|
754
|
+
const result = await invokeCallbackWithReconnect(callbackContext, () => callbackContext.fetch, [serialized], "Fetch", invokeClientCallback);
|
|
712
755
|
if (result && typeof result === "object" && result.__streamingResponse) {
|
|
713
756
|
const response = result.response;
|
|
714
757
|
response.__isCallbackStream = true;
|
|
@@ -718,10 +761,7 @@ async function handleCreateRuntime(message, connection, state) {
|
|
|
718
761
|
},
|
|
719
762
|
fs: {
|
|
720
763
|
getDirectory: async (dirPath) => {
|
|
721
|
-
const conn = callbackContext
|
|
722
|
-
if (!conn) {
|
|
723
|
-
throw new Error("FS callbacks not available");
|
|
724
|
-
}
|
|
764
|
+
const conn = await waitForConnection(callbackContext);
|
|
725
765
|
return createCallbackFileSystemHandler({
|
|
726
766
|
connection: conn,
|
|
727
767
|
callbackContext,
|
|
@@ -949,6 +989,12 @@ async function handleDispatchRequest(message, connection, state) {
|
|
|
949
989
|
return;
|
|
950
990
|
}
|
|
951
991
|
instance.lastActivity = Date.now();
|
|
992
|
+
const dispatchAbortController = new AbortController;
|
|
993
|
+
connection.dispatchAbortControllers.set(message.requestId, dispatchAbortController);
|
|
994
|
+
if (connection.earlyAbortedDispatches.has(message.requestId) || message.request.signalAborted) {
|
|
995
|
+
dispatchAbortController.abort();
|
|
996
|
+
connection.earlyAbortedDispatches.delete(message.requestId);
|
|
997
|
+
}
|
|
952
998
|
try {
|
|
953
999
|
let requestBody = null;
|
|
954
1000
|
if (message.request.bodyStreamId !== undefined) {
|
|
@@ -959,9 +1005,12 @@ async function handleDispatchRequest(message, connection, state) {
|
|
|
959
1005
|
const request = new Request(message.request.url, {
|
|
960
1006
|
method: message.request.method,
|
|
961
1007
|
headers: message.request.headers,
|
|
962
|
-
body: requestBody
|
|
1008
|
+
body: requestBody,
|
|
1009
|
+
signal: dispatchAbortController.signal
|
|
1010
|
+
});
|
|
1011
|
+
const response = await instance.runtime.fetch.dispatchRequest(request, {
|
|
1012
|
+
signal: dispatchAbortController.signal
|
|
963
1013
|
});
|
|
964
|
-
const response = await instance.runtime.fetch.dispatchRequest(request);
|
|
965
1014
|
if (response.body) {
|
|
966
1015
|
await sendStreamedResponse(connection, message.requestId, response);
|
|
967
1016
|
} else {
|
|
@@ -981,7 +1030,18 @@ async function handleDispatchRequest(message, connection, state) {
|
|
|
981
1030
|
} catch (err) {
|
|
982
1031
|
const error = err;
|
|
983
1032
|
sendError(connection.socket, message.requestId, ErrorCode.SCRIPT_ERROR, error.message, { name: error.name, stack: error.stack });
|
|
1033
|
+
} finally {
|
|
1034
|
+
connection.dispatchAbortControllers.delete(message.requestId);
|
|
1035
|
+
connection.earlyAbortedDispatches.delete(message.requestId);
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
1038
|
+
function handleDispatchRequestAbort(message, connection) {
|
|
1039
|
+
const controller = connection.dispatchAbortControllers.get(message.targetRequestId);
|
|
1040
|
+
if (controller) {
|
|
1041
|
+
controller.abort();
|
|
1042
|
+
return;
|
|
984
1043
|
}
|
|
1044
|
+
connection.earlyAbortedDispatches.add(message.targetRequestId);
|
|
985
1045
|
}
|
|
986
1046
|
function receiveStreamedBody(connection, streamId) {
|
|
987
1047
|
return new Promise((resolve, reject) => {
|
|
@@ -1507,13 +1567,35 @@ async function handleRunTests(message, connection, state) {
|
|
|
1507
1567
|
return;
|
|
1508
1568
|
}
|
|
1509
1569
|
instance.lastActivity = Date.now();
|
|
1570
|
+
if (instance.pendingTestRun) {
|
|
1571
|
+
try {
|
|
1572
|
+
const results = await instance.pendingTestRun.promise;
|
|
1573
|
+
const currentConn = instance.callbackContext?.connection;
|
|
1574
|
+
if (currentConn) {
|
|
1575
|
+
sendOk(currentConn.socket, message.requestId, results);
|
|
1576
|
+
}
|
|
1577
|
+
} catch (err) {
|
|
1578
|
+
const error = err;
|
|
1579
|
+
const currentConn = instance.callbackContext?.connection;
|
|
1580
|
+
if (currentConn) {
|
|
1581
|
+
sendError(currentConn.socket, message.requestId, ErrorCode.SCRIPT_ERROR, error.message, { name: error.name, stack: error.stack });
|
|
1582
|
+
}
|
|
1583
|
+
}
|
|
1584
|
+
return;
|
|
1585
|
+
}
|
|
1510
1586
|
try {
|
|
1511
1587
|
const timeout = message.timeout ?? 30000;
|
|
1512
|
-
const
|
|
1513
|
-
|
|
1588
|
+
const runPromise = instance.runtime.testEnvironment.runTests(timeout);
|
|
1589
|
+
instance.pendingTestRun = { promise: runPromise };
|
|
1590
|
+
const results = await runPromise;
|
|
1591
|
+
instance.pendingTestRun = undefined;
|
|
1592
|
+
const currentConn = instance.callbackContext?.connection ?? connection;
|
|
1593
|
+
sendOk(currentConn.socket, message.requestId, results);
|
|
1514
1594
|
} catch (err) {
|
|
1595
|
+
instance.pendingTestRun = undefined;
|
|
1515
1596
|
const error = err;
|
|
1516
|
-
|
|
1597
|
+
const currentConn = instance.callbackContext?.connection ?? connection;
|
|
1598
|
+
sendError(currentConn.socket, message.requestId, ErrorCode.SCRIPT_ERROR, error.message, { name: error.name, stack: error.stack });
|
|
1517
1599
|
}
|
|
1518
1600
|
}
|
|
1519
1601
|
async function handleResetTestEnv(message, connection, state) {
|
|
@@ -1595,4 +1677,4 @@ export {
|
|
|
1595
1677
|
handleConnection
|
|
1596
1678
|
};
|
|
1597
1679
|
|
|
1598
|
-
//# debugId=
|
|
1680
|
+
//# debugId=23FC39B31009F9AA64756E2164756E21
|