@ricsam/isolate-daemon 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.
- package/README.md +27 -0
- package/dist/cjs/callback-fs-handler.cjs +68 -37
- package/dist/cjs/callback-fs-handler.cjs.map +3 -3
- package/dist/cjs/connection.cjs +255 -69
- package/dist/cjs/connection.cjs.map +3 -3
- package/dist/cjs/daemon.cjs +97 -0
- package/dist/cjs/daemon.cjs.map +10 -0
- package/dist/cjs/index.cjs +4 -2
- package/dist/cjs/index.cjs.map +3 -3
- package/dist/cjs/package.json +1 -1
- package/dist/mjs/callback-fs-handler.mjs +68 -37
- package/dist/mjs/callback-fs-handler.mjs.map +3 -3
- package/dist/mjs/connection.mjs +255 -70
- package/dist/mjs/connection.mjs.map +3 -3
- package/{bin/daemon.js → dist/mjs/daemon.mjs} +41 -43
- package/dist/mjs/daemon.mjs.map +10 -0
- package/dist/mjs/index.mjs +4 -2
- package/dist/mjs/index.mjs.map +3 -3
- package/dist/mjs/package.json +1 -1
- package/dist/types/callback-fs-handler.d.ts +3 -3
- package/dist/types/daemon.d.ts +15 -0
- package/dist/types/types.d.ts +37 -1
- package/package.json +7 -2
|
@@ -1,45 +1,51 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
// @bun
|
|
2
3
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
*
|
|
6
|
-
* Usage:
|
|
7
|
-
* isolate-daemon [options]
|
|
8
|
-
*
|
|
9
|
-
* Options:
|
|
10
|
-
* --socket <path> Unix socket path (default: /tmp/isolate-daemon.sock)
|
|
11
|
-
* --host <host> TCP host (default: 127.0.0.1)
|
|
12
|
-
* --port <port> TCP port (default: 47891)
|
|
13
|
-
* --max-isolates <n> Maximum isolates (default: 100)
|
|
14
|
-
* --memory-limit <mb> Default memory limit (default: 128)
|
|
15
|
-
*/
|
|
16
|
-
|
|
17
|
-
import { startDaemon } from "../src/index.ts";
|
|
18
|
-
|
|
4
|
+
// packages/isolate-daemon/src/daemon.ts
|
|
5
|
+
import { startDaemon } from "./index.mjs";
|
|
19
6
|
function parseArgs(args) {
|
|
20
7
|
const options = {};
|
|
21
|
-
|
|
22
|
-
for (let i = 0; i < args.length; i++) {
|
|
8
|
+
for (let i = 0;i < args.length; i++) {
|
|
23
9
|
const arg = args[i];
|
|
24
|
-
|
|
25
10
|
switch (arg) {
|
|
26
11
|
case "--socket":
|
|
27
|
-
|
|
12
|
+
i++;
|
|
13
|
+
if (args[i]) {
|
|
14
|
+
options.socketPath = args[i];
|
|
15
|
+
}
|
|
28
16
|
break;
|
|
29
17
|
case "--host":
|
|
30
|
-
|
|
31
|
-
|
|
18
|
+
i++;
|
|
19
|
+
if (args[i]) {
|
|
20
|
+
options.host = args[i];
|
|
21
|
+
options.socketPath = undefined;
|
|
22
|
+
}
|
|
32
23
|
break;
|
|
33
|
-
case "--port":
|
|
34
|
-
|
|
35
|
-
|
|
24
|
+
case "--port": {
|
|
25
|
+
i++;
|
|
26
|
+
const value = args[i];
|
|
27
|
+
if (value !== undefined) {
|
|
28
|
+
options.port = parseInt(value, 10);
|
|
29
|
+
options.socketPath = undefined;
|
|
30
|
+
}
|
|
36
31
|
break;
|
|
37
|
-
|
|
38
|
-
|
|
32
|
+
}
|
|
33
|
+
case "--max-isolates": {
|
|
34
|
+
i++;
|
|
35
|
+
const value = args[i];
|
|
36
|
+
if (value !== undefined) {
|
|
37
|
+
options.maxIsolates = parseInt(value, 10);
|
|
38
|
+
}
|
|
39
39
|
break;
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
}
|
|
41
|
+
case "--memory-limit": {
|
|
42
|
+
i++;
|
|
43
|
+
const value = args[i];
|
|
44
|
+
if (value !== undefined) {
|
|
45
|
+
options.defaultMemoryLimitMB = parseInt(value, 10);
|
|
46
|
+
}
|
|
42
47
|
break;
|
|
48
|
+
}
|
|
43
49
|
case "--help":
|
|
44
50
|
case "-h":
|
|
45
51
|
console.log(`
|
|
@@ -58,41 +64,33 @@ Options:
|
|
|
58
64
|
`);
|
|
59
65
|
process.exit(0);
|
|
60
66
|
default:
|
|
61
|
-
if (arg.startsWith("-")) {
|
|
67
|
+
if (arg !== undefined && arg.startsWith("-")) {
|
|
62
68
|
console.error(`Unknown option: ${arg}`);
|
|
63
69
|
process.exit(1);
|
|
64
70
|
}
|
|
65
71
|
}
|
|
66
72
|
}
|
|
67
|
-
|
|
68
73
|
return options;
|
|
69
74
|
}
|
|
70
|
-
|
|
71
75
|
async function main() {
|
|
72
76
|
const options = parseArgs(process.argv.slice(2));
|
|
73
|
-
|
|
74
77
|
const daemon = await startDaemon(options);
|
|
75
|
-
|
|
76
|
-
// Handle shutdown signals
|
|
77
78
|
const shutdown = async () => {
|
|
78
|
-
console.log(
|
|
79
|
+
console.log(`
|
|
80
|
+
Shutting down...`);
|
|
79
81
|
await daemon.close();
|
|
80
82
|
process.exit(0);
|
|
81
83
|
};
|
|
82
|
-
|
|
83
84
|
process.on("SIGINT", shutdown);
|
|
84
85
|
process.on("SIGTERM", shutdown);
|
|
85
|
-
|
|
86
|
-
// Log stats periodically
|
|
87
86
|
setInterval(() => {
|
|
88
87
|
const stats = daemon.getStats();
|
|
89
|
-
console.log(
|
|
90
|
-
`[stats] connections: ${stats.activeConnections}, isolates: ${stats.activeIsolates}, total requests: ${stats.totalRequestsProcessed}`
|
|
91
|
-
);
|
|
88
|
+
console.log(`[stats] connections: ${stats.activeConnections}, isolates: ${stats.activeIsolates}, total requests: ${stats.totalRequestsProcessed}`);
|
|
92
89
|
}, 60000);
|
|
93
90
|
}
|
|
94
|
-
|
|
95
91
|
main().catch((err) => {
|
|
96
92
|
console.error("Failed to start daemon:", err);
|
|
97
93
|
process.exit(1);
|
|
98
94
|
});
|
|
95
|
+
|
|
96
|
+
//# debugId=7CA4C42215120D5764756E2164756E21
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/daemon.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"#!/usr/bin/env node\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.mjs\";\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 // Log stats periodically\n setInterval(() => {\n const stats = daemon.getStats();\n console.log(\n `[stats] connections: ${stats.activeConnections}, isolates: ${stats.activeIsolates}, total requests: ${stats.totalRequestsProcessed}`\n );\n }, 60000);\n}\n\nmain().catch((err) => {\n console.error(\"Failed to start daemon:\", err);\n process.exit(1);\n});\n"
|
|
6
|
+
],
|
|
7
|
+
"mappings": ";;;;AAgBA;AAEA,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,YAAY,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,EAG9B,YAAY,MAAM;AAAA,IAChB,MAAM,QAAQ,OAAO,SAAS;AAAA,IAC9B,QAAQ,IACN,wBAAwB,MAAM,gCAAgC,MAAM,mCAAmC,MAAM,wBAC/G;AAAA,KACC,KAAK;AAAA;AAGV,KAAK,EAAE,MAAM,CAAC,QAAQ;AAAA,EACpB,QAAQ,MAAM,2BAA2B,GAAG;AAAA,EAC5C,QAAQ,KAAK,CAAC;AAAA,CACf;",
|
|
8
|
+
"debugId": "7CA4C42215120D5764756E2164756E21",
|
|
9
|
+
"names": []
|
|
10
|
+
}
|
package/dist/mjs/index.mjs
CHANGED
|
@@ -24,7 +24,8 @@ async function startDaemon(options = {}) {
|
|
|
24
24
|
totalIsolatesCreated: 0,
|
|
25
25
|
totalRequestsProcessed: 0
|
|
26
26
|
},
|
|
27
|
-
options: resolvedOptions
|
|
27
|
+
options: resolvedOptions,
|
|
28
|
+
namespacedRuntimes: new Map
|
|
28
29
|
};
|
|
29
30
|
const server = createServer((socket) => {
|
|
30
31
|
handleConnection(socket, state);
|
|
@@ -69,6 +70,7 @@ async function startDaemon(options = {}) {
|
|
|
69
70
|
}
|
|
70
71
|
state.isolates.clear();
|
|
71
72
|
state.connections.clear();
|
|
73
|
+
state.namespacedRuntimes.clear();
|
|
72
74
|
await closeServer(server);
|
|
73
75
|
if (resolvedOptions.socketPath) {
|
|
74
76
|
try {
|
|
@@ -98,4 +100,4 @@ export {
|
|
|
98
100
|
startDaemon
|
|
99
101
|
};
|
|
100
102
|
|
|
101
|
-
//# debugId=
|
|
103
|
+
//# debugId=0CDF49C6B0636B7964756E2164756E21
|
package/dist/mjs/index.mjs.map
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/index.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
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.mjs\";\nimport type {\n DaemonOptions,\n DaemonHandle,\n DaemonState,\n DaemonStats,\n} from \"./types.mjs\";\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 };\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\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"
|
|
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.mjs\";\nimport type {\n DaemonOptions,\n DaemonHandle,\n DaemonState,\n DaemonStats,\n} from \"./types.mjs\";\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 };\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": ";;AAMA;AACA;AACA;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,
|
|
8
|
-
"debugId": "
|
|
7
|
+
"mappings": ";;AAMA;AACA;AACA;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,EAC1B;AAAA,EAEA,MAAM,SAAS,aAAa,CAAC,WAAW;AAAA,IACtC,iBAAiB,QAAQ,KAAK;AAAA,IAC9B,YAAY,KAAK;AAAA,GAClB;AAAA,EAGD,IAAI,gBAAgB,YAAY;AAAA,IAC9B,IAAI;AAAA,MACF,MAAM,OAAO,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,OAAO,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": "0CDF49C6B0636B7964756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
package/dist/mjs/package.json
CHANGED
|
@@ -5,14 +5,13 @@
|
|
|
5
5
|
* FileSystemHandler interface used by @ricsam/isolate-fs.
|
|
6
6
|
*/
|
|
7
7
|
import type { FileSystemHandler } from "@ricsam/isolate-fs";
|
|
8
|
-
import type {
|
|
9
|
-
import type { ConnectionState } from "./types.ts";
|
|
8
|
+
import type { ConnectionState, CallbackContext } from "./types.ts";
|
|
10
9
|
interface InvokeClientCallback {
|
|
11
10
|
(connection: ConnectionState, callbackId: number, args: unknown[]): Promise<unknown>;
|
|
12
11
|
}
|
|
13
12
|
interface CallbackFsHandlerOptions {
|
|
14
13
|
connection: ConnectionState;
|
|
15
|
-
|
|
14
|
+
callbackContext: CallbackContext;
|
|
16
15
|
invokeClientCallback: InvokeClientCallback;
|
|
17
16
|
basePath?: string;
|
|
18
17
|
}
|
|
@@ -20,6 +19,7 @@ interface CallbackFsHandlerOptions {
|
|
|
20
19
|
* Create a FileSystemHandler that invokes client callbacks.
|
|
21
20
|
*
|
|
22
21
|
* Maps WHATWG FileSystem API operations to simple POSIX-like callbacks.
|
|
22
|
+
* Uses callbackContext for dynamic callback ID lookup to support runtime reuse.
|
|
23
23
|
*/
|
|
24
24
|
export declare function createCallbackFileSystemHandler(options: CallbackFsHandlerOptions): FileSystemHandler;
|
|
25
25
|
export {};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* CLI entry point for the isolate daemon.
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* isolate-daemon [options]
|
|
7
|
+
*
|
|
8
|
+
* Options:
|
|
9
|
+
* --socket <path> Unix socket path (default: /tmp/isolate-daemon.sock)
|
|
10
|
+
* --host <host> TCP host (default: 127.0.0.1)
|
|
11
|
+
* --port <port> TCP port (default: 47891)
|
|
12
|
+
* --max-isolates <n> Maximum isolates (default: 100)
|
|
13
|
+
* --memory-limit <mb> Default memory limit (default: 128)
|
|
14
|
+
*/
|
|
15
|
+
export {};
|
package/dist/types/types.d.ts
CHANGED
|
@@ -47,7 +47,7 @@ export interface DaemonStats {
|
|
|
47
47
|
export interface IsolateInstance {
|
|
48
48
|
isolateId: string;
|
|
49
49
|
runtime: InternalRuntimeHandle;
|
|
50
|
-
ownerConnection: Socket;
|
|
50
|
+
ownerConnection: Socket | null;
|
|
51
51
|
callbacks: Map<number, CallbackRegistration>;
|
|
52
52
|
createdAt: number;
|
|
53
53
|
lastActivity: number;
|
|
@@ -69,6 +69,40 @@ export interface IsolateInstance {
|
|
|
69
69
|
returnedIterators?: Map<number, AsyncIterator<unknown>>;
|
|
70
70
|
/** Next ID for daemon-local callback registration (starts at high number to avoid conflicts) */
|
|
71
71
|
nextLocalCallbackId?: number;
|
|
72
|
+
/** Namespace ID for pooling/reuse (if set, runtime is cached on dispose) */
|
|
73
|
+
namespaceId?: string;
|
|
74
|
+
/** Whether this runtime is soft-deleted (disposed but cached for reuse) */
|
|
75
|
+
isDisposed: boolean;
|
|
76
|
+
/** Timestamp when runtime was disposed (for LRU eviction) */
|
|
77
|
+
disposedAt?: number;
|
|
78
|
+
/** Mutable context for callbacks - allows updating callback IDs/connection on reuse */
|
|
79
|
+
callbackContext?: CallbackContext;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Mutable context for callbacks that can be updated on runtime reuse.
|
|
83
|
+
* This allows closures to reference current callback IDs instead of captured values.
|
|
84
|
+
*/
|
|
85
|
+
export interface CallbackContext {
|
|
86
|
+
/** Current connection state (updated on reuse) */
|
|
87
|
+
connection: ConnectionState | null;
|
|
88
|
+
/** Console onEntry callback ID */
|
|
89
|
+
consoleOnEntry?: number;
|
|
90
|
+
/** Fetch callback ID */
|
|
91
|
+
fetch?: number;
|
|
92
|
+
/** Module loader callback ID */
|
|
93
|
+
moduleLoader?: number;
|
|
94
|
+
/** FS callback IDs by name */
|
|
95
|
+
fs: {
|
|
96
|
+
readFile?: number;
|
|
97
|
+
writeFile?: number;
|
|
98
|
+
stat?: number;
|
|
99
|
+
readdir?: number;
|
|
100
|
+
unlink?: number;
|
|
101
|
+
mkdir?: number;
|
|
102
|
+
rmdir?: number;
|
|
103
|
+
};
|
|
104
|
+
/** Custom function callback IDs by name */
|
|
105
|
+
custom: Map<string, number>;
|
|
72
106
|
}
|
|
73
107
|
/**
|
|
74
108
|
* Pending request waiting for response.
|
|
@@ -125,4 +159,6 @@ export interface DaemonState {
|
|
|
125
159
|
connections: Map<Socket, ConnectionState>;
|
|
126
160
|
stats: DaemonStats;
|
|
127
161
|
options: Required<DaemonOptions>;
|
|
162
|
+
/** Index of namespaced runtimes by namespace ID for fast lookup */
|
|
163
|
+
namespacedRuntimes: Map<string, IsolateInstance>;
|
|
128
164
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ricsam/isolate-daemon",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.7",
|
|
4
4
|
"main": "./dist/cjs/index.cjs",
|
|
5
5
|
"types": "./dist/types/index.d.ts",
|
|
6
6
|
"exports": {
|
|
@@ -8,10 +8,15 @@
|
|
|
8
8
|
"types": "./dist/types/index.d.ts",
|
|
9
9
|
"require": "./dist/cjs/index.cjs",
|
|
10
10
|
"import": "./dist/mjs/index.mjs"
|
|
11
|
+
},
|
|
12
|
+
"./daemon": {
|
|
13
|
+
"types": "./dist/types/daemon.d.ts",
|
|
14
|
+
"require": "./dist/cjs/daemon.cjs",
|
|
15
|
+
"import": "./dist/mjs/daemon.mjs"
|
|
11
16
|
}
|
|
12
17
|
},
|
|
13
18
|
"bin": {
|
|
14
|
-
"isolate-daemon": "./
|
|
19
|
+
"isolate-daemon": "./dist/cjs/daemon.cjs"
|
|
15
20
|
},
|
|
16
21
|
"scripts": {
|
|
17
22
|
"build": "tsc",
|