@ricsam/isolate-daemon 0.0.1 → 0.1.0
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 +36 -31
- package/bin/daemon.js +98 -0
- package/dist/cjs/callback-fs-handler.cjs +314 -0
- package/dist/cjs/callback-fs-handler.cjs.map +10 -0
- package/dist/cjs/connection.cjs +580 -0
- package/dist/cjs/connection.cjs.map +10 -0
- package/dist/cjs/index.cjs +132 -0
- package/dist/cjs/index.cjs.map +10 -0
- package/dist/cjs/package.json +5 -0
- package/dist/cjs/types.cjs +26 -0
- package/dist/cjs/types.cjs.map +9 -0
- package/dist/mjs/callback-fs-handler.mjs +283 -0
- package/dist/mjs/callback-fs-handler.mjs.map +10 -0
- package/dist/mjs/connection.mjs +561 -0
- package/dist/mjs/connection.mjs.map +10 -0
- package/dist/mjs/index.mjs +101 -0
- package/dist/mjs/index.mjs.map +10 -0
- package/dist/mjs/package.json +5 -0
- package/dist/mjs/types.mjs +3 -0
- package/dist/mjs/types.mjs.map +9 -0
- package/dist/types/callback-fs-handler.d.ts +25 -0
- package/dist/types/connection.d.ts +9 -0
- package/dist/types/index.d.ts +14 -0
- package/dist/types/types.d.ts +91 -0
- package/package.json +55 -6
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
// @bun @bun-cjs
|
|
2
|
+
(function(exports, require, module, __filename, __dirname) {var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __moduleCache = /* @__PURE__ */ new WeakMap;
|
|
7
|
+
var __toCommonJS = (from) => {
|
|
8
|
+
var entry = __moduleCache.get(from), desc;
|
|
9
|
+
if (entry)
|
|
10
|
+
return entry;
|
|
11
|
+
entry = __defProp({}, "__esModule", { value: true });
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function")
|
|
13
|
+
__getOwnPropNames(from).map((key) => !__hasOwnProp.call(entry, key) && __defProp(entry, key, {
|
|
14
|
+
get: () => from[key],
|
|
15
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
16
|
+
}));
|
|
17
|
+
__moduleCache.set(from, entry);
|
|
18
|
+
return entry;
|
|
19
|
+
};
|
|
20
|
+
var __export = (target, all) => {
|
|
21
|
+
for (var name in all)
|
|
22
|
+
__defProp(target, name, {
|
|
23
|
+
get: all[name],
|
|
24
|
+
enumerable: true,
|
|
25
|
+
configurable: true,
|
|
26
|
+
set: (newValue) => all[name] = () => newValue
|
|
27
|
+
});
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
// packages/isolate-daemon/src/index.ts
|
|
31
|
+
var exports_src = {};
|
|
32
|
+
__export(exports_src, {
|
|
33
|
+
startDaemon: () => startDaemon
|
|
34
|
+
});
|
|
35
|
+
module.exports = __toCommonJS(exports_src);
|
|
36
|
+
var import_node_net = require("net");
|
|
37
|
+
var import_promises = require("fs/promises");
|
|
38
|
+
var import_connection = require("./connection.cjs");
|
|
39
|
+
var DEFAULT_OPTIONS = {
|
|
40
|
+
socketPath: "/tmp/isolate-daemon.sock",
|
|
41
|
+
host: "127.0.0.1",
|
|
42
|
+
port: 47891,
|
|
43
|
+
maxIsolates: 100,
|
|
44
|
+
defaultMemoryLimit: 128
|
|
45
|
+
};
|
|
46
|
+
async function startDaemon(options = {}) {
|
|
47
|
+
const resolvedOptions = {
|
|
48
|
+
...DEFAULT_OPTIONS,
|
|
49
|
+
...options
|
|
50
|
+
};
|
|
51
|
+
const state = {
|
|
52
|
+
isolates: new Map,
|
|
53
|
+
connections: new Map,
|
|
54
|
+
stats: {
|
|
55
|
+
activeIsolates: 0,
|
|
56
|
+
activeConnections: 0,
|
|
57
|
+
totalIsolatesCreated: 0,
|
|
58
|
+
totalRequestsProcessed: 0
|
|
59
|
+
},
|
|
60
|
+
options: resolvedOptions
|
|
61
|
+
};
|
|
62
|
+
const server = import_node_net.createServer((socket) => {
|
|
63
|
+
import_connection.handleConnection(socket, state);
|
|
64
|
+
updateStats(state);
|
|
65
|
+
});
|
|
66
|
+
if (resolvedOptions.socketPath) {
|
|
67
|
+
try {
|
|
68
|
+
await import_promises.unlink(resolvedOptions.socketPath);
|
|
69
|
+
} catch {}
|
|
70
|
+
}
|
|
71
|
+
await new Promise((resolve, reject) => {
|
|
72
|
+
server.on("error", reject);
|
|
73
|
+
if (resolvedOptions.socketPath) {
|
|
74
|
+
server.listen(resolvedOptions.socketPath, () => {
|
|
75
|
+
server.removeListener("error", reject);
|
|
76
|
+
resolve();
|
|
77
|
+
});
|
|
78
|
+
} else {
|
|
79
|
+
server.listen(resolvedOptions.port, resolvedOptions.host, () => {
|
|
80
|
+
server.removeListener("error", reject);
|
|
81
|
+
resolve();
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
const address = resolvedOptions.socketPath ? resolvedOptions.socketPath : `${resolvedOptions.host}:${resolvedOptions.port}`;
|
|
86
|
+
console.log(`Isolate daemon listening on ${address}`);
|
|
87
|
+
return {
|
|
88
|
+
address,
|
|
89
|
+
getStats: () => ({
|
|
90
|
+
...state.stats,
|
|
91
|
+
activeIsolates: state.isolates.size,
|
|
92
|
+
activeConnections: state.connections.size
|
|
93
|
+
}),
|
|
94
|
+
close: async () => {
|
|
95
|
+
for (const [socket] of state.connections) {
|
|
96
|
+
socket.destroy();
|
|
97
|
+
}
|
|
98
|
+
for (const [, instance] of state.isolates) {
|
|
99
|
+
try {
|
|
100
|
+
instance.runtime.dispose();
|
|
101
|
+
} catch {}
|
|
102
|
+
}
|
|
103
|
+
state.isolates.clear();
|
|
104
|
+
state.connections.clear();
|
|
105
|
+
await closeServer(server);
|
|
106
|
+
if (resolvedOptions.socketPath) {
|
|
107
|
+
try {
|
|
108
|
+
await import_promises.unlink(resolvedOptions.socketPath);
|
|
109
|
+
} catch {}
|
|
110
|
+
}
|
|
111
|
+
console.log("Isolate daemon stopped");
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
function closeServer(server) {
|
|
116
|
+
return new Promise((resolve, reject) => {
|
|
117
|
+
server.close((err) => {
|
|
118
|
+
if (err) {
|
|
119
|
+
reject(err);
|
|
120
|
+
} else {
|
|
121
|
+
resolve();
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
function updateStats(state) {
|
|
127
|
+
state.stats.activeIsolates = state.isolates.size;
|
|
128
|
+
state.stats.activeConnections = state.connections.size;
|
|
129
|
+
}
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
//# debugId=04325A2C81076C8A64756E2164756E21
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/index.ts"],
|
|
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.cjs\";\nimport type {\n DaemonOptions,\n DaemonHandle,\n DaemonState,\n DaemonStats,\n} from \"./types.cjs\";\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 defaultMemoryLimit: 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"
|
|
6
|
+
],
|
|
7
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAM0C,IAA1C;AACuB,IAAvB;AACiC,IAAjC;AAUA,IAAM,kBAA2C;AAAA,EAC/C,YAAY;AAAA,EACZ,MAAM;AAAA,EACN,MAAM;AAAA,EACN,aAAa;AAAA,EACb,oBAAoB;AACtB;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,6BAAa,CAAC,WAAW;AAAA,IACtC,mCAAiB,QAAQ,KAAK;AAAA,IAC9B,YAAY,KAAK;AAAA,GAClB;AAAA,EAGD,IAAI,gBAAgB,YAAY;AAAA,IAC9B,IAAI;AAAA,MACF,MAAM,uBAAO,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,uBAAO,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": "04325A2C81076C8A64756E2164756E21",
|
|
9
|
+
"names": []
|
|
10
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// @bun @bun-cjs
|
|
2
|
+
(function(exports, require, module, __filename, __dirname) {var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __moduleCache = /* @__PURE__ */ new WeakMap;
|
|
7
|
+
var __toCommonJS = (from) => {
|
|
8
|
+
var entry = __moduleCache.get(from), desc;
|
|
9
|
+
if (entry)
|
|
10
|
+
return entry;
|
|
11
|
+
entry = __defProp({}, "__esModule", { value: true });
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function")
|
|
13
|
+
__getOwnPropNames(from).map((key) => !__hasOwnProp.call(entry, key) && __defProp(entry, key, {
|
|
14
|
+
get: () => from[key],
|
|
15
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
16
|
+
}));
|
|
17
|
+
__moduleCache.set(from, entry);
|
|
18
|
+
return entry;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
// packages/isolate-daemon/src/types.ts
|
|
22
|
+
var exports_types = {};
|
|
23
|
+
module.exports = __toCommonJS(exports_types);
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
//# debugId=72DC57A69CFF998A64756E2164756E21
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// packages/isolate-daemon/src/callback-fs-handler.ts
|
|
3
|
+
function createCallbackFileSystemHandler(options) {
|
|
4
|
+
const { connection, callbacks, invokeClientCallback, basePath = "" } = options;
|
|
5
|
+
const resolvePath = (path) => {
|
|
6
|
+
const cleanPath = path.startsWith("/") ? path.slice(1) : path;
|
|
7
|
+
if (!basePath || basePath === "/") {
|
|
8
|
+
return `/${cleanPath}`;
|
|
9
|
+
}
|
|
10
|
+
const cleanBase = basePath.endsWith("/") ? basePath.slice(0, -1) : basePath;
|
|
11
|
+
return `${cleanBase}/${cleanPath}`;
|
|
12
|
+
};
|
|
13
|
+
return {
|
|
14
|
+
async getFileHandle(path, opts) {
|
|
15
|
+
const fullPath = resolvePath(path);
|
|
16
|
+
if (opts?.create) {
|
|
17
|
+
if (callbacks.writeFile) {
|
|
18
|
+
try {
|
|
19
|
+
if (callbacks.stat) {
|
|
20
|
+
try {
|
|
21
|
+
await invokeClientCallback(connection, callbacks.stat.callbackId, [fullPath]);
|
|
22
|
+
return;
|
|
23
|
+
} catch {}
|
|
24
|
+
}
|
|
25
|
+
await invokeClientCallback(connection, callbacks.writeFile.callbackId, [
|
|
26
|
+
fullPath,
|
|
27
|
+
new Uint8Array(0)
|
|
28
|
+
]);
|
|
29
|
+
} catch (err) {
|
|
30
|
+
const error = err;
|
|
31
|
+
throw new Error(`[NotFoundError]${error.message}`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
if (callbacks.stat) {
|
|
37
|
+
try {
|
|
38
|
+
const result = await invokeClientCallback(connection, callbacks.stat.callbackId, [
|
|
39
|
+
fullPath
|
|
40
|
+
]);
|
|
41
|
+
if (!result.isFile) {
|
|
42
|
+
throw new Error(`[TypeMismatchError]Not a file: ${fullPath}`);
|
|
43
|
+
}
|
|
44
|
+
} catch (err) {
|
|
45
|
+
const error = err;
|
|
46
|
+
if (error.message.includes("TypeMismatchError"))
|
|
47
|
+
throw error;
|
|
48
|
+
throw new Error(`[NotFoundError]File not found: ${fullPath}`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
async getDirectoryHandle(path, opts) {
|
|
53
|
+
const fullPath = resolvePath(path);
|
|
54
|
+
if (opts?.create) {
|
|
55
|
+
if (callbacks.mkdir) {
|
|
56
|
+
try {
|
|
57
|
+
await invokeClientCallback(connection, callbacks.mkdir.callbackId, [
|
|
58
|
+
fullPath,
|
|
59
|
+
{ recursive: true }
|
|
60
|
+
]);
|
|
61
|
+
} catch {}
|
|
62
|
+
}
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
if (callbacks.stat) {
|
|
66
|
+
try {
|
|
67
|
+
const result = await invokeClientCallback(connection, callbacks.stat.callbackId, [
|
|
68
|
+
fullPath
|
|
69
|
+
]);
|
|
70
|
+
if (!result.isDirectory) {
|
|
71
|
+
throw new Error(`[TypeMismatchError]Not a directory: ${fullPath}`);
|
|
72
|
+
}
|
|
73
|
+
} catch (err) {
|
|
74
|
+
const error = err;
|
|
75
|
+
if (error.message.includes("TypeMismatchError"))
|
|
76
|
+
throw error;
|
|
77
|
+
throw new Error(`[NotFoundError]Directory not found: ${fullPath}`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
async removeEntry(path, opts) {
|
|
82
|
+
const fullPath = resolvePath(path);
|
|
83
|
+
let isFile = true;
|
|
84
|
+
if (callbacks.stat) {
|
|
85
|
+
try {
|
|
86
|
+
const result = await invokeClientCallback(connection, callbacks.stat.callbackId, [
|
|
87
|
+
fullPath
|
|
88
|
+
]);
|
|
89
|
+
isFile = result.isFile;
|
|
90
|
+
} catch {
|
|
91
|
+
throw new Error(`[NotFoundError]Entry not found: ${fullPath}`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
if (isFile) {
|
|
95
|
+
if (!callbacks.unlink) {
|
|
96
|
+
throw new Error(`[NotAllowedError]File deletion not supported`);
|
|
97
|
+
}
|
|
98
|
+
await invokeClientCallback(connection, callbacks.unlink.callbackId, [fullPath]);
|
|
99
|
+
} else {
|
|
100
|
+
if (!callbacks.rmdir) {
|
|
101
|
+
throw new Error(`[NotAllowedError]Directory deletion not supported`);
|
|
102
|
+
}
|
|
103
|
+
await invokeClientCallback(connection, callbacks.rmdir.callbackId, [fullPath]);
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
async readDirectory(path) {
|
|
107
|
+
const fullPath = resolvePath(path);
|
|
108
|
+
if (!callbacks.readdir) {
|
|
109
|
+
throw new Error(`[NotAllowedError]Directory reading not supported`);
|
|
110
|
+
}
|
|
111
|
+
const entries = await invokeClientCallback(connection, callbacks.readdir.callbackId, [
|
|
112
|
+
fullPath
|
|
113
|
+
]);
|
|
114
|
+
const result = [];
|
|
115
|
+
for (const name of entries) {
|
|
116
|
+
const entryPath = fullPath ? `${fullPath}/${name}` : name;
|
|
117
|
+
let kind = "file";
|
|
118
|
+
if (callbacks.stat) {
|
|
119
|
+
try {
|
|
120
|
+
const stat = await invokeClientCallback(connection, callbacks.stat.callbackId, [
|
|
121
|
+
entryPath
|
|
122
|
+
]);
|
|
123
|
+
kind = stat.isDirectory ? "directory" : "file";
|
|
124
|
+
} catch {}
|
|
125
|
+
}
|
|
126
|
+
result.push({ name, kind });
|
|
127
|
+
}
|
|
128
|
+
return result;
|
|
129
|
+
},
|
|
130
|
+
async readFile(path) {
|
|
131
|
+
const fullPath = resolvePath(path);
|
|
132
|
+
if (!callbacks.readFile) {
|
|
133
|
+
throw new Error(`[NotAllowedError]File reading not supported`);
|
|
134
|
+
}
|
|
135
|
+
const data = await invokeClientCallback(connection, callbacks.readFile.callbackId, [
|
|
136
|
+
fullPath
|
|
137
|
+
]);
|
|
138
|
+
let bytes;
|
|
139
|
+
if (data instanceof Uint8Array) {
|
|
140
|
+
bytes = data;
|
|
141
|
+
} else if (Array.isArray(data)) {
|
|
142
|
+
bytes = new Uint8Array(data);
|
|
143
|
+
} else if (data instanceof ArrayBuffer) {
|
|
144
|
+
bytes = new Uint8Array(data);
|
|
145
|
+
} else {
|
|
146
|
+
bytes = new Uint8Array(0);
|
|
147
|
+
}
|
|
148
|
+
let size = bytes.length;
|
|
149
|
+
let lastModified = Date.now();
|
|
150
|
+
if (callbacks.stat) {
|
|
151
|
+
try {
|
|
152
|
+
const stat = await invokeClientCallback(connection, callbacks.stat.callbackId, [
|
|
153
|
+
fullPath
|
|
154
|
+
]);
|
|
155
|
+
size = stat.size;
|
|
156
|
+
if (stat.lastModified) {
|
|
157
|
+
lastModified = stat.lastModified;
|
|
158
|
+
}
|
|
159
|
+
} catch {}
|
|
160
|
+
}
|
|
161
|
+
const ext = path.split(".").pop()?.toLowerCase() || "";
|
|
162
|
+
const mimeTypes = {
|
|
163
|
+
txt: "text/plain",
|
|
164
|
+
html: "text/html",
|
|
165
|
+
htm: "text/html",
|
|
166
|
+
css: "text/css",
|
|
167
|
+
js: "text/javascript",
|
|
168
|
+
json: "application/json",
|
|
169
|
+
xml: "application/xml",
|
|
170
|
+
png: "image/png",
|
|
171
|
+
jpg: "image/jpeg",
|
|
172
|
+
jpeg: "image/jpeg",
|
|
173
|
+
gif: "image/gif",
|
|
174
|
+
svg: "image/svg+xml",
|
|
175
|
+
pdf: "application/pdf"
|
|
176
|
+
};
|
|
177
|
+
const type = mimeTypes[ext] || "application/octet-stream";
|
|
178
|
+
return { data: bytes, size, lastModified, type };
|
|
179
|
+
},
|
|
180
|
+
async writeFile(path, data, position) {
|
|
181
|
+
const fullPath = resolvePath(path);
|
|
182
|
+
if (!callbacks.writeFile) {
|
|
183
|
+
throw new Error(`[NotAllowedError]File writing not supported`);
|
|
184
|
+
}
|
|
185
|
+
if (position !== undefined && position > 0) {
|
|
186
|
+
if (callbacks.readFile) {
|
|
187
|
+
try {
|
|
188
|
+
const existing = await invokeClientCallback(connection, callbacks.readFile.callbackId, [fullPath]);
|
|
189
|
+
let existingBytes;
|
|
190
|
+
if (existing instanceof Uint8Array) {
|
|
191
|
+
existingBytes = existing;
|
|
192
|
+
} else if (Array.isArray(existing)) {
|
|
193
|
+
existingBytes = new Uint8Array(existing);
|
|
194
|
+
} else if (existing instanceof ArrayBuffer) {
|
|
195
|
+
existingBytes = new Uint8Array(existing);
|
|
196
|
+
} else {
|
|
197
|
+
existingBytes = new Uint8Array(0);
|
|
198
|
+
}
|
|
199
|
+
const newSize = Math.max(existingBytes.length, position + data.length);
|
|
200
|
+
const merged = new Uint8Array(newSize);
|
|
201
|
+
merged.set(existingBytes);
|
|
202
|
+
merged.set(data, position);
|
|
203
|
+
await invokeClientCallback(connection, callbacks.writeFile.callbackId, [
|
|
204
|
+
fullPath,
|
|
205
|
+
merged
|
|
206
|
+
]);
|
|
207
|
+
return;
|
|
208
|
+
} catch {
|
|
209
|
+
const newData = new Uint8Array(position + data.length);
|
|
210
|
+
newData.set(data, position);
|
|
211
|
+
await invokeClientCallback(connection, callbacks.writeFile.callbackId, [
|
|
212
|
+
fullPath,
|
|
213
|
+
newData
|
|
214
|
+
]);
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
await invokeClientCallback(connection, callbacks.writeFile.callbackId, [fullPath, data]);
|
|
220
|
+
},
|
|
221
|
+
async truncateFile(path, size) {
|
|
222
|
+
const fullPath = resolvePath(path);
|
|
223
|
+
if (!callbacks.readFile || !callbacks.writeFile) {
|
|
224
|
+
throw new Error(`[NotAllowedError]File truncation not supported`);
|
|
225
|
+
}
|
|
226
|
+
const existing = await invokeClientCallback(connection, callbacks.readFile.callbackId, [
|
|
227
|
+
fullPath
|
|
228
|
+
]);
|
|
229
|
+
let existingBytes;
|
|
230
|
+
if (existing instanceof Uint8Array) {
|
|
231
|
+
existingBytes = existing;
|
|
232
|
+
} else if (Array.isArray(existing)) {
|
|
233
|
+
existingBytes = new Uint8Array(existing);
|
|
234
|
+
} else if (existing instanceof ArrayBuffer) {
|
|
235
|
+
existingBytes = new Uint8Array(existing);
|
|
236
|
+
} else {
|
|
237
|
+
existingBytes = new Uint8Array(0);
|
|
238
|
+
}
|
|
239
|
+
const truncated = new Uint8Array(size);
|
|
240
|
+
truncated.set(existingBytes.slice(0, size));
|
|
241
|
+
await invokeClientCallback(connection, callbacks.writeFile.callbackId, [fullPath, truncated]);
|
|
242
|
+
},
|
|
243
|
+
async getFileMetadata(path) {
|
|
244
|
+
const fullPath = resolvePath(path);
|
|
245
|
+
if (!callbacks.stat) {
|
|
246
|
+
throw new Error(`[NotAllowedError]File stat not supported`);
|
|
247
|
+
}
|
|
248
|
+
const stat = await invokeClientCallback(connection, callbacks.stat.callbackId, [
|
|
249
|
+
fullPath
|
|
250
|
+
]);
|
|
251
|
+
if (!stat.isFile) {
|
|
252
|
+
throw new Error(`[TypeMismatchError]Not a file: ${fullPath}`);
|
|
253
|
+
}
|
|
254
|
+
const ext = path.split(".").pop()?.toLowerCase() || "";
|
|
255
|
+
const mimeTypes = {
|
|
256
|
+
txt: "text/plain",
|
|
257
|
+
html: "text/html",
|
|
258
|
+
htm: "text/html",
|
|
259
|
+
css: "text/css",
|
|
260
|
+
js: "text/javascript",
|
|
261
|
+
json: "application/json",
|
|
262
|
+
xml: "application/xml",
|
|
263
|
+
png: "image/png",
|
|
264
|
+
jpg: "image/jpeg",
|
|
265
|
+
jpeg: "image/jpeg",
|
|
266
|
+
gif: "image/gif",
|
|
267
|
+
svg: "image/svg+xml",
|
|
268
|
+
pdf: "application/pdf"
|
|
269
|
+
};
|
|
270
|
+
const type = mimeTypes[ext] || "application/octet-stream";
|
|
271
|
+
return {
|
|
272
|
+
size: stat.size,
|
|
273
|
+
lastModified: stat.lastModified ?? Date.now(),
|
|
274
|
+
type
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
export {
|
|
280
|
+
createCallbackFileSystemHandler
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
//# debugId=5A61ECFB1E29512E64756E2164756E21
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/callback-fs-handler.ts"],
|
|
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 { FsCallbackRegistrations, CallbackRegistration } from \"@ricsam/isolate-protocol\";\nimport type { ConnectionState } from \"./types.mjs\";\n\ninterface InvokeClientCallback {\n (connection: ConnectionState, callbackId: number, args: unknown[]): Promise<unknown>;\n}\n\ninterface CallbackFsHandlerOptions {\n connection: ConnectionState;\n callbacks: FsCallbackRegistrations;\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 */\nexport function createCallbackFileSystemHandler(\n options: CallbackFsHandlerOptions\n): FileSystemHandler {\n const { connection, callbacks, 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 return {\n async getFileHandle(path: string, opts?: { create?: boolean }): Promise<void> {\n const fullPath = resolvePath(path);\n\n if (opts?.create) {\n // Ensure file exists by writing empty content if it doesn't exist\n if (callbacks.writeFile) {\n try {\n // Check if file exists first\n if (callbacks.stat) {\n try {\n await invokeClientCallback(connection, callbacks.stat.callbackId, [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(connection, callbacks.writeFile.callbackId, [\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 if (callbacks.stat) {\n try {\n const result = (await invokeClientCallback(connection, callbacks.stat.callbackId, [\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\n if (opts?.create) {\n if (callbacks.mkdir) {\n try {\n await invokeClientCallback(connection, callbacks.mkdir.callbackId, [\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 if (callbacks.stat) {\n try {\n const result = (await invokeClientCallback(connection, callbacks.stat.callbackId, [\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\n // Check if it's a file or directory\n let isFile = true;\n if (callbacks.stat) {\n try {\n const result = (await invokeClientCallback(connection, callbacks.stat.callbackId, [\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 if (!callbacks.unlink) {\n throw new Error(`[NotAllowedError]File deletion not supported`);\n }\n await invokeClientCallback(connection, callbacks.unlink.callbackId, [fullPath]);\n } else {\n if (!callbacks.rmdir) {\n throw new Error(`[NotAllowedError]Directory deletion not supported`);\n }\n // Note: recursive option may need special handling\n await invokeClientCallback(connection, callbacks.rmdir.callbackId, [fullPath]);\n }\n },\n\n async readDirectory(\n path: string\n ): Promise<Array<{ name: string; kind: \"file\" | \"directory\" }>> {\n const fullPath = resolvePath(path);\n\n if (!callbacks.readdir) {\n throw new Error(`[NotAllowedError]Directory reading not supported`);\n }\n\n const entries = (await invokeClientCallback(connection, callbacks.readdir.callbackId, [\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 for (const name of entries) {\n const entryPath = fullPath ? `${fullPath}/${name}` : name;\n let kind: \"file\" | \"directory\" = \"file\";\n\n if (callbacks.stat) {\n try {\n const stat = (await invokeClientCallback(connection, callbacks.stat.callbackId, [\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\n if (!callbacks.readFile) {\n throw new Error(`[NotAllowedError]File reading not supported`);\n }\n\n const data = (await invokeClientCallback(connection, callbacks.readFile.callbackId, [\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 if (callbacks.stat) {\n try {\n const stat = (await invokeClientCallback(connection, callbacks.stat.callbackId, [\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 mimeTypes: 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 const type = mimeTypes[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\n if (!callbacks.writeFile) {\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 if (callbacks.readFile) {\n try {\n const existing = (await invokeClientCallback(\n connection,\n callbacks.readFile.callbackId,\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(connection, callbacks.writeFile.callbackId, [\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(connection, callbacks.writeFile.callbackId, [\n fullPath,\n newData,\n ]);\n return;\n }\n }\n }\n\n await invokeClientCallback(connection, callbacks.writeFile.callbackId, [fullPath, data]);\n },\n\n async truncateFile(path: string, size: number): Promise<void> {\n const fullPath = resolvePath(path);\n\n if (!callbacks.readFile || !callbacks.writeFile) {\n throw new Error(`[NotAllowedError]File truncation not supported`);\n }\n\n // Read existing content\n const existing = (await invokeClientCallback(connection, callbacks.readFile.callbackId, [\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(connection, callbacks.writeFile.callbackId, [fullPath, truncated]);\n },\n\n async getFileMetadata(\n path: string\n ): Promise<{ size: number; lastModified: number; type: string }> {\n const fullPath = resolvePath(path);\n\n if (!callbacks.stat) {\n throw new Error(`[NotAllowedError]File stat not supported`);\n }\n\n const stat = (await invokeClientCallback(connection, callbacks.stat.callbackId, [\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 mimeTypes: 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 const type = mimeTypes[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
|
+
],
|
|
7
|
+
"mappings": "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",
|
|
8
|
+
"debugId": "5A61ECFB1E29512E64756E2164756E21",
|
|
9
|
+
"names": []
|
|
10
|
+
}
|