@secure-exec/nodejs 0.2.0-rc.1

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.
Files changed (68) hide show
  1. package/LICENSE +191 -0
  2. package/README.md +7 -0
  3. package/dist/bindings.d.ts +31 -0
  4. package/dist/bindings.js +67 -0
  5. package/dist/bridge/active-handles.d.ts +22 -0
  6. package/dist/bridge/active-handles.js +112 -0
  7. package/dist/bridge/child-process.d.ts +99 -0
  8. package/dist/bridge/child-process.js +672 -0
  9. package/dist/bridge/dispatch.d.ts +2 -0
  10. package/dist/bridge/dispatch.js +40 -0
  11. package/dist/bridge/fs.d.ts +502 -0
  12. package/dist/bridge/fs.js +3307 -0
  13. package/dist/bridge/index.d.ts +10 -0
  14. package/dist/bridge/index.js +41 -0
  15. package/dist/bridge/module.d.ts +75 -0
  16. package/dist/bridge/module.js +325 -0
  17. package/dist/bridge/network.d.ts +1093 -0
  18. package/dist/bridge/network.js +8651 -0
  19. package/dist/bridge/os.d.ts +13 -0
  20. package/dist/bridge/os.js +256 -0
  21. package/dist/bridge/polyfills.d.ts +9 -0
  22. package/dist/bridge/polyfills.js +67 -0
  23. package/dist/bridge/process.d.ts +121 -0
  24. package/dist/bridge/process.js +1382 -0
  25. package/dist/bridge/whatwg-url.d.ts +67 -0
  26. package/dist/bridge/whatwg-url.js +712 -0
  27. package/dist/bridge-contract.d.ts +774 -0
  28. package/dist/bridge-contract.js +172 -0
  29. package/dist/bridge-handlers.d.ts +199 -0
  30. package/dist/bridge-handlers.js +4263 -0
  31. package/dist/bridge-loader.d.ts +9 -0
  32. package/dist/bridge-loader.js +87 -0
  33. package/dist/bridge-setup.d.ts +1 -0
  34. package/dist/bridge-setup.js +3 -0
  35. package/dist/bridge.js +21652 -0
  36. package/dist/builtin-modules.d.ts +25 -0
  37. package/dist/builtin-modules.js +312 -0
  38. package/dist/default-network-adapter.d.ts +13 -0
  39. package/dist/default-network-adapter.js +351 -0
  40. package/dist/driver.d.ts +87 -0
  41. package/dist/driver.js +191 -0
  42. package/dist/esm-compiler.d.ts +14 -0
  43. package/dist/esm-compiler.js +68 -0
  44. package/dist/execution-driver.d.ts +37 -0
  45. package/dist/execution-driver.js +977 -0
  46. package/dist/host-network-adapter.d.ts +7 -0
  47. package/dist/host-network-adapter.js +279 -0
  48. package/dist/index.d.ts +20 -0
  49. package/dist/index.js +23 -0
  50. package/dist/isolate-bootstrap.d.ts +86 -0
  51. package/dist/isolate-bootstrap.js +125 -0
  52. package/dist/ivm-compat.d.ts +7 -0
  53. package/dist/ivm-compat.js +31 -0
  54. package/dist/kernel-runtime.d.ts +58 -0
  55. package/dist/kernel-runtime.js +535 -0
  56. package/dist/module-access.d.ts +75 -0
  57. package/dist/module-access.js +606 -0
  58. package/dist/module-resolver.d.ts +8 -0
  59. package/dist/module-resolver.js +150 -0
  60. package/dist/os-filesystem.d.ts +42 -0
  61. package/dist/os-filesystem.js +161 -0
  62. package/dist/package-bundler.d.ts +36 -0
  63. package/dist/package-bundler.js +497 -0
  64. package/dist/polyfills.d.ts +17 -0
  65. package/dist/polyfills.js +97 -0
  66. package/dist/worker-adapter.d.ts +21 -0
  67. package/dist/worker-adapter.js +34 -0
  68. package/package.json +123 -0
@@ -0,0 +1,497 @@
1
+ // Path utilities (since we can't use node:path in a way that works in isolate)
2
+ function dirname(p) {
3
+ const lastSlash = p.lastIndexOf("/");
4
+ if (lastSlash === -1)
5
+ return ".";
6
+ if (lastSlash === 0)
7
+ return "/";
8
+ return p.slice(0, lastSlash);
9
+ }
10
+ function join(...parts) {
11
+ const segments = [];
12
+ for (const part of parts) {
13
+ if (part.startsWith("/")) {
14
+ segments.length = 0;
15
+ }
16
+ for (const seg of part.split("/")) {
17
+ if (seg === "..") {
18
+ segments.pop();
19
+ }
20
+ else if (seg && seg !== ".") {
21
+ segments.push(seg);
22
+ }
23
+ }
24
+ }
25
+ return `/${segments.join("/")}`;
26
+ }
27
+ const FILE_EXTENSIONS = [".js", ".json", ".mjs", ".cjs"];
28
+ export function createResolutionCache() {
29
+ return {
30
+ resolveResults: new Map(),
31
+ packageJsonResults: new Map(),
32
+ existsResults: new Map(),
33
+ statResults: new Map(),
34
+ };
35
+ }
36
+ /**
37
+ * Resolve a module request to an absolute path in the virtual filesystem
38
+ */
39
+ export async function resolveModule(request, fromDir, fs, mode = "require", cache) {
40
+ // Check top-level cache
41
+ if (cache) {
42
+ const cacheKey = `${request}\0${fromDir}\0${mode}`;
43
+ if (cache.resolveResults.has(cacheKey)) {
44
+ return cache.resolveResults.get(cacheKey);
45
+ }
46
+ }
47
+ let result;
48
+ // Absolute paths - resolve directly
49
+ if (request.startsWith("/")) {
50
+ result = await resolveAbsolute(request, fs, mode, cache);
51
+ }
52
+ else if (
53
+ // Relative imports (including bare '.' and '..')
54
+ request.startsWith("./") ||
55
+ request.startsWith("../") ||
56
+ request === "." ||
57
+ request === "..") {
58
+ result = await resolveRelative(request, fromDir, fs, mode, cache);
59
+ }
60
+ else if (request.startsWith("#")) {
61
+ // Package import maps, e.g. "#dev"
62
+ result = await resolvePackageImports(request, fromDir, fs, mode, cache);
63
+ }
64
+ else {
65
+ // Bare imports - walk up node_modules
66
+ result = await resolveNodeModules(request, fromDir, fs, mode, cache);
67
+ }
68
+ // Store in top-level cache
69
+ if (cache) {
70
+ const cacheKey = `${request}\0${fromDir}\0${mode}`;
71
+ cache.resolveResults.set(cacheKey, result);
72
+ }
73
+ return result;
74
+ }
75
+ /** Resolve `#`-prefixed import-map specifiers by walking up to find the nearest package.json with `imports`. */
76
+ async function resolvePackageImports(request, fromDir, fs, mode, cache) {
77
+ let dir = fromDir;
78
+ while (dir !== "" && dir !== ".") {
79
+ const pkgJsonPath = join(dir, "package.json");
80
+ const pkgJson = await readPackageJson(fs, pkgJsonPath, cache);
81
+ if (pkgJson?.imports !== undefined) {
82
+ const target = resolveImportsTarget(pkgJson.imports, request, mode);
83
+ if (!target) {
84
+ return null;
85
+ }
86
+ if (target.startsWith("#")) {
87
+ // Avoid recursive import-map loops.
88
+ return null;
89
+ }
90
+ const targetPath = target.startsWith("/")
91
+ ? target
92
+ : join(dir, normalizePackagePath(target));
93
+ return resolvePath(targetPath, fs, mode, cache);
94
+ }
95
+ if (dir === "/") {
96
+ break;
97
+ }
98
+ dir = dirname(dir);
99
+ }
100
+ return null;
101
+ }
102
+ /**
103
+ * Resolve an absolute path
104
+ */
105
+ async function resolveAbsolute(request, fs, mode, cache) {
106
+ return resolvePath(request, fs, mode, cache);
107
+ }
108
+ /**
109
+ * Resolve a relative import
110
+ */
111
+ async function resolveRelative(request, fromDir, fs, mode, cache) {
112
+ const basePath = join(fromDir, request);
113
+ return resolvePath(basePath, fs, mode, cache);
114
+ }
115
+ /**
116
+ * Resolve a bare module import by walking up node_modules
117
+ */
118
+ /** Walk up from `fromDir` checking `node_modules/` (including pnpm virtual-store layouts) for the package. */
119
+ async function resolveNodeModules(request, fromDir, fs, mode, cache) {
120
+ // Handle scoped packages: @scope/package
121
+ let packageName;
122
+ let subpath;
123
+ if (request.startsWith("@")) {
124
+ // Scoped package: @scope/package or @scope/package/subpath
125
+ const parts = request.split("/");
126
+ if (parts.length >= 2) {
127
+ packageName = `${parts[0]}/${parts[1]}`;
128
+ subpath = parts.slice(2).join("/");
129
+ }
130
+ else {
131
+ return null;
132
+ }
133
+ }
134
+ else {
135
+ // Regular package: package or package/subpath
136
+ const slashIndex = request.indexOf("/");
137
+ if (slashIndex === -1) {
138
+ packageName = request;
139
+ subpath = "";
140
+ }
141
+ else {
142
+ packageName = request.slice(0, slashIndex);
143
+ subpath = request.slice(slashIndex + 1);
144
+ }
145
+ }
146
+ let dir = fromDir;
147
+ while (dir !== "" && dir !== ".") {
148
+ const candidatePackageDirs = getNodeModulesCandidatePackageDirs(dir, packageName);
149
+ for (const packageDir of candidatePackageDirs) {
150
+ let entry;
151
+ try {
152
+ entry = await resolvePackageEntryFromDir(packageDir, subpath, fs, mode, cache);
153
+ }
154
+ catch (error) {
155
+ if (isPermissionProbeError(error)) {
156
+ continue;
157
+ }
158
+ throw error;
159
+ }
160
+ if (entry) {
161
+ return entry;
162
+ }
163
+ }
164
+ if (dir === "/")
165
+ break;
166
+ dir = dirname(dir);
167
+ }
168
+ // Also check root node_modules
169
+ const rootPackageDir = join("/node_modules", packageName);
170
+ let rootEntry;
171
+ try {
172
+ rootEntry = await resolvePackageEntryFromDir(rootPackageDir, subpath, fs, mode, cache);
173
+ }
174
+ catch (error) {
175
+ if (isPermissionProbeError(error)) {
176
+ rootEntry = null;
177
+ }
178
+ else {
179
+ throw error;
180
+ }
181
+ }
182
+ if (rootEntry) {
183
+ return rootEntry;
184
+ }
185
+ return null;
186
+ }
187
+ function getNodeModulesCandidatePackageDirs(dir, packageName) {
188
+ const candidates = new Set();
189
+ candidates.add(join(dir, "node_modules", packageName));
190
+ candidates.add(join(dir, "node_modules", ".pnpm", "node_modules", packageName));
191
+ // Match Node's "parent node_modules" lookup when the current directory is
192
+ // already a node_modules folder.
193
+ if (dir === "/node_modules" || dir.endsWith("/node_modules")) {
194
+ candidates.add(join(dir, packageName));
195
+ }
196
+ // Support pnpm virtual-store layouts where transitive dependencies are linked
197
+ // under <root>/node_modules/.pnpm/node_modules.
198
+ const nodeModulesSegment = "/node_modules/";
199
+ const nodeModulesIndex = dir.lastIndexOf(nodeModulesSegment);
200
+ if (nodeModulesIndex !== -1) {
201
+ const nodeModulesRoot = dir.slice(0, nodeModulesIndex + nodeModulesSegment.length - 1);
202
+ candidates.add(join(nodeModulesRoot, ".pnpm", "node_modules", packageName));
203
+ }
204
+ return Array.from(candidates);
205
+ }
206
+ /**
207
+ * Given a package directory and optional subpath, resolve the entry file using
208
+ * `exports` map (if present), then `main`, then `index.js` fallback. When
209
+ * `exports` is defined, no fallback to `main` occurs (Node.js semantics).
210
+ */
211
+ async function resolvePackageEntryFromDir(packageDir, subpath, fs, mode, cache) {
212
+ const pkgJsonPath = join(packageDir, "package.json");
213
+ const pkgJson = await readPackageJson(fs, pkgJsonPath, cache);
214
+ if (!pkgJson && !(await cachedSafeExists(fs, packageDir, cache))) {
215
+ return null;
216
+ }
217
+ // If package uses "exports", follow it and do not fall back to main/subpath
218
+ if (pkgJson?.exports !== undefined) {
219
+ const exportsTarget = resolveExportsTarget(pkgJson.exports, subpath ? `./${subpath}` : ".", mode);
220
+ if (!exportsTarget) {
221
+ return null;
222
+ }
223
+ const targetPath = join(packageDir, normalizePackagePath(exportsTarget));
224
+ const resolvedTarget = await resolvePath(targetPath, fs, mode, cache);
225
+ return resolvedTarget ?? targetPath;
226
+ }
227
+ // Bare subpath import without exports map: package/sub/path
228
+ if (subpath) {
229
+ return resolvePath(join(packageDir, subpath), fs, mode, cache);
230
+ }
231
+ // Root package import
232
+ const entryField = getPackageEntryField(pkgJson, mode);
233
+ if (entryField) {
234
+ const entryPath = join(packageDir, normalizePackagePath(entryField));
235
+ const resolved = await resolvePath(entryPath, fs, mode, cache);
236
+ if (resolved)
237
+ return resolved;
238
+ if (pkgJson) {
239
+ return entryPath;
240
+ }
241
+ }
242
+ // Default fallback
243
+ return resolvePath(join(packageDir, "index"), fs, mode, cache);
244
+ }
245
+ async function resolvePath(basePath, fs, mode, cache) {
246
+ let isDirectory = false;
247
+ // Use cached stat when available
248
+ const statResult = await cachedStat(fs, basePath, cache);
249
+ if (statResult !== null) {
250
+ if (!statResult.isDirectory) {
251
+ return basePath;
252
+ }
253
+ isDirectory = true;
254
+ }
255
+ // For extensionless specifiers, try files before directory resolution.
256
+ for (const ext of FILE_EXTENSIONS) {
257
+ const withExt = `${basePath}${ext}`;
258
+ if (await cachedSafeExists(fs, withExt, cache)) {
259
+ return withExt;
260
+ }
261
+ }
262
+ if (isDirectory) {
263
+ const pkgJsonPath = join(basePath, "package.json");
264
+ const pkgJson = await readPackageJson(fs, pkgJsonPath, cache);
265
+ const entryField = getPackageEntryField(pkgJson, mode);
266
+ if (entryField) {
267
+ const entryPath = join(basePath, normalizePackagePath(entryField));
268
+ // Avoid directory self-reference loops like "main": "."
269
+ if (entryPath !== basePath) {
270
+ const entry = await resolvePath(entryPath, fs, mode, cache);
271
+ if (entry)
272
+ return entry;
273
+ }
274
+ }
275
+ for (const ext of FILE_EXTENSIONS) {
276
+ const indexPath = join(basePath, `index${ext}`);
277
+ if (await cachedSafeExists(fs, indexPath, cache)) {
278
+ return indexPath;
279
+ }
280
+ }
281
+ }
282
+ return null;
283
+ }
284
+ async function readPackageJson(fs, pkgJsonPath, cache) {
285
+ if (cache?.packageJsonResults.has(pkgJsonPath)) {
286
+ return cache.packageJsonResults.get(pkgJsonPath);
287
+ }
288
+ if (!(await cachedSafeExists(fs, pkgJsonPath, cache))) {
289
+ cache?.packageJsonResults.set(pkgJsonPath, null);
290
+ return null;
291
+ }
292
+ try {
293
+ const result = JSON.parse(await fs.readTextFile(pkgJsonPath));
294
+ cache?.packageJsonResults.set(pkgJsonPath, result);
295
+ return result;
296
+ }
297
+ catch {
298
+ cache?.packageJsonResults.set(pkgJsonPath, null);
299
+ return null;
300
+ }
301
+ }
302
+ /** Treat EACCES/EPERM as "path not available" during resolution probing. */
303
+ function isPermissionProbeError(error) {
304
+ const err = error;
305
+ return err?.code === "EACCES" || err?.code === "EPERM";
306
+ }
307
+ async function safeExists(fs, path) {
308
+ try {
309
+ return await fs.exists(path);
310
+ }
311
+ catch (error) {
312
+ if (isPermissionProbeError(error)) {
313
+ return false;
314
+ }
315
+ throw error;
316
+ }
317
+ }
318
+ /** Cached wrapper around safeExists — avoids repeated VFS probes for the same path. */
319
+ async function cachedSafeExists(fs, path, cache) {
320
+ if (cache?.existsResults.has(path)) {
321
+ return cache.existsResults.get(path);
322
+ }
323
+ const result = await safeExists(fs, path);
324
+ cache?.existsResults.set(path, result);
325
+ return result;
326
+ }
327
+ /** Cached stat — returns { isDirectory } or null for ENOENT. */
328
+ async function cachedStat(fs, path, cache) {
329
+ if (cache?.statResults.has(path)) {
330
+ return cache.statResults.get(path);
331
+ }
332
+ try {
333
+ const statInfo = await fs.stat(path);
334
+ const result = { isDirectory: statInfo.isDirectory };
335
+ cache?.statResults.set(path, result);
336
+ return result;
337
+ }
338
+ catch (error) {
339
+ const err = error;
340
+ if (err?.code && err.code !== "ENOENT") {
341
+ throw err;
342
+ }
343
+ cache?.statResults.set(path, null);
344
+ return null;
345
+ }
346
+ }
347
+ function normalizePackagePath(value) {
348
+ return value.replace(/^\.\//, "").replace(/\/$/, "");
349
+ }
350
+ function getPackageEntryField(pkgJson, _mode) {
351
+ if (!pkgJson)
352
+ return "index.js";
353
+ // Match Node's package entrypoint precedence when exports is absent.
354
+ if (typeof pkgJson.main === "string")
355
+ return pkgJson.main;
356
+ return "index.js";
357
+ }
358
+ /**
359
+ * Implement Node.js `package.json` "exports" resolution. Handles string, array,
360
+ * conditions-object, subpath keys, and wildcard `*` patterns.
361
+ */
362
+ function resolveExportsTarget(exportsField, subpath, mode) {
363
+ // "exports": "./dist/index.js"
364
+ if (typeof exportsField === "string") {
365
+ return subpath === "." ? exportsField : null;
366
+ }
367
+ // "exports": ["./a.js", "./b.js"]
368
+ if (Array.isArray(exportsField)) {
369
+ for (const item of exportsField) {
370
+ const resolved = resolveExportsTarget(item, subpath, mode);
371
+ if (resolved)
372
+ return resolved;
373
+ }
374
+ return null;
375
+ }
376
+ if (!exportsField || typeof exportsField !== "object") {
377
+ return null;
378
+ }
379
+ const record = exportsField;
380
+ // Root conditions object (no "./" keys)
381
+ if (subpath === "." && !Object.keys(record).some((key) => key.startsWith("./"))) {
382
+ return resolveConditionalTarget(record, mode);
383
+ }
384
+ // Exact subpath key first
385
+ if (subpath in record) {
386
+ return resolveExportsTarget(record[subpath], ".", mode);
387
+ }
388
+ // Pattern keys like "./*"
389
+ for (const [key, value] of Object.entries(record)) {
390
+ if (!key.includes("*"))
391
+ continue;
392
+ const [prefix, suffix] = key.split("*");
393
+ if (!subpath.startsWith(prefix) || !subpath.endsWith(suffix))
394
+ continue;
395
+ const wildcard = subpath.slice(prefix.length, subpath.length - suffix.length);
396
+ const resolved = resolveExportsTarget(value, ".", mode);
397
+ if (!resolved)
398
+ continue;
399
+ return resolved.replaceAll("*", wildcard);
400
+ }
401
+ // Root key may still be present in object with subpaths
402
+ if (subpath === "." && "." in record) {
403
+ return resolveExportsTarget(record["."], ".", mode);
404
+ }
405
+ return null;
406
+ }
407
+ /** Pick the first matching condition key (import/require/node/default) from an exports conditions object. */
408
+ function resolveConditionalTarget(record, mode) {
409
+ const order = mode === "import"
410
+ ? ["import", "node", "module", "default", "require"]
411
+ : ["require", "node", "default", "import", "module"];
412
+ for (const key of order) {
413
+ if (!(key in record))
414
+ continue;
415
+ const resolved = resolveExportsTarget(record[key], ".", mode);
416
+ if (resolved)
417
+ return resolved;
418
+ }
419
+ // Last resort: first key that resolves
420
+ for (const value of Object.values(record)) {
421
+ const resolved = resolveExportsTarget(value, ".", mode);
422
+ if (resolved)
423
+ return resolved;
424
+ }
425
+ return null;
426
+ }
427
+ /** Resolve a `#`-prefixed specifier against a package.json `imports` field, including wildcard patterns. */
428
+ function resolveImportsTarget(importsField, specifier, mode) {
429
+ if (typeof importsField === "string") {
430
+ return importsField;
431
+ }
432
+ if (Array.isArray(importsField)) {
433
+ for (const item of importsField) {
434
+ const resolved = resolveImportsTarget(item, specifier, mode);
435
+ if (resolved) {
436
+ return resolved;
437
+ }
438
+ }
439
+ return null;
440
+ }
441
+ if (!importsField || typeof importsField !== "object") {
442
+ return null;
443
+ }
444
+ const record = importsField;
445
+ if (specifier in record) {
446
+ return resolveExportsTarget(record[specifier], ".", mode);
447
+ }
448
+ for (const [key, value] of Object.entries(record)) {
449
+ if (!key.includes("*"))
450
+ continue;
451
+ const [prefix, suffix] = key.split("*");
452
+ if (!specifier.startsWith(prefix) || !specifier.endsWith(suffix))
453
+ continue;
454
+ const wildcard = specifier.slice(prefix.length, specifier.length - suffix.length);
455
+ const resolved = resolveExportsTarget(value, ".", mode);
456
+ if (!resolved)
457
+ continue;
458
+ return resolved.replaceAll("*", wildcard);
459
+ }
460
+ return null;
461
+ }
462
+ /**
463
+ * Load a file's content from the virtual filesystem
464
+ */
465
+ export async function loadFile(path, fs) {
466
+ try {
467
+ return await fs.readTextFile(path);
468
+ }
469
+ catch {
470
+ return null;
471
+ }
472
+ }
473
+ /**
474
+ * Legacy function - bundle a package from node_modules (simple approach)
475
+ * This is kept for backwards compatibility but the new dynamic resolution is preferred
476
+ */
477
+ export async function bundlePackage(packageName, fs) {
478
+ // Resolve the package entry point
479
+ const entryPath = await resolveNodeModules(packageName, "/", fs, "require");
480
+ if (!entryPath) {
481
+ return null;
482
+ }
483
+ try {
484
+ const entryCode = await fs.readTextFile(entryPath);
485
+ // Wrap the code in an IIFE that sets up module.exports
486
+ const wrappedCode = `(function() {
487
+ var module = { exports: {} };
488
+ var exports = module.exports;
489
+ ${entryCode}
490
+ return module.exports;
491
+ })()`;
492
+ return wrappedCode;
493
+ }
494
+ catch {
495
+ return null;
496
+ }
497
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Bundle a stdlib polyfill module using esbuild
3
+ */
4
+ export declare function bundlePolyfill(moduleName: string): Promise<string>;
5
+ /**
6
+ * Get all available stdlib modules (those with non-null polyfills)
7
+ */
8
+ export declare function getAvailableStdlib(): string[];
9
+ /**
10
+ * Check if a module has a polyfill available
11
+ * Note: fs returns null from node-stdlib-browser since we provide our own implementation
12
+ */
13
+ export declare function hasPolyfill(moduleName: string): boolean;
14
+ /**
15
+ * Pre-bundle all polyfills (for faster startup)
16
+ */
17
+ export declare function prebundleAllPolyfills(): Promise<Map<string, string>>;
@@ -0,0 +1,97 @@
1
+ import * as esbuild from "esbuild";
2
+ import stdLibBrowser from "node-stdlib-browser";
3
+ // Cache bundled polyfills
4
+ const polyfillCache = new Map();
5
+ // node-stdlib-browser provides the mapping from Node.js stdlib to polyfill paths
6
+ // e.g., { path: "/path/to/path-browserify/index.js", fs: null, ... }
7
+ // We use this mapping instead of maintaining our own
8
+ /**
9
+ * Bundle a stdlib polyfill module using esbuild
10
+ */
11
+ export async function bundlePolyfill(moduleName) {
12
+ const cached = polyfillCache.get(moduleName);
13
+ if (cached)
14
+ return cached;
15
+ // Get the polyfill entry point from node-stdlib-browser
16
+ const entryPoint = stdLibBrowser[moduleName];
17
+ if (!entryPoint) {
18
+ throw new Error(`No polyfill available for module: ${moduleName}`);
19
+ }
20
+ // Build alias mappings for all Node.js builtins
21
+ // This ensures nested dependencies (like crypto -> stream) are resolved correctly
22
+ const alias = {};
23
+ for (const [name, path] of Object.entries(stdLibBrowser)) {
24
+ if (path !== null) {
25
+ alias[name] = path;
26
+ alias[`node:${name}`] = path;
27
+ }
28
+ }
29
+ // Bundle using esbuild with CommonJS format
30
+ // This ensures proper module.exports handling for all module types including JSON
31
+ const result = await esbuild.build({
32
+ entryPoints: [entryPoint],
33
+ bundle: true,
34
+ write: false,
35
+ format: "cjs",
36
+ platform: "browser",
37
+ target: "es2020",
38
+ minify: false,
39
+ alias,
40
+ define: {
41
+ "process.env.NODE_ENV": '"production"',
42
+ global: "globalThis",
43
+ },
44
+ // Externalize 'process' - we provide our own process polyfill in the bridge.
45
+ // Without this, node-stdlib-browser's process polyfill gets bundled and
46
+ // overwrites globalThis.process, breaking process.argv modifications.
47
+ external: ["process"],
48
+ });
49
+ const code = result.outputFiles[0].text;
50
+ // Check if this is a JSON module (esbuild creates *_default but doesn't export it)
51
+ // For JSON modules, look for the default export pattern and extract it
52
+ const defaultExportMatch = code.match(/var\s+(\w+_default)\s*=\s*\{/);
53
+ let wrappedCode;
54
+ if (defaultExportMatch && !code.includes("module.exports")) {
55
+ // JSON module: wrap and return the default export object
56
+ const defaultVar = defaultExportMatch[1];
57
+ wrappedCode = `(function() {
58
+ ${code}
59
+ return ${defaultVar};
60
+ })()`;
61
+ }
62
+ else {
63
+ // Regular CommonJS module: wrap and return module.exports
64
+ wrappedCode = `(function() {
65
+ var module = { exports: {} };
66
+ var exports = module.exports;
67
+ ${code}
68
+ return module.exports;
69
+ })()`;
70
+ }
71
+ polyfillCache.set(moduleName, wrappedCode);
72
+ return wrappedCode;
73
+ }
74
+ /**
75
+ * Get all available stdlib modules (those with non-null polyfills)
76
+ */
77
+ export function getAvailableStdlib() {
78
+ return Object.keys(stdLibBrowser).filter((key) => stdLibBrowser[key] !== null);
79
+ }
80
+ /**
81
+ * Check if a module has a polyfill available
82
+ * Note: fs returns null from node-stdlib-browser since we provide our own implementation
83
+ */
84
+ export function hasPolyfill(moduleName) {
85
+ // Strip node: prefix
86
+ const name = moduleName.replace(/^node:/, "");
87
+ const polyfill = stdLibBrowser[name];
88
+ return polyfill !== undefined && polyfill !== null;
89
+ }
90
+ /**
91
+ * Pre-bundle all polyfills (for faster startup)
92
+ */
93
+ export async function prebundleAllPolyfills() {
94
+ const modules = getAvailableStdlib();
95
+ await Promise.all(modules.map((m) => bundlePolyfill(m)));
96
+ return new Map(polyfillCache);
97
+ }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Node.js worker adapter.
3
+ *
4
+ * Wraps node:worker_threads for spawning Workers.
5
+ * Used by the WasmVM runtime for WASM process execution.
6
+ */
7
+ export interface WorkerHandle {
8
+ postMessage(data: unknown, transferList?: Transferable[]): void;
9
+ onMessage(handler: (data: unknown) => void): void;
10
+ onError(handler: (err: Error) => void): void;
11
+ onExit(handler: (code: number) => void): void;
12
+ terminate(): Promise<number>;
13
+ }
14
+ export declare class NodeWorkerAdapter {
15
+ /**
16
+ * Spawn a Worker for the given script.
17
+ */
18
+ static create(scriptPath: string | URL, options?: {
19
+ workerData?: unknown;
20
+ }): WorkerHandle;
21
+ }
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Node.js worker adapter.
3
+ *
4
+ * Wraps node:worker_threads for spawning Workers.
5
+ * Used by the WasmVM runtime for WASM process execution.
6
+ */
7
+ import { Worker } from "node:worker_threads";
8
+ export class NodeWorkerAdapter {
9
+ /**
10
+ * Spawn a Worker for the given script.
11
+ */
12
+ static create(scriptPath, options) {
13
+ const worker = new Worker(scriptPath, {
14
+ workerData: options?.workerData,
15
+ });
16
+ return {
17
+ postMessage(data, transferList) {
18
+ worker.postMessage(data, transferList);
19
+ },
20
+ onMessage(handler) {
21
+ worker.on("message", handler);
22
+ },
23
+ onError(handler) {
24
+ worker.on("error", handler);
25
+ },
26
+ onExit(handler) {
27
+ worker.on("exit", handler);
28
+ },
29
+ terminate() {
30
+ return worker.terminate();
31
+ },
32
+ };
33
+ }
34
+ }