@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,150 @@
1
+ import { normalizeBuiltinSpecifier, getPathDir, } from "./builtin-modules.js";
2
+ import { resolveModule } from "./package-bundler.js";
3
+ import { isESM } from "@secure-exec/core/internal/shared/esm-utils";
4
+ import { parseJsonWithLimit } from "./isolate-bootstrap.js";
5
+ export async function getNearestPackageType(deps, filePath) {
6
+ let currentDir = getPathDir(filePath);
7
+ const visitedDirs = [];
8
+ while (true) {
9
+ if (deps.packageTypeCache.has(currentDir)) {
10
+ return deps.packageTypeCache.get(currentDir) ?? null;
11
+ }
12
+ visitedDirs.push(currentDir);
13
+ const packageJsonPath = currentDir === "/" ? "/package.json" : `${currentDir}/package.json`;
14
+ let hasPackageJson = false;
15
+ try {
16
+ hasPackageJson = await deps.filesystem.exists(packageJsonPath);
17
+ }
18
+ catch (error) {
19
+ const err = error;
20
+ if (err?.code !== "EACCES" && err?.code !== "EPERM") {
21
+ throw err;
22
+ }
23
+ }
24
+ if (hasPackageJson) {
25
+ try {
26
+ const packageJsonText = await deps.filesystem.readTextFile(packageJsonPath);
27
+ const pkgJson = parseJsonWithLimit(`package.json ${packageJsonPath}`, packageJsonText, deps.isolateJsonPayloadLimitBytes);
28
+ const packageType = pkgJson.type === "module" || pkgJson.type === "commonjs"
29
+ ? pkgJson.type
30
+ : null;
31
+ for (const dir of visitedDirs) {
32
+ deps.packageTypeCache.set(dir, packageType);
33
+ }
34
+ return packageType;
35
+ }
36
+ catch {
37
+ for (const dir of visitedDirs) {
38
+ deps.packageTypeCache.set(dir, null);
39
+ }
40
+ return null;
41
+ }
42
+ }
43
+ if (currentDir === "/") {
44
+ for (const dir of visitedDirs) {
45
+ deps.packageTypeCache.set(dir, null);
46
+ }
47
+ return null;
48
+ }
49
+ currentDir = getPathDir(currentDir);
50
+ }
51
+ }
52
+ export async function getModuleFormat(deps, filePath, sourceCode) {
53
+ const cached = deps.moduleFormatCache.get(filePath);
54
+ if (cached) {
55
+ return cached;
56
+ }
57
+ let format;
58
+ if (filePath.endsWith(".mjs")) {
59
+ format = "esm";
60
+ }
61
+ else if (filePath.endsWith(".cjs")) {
62
+ format = "cjs";
63
+ }
64
+ else if (filePath.endsWith(".json")) {
65
+ format = "json";
66
+ }
67
+ else if (filePath.endsWith(".js")) {
68
+ const packageType = await getNearestPackageType(deps, filePath);
69
+ if (packageType === "module") {
70
+ format = "esm";
71
+ }
72
+ else if (packageType === "commonjs") {
73
+ format = "cjs";
74
+ }
75
+ else if (sourceCode && isESM(sourceCode, filePath)) {
76
+ // Some package managers/projected filesystems omit package.json.
77
+ // Fall back to syntax-based detection for plain .js modules.
78
+ format = "esm";
79
+ }
80
+ else {
81
+ format = "cjs";
82
+ }
83
+ }
84
+ else {
85
+ format = "cjs";
86
+ }
87
+ deps.moduleFormatCache.set(filePath, format);
88
+ return format;
89
+ }
90
+ export async function shouldRunAsESM(deps, code, filePath) {
91
+ // Keep heuristic mode for string-only snippets without file metadata.
92
+ if (!filePath) {
93
+ return isESM(code);
94
+ }
95
+ return (await getModuleFormat(deps, filePath)) === "esm";
96
+ }
97
+ export async function resolveReferrerDirectory(deps, referrerPath) {
98
+ if (referrerPath === "" || referrerPath === "/") {
99
+ return "/";
100
+ }
101
+ // Dynamic import hooks may pass either a module file path or a module
102
+ // directory path. Prefer filesystem metadata so we do not strip one level
103
+ // when the referrer is already a directory.
104
+ if (deps.filesystem) {
105
+ try {
106
+ const statInfo = await deps.filesystem.stat(referrerPath);
107
+ if (statInfo.isDirectory) {
108
+ return referrerPath;
109
+ }
110
+ }
111
+ catch {
112
+ // Fall back to string-based path handling below.
113
+ }
114
+ }
115
+ if (referrerPath.endsWith("/")) {
116
+ return referrerPath.slice(0, -1) || "/";
117
+ }
118
+ const lastSlash = referrerPath.lastIndexOf("/");
119
+ if (lastSlash <= 0) {
120
+ return "/";
121
+ }
122
+ return referrerPath.slice(0, lastSlash);
123
+ }
124
+ export async function resolveESMPath(deps, specifier, referrerPath) {
125
+ // Handle built-ins and bridged modules first.
126
+ const builtinSpecifier = normalizeBuiltinSpecifier(specifier);
127
+ if (builtinSpecifier) {
128
+ return builtinSpecifier;
129
+ }
130
+ const referrerDir = await resolveReferrerDirectory(deps, referrerPath);
131
+ // Preserve direct path imports before falling back to node_modules
132
+ // resolution so missing relative modules report the resolved sandbox path.
133
+ if (specifier.startsWith("/")) {
134
+ return specifier;
135
+ }
136
+ if (specifier.startsWith("./") || specifier.startsWith("../")) {
137
+ const parts = referrerDir.split("/").filter(Boolean);
138
+ for (const part of specifier.split("/")) {
139
+ if (part === "..") {
140
+ parts.pop();
141
+ continue;
142
+ }
143
+ if (part !== ".") {
144
+ parts.push(part);
145
+ }
146
+ }
147
+ return `/${parts.join("/")}`;
148
+ }
149
+ return resolveModule(specifier, referrerDir, deps.filesystem, "import", deps.resolutionCache);
150
+ }
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Node.js filesystem adapter for kernel integration.
3
+ *
4
+ * Implements VirtualFileSystem by delegating to node:fs/promises.
5
+ * When the kernel uses a HostNodeFileSystem, file operations go to the
6
+ * real host filesystem (sandboxed by the root path).
7
+ */
8
+ import type { VirtualFileSystem, VirtualStat, VirtualDirEntry } from "@secure-exec/core";
9
+ export interface HostNodeFileSystemOptions {
10
+ /** Root directory on the host — all paths are relative to this. */
11
+ root?: string;
12
+ }
13
+ export declare class HostNodeFileSystem implements VirtualFileSystem {
14
+ private root;
15
+ constructor(options?: HostNodeFileSystemOptions);
16
+ private resolve;
17
+ prepareOpenSync(p: string, flags: number): boolean;
18
+ readFile(p: string): Promise<Uint8Array>;
19
+ readTextFile(p: string): Promise<string>;
20
+ readDir(p: string): Promise<string[]>;
21
+ readDirWithTypes(p: string): Promise<VirtualDirEntry[]>;
22
+ writeFile(p: string, content: string | Uint8Array): Promise<void>;
23
+ createDir(p: string): Promise<void>;
24
+ mkdir(p: string, options?: {
25
+ recursive?: boolean;
26
+ }): Promise<void>;
27
+ exists(p: string): Promise<boolean>;
28
+ stat(p: string): Promise<VirtualStat>;
29
+ removeFile(p: string): Promise<void>;
30
+ removeDir(p: string): Promise<void>;
31
+ rename(oldPath: string, newPath: string): Promise<void>;
32
+ realpath(p: string): Promise<string>;
33
+ symlink(target: string, linkPath: string): Promise<void>;
34
+ readlink(p: string): Promise<string>;
35
+ lstat(p: string): Promise<VirtualStat>;
36
+ link(oldPath: string, newPath: string): Promise<void>;
37
+ chmod(p: string, mode: number): Promise<void>;
38
+ chown(p: string, uid: number, gid: number): Promise<void>;
39
+ utimes(p: string, atime: number, mtime: number): Promise<void>;
40
+ truncate(p: string, length: number): Promise<void>;
41
+ pread(p: string, offset: number, length: number): Promise<Uint8Array>;
42
+ }
@@ -0,0 +1,161 @@
1
+ /**
2
+ * Node.js filesystem adapter for kernel integration.
3
+ *
4
+ * Implements VirtualFileSystem by delegating to node:fs/promises.
5
+ * When the kernel uses a HostNodeFileSystem, file operations go to the
6
+ * real host filesystem (sandboxed by the root path).
7
+ */
8
+ import * as fs from "node:fs/promises";
9
+ import * as fsSync from "node:fs";
10
+ import * as path from "node:path";
11
+ import { KernelError, O_CREAT, O_EXCL, O_TRUNC } from "@secure-exec/core";
12
+ export class HostNodeFileSystem {
13
+ root;
14
+ constructor(options) {
15
+ this.root = options?.root ?? "/";
16
+ }
17
+ resolve(p) {
18
+ // Map virtual path to host path under root
19
+ const normalized = path.posix.normalize(p);
20
+ return path.join(this.root, normalized);
21
+ }
22
+ prepareOpenSync(p, flags) {
23
+ const hostPath = this.resolve(p);
24
+ const hasCreate = (flags & O_CREAT) !== 0;
25
+ const hasExcl = (flags & O_EXCL) !== 0;
26
+ const hasTrunc = (flags & O_TRUNC) !== 0;
27
+ const exists = fsSync.existsSync(hostPath);
28
+ if (hasCreate && hasExcl && exists) {
29
+ throw new KernelError("EEXIST", `file already exists, open '${p}'`);
30
+ }
31
+ let created = false;
32
+ if (!exists && hasCreate) {
33
+ fsSync.mkdirSync(path.dirname(hostPath), { recursive: true });
34
+ fsSync.writeFileSync(hostPath, new Uint8Array(0));
35
+ created = true;
36
+ }
37
+ if (hasTrunc) {
38
+ try {
39
+ fsSync.truncateSync(hostPath, 0);
40
+ }
41
+ catch (error) {
42
+ const err = error;
43
+ if (err.code === "ENOENT") {
44
+ throw new KernelError("ENOENT", `no such file or directory, open '${p}'`);
45
+ }
46
+ if (err.code === "EISDIR") {
47
+ throw new KernelError("EISDIR", `illegal operation on a directory, open '${p}'`);
48
+ }
49
+ throw error;
50
+ }
51
+ }
52
+ return created;
53
+ }
54
+ async readFile(p) {
55
+ return new Uint8Array(await fs.readFile(this.resolve(p)));
56
+ }
57
+ async readTextFile(p) {
58
+ return fs.readFile(this.resolve(p), "utf-8");
59
+ }
60
+ async readDir(p) {
61
+ return fs.readdir(this.resolve(p));
62
+ }
63
+ async readDirWithTypes(p) {
64
+ const entries = await fs.readdir(this.resolve(p), {
65
+ withFileTypes: true,
66
+ });
67
+ return entries.map((e) => ({
68
+ name: e.name,
69
+ isDirectory: e.isDirectory(),
70
+ isSymbolicLink: e.isSymbolicLink(),
71
+ }));
72
+ }
73
+ async writeFile(p, content) {
74
+ const hostPath = this.resolve(p);
75
+ await fs.mkdir(path.dirname(hostPath), { recursive: true });
76
+ await fs.writeFile(hostPath, content);
77
+ }
78
+ async createDir(p) {
79
+ await fs.mkdir(this.resolve(p));
80
+ }
81
+ async mkdir(p, options) {
82
+ await fs.mkdir(this.resolve(p), { recursive: options?.recursive ?? true });
83
+ }
84
+ async exists(p) {
85
+ try {
86
+ await fs.access(this.resolve(p));
87
+ return true;
88
+ }
89
+ catch {
90
+ return false;
91
+ }
92
+ }
93
+ async stat(p) {
94
+ const s = await fs.stat(this.resolve(p));
95
+ return toVirtualStat(s);
96
+ }
97
+ async removeFile(p) {
98
+ await fs.unlink(this.resolve(p));
99
+ }
100
+ async removeDir(p) {
101
+ await fs.rmdir(this.resolve(p));
102
+ }
103
+ async rename(oldPath, newPath) {
104
+ await fs.rename(this.resolve(oldPath), this.resolve(newPath));
105
+ }
106
+ async realpath(p) {
107
+ return fs.realpath(this.resolve(p));
108
+ }
109
+ async symlink(target, linkPath) {
110
+ await fs.symlink(target, this.resolve(linkPath));
111
+ }
112
+ async readlink(p) {
113
+ return fs.readlink(this.resolve(p));
114
+ }
115
+ async lstat(p) {
116
+ const s = await fs.lstat(this.resolve(p));
117
+ return toVirtualStat(s);
118
+ }
119
+ async link(oldPath, newPath) {
120
+ await fs.link(this.resolve(oldPath), this.resolve(newPath));
121
+ }
122
+ async chmod(p, mode) {
123
+ await fs.chmod(this.resolve(p), mode);
124
+ }
125
+ async chown(p, uid, gid) {
126
+ await fs.chown(this.resolve(p), uid, gid);
127
+ }
128
+ async utimes(p, atime, mtime) {
129
+ await fs.utimes(this.resolve(p), atime / 1000, mtime / 1000);
130
+ }
131
+ async truncate(p, length) {
132
+ await fs.truncate(this.resolve(p), length);
133
+ }
134
+ async pread(p, offset, length) {
135
+ const handle = await fs.open(this.resolve(p), "r");
136
+ try {
137
+ const buf = new Uint8Array(length);
138
+ const { bytesRead } = await handle.read(buf, 0, length, offset);
139
+ return bytesRead < length ? buf.slice(0, bytesRead) : buf;
140
+ }
141
+ finally {
142
+ await handle.close();
143
+ }
144
+ }
145
+ }
146
+ function toVirtualStat(s) {
147
+ return {
148
+ mode: s.mode,
149
+ size: s.size,
150
+ isDirectory: s.isDirectory(),
151
+ isSymbolicLink: s.isSymbolicLink(),
152
+ atimeMs: s.atimeMs,
153
+ mtimeMs: s.mtimeMs,
154
+ ctimeMs: s.ctimeMs,
155
+ birthtimeMs: s.birthtimeMs,
156
+ ino: s.ino,
157
+ nlink: s.nlink,
158
+ uid: s.uid,
159
+ gid: s.gid,
160
+ };
161
+ }
@@ -0,0 +1,36 @@
1
+ import type { VirtualFileSystem } from "@secure-exec/core";
2
+ type ResolveMode = "require" | "import";
3
+ interface PackageJson {
4
+ main?: string;
5
+ type?: "module" | "commonjs";
6
+ exports?: unknown;
7
+ imports?: unknown;
8
+ }
9
+ /** Caches for module resolution to avoid redundant VFS probes. */
10
+ export interface ResolutionCache {
11
+ /** Top-level resolution results keyed by `request\0fromDir\0mode` */
12
+ resolveResults: Map<string, string | null>;
13
+ /** Parsed package.json content by path */
14
+ packageJsonResults: Map<string, PackageJson | null>;
15
+ /** File existence by path */
16
+ existsResults: Map<string, boolean>;
17
+ /** Stat results by path (null = ENOENT) */
18
+ statResults: Map<string, {
19
+ isDirectory: boolean;
20
+ } | null>;
21
+ }
22
+ export declare function createResolutionCache(): ResolutionCache;
23
+ /**
24
+ * Resolve a module request to an absolute path in the virtual filesystem
25
+ */
26
+ export declare function resolveModule(request: string, fromDir: string, fs: VirtualFileSystem, mode?: ResolveMode, cache?: ResolutionCache): Promise<string | null>;
27
+ /**
28
+ * Load a file's content from the virtual filesystem
29
+ */
30
+ export declare function loadFile(path: string, fs: VirtualFileSystem): Promise<string | null>;
31
+ /**
32
+ * Legacy function - bundle a package from node_modules (simple approach)
33
+ * This is kept for backwards compatibility but the new dynamic resolution is preferred
34
+ */
35
+ export declare function bundlePackage(packageName: string, fs: VirtualFileSystem): Promise<string | null>;
36
+ export {};