@lifo-sh/node-runner 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/dist/index.cjs +417 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +79 -0
- package/dist/index.d.ts +79 -0
- package/dist/index.js +390 -0
- package/dist/index.js.map +1 -0
- package/package.json +39 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,417 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
NodeRunner: () => NodeRunner
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(index_exports);
|
|
26
|
+
|
|
27
|
+
// src/runner.ts
|
|
28
|
+
var import_vfs = require("@lifo-sh/vfs");
|
|
29
|
+
|
|
30
|
+
// src/builtins.ts
|
|
31
|
+
var minimalPath = {
|
|
32
|
+
join(...parts) {
|
|
33
|
+
const joined = parts.join("/").replace(/\/+/g, "/");
|
|
34
|
+
return minimalPath.normalize(joined);
|
|
35
|
+
},
|
|
36
|
+
normalize(p) {
|
|
37
|
+
const parts = p.split("/");
|
|
38
|
+
const result = [];
|
|
39
|
+
for (const part of parts) {
|
|
40
|
+
if (part === "..") {
|
|
41
|
+
result.pop();
|
|
42
|
+
} else if (part !== "." && part !== "") {
|
|
43
|
+
result.push(part);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return (p.startsWith("/") ? "/" : "") + result.join("/");
|
|
47
|
+
},
|
|
48
|
+
dirname(p) {
|
|
49
|
+
const idx = p.lastIndexOf("/");
|
|
50
|
+
if (idx === -1) return ".";
|
|
51
|
+
if (idx === 0) return "/";
|
|
52
|
+
return p.slice(0, idx);
|
|
53
|
+
},
|
|
54
|
+
basename(p, ext) {
|
|
55
|
+
const base = p.slice(p.lastIndexOf("/") + 1);
|
|
56
|
+
if (ext && base.endsWith(ext)) return base.slice(0, -ext.length);
|
|
57
|
+
return base;
|
|
58
|
+
},
|
|
59
|
+
extname(p) {
|
|
60
|
+
const base = minimalPath.basename(p);
|
|
61
|
+
const dot = base.lastIndexOf(".");
|
|
62
|
+
if (dot <= 0) return "";
|
|
63
|
+
return base.slice(dot);
|
|
64
|
+
},
|
|
65
|
+
resolve(...parts) {
|
|
66
|
+
let resolved = "";
|
|
67
|
+
for (let i = parts.length - 1; i >= 0; i--) {
|
|
68
|
+
resolved = parts[i] + "/" + resolved;
|
|
69
|
+
if (parts[i].startsWith("/")) break;
|
|
70
|
+
}
|
|
71
|
+
return minimalPath.normalize(resolved);
|
|
72
|
+
},
|
|
73
|
+
isAbsolute(p) {
|
|
74
|
+
return p.startsWith("/");
|
|
75
|
+
},
|
|
76
|
+
sep: "/",
|
|
77
|
+
delimiter: ":",
|
|
78
|
+
posix: null
|
|
79
|
+
};
|
|
80
|
+
minimalPath.posix = minimalPath;
|
|
81
|
+
function createScriptConsole(stdout, stderr) {
|
|
82
|
+
const format = (...args) => args.map(
|
|
83
|
+
(a) => typeof a === "string" ? a : JSON.stringify(a, null, 2) ?? String(a)
|
|
84
|
+
).join(" ");
|
|
85
|
+
return {
|
|
86
|
+
log: (...args) => stdout.push(format(...args)),
|
|
87
|
+
error: (...args) => stderr.push(format(...args)),
|
|
88
|
+
warn: (...args) => stderr.push(format(...args)),
|
|
89
|
+
info: (...args) => stdout.push(format(...args)),
|
|
90
|
+
debug: (...args) => stdout.push(format(...args))
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
function createMinimalProcess(env, cwd, argv) {
|
|
94
|
+
return {
|
|
95
|
+
env,
|
|
96
|
+
cwd: () => cwd,
|
|
97
|
+
argv: ["node", ...argv],
|
|
98
|
+
exit: (code) => {
|
|
99
|
+
throw new ProcessExitError(code ?? 0);
|
|
100
|
+
},
|
|
101
|
+
platform: "browser",
|
|
102
|
+
arch: "wasm",
|
|
103
|
+
version: "v20.0.0",
|
|
104
|
+
versions: { node: "20.0.0" },
|
|
105
|
+
pid: 1,
|
|
106
|
+
ppid: 0,
|
|
107
|
+
stdout: {
|
|
108
|
+
write: (_s) => {
|
|
109
|
+
return true;
|
|
110
|
+
}
|
|
111
|
+
},
|
|
112
|
+
stderr: {
|
|
113
|
+
write: (_s) => {
|
|
114
|
+
return true;
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
nextTick: (fn) => Promise.resolve().then(fn),
|
|
118
|
+
hrtime: {
|
|
119
|
+
bigint: () => BigInt(Math.round(performance.now() * 1e6))
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
var ProcessExitError = class extends Error {
|
|
124
|
+
code;
|
|
125
|
+
constructor(code) {
|
|
126
|
+
super(`process.exit(${code})`);
|
|
127
|
+
this.name = "ProcessExitError";
|
|
128
|
+
this.code = code;
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
function createBuiltinModules(_vfs, env, cwd, argv, scriptConsole, nodeCompat) {
|
|
132
|
+
const builtins = /* @__PURE__ */ new Map();
|
|
133
|
+
builtins.set("path", nodeCompat?.path ?? minimalPath);
|
|
134
|
+
const process = nodeCompat?.process ?? createMinimalProcess(env, cwd, argv);
|
|
135
|
+
builtins.set("process", process);
|
|
136
|
+
const console_ = scriptConsole;
|
|
137
|
+
builtins.set("console", console_);
|
|
138
|
+
if (nodeCompat?.fs) builtins.set("fs", nodeCompat.fs);
|
|
139
|
+
if (nodeCompat?.fsPromises) builtins.set("fs/promises", nodeCompat.fsPromises);
|
|
140
|
+
if (nodeCompat?.events) builtins.set("events", nodeCompat.events);
|
|
141
|
+
if (nodeCompat?.buffer) builtins.set("buffer", nodeCompat.buffer);
|
|
142
|
+
if (nodeCompat?.util) builtins.set("util", nodeCompat.util);
|
|
143
|
+
if (nodeCompat?.timers) builtins.set("timers", nodeCompat.timers);
|
|
144
|
+
if (nodeCompat?.assert) builtins.set("assert", nodeCompat.assert);
|
|
145
|
+
if (nodeCompat?.os) builtins.set("os", nodeCompat.os);
|
|
146
|
+
if (nodeCompat?.stream) builtins.set("stream", nodeCompat.stream);
|
|
147
|
+
if (nodeCompat?.url) builtins.set("url", nodeCompat.url);
|
|
148
|
+
if (nodeCompat?.crypto) builtins.set("crypto", nodeCompat.crypto);
|
|
149
|
+
return builtins;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// src/resolver.ts
|
|
153
|
+
var textDecoder = new TextDecoder();
|
|
154
|
+
function resolveModule(vfs, id, fromDir) {
|
|
155
|
+
const candidates = buildCandidates(id, fromDir);
|
|
156
|
+
for (const candidate of candidates) {
|
|
157
|
+
try {
|
|
158
|
+
if (!vfs.existsSync(candidate)) continue;
|
|
159
|
+
const stat = vfs.statSync(candidate);
|
|
160
|
+
if (stat.isDirectory()) {
|
|
161
|
+
const fromPkg = tryPackageMain(vfs, candidate);
|
|
162
|
+
if (fromPkg) return fromPkg;
|
|
163
|
+
const fromIndex = tryIndexFile(vfs, candidate);
|
|
164
|
+
if (fromIndex) return fromIndex;
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
if (stat.isFile()) {
|
|
168
|
+
const source = textDecoder.decode(vfs.readFileSync(candidate));
|
|
169
|
+
return { path: candidate, source };
|
|
170
|
+
}
|
|
171
|
+
} catch {
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return null;
|
|
175
|
+
}
|
|
176
|
+
function buildCandidates(id, fromDir) {
|
|
177
|
+
const candidates = [];
|
|
178
|
+
if (id.startsWith("./") || id.startsWith("../") || id.startsWith("/")) {
|
|
179
|
+
const resolved = id.startsWith("/") ? id : normalizePath(fromDir + "/" + id);
|
|
180
|
+
candidates.push(resolved);
|
|
181
|
+
candidates.push(resolved + ".js");
|
|
182
|
+
candidates.push(resolved + ".ts");
|
|
183
|
+
candidates.push(resolved + ".mjs");
|
|
184
|
+
candidates.push(resolved + ".json");
|
|
185
|
+
} else {
|
|
186
|
+
let dir = fromDir;
|
|
187
|
+
while (true) {
|
|
188
|
+
candidates.push(normalizePath(dir + "/node_modules/" + id));
|
|
189
|
+
candidates.push(normalizePath(dir + "/node_modules/" + id + ".js"));
|
|
190
|
+
candidates.push(normalizePath(dir + "/node_modules/" + id + ".json"));
|
|
191
|
+
const parent = dir.slice(0, dir.lastIndexOf("/"));
|
|
192
|
+
if (parent === dir || parent === "") break;
|
|
193
|
+
dir = parent;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
return candidates;
|
|
197
|
+
}
|
|
198
|
+
function tryPackageMain(vfs, dirPath) {
|
|
199
|
+
const pkgPath = dirPath + "/package.json";
|
|
200
|
+
try {
|
|
201
|
+
if (!vfs.existsSync(pkgPath)) return null;
|
|
202
|
+
const raw = textDecoder.decode(vfs.readFileSync(pkgPath));
|
|
203
|
+
const pkg = JSON.parse(raw);
|
|
204
|
+
const main = pkg.main || pkg.module || "index.js";
|
|
205
|
+
return resolveModule(vfs, "./" + main, dirPath);
|
|
206
|
+
} catch {
|
|
207
|
+
return null;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
function tryIndexFile(vfs, dirPath) {
|
|
211
|
+
for (const name of ["index.js", "index.ts", "index.mjs", "index.json"]) {
|
|
212
|
+
const fullPath = dirPath + "/" + name;
|
|
213
|
+
try {
|
|
214
|
+
if (!vfs.existsSync(fullPath)) continue;
|
|
215
|
+
const source = textDecoder.decode(vfs.readFileSync(fullPath));
|
|
216
|
+
return { path: fullPath, source };
|
|
217
|
+
} catch {
|
|
218
|
+
continue;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
return null;
|
|
222
|
+
}
|
|
223
|
+
function normalizePath(p) {
|
|
224
|
+
const parts = p.split("/");
|
|
225
|
+
const result = [];
|
|
226
|
+
for (const part of parts) {
|
|
227
|
+
if (part === "..") result.pop();
|
|
228
|
+
else if (part !== "." && part !== "") result.push(part);
|
|
229
|
+
}
|
|
230
|
+
return (p.startsWith("/") ? "/" : "") + result.join("/");
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// src/runner.ts
|
|
234
|
+
var textEncoder = new TextEncoder();
|
|
235
|
+
var NodeRunner = class {
|
|
236
|
+
vfs;
|
|
237
|
+
nodeCompat;
|
|
238
|
+
env;
|
|
239
|
+
cwd;
|
|
240
|
+
argv;
|
|
241
|
+
constructor(options) {
|
|
242
|
+
this.vfs = options.vfs ?? (0, import_vfs.createVFS)();
|
|
243
|
+
this.nodeCompat = options.nodeCompat;
|
|
244
|
+
this.env = options.env ?? {};
|
|
245
|
+
this.cwd = options.cwd ?? "/";
|
|
246
|
+
this.argv = options.argv ?? [];
|
|
247
|
+
if (options.files) {
|
|
248
|
+
this.writeFilesSync(options.files);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
/** Write files to the VFS synchronously, creating parent directories as needed */
|
|
252
|
+
writeFilesSync(files) {
|
|
253
|
+
for (const [filePath, content] of Object.entries(files)) {
|
|
254
|
+
const parts = filePath.split("/").filter(Boolean);
|
|
255
|
+
for (let i = 1; i < parts.length; i++) {
|
|
256
|
+
const dir = "/" + parts.slice(0, i).join("/");
|
|
257
|
+
try {
|
|
258
|
+
this.vfs.mkdirSync(dir);
|
|
259
|
+
} catch {
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
this.vfs.writeFileSync(filePath, textEncoder.encode(content));
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
/** Write files to the VFS asynchronously (persists to IndexedDB backend) */
|
|
266
|
+
async writeFiles(files) {
|
|
267
|
+
for (const [filePath, content] of Object.entries(files)) {
|
|
268
|
+
const parts = filePath.split("/").filter(Boolean);
|
|
269
|
+
for (let i = 1; i < parts.length; i++) {
|
|
270
|
+
const dir = "/" + parts.slice(0, i).join("/");
|
|
271
|
+
try {
|
|
272
|
+
await this.vfs.mkdir(dir);
|
|
273
|
+
} catch {
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
await this.vfs.writeFile(filePath, textEncoder.encode(content));
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
/** Run a script file from the VFS, resolving extensions and index files */
|
|
280
|
+
async runFile(filePath) {
|
|
281
|
+
const absPath = filePath.startsWith("/") ? filePath : this.cwd + "/" + filePath;
|
|
282
|
+
const scriptDir = absPath.slice(0, absPath.lastIndexOf("/")) || "/";
|
|
283
|
+
const id = absPath.startsWith("/") ? absPath : "./" + absPath;
|
|
284
|
+
const resolved = resolveModule(this.vfs, id, scriptDir);
|
|
285
|
+
if (!resolved) {
|
|
286
|
+
return {
|
|
287
|
+
exitCode: 1,
|
|
288
|
+
stdout: "",
|
|
289
|
+
stderr: `Error: ENOENT: no such file or directory, open '${absPath}'`
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
return this.execute(resolved.source, resolved.path);
|
|
293
|
+
}
|
|
294
|
+
/** Run a script from a source string */
|
|
295
|
+
async runScript(source, filename) {
|
|
296
|
+
const absPath = filename ? filename.startsWith("/") ? filename : this.cwd + "/" + filename : this.cwd + "/__script__.js";
|
|
297
|
+
return this.execute(source, absPath);
|
|
298
|
+
}
|
|
299
|
+
/** Write a file to the VFS then run it */
|
|
300
|
+
async writeAndRun(filePath, source) {
|
|
301
|
+
this.writeFilesSync({ [filePath]: source });
|
|
302
|
+
return this.runFile(filePath);
|
|
303
|
+
}
|
|
304
|
+
execute(source, absPath) {
|
|
305
|
+
const stdoutLines = [];
|
|
306
|
+
const stderrLines = [];
|
|
307
|
+
const scriptConsole = createScriptConsole(stdoutLines, stderrLines);
|
|
308
|
+
const builtins = createBuiltinModules(
|
|
309
|
+
this.vfs,
|
|
310
|
+
this.env,
|
|
311
|
+
this.cwd,
|
|
312
|
+
[absPath, ...this.argv],
|
|
313
|
+
scriptConsole,
|
|
314
|
+
this.nodeCompat
|
|
315
|
+
);
|
|
316
|
+
const moduleCache = /* @__PURE__ */ new Map();
|
|
317
|
+
const createRequire = (fromDir) => {
|
|
318
|
+
const require2 = (id) => {
|
|
319
|
+
if (builtins.has(id)) return builtins.get(id);
|
|
320
|
+
const resolved = resolveModule(this.vfs, id, fromDir);
|
|
321
|
+
if (!resolved) {
|
|
322
|
+
throw new Error(`Cannot find module '${id}'`);
|
|
323
|
+
}
|
|
324
|
+
if (moduleCache.has(resolved.path)) {
|
|
325
|
+
return moduleCache.get(resolved.path);
|
|
326
|
+
}
|
|
327
|
+
if (resolved.path.endsWith(".json")) {
|
|
328
|
+
const json = JSON.parse(resolved.source);
|
|
329
|
+
moduleCache.set(resolved.path, json);
|
|
330
|
+
return json;
|
|
331
|
+
}
|
|
332
|
+
const moduleExports = {};
|
|
333
|
+
const moduleObj = { exports: moduleExports };
|
|
334
|
+
moduleCache.set(resolved.path, moduleExports);
|
|
335
|
+
const moduleDir = resolved.path.slice(
|
|
336
|
+
0,
|
|
337
|
+
resolved.path.lastIndexOf("/")
|
|
338
|
+
);
|
|
339
|
+
const childRequire = createRequire(moduleDir);
|
|
340
|
+
const factory = compileModule(resolved.source, resolved.path);
|
|
341
|
+
factory(
|
|
342
|
+
moduleObj.exports,
|
|
343
|
+
childRequire,
|
|
344
|
+
moduleObj,
|
|
345
|
+
resolved.path,
|
|
346
|
+
moduleDir,
|
|
347
|
+
scriptConsole,
|
|
348
|
+
builtins.get("process")
|
|
349
|
+
);
|
|
350
|
+
moduleCache.set(resolved.path, moduleObj.exports);
|
|
351
|
+
return moduleObj.exports;
|
|
352
|
+
};
|
|
353
|
+
require2.resolve = (id) => {
|
|
354
|
+
if (builtins.has(id)) return id;
|
|
355
|
+
const resolved = resolveModule(this.vfs, id, fromDir);
|
|
356
|
+
if (!resolved) throw new Error(`Cannot find module '${id}'`);
|
|
357
|
+
return resolved.path;
|
|
358
|
+
};
|
|
359
|
+
return require2;
|
|
360
|
+
};
|
|
361
|
+
let exitCode = 0;
|
|
362
|
+
try {
|
|
363
|
+
const scriptDir = absPath.slice(0, absPath.lastIndexOf("/")) || "/";
|
|
364
|
+
const require2 = createRequire(scriptDir);
|
|
365
|
+
const factory = compileModule(source, absPath);
|
|
366
|
+
const moduleExports = {};
|
|
367
|
+
const moduleObj = { exports: moduleExports };
|
|
368
|
+
const processObj = builtins.get("process");
|
|
369
|
+
if (processObj) {
|
|
370
|
+
const stdout = processObj.stdout;
|
|
371
|
+
const stderr = processObj.stderr;
|
|
372
|
+
if (stdout)
|
|
373
|
+
stdout.write = (s) => {
|
|
374
|
+
stdoutLines.push(String(s));
|
|
375
|
+
return true;
|
|
376
|
+
};
|
|
377
|
+
if (stderr)
|
|
378
|
+
stderr.write = (s) => {
|
|
379
|
+
stderrLines.push(String(s));
|
|
380
|
+
return true;
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
factory(moduleObj.exports, require2, moduleObj, absPath, scriptDir, scriptConsole, builtins.get("process"));
|
|
384
|
+
} catch (err) {
|
|
385
|
+
if (err instanceof ProcessExitError) {
|
|
386
|
+
exitCode = err.code;
|
|
387
|
+
} else {
|
|
388
|
+
exitCode = 1;
|
|
389
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
390
|
+
const stack = err instanceof Error && err.stack ? err.stack : "";
|
|
391
|
+
stderrLines.push(stack || message);
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
return {
|
|
395
|
+
exitCode,
|
|
396
|
+
stdout: stdoutLines.join("\n"),
|
|
397
|
+
stderr: stderrLines.join("\n")
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
};
|
|
401
|
+
function compileModule(source, filename) {
|
|
402
|
+
const wrapped = `(function(exports, require, module, __filename, __dirname, console, process) {
|
|
403
|
+
${source}
|
|
404
|
+
})`;
|
|
405
|
+
try {
|
|
406
|
+
return (0, eval)(wrapped);
|
|
407
|
+
} catch (err) {
|
|
408
|
+
throw new SyntaxError(
|
|
409
|
+
`Failed to compile '${filename}': ${err instanceof Error ? err.message : String(err)}`
|
|
410
|
+
);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
414
|
+
0 && (module.exports = {
|
|
415
|
+
NodeRunner
|
|
416
|
+
});
|
|
417
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/runner.ts","../src/builtins.ts","../src/resolver.ts"],"sourcesContent":["export { NodeRunner } from \"./runner.js\";\nexport type {\n NodeRunnerOptions,\n RunResult,\n NodeCompatModules,\n ScriptConsole,\n ResolvedModule,\n} from \"./types.js\";\n","import { createVFS, type VFS } from \"@lifo-sh/vfs\";\nimport type {\n NodeRunnerOptions,\n NodeCompatModules,\n RunResult,\n ModuleFactory,\n} from \"./types.js\";\nimport {\n createBuiltinModules,\n createScriptConsole,\n ProcessExitError,\n} from \"./builtins.js\";\nimport { resolveModule } from \"./resolver.js\";\n\nconst textEncoder = new TextEncoder();\n\nexport class NodeRunner {\n readonly vfs: VFS;\n private nodeCompat?: NodeCompatModules;\n private env: Record<string, string>;\n private cwd: string;\n private argv: string[];\n\n constructor(options: NodeRunnerOptions) {\n this.vfs = options.vfs ?? createVFS();\n this.nodeCompat = options.nodeCompat;\n this.env = options.env ?? {};\n this.cwd = options.cwd ?? \"/\";\n this.argv = options.argv ?? [];\n\n if (options.files) {\n this.writeFilesSync(options.files);\n }\n }\n\n /** Write files to the VFS synchronously, creating parent directories as needed */\n writeFilesSync(files: Record<string, string>): void {\n for (const [filePath, content] of Object.entries(files)) {\n const parts = filePath.split(\"/\").filter(Boolean);\n for (let i = 1; i < parts.length; i++) {\n const dir = \"/\" + parts.slice(0, i).join(\"/\");\n try {\n this.vfs.mkdirSync(dir);\n } catch {\n // already exists\n }\n }\n this.vfs.writeFileSync(filePath, textEncoder.encode(content));\n }\n }\n\n /** Write files to the VFS asynchronously (persists to IndexedDB backend) */\n async writeFiles(files: Record<string, string>): Promise<void> {\n for (const [filePath, content] of Object.entries(files)) {\n const parts = filePath.split(\"/\").filter(Boolean);\n for (let i = 1; i < parts.length; i++) {\n const dir = \"/\" + parts.slice(0, i).join(\"/\");\n try {\n await this.vfs.mkdir(dir);\n } catch {\n // already exists\n }\n }\n await this.vfs.writeFile(filePath, textEncoder.encode(content));\n }\n }\n\n /** Run a script file from the VFS, resolving extensions and index files */\n async runFile(filePath: string): Promise<RunResult> {\n const absPath = filePath.startsWith(\"/\")\n ? filePath\n : this.cwd + \"/\" + filePath;\n\n const scriptDir = absPath.slice(0, absPath.lastIndexOf(\"/\")) || \"/\";\n const id = absPath.startsWith(\"/\") ? absPath : \"./\" + absPath;\n const resolved = resolveModule(this.vfs, id, scriptDir);\n\n if (!resolved) {\n return {\n exitCode: 1,\n stdout: \"\",\n stderr: `Error: ENOENT: no such file or directory, open '${absPath}'`,\n };\n }\n\n return this.execute(resolved.source, resolved.path);\n }\n\n /** Run a script from a source string */\n async runScript(source: string, filename?: string): Promise<RunResult> {\n const absPath = filename\n ? filename.startsWith(\"/\")\n ? filename\n : this.cwd + \"/\" + filename\n : this.cwd + \"/__script__.js\";\n\n return this.execute(source, absPath);\n }\n\n /** Write a file to the VFS then run it */\n async writeAndRun(filePath: string, source: string): Promise<RunResult> {\n this.writeFilesSync({ [filePath]: source });\n return this.runFile(filePath);\n }\n\n private execute(source: string, absPath: string): RunResult {\n const stdoutLines: string[] = [];\n const stderrLines: string[] = [];\n const scriptConsole = createScriptConsole(stdoutLines, stderrLines);\n\n const builtins = createBuiltinModules(\n this.vfs,\n this.env,\n this.cwd,\n [absPath, ...this.argv],\n scriptConsole,\n this.nodeCompat,\n );\n\n // Module cache (keyed by absolute path)\n const moduleCache = new Map<string, Record<string, unknown>>();\n\n const createRequire = (fromDir: string) => {\n const require = (id: string): unknown => {\n // Check builtins first\n if (builtins.has(id)) return builtins.get(id);\n\n // Resolve from VFS\n const resolved = resolveModule(this.vfs, id, fromDir);\n if (!resolved) {\n throw new Error(`Cannot find module '${id}'`);\n }\n\n // Return cached if available\n if (moduleCache.has(resolved.path)) {\n return moduleCache.get(resolved.path);\n }\n\n // Handle JSON\n if (resolved.path.endsWith(\".json\")) {\n const json = JSON.parse(resolved.source);\n moduleCache.set(resolved.path, json);\n return json;\n }\n\n // Execute module\n const moduleExports: Record<string, unknown> = {};\n const moduleObj = { exports: moduleExports };\n moduleCache.set(resolved.path, moduleExports);\n\n const moduleDir = resolved.path.slice(\n 0,\n resolved.path.lastIndexOf(\"/\"),\n );\n const childRequire = createRequire(moduleDir);\n\n const factory = compileModule(resolved.source, resolved.path);\n factory(\n moduleObj.exports,\n childRequire,\n moduleObj,\n resolved.path,\n moduleDir,\n scriptConsole,\n builtins.get(\"process\"),\n );\n\n // Update cache with potentially reassigned module.exports\n moduleCache.set(resolved.path, moduleObj.exports);\n return moduleObj.exports;\n };\n\n require.resolve = (id: string): string => {\n if (builtins.has(id)) return id;\n const resolved = resolveModule(this.vfs, id, fromDir);\n if (!resolved) throw new Error(`Cannot find module '${id}'`);\n return resolved.path;\n };\n\n return require;\n };\n\n let exitCode = 0;\n\n try {\n const scriptDir = absPath.slice(0, absPath.lastIndexOf(\"/\")) || \"/\";\n const require = createRequire(scriptDir);\n const factory = compileModule(source, absPath);\n const moduleExports: Record<string, unknown> = {};\n const moduleObj = { exports: moduleExports };\n\n // Wire process.stdout/stderr to capture\n const processObj = builtins.get(\"process\") as Record<string, unknown>;\n if (processObj) {\n const stdout = processObj.stdout as Record<string, unknown>;\n const stderr = processObj.stderr as Record<string, unknown>;\n if (stdout)\n stdout.write = (s: string) => {\n stdoutLines.push(String(s));\n return true;\n };\n if (stderr)\n stderr.write = (s: string) => {\n stderrLines.push(String(s));\n return true;\n };\n }\n\n factory(moduleObj.exports, require, moduleObj, absPath, scriptDir, scriptConsole, builtins.get(\"process\"));\n } catch (err) {\n if (err instanceof ProcessExitError) {\n exitCode = err.code;\n } else {\n exitCode = 1;\n const message =\n err instanceof Error ? err.message : String(err);\n const stack =\n err instanceof Error && err.stack ? err.stack : \"\";\n stderrLines.push(stack || message);\n }\n }\n\n return {\n exitCode,\n stdout: stdoutLines.join(\"\\n\"),\n stderr: stderrLines.join(\"\\n\"),\n };\n }\n}\n\n/** Compile source code into a module factory function */\nfunction compileModule(source: string, filename: string): ModuleFactory {\n // Wrap in a function with CommonJS-style arguments + console/process injected as params\n const wrapped = `(function(exports, require, module, __filename, __dirname, console, process) {\\n${source}\\n})`;\n try {\n // eslint-disable-next-line no-eval\n return (0, eval)(wrapped) as ModuleFactory;\n } catch (err) {\n throw new SyntaxError(\n `Failed to compile '${filename}': ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n}\n","import type { NodeCompatModules, ScriptConsole } from \"./types.js\";\nimport type { VFS } from \"@lifo-sh/vfs\";\n\n/** Minimal path module (always available, no node-compat needed) */\nconst minimalPath = {\n join(...parts: string[]): string {\n const joined = parts.join(\"/\").replace(/\\/+/g, \"/\");\n return minimalPath.normalize(joined);\n },\n normalize(p: string): string {\n const parts = p.split(\"/\");\n const result: string[] = [];\n for (const part of parts) {\n if (part === \"..\") {\n result.pop();\n } else if (part !== \".\" && part !== \"\") {\n result.push(part);\n }\n }\n return (p.startsWith(\"/\") ? \"/\" : \"\") + result.join(\"/\");\n },\n dirname(p: string): string {\n const idx = p.lastIndexOf(\"/\");\n if (idx === -1) return \".\";\n if (idx === 0) return \"/\";\n return p.slice(0, idx);\n },\n basename(p: string, ext?: string): string {\n const base = p.slice(p.lastIndexOf(\"/\") + 1);\n if (ext && base.endsWith(ext)) return base.slice(0, -ext.length);\n return base;\n },\n extname(p: string): string {\n const base = minimalPath.basename(p);\n const dot = base.lastIndexOf(\".\");\n if (dot <= 0) return \"\";\n return base.slice(dot);\n },\n resolve(...parts: string[]): string {\n let resolved = \"\";\n for (let i = parts.length - 1; i >= 0; i--) {\n resolved = parts[i] + \"/\" + resolved;\n if (parts[i].startsWith(\"/\")) break;\n }\n return minimalPath.normalize(resolved);\n },\n isAbsolute(p: string): boolean {\n return p.startsWith(\"/\");\n },\n sep: \"/\",\n delimiter: \":\",\n posix: null as unknown,\n};\nminimalPath.posix = minimalPath;\n\n/** Minimal console that captures output */\nexport function createScriptConsole(\n stdout: string[],\n stderr: string[],\n): ScriptConsole {\n const format = (...args: unknown[]): string =>\n args\n .map((a) =>\n typeof a === \"string\" ? a : JSON.stringify(a, null, 2) ?? String(a),\n )\n .join(\" \");\n\n return {\n log: (...args) => stdout.push(format(...args)),\n error: (...args) => stderr.push(format(...args)),\n warn: (...args) => stderr.push(format(...args)),\n info: (...args) => stdout.push(format(...args)),\n debug: (...args) => stdout.push(format(...args)),\n };\n}\n\n/** Minimal process object (always available) */\nexport function createMinimalProcess(\n env: Record<string, string>,\n cwd: string,\n argv: string[],\n) {\n return {\n env,\n cwd: () => cwd,\n argv: [\"node\", ...argv],\n exit: (code?: number) => {\n throw new ProcessExitError(code ?? 0);\n },\n platform: \"browser\" as const,\n arch: \"wasm\" as const,\n version: \"v20.0.0\",\n versions: { node: \"20.0.0\" },\n pid: 1,\n ppid: 0,\n stdout: {\n write: (_s: string) => {\n /* captured via console */\n return true;\n },\n },\n stderr: {\n write: (_s: string) => {\n return true;\n },\n },\n nextTick: (fn: () => void) => Promise.resolve().then(fn),\n hrtime: {\n bigint: () => BigInt(Math.round(performance.now() * 1e6)),\n },\n };\n}\n\nexport class ProcessExitError extends Error {\n readonly code: number;\n constructor(code: number) {\n super(`process.exit(${code})`);\n this.name = \"ProcessExitError\";\n this.code = code;\n }\n}\n\n/** Build the full builtin module map */\nexport function createBuiltinModules(\n _vfs: VFS,\n env: Record<string, string>,\n cwd: string,\n argv: string[],\n scriptConsole: ScriptConsole,\n nodeCompat?: NodeCompatModules,\n): Map<string, unknown> {\n const builtins = new Map<string, unknown>();\n\n // Always available (no deps)\n builtins.set(\"path\", nodeCompat?.path ?? minimalPath);\n\n const process = nodeCompat?.process ?? createMinimalProcess(env, cwd, argv);\n builtins.set(\"process\", process);\n\n // Wire console output to stdout/stderr capture\n const console_ = scriptConsole;\n builtins.set(\"console\", console_);\n\n // node-compat modules (if provided)\n if (nodeCompat?.fs) builtins.set(\"fs\", nodeCompat.fs);\n if (nodeCompat?.fsPromises) builtins.set(\"fs/promises\", nodeCompat.fsPromises);\n if (nodeCompat?.events) builtins.set(\"events\", nodeCompat.events);\n if (nodeCompat?.buffer) builtins.set(\"buffer\", nodeCompat.buffer);\n if (nodeCompat?.util) builtins.set(\"util\", nodeCompat.util);\n if (nodeCompat?.timers) builtins.set(\"timers\", nodeCompat.timers);\n if (nodeCompat?.assert) builtins.set(\"assert\", nodeCompat.assert);\n if (nodeCompat?.os) builtins.set(\"os\", nodeCompat.os);\n if (nodeCompat?.stream) builtins.set(\"stream\", nodeCompat.stream);\n if (nodeCompat?.url) builtins.set(\"url\", nodeCompat.url);\n if (nodeCompat?.crypto) builtins.set(\"crypto\", nodeCompat.crypto);\n\n return builtins;\n}\n","import type { VFS } from \"@lifo-sh/vfs\";\nimport type { ResolvedModule } from \"./types.js\";\n\nconst textDecoder = new TextDecoder();\n\n/** Resolve and load a module from the VFS */\nexport function resolveModule(\n vfs: VFS,\n id: string,\n fromDir: string,\n): ResolvedModule | null {\n // Try direct path first, then with extensions\n const candidates = buildCandidates(id, fromDir);\n\n for (const candidate of candidates) {\n try {\n if (!vfs.existsSync(candidate)) continue;\n const stat = vfs.statSync(candidate);\n\n if (stat.isDirectory()) {\n // Try package.json main, then index.js\n const fromPkg = tryPackageMain(vfs, candidate);\n if (fromPkg) return fromPkg;\n const fromIndex = tryIndexFile(vfs, candidate);\n if (fromIndex) return fromIndex;\n continue;\n }\n\n if (stat.isFile()) {\n const source = textDecoder.decode(vfs.readFileSync(candidate));\n return { path: candidate, source };\n }\n } catch {\n // File doesn't exist or can't be read, try next candidate\n }\n }\n\n return null;\n}\n\nfunction buildCandidates(id: string, fromDir: string): string[] {\n const candidates: string[] = [];\n\n if (id.startsWith(\"./\") || id.startsWith(\"../\") || id.startsWith(\"/\")) {\n // Relative/absolute path\n const resolved = id.startsWith(\"/\") ? id : normalizePath(fromDir + \"/\" + id);\n candidates.push(resolved);\n candidates.push(resolved + \".js\");\n candidates.push(resolved + \".ts\");\n candidates.push(resolved + \".mjs\");\n candidates.push(resolved + \".json\");\n } else {\n // Bare specifier → look in node_modules\n let dir = fromDir;\n while (true) {\n candidates.push(normalizePath(dir + \"/node_modules/\" + id));\n candidates.push(normalizePath(dir + \"/node_modules/\" + id + \".js\"));\n candidates.push(normalizePath(dir + \"/node_modules/\" + id + \".json\"));\n const parent = dir.slice(0, dir.lastIndexOf(\"/\"));\n if (parent === dir || parent === \"\") break;\n dir = parent;\n }\n }\n\n return candidates;\n}\n\nfunction tryPackageMain(vfs: VFS, dirPath: string): ResolvedModule | null {\n const pkgPath = dirPath + \"/package.json\";\n try {\n if (!vfs.existsSync(pkgPath)) return null;\n const raw = textDecoder.decode(vfs.readFileSync(pkgPath));\n const pkg = JSON.parse(raw);\n const main = pkg.main || pkg.module || \"index.js\";\n return resolveModule(vfs, \"./\" + main, dirPath);\n } catch {\n return null;\n }\n}\n\nfunction tryIndexFile(vfs: VFS, dirPath: string): ResolvedModule | null {\n for (const name of [\"index.js\", \"index.ts\", \"index.mjs\", \"index.json\"]) {\n const fullPath = dirPath + \"/\" + name;\n try {\n if (!vfs.existsSync(fullPath)) continue;\n const source = textDecoder.decode(vfs.readFileSync(fullPath));\n return { path: fullPath, source };\n } catch {\n continue;\n }\n }\n return null;\n}\n\nfunction normalizePath(p: string): string {\n const parts = p.split(\"/\");\n const result: string[] = [];\n for (const part of parts) {\n if (part === \"..\") result.pop();\n else if (part !== \".\" && part !== \"\") result.push(part);\n }\n return (p.startsWith(\"/\") ? \"/\" : \"\") + result.join(\"/\");\n}\n"],"mappings":"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","names":["require"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { VFS } from '@lifo-sh/vfs';
|
|
2
|
+
|
|
3
|
+
/** Options for creating a NodeRunner instance */
|
|
4
|
+
interface NodeRunnerOptions {
|
|
5
|
+
/** Virtual file system instance (defaults to IndexedDB-backed VFS) */
|
|
6
|
+
vfs?: VFS;
|
|
7
|
+
/** Files to pre-populate in the VFS (path → content) */
|
|
8
|
+
files?: Record<string, string>;
|
|
9
|
+
/** Optional node-compat modules to provide full Node.js API support */
|
|
10
|
+
nodeCompat?: NodeCompatModules;
|
|
11
|
+
/** Environment variables available to scripts via process.env */
|
|
12
|
+
env?: Record<string, string>;
|
|
13
|
+
/** Current working directory for script execution */
|
|
14
|
+
cwd?: string;
|
|
15
|
+
/** Arguments passed to the script (process.argv) */
|
|
16
|
+
argv?: string[];
|
|
17
|
+
}
|
|
18
|
+
/** Result of executing a script */
|
|
19
|
+
interface RunResult {
|
|
20
|
+
/** Exit code (0 = success) */
|
|
21
|
+
exitCode: number;
|
|
22
|
+
/** Captured stdout output */
|
|
23
|
+
stdout: string;
|
|
24
|
+
/** Captured stderr output */
|
|
25
|
+
stderr: string;
|
|
26
|
+
}
|
|
27
|
+
/** Optional node-compat module map for full Node.js API support */
|
|
28
|
+
interface NodeCompatModules {
|
|
29
|
+
fs?: unknown;
|
|
30
|
+
fsPromises?: unknown;
|
|
31
|
+
path?: unknown;
|
|
32
|
+
events?: unknown;
|
|
33
|
+
buffer?: unknown;
|
|
34
|
+
process?: unknown;
|
|
35
|
+
util?: unknown;
|
|
36
|
+
timers?: unknown;
|
|
37
|
+
assert?: unknown;
|
|
38
|
+
os?: unknown;
|
|
39
|
+
stream?: unknown;
|
|
40
|
+
url?: unknown;
|
|
41
|
+
crypto?: unknown;
|
|
42
|
+
}
|
|
43
|
+
/** Console-like interface for capturing script output */
|
|
44
|
+
interface ScriptConsole {
|
|
45
|
+
log(...args: unknown[]): void;
|
|
46
|
+
error(...args: unknown[]): void;
|
|
47
|
+
warn(...args: unknown[]): void;
|
|
48
|
+
info(...args: unknown[]): void;
|
|
49
|
+
debug(...args: unknown[]): void;
|
|
50
|
+
}
|
|
51
|
+
/** Module resolution result */
|
|
52
|
+
interface ResolvedModule {
|
|
53
|
+
/** Resolved absolute path */
|
|
54
|
+
path: string;
|
|
55
|
+
/** Module source code */
|
|
56
|
+
source: string;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
declare class NodeRunner {
|
|
60
|
+
readonly vfs: VFS;
|
|
61
|
+
private nodeCompat?;
|
|
62
|
+
private env;
|
|
63
|
+
private cwd;
|
|
64
|
+
private argv;
|
|
65
|
+
constructor(options: NodeRunnerOptions);
|
|
66
|
+
/** Write files to the VFS synchronously, creating parent directories as needed */
|
|
67
|
+
writeFilesSync(files: Record<string, string>): void;
|
|
68
|
+
/** Write files to the VFS asynchronously (persists to IndexedDB backend) */
|
|
69
|
+
writeFiles(files: Record<string, string>): Promise<void>;
|
|
70
|
+
/** Run a script file from the VFS, resolving extensions and index files */
|
|
71
|
+
runFile(filePath: string): Promise<RunResult>;
|
|
72
|
+
/** Run a script from a source string */
|
|
73
|
+
runScript(source: string, filename?: string): Promise<RunResult>;
|
|
74
|
+
/** Write a file to the VFS then run it */
|
|
75
|
+
writeAndRun(filePath: string, source: string): Promise<RunResult>;
|
|
76
|
+
private execute;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export { type NodeCompatModules, NodeRunner, type NodeRunnerOptions, type ResolvedModule, type RunResult, type ScriptConsole };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { VFS } from '@lifo-sh/vfs';
|
|
2
|
+
|
|
3
|
+
/** Options for creating a NodeRunner instance */
|
|
4
|
+
interface NodeRunnerOptions {
|
|
5
|
+
/** Virtual file system instance (defaults to IndexedDB-backed VFS) */
|
|
6
|
+
vfs?: VFS;
|
|
7
|
+
/** Files to pre-populate in the VFS (path → content) */
|
|
8
|
+
files?: Record<string, string>;
|
|
9
|
+
/** Optional node-compat modules to provide full Node.js API support */
|
|
10
|
+
nodeCompat?: NodeCompatModules;
|
|
11
|
+
/** Environment variables available to scripts via process.env */
|
|
12
|
+
env?: Record<string, string>;
|
|
13
|
+
/** Current working directory for script execution */
|
|
14
|
+
cwd?: string;
|
|
15
|
+
/** Arguments passed to the script (process.argv) */
|
|
16
|
+
argv?: string[];
|
|
17
|
+
}
|
|
18
|
+
/** Result of executing a script */
|
|
19
|
+
interface RunResult {
|
|
20
|
+
/** Exit code (0 = success) */
|
|
21
|
+
exitCode: number;
|
|
22
|
+
/** Captured stdout output */
|
|
23
|
+
stdout: string;
|
|
24
|
+
/** Captured stderr output */
|
|
25
|
+
stderr: string;
|
|
26
|
+
}
|
|
27
|
+
/** Optional node-compat module map for full Node.js API support */
|
|
28
|
+
interface NodeCompatModules {
|
|
29
|
+
fs?: unknown;
|
|
30
|
+
fsPromises?: unknown;
|
|
31
|
+
path?: unknown;
|
|
32
|
+
events?: unknown;
|
|
33
|
+
buffer?: unknown;
|
|
34
|
+
process?: unknown;
|
|
35
|
+
util?: unknown;
|
|
36
|
+
timers?: unknown;
|
|
37
|
+
assert?: unknown;
|
|
38
|
+
os?: unknown;
|
|
39
|
+
stream?: unknown;
|
|
40
|
+
url?: unknown;
|
|
41
|
+
crypto?: unknown;
|
|
42
|
+
}
|
|
43
|
+
/** Console-like interface for capturing script output */
|
|
44
|
+
interface ScriptConsole {
|
|
45
|
+
log(...args: unknown[]): void;
|
|
46
|
+
error(...args: unknown[]): void;
|
|
47
|
+
warn(...args: unknown[]): void;
|
|
48
|
+
info(...args: unknown[]): void;
|
|
49
|
+
debug(...args: unknown[]): void;
|
|
50
|
+
}
|
|
51
|
+
/** Module resolution result */
|
|
52
|
+
interface ResolvedModule {
|
|
53
|
+
/** Resolved absolute path */
|
|
54
|
+
path: string;
|
|
55
|
+
/** Module source code */
|
|
56
|
+
source: string;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
declare class NodeRunner {
|
|
60
|
+
readonly vfs: VFS;
|
|
61
|
+
private nodeCompat?;
|
|
62
|
+
private env;
|
|
63
|
+
private cwd;
|
|
64
|
+
private argv;
|
|
65
|
+
constructor(options: NodeRunnerOptions);
|
|
66
|
+
/** Write files to the VFS synchronously, creating parent directories as needed */
|
|
67
|
+
writeFilesSync(files: Record<string, string>): void;
|
|
68
|
+
/** Write files to the VFS asynchronously (persists to IndexedDB backend) */
|
|
69
|
+
writeFiles(files: Record<string, string>): Promise<void>;
|
|
70
|
+
/** Run a script file from the VFS, resolving extensions and index files */
|
|
71
|
+
runFile(filePath: string): Promise<RunResult>;
|
|
72
|
+
/** Run a script from a source string */
|
|
73
|
+
runScript(source: string, filename?: string): Promise<RunResult>;
|
|
74
|
+
/** Write a file to the VFS then run it */
|
|
75
|
+
writeAndRun(filePath: string, source: string): Promise<RunResult>;
|
|
76
|
+
private execute;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export { type NodeCompatModules, NodeRunner, type NodeRunnerOptions, type ResolvedModule, type RunResult, type ScriptConsole };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
// src/runner.ts
|
|
2
|
+
import { createVFS } from "@lifo-sh/vfs";
|
|
3
|
+
|
|
4
|
+
// src/builtins.ts
|
|
5
|
+
var minimalPath = {
|
|
6
|
+
join(...parts) {
|
|
7
|
+
const joined = parts.join("/").replace(/\/+/g, "/");
|
|
8
|
+
return minimalPath.normalize(joined);
|
|
9
|
+
},
|
|
10
|
+
normalize(p) {
|
|
11
|
+
const parts = p.split("/");
|
|
12
|
+
const result = [];
|
|
13
|
+
for (const part of parts) {
|
|
14
|
+
if (part === "..") {
|
|
15
|
+
result.pop();
|
|
16
|
+
} else if (part !== "." && part !== "") {
|
|
17
|
+
result.push(part);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return (p.startsWith("/") ? "/" : "") + result.join("/");
|
|
21
|
+
},
|
|
22
|
+
dirname(p) {
|
|
23
|
+
const idx = p.lastIndexOf("/");
|
|
24
|
+
if (idx === -1) return ".";
|
|
25
|
+
if (idx === 0) return "/";
|
|
26
|
+
return p.slice(0, idx);
|
|
27
|
+
},
|
|
28
|
+
basename(p, ext) {
|
|
29
|
+
const base = p.slice(p.lastIndexOf("/") + 1);
|
|
30
|
+
if (ext && base.endsWith(ext)) return base.slice(0, -ext.length);
|
|
31
|
+
return base;
|
|
32
|
+
},
|
|
33
|
+
extname(p) {
|
|
34
|
+
const base = minimalPath.basename(p);
|
|
35
|
+
const dot = base.lastIndexOf(".");
|
|
36
|
+
if (dot <= 0) return "";
|
|
37
|
+
return base.slice(dot);
|
|
38
|
+
},
|
|
39
|
+
resolve(...parts) {
|
|
40
|
+
let resolved = "";
|
|
41
|
+
for (let i = parts.length - 1; i >= 0; i--) {
|
|
42
|
+
resolved = parts[i] + "/" + resolved;
|
|
43
|
+
if (parts[i].startsWith("/")) break;
|
|
44
|
+
}
|
|
45
|
+
return minimalPath.normalize(resolved);
|
|
46
|
+
},
|
|
47
|
+
isAbsolute(p) {
|
|
48
|
+
return p.startsWith("/");
|
|
49
|
+
},
|
|
50
|
+
sep: "/",
|
|
51
|
+
delimiter: ":",
|
|
52
|
+
posix: null
|
|
53
|
+
};
|
|
54
|
+
minimalPath.posix = minimalPath;
|
|
55
|
+
function createScriptConsole(stdout, stderr) {
|
|
56
|
+
const format = (...args) => args.map(
|
|
57
|
+
(a) => typeof a === "string" ? a : JSON.stringify(a, null, 2) ?? String(a)
|
|
58
|
+
).join(" ");
|
|
59
|
+
return {
|
|
60
|
+
log: (...args) => stdout.push(format(...args)),
|
|
61
|
+
error: (...args) => stderr.push(format(...args)),
|
|
62
|
+
warn: (...args) => stderr.push(format(...args)),
|
|
63
|
+
info: (...args) => stdout.push(format(...args)),
|
|
64
|
+
debug: (...args) => stdout.push(format(...args))
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
function createMinimalProcess(env, cwd, argv) {
|
|
68
|
+
return {
|
|
69
|
+
env,
|
|
70
|
+
cwd: () => cwd,
|
|
71
|
+
argv: ["node", ...argv],
|
|
72
|
+
exit: (code) => {
|
|
73
|
+
throw new ProcessExitError(code ?? 0);
|
|
74
|
+
},
|
|
75
|
+
platform: "browser",
|
|
76
|
+
arch: "wasm",
|
|
77
|
+
version: "v20.0.0",
|
|
78
|
+
versions: { node: "20.0.0" },
|
|
79
|
+
pid: 1,
|
|
80
|
+
ppid: 0,
|
|
81
|
+
stdout: {
|
|
82
|
+
write: (_s) => {
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
stderr: {
|
|
87
|
+
write: (_s) => {
|
|
88
|
+
return true;
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
nextTick: (fn) => Promise.resolve().then(fn),
|
|
92
|
+
hrtime: {
|
|
93
|
+
bigint: () => BigInt(Math.round(performance.now() * 1e6))
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
var ProcessExitError = class extends Error {
|
|
98
|
+
code;
|
|
99
|
+
constructor(code) {
|
|
100
|
+
super(`process.exit(${code})`);
|
|
101
|
+
this.name = "ProcessExitError";
|
|
102
|
+
this.code = code;
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
function createBuiltinModules(_vfs, env, cwd, argv, scriptConsole, nodeCompat) {
|
|
106
|
+
const builtins = /* @__PURE__ */ new Map();
|
|
107
|
+
builtins.set("path", nodeCompat?.path ?? minimalPath);
|
|
108
|
+
const process = nodeCompat?.process ?? createMinimalProcess(env, cwd, argv);
|
|
109
|
+
builtins.set("process", process);
|
|
110
|
+
const console_ = scriptConsole;
|
|
111
|
+
builtins.set("console", console_);
|
|
112
|
+
if (nodeCompat?.fs) builtins.set("fs", nodeCompat.fs);
|
|
113
|
+
if (nodeCompat?.fsPromises) builtins.set("fs/promises", nodeCompat.fsPromises);
|
|
114
|
+
if (nodeCompat?.events) builtins.set("events", nodeCompat.events);
|
|
115
|
+
if (nodeCompat?.buffer) builtins.set("buffer", nodeCompat.buffer);
|
|
116
|
+
if (nodeCompat?.util) builtins.set("util", nodeCompat.util);
|
|
117
|
+
if (nodeCompat?.timers) builtins.set("timers", nodeCompat.timers);
|
|
118
|
+
if (nodeCompat?.assert) builtins.set("assert", nodeCompat.assert);
|
|
119
|
+
if (nodeCompat?.os) builtins.set("os", nodeCompat.os);
|
|
120
|
+
if (nodeCompat?.stream) builtins.set("stream", nodeCompat.stream);
|
|
121
|
+
if (nodeCompat?.url) builtins.set("url", nodeCompat.url);
|
|
122
|
+
if (nodeCompat?.crypto) builtins.set("crypto", nodeCompat.crypto);
|
|
123
|
+
return builtins;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// src/resolver.ts
|
|
127
|
+
var textDecoder = new TextDecoder();
|
|
128
|
+
function resolveModule(vfs, id, fromDir) {
|
|
129
|
+
const candidates = buildCandidates(id, fromDir);
|
|
130
|
+
for (const candidate of candidates) {
|
|
131
|
+
try {
|
|
132
|
+
if (!vfs.existsSync(candidate)) continue;
|
|
133
|
+
const stat = vfs.statSync(candidate);
|
|
134
|
+
if (stat.isDirectory()) {
|
|
135
|
+
const fromPkg = tryPackageMain(vfs, candidate);
|
|
136
|
+
if (fromPkg) return fromPkg;
|
|
137
|
+
const fromIndex = tryIndexFile(vfs, candidate);
|
|
138
|
+
if (fromIndex) return fromIndex;
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
if (stat.isFile()) {
|
|
142
|
+
const source = textDecoder.decode(vfs.readFileSync(candidate));
|
|
143
|
+
return { path: candidate, source };
|
|
144
|
+
}
|
|
145
|
+
} catch {
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
150
|
+
function buildCandidates(id, fromDir) {
|
|
151
|
+
const candidates = [];
|
|
152
|
+
if (id.startsWith("./") || id.startsWith("../") || id.startsWith("/")) {
|
|
153
|
+
const resolved = id.startsWith("/") ? id : normalizePath(fromDir + "/" + id);
|
|
154
|
+
candidates.push(resolved);
|
|
155
|
+
candidates.push(resolved + ".js");
|
|
156
|
+
candidates.push(resolved + ".ts");
|
|
157
|
+
candidates.push(resolved + ".mjs");
|
|
158
|
+
candidates.push(resolved + ".json");
|
|
159
|
+
} else {
|
|
160
|
+
let dir = fromDir;
|
|
161
|
+
while (true) {
|
|
162
|
+
candidates.push(normalizePath(dir + "/node_modules/" + id));
|
|
163
|
+
candidates.push(normalizePath(dir + "/node_modules/" + id + ".js"));
|
|
164
|
+
candidates.push(normalizePath(dir + "/node_modules/" + id + ".json"));
|
|
165
|
+
const parent = dir.slice(0, dir.lastIndexOf("/"));
|
|
166
|
+
if (parent === dir || parent === "") break;
|
|
167
|
+
dir = parent;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return candidates;
|
|
171
|
+
}
|
|
172
|
+
function tryPackageMain(vfs, dirPath) {
|
|
173
|
+
const pkgPath = dirPath + "/package.json";
|
|
174
|
+
try {
|
|
175
|
+
if (!vfs.existsSync(pkgPath)) return null;
|
|
176
|
+
const raw = textDecoder.decode(vfs.readFileSync(pkgPath));
|
|
177
|
+
const pkg = JSON.parse(raw);
|
|
178
|
+
const main = pkg.main || pkg.module || "index.js";
|
|
179
|
+
return resolveModule(vfs, "./" + main, dirPath);
|
|
180
|
+
} catch {
|
|
181
|
+
return null;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
function tryIndexFile(vfs, dirPath) {
|
|
185
|
+
for (const name of ["index.js", "index.ts", "index.mjs", "index.json"]) {
|
|
186
|
+
const fullPath = dirPath + "/" + name;
|
|
187
|
+
try {
|
|
188
|
+
if (!vfs.existsSync(fullPath)) continue;
|
|
189
|
+
const source = textDecoder.decode(vfs.readFileSync(fullPath));
|
|
190
|
+
return { path: fullPath, source };
|
|
191
|
+
} catch {
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
return null;
|
|
196
|
+
}
|
|
197
|
+
function normalizePath(p) {
|
|
198
|
+
const parts = p.split("/");
|
|
199
|
+
const result = [];
|
|
200
|
+
for (const part of parts) {
|
|
201
|
+
if (part === "..") result.pop();
|
|
202
|
+
else if (part !== "." && part !== "") result.push(part);
|
|
203
|
+
}
|
|
204
|
+
return (p.startsWith("/") ? "/" : "") + result.join("/");
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// src/runner.ts
|
|
208
|
+
var textEncoder = new TextEncoder();
|
|
209
|
+
var NodeRunner = class {
|
|
210
|
+
vfs;
|
|
211
|
+
nodeCompat;
|
|
212
|
+
env;
|
|
213
|
+
cwd;
|
|
214
|
+
argv;
|
|
215
|
+
constructor(options) {
|
|
216
|
+
this.vfs = options.vfs ?? createVFS();
|
|
217
|
+
this.nodeCompat = options.nodeCompat;
|
|
218
|
+
this.env = options.env ?? {};
|
|
219
|
+
this.cwd = options.cwd ?? "/";
|
|
220
|
+
this.argv = options.argv ?? [];
|
|
221
|
+
if (options.files) {
|
|
222
|
+
this.writeFilesSync(options.files);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
/** Write files to the VFS synchronously, creating parent directories as needed */
|
|
226
|
+
writeFilesSync(files) {
|
|
227
|
+
for (const [filePath, content] of Object.entries(files)) {
|
|
228
|
+
const parts = filePath.split("/").filter(Boolean);
|
|
229
|
+
for (let i = 1; i < parts.length; i++) {
|
|
230
|
+
const dir = "/" + parts.slice(0, i).join("/");
|
|
231
|
+
try {
|
|
232
|
+
this.vfs.mkdirSync(dir);
|
|
233
|
+
} catch {
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
this.vfs.writeFileSync(filePath, textEncoder.encode(content));
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
/** Write files to the VFS asynchronously (persists to IndexedDB backend) */
|
|
240
|
+
async writeFiles(files) {
|
|
241
|
+
for (const [filePath, content] of Object.entries(files)) {
|
|
242
|
+
const parts = filePath.split("/").filter(Boolean);
|
|
243
|
+
for (let i = 1; i < parts.length; i++) {
|
|
244
|
+
const dir = "/" + parts.slice(0, i).join("/");
|
|
245
|
+
try {
|
|
246
|
+
await this.vfs.mkdir(dir);
|
|
247
|
+
} catch {
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
await this.vfs.writeFile(filePath, textEncoder.encode(content));
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
/** Run a script file from the VFS, resolving extensions and index files */
|
|
254
|
+
async runFile(filePath) {
|
|
255
|
+
const absPath = filePath.startsWith("/") ? filePath : this.cwd + "/" + filePath;
|
|
256
|
+
const scriptDir = absPath.slice(0, absPath.lastIndexOf("/")) || "/";
|
|
257
|
+
const id = absPath.startsWith("/") ? absPath : "./" + absPath;
|
|
258
|
+
const resolved = resolveModule(this.vfs, id, scriptDir);
|
|
259
|
+
if (!resolved) {
|
|
260
|
+
return {
|
|
261
|
+
exitCode: 1,
|
|
262
|
+
stdout: "",
|
|
263
|
+
stderr: `Error: ENOENT: no such file or directory, open '${absPath}'`
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
return this.execute(resolved.source, resolved.path);
|
|
267
|
+
}
|
|
268
|
+
/** Run a script from a source string */
|
|
269
|
+
async runScript(source, filename) {
|
|
270
|
+
const absPath = filename ? filename.startsWith("/") ? filename : this.cwd + "/" + filename : this.cwd + "/__script__.js";
|
|
271
|
+
return this.execute(source, absPath);
|
|
272
|
+
}
|
|
273
|
+
/** Write a file to the VFS then run it */
|
|
274
|
+
async writeAndRun(filePath, source) {
|
|
275
|
+
this.writeFilesSync({ [filePath]: source });
|
|
276
|
+
return this.runFile(filePath);
|
|
277
|
+
}
|
|
278
|
+
execute(source, absPath) {
|
|
279
|
+
const stdoutLines = [];
|
|
280
|
+
const stderrLines = [];
|
|
281
|
+
const scriptConsole = createScriptConsole(stdoutLines, stderrLines);
|
|
282
|
+
const builtins = createBuiltinModules(
|
|
283
|
+
this.vfs,
|
|
284
|
+
this.env,
|
|
285
|
+
this.cwd,
|
|
286
|
+
[absPath, ...this.argv],
|
|
287
|
+
scriptConsole,
|
|
288
|
+
this.nodeCompat
|
|
289
|
+
);
|
|
290
|
+
const moduleCache = /* @__PURE__ */ new Map();
|
|
291
|
+
const createRequire = (fromDir) => {
|
|
292
|
+
const require2 = (id) => {
|
|
293
|
+
if (builtins.has(id)) return builtins.get(id);
|
|
294
|
+
const resolved = resolveModule(this.vfs, id, fromDir);
|
|
295
|
+
if (!resolved) {
|
|
296
|
+
throw new Error(`Cannot find module '${id}'`);
|
|
297
|
+
}
|
|
298
|
+
if (moduleCache.has(resolved.path)) {
|
|
299
|
+
return moduleCache.get(resolved.path);
|
|
300
|
+
}
|
|
301
|
+
if (resolved.path.endsWith(".json")) {
|
|
302
|
+
const json = JSON.parse(resolved.source);
|
|
303
|
+
moduleCache.set(resolved.path, json);
|
|
304
|
+
return json;
|
|
305
|
+
}
|
|
306
|
+
const moduleExports = {};
|
|
307
|
+
const moduleObj = { exports: moduleExports };
|
|
308
|
+
moduleCache.set(resolved.path, moduleExports);
|
|
309
|
+
const moduleDir = resolved.path.slice(
|
|
310
|
+
0,
|
|
311
|
+
resolved.path.lastIndexOf("/")
|
|
312
|
+
);
|
|
313
|
+
const childRequire = createRequire(moduleDir);
|
|
314
|
+
const factory = compileModule(resolved.source, resolved.path);
|
|
315
|
+
factory(
|
|
316
|
+
moduleObj.exports,
|
|
317
|
+
childRequire,
|
|
318
|
+
moduleObj,
|
|
319
|
+
resolved.path,
|
|
320
|
+
moduleDir,
|
|
321
|
+
scriptConsole,
|
|
322
|
+
builtins.get("process")
|
|
323
|
+
);
|
|
324
|
+
moduleCache.set(resolved.path, moduleObj.exports);
|
|
325
|
+
return moduleObj.exports;
|
|
326
|
+
};
|
|
327
|
+
require2.resolve = (id) => {
|
|
328
|
+
if (builtins.has(id)) return id;
|
|
329
|
+
const resolved = resolveModule(this.vfs, id, fromDir);
|
|
330
|
+
if (!resolved) throw new Error(`Cannot find module '${id}'`);
|
|
331
|
+
return resolved.path;
|
|
332
|
+
};
|
|
333
|
+
return require2;
|
|
334
|
+
};
|
|
335
|
+
let exitCode = 0;
|
|
336
|
+
try {
|
|
337
|
+
const scriptDir = absPath.slice(0, absPath.lastIndexOf("/")) || "/";
|
|
338
|
+
const require2 = createRequire(scriptDir);
|
|
339
|
+
const factory = compileModule(source, absPath);
|
|
340
|
+
const moduleExports = {};
|
|
341
|
+
const moduleObj = { exports: moduleExports };
|
|
342
|
+
const processObj = builtins.get("process");
|
|
343
|
+
if (processObj) {
|
|
344
|
+
const stdout = processObj.stdout;
|
|
345
|
+
const stderr = processObj.stderr;
|
|
346
|
+
if (stdout)
|
|
347
|
+
stdout.write = (s) => {
|
|
348
|
+
stdoutLines.push(String(s));
|
|
349
|
+
return true;
|
|
350
|
+
};
|
|
351
|
+
if (stderr)
|
|
352
|
+
stderr.write = (s) => {
|
|
353
|
+
stderrLines.push(String(s));
|
|
354
|
+
return true;
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
factory(moduleObj.exports, require2, moduleObj, absPath, scriptDir, scriptConsole, builtins.get("process"));
|
|
358
|
+
} catch (err) {
|
|
359
|
+
if (err instanceof ProcessExitError) {
|
|
360
|
+
exitCode = err.code;
|
|
361
|
+
} else {
|
|
362
|
+
exitCode = 1;
|
|
363
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
364
|
+
const stack = err instanceof Error && err.stack ? err.stack : "";
|
|
365
|
+
stderrLines.push(stack || message);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
return {
|
|
369
|
+
exitCode,
|
|
370
|
+
stdout: stdoutLines.join("\n"),
|
|
371
|
+
stderr: stderrLines.join("\n")
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
};
|
|
375
|
+
function compileModule(source, filename) {
|
|
376
|
+
const wrapped = `(function(exports, require, module, __filename, __dirname, console, process) {
|
|
377
|
+
${source}
|
|
378
|
+
})`;
|
|
379
|
+
try {
|
|
380
|
+
return (0, eval)(wrapped);
|
|
381
|
+
} catch (err) {
|
|
382
|
+
throw new SyntaxError(
|
|
383
|
+
`Failed to compile '${filename}': ${err instanceof Error ? err.message : String(err)}`
|
|
384
|
+
);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
export {
|
|
388
|
+
NodeRunner
|
|
389
|
+
};
|
|
390
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/runner.ts","../src/builtins.ts","../src/resolver.ts"],"sourcesContent":["import { createVFS, type VFS } from \"@lifo-sh/vfs\";\nimport type {\n NodeRunnerOptions,\n NodeCompatModules,\n RunResult,\n ModuleFactory,\n} from \"./types.js\";\nimport {\n createBuiltinModules,\n createScriptConsole,\n ProcessExitError,\n} from \"./builtins.js\";\nimport { resolveModule } from \"./resolver.js\";\n\nconst textEncoder = new TextEncoder();\n\nexport class NodeRunner {\n readonly vfs: VFS;\n private nodeCompat?: NodeCompatModules;\n private env: Record<string, string>;\n private cwd: string;\n private argv: string[];\n\n constructor(options: NodeRunnerOptions) {\n this.vfs = options.vfs ?? createVFS();\n this.nodeCompat = options.nodeCompat;\n this.env = options.env ?? {};\n this.cwd = options.cwd ?? \"/\";\n this.argv = options.argv ?? [];\n\n if (options.files) {\n this.writeFilesSync(options.files);\n }\n }\n\n /** Write files to the VFS synchronously, creating parent directories as needed */\n writeFilesSync(files: Record<string, string>): void {\n for (const [filePath, content] of Object.entries(files)) {\n const parts = filePath.split(\"/\").filter(Boolean);\n for (let i = 1; i < parts.length; i++) {\n const dir = \"/\" + parts.slice(0, i).join(\"/\");\n try {\n this.vfs.mkdirSync(dir);\n } catch {\n // already exists\n }\n }\n this.vfs.writeFileSync(filePath, textEncoder.encode(content));\n }\n }\n\n /** Write files to the VFS asynchronously (persists to IndexedDB backend) */\n async writeFiles(files: Record<string, string>): Promise<void> {\n for (const [filePath, content] of Object.entries(files)) {\n const parts = filePath.split(\"/\").filter(Boolean);\n for (let i = 1; i < parts.length; i++) {\n const dir = \"/\" + parts.slice(0, i).join(\"/\");\n try {\n await this.vfs.mkdir(dir);\n } catch {\n // already exists\n }\n }\n await this.vfs.writeFile(filePath, textEncoder.encode(content));\n }\n }\n\n /** Run a script file from the VFS, resolving extensions and index files */\n async runFile(filePath: string): Promise<RunResult> {\n const absPath = filePath.startsWith(\"/\")\n ? filePath\n : this.cwd + \"/\" + filePath;\n\n const scriptDir = absPath.slice(0, absPath.lastIndexOf(\"/\")) || \"/\";\n const id = absPath.startsWith(\"/\") ? absPath : \"./\" + absPath;\n const resolved = resolveModule(this.vfs, id, scriptDir);\n\n if (!resolved) {\n return {\n exitCode: 1,\n stdout: \"\",\n stderr: `Error: ENOENT: no such file or directory, open '${absPath}'`,\n };\n }\n\n return this.execute(resolved.source, resolved.path);\n }\n\n /** Run a script from a source string */\n async runScript(source: string, filename?: string): Promise<RunResult> {\n const absPath = filename\n ? filename.startsWith(\"/\")\n ? filename\n : this.cwd + \"/\" + filename\n : this.cwd + \"/__script__.js\";\n\n return this.execute(source, absPath);\n }\n\n /** Write a file to the VFS then run it */\n async writeAndRun(filePath: string, source: string): Promise<RunResult> {\n this.writeFilesSync({ [filePath]: source });\n return this.runFile(filePath);\n }\n\n private execute(source: string, absPath: string): RunResult {\n const stdoutLines: string[] = [];\n const stderrLines: string[] = [];\n const scriptConsole = createScriptConsole(stdoutLines, stderrLines);\n\n const builtins = createBuiltinModules(\n this.vfs,\n this.env,\n this.cwd,\n [absPath, ...this.argv],\n scriptConsole,\n this.nodeCompat,\n );\n\n // Module cache (keyed by absolute path)\n const moduleCache = new Map<string, Record<string, unknown>>();\n\n const createRequire = (fromDir: string) => {\n const require = (id: string): unknown => {\n // Check builtins first\n if (builtins.has(id)) return builtins.get(id);\n\n // Resolve from VFS\n const resolved = resolveModule(this.vfs, id, fromDir);\n if (!resolved) {\n throw new Error(`Cannot find module '${id}'`);\n }\n\n // Return cached if available\n if (moduleCache.has(resolved.path)) {\n return moduleCache.get(resolved.path);\n }\n\n // Handle JSON\n if (resolved.path.endsWith(\".json\")) {\n const json = JSON.parse(resolved.source);\n moduleCache.set(resolved.path, json);\n return json;\n }\n\n // Execute module\n const moduleExports: Record<string, unknown> = {};\n const moduleObj = { exports: moduleExports };\n moduleCache.set(resolved.path, moduleExports);\n\n const moduleDir = resolved.path.slice(\n 0,\n resolved.path.lastIndexOf(\"/\"),\n );\n const childRequire = createRequire(moduleDir);\n\n const factory = compileModule(resolved.source, resolved.path);\n factory(\n moduleObj.exports,\n childRequire,\n moduleObj,\n resolved.path,\n moduleDir,\n scriptConsole,\n builtins.get(\"process\"),\n );\n\n // Update cache with potentially reassigned module.exports\n moduleCache.set(resolved.path, moduleObj.exports);\n return moduleObj.exports;\n };\n\n require.resolve = (id: string): string => {\n if (builtins.has(id)) return id;\n const resolved = resolveModule(this.vfs, id, fromDir);\n if (!resolved) throw new Error(`Cannot find module '${id}'`);\n return resolved.path;\n };\n\n return require;\n };\n\n let exitCode = 0;\n\n try {\n const scriptDir = absPath.slice(0, absPath.lastIndexOf(\"/\")) || \"/\";\n const require = createRequire(scriptDir);\n const factory = compileModule(source, absPath);\n const moduleExports: Record<string, unknown> = {};\n const moduleObj = { exports: moduleExports };\n\n // Wire process.stdout/stderr to capture\n const processObj = builtins.get(\"process\") as Record<string, unknown>;\n if (processObj) {\n const stdout = processObj.stdout as Record<string, unknown>;\n const stderr = processObj.stderr as Record<string, unknown>;\n if (stdout)\n stdout.write = (s: string) => {\n stdoutLines.push(String(s));\n return true;\n };\n if (stderr)\n stderr.write = (s: string) => {\n stderrLines.push(String(s));\n return true;\n };\n }\n\n factory(moduleObj.exports, require, moduleObj, absPath, scriptDir, scriptConsole, builtins.get(\"process\"));\n } catch (err) {\n if (err instanceof ProcessExitError) {\n exitCode = err.code;\n } else {\n exitCode = 1;\n const message =\n err instanceof Error ? err.message : String(err);\n const stack =\n err instanceof Error && err.stack ? err.stack : \"\";\n stderrLines.push(stack || message);\n }\n }\n\n return {\n exitCode,\n stdout: stdoutLines.join(\"\\n\"),\n stderr: stderrLines.join(\"\\n\"),\n };\n }\n}\n\n/** Compile source code into a module factory function */\nfunction compileModule(source: string, filename: string): ModuleFactory {\n // Wrap in a function with CommonJS-style arguments + console/process injected as params\n const wrapped = `(function(exports, require, module, __filename, __dirname, console, process) {\\n${source}\\n})`;\n try {\n // eslint-disable-next-line no-eval\n return (0, eval)(wrapped) as ModuleFactory;\n } catch (err) {\n throw new SyntaxError(\n `Failed to compile '${filename}': ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n}\n","import type { NodeCompatModules, ScriptConsole } from \"./types.js\";\nimport type { VFS } from \"@lifo-sh/vfs\";\n\n/** Minimal path module (always available, no node-compat needed) */\nconst minimalPath = {\n join(...parts: string[]): string {\n const joined = parts.join(\"/\").replace(/\\/+/g, \"/\");\n return minimalPath.normalize(joined);\n },\n normalize(p: string): string {\n const parts = p.split(\"/\");\n const result: string[] = [];\n for (const part of parts) {\n if (part === \"..\") {\n result.pop();\n } else if (part !== \".\" && part !== \"\") {\n result.push(part);\n }\n }\n return (p.startsWith(\"/\") ? \"/\" : \"\") + result.join(\"/\");\n },\n dirname(p: string): string {\n const idx = p.lastIndexOf(\"/\");\n if (idx === -1) return \".\";\n if (idx === 0) return \"/\";\n return p.slice(0, idx);\n },\n basename(p: string, ext?: string): string {\n const base = p.slice(p.lastIndexOf(\"/\") + 1);\n if (ext && base.endsWith(ext)) return base.slice(0, -ext.length);\n return base;\n },\n extname(p: string): string {\n const base = minimalPath.basename(p);\n const dot = base.lastIndexOf(\".\");\n if (dot <= 0) return \"\";\n return base.slice(dot);\n },\n resolve(...parts: string[]): string {\n let resolved = \"\";\n for (let i = parts.length - 1; i >= 0; i--) {\n resolved = parts[i] + \"/\" + resolved;\n if (parts[i].startsWith(\"/\")) break;\n }\n return minimalPath.normalize(resolved);\n },\n isAbsolute(p: string): boolean {\n return p.startsWith(\"/\");\n },\n sep: \"/\",\n delimiter: \":\",\n posix: null as unknown,\n};\nminimalPath.posix = minimalPath;\n\n/** Minimal console that captures output */\nexport function createScriptConsole(\n stdout: string[],\n stderr: string[],\n): ScriptConsole {\n const format = (...args: unknown[]): string =>\n args\n .map((a) =>\n typeof a === \"string\" ? a : JSON.stringify(a, null, 2) ?? String(a),\n )\n .join(\" \");\n\n return {\n log: (...args) => stdout.push(format(...args)),\n error: (...args) => stderr.push(format(...args)),\n warn: (...args) => stderr.push(format(...args)),\n info: (...args) => stdout.push(format(...args)),\n debug: (...args) => stdout.push(format(...args)),\n };\n}\n\n/** Minimal process object (always available) */\nexport function createMinimalProcess(\n env: Record<string, string>,\n cwd: string,\n argv: string[],\n) {\n return {\n env,\n cwd: () => cwd,\n argv: [\"node\", ...argv],\n exit: (code?: number) => {\n throw new ProcessExitError(code ?? 0);\n },\n platform: \"browser\" as const,\n arch: \"wasm\" as const,\n version: \"v20.0.0\",\n versions: { node: \"20.0.0\" },\n pid: 1,\n ppid: 0,\n stdout: {\n write: (_s: string) => {\n /* captured via console */\n return true;\n },\n },\n stderr: {\n write: (_s: string) => {\n return true;\n },\n },\n nextTick: (fn: () => void) => Promise.resolve().then(fn),\n hrtime: {\n bigint: () => BigInt(Math.round(performance.now() * 1e6)),\n },\n };\n}\n\nexport class ProcessExitError extends Error {\n readonly code: number;\n constructor(code: number) {\n super(`process.exit(${code})`);\n this.name = \"ProcessExitError\";\n this.code = code;\n }\n}\n\n/** Build the full builtin module map */\nexport function createBuiltinModules(\n _vfs: VFS,\n env: Record<string, string>,\n cwd: string,\n argv: string[],\n scriptConsole: ScriptConsole,\n nodeCompat?: NodeCompatModules,\n): Map<string, unknown> {\n const builtins = new Map<string, unknown>();\n\n // Always available (no deps)\n builtins.set(\"path\", nodeCompat?.path ?? minimalPath);\n\n const process = nodeCompat?.process ?? createMinimalProcess(env, cwd, argv);\n builtins.set(\"process\", process);\n\n // Wire console output to stdout/stderr capture\n const console_ = scriptConsole;\n builtins.set(\"console\", console_);\n\n // node-compat modules (if provided)\n if (nodeCompat?.fs) builtins.set(\"fs\", nodeCompat.fs);\n if (nodeCompat?.fsPromises) builtins.set(\"fs/promises\", nodeCompat.fsPromises);\n if (nodeCompat?.events) builtins.set(\"events\", nodeCompat.events);\n if (nodeCompat?.buffer) builtins.set(\"buffer\", nodeCompat.buffer);\n if (nodeCompat?.util) builtins.set(\"util\", nodeCompat.util);\n if (nodeCompat?.timers) builtins.set(\"timers\", nodeCompat.timers);\n if (nodeCompat?.assert) builtins.set(\"assert\", nodeCompat.assert);\n if (nodeCompat?.os) builtins.set(\"os\", nodeCompat.os);\n if (nodeCompat?.stream) builtins.set(\"stream\", nodeCompat.stream);\n if (nodeCompat?.url) builtins.set(\"url\", nodeCompat.url);\n if (nodeCompat?.crypto) builtins.set(\"crypto\", nodeCompat.crypto);\n\n return builtins;\n}\n","import type { VFS } from \"@lifo-sh/vfs\";\nimport type { ResolvedModule } from \"./types.js\";\n\nconst textDecoder = new TextDecoder();\n\n/** Resolve and load a module from the VFS */\nexport function resolveModule(\n vfs: VFS,\n id: string,\n fromDir: string,\n): ResolvedModule | null {\n // Try direct path first, then with extensions\n const candidates = buildCandidates(id, fromDir);\n\n for (const candidate of candidates) {\n try {\n if (!vfs.existsSync(candidate)) continue;\n const stat = vfs.statSync(candidate);\n\n if (stat.isDirectory()) {\n // Try package.json main, then index.js\n const fromPkg = tryPackageMain(vfs, candidate);\n if (fromPkg) return fromPkg;\n const fromIndex = tryIndexFile(vfs, candidate);\n if (fromIndex) return fromIndex;\n continue;\n }\n\n if (stat.isFile()) {\n const source = textDecoder.decode(vfs.readFileSync(candidate));\n return { path: candidate, source };\n }\n } catch {\n // File doesn't exist or can't be read, try next candidate\n }\n }\n\n return null;\n}\n\nfunction buildCandidates(id: string, fromDir: string): string[] {\n const candidates: string[] = [];\n\n if (id.startsWith(\"./\") || id.startsWith(\"../\") || id.startsWith(\"/\")) {\n // Relative/absolute path\n const resolved = id.startsWith(\"/\") ? id : normalizePath(fromDir + \"/\" + id);\n candidates.push(resolved);\n candidates.push(resolved + \".js\");\n candidates.push(resolved + \".ts\");\n candidates.push(resolved + \".mjs\");\n candidates.push(resolved + \".json\");\n } else {\n // Bare specifier → look in node_modules\n let dir = fromDir;\n while (true) {\n candidates.push(normalizePath(dir + \"/node_modules/\" + id));\n candidates.push(normalizePath(dir + \"/node_modules/\" + id + \".js\"));\n candidates.push(normalizePath(dir + \"/node_modules/\" + id + \".json\"));\n const parent = dir.slice(0, dir.lastIndexOf(\"/\"));\n if (parent === dir || parent === \"\") break;\n dir = parent;\n }\n }\n\n return candidates;\n}\n\nfunction tryPackageMain(vfs: VFS, dirPath: string): ResolvedModule | null {\n const pkgPath = dirPath + \"/package.json\";\n try {\n if (!vfs.existsSync(pkgPath)) return null;\n const raw = textDecoder.decode(vfs.readFileSync(pkgPath));\n const pkg = JSON.parse(raw);\n const main = pkg.main || pkg.module || \"index.js\";\n return resolveModule(vfs, \"./\" + main, dirPath);\n } catch {\n return null;\n }\n}\n\nfunction tryIndexFile(vfs: VFS, dirPath: string): ResolvedModule | null {\n for (const name of [\"index.js\", \"index.ts\", \"index.mjs\", \"index.json\"]) {\n const fullPath = dirPath + \"/\" + name;\n try {\n if (!vfs.existsSync(fullPath)) continue;\n const source = textDecoder.decode(vfs.readFileSync(fullPath));\n return { path: fullPath, source };\n } catch {\n continue;\n }\n }\n return null;\n}\n\nfunction normalizePath(p: string): string {\n const parts = p.split(\"/\");\n const result: string[] = [];\n for (const part of parts) {\n if (part === \"..\") result.pop();\n else if (part !== \".\" && part !== \"\") result.push(part);\n }\n return (p.startsWith(\"/\") ? \"/\" : \"\") + result.join(\"/\");\n}\n"],"mappings":"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","names":["require"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@lifo-sh/node-runner",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Virtual Node.js script runner for the browser",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.cjs",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js",
|
|
13
|
+
"require": "./dist/index.cjs"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": ["dist"],
|
|
17
|
+
"scripts": {
|
|
18
|
+
"build": "tsup",
|
|
19
|
+
"dev": "tsup --watch",
|
|
20
|
+
"test": "vitest run",
|
|
21
|
+
"typecheck": "tsc --noEmit"
|
|
22
|
+
},
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"@lifo-sh/vfs": "^0.1.0"
|
|
25
|
+
},
|
|
26
|
+
"peerDependencies": {
|
|
27
|
+
"@lifo-sh/node-compat": ">=0.1.0"
|
|
28
|
+
},
|
|
29
|
+
"peerDependenciesMeta": {
|
|
30
|
+
"@lifo-sh/node-compat": {
|
|
31
|
+
"optional": true
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"@lifo-sh/node-compat": "^0.1.0",
|
|
36
|
+
"tsup": "^8.0.0",
|
|
37
|
+
"vitest": "^3.0.0"
|
|
38
|
+
}
|
|
39
|
+
}
|