@secure-exec/browser 0.0.0-main.bccb3a2

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.
@@ -0,0 +1,459 @@
1
+ import { getBrowserSystemDriverOptions } from "./driver.js";
2
+ import { createFsStub, createNetworkStub, loadFile, mkdir, resolveModule, } from "./runtime.js";
3
+ import { assertBrowserSyncBridgeSupport, createBrowserSyncBridgePayload, SYNC_BRIDGE_KIND_BINARY, SYNC_BRIDGE_KIND_JSON, SYNC_BRIDGE_KIND_NONE, SYNC_BRIDGE_KIND_TEXT, SYNC_BRIDGE_PAYLOAD_LIMIT_ERROR_CODE, SYNC_BRIDGE_SIGNAL_KIND_INDEX, SYNC_BRIDGE_SIGNAL_LENGTH_INDEX, SYNC_BRIDGE_SIGNAL_STATE_INDEX, SYNC_BRIDGE_SIGNAL_STATE_READY, SYNC_BRIDGE_SIGNAL_STATUS_INDEX, SYNC_BRIDGE_STATUS_ERROR, SYNC_BRIDGE_STATUS_OK, toBrowserSyncBridgeError, } from "./sync-bridge.js";
4
+ const DEFAULT_BROWSER_TIMING_MITIGATION = "freeze";
5
+ const BROWSER_OPTION_VALIDATORS = [
6
+ {
7
+ label: "memoryLimit",
8
+ hasValue: (options) => options.memoryLimit !== undefined,
9
+ },
10
+ {
11
+ label: "cpuTimeLimitMs",
12
+ hasValue: (options) => options.cpuTimeLimitMs !== undefined,
13
+ },
14
+ ];
15
+ function serializePermissions(permissions) {
16
+ if (!permissions) {
17
+ return undefined;
18
+ }
19
+ const serialize = (fn) => typeof fn === "function" ? fn.toString() : undefined;
20
+ return {
21
+ fs: serialize(permissions.fs),
22
+ network: serialize(permissions.network),
23
+ childProcess: serialize(permissions.childProcess),
24
+ env: serialize(permissions.env),
25
+ };
26
+ }
27
+ function resolveWorkerUrl(workerUrl) {
28
+ if (workerUrl instanceof URL) {
29
+ return workerUrl;
30
+ }
31
+ if (workerUrl) {
32
+ return new URL(workerUrl, import.meta.url);
33
+ }
34
+ return new URL("./worker.js", import.meta.url);
35
+ }
36
+ function createWorkerControlToken() {
37
+ if (typeof globalThis.crypto?.randomUUID === "function") {
38
+ return globalThis.crypto.randomUUID();
39
+ }
40
+ return `browser-runtime-${Date.now()}-${Math.random().toString(16).slice(2)}`;
41
+ }
42
+ function toBrowserWorkerExecOptions(options) {
43
+ if (!options) {
44
+ return undefined;
45
+ }
46
+ return {
47
+ filePath: options.filePath,
48
+ env: options.env,
49
+ cwd: options.cwd,
50
+ stdin: options.stdin,
51
+ timingMitigation: options.timingMitigation,
52
+ };
53
+ }
54
+ function validateBrowserRuntimeOptions(options) {
55
+ const unsupported = BROWSER_OPTION_VALIDATORS.filter((validator) => validator.hasValue(options)).map((validator) => validator.label);
56
+ if (unsupported.length === 0) {
57
+ return;
58
+ }
59
+ throw new Error(`Browser runtime does not support Node-only options: ${unsupported.join(", ")}`);
60
+ }
61
+ function validateBrowserExecOptions(options) {
62
+ const unsupported = [];
63
+ if (options?.cpuTimeLimitMs !== undefined) {
64
+ unsupported.push("cpuTimeLimitMs");
65
+ }
66
+ if (unsupported.length === 0) {
67
+ return;
68
+ }
69
+ throw new Error(`Browser runtime does not support Node-only exec options: ${unsupported.join(", ")}`);
70
+ }
71
+ function isStdioMessage(message) {
72
+ return message.type === "stdio";
73
+ }
74
+ function isResponseMessage(message) {
75
+ return message.type === "response";
76
+ }
77
+ function isSyncRequestMessage(message) {
78
+ return message.type === "sync-request";
79
+ }
80
+ function createSyncBridgeFilesystem(options) {
81
+ return options.system.filesystem ?? createFsStub();
82
+ }
83
+ function throwBridgePayloadTooLarge(label, actualBytes, maxBytes) {
84
+ const error = new Error(`[${SYNC_BRIDGE_PAYLOAD_LIMIT_ERROR_CODE}] ${label}: payload is ${actualBytes} bytes, limit is ${maxBytes} bytes`);
85
+ error.code = SYNC_BRIDGE_PAYLOAD_LIMIT_ERROR_CODE;
86
+ throw error;
87
+ }
88
+ function toUint8Array(value) {
89
+ if (value instanceof Uint8Array) {
90
+ return value;
91
+ }
92
+ if (ArrayBuffer.isView(value)) {
93
+ return new Uint8Array(value.buffer, value.byteOffset, value.byteLength);
94
+ }
95
+ if (value instanceof ArrayBuffer) {
96
+ return new Uint8Array(value);
97
+ }
98
+ return new TextEncoder().encode(String(value));
99
+ }
100
+ function createSyncBridgeResponseBytes(response, encoder) {
101
+ switch (response.kind) {
102
+ case SYNC_BRIDGE_KIND_NONE:
103
+ return new Uint8Array(0);
104
+ case SYNC_BRIDGE_KIND_TEXT:
105
+ return encoder.encode(response.value);
106
+ case SYNC_BRIDGE_KIND_BINARY:
107
+ return response.value;
108
+ case SYNC_BRIDGE_KIND_JSON:
109
+ return encoder.encode(JSON.stringify(response.value));
110
+ default:
111
+ return new Uint8Array(0);
112
+ }
113
+ }
114
+ async function handleSyncBridgeOperation(filesystem, message) {
115
+ switch (message.operation) {
116
+ case "fs.readFile":
117
+ return {
118
+ kind: SYNC_BRIDGE_KIND_TEXT,
119
+ value: await filesystem.readTextFile(String(message.args[0])),
120
+ };
121
+ case "fs.writeFile":
122
+ await filesystem.writeFile(String(message.args[0]), String(message.args[1] ?? ""));
123
+ return { kind: SYNC_BRIDGE_KIND_NONE };
124
+ case "fs.readFileBinary":
125
+ return {
126
+ kind: SYNC_BRIDGE_KIND_BINARY,
127
+ value: await filesystem.readFile(String(message.args[0])),
128
+ };
129
+ case "fs.writeFileBinary":
130
+ await filesystem.writeFile(String(message.args[0]), toUint8Array(message.args[1]));
131
+ return { kind: SYNC_BRIDGE_KIND_NONE };
132
+ case "fs.readDir":
133
+ return {
134
+ kind: SYNC_BRIDGE_KIND_JSON,
135
+ value: await filesystem.readDirWithTypes(String(message.args[0])),
136
+ };
137
+ case "fs.createDir":
138
+ await filesystem.createDir(String(message.args[0]));
139
+ return { kind: SYNC_BRIDGE_KIND_NONE };
140
+ case "fs.mkdir":
141
+ await mkdir(filesystem, String(message.args[0]));
142
+ return { kind: SYNC_BRIDGE_KIND_NONE };
143
+ case "fs.rmdir":
144
+ await filesystem.removeDir(String(message.args[0]));
145
+ return { kind: SYNC_BRIDGE_KIND_NONE };
146
+ case "fs.exists":
147
+ return {
148
+ kind: SYNC_BRIDGE_KIND_JSON,
149
+ value: await filesystem.exists(String(message.args[0])),
150
+ };
151
+ case "fs.stat":
152
+ return {
153
+ kind: SYNC_BRIDGE_KIND_JSON,
154
+ value: await filesystem.stat(String(message.args[0])),
155
+ };
156
+ case "fs.lstat":
157
+ return {
158
+ kind: SYNC_BRIDGE_KIND_JSON,
159
+ value: await filesystem.lstat(String(message.args[0])),
160
+ };
161
+ case "fs.unlink":
162
+ await filesystem.removeFile(String(message.args[0]));
163
+ return { kind: SYNC_BRIDGE_KIND_NONE };
164
+ case "fs.rename":
165
+ await filesystem.rename(String(message.args[0]), String(message.args[1]));
166
+ return { kind: SYNC_BRIDGE_KIND_NONE };
167
+ case "fs.realpath":
168
+ return {
169
+ kind: SYNC_BRIDGE_KIND_TEXT,
170
+ value: await filesystem.realpath(String(message.args[0])),
171
+ };
172
+ case "fs.readlink":
173
+ return {
174
+ kind: SYNC_BRIDGE_KIND_TEXT,
175
+ value: await filesystem.readlink(String(message.args[0])),
176
+ };
177
+ case "fs.symlink":
178
+ await filesystem.symlink(String(message.args[0]), String(message.args[1]));
179
+ return { kind: SYNC_BRIDGE_KIND_NONE };
180
+ case "fs.link":
181
+ await filesystem.link(String(message.args[0]), String(message.args[1]));
182
+ return { kind: SYNC_BRIDGE_KIND_NONE };
183
+ case "fs.chmod":
184
+ await filesystem.chmod(String(message.args[0]), Number(message.args[1]));
185
+ return { kind: SYNC_BRIDGE_KIND_NONE };
186
+ case "fs.truncate":
187
+ await filesystem.truncate(String(message.args[0]), Number(message.args[1]));
188
+ return { kind: SYNC_BRIDGE_KIND_NONE };
189
+ case "module.resolve": {
190
+ const resolved = await resolveModule(String(message.args[0]), String(message.args[1]), filesystem);
191
+ return resolved === null
192
+ ? { kind: SYNC_BRIDGE_KIND_NONE }
193
+ : { kind: SYNC_BRIDGE_KIND_TEXT, value: resolved };
194
+ }
195
+ case "module.loadFile": {
196
+ const source = await loadFile(String(message.args[0]), filesystem);
197
+ return source === null
198
+ ? { kind: SYNC_BRIDGE_KIND_NONE }
199
+ : { kind: SYNC_BRIDGE_KIND_TEXT, value: source };
200
+ }
201
+ default:
202
+ throw new Error(`Unsupported browser sync bridge operation: ${String(message.operation)}`);
203
+ }
204
+ }
205
+ export class BrowserRuntimeDriver {
206
+ worker;
207
+ pending = new Map();
208
+ controlToken = createWorkerControlToken();
209
+ defaultOnStdio;
210
+ defaultTimingMitigation;
211
+ networkAdapter;
212
+ syncBridge;
213
+ syncFilesystem;
214
+ ready;
215
+ encoder = new TextEncoder();
216
+ nextId = 1;
217
+ disposed = false;
218
+ constructor(options, factoryOptions = {}) {
219
+ if (typeof Worker === "undefined") {
220
+ throw new Error("Browser runtime requires a global Worker implementation");
221
+ }
222
+ assertBrowserSyncBridgeSupport();
223
+ this.defaultOnStdio = options.onStdio;
224
+ this.defaultTimingMitigation =
225
+ options.timingMitigation ??
226
+ options.runtime.process.timingMitigation ??
227
+ DEFAULT_BROWSER_TIMING_MITIGATION;
228
+ this.networkAdapter = options.system.network ?? createNetworkStub();
229
+ this.syncBridge = createBrowserSyncBridgePayload(options.payloadLimits);
230
+ this.syncFilesystem = createSyncBridgeFilesystem(options);
231
+ this.worker = new Worker(resolveWorkerUrl(factoryOptions.workerUrl), {
232
+ type: "module",
233
+ });
234
+ this.worker.onmessage = this.handleWorkerMessage;
235
+ this.worker.onerror = this.handleWorkerError;
236
+ const browserSystemOptions = getBrowserSystemDriverOptions(options.system);
237
+ const initPayload = {
238
+ processConfig: options.runtime.process,
239
+ osConfig: options.runtime.os,
240
+ permissions: serializePermissions(options.system.permissions),
241
+ filesystem: browserSystemOptions.filesystem,
242
+ networkEnabled: browserSystemOptions.networkEnabled,
243
+ timingMitigation: this.defaultTimingMitigation,
244
+ payloadLimits: options.payloadLimits,
245
+ syncBridge: this.syncBridge,
246
+ };
247
+ this.ready = this.callWorker("init", initPayload).then(() => undefined);
248
+ this.ready.catch(() => undefined);
249
+ }
250
+ get network() {
251
+ const adapter = this.networkAdapter;
252
+ return {
253
+ fetch: (url, options) => adapter.fetch(url, options),
254
+ dnsLookup: (hostname) => adapter.dnsLookup(hostname),
255
+ httpRequest: (url, options) => adapter.httpRequest(url, options),
256
+ };
257
+ }
258
+ handleWorkerError = (event) => {
259
+ if (this.disposed) {
260
+ return;
261
+ }
262
+ const error = event.error instanceof Error
263
+ ? event.error
264
+ : new Error(event.message
265
+ ? `Browser runtime worker error: ${event.message} (${event.filename}:${event.lineno}:${event.colno})`
266
+ : "Browser runtime worker error");
267
+ this.cleanup(error, { terminateWorker: true });
268
+ };
269
+ handleWorkerMessage = (event) => {
270
+ if (this.disposed) {
271
+ return;
272
+ }
273
+ const message = event.data;
274
+ if (message.controlToken !== this.controlToken) {
275
+ return;
276
+ }
277
+ if (isSyncRequestMessage(message)) {
278
+ void this.handleSyncRequest(message);
279
+ return;
280
+ }
281
+ if (isStdioMessage(message)) {
282
+ const pending = this.pending.get(message.requestId);
283
+ const hook = pending?.hook ?? this.defaultOnStdio;
284
+ if (!hook) {
285
+ return;
286
+ }
287
+ try {
288
+ hook({ channel: message.channel, message: message.message });
289
+ }
290
+ catch {
291
+ // Ignore host hook errors so sandbox execution can continue.
292
+ }
293
+ return;
294
+ }
295
+ if (!isResponseMessage(message)) {
296
+ return;
297
+ }
298
+ const pending = this.pending.get(message.id);
299
+ if (!pending) {
300
+ return;
301
+ }
302
+ this.pending.delete(message.id);
303
+ if (message.ok) {
304
+ pending.resolve(message.result);
305
+ return;
306
+ }
307
+ const error = new Error(message.error.message);
308
+ if (message.error.stack) {
309
+ error.stack = message.error.stack;
310
+ }
311
+ error.code = message.error.code;
312
+ pending.reject(error);
313
+ };
314
+ async handleSyncRequest(message) {
315
+ const signal = new Int32Array(this.syncBridge.signalBuffer);
316
+ const data = new Uint8Array(this.syncBridge.dataBuffer);
317
+ try {
318
+ const response = await handleSyncBridgeOperation(this.syncFilesystem, message);
319
+ const bytes = createSyncBridgeResponseBytes(response, this.encoder);
320
+ if (bytes.byteLength > data.byteLength) {
321
+ const suffix = typeof message.args[0] === "string" ? ` ${message.args[0]}` : "";
322
+ throwBridgePayloadTooLarge(`${message.operation}${suffix}`, bytes.byteLength, data.byteLength);
323
+ }
324
+ data.set(bytes, 0);
325
+ Atomics.store(signal, SYNC_BRIDGE_SIGNAL_STATUS_INDEX, SYNC_BRIDGE_STATUS_OK);
326
+ Atomics.store(signal, SYNC_BRIDGE_SIGNAL_KIND_INDEX, response.kind);
327
+ Atomics.store(signal, SYNC_BRIDGE_SIGNAL_LENGTH_INDEX, bytes.byteLength);
328
+ }
329
+ catch (error) {
330
+ let bytes = this.encoder.encode(JSON.stringify(toBrowserSyncBridgeError(error)));
331
+ if (bytes.byteLength > data.byteLength) {
332
+ bytes = this.encoder.encode(JSON.stringify({
333
+ message: "Browser runtime sync bridge error exceeded shared buffer capacity",
334
+ code: SYNC_BRIDGE_PAYLOAD_LIMIT_ERROR_CODE,
335
+ }));
336
+ }
337
+ data.set(bytes, 0);
338
+ Atomics.store(signal, SYNC_BRIDGE_SIGNAL_STATUS_INDEX, SYNC_BRIDGE_STATUS_ERROR);
339
+ Atomics.store(signal, SYNC_BRIDGE_SIGNAL_KIND_INDEX, SYNC_BRIDGE_KIND_JSON);
340
+ Atomics.store(signal, SYNC_BRIDGE_SIGNAL_LENGTH_INDEX, bytes.byteLength);
341
+ }
342
+ Atomics.store(signal, SYNC_BRIDGE_SIGNAL_STATE_INDEX, SYNC_BRIDGE_SIGNAL_STATE_READY);
343
+ Atomics.notify(signal, SYNC_BRIDGE_SIGNAL_STATE_INDEX, 1);
344
+ }
345
+ rejectAllPending(error) {
346
+ const entries = Array.from(this.pending.values());
347
+ this.pending.clear();
348
+ for (const pending of entries) {
349
+ pending.reject(error);
350
+ }
351
+ }
352
+ clearWorkerHandlers() {
353
+ try {
354
+ this.worker.onmessage = null;
355
+ }
356
+ catch {
357
+ // Ignore host Worker implementations with non-writable event hooks.
358
+ }
359
+ try {
360
+ this.worker.onerror = null;
361
+ }
362
+ catch {
363
+ // Ignore host Worker implementations with non-writable event hooks.
364
+ }
365
+ }
366
+ resetSyncBridgeState() {
367
+ new Int32Array(this.syncBridge.signalBuffer).fill(0);
368
+ new Uint8Array(this.syncBridge.dataBuffer).fill(0);
369
+ }
370
+ cleanup(error, options = {}) {
371
+ if (this.disposed) {
372
+ this.rejectAllPending(error);
373
+ return;
374
+ }
375
+ this.disposed = true;
376
+ this.clearWorkerHandlers();
377
+ if (options.terminateWorker) {
378
+ try {
379
+ this.worker.terminate();
380
+ }
381
+ catch {
382
+ // Ignore termination errors while tearing down a broken worker.
383
+ }
384
+ }
385
+ this.resetSyncBridgeState();
386
+ this.rejectAllPending(error);
387
+ }
388
+ callWorker(type, payload, hook) {
389
+ if (this.disposed) {
390
+ return Promise.reject(new Error("Browser runtime has been disposed"));
391
+ }
392
+ const id = this.nextId++;
393
+ const message = payload === undefined
394
+ ? {
395
+ controlToken: this.controlToken,
396
+ id,
397
+ type,
398
+ }
399
+ : {
400
+ controlToken: this.controlToken,
401
+ id,
402
+ type,
403
+ payload,
404
+ };
405
+ return new Promise((resolve, reject) => {
406
+ this.pending.set(id, { resolve, reject, hook });
407
+ try {
408
+ this.worker.postMessage(message);
409
+ }
410
+ catch (error) {
411
+ this.pending.delete(id);
412
+ reject(error);
413
+ }
414
+ });
415
+ }
416
+ async run(code, filePath) {
417
+ await this.ready;
418
+ const hook = this.defaultOnStdio;
419
+ return this.callWorker("run", {
420
+ code,
421
+ filePath,
422
+ captureStdio: Boolean(hook),
423
+ }, hook);
424
+ }
425
+ async exec(code, options) {
426
+ validateBrowserExecOptions(options);
427
+ await this.ready;
428
+ const hook = options?.onStdio ?? this.defaultOnStdio;
429
+ return this.callWorker("exec", {
430
+ code,
431
+ options: toBrowserWorkerExecOptions(options),
432
+ captureStdio: Boolean(hook),
433
+ }, hook);
434
+ }
435
+ async dispatchExtensionRequest(namespace, payload) {
436
+ await this.ready;
437
+ const response = await this.callWorker("extension", { namespace, payload });
438
+ return response.payload;
439
+ }
440
+ dispose() {
441
+ if (this.disposed) {
442
+ return;
443
+ }
444
+ this.cleanup(new Error("Browser runtime has been disposed"), {
445
+ terminateWorker: true,
446
+ });
447
+ }
448
+ async terminate() {
449
+ this.dispose();
450
+ }
451
+ }
452
+ export function createBrowserRuntimeDriverFactory(factoryOptions = {}) {
453
+ return {
454
+ createRuntimeDriver(options) {
455
+ validateBrowserRuntimeOptions(options);
456
+ return new BrowserRuntimeDriver(options, factoryOptions);
457
+ },
458
+ };
459
+ }
@@ -0,0 +1,222 @@
1
+ import { createInMemoryFileSystem, InMemoryFileSystem } from "./os-filesystem.js";
2
+ export type StdioChannel = "stdout" | "stderr";
3
+ export type TimingMitigation = "off" | "freeze";
4
+ type BodyLike = unknown;
5
+ export interface VirtualDirEntry {
6
+ name: string;
7
+ isDirectory: boolean;
8
+ isSymbolicLink?: boolean;
9
+ }
10
+ export interface VirtualStat {
11
+ mode: number;
12
+ size: number;
13
+ blocks: number;
14
+ dev: number;
15
+ rdev: number;
16
+ isDirectory: boolean;
17
+ isSymbolicLink: boolean;
18
+ atimeMs: number;
19
+ mtimeMs: number;
20
+ ctimeMs: number;
21
+ birthtimeMs: number;
22
+ ino: number;
23
+ nlink: number;
24
+ uid: number;
25
+ gid: number;
26
+ }
27
+ export interface VirtualFileSystem {
28
+ readFile(path: string): Promise<Uint8Array>;
29
+ readTextFile(path: string): Promise<string>;
30
+ readDir(path: string): Promise<string[]>;
31
+ readDirWithTypes(path: string): Promise<VirtualDirEntry[]>;
32
+ writeFile(path: string, content: string | Uint8Array): Promise<void>;
33
+ createDir(path: string): Promise<void>;
34
+ mkdir(path: string, options?: {
35
+ recursive?: boolean;
36
+ }): Promise<void>;
37
+ exists(path: string): Promise<boolean>;
38
+ stat(path: string): Promise<VirtualStat>;
39
+ removeFile(path: string): Promise<void>;
40
+ removeDir(path: string): Promise<void>;
41
+ rename(oldPath: string, newPath: string): Promise<void>;
42
+ realpath(path: string): Promise<string>;
43
+ symlink(target: string, linkPath: string): Promise<void>;
44
+ readlink(path: string): Promise<string>;
45
+ lstat(path: string): Promise<VirtualStat>;
46
+ link(oldPath: string, newPath: string): Promise<void>;
47
+ chmod(path: string, mode: number): Promise<void>;
48
+ chown(path: string, uid: number, gid: number): Promise<void>;
49
+ utimes(path: string, atime: number, mtime: number): Promise<void>;
50
+ truncate(path: string, length: number): Promise<void>;
51
+ pread(path: string, offset: number, length: number): Promise<Uint8Array>;
52
+ pwrite(path: string, offset: number, data: Uint8Array): Promise<void>;
53
+ }
54
+ export type PermissionDecision = boolean | {
55
+ allowed: boolean;
56
+ reason?: string;
57
+ } | {
58
+ allow: boolean;
59
+ reason?: string;
60
+ };
61
+ export type PermissionCheck<T = unknown> = (request: T) => PermissionDecision;
62
+ export interface Permissions {
63
+ fs?: PermissionCheck<{
64
+ path: string;
65
+ operation: string;
66
+ }>;
67
+ network?: PermissionCheck<{
68
+ url?: string;
69
+ host?: string;
70
+ port?: number;
71
+ }>;
72
+ childProcess?: PermissionCheck<{
73
+ command: string;
74
+ args: string[];
75
+ }>;
76
+ env?: PermissionCheck<{
77
+ name: string;
78
+ value: string;
79
+ }>;
80
+ }
81
+ export declare const allowAllFs: PermissionCheck;
82
+ export declare const allowAllNetwork: PermissionCheck;
83
+ export declare const allowAllChildProcess: PermissionCheck;
84
+ export declare const allowAllEnv: PermissionCheck;
85
+ export declare const allowAll: Permissions;
86
+ export interface ExecOptions {
87
+ filePath?: string;
88
+ env?: Record<string, string>;
89
+ cwd?: string;
90
+ stdin?: string;
91
+ cpuTimeLimitMs?: number;
92
+ timingMitigation?: TimingMitigation;
93
+ onStdio?: StdioHook;
94
+ }
95
+ export interface ExecResult {
96
+ code: number;
97
+ exitCode?: number;
98
+ stdout?: string;
99
+ stderr?: string;
100
+ errorMessage?: string;
101
+ }
102
+ export interface RunResult<T = unknown> {
103
+ value?: T;
104
+ code: number;
105
+ errorMessage?: string;
106
+ exports?: T;
107
+ }
108
+ export interface OSConfig {
109
+ homedir?: string;
110
+ tmpdir?: string;
111
+ }
112
+ export interface ProcessConfig {
113
+ cwd?: string;
114
+ env?: Record<string, string>;
115
+ argv?: string[];
116
+ timingMitigation?: TimingMitigation;
117
+ frozenTimeMs?: number;
118
+ }
119
+ export type StdioEvent = {
120
+ channel: StdioChannel;
121
+ message: string;
122
+ };
123
+ export type StdioHook = (event: StdioEvent) => void;
124
+ export interface CommandExecutor {
125
+ spawn(command: string, args: string[], options?: {
126
+ cwd?: string;
127
+ env?: Record<string, string>;
128
+ onStdout?: (data: Uint8Array) => void;
129
+ onStderr?: (data: Uint8Array) => void;
130
+ }): {
131
+ wait(): Promise<number>;
132
+ writeStdin(data: Uint8Array | string): void;
133
+ closeStdin(): void;
134
+ kill(signal?: number): void;
135
+ };
136
+ }
137
+ export interface NetworkAdapter {
138
+ fetch(url: string, options?: {
139
+ method?: string;
140
+ headers?: Record<string, string>;
141
+ body?: BodyLike | null;
142
+ }): Promise<{
143
+ ok: boolean;
144
+ status: number;
145
+ statusText: string;
146
+ headers: Record<string, string>;
147
+ body: string;
148
+ url: string;
149
+ redirected: boolean;
150
+ }>;
151
+ dnsLookup(hostname: string): Promise<{
152
+ address?: string;
153
+ family?: number;
154
+ error?: string;
155
+ code?: string;
156
+ }>;
157
+ httpRequest(url: string, options?: {
158
+ method?: string;
159
+ headers?: Record<string, string>;
160
+ body?: BodyLike | null;
161
+ }): Promise<{
162
+ status: number;
163
+ statusText: string;
164
+ headers: Record<string, string>;
165
+ body: string;
166
+ url: string;
167
+ }>;
168
+ }
169
+ export interface SystemDriver {
170
+ filesystem?: VirtualFileSystem;
171
+ network?: NetworkAdapter;
172
+ commandExecutor?: CommandExecutor;
173
+ permissions?: Permissions;
174
+ runtime: {
175
+ process: ProcessConfig;
176
+ os: OSConfig;
177
+ };
178
+ }
179
+ export interface RuntimeDriverOptions {
180
+ system: SystemDriver;
181
+ runtime: {
182
+ process: ProcessConfig;
183
+ os: OSConfig;
184
+ };
185
+ memoryLimit?: number;
186
+ cpuTimeLimitMs?: number;
187
+ timingMitigation?: TimingMitigation;
188
+ onStdio?: StdioHook;
189
+ payloadLimits?: {
190
+ base64TransferBytes?: number;
191
+ jsonPayloadBytes?: number;
192
+ };
193
+ }
194
+ export interface NodeRuntimeDriver {
195
+ exec(code: string, options?: ExecOptions): Promise<ExecResult>;
196
+ run<T = unknown>(code: string, filePath?: string): Promise<RunResult<T>>;
197
+ dispose(): void;
198
+ terminate?(): Promise<void>;
199
+ }
200
+ export interface NodeRuntimeDriverFactory {
201
+ createRuntimeDriver(options: RuntimeDriverOptions): NodeRuntimeDriver;
202
+ }
203
+ export declare function filterEnv(env: Record<string, string> | undefined, permissions?: Permissions): Record<string, string>;
204
+ export declare function createEnosysError(operation: string): Error;
205
+ export declare function createFsStub(): VirtualFileSystem;
206
+ export declare function createNetworkStub(): NetworkAdapter;
207
+ export declare function createCommandExecutorStub(): CommandExecutor;
208
+ export declare function wrapFileSystem(filesystem: VirtualFileSystem, permissions?: Permissions): VirtualFileSystem;
209
+ export declare function wrapNetworkAdapter(adapter: NetworkAdapter, permissions?: Permissions): NetworkAdapter;
210
+ export declare function mkdir(filesystem: VirtualFileSystem, path: string, options?: {
211
+ recursive?: boolean;
212
+ } | boolean): Promise<void>;
213
+ export declare function loadFile(path: string, filesystem: VirtualFileSystem): Promise<string | null>;
214
+ export declare function resolveModule(specifier: string, fromPath: string, filesystem: VirtualFileSystem, _mode?: "require" | "import"): Promise<string | null>;
215
+ export declare function isESM(code: string, filePath?: string): boolean;
216
+ export declare function transformDynamicImport(code: string): string;
217
+ export declare const POLYFILL_CODE_MAP: Record<string, string>;
218
+ export declare function exposeCustomGlobal(name: string, value: unknown): void;
219
+ export declare function exposeMutableRuntimeStateGlobal(name: string, value: unknown): void;
220
+ export declare function getIsolateRuntimeSource(id: string): string;
221
+ export declare function getRequireSetupCode(): string;
222
+ export { createInMemoryFileSystem, InMemoryFileSystem };