@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.
@@ -1,45 +1,51 @@
1
1
  #!/usr/bin/env node
2
+ // @bun
2
3
 
3
- /**
4
- * CLI entry point for the isolate daemon.
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
- options.socketPath = args[++i];
12
+ i++;
13
+ if (args[i]) {
14
+ options.socketPath = args[i];
15
+ }
28
16
  break;
29
17
  case "--host":
30
- options.host = args[++i];
31
- options.socketPath = undefined; // Use TCP instead
18
+ i++;
19
+ if (args[i]) {
20
+ options.host = args[i];
21
+ options.socketPath = undefined;
22
+ }
32
23
  break;
33
- case "--port":
34
- options.port = parseInt(args[++i], 10);
35
- options.socketPath = undefined; // Use TCP instead
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
- case "--max-isolates":
38
- options.maxIsolates = parseInt(args[++i], 10);
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
- case "--memory-limit":
41
- options.defaultMemoryLimitMB = parseInt(args[++i], 10);
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("\nShutting down...");
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
+ }
@@ -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=8B7DF84C4AC08DDD64756E2164756E21
103
+ //# debugId=0CDF49C6B0636B7964756E2164756E21
@@ -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,EACX;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,MAGxB,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": "8B7DF84C4AC08DDD64756E2164756E21",
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
  }
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@ricsam/isolate-daemon",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
4
4
  "type": "module"
5
5
  }
@@ -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 { FsCallbackRegistrations } from "@ricsam/isolate-protocol";
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
- callbacks: FsCallbackRegistrations;
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 {};
@@ -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.5",
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": "./bin/daemon.js"
19
+ "isolate-daemon": "./dist/cjs/daemon.cjs"
15
20
  },
16
21
  "scripts": {
17
22
  "build": "tsc",