sandboxedjs 0.1.29 → 0.1.30

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
@@ -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.
@@ -2777,6 +2786,68 @@ declare class LocalRuntimePod implements RuntimePod {
2777
2786
  private assertActive;
2778
2787
  }
2779
2788
 
2789
+ /**
2790
+ * A pod that evaluates each guest program on its own thread.
2791
+ *
2792
+ * Everything shared stays here: the volume, the HTTP router, the package
2793
+ * installer and the process table. Only the program's own evaluation moves,
2794
+ * and it reaches back for the rest through a {@link SyncChannelServer}.
2795
+ *
2796
+ * That arrangement is forced rather than chosen. A synchronous call has to
2797
+ * block the caller while the work it is waiting on still makes progress, so
2798
+ * the blocking side cannot be the side that owns the resources — otherwise a
2799
+ * child process needing the filesystem would have to call into a thread that
2800
+ * is frozen waiting for that child. The guest blocks; the host never does.
2801
+ *
2802
+ * It inherits from {@link LocalRuntimePod} because every other part of the
2803
+ * contract is identical, and overriding one method is a smaller and more
2804
+ * honest claim than reimplementing nine. Both are held to the same contract
2805
+ * suite (`test/pod-contract.ts`).
2806
+ */
2807
+
2808
+ interface WorkerRuntimeOptions extends LocalRuntimeOptions {
2809
+ /** Where the guest bundle lives; defaults to the copy shipped beside this one. */
2810
+ workerUrl?: string | URL;
2811
+ }
2812
+ declare class WorkerRuntimePod extends LocalRuntimePod {
2813
+ private readonly workerUrl;
2814
+ /** Live workers, so teardown can stop them all. */
2815
+ private readonly live;
2816
+ protected constructor(options: WorkerRuntimeOptions);
2817
+ /**
2818
+ * Boot a Worker-backed pod, or return null when this host cannot support one.
2819
+ *
2820
+ * Declining is a first-class outcome. `SharedArrayBuffer` needs cross-origin
2821
+ * isolation, a bundler may have made the guest script unreachable, and a host
2822
+ * that supplied its own module objects has handed over things no thread
2823
+ * boundary can carry. In every case the caller falls back to the in-realm pod
2824
+ * and keeps working.
2825
+ */
2826
+ static tryBoot(options?: WorkerRuntimeOptions): Promise<WorkerRuntimePod | null>;
2827
+ spawn(command: string, args?: string[], options?: Record<string, unknown>): Promise<RuntimeProcess>;
2828
+ private spawnInWorker;
2829
+ /** Start an asynchronous child on the host's behalf and relay its events. */
2830
+ private startChild;
2831
+ /** Run a child to completion and collect it, for the guest's `spawnSync`. */
2832
+ private runChildToCompletion;
2833
+ private readonly proxies;
2834
+ private readonly waiting;
2835
+ private nextRequestId;
2836
+ /**
2837
+ * Register a stand-in for a server that is actually running in the Worker.
2838
+ *
2839
+ * The router only knows how to reach servers on this thread, so each bound
2840
+ * port gets a local server whose whole job is to forward and wait.
2841
+ */
2842
+ private proxyPort;
2843
+ private forward;
2844
+ private settleProxied;
2845
+ private closeProxies;
2846
+ teardown(): void;
2847
+ /** Does anything under `cwd` need a module only the host can supply? */
2848
+ private needsHostModules;
2849
+ }
2850
+
2780
2851
  interface RegistryManifest {
2781
2852
  name: string;
2782
2853
  version: string;
@@ -2868,6 +2939,58 @@ declare function startRuntimeWorker(options?: {
2868
2939
  */
2869
2940
  declare function syncChannelSupported(): boolean;
2870
2941
 
2942
+ /**
2943
+ * Wiring a container's HTTP servers up to real URLs in the page.
2944
+ *
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.
2954
+ */
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>;
2993
+
2871
2994
  /**
2872
2995
  * sandboxedjs — a Linux-like container that runs entirely inside Node.js.
2873
2996
  *
@@ -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
@@ -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.
@@ -2777,6 +2786,68 @@ declare class LocalRuntimePod implements RuntimePod {
2777
2786
  private assertActive;
2778
2787
  }
2779
2788
 
2789
+ /**
2790
+ * A pod that evaluates each guest program on its own thread.
2791
+ *
2792
+ * Everything shared stays here: the volume, the HTTP router, the package
2793
+ * installer and the process table. Only the program's own evaluation moves,
2794
+ * and it reaches back for the rest through a {@link SyncChannelServer}.
2795
+ *
2796
+ * That arrangement is forced rather than chosen. A synchronous call has to
2797
+ * block the caller while the work it is waiting on still makes progress, so
2798
+ * the blocking side cannot be the side that owns the resources — otherwise a
2799
+ * child process needing the filesystem would have to call into a thread that
2800
+ * is frozen waiting for that child. The guest blocks; the host never does.
2801
+ *
2802
+ * It inherits from {@link LocalRuntimePod} because every other part of the
2803
+ * contract is identical, and overriding one method is a smaller and more
2804
+ * honest claim than reimplementing nine. Both are held to the same contract
2805
+ * suite (`test/pod-contract.ts`).
2806
+ */
2807
+
2808
+ interface WorkerRuntimeOptions extends LocalRuntimeOptions {
2809
+ /** Where the guest bundle lives; defaults to the copy shipped beside this one. */
2810
+ workerUrl?: string | URL;
2811
+ }
2812
+ declare class WorkerRuntimePod extends LocalRuntimePod {
2813
+ private readonly workerUrl;
2814
+ /** Live workers, so teardown can stop them all. */
2815
+ private readonly live;
2816
+ protected constructor(options: WorkerRuntimeOptions);
2817
+ /**
2818
+ * Boot a Worker-backed pod, or return null when this host cannot support one.
2819
+ *
2820
+ * Declining is a first-class outcome. `SharedArrayBuffer` needs cross-origin
2821
+ * isolation, a bundler may have made the guest script unreachable, and a host
2822
+ * that supplied its own module objects has handed over things no thread
2823
+ * boundary can carry. In every case the caller falls back to the in-realm pod
2824
+ * and keeps working.
2825
+ */
2826
+ static tryBoot(options?: WorkerRuntimeOptions): Promise<WorkerRuntimePod | null>;
2827
+ spawn(command: string, args?: string[], options?: Record<string, unknown>): Promise<RuntimeProcess>;
2828
+ private spawnInWorker;
2829
+ /** Start an asynchronous child on the host's behalf and relay its events. */
2830
+ private startChild;
2831
+ /** Run a child to completion and collect it, for the guest's `spawnSync`. */
2832
+ private runChildToCompletion;
2833
+ private readonly proxies;
2834
+ private readonly waiting;
2835
+ private nextRequestId;
2836
+ /**
2837
+ * Register a stand-in for a server that is actually running in the Worker.
2838
+ *
2839
+ * The router only knows how to reach servers on this thread, so each bound
2840
+ * port gets a local server whose whole job is to forward and wait.
2841
+ */
2842
+ private proxyPort;
2843
+ private forward;
2844
+ private settleProxied;
2845
+ private closeProxies;
2846
+ teardown(): void;
2847
+ /** Does anything under `cwd` need a module only the host can supply? */
2848
+ private needsHostModules;
2849
+ }
2850
+
2780
2851
  interface RegistryManifest {
2781
2852
  name: string;
2782
2853
  version: string;
@@ -2868,6 +2939,58 @@ declare function startRuntimeWorker(options?: {
2868
2939
  */
2869
2940
  declare function syncChannelSupported(): boolean;
2870
2941
 
2942
+ /**
2943
+ * Wiring a container's HTTP servers up to real URLs in the page.
2944
+ *
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.
2954
+ */
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>;
2993
+
2871
2994
  /**
2872
2995
  * sandboxedjs — a Linux-like container that runs entirely inside Node.js.
2873
2996
  *
@@ -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.js CHANGED
@@ -19695,6 +19695,10 @@ var KernelChildProcess = class {
19695
19695
  started = false;
19696
19696
  cancelled = false;
19697
19697
  constructor(kernel, cred, config, pid) {
19698
+ if (config.inheritStdio) {
19699
+ this.stdin.isTTY = true;
19700
+ this.stdin.interactive = true;
19701
+ }
19698
19702
  this.kernel = kernel;
19699
19703
  this.cred = cred;
19700
19704
  this.pid = pid;
@@ -22099,7 +22103,8 @@ var ChildProcess = class extends EventEmitter4 {
22099
22103
  queueMicrotask(() => this.emit("close", code, null));
22100
22104
  }
22101
22105
  };
22102
- function createChildProcessModule(spawnChild, defaultCwd, syncSpawn) {
22106
+ function createChildProcessModule(spawnChild, defaultCwd, syncSpawn, defaultEnv = () => ({})) {
22107
+ const environmentFor = (options) => options.env ? { ...options.env } : defaultEnv();
22103
22108
  const throughShell = (command, options) => {
22104
22109
  const shell = typeof options.shell === "string" ? options.shell : "/bin/sh";
22105
22110
  return { file: shell, args: ["-c", command] };
@@ -22110,7 +22115,7 @@ function createChildProcessModule(spawnChild, defaultCwd, syncSpawn) {
22110
22115
  command: resolved.file,
22111
22116
  args: resolved.args,
22112
22117
  cwd: options.cwd ?? defaultCwd(),
22113
- ...options.env ? { env: options.env } : {}
22118
+ env: environmentFor(options)
22114
22119
  });
22115
22120
  return new ChildProcess(handle, resolved.file, resolved.args);
22116
22121
  };
@@ -22156,11 +22161,11 @@ ${err.join("")}`),
22156
22161
  exec,
22157
22162
  execFile,
22158
22163
  fork: (modulePath, args = [], options = {}) => spawn("node", [modulePath, ...args], options),
22159
- ...buildSyncFamily(syncSpawn, throughShell, defaultCwd),
22164
+ ...buildSyncFamily(syncSpawn, throughShell, defaultCwd, environmentFor),
22160
22165
  ChildProcess
22161
22166
  };
22162
22167
  }
22163
- function buildSyncFamily(syncSpawn, throughShell, defaultCwd) {
22168
+ function buildSyncFamily(syncSpawn, throughShell, defaultCwd, environmentFor) {
22164
22169
  if (!syncSpawn) {
22165
22170
  return {
22166
22171
  execSync: unavailable("execSync"),
@@ -22171,12 +22176,14 @@ function buildSyncFamily(syncSpawn, throughShell, defaultCwd) {
22171
22176
  const run = (file3, args, options) => {
22172
22177
  const resolved = options.shell ? throughShell([file3, ...args].join(" "), options) : { file: file3, args };
22173
22178
  const input = options.input === void 0 ? void 0 : typeof options.input === "string" ? options.input : new TextDecoder().decode(options.input);
22179
+ const inherit = options.stdio === "inherit" || Array.isArray(options.stdio) && options.stdio[0] === "inherit";
22174
22180
  return syncSpawn({
22175
22181
  command: resolved.file,
22176
22182
  args: resolved.args,
22177
22183
  cwd: options.cwd ?? defaultCwd(),
22178
- ...options.env ? { env: options.env } : {},
22179
- ...input === void 0 ? {} : { input }
22184
+ env: environmentFor(options),
22185
+ ...input === void 0 ? {} : { input },
22186
+ ...inherit ? { inheritStdio: true } : {}
22180
22187
  });
22181
22188
  };
22182
22189
  const asOutput = (text2, options) => options.encoding === "buffer" || options.encoding === void 0 ? Buffer2.from(text2) : text2;
@@ -22856,7 +22863,7 @@ function createCoreModules(options) {
22856
22863
  };
22857
22864
  const http = options.http ? createHttpModule(options.http.router, options.http.owner, httpOptions, "http:") : createUnsupportedModule("http");
22858
22865
  const https = options.http ? createHttpModule(options.http.router, options.http.owner, httpOptions, "https:") : createUnsupportedModule("https");
22859
- const childProcess = options.spawnChild ? createChildProcessModule(options.spawnChild, () => cwd, options.syncSpawn) : createUnsupportedModule("child_process");
22866
+ const childProcess = options.spawnChild ? createChildProcessModule(options.spawnChild, () => cwd, options.syncSpawn, () => ({ ...processObject.env })) : createUnsupportedModule("child_process");
22860
22867
  const readline = createReadlineModule(() => processObject.stdin, () => processObject.stdout);
22861
22868
  const dns = createDnsModule();
22862
22869
  const builtins = {
@@ -24101,8 +24108,10 @@ var MirroringVolume = class {
24101
24108
  * container pays nothing for this.
24102
24109
  */
24103
24110
  attach(mirror, root) {
24111
+ const cleanRoot = clean(root);
24112
+ if (this.mirror === mirror && this.root === cleanRoot) return;
24104
24113
  this.mirror = mirror;
24105
- this.root = clean(root);
24114
+ this.root = cleanRoot;
24106
24115
  this.seed();
24107
24116
  }
24108
24117
  detach() {
@@ -25237,9 +25246,10 @@ var WorkerRuntimePod = class _WorkerRuntimePod extends LocalRuntimePod {
25237
25246
  ...this.workerUrl ? { url: this.workerUrl } : {},
25238
25247
  timeoutMs: 15e3
25239
25248
  });
25249
+ const streams = { target: null };
25240
25250
  const server = new SyncChannelServer(buffers, serveSyncSyscalls({
25241
25251
  volume: this.volume,
25242
- spawnChild: (request) => this.runChildToCompletion(request)
25252
+ spawnChild: (request) => this.runChildToCompletion(request, streams.target)
25243
25253
  }));
25244
25254
  const entry = { worker, server };
25245
25255
  this.live.add(entry);
@@ -25249,6 +25259,7 @@ var WorkerRuntimePod = class _WorkerRuntimePod extends LocalRuntimePod {
25249
25259
  this.closeProxies(owner);
25250
25260
  void worker.terminate();
25251
25261
  });
25262
+ streams.target = process2;
25252
25263
  const children = /* @__PURE__ */ new Map();
25253
25264
  worker.onMessage((raw) => {
25254
25265
  const message = raw;
@@ -25315,7 +25326,7 @@ var WorkerRuntimePod = class _WorkerRuntimePod extends LocalRuntimePod {
25315
25326
  handle.exec();
25316
25327
  }
25317
25328
  /** Run a child to completion and collect it, for the guest's `spawnSync`. */
25318
- runChildToCompletion(request) {
25329
+ runChildToCompletion(request, streamTo) {
25319
25330
  return new Promise((resolve2) => {
25320
25331
  let handle;
25321
25332
  try {
@@ -25323,7 +25334,8 @@ var WorkerRuntimePod = class _WorkerRuntimePod extends LocalRuntimePod {
25323
25334
  command: request.command,
25324
25335
  args: request.args,
25325
25336
  cwd: request.cwd,
25326
- ...request.env ? { env: request.env } : {}
25337
+ ...request.env ? { env: request.env } : {},
25338
+ ...request.inheritStdio ? { inheritStdio: true } : {}
25327
25339
  });
25328
25340
  } catch (error) {
25329
25341
  const failure = error;
@@ -25332,18 +25344,21 @@ var WorkerRuntimePod = class _WorkerRuntimePod extends LocalRuntimePod {
25332
25344
  }
25333
25345
  let stdout = "";
25334
25346
  let stderr = "";
25347
+ const live = request.inheritStdio ? streamTo : null;
25335
25348
  handle.on("stdout", (text2) => {
25336
25349
  stdout += text2;
25350
+ live?.output(text2);
25337
25351
  });
25338
25352
  handle.on("stderr", (text2) => {
25339
25353
  stderr += text2;
25354
+ live?.error(text2);
25340
25355
  });
25341
25356
  handle.on("exit", (code) => resolve2({ status: code, stdout, stderr, signal: null }));
25342
25357
  handle.exec();
25343
25358
  if (request.input !== void 0) {
25344
25359
  handle.sendStdin?.(request.input);
25345
25360
  }
25346
- handle.endStdin?.();
25361
+ if (!request.inheritStdio) handle.endStdin?.();
25347
25362
  });
25348
25363
  }
25349
25364
  // ── HTTP servers living on another thread ─────────────────────────────────
@@ -26344,8 +26359,99 @@ init_variables();
26344
26359
  init_arith();
26345
26360
  init_expand();
26346
26361
  init_builtins();
26362
+
26363
+ // src/preview/register.ts
26364
+ function serveContainerOn(port, box) {
26365
+ port.onmessage = async (event) => {
26366
+ const request = event.data;
26367
+ try {
26368
+ const response = await box.request(request.port, {
26369
+ method: request.method,
26370
+ path: request.path,
26371
+ headers: request.headers,
26372
+ ...request.body ? { body: new Uint8Array(request.body) } : {}
26373
+ });
26374
+ const bytes2 = response.bytes.slice();
26375
+ port.postMessage(
26376
+ {
26377
+ id: request.id,
26378
+ response: {
26379
+ status: response.status,
26380
+ statusText: response.statusText,
26381
+ headers: response.headers,
26382
+ body: bytes2.buffer
26383
+ }
26384
+ },
26385
+ [bytes2.buffer]
26386
+ );
26387
+ } catch (error) {
26388
+ const message = new TextEncoder().encode(error instanceof Error ? error.message : String(error));
26389
+ port.postMessage({
26390
+ id: request.id,
26391
+ response: { status: 502, statusText: "Bad Gateway", headers: {}, body: message.buffer }
26392
+ });
26393
+ }
26394
+ };
26395
+ port.start?.();
26396
+ }
26397
+ async function createPreview(box, options = {}) {
26398
+ if (typeof navigator === "undefined" || !("serviceWorker" in navigator)) return null;
26399
+ const scriptUrl = options.scriptUrl ?? new URL("./service-worker.js", import.meta.url);
26400
+ let registration;
26401
+ try {
26402
+ registration = await navigator.serviceWorker.register(scriptUrl, {
26403
+ type: "module",
26404
+ ...options.scope ? { scope: options.scope } : {}
26405
+ });
26406
+ } catch {
26407
+ return null;
26408
+ }
26409
+ const worker = registration.active ?? registration.waiting ?? registration.installing;
26410
+ if (!worker) return null;
26411
+ if (worker.state !== "activated") {
26412
+ const activated = await new Promise((resolve2) => {
26413
+ const check = () => {
26414
+ if (worker.state === "activated") {
26415
+ worker.removeEventListener("statechange", check);
26416
+ resolve2(true);
26417
+ } else if (worker.state === "redundant") {
26418
+ worker.removeEventListener("statechange", check);
26419
+ resolve2(false);
26420
+ }
26421
+ };
26422
+ worker.addEventListener("statechange", check);
26423
+ setTimeout(() => resolve2(worker.state === "activated"), 1e4);
26424
+ check();
26425
+ });
26426
+ if (!activated) return null;
26427
+ }
26428
+ const channel = new MessageChannel();
26429
+ serveContainerOn(channel.port1, box);
26430
+ worker.postMessage({ type: "sandboxedjs:connect" }, [channel.port2]);
26431
+ const base2 = registration.scope.replace(/\/$/, "");
26432
+ return {
26433
+ urlFor: (port) => `${base2}/__sbx__/${port}/`,
26434
+ dispose: async () => {
26435
+ channel.port1.close();
26436
+ await registration.unregister();
26437
+ }
26438
+ };
26439
+ }
26440
+ async function renderInto(box, element, options = { port: 80 }) {
26441
+ const response = await box.request(options.port, { path: options.path ?? "/" });
26442
+ const frame = document.createElement("iframe");
26443
+ frame.setAttribute("sandbox", "allow-scripts");
26444
+ frame.style.width = "100%";
26445
+ frame.style.height = "100%";
26446
+ frame.style.border = "0";
26447
+ frame.srcdoc = response.body;
26448
+ element.replaceChildren(frame);
26449
+ return frame;
26450
+ }
26451
+
26452
+ // src/index.ts
26347
26453
  var src_default = createContainer;
26348
26454
 
26349
- export { ArithError, BufferSink, CallbackSink, CleanPackageInstaller, CommandRegistry, CommonJsEngine, Container, ContainerFs, ERRNO, FileInput, FileOutput, IncompleteInputError, KERNEL_NAME, KERNEL_RELEASE, Kernel, LocalRuntimePod, MemoryVolume, NODE_VERSION, NPM_VERSION, NetworkStack, NullInput, NullOutput, OS_RELEASE, PYTHON_VERSION, Pipe, Process, ProcessTable, ROOT_CRED, SIGNALS, SIGNAL_NAMES, Session, Shell, ShellExit, Lexer as ShellLexer, ShellSyntaxError, Stats, SysError, TeeOutput, Terminal, UserDatabase, Variables, Vfs, VirtualHttpRouter, VirtualHttpServer, VirtualIncomingMessage, VirtualServerResponse, WASM_ALIASES, allCommands, applyChmod, braceExpand, buildRootfs, builtinNames, captureStdio, configureCPython, configurePython, createChildProcessModule, createContainer, createContext, createCoreModules, src_default 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_exports as posixPath, resetPidCounter, shellQuote, startRuntimeWorker, strerror, syncChannelSupported, transformEsm, unameInfo };
26455
+ export { ArithError, BufferSink, CallbackSink, CleanPackageInstaller, CommandRegistry, CommonJsEngine, Container, ContainerFs, ERRNO, FileInput, FileOutput, IncompleteInputError, KERNEL_NAME, KERNEL_RELEASE, Kernel, LocalRuntimePod, MemoryVolume, NODE_VERSION, NPM_VERSION, NetworkStack, NullInput, NullOutput, OS_RELEASE, PYTHON_VERSION, Pipe, Process, ProcessTable, ROOT_CRED, SIGNALS, SIGNAL_NAMES, Session, Shell, ShellExit, Lexer as ShellLexer, ShellSyntaxError, Stats, SysError, TeeOutput, Terminal, UserDatabase, Variables, Vfs, VirtualHttpRouter, VirtualHttpServer, VirtualIncomingMessage, VirtualServerResponse, WASM_ALIASES, WorkerRuntimePod, allCommands, applyChmod, braceExpand, buildRootfs, builtinNames, captureStdio, configureCPython, configurePython, createChildProcessModule, createContainer, createContext, createCoreModules, createPreview, src_default 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_exports as posixPath, renderInto, resetPidCounter, shellQuote, startRuntimeWorker, strerror, syncChannelSupported, transformEsm, unameInfo };
26350
26456
  //# sourceMappingURL=index.js.map
26351
26457
  //# sourceMappingURL=index.js.map