sandboxedjs 0.1.29 → 0.1.31

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
@@ -6,7 +6,7 @@ interface ChildHandle {
6
6
  pid: number;
7
7
  state: "starting" | "running" | "exited";
8
8
  exitCode: number | undefined;
9
- on(event: "stdout" | "stderr" | "exit", listener: (...args: any[]) => void): unknown;
9
+ on(event: "stdout" | "stderr" | "exit" | "rawmode", listener: (...args: any[]) => void): unknown;
10
10
  exec(): void;
11
11
  sendStdin(data: string): void;
12
12
  /**
@@ -25,6 +25,14 @@ interface ChildSpawnConfig {
25
25
  cwd?: string;
26
26
  env?: Record<string, string>;
27
27
  parentPid?: number;
28
+ /**
29
+ * The child was given the parent's streams (`stdio: "inherit"`).
30
+ *
31
+ * Its input is then the parent's terminal rather than a pipe that will end,
32
+ * which is the difference between a program that waits for what the user
33
+ * types and one that reads to end-of-input and stops.
34
+ */
35
+ inheritStdio?: boolean;
28
36
  }
29
37
  type SpawnChild = (config: ChildSpawnConfig) => ChildHandle;
30
38
  /**
@@ -40,6 +48,7 @@ type SyncSpawn = (request: {
40
48
  cwd: string;
41
49
  env?: Record<string, string>;
42
50
  input?: string;
51
+ inheritStdio?: boolean;
43
52
  }) => {
44
53
  status: number | null;
45
54
  stdout: string;
@@ -50,7 +59,7 @@ type SyncSpawn = (request: {
50
59
  message: string;
51
60
  };
52
61
  };
53
- declare function createChildProcessModule(spawnChild: SpawnChild, defaultCwd: () => string, syncSpawn?: SyncSpawn): Record<string, unknown>;
62
+ declare function createChildProcessModule(spawnChild: SpawnChild, defaultCwd: () => string, syncSpawn?: SyncSpawn, defaultEnv?: () => Record<string, string>): Record<string, unknown>;
54
63
 
55
64
  /**
56
65
  * Clean-room contracts between SandboxedJS and its JavaScript runtime.
@@ -433,7 +442,10 @@ declare class Pipe implements InputStream, OutputStream {
433
442
  * whole line on every keystroke — so the terminal must stop echoing, or
434
443
  * every character appears twice.
435
444
  */
436
- rawMode: boolean;
445
+ private raw;
446
+ onRawMode?: (enabled: boolean) => void;
447
+ get rawMode(): boolean;
448
+ set rawMode(enabled: boolean);
437
449
  columns: number | undefined;
438
450
  rows: number | undefined;
439
451
  get closed(): boolean;
@@ -1382,6 +1394,8 @@ declare class Shell {
1382
1394
  constructor(init: ShellInit);
1383
1395
  /** Parse and run a script fragment. Returns the last exit status. */
1384
1396
  execute(source: string, io: ShellIO): Promise<number>;
1397
+ /** Signal the current terminal pipeline without terminating the interactive shell. */
1398
+ interruptForeground(signal: string, stdin: InputStream): void;
1385
1399
  get isExiting(): boolean;
1386
1400
  /** True when `source` is not yet a complete command (for REPL continuation). */
1387
1401
  static isIncomplete(source: string): boolean;
@@ -1665,12 +1679,15 @@ interface ContainerOptions {
1665
1679
  /**
1666
1680
  * Where guest programs run.
1667
1681
  *
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.
1682
+ * `"auto"` (the default) tries the worker runtime and reports any fallback.
1683
+ * `"worker"` requires a working guest worker and shared-memory channel: boot
1684
+ * rejects with the cause if either is unavailable, rather than letting
1685
+ * synchronous child-process calls fail later. `"realm"` opts out explicitly.
1686
+ * Processes requiring host-native modules can still run in the host realm.
1672
1687
  */
1673
- isolation?: "worker" | "realm";
1688
+ isolation?: "auto" | "worker" | "realm";
1689
+ /** Receives the reason for an automatic fallback; defaults to console.warn. */
1690
+ onRuntimeFallback?: (error: Error) => void;
1674
1691
  /**
1675
1692
  * Where to load the guest worker bundle from.
1676
1693
  *
@@ -2457,6 +2474,7 @@ declare class VirtualHttpServer extends EventEmitter {
2457
2474
  readonly owner: string;
2458
2475
  listening: boolean;
2459
2476
  private portValue;
2477
+ private referenced;
2460
2478
  constructor(router: VirtualHttpRouter, owner: string, listener?: (req: VirtualIncomingMessage, res: VirtualServerResponse) => void);
2461
2479
  listen(...args: any[]): this;
2462
2480
  close(callback?: (error?: Error) => void): this;
@@ -2467,17 +2485,20 @@ declare class VirtualHttpServer extends EventEmitter {
2467
2485
  } | null;
2468
2486
  ref(): this;
2469
2487
  unref(): this;
2488
+ hasRef(): boolean;
2470
2489
  setTimeout(_milliseconds: number, callback?: () => void): this;
2471
2490
  }
2472
2491
  declare class VirtualHttpRouter {
2473
2492
  private readonly servers;
2474
2493
  /** Notified when a server begins listening, for `onServerReady`. */
2475
2494
  onListen: ((port: number) => void) | undefined;
2495
+ onClose: ((port: number) => void) | undefined;
2476
2496
  register(port: number, server: VirtualHttpServer, owner: string): void;
2477
2497
  unregister(port: number, server: VirtualHttpServer): void;
2478
2498
  /** Whether anything in this container is listening on `port`. */
2479
2499
  activePortsIncludes(port: number): boolean;
2480
2500
  activePorts(owner?: string): number[];
2501
+ referencedPorts(owner: string): number[];
2481
2502
  closeOwner(owner: string): void;
2482
2503
  closeAll(): void;
2483
2504
  request(port: number, init?: VirtualRequestInit): Promise<RuntimeHttpResponse>;
@@ -2734,6 +2755,7 @@ declare class LocalRuntimePod implements RuntimePod {
2734
2755
  spawn(config: ChildSpawnConfig): ChildHandle;
2735
2756
  };
2736
2757
  private disposed;
2758
+ private readonly running;
2737
2759
  protected readonly workdir: string;
2738
2760
  protected readonly env: Record<string, string>;
2739
2761
  protected readonly aliases: Record<string, string>;
@@ -2777,6 +2799,62 @@ declare class LocalRuntimePod implements RuntimePod {
2777
2799
  private assertActive;
2778
2800
  }
2779
2801
 
2802
+ /**
2803
+ * A pod that evaluates each guest program on its own thread.
2804
+ *
2805
+ * Everything shared stays here: the volume, the HTTP router, the package
2806
+ * installer and the process table. Only the program's own evaluation moves,
2807
+ * and it reaches back for the rest through a {@link SyncChannelServer}.
2808
+ *
2809
+ * That arrangement is forced rather than chosen. A synchronous call has to
2810
+ * block the caller while the work it is waiting on still makes progress, so
2811
+ * the blocking side cannot be the side that owns the resources — otherwise a
2812
+ * child process needing the filesystem would have to call into a thread that
2813
+ * is frozen waiting for that child. The guest blocks; the host never does.
2814
+ *
2815
+ * It inherits from {@link LocalRuntimePod} because every other part of the
2816
+ * contract is identical, and overriding one method is a smaller and more
2817
+ * honest claim than reimplementing nine. Both are held to the same contract
2818
+ * suite (`test/pod-contract.ts`).
2819
+ */
2820
+
2821
+ interface WorkerRuntimeOptions extends LocalRuntimeOptions {
2822
+ /** Where the guest bundle lives; defaults to the copy shipped beside this one. */
2823
+ workerUrl?: string | URL;
2824
+ }
2825
+ declare class WorkerRuntimePod extends LocalRuntimePod {
2826
+ private readonly workerUrl;
2827
+ /** Live workers, so teardown can stop them all. */
2828
+ private readonly live;
2829
+ protected constructor(options: WorkerRuntimeOptions);
2830
+ /** Boot without silently dropping synchronous child-process support. */
2831
+ static boot(options?: WorkerRuntimeOptions): Promise<WorkerRuntimePod>;
2832
+ /** Compatibility mode, with an observable explanation for every fallback. */
2833
+ static tryBoot(options?: WorkerRuntimeOptions, onFallback?: (error: Error) => void): Promise<WorkerRuntimePod | null>;
2834
+ spawn(command: string, args?: string[], options?: Record<string, unknown>): Promise<RuntimeProcess>;
2835
+ private spawnInWorker;
2836
+ /** Start an asynchronous child on the host's behalf and relay its events. */
2837
+ private startChild;
2838
+ /** Run a child to completion and collect it, for the guest's `spawnSync`. */
2839
+ private runChildToCompletion;
2840
+ private readonly proxies;
2841
+ private readonly waiting;
2842
+ private nextRequestId;
2843
+ /**
2844
+ * Register a stand-in for a server that is actually running in the Worker.
2845
+ *
2846
+ * The router only knows how to reach servers on this thread, so each bound
2847
+ * port gets a local server whose whole job is to forward and wait.
2848
+ */
2849
+ private proxyPort;
2850
+ private forward;
2851
+ private settleProxied;
2852
+ private closeProxies;
2853
+ teardown(): void;
2854
+ /** Does anything under `cwd` need a module only the host can supply? */
2855
+ private needsHostModules;
2856
+ }
2857
+
2780
2858
  interface RegistryManifest {
2781
2859
  name: string;
2782
2860
  version: string;
@@ -2837,8 +2915,8 @@ declare function extractNpmTarball(volume: RuntimeVolume, compressed: Uint8Array
2837
2915
  * own dependency cache, where no such file exists. That is the same trap
2838
2916
  * Rolldown's WASI binding falls into, and it surfaces just as obliquely.
2839
2917
  *
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.
2918
+ * So this never assumes it worked. Strict boot surfaces the failure; automatic mode reports
2919
+ * the cause before choosing the in-realm pod.
2842
2920
  */
2843
2921
  interface RuntimeWorker {
2844
2922
  postMessage(message: unknown): void;
@@ -2859,14 +2937,59 @@ declare function startRuntimeWorker(options?: {
2859
2937
  timeoutMs?: number;
2860
2938
  }): Promise<RuntimeWorker>;
2861
2939
 
2940
+ declare function syncChannelSupported(): boolean;
2941
+
2862
2942
  /**
2863
- * Can this environment support a blocking client at all?
2943
+ * Wiring a container's HTTP servers up to real URLs in the page.
2864
2944
  *
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.
2945
+ * The service worker does the routing; this is the half that lives in the page
2946
+ * and actually knows about the container.
2947
+ *
2948
+ * **Read the origin note before using this.** A preview served this way runs on
2949
+ * *your* origin, so scripts inside it can reach `window.parent`, your cookies
2950
+ * and your `localStorage` — the sandbox contains the program's *filesystem and
2951
+ * process table*, not the page it serves. For code you did not write, either
2952
+ * host the preview on a separate origin, or use {@link renderInto}, which puts
2953
+ * the response in an iframe with no origin at all.
2868
2954
  */
2869
- declare function syncChannelSupported(): boolean;
2955
+
2956
+ interface PreviewOptions {
2957
+ /** Where the worker script lives; defaults to the copy shipped beside the bundle. */
2958
+ scriptUrl?: string | URL;
2959
+ /** Registration scope. Must be able to see the paths a preview will request. */
2960
+ scope?: string;
2961
+ }
2962
+ interface Preview {
2963
+ /** The URL an iframe should be pointed at to see `port`. */
2964
+ urlFor(port: number): string;
2965
+ /** Stop answering requests and unregister the worker. */
2966
+ dispose(): Promise<void>;
2967
+ }
2968
+ /**
2969
+ * Register the preview worker and start answering its requests from `box`.
2970
+ *
2971
+ * Resolves to null where service workers are unavailable — a non-secure origin,
2972
+ * a browser with them disabled, or any non-browser host. Callers should treat
2973
+ * that as "no preview URLs here" and fall back to `box.request`.
2974
+ */
2975
+ declare function createPreview(box: Container, options?: PreviewOptions): Promise<Preview | null>;
2976
+ /**
2977
+ * Show one response from the container inside an element, without letting it
2978
+ * touch the page.
2979
+ *
2980
+ * The iframe is sandboxed with `allow-scripts` and deliberately *without*
2981
+ * `allow-same-origin`, which puts the document in an opaque origin: its scripts
2982
+ * run, and they can reach neither this page's DOM nor its cookies and storage.
2983
+ * That combination is what makes it safe to render output you do not trust.
2984
+ *
2985
+ * The trade-off is that only this one response exists — a page that asks for
2986
+ * `/main.js` gets nothing, because there is no origin to serve it from. For a
2987
+ * whole site, use {@link createPreview}, and read its note about origins first.
2988
+ */
2989
+ declare function renderInto(box: Container, element: HTMLElement, options?: {
2990
+ port: number;
2991
+ path?: string;
2992
+ }): Promise<HTMLIFrameElement>;
2870
2993
 
2871
2994
  /**
2872
2995
  * sandboxedjs — a Linux-like container that runs entirely inside Node.js.
@@ -2886,4 +3009,4 @@ declare function syncChannelSupported(): boolean;
2886
3009
  * ```
2887
3010
  */
2888
3011
 
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 };
3012
+ 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, type Preview, type PreviewOptions, 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 WorkerRuntimeOptions, WorkerRuntimePod, type WriteOptions, allCommands, applyChmod, braceExpand, buildRootfs, builtinNames, captureStdio, configureCPython, configurePython, createChildProcessModule, createContainer, createContext, createCoreModules, createPreview, 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, renderInto, resetPidCounter, shellQuote, startRuntimeWorker, strerror, syncChannelSupported, transformEsm, unameInfo };
package/dist/index.d.ts CHANGED
@@ -6,7 +6,7 @@ interface ChildHandle {
6
6
  pid: number;
7
7
  state: "starting" | "running" | "exited";
8
8
  exitCode: number | undefined;
9
- on(event: "stdout" | "stderr" | "exit", listener: (...args: any[]) => void): unknown;
9
+ on(event: "stdout" | "stderr" | "exit" | "rawmode", listener: (...args: any[]) => void): unknown;
10
10
  exec(): void;
11
11
  sendStdin(data: string): void;
12
12
  /**
@@ -25,6 +25,14 @@ interface ChildSpawnConfig {
25
25
  cwd?: string;
26
26
  env?: Record<string, string>;
27
27
  parentPid?: number;
28
+ /**
29
+ * The child was given the parent's streams (`stdio: "inherit"`).
30
+ *
31
+ * Its input is then the parent's terminal rather than a pipe that will end,
32
+ * which is the difference between a program that waits for what the user
33
+ * types and one that reads to end-of-input and stops.
34
+ */
35
+ inheritStdio?: boolean;
28
36
  }
29
37
  type SpawnChild = (config: ChildSpawnConfig) => ChildHandle;
30
38
  /**
@@ -40,6 +48,7 @@ type SyncSpawn = (request: {
40
48
  cwd: string;
41
49
  env?: Record<string, string>;
42
50
  input?: string;
51
+ inheritStdio?: boolean;
43
52
  }) => {
44
53
  status: number | null;
45
54
  stdout: string;
@@ -50,7 +59,7 @@ type SyncSpawn = (request: {
50
59
  message: string;
51
60
  };
52
61
  };
53
- declare function createChildProcessModule(spawnChild: SpawnChild, defaultCwd: () => string, syncSpawn?: SyncSpawn): Record<string, unknown>;
62
+ declare function createChildProcessModule(spawnChild: SpawnChild, defaultCwd: () => string, syncSpawn?: SyncSpawn, defaultEnv?: () => Record<string, string>): Record<string, unknown>;
54
63
 
55
64
  /**
56
65
  * Clean-room contracts between SandboxedJS and its JavaScript runtime.
@@ -433,7 +442,10 @@ declare class Pipe implements InputStream, OutputStream {
433
442
  * whole line on every keystroke — so the terminal must stop echoing, or
434
443
  * every character appears twice.
435
444
  */
436
- rawMode: boolean;
445
+ private raw;
446
+ onRawMode?: (enabled: boolean) => void;
447
+ get rawMode(): boolean;
448
+ set rawMode(enabled: boolean);
437
449
  columns: number | undefined;
438
450
  rows: number | undefined;
439
451
  get closed(): boolean;
@@ -1382,6 +1394,8 @@ declare class Shell {
1382
1394
  constructor(init: ShellInit);
1383
1395
  /** Parse and run a script fragment. Returns the last exit status. */
1384
1396
  execute(source: string, io: ShellIO): Promise<number>;
1397
+ /** Signal the current terminal pipeline without terminating the interactive shell. */
1398
+ interruptForeground(signal: string, stdin: InputStream): void;
1385
1399
  get isExiting(): boolean;
1386
1400
  /** True when `source` is not yet a complete command (for REPL continuation). */
1387
1401
  static isIncomplete(source: string): boolean;
@@ -1665,12 +1679,15 @@ interface ContainerOptions {
1665
1679
  /**
1666
1680
  * Where guest programs run.
1667
1681
  *
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.
1682
+ * `"auto"` (the default) tries the worker runtime and reports any fallback.
1683
+ * `"worker"` requires a working guest worker and shared-memory channel: boot
1684
+ * rejects with the cause if either is unavailable, rather than letting
1685
+ * synchronous child-process calls fail later. `"realm"` opts out explicitly.
1686
+ * Processes requiring host-native modules can still run in the host realm.
1672
1687
  */
1673
- isolation?: "worker" | "realm";
1688
+ isolation?: "auto" | "worker" | "realm";
1689
+ /** Receives the reason for an automatic fallback; defaults to console.warn. */
1690
+ onRuntimeFallback?: (error: Error) => void;
1674
1691
  /**
1675
1692
  * Where to load the guest worker bundle from.
1676
1693
  *
@@ -2457,6 +2474,7 @@ declare class VirtualHttpServer extends EventEmitter {
2457
2474
  readonly owner: string;
2458
2475
  listening: boolean;
2459
2476
  private portValue;
2477
+ private referenced;
2460
2478
  constructor(router: VirtualHttpRouter, owner: string, listener?: (req: VirtualIncomingMessage, res: VirtualServerResponse) => void);
2461
2479
  listen(...args: any[]): this;
2462
2480
  close(callback?: (error?: Error) => void): this;
@@ -2467,17 +2485,20 @@ declare class VirtualHttpServer extends EventEmitter {
2467
2485
  } | null;
2468
2486
  ref(): this;
2469
2487
  unref(): this;
2488
+ hasRef(): boolean;
2470
2489
  setTimeout(_milliseconds: number, callback?: () => void): this;
2471
2490
  }
2472
2491
  declare class VirtualHttpRouter {
2473
2492
  private readonly servers;
2474
2493
  /** Notified when a server begins listening, for `onServerReady`. */
2475
2494
  onListen: ((port: number) => void) | undefined;
2495
+ onClose: ((port: number) => void) | undefined;
2476
2496
  register(port: number, server: VirtualHttpServer, owner: string): void;
2477
2497
  unregister(port: number, server: VirtualHttpServer): void;
2478
2498
  /** Whether anything in this container is listening on `port`. */
2479
2499
  activePortsIncludes(port: number): boolean;
2480
2500
  activePorts(owner?: string): number[];
2501
+ referencedPorts(owner: string): number[];
2481
2502
  closeOwner(owner: string): void;
2482
2503
  closeAll(): void;
2483
2504
  request(port: number, init?: VirtualRequestInit): Promise<RuntimeHttpResponse>;
@@ -2734,6 +2755,7 @@ declare class LocalRuntimePod implements RuntimePod {
2734
2755
  spawn(config: ChildSpawnConfig): ChildHandle;
2735
2756
  };
2736
2757
  private disposed;
2758
+ private readonly running;
2737
2759
  protected readonly workdir: string;
2738
2760
  protected readonly env: Record<string, string>;
2739
2761
  protected readonly aliases: Record<string, string>;
@@ -2777,6 +2799,62 @@ declare class LocalRuntimePod implements RuntimePod {
2777
2799
  private assertActive;
2778
2800
  }
2779
2801
 
2802
+ /**
2803
+ * A pod that evaluates each guest program on its own thread.
2804
+ *
2805
+ * Everything shared stays here: the volume, the HTTP router, the package
2806
+ * installer and the process table. Only the program's own evaluation moves,
2807
+ * and it reaches back for the rest through a {@link SyncChannelServer}.
2808
+ *
2809
+ * That arrangement is forced rather than chosen. A synchronous call has to
2810
+ * block the caller while the work it is waiting on still makes progress, so
2811
+ * the blocking side cannot be the side that owns the resources — otherwise a
2812
+ * child process needing the filesystem would have to call into a thread that
2813
+ * is frozen waiting for that child. The guest blocks; the host never does.
2814
+ *
2815
+ * It inherits from {@link LocalRuntimePod} because every other part of the
2816
+ * contract is identical, and overriding one method is a smaller and more
2817
+ * honest claim than reimplementing nine. Both are held to the same contract
2818
+ * suite (`test/pod-contract.ts`).
2819
+ */
2820
+
2821
+ interface WorkerRuntimeOptions extends LocalRuntimeOptions {
2822
+ /** Where the guest bundle lives; defaults to the copy shipped beside this one. */
2823
+ workerUrl?: string | URL;
2824
+ }
2825
+ declare class WorkerRuntimePod extends LocalRuntimePod {
2826
+ private readonly workerUrl;
2827
+ /** Live workers, so teardown can stop them all. */
2828
+ private readonly live;
2829
+ protected constructor(options: WorkerRuntimeOptions);
2830
+ /** Boot without silently dropping synchronous child-process support. */
2831
+ static boot(options?: WorkerRuntimeOptions): Promise<WorkerRuntimePod>;
2832
+ /** Compatibility mode, with an observable explanation for every fallback. */
2833
+ static tryBoot(options?: WorkerRuntimeOptions, onFallback?: (error: Error) => void): Promise<WorkerRuntimePod | null>;
2834
+ spawn(command: string, args?: string[], options?: Record<string, unknown>): Promise<RuntimeProcess>;
2835
+ private spawnInWorker;
2836
+ /** Start an asynchronous child on the host's behalf and relay its events. */
2837
+ private startChild;
2838
+ /** Run a child to completion and collect it, for the guest's `spawnSync`. */
2839
+ private runChildToCompletion;
2840
+ private readonly proxies;
2841
+ private readonly waiting;
2842
+ private nextRequestId;
2843
+ /**
2844
+ * Register a stand-in for a server that is actually running in the Worker.
2845
+ *
2846
+ * The router only knows how to reach servers on this thread, so each bound
2847
+ * port gets a local server whose whole job is to forward and wait.
2848
+ */
2849
+ private proxyPort;
2850
+ private forward;
2851
+ private settleProxied;
2852
+ private closeProxies;
2853
+ teardown(): void;
2854
+ /** Does anything under `cwd` need a module only the host can supply? */
2855
+ private needsHostModules;
2856
+ }
2857
+
2780
2858
  interface RegistryManifest {
2781
2859
  name: string;
2782
2860
  version: string;
@@ -2837,8 +2915,8 @@ declare function extractNpmTarball(volume: RuntimeVolume, compressed: Uint8Array
2837
2915
  * own dependency cache, where no such file exists. That is the same trap
2838
2916
  * Rolldown's WASI binding falls into, and it surfaces just as obliquely.
2839
2917
  *
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.
2918
+ * So this never assumes it worked. Strict boot surfaces the failure; automatic mode reports
2919
+ * the cause before choosing the in-realm pod.
2842
2920
  */
2843
2921
  interface RuntimeWorker {
2844
2922
  postMessage(message: unknown): void;
@@ -2859,14 +2937,59 @@ declare function startRuntimeWorker(options?: {
2859
2937
  timeoutMs?: number;
2860
2938
  }): Promise<RuntimeWorker>;
2861
2939
 
2940
+ declare function syncChannelSupported(): boolean;
2941
+
2862
2942
  /**
2863
- * Can this environment support a blocking client at all?
2943
+ * Wiring a container's HTTP servers up to real URLs in the page.
2864
2944
  *
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.
2945
+ * The service worker does the routing; this is the half that lives in the page
2946
+ * and actually knows about the container.
2947
+ *
2948
+ * **Read the origin note before using this.** A preview served this way runs on
2949
+ * *your* origin, so scripts inside it can reach `window.parent`, your cookies
2950
+ * and your `localStorage` — the sandbox contains the program's *filesystem and
2951
+ * process table*, not the page it serves. For code you did not write, either
2952
+ * host the preview on a separate origin, or use {@link renderInto}, which puts
2953
+ * the response in an iframe with no origin at all.
2868
2954
  */
2869
- declare function syncChannelSupported(): boolean;
2955
+
2956
+ interface PreviewOptions {
2957
+ /** Where the worker script lives; defaults to the copy shipped beside the bundle. */
2958
+ scriptUrl?: string | URL;
2959
+ /** Registration scope. Must be able to see the paths a preview will request. */
2960
+ scope?: string;
2961
+ }
2962
+ interface Preview {
2963
+ /** The URL an iframe should be pointed at to see `port`. */
2964
+ urlFor(port: number): string;
2965
+ /** Stop answering requests and unregister the worker. */
2966
+ dispose(): Promise<void>;
2967
+ }
2968
+ /**
2969
+ * Register the preview worker and start answering its requests from `box`.
2970
+ *
2971
+ * Resolves to null where service workers are unavailable — a non-secure origin,
2972
+ * a browser with them disabled, or any non-browser host. Callers should treat
2973
+ * that as "no preview URLs here" and fall back to `box.request`.
2974
+ */
2975
+ declare function createPreview(box: Container, options?: PreviewOptions): Promise<Preview | null>;
2976
+ /**
2977
+ * Show one response from the container inside an element, without letting it
2978
+ * touch the page.
2979
+ *
2980
+ * The iframe is sandboxed with `allow-scripts` and deliberately *without*
2981
+ * `allow-same-origin`, which puts the document in an opaque origin: its scripts
2982
+ * run, and they can reach neither this page's DOM nor its cookies and storage.
2983
+ * That combination is what makes it safe to render output you do not trust.
2984
+ *
2985
+ * The trade-off is that only this one response exists — a page that asks for
2986
+ * `/main.js` gets nothing, because there is no origin to serve it from. For a
2987
+ * whole site, use {@link createPreview}, and read its note about origins first.
2988
+ */
2989
+ declare function renderInto(box: Container, element: HTMLElement, options?: {
2990
+ port: number;
2991
+ path?: string;
2992
+ }): Promise<HTMLIFrameElement>;
2870
2993
 
2871
2994
  /**
2872
2995
  * sandboxedjs — a Linux-like container that runs entirely inside Node.js.
@@ -2886,4 +3009,4 @@ declare function syncChannelSupported(): boolean;
2886
3009
  * ```
2887
3010
  */
2888
3011
 
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 };
3012
+ 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, type Preview, type PreviewOptions, 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 WorkerRuntimeOptions, WorkerRuntimePod, type WriteOptions, allCommands, applyChmod, braceExpand, buildRootfs, builtinNames, captureStdio, configureCPython, configurePython, createChildProcessModule, createContainer, createContext, createCoreModules, createPreview, 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, renderInto, resetPidCounter, shellQuote, startRuntimeWorker, strerror, syncChannelSupported, transformEsm, unameInfo };