@ricsam/isolate-daemon 0.1.13 → 0.1.15
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 +1 -0
- package/dist/cjs/callback-fs-handler.cjs +18 -33
- package/dist/cjs/callback-fs-handler.cjs.map +3 -3
- package/dist/cjs/connection.cjs +547 -793
- package/dist/cjs/connection.cjs.map +3 -3
- package/dist/cjs/index.cjs +3 -2
- package/dist/cjs/index.cjs.map +3 -3
- package/dist/cjs/package.json +1 -1
- package/dist/mjs/callback-fs-handler.mjs +18 -33
- package/dist/mjs/callback-fs-handler.mjs.map +3 -3
- package/dist/mjs/connection.mjs +553 -795
- package/dist/mjs/connection.mjs.map +3 -3
- package/dist/mjs/index.mjs +3 -2
- package/dist/mjs/index.mjs.map +3 -3
- package/dist/mjs/package.json +1 -1
- package/dist/types/types.d.ts +15 -20
- package/package.json +1 -1
package/dist/mjs/index.mjs
CHANGED
|
@@ -24,7 +24,8 @@ async function startDaemon(options = {}) {
|
|
|
24
24
|
totalRequestsProcessed: 0
|
|
25
25
|
},
|
|
26
26
|
options: resolvedOptions,
|
|
27
|
-
namespacedRuntimes: new Map
|
|
27
|
+
namespacedRuntimes: new Map,
|
|
28
|
+
namespacedCreatesInFlight: new Set
|
|
28
29
|
};
|
|
29
30
|
const server = createServer((socket) => {
|
|
30
31
|
handleConnection(socket, state);
|
|
@@ -99,4 +100,4 @@ export {
|
|
|
99
100
|
startDaemon
|
|
100
101
|
};
|
|
101
102
|
|
|
102
|
-
//# debugId=
|
|
103
|
+
//# debugId=DFEB146C130DE4A864756E2164756E21
|
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 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"
|
|
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 namespacedCreatesInFlight: new Set(),\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,IACT,oBAAoB,IAAI;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,IACxB,2BAA2B,IAAI;AAAA,EACjC;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": "DFEB146C130DE4A864756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
package/dist/mjs/package.json
CHANGED
package/dist/types/types.d.ts
CHANGED
|
@@ -2,11 +2,8 @@
|
|
|
2
2
|
* Internal types for the isolate daemon.
|
|
3
3
|
*/
|
|
4
4
|
import type { Socket } from "node:net";
|
|
5
|
-
import type
|
|
6
|
-
import type { InternalRuntimeHandle } from "@ricsam/isolate-runtime";
|
|
5
|
+
import type { RuntimeHandle } from "@ricsam/isolate-runtime";
|
|
7
6
|
import type { CallbackRegistration } from "@ricsam/isolate-protocol";
|
|
8
|
-
import type { PlaywrightHandle } from "@ricsam/isolate-playwright";
|
|
9
|
-
import type { SourceMap } from "@ricsam/isolate-transform";
|
|
10
7
|
/**
|
|
11
8
|
* Options for starting the daemon.
|
|
12
9
|
*/
|
|
@@ -47,25 +44,11 @@ export interface DaemonStats {
|
|
|
47
44
|
*/
|
|
48
45
|
export interface IsolateInstance {
|
|
49
46
|
isolateId: string;
|
|
50
|
-
runtime:
|
|
47
|
+
runtime: RuntimeHandle;
|
|
51
48
|
ownerConnection: Socket | null;
|
|
52
49
|
callbacks: Map<number, CallbackRegistration>;
|
|
53
50
|
createdAt: number;
|
|
54
51
|
lastActivity: number;
|
|
55
|
-
/** Whether test environment is enabled */
|
|
56
|
-
testEnvironmentEnabled?: boolean;
|
|
57
|
-
/** Playwright handle for event management (if setup) */
|
|
58
|
-
playwrightHandle?: PlaywrightHandle;
|
|
59
|
-
/** Module loader callback ID (if registered) */
|
|
60
|
-
moduleLoaderCallbackId?: number;
|
|
61
|
-
/** Cache of compiled ES modules */
|
|
62
|
-
moduleCache?: Map<string, ivm.Module>;
|
|
63
|
-
/** Map from module to its filename (for tracking importer path) */
|
|
64
|
-
moduleToFilename?: Map<ivm.Module, string>;
|
|
65
|
-
/** Pending callback promises for current eval */
|
|
66
|
-
pendingCallbacks: Promise<unknown>[];
|
|
67
|
-
/** Source maps for error stack trace mapping */
|
|
68
|
-
sourceMaps?: Map<string, SourceMap>;
|
|
69
52
|
/** Functions returned by custom function calls (callable from isolate) */
|
|
70
53
|
returnedCallbacks?: Map<number, Function>;
|
|
71
54
|
/** Promises returned by custom function calls (resolvable from isolate) */
|
|
@@ -78,6 +61,8 @@ export interface IsolateInstance {
|
|
|
78
61
|
namespaceId?: string;
|
|
79
62
|
/** Whether this runtime is soft-deleted (disposed but cached for reuse) */
|
|
80
63
|
isDisposed: boolean;
|
|
64
|
+
/** Whether runtime hit unrecoverable module-linking state and must not be reused */
|
|
65
|
+
isPoisoned: boolean;
|
|
81
66
|
/** Timestamp when runtime was disposed (for LRU eviction) */
|
|
82
67
|
disposedAt?: number;
|
|
83
68
|
/** Mutable context for callbacks - allows updating callback IDs/connection on reuse */
|
|
@@ -96,6 +81,15 @@ export interface CallbackContext {
|
|
|
96
81
|
fetch?: number;
|
|
97
82
|
/** Module loader callback ID */
|
|
98
83
|
moduleLoader?: number;
|
|
84
|
+
/** testEnvironment.onEvent callback ID */
|
|
85
|
+
testEnvironmentOnEvent?: number;
|
|
86
|
+
/** Playwright callback IDs */
|
|
87
|
+
playwright: {
|
|
88
|
+
handlerCallbackId?: number;
|
|
89
|
+
onBrowserConsoleLogCallbackId?: number;
|
|
90
|
+
onNetworkRequestCallbackId?: number;
|
|
91
|
+
onNetworkResponseCallbackId?: number;
|
|
92
|
+
};
|
|
99
93
|
/** FS callback IDs by name */
|
|
100
94
|
fs: {
|
|
101
95
|
readFile?: number;
|
|
@@ -115,7 +109,6 @@ export interface CallbackContext {
|
|
|
115
109
|
export interface PendingRequest {
|
|
116
110
|
resolve: (result: unknown) => void;
|
|
117
111
|
reject: (error: Error) => void;
|
|
118
|
-
timeoutId?: ReturnType<typeof setTimeout>;
|
|
119
112
|
}
|
|
120
113
|
/**
|
|
121
114
|
* Stream session for tracking active streams.
|
|
@@ -187,4 +180,6 @@ export interface DaemonState {
|
|
|
187
180
|
options: Required<DaemonOptions>;
|
|
188
181
|
/** Index of namespaced runtimes by namespace ID for fast lookup */
|
|
189
182
|
namespacedRuntimes: Map<string, IsolateInstance>;
|
|
183
|
+
/** Track in-flight namespace creations to prevent thundering herd */
|
|
184
|
+
namespacedCreatesInFlight: Set<string>;
|
|
190
185
|
}
|