sandboxedjs 0.1.27 → 0.1.29

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.d.cts CHANGED
@@ -9,6 +9,14 @@ interface ChildHandle {
9
9
  on(event: "stdout" | "stderr" | "exit", listener: (...args: any[]) => void): unknown;
10
10
  exec(): void;
11
11
  sendStdin(data: string): void;
12
+ /**
13
+ * Close the child's input.
14
+ *
15
+ * Without this a child that reads stdin to EOF — every filter, and every
16
+ * tool a library pipes into, `xsel` and `base64` alike — waits forever for
17
+ * an end that never comes, and the parent waits on its exit.
18
+ */
19
+ endStdin?(): void;
12
20
  kill(signal?: string): void;
13
21
  }
14
22
  interface ChildSpawnConfig {
@@ -19,7 +27,30 @@ interface ChildSpawnConfig {
19
27
  parentPid?: number;
20
28
  }
21
29
  type SpawnChild = (config: ChildSpawnConfig) => ChildHandle;
22
- declare function createChildProcessModule(spawnChild: SpawnChild, defaultCwd: () => string): Record<string, unknown>;
30
+ /**
31
+ * Run a child to completion without returning to the event loop.
32
+ *
33
+ * Supplied only by a pod that can actually block — one whose guest runs on its
34
+ * own thread. Where it is absent the synchronous entry points keep reporting
35
+ * that they are unavailable, which is the honest answer for an in-realm pod.
36
+ */
37
+ type SyncSpawn = (request: {
38
+ command: string;
39
+ args: string[];
40
+ cwd: string;
41
+ env?: Record<string, string>;
42
+ input?: string;
43
+ }) => {
44
+ status: number | null;
45
+ stdout: string;
46
+ stderr: string;
47
+ signal: string | null;
48
+ error?: {
49
+ code?: string;
50
+ message: string;
51
+ };
52
+ };
53
+ declare function createChildProcessModule(spawnChild: SpawnChild, defaultCwd: () => string, syncSpawn?: SyncSpawn): Record<string, unknown>;
23
54
 
24
55
  /**
25
56
  * Clean-room contracts between SandboxedJS and its JavaScript runtime.
@@ -1631,6 +1662,24 @@ interface ContainerOptions {
1631
1662
  * ```
1632
1663
  */
1633
1664
  pod?: RuntimePod;
1665
+ /**
1666
+ * Where guest programs run.
1667
+ *
1668
+ * `"worker"` (the default) gives each program its own thread, which is what
1669
+ * makes synchronous child processes work and keeps guest code out of the
1670
+ * host's realm; it falls back automatically where the host cannot support it.
1671
+ * `"realm"` forces the in-realm runtime.
1672
+ */
1673
+ isolation?: "worker" | "realm";
1674
+ /**
1675
+ * Where to load the guest worker bundle from.
1676
+ *
1677
+ * Defaults to the copy shipped beside the main bundle, which is what a
1678
+ * published package wants. Worth setting when a bundler has moved or
1679
+ * rewritten it — or when running from source, where the built file is the
1680
+ * only one a Worker can load.
1681
+ */
1682
+ workerUrl?: string | URL;
1634
1683
  /** Python runtime settings; a browser host uses this to locate the wasm. */
1635
1684
  python?: PythonOptions;
1636
1685
  }
@@ -2426,6 +2475,8 @@ declare class VirtualHttpRouter {
2426
2475
  onListen: ((port: number) => void) | undefined;
2427
2476
  register(port: number, server: VirtualHttpServer, owner: string): void;
2428
2477
  unregister(port: number, server: VirtualHttpServer): void;
2478
+ /** Whether anything in this container is listening on `port`. */
2479
+ activePortsIncludes(port: number): boolean;
2429
2480
  activePorts(owner?: string): number[];
2430
2481
  closeOwner(owner: string): void;
2431
2482
  closeAll(): void;
@@ -2443,9 +2494,18 @@ interface CoreModulesOptions {
2443
2494
  http?: {
2444
2495
  router: VirtualHttpRouter;
2445
2496
  owner: string;
2497
+ fetch?: typeof globalThis.fetch;
2446
2498
  };
2447
2499
  /** Backs `child_process`; without it the module reports as unavailable. */
2448
2500
  spawnChild?: SpawnChild;
2501
+ /**
2502
+ * Backs the `*Sync` half of `child_process`.
2503
+ *
2504
+ * Only a pod whose guest runs on its own thread can supply this — blocking
2505
+ * requires somewhere else for the child's work to happen. Without it the
2506
+ * synchronous entry points keep reporting that they are unavailable.
2507
+ */
2508
+ syncSpawn?: SyncSpawn;
2449
2509
  /** File holding the process's standard input, exposed as descriptor 0. */
2450
2510
  stdinPath?: string;
2451
2511
  /** Keep `process.stdin` open and fed by {@link writeStdin} rather than ending it. */
@@ -2485,6 +2545,21 @@ declare function createCoreModules(options: CoreModulesOptions): {
2485
2545
  pendingHandles(): number;
2486
2546
  /** Active timers which called `unref()` and therefore only merit startup grace. */
2487
2547
  pendingUnrefed(): number;
2548
+ /**
2549
+ * Cancel every timer this process still holds.
2550
+ *
2551
+ * Called when a process is killed: the process is finished, but its
2552
+ * scheduled work would otherwise keep running on the host's event loop.
2553
+ */
2554
+ cancelTimers(): void;
2555
+ /**
2556
+ * Client requests sent but not yet read to completion.
2557
+ *
2558
+ * An outbound request is event-loop work in exactly the way a timer is, and
2559
+ * it schedules no timer of its own. Counting it is what stops a program from
2560
+ * exiting in the gap between `http.get` and its response callback.
2561
+ */
2562
+ pendingRequests(): number;
2488
2563
  };
2489
2564
 
2490
2565
  interface EsmTransformResult {
@@ -2514,6 +2589,99 @@ declare function looksLikeEsm(source: string): boolean;
2514
2589
  */
2515
2590
  declare function transformEsm(source: string, filename?: string): EsmTransformResult | null;
2516
2591
 
2592
+ /**
2593
+ * A volume that keeps a second, foreign filesystem in step with itself.
2594
+ *
2595
+ * Rolldown's browser WebAssembly binding owns a `memfs` volume of its own —
2596
+ * its Rust resolver reads through WASI, not through anything JavaScript can
2597
+ * hand it. So the sandbox project has to exist in two places at once, and the
2598
+ * copy has to stay current: a dev server reads `index.html` when the request
2599
+ * arrives, not when the process started.
2600
+ *
2601
+ * Mirroring on write rather than copying up-front is what makes that true. The
2602
+ * previous approach took one deep snapshot per spawn, which was both expensive
2603
+ * — every `npm run dev` re-copied `node_modules` — and already stale by the
2604
+ * time it mattered, so an edit during a session was served from the old tree.
2605
+ *
2606
+ * The mirror is deliberately one-way and best-effort. It is a cache for a
2607
+ * consumer that only reads; a write that fails to reach it must never fail the
2608
+ * write that the container itself made.
2609
+ */
2610
+
2611
+ /** The slice of a `memfs`-style filesystem the mirror writes through. */
2612
+ interface MirrorFs {
2613
+ mkdirSync(path: string, options?: {
2614
+ recursive?: boolean;
2615
+ }): void;
2616
+ writeFileSync(path: string, data: Uint8Array): void;
2617
+ symlinkSync(target: string, path: string): void;
2618
+ rmSync?(path: string, options?: {
2619
+ recursive?: boolean;
2620
+ force?: boolean;
2621
+ }): void;
2622
+ unlinkSync?(path: string): void;
2623
+ rmdirSync?(path: string): void;
2624
+ }
2625
+ /**
2626
+ * A `RuntimeVolume` that forwards every mutation to an optional mirror.
2627
+ *
2628
+ * Wrapping rather than modifying `MemoryVolume` keeps the mirroring concern out
2629
+ * of the filesystem, and keeps this transparent to the kernel, whose `Vfs`
2630
+ * takes any `RuntimeVolume`.
2631
+ */
2632
+ declare class MirroringVolume implements RuntimeVolume {
2633
+ private readonly inner;
2634
+ private mirror;
2635
+ private root;
2636
+ constructor(inner?: MemoryVolume);
2637
+ /**
2638
+ * Start mirroring the tree under `root`, seeding it with what is there now.
2639
+ *
2640
+ * Called once the Rolldown binding is known to be in play; before that a
2641
+ * container pays nothing for this.
2642
+ */
2643
+ attach(mirror: MirrorFs, root: string): void;
2644
+ detach(): void;
2645
+ /** Copy the whole subtree across. The only bulk operation that remains. */
2646
+ private seed;
2647
+ /** Is this path inside the mirrored subtree? */
2648
+ private mirrored;
2649
+ /**
2650
+ * Run a mirror update, swallowing failure.
2651
+ *
2652
+ * The mirror is a read-only cache for another engine. If it rejects
2653
+ * something — an unsupported operation, a path it has not seen — the
2654
+ * container's own write has still happened and must still succeed.
2655
+ */
2656
+ private safely;
2657
+ /** Push a path's current state across, whatever it is now. */
2658
+ private sync;
2659
+ readFileSync(path: string): Uint8Array;
2660
+ readdirSync(path: string): string[];
2661
+ lstatSync(path: string): VolumeStat;
2662
+ readlinkSync(path: string): string;
2663
+ getStats(): VolumeStats;
2664
+ writeFileSync(path: string, data: string | Uint8Array): void;
2665
+ appendFileSync(path: string, data: string | Uint8Array): void;
2666
+ mkdirSync(path: string, options?: {
2667
+ mode?: number;
2668
+ }): void;
2669
+ rmdirSync(path: string): void;
2670
+ unlinkSync(path: string): void;
2671
+ renameSync(from: string, to: string): void;
2672
+ symlinkSync(target: string, path: string): void;
2673
+ linkSync(existing: string, path: string): void;
2674
+ truncateSync(path: string, length?: number): void;
2675
+ chmodSync(path: string, mode: number): void;
2676
+ lchmodSync(path: string, mode: number): void;
2677
+ chownSync(path: string, uid: number, gid: number): void;
2678
+ lchownSync(path: string, uid: number, gid: number): void;
2679
+ utimesSync(path: string, atime: Date, mtime: Date): void;
2680
+ snapshot(): MemoryVolumeSnapshotEntry[];
2681
+ /** A restore replaces everything, so the mirror is rebuilt rather than patched. */
2682
+ restore(entries: MemoryVolumeSnapshotEntry[]): void;
2683
+ }
2684
+
2517
2685
  interface LocalRuntimeOptions {
2518
2686
  workdir?: string;
2519
2687
  env?: Record<string, string>;
@@ -2550,10 +2718,10 @@ declare const WASM_ALIASES: Record<string, string>;
2550
2718
  * dedicated Worker before this becomes the default untrusted-code path.
2551
2719
  */
2552
2720
  declare class LocalRuntimePod implements RuntimePod {
2553
- readonly volume: MemoryVolume;
2721
+ readonly volume: MirroringVolume;
2554
2722
  readonly packages: RuntimePackageInstaller;
2555
2723
  readonly instanceId: string;
2556
- private readonly router;
2724
+ protected readonly router: VirtualHttpRouter;
2557
2725
  readonly proxy: {
2558
2726
  activePorts: (_instanceId?: string) => number[];
2559
2727
  };
@@ -2566,13 +2734,15 @@ declare class LocalRuntimePod implements RuntimePod {
2566
2734
  spawn(config: ChildSpawnConfig): ChildHandle;
2567
2735
  };
2568
2736
  private disposed;
2569
- private readonly workdir;
2570
- private readonly env;
2571
- private readonly aliases;
2737
+ protected readonly workdir: string;
2738
+ protected readonly env: Record<string, string>;
2739
+ protected readonly aliases: Record<string, string>;
2572
2740
  private readonly modules;
2573
2741
  private readonly esbuild;
2574
2742
  private rolldownBinding;
2575
- private constructor();
2743
+ /** Backs outbound `http`/`https` client requests from inside the sandbox. */
2744
+ private readonly fetch;
2745
+ protected constructor(options: LocalRuntimeOptions);
2576
2746
  static boot(options?: LocalRuntimeOptions): Promise<LocalRuntimePod>;
2577
2747
  spawn(command: string, args?: string[], options?: Record<string, unknown>): Promise<RuntimeProcess>;
2578
2748
  /**
@@ -2657,6 +2827,47 @@ declare class CleanPackageInstaller implements RuntimePackageInstaller {
2657
2827
  }
2658
2828
  declare function extractNpmTarball(volume: RuntimeVolume, compressed: Uint8Array, destination: string): void;
2659
2829
 
2830
+ /**
2831
+ * Starting the guest Worker, in whichever environment the host happens to be.
2832
+ *
2833
+ * The awkward part of shipping a Worker from a library is not creating it, but
2834
+ * naming it. `new URL("./worker-entry.js", import.meta.url)` is the form every
2835
+ * bundler recognises, and it resolves correctly from `dist/` — but a bundler
2836
+ * that *pre-bundles* this package rewrites `import.meta.url` to point into its
2837
+ * own dependency cache, where no such file exists. That is the same trap
2838
+ * Rolldown's WASI binding falls into, and it surfaces just as obliquely.
2839
+ *
2840
+ * So this never assumes it worked. The caller treats a failure to start as
2841
+ * "this host cannot run the Worker pod" and uses the in-realm pod instead.
2842
+ */
2843
+ interface RuntimeWorker {
2844
+ postMessage(message: unknown): void;
2845
+ onMessage(listener: (message: unknown) => void): void;
2846
+ onError(listener: (error: unknown) => void): void;
2847
+ terminate(): unknown;
2848
+ }
2849
+ /**
2850
+ * Start the guest Worker and wait for it to say it is alive.
2851
+ *
2852
+ * Rejects rather than hanging when the script cannot be loaded: a Worker whose
2853
+ * script 404s reports an `error` event and would otherwise leave the caller
2854
+ * waiting for a ready message that can never arrive.
2855
+ */
2856
+ declare function startRuntimeWorker(options?: {
2857
+ url?: string | URL;
2858
+ workerData?: Record<string, unknown>;
2859
+ timeoutMs?: number;
2860
+ }): Promise<RuntimeWorker>;
2861
+
2862
+ /**
2863
+ * Can this environment support a blocking client at all?
2864
+ *
2865
+ * `SharedArrayBuffer` needs cross-origin isolation in a browser, and
2866
+ * `Atomics.wait` is forbidden on a browser's main thread — which is why the
2867
+ * client is always the Worker.
2868
+ */
2869
+ declare function syncChannelSupported(): boolean;
2870
+
2660
2871
  /**
2661
2872
  * sandboxedjs — a Linux-like container that runs entirely inside Node.js.
2662
2873
  *
@@ -2675,4 +2886,4 @@ declare function extractNpmTarball(volume: RuntimeVolume, compressed: Uint8Array
2675
2886
  * ```
2676
2887
  */
2677
2888
 
2678
- export { ArithError, BufferSink, type CPythonOptions, CallbackSink, type ChildHandle, type ChildSpawnConfig, type CleanInstallerOptions, CleanPackageInstaller, type Command, CommandRegistry, CommonJsEngine, type CommonJsEngineOptions, type CommonJsModule, Container, ContainerFs, type ContainerOptions, type ContextInit, type CoreModulesOptions, type Cred, type DirEntry, ERRNO, type Env, type ErrnoCode, type EsmTransformResult, type ExecContext, type ExecOptions, type ExecResult, type FileData, FileInput, FileOutput, type GroupEntry, type HttpResponse, IncompleteInputError, type InputStream, type InstallOptions, type Job, KERNEL_NAME, KERNEL_RELEASE, Kernel, type KernelOptions, type ListeningPort, type LocalRuntimeOptions, LocalRuntimePod, MemoryVolume, type MountEntry, NODE_VERSION, NPM_VERSION, type NetInterface, type NetworkOptions, NetworkStack, NullInput, NullOutput, OS_RELEASE, type OutputStream, PYTHON_VERSION, type PasswdEntry, Pipe, Process, type ProcessKind, type ProcessOptions, type ProcessState, ProcessTable, type PythonOptions, ROOT_CRED, type ResolvedExecutable, type RootfsOptions, type RunOptions, type RunResult, type RuntimePackageInstaller, type RuntimePod, type RuntimeProcess, type RuntimeProcessManager, type RuntimeProcessResult, type RuntimeVolume, SIGNALS, SIGNAL_NAMES, Session, type SessionInit, type SessionResult, type SessionRunOptions, Shell, ShellExit, type ShellIO, type ShellInit, Lexer as ShellLexer, type ShellOptions, ShellSyntaxError, type SpawnChild, type SpawnHandle, Stats, type Stdio, SysError, TeeOutput, Terminal, type TerminalOptions, UserDatabase, Variables, Vfs, VirtualHttpRouter, VirtualHttpServer, VirtualIncomingMessage, type VirtualNode, type VirtualProvider, VirtualServerResponse, WASM_ALIASES, type WriteOptions, allCommands, applyChmod, braceExpand, buildRootfs, builtinNames, captureStdio, configureCPython, configurePython, createChildProcessModule, createContainer, createContext, createCoreModules, createContainer as default, defineCommand, evalArith, exitCodeForSignal, expandPrompt, expandWord, expandWords, extractNpmTarball, fnmatch, formatMode, getBuiltin, glob, globToRegex, hasMagic, installUserland, isBuiltinName, isCPythonAvailable, isPythonAvailable, isSysError, looksLikeEsm, makeCred, normalizeSignal, octalMode, parse as parseShell, parseUmask, path as posixPath, resetPidCounter, shellQuote, strerror, transformEsm, unameInfo };
2889
+ export { ArithError, BufferSink, type CPythonOptions, CallbackSink, type ChildHandle, type ChildSpawnConfig, type CleanInstallerOptions, CleanPackageInstaller, type Command, CommandRegistry, CommonJsEngine, type CommonJsEngineOptions, type CommonJsModule, Container, ContainerFs, type ContainerOptions, type ContextInit, type CoreModulesOptions, type Cred, type DirEntry, ERRNO, type Env, type ErrnoCode, type EsmTransformResult, type ExecContext, type ExecOptions, type ExecResult, type FileData, FileInput, FileOutput, type GroupEntry, type HttpResponse, IncompleteInputError, type InputStream, type InstallOptions, type Job, KERNEL_NAME, KERNEL_RELEASE, Kernel, type KernelOptions, type ListeningPort, type LocalRuntimeOptions, LocalRuntimePod, MemoryVolume, type MountEntry, NODE_VERSION, NPM_VERSION, type NetInterface, type NetworkOptions, NetworkStack, NullInput, NullOutput, OS_RELEASE, type OutputStream, PYTHON_VERSION, type PasswdEntry, Pipe, Process, type ProcessKind, type ProcessOptions, type ProcessState, ProcessTable, type PythonOptions, ROOT_CRED, type ResolvedExecutable, type RootfsOptions, type RunOptions, type RunResult, type RuntimePackageInstaller, type RuntimePod, type RuntimeProcess, type RuntimeProcessManager, type RuntimeProcessResult, type RuntimeVolume, SIGNALS, SIGNAL_NAMES, Session, type SessionInit, type SessionResult, type SessionRunOptions, Shell, ShellExit, type ShellIO, type ShellInit, Lexer as ShellLexer, type ShellOptions, ShellSyntaxError, type SpawnChild, type SpawnHandle, Stats, type Stdio, SysError, TeeOutput, Terminal, type TerminalOptions, UserDatabase, Variables, Vfs, VirtualHttpRouter, VirtualHttpServer, VirtualIncomingMessage, type VirtualNode, type VirtualProvider, VirtualServerResponse, WASM_ALIASES, type WriteOptions, allCommands, applyChmod, braceExpand, buildRootfs, builtinNames, captureStdio, configureCPython, configurePython, createChildProcessModule, createContainer, createContext, createCoreModules, createContainer as default, defineCommand, evalArith, exitCodeForSignal, expandPrompt, expandWord, expandWords, extractNpmTarball, fnmatch, formatMode, getBuiltin, glob, globToRegex, hasMagic, installUserland, isBuiltinName, isCPythonAvailable, isPythonAvailable, isSysError, looksLikeEsm, makeCred, normalizeSignal, octalMode, parse as parseShell, parseUmask, path as posixPath, resetPidCounter, shellQuote, startRuntimeWorker, strerror, syncChannelSupported, transformEsm, unameInfo };
package/dist/index.d.ts CHANGED
@@ -9,6 +9,14 @@ interface ChildHandle {
9
9
  on(event: "stdout" | "stderr" | "exit", listener: (...args: any[]) => void): unknown;
10
10
  exec(): void;
11
11
  sendStdin(data: string): void;
12
+ /**
13
+ * Close the child's input.
14
+ *
15
+ * Without this a child that reads stdin to EOF — every filter, and every
16
+ * tool a library pipes into, `xsel` and `base64` alike — waits forever for
17
+ * an end that never comes, and the parent waits on its exit.
18
+ */
19
+ endStdin?(): void;
12
20
  kill(signal?: string): void;
13
21
  }
14
22
  interface ChildSpawnConfig {
@@ -19,7 +27,30 @@ interface ChildSpawnConfig {
19
27
  parentPid?: number;
20
28
  }
21
29
  type SpawnChild = (config: ChildSpawnConfig) => ChildHandle;
22
- declare function createChildProcessModule(spawnChild: SpawnChild, defaultCwd: () => string): Record<string, unknown>;
30
+ /**
31
+ * Run a child to completion without returning to the event loop.
32
+ *
33
+ * Supplied only by a pod that can actually block — one whose guest runs on its
34
+ * own thread. Where it is absent the synchronous entry points keep reporting
35
+ * that they are unavailable, which is the honest answer for an in-realm pod.
36
+ */
37
+ type SyncSpawn = (request: {
38
+ command: string;
39
+ args: string[];
40
+ cwd: string;
41
+ env?: Record<string, string>;
42
+ input?: string;
43
+ }) => {
44
+ status: number | null;
45
+ stdout: string;
46
+ stderr: string;
47
+ signal: string | null;
48
+ error?: {
49
+ code?: string;
50
+ message: string;
51
+ };
52
+ };
53
+ declare function createChildProcessModule(spawnChild: SpawnChild, defaultCwd: () => string, syncSpawn?: SyncSpawn): Record<string, unknown>;
23
54
 
24
55
  /**
25
56
  * Clean-room contracts between SandboxedJS and its JavaScript runtime.
@@ -1631,6 +1662,24 @@ interface ContainerOptions {
1631
1662
  * ```
1632
1663
  */
1633
1664
  pod?: RuntimePod;
1665
+ /**
1666
+ * Where guest programs run.
1667
+ *
1668
+ * `"worker"` (the default) gives each program its own thread, which is what
1669
+ * makes synchronous child processes work and keeps guest code out of the
1670
+ * host's realm; it falls back automatically where the host cannot support it.
1671
+ * `"realm"` forces the in-realm runtime.
1672
+ */
1673
+ isolation?: "worker" | "realm";
1674
+ /**
1675
+ * Where to load the guest worker bundle from.
1676
+ *
1677
+ * Defaults to the copy shipped beside the main bundle, which is what a
1678
+ * published package wants. Worth setting when a bundler has moved or
1679
+ * rewritten it — or when running from source, where the built file is the
1680
+ * only one a Worker can load.
1681
+ */
1682
+ workerUrl?: string | URL;
1634
1683
  /** Python runtime settings; a browser host uses this to locate the wasm. */
1635
1684
  python?: PythonOptions;
1636
1685
  }
@@ -2426,6 +2475,8 @@ declare class VirtualHttpRouter {
2426
2475
  onListen: ((port: number) => void) | undefined;
2427
2476
  register(port: number, server: VirtualHttpServer, owner: string): void;
2428
2477
  unregister(port: number, server: VirtualHttpServer): void;
2478
+ /** Whether anything in this container is listening on `port`. */
2479
+ activePortsIncludes(port: number): boolean;
2429
2480
  activePorts(owner?: string): number[];
2430
2481
  closeOwner(owner: string): void;
2431
2482
  closeAll(): void;
@@ -2443,9 +2494,18 @@ interface CoreModulesOptions {
2443
2494
  http?: {
2444
2495
  router: VirtualHttpRouter;
2445
2496
  owner: string;
2497
+ fetch?: typeof globalThis.fetch;
2446
2498
  };
2447
2499
  /** Backs `child_process`; without it the module reports as unavailable. */
2448
2500
  spawnChild?: SpawnChild;
2501
+ /**
2502
+ * Backs the `*Sync` half of `child_process`.
2503
+ *
2504
+ * Only a pod whose guest runs on its own thread can supply this — blocking
2505
+ * requires somewhere else for the child's work to happen. Without it the
2506
+ * synchronous entry points keep reporting that they are unavailable.
2507
+ */
2508
+ syncSpawn?: SyncSpawn;
2449
2509
  /** File holding the process's standard input, exposed as descriptor 0. */
2450
2510
  stdinPath?: string;
2451
2511
  /** Keep `process.stdin` open and fed by {@link writeStdin} rather than ending it. */
@@ -2485,6 +2545,21 @@ declare function createCoreModules(options: CoreModulesOptions): {
2485
2545
  pendingHandles(): number;
2486
2546
  /** Active timers which called `unref()` and therefore only merit startup grace. */
2487
2547
  pendingUnrefed(): number;
2548
+ /**
2549
+ * Cancel every timer this process still holds.
2550
+ *
2551
+ * Called when a process is killed: the process is finished, but its
2552
+ * scheduled work would otherwise keep running on the host's event loop.
2553
+ */
2554
+ cancelTimers(): void;
2555
+ /**
2556
+ * Client requests sent but not yet read to completion.
2557
+ *
2558
+ * An outbound request is event-loop work in exactly the way a timer is, and
2559
+ * it schedules no timer of its own. Counting it is what stops a program from
2560
+ * exiting in the gap between `http.get` and its response callback.
2561
+ */
2562
+ pendingRequests(): number;
2488
2563
  };
2489
2564
 
2490
2565
  interface EsmTransformResult {
@@ -2514,6 +2589,99 @@ declare function looksLikeEsm(source: string): boolean;
2514
2589
  */
2515
2590
  declare function transformEsm(source: string, filename?: string): EsmTransformResult | null;
2516
2591
 
2592
+ /**
2593
+ * A volume that keeps a second, foreign filesystem in step with itself.
2594
+ *
2595
+ * Rolldown's browser WebAssembly binding owns a `memfs` volume of its own —
2596
+ * its Rust resolver reads through WASI, not through anything JavaScript can
2597
+ * hand it. So the sandbox project has to exist in two places at once, and the
2598
+ * copy has to stay current: a dev server reads `index.html` when the request
2599
+ * arrives, not when the process started.
2600
+ *
2601
+ * Mirroring on write rather than copying up-front is what makes that true. The
2602
+ * previous approach took one deep snapshot per spawn, which was both expensive
2603
+ * — every `npm run dev` re-copied `node_modules` — and already stale by the
2604
+ * time it mattered, so an edit during a session was served from the old tree.
2605
+ *
2606
+ * The mirror is deliberately one-way and best-effort. It is a cache for a
2607
+ * consumer that only reads; a write that fails to reach it must never fail the
2608
+ * write that the container itself made.
2609
+ */
2610
+
2611
+ /** The slice of a `memfs`-style filesystem the mirror writes through. */
2612
+ interface MirrorFs {
2613
+ mkdirSync(path: string, options?: {
2614
+ recursive?: boolean;
2615
+ }): void;
2616
+ writeFileSync(path: string, data: Uint8Array): void;
2617
+ symlinkSync(target: string, path: string): void;
2618
+ rmSync?(path: string, options?: {
2619
+ recursive?: boolean;
2620
+ force?: boolean;
2621
+ }): void;
2622
+ unlinkSync?(path: string): void;
2623
+ rmdirSync?(path: string): void;
2624
+ }
2625
+ /**
2626
+ * A `RuntimeVolume` that forwards every mutation to an optional mirror.
2627
+ *
2628
+ * Wrapping rather than modifying `MemoryVolume` keeps the mirroring concern out
2629
+ * of the filesystem, and keeps this transparent to the kernel, whose `Vfs`
2630
+ * takes any `RuntimeVolume`.
2631
+ */
2632
+ declare class MirroringVolume implements RuntimeVolume {
2633
+ private readonly inner;
2634
+ private mirror;
2635
+ private root;
2636
+ constructor(inner?: MemoryVolume);
2637
+ /**
2638
+ * Start mirroring the tree under `root`, seeding it with what is there now.
2639
+ *
2640
+ * Called once the Rolldown binding is known to be in play; before that a
2641
+ * container pays nothing for this.
2642
+ */
2643
+ attach(mirror: MirrorFs, root: string): void;
2644
+ detach(): void;
2645
+ /** Copy the whole subtree across. The only bulk operation that remains. */
2646
+ private seed;
2647
+ /** Is this path inside the mirrored subtree? */
2648
+ private mirrored;
2649
+ /**
2650
+ * Run a mirror update, swallowing failure.
2651
+ *
2652
+ * The mirror is a read-only cache for another engine. If it rejects
2653
+ * something — an unsupported operation, a path it has not seen — the
2654
+ * container's own write has still happened and must still succeed.
2655
+ */
2656
+ private safely;
2657
+ /** Push a path's current state across, whatever it is now. */
2658
+ private sync;
2659
+ readFileSync(path: string): Uint8Array;
2660
+ readdirSync(path: string): string[];
2661
+ lstatSync(path: string): VolumeStat;
2662
+ readlinkSync(path: string): string;
2663
+ getStats(): VolumeStats;
2664
+ writeFileSync(path: string, data: string | Uint8Array): void;
2665
+ appendFileSync(path: string, data: string | Uint8Array): void;
2666
+ mkdirSync(path: string, options?: {
2667
+ mode?: number;
2668
+ }): void;
2669
+ rmdirSync(path: string): void;
2670
+ unlinkSync(path: string): void;
2671
+ renameSync(from: string, to: string): void;
2672
+ symlinkSync(target: string, path: string): void;
2673
+ linkSync(existing: string, path: string): void;
2674
+ truncateSync(path: string, length?: number): void;
2675
+ chmodSync(path: string, mode: number): void;
2676
+ lchmodSync(path: string, mode: number): void;
2677
+ chownSync(path: string, uid: number, gid: number): void;
2678
+ lchownSync(path: string, uid: number, gid: number): void;
2679
+ utimesSync(path: string, atime: Date, mtime: Date): void;
2680
+ snapshot(): MemoryVolumeSnapshotEntry[];
2681
+ /** A restore replaces everything, so the mirror is rebuilt rather than patched. */
2682
+ restore(entries: MemoryVolumeSnapshotEntry[]): void;
2683
+ }
2684
+
2517
2685
  interface LocalRuntimeOptions {
2518
2686
  workdir?: string;
2519
2687
  env?: Record<string, string>;
@@ -2550,10 +2718,10 @@ declare const WASM_ALIASES: Record<string, string>;
2550
2718
  * dedicated Worker before this becomes the default untrusted-code path.
2551
2719
  */
2552
2720
  declare class LocalRuntimePod implements RuntimePod {
2553
- readonly volume: MemoryVolume;
2721
+ readonly volume: MirroringVolume;
2554
2722
  readonly packages: RuntimePackageInstaller;
2555
2723
  readonly instanceId: string;
2556
- private readonly router;
2724
+ protected readonly router: VirtualHttpRouter;
2557
2725
  readonly proxy: {
2558
2726
  activePorts: (_instanceId?: string) => number[];
2559
2727
  };
@@ -2566,13 +2734,15 @@ declare class LocalRuntimePod implements RuntimePod {
2566
2734
  spawn(config: ChildSpawnConfig): ChildHandle;
2567
2735
  };
2568
2736
  private disposed;
2569
- private readonly workdir;
2570
- private readonly env;
2571
- private readonly aliases;
2737
+ protected readonly workdir: string;
2738
+ protected readonly env: Record<string, string>;
2739
+ protected readonly aliases: Record<string, string>;
2572
2740
  private readonly modules;
2573
2741
  private readonly esbuild;
2574
2742
  private rolldownBinding;
2575
- private constructor();
2743
+ /** Backs outbound `http`/`https` client requests from inside the sandbox. */
2744
+ private readonly fetch;
2745
+ protected constructor(options: LocalRuntimeOptions);
2576
2746
  static boot(options?: LocalRuntimeOptions): Promise<LocalRuntimePod>;
2577
2747
  spawn(command: string, args?: string[], options?: Record<string, unknown>): Promise<RuntimeProcess>;
2578
2748
  /**
@@ -2657,6 +2827,47 @@ declare class CleanPackageInstaller implements RuntimePackageInstaller {
2657
2827
  }
2658
2828
  declare function extractNpmTarball(volume: RuntimeVolume, compressed: Uint8Array, destination: string): void;
2659
2829
 
2830
+ /**
2831
+ * Starting the guest Worker, in whichever environment the host happens to be.
2832
+ *
2833
+ * The awkward part of shipping a Worker from a library is not creating it, but
2834
+ * naming it. `new URL("./worker-entry.js", import.meta.url)` is the form every
2835
+ * bundler recognises, and it resolves correctly from `dist/` — but a bundler
2836
+ * that *pre-bundles* this package rewrites `import.meta.url` to point into its
2837
+ * own dependency cache, where no such file exists. That is the same trap
2838
+ * Rolldown's WASI binding falls into, and it surfaces just as obliquely.
2839
+ *
2840
+ * So this never assumes it worked. The caller treats a failure to start as
2841
+ * "this host cannot run the Worker pod" and uses the in-realm pod instead.
2842
+ */
2843
+ interface RuntimeWorker {
2844
+ postMessage(message: unknown): void;
2845
+ onMessage(listener: (message: unknown) => void): void;
2846
+ onError(listener: (error: unknown) => void): void;
2847
+ terminate(): unknown;
2848
+ }
2849
+ /**
2850
+ * Start the guest Worker and wait for it to say it is alive.
2851
+ *
2852
+ * Rejects rather than hanging when the script cannot be loaded: a Worker whose
2853
+ * script 404s reports an `error` event and would otherwise leave the caller
2854
+ * waiting for a ready message that can never arrive.
2855
+ */
2856
+ declare function startRuntimeWorker(options?: {
2857
+ url?: string | URL;
2858
+ workerData?: Record<string, unknown>;
2859
+ timeoutMs?: number;
2860
+ }): Promise<RuntimeWorker>;
2861
+
2862
+ /**
2863
+ * Can this environment support a blocking client at all?
2864
+ *
2865
+ * `SharedArrayBuffer` needs cross-origin isolation in a browser, and
2866
+ * `Atomics.wait` is forbidden on a browser's main thread — which is why the
2867
+ * client is always the Worker.
2868
+ */
2869
+ declare function syncChannelSupported(): boolean;
2870
+
2660
2871
  /**
2661
2872
  * sandboxedjs — a Linux-like container that runs entirely inside Node.js.
2662
2873
  *
@@ -2675,4 +2886,4 @@ declare function extractNpmTarball(volume: RuntimeVolume, compressed: Uint8Array
2675
2886
  * ```
2676
2887
  */
2677
2888
 
2678
- export { ArithError, BufferSink, type CPythonOptions, CallbackSink, type ChildHandle, type ChildSpawnConfig, type CleanInstallerOptions, CleanPackageInstaller, type Command, CommandRegistry, CommonJsEngine, type CommonJsEngineOptions, type CommonJsModule, Container, ContainerFs, type ContainerOptions, type ContextInit, type CoreModulesOptions, type Cred, type DirEntry, ERRNO, type Env, type ErrnoCode, type EsmTransformResult, type ExecContext, type ExecOptions, type ExecResult, type FileData, FileInput, FileOutput, type GroupEntry, type HttpResponse, IncompleteInputError, type InputStream, type InstallOptions, type Job, KERNEL_NAME, KERNEL_RELEASE, Kernel, type KernelOptions, type ListeningPort, type LocalRuntimeOptions, LocalRuntimePod, MemoryVolume, type MountEntry, NODE_VERSION, NPM_VERSION, type NetInterface, type NetworkOptions, NetworkStack, NullInput, NullOutput, OS_RELEASE, type OutputStream, PYTHON_VERSION, type PasswdEntry, Pipe, Process, type ProcessKind, type ProcessOptions, type ProcessState, ProcessTable, type PythonOptions, ROOT_CRED, type ResolvedExecutable, type RootfsOptions, type RunOptions, type RunResult, type RuntimePackageInstaller, type RuntimePod, type RuntimeProcess, type RuntimeProcessManager, type RuntimeProcessResult, type RuntimeVolume, SIGNALS, SIGNAL_NAMES, Session, type SessionInit, type SessionResult, type SessionRunOptions, Shell, ShellExit, type ShellIO, type ShellInit, Lexer as ShellLexer, type ShellOptions, ShellSyntaxError, type SpawnChild, type SpawnHandle, Stats, type Stdio, SysError, TeeOutput, Terminal, type TerminalOptions, UserDatabase, Variables, Vfs, VirtualHttpRouter, VirtualHttpServer, VirtualIncomingMessage, type VirtualNode, type VirtualProvider, VirtualServerResponse, WASM_ALIASES, type WriteOptions, allCommands, applyChmod, braceExpand, buildRootfs, builtinNames, captureStdio, configureCPython, configurePython, createChildProcessModule, createContainer, createContext, createCoreModules, createContainer as default, defineCommand, evalArith, exitCodeForSignal, expandPrompt, expandWord, expandWords, extractNpmTarball, fnmatch, formatMode, getBuiltin, glob, globToRegex, hasMagic, installUserland, isBuiltinName, isCPythonAvailable, isPythonAvailable, isSysError, looksLikeEsm, makeCred, normalizeSignal, octalMode, parse as parseShell, parseUmask, path as posixPath, resetPidCounter, shellQuote, strerror, transformEsm, unameInfo };
2889
+ export { ArithError, BufferSink, type CPythonOptions, CallbackSink, type ChildHandle, type ChildSpawnConfig, type CleanInstallerOptions, CleanPackageInstaller, type Command, CommandRegistry, CommonJsEngine, type CommonJsEngineOptions, type CommonJsModule, Container, ContainerFs, type ContainerOptions, type ContextInit, type CoreModulesOptions, type Cred, type DirEntry, ERRNO, type Env, type ErrnoCode, type EsmTransformResult, type ExecContext, type ExecOptions, type ExecResult, type FileData, FileInput, FileOutput, type GroupEntry, type HttpResponse, IncompleteInputError, type InputStream, type InstallOptions, type Job, KERNEL_NAME, KERNEL_RELEASE, Kernel, type KernelOptions, type ListeningPort, type LocalRuntimeOptions, LocalRuntimePod, MemoryVolume, type MountEntry, NODE_VERSION, NPM_VERSION, type NetInterface, type NetworkOptions, NetworkStack, NullInput, NullOutput, OS_RELEASE, type OutputStream, PYTHON_VERSION, type PasswdEntry, Pipe, Process, type ProcessKind, type ProcessOptions, type ProcessState, ProcessTable, type PythonOptions, ROOT_CRED, type ResolvedExecutable, type RootfsOptions, type RunOptions, type RunResult, type RuntimePackageInstaller, type RuntimePod, type RuntimeProcess, type RuntimeProcessManager, type RuntimeProcessResult, type RuntimeVolume, SIGNALS, SIGNAL_NAMES, Session, type SessionInit, type SessionResult, type SessionRunOptions, Shell, ShellExit, type ShellIO, type ShellInit, Lexer as ShellLexer, type ShellOptions, ShellSyntaxError, type SpawnChild, type SpawnHandle, Stats, type Stdio, SysError, TeeOutput, Terminal, type TerminalOptions, UserDatabase, Variables, Vfs, VirtualHttpRouter, VirtualHttpServer, VirtualIncomingMessage, type VirtualNode, type VirtualProvider, VirtualServerResponse, WASM_ALIASES, type WriteOptions, allCommands, applyChmod, braceExpand, buildRootfs, builtinNames, captureStdio, configureCPython, configurePython, createChildProcessModule, createContainer, createContext, createCoreModules, createContainer as default, defineCommand, evalArith, exitCodeForSignal, expandPrompt, expandWord, expandWords, extractNpmTarball, fnmatch, formatMode, getBuiltin, glob, globToRegex, hasMagic, installUserland, isBuiltinName, isCPythonAvailable, isPythonAvailable, isSysError, looksLikeEsm, makeCred, normalizeSignal, octalMode, parse as parseShell, parseUmask, path as posixPath, resetPidCounter, shellQuote, startRuntimeWorker, strerror, syncChannelSupported, transformEsm, unameInfo };