sandboxedjs 0.1.25 → 0.1.27

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
@@ -1,4 +1,4 @@
1
- import EventEmitter from 'events';
1
+ import EventEmitter from 'events/events.js';
2
2
  import streamModule from 'stream-browserify';
3
3
 
4
4
  /** The handle a pod's process manager returns. */
@@ -81,7 +81,12 @@ interface RuntimeProcessResult {
81
81
  }
82
82
  interface RuntimeProcess {
83
83
  readonly completion: Promise<RuntimeProcessResult>;
84
- on(event: "output" | "error" | "exit", listener: (...args: any[]) => void): this;
84
+ /**
85
+ * `output` and `error` carry stdout and stderr; `exit` the code. `rawmode`
86
+ * reports the program turning terminal raw mode on or off, which a terminal
87
+ * needs so that it stops echoing input the program is drawing itself.
88
+ */
89
+ on(event: "output" | "error" | "exit" | "rawmode", listener: (...args: any[]) => void): this;
85
90
  write(data: string): void;
86
91
  kill(signal?: string): void;
87
92
  }
@@ -191,7 +196,7 @@ interface DirEntry {
191
196
  /**
192
197
  * The container's virtual filesystem.
193
198
  *
194
- * Real file content lives in a Nodepod `MemoryVolume`, which is deliberately the
199
+ * Real file content lives in the RuntimePod's `MemoryVolume`, deliberately the
195
200
  * *same* volume the Node.js worker processes see — so a file written by `echo`
196
201
  * is readable by `require('fs')` inside a spawned script, and vice versa.
197
202
  *
@@ -390,6 +395,14 @@ declare class Pipe implements InputStream, OutputStream {
390
395
  isTTY: boolean;
391
396
  /** Set on pipes owned by an outside caller, who may never call `end()`. */
392
397
  interactive: boolean;
398
+ /**
399
+ * Set while the running program has put the terminal in raw mode.
400
+ *
401
+ * A program in raw mode draws its own input — a prompt library redraws the
402
+ * whole line on every keystroke — so the terminal must stop echoing, or
403
+ * every character appears twice.
404
+ */
405
+ rawMode: boolean;
393
406
  columns: number | undefined;
394
407
  rows: number | undefined;
395
408
  get closed(): boolean;
@@ -733,7 +746,7 @@ declare function createContext(init: ContextInit): ExecContext;
733
746
  * The container's network stack.
734
747
  *
735
748
  * There is no real socket layer: HTTP servers started inside the container are
736
- * registered with Nodepod's request proxy, and this module is the routing and
749
+ * registered with the RuntimePod's request proxy, and this module is the routing and
737
750
  * name-resolution layer on top — interfaces for `ip`/`ifconfig`, a hosts file
738
751
  * resolver, a listening-port table for `ss`/`netstat`, and an outbound policy
739
752
  * that decides whether `curl https://example.com` is allowed to touch the real
@@ -792,7 +805,7 @@ declare class NetworkStack {
792
805
  registerListener(port: number, info: Omit<ListeningPort, "port" | "since">): void;
793
806
  unregisterListener(port: number): void;
794
807
  listening(): ListeningPort[];
795
- /** Ports Nodepod's proxy has registered for this instance. */
808
+ /** Ports the pod's proxy has registered for this instance. */
796
809
  private knownPodPorts;
797
810
  /** True when something inside the container answers on `port`. */
798
811
  isPortOpen(port: number, timeoutMs?: number): Promise<boolean>;
@@ -1719,7 +1732,7 @@ declare class Container {
1719
1732
  /**
1720
1733
  * Deliver a request whose body is bytes, without letting them become text.
1721
1734
  *
1722
- * Nodepod's public `request()` runs the body through `toString("utf8")` on
1735
+ * A RuntimePod's public `request()` may run the body through `toString("utf8")` on
1723
1736
  * its way in, so anything above `0x7f` is replaced: a five-byte payload
1724
1737
  * containing `0x89` and `0xff` arrives as nine. That silently destroys every
1725
1738
  * upload — an image or a video reaches the server the wrong size and no
@@ -1759,7 +1772,7 @@ declare class Container {
1759
1772
  get cwd(): string;
1760
1773
  get env(): Record<string, string>;
1761
1774
  private assertActive;
1762
- /** Tear down every process and release the Nodepod instance. */
1775
+ /** Tear down every process and release the runtime pod. */
1763
1776
  dispose(): void;
1764
1777
  get isDisposed(): boolean;
1765
1778
  }
@@ -2101,17 +2114,16 @@ interface RootfsOptions {
2101
2114
  declare function buildRootfs(vfs: Vfs, opts?: RootfsOptions): void;
2102
2115
 
2103
2116
  /**
2104
- * The Node.js runtime, backed by Nodepod.
2117
+ * The Node.js command adapter, backed by the clean-room RuntimePod.
2105
2118
  *
2106
- * Nodepod runs the script in an isolated worker over the *same* memory volume
2119
+ * The pod runs the script over the *same* memory volume
2107
2120
  * the container's filesystem uses, so `require('fs')` inside a script sees the
2108
2121
  * files `echo` and `tar` created, and anything the script writes is visible to
2109
2122
  * the shell afterwards.
2110
2123
  *
2111
- * Two gaps in the underlying `spawn` are papered over here:
2124
+ * Two details of the underlying `spawn` are handled here:
2112
2125
  * - only `node` resolves as a command, so everything else is dispatched by our
2113
- * own kernel rather than being handed to Nodepod's shell (which hangs on an
2114
- * unknown command);
2126
+ * own kernel rather than being handed to the Node process runner;
2115
2127
  * - the worker's stdin has no end-of-stream signal, so when a pipeline feeds
2116
2128
  * a script we materialise stdin as a file and install a real stdin stream
2117
2129
  * over it before the script loads.
@@ -2262,6 +2274,16 @@ declare class CommonJsEngine {
2262
2274
  main: CommonJsModule | null;
2263
2275
  /** `package.json` per directory; resolution reads them constantly. */
2264
2276
  private readonly manifests;
2277
+ private evaluationDepth;
2278
+ /**
2279
+ * Is a module body running synchronously right now?
2280
+ *
2281
+ * `process.exit` unwinds by throwing, and that is only safe while one of
2282
+ * this engine's own frames is on the stack to catch it. Thrown from a later
2283
+ * callback — a stream handler, a timer — it would escape into whichever
2284
+ * library called that callback and surface as an unrelated crash.
2285
+ */
2286
+ get isEvaluating(): boolean;
2265
2287
  constructor(volume: RuntimeVolume, options?: Omit<CommonJsEngineOptions, "volume">);
2266
2288
  /**
2267
2289
  * Evaluate an entry point.
@@ -2426,12 +2448,31 @@ interface CoreModulesOptions {
2426
2448
  spawnChild?: SpawnChild;
2427
2449
  /** File holding the process's standard input, exposed as descriptor 0. */
2428
2450
  stdinPath?: string;
2451
+ /** Keep `process.stdin` open and fed by {@link writeStdin} rather than ending it. */
2452
+ interactiveStdin?: boolean;
2453
+ /** Report the standard streams as a terminal, which is what makes CLIs prompt. */
2454
+ tty?: boolean;
2455
+ /** Called when the program turns raw mode on or off. */
2456
+ onRawMode?: (enabled: boolean) => void;
2429
2457
  }
2430
2458
  /** Build the core-module table injected into each isolated JS worker. */
2431
2459
  declare function createCoreModules(options: CoreModulesOptions): {
2432
2460
  builtins: Record<string, unknown>;
2433
2461
  globals: Record<string, unknown>;
2434
2462
  process: Record<string, any>;
2463
+ /** Deliver a chunk of input to an interactive `process.stdin`. */
2464
+ writeStdin(data: string): void;
2465
+ /** Signal end-of-input to an interactive `process.stdin`. */
2466
+ endStdin(): void;
2467
+ /**
2468
+ * Is the program waiting on input?
2469
+ *
2470
+ * Node keeps a process alive for an open stdin only while something is
2471
+ * actually reading it, and that distinction matters here: a CLI sitting on a
2472
+ * prompt has no timers pending and would otherwise look finished, while a
2473
+ * program that never touches stdin must still be allowed to exit.
2474
+ */
2475
+ readingStdin(): boolean;
2435
2476
  /**
2436
2477
  * How many timers this process still has outstanding.
2437
2478
  *
@@ -2442,6 +2483,8 @@ declare function createCoreModules(options: CoreModulesOptions): {
2442
2483
  * script that has simply finished.
2443
2484
  */
2444
2485
  pendingHandles(): number;
2486
+ /** Active timers which called `unref()` and therefore only merit startup grace. */
2487
+ pendingUnrefed(): number;
2445
2488
  };
2446
2489
 
2447
2490
  interface EsmTransformResult {
@@ -2528,9 +2571,18 @@ declare class LocalRuntimePod implements RuntimePod {
2528
2571
  private readonly aliases;
2529
2572
  private readonly modules;
2530
2573
  private readonly esbuild;
2574
+ private rolldownBinding;
2531
2575
  private constructor();
2532
2576
  static boot(options?: LocalRuntimeOptions): Promise<LocalRuntimePod>;
2533
2577
  spawn(command: string, args?: string[], options?: Record<string, unknown>): Promise<RuntimeProcess>;
2578
+ /**
2579
+ * Rolldown's JavaScript API synchronously requires its compiled binding.
2580
+ * When a project contains Rolldown, preload the official WASI build in the
2581
+ * host and expose it through the module override table before evaluation.
2582
+ * Keeping this demand-driven avoids adding WASM startup cost to ordinary
2583
+ * shells and Node programs.
2584
+ */
2585
+ private prepareRolldown;
2534
2586
  /**
2535
2587
  * Wait until the process has either started serving or genuinely run out of
2536
2588
  * work.
@@ -2565,6 +2617,10 @@ interface RegistryManifest {
2565
2617
  };
2566
2618
  dependencies?: Record<string, string>;
2567
2619
  optionalDependencies?: Record<string, string>;
2620
+ os?: string[];
2621
+ cpu?: string[];
2622
+ libc?: string[];
2623
+ main?: string;
2568
2624
  bin?: string | Record<string, string>;
2569
2625
  }
2570
2626
  interface CleanInstallerOptions {
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import EventEmitter from 'events';
1
+ import EventEmitter from 'events/events.js';
2
2
  import streamModule from 'stream-browserify';
3
3
 
4
4
  /** The handle a pod's process manager returns. */
@@ -81,7 +81,12 @@ interface RuntimeProcessResult {
81
81
  }
82
82
  interface RuntimeProcess {
83
83
  readonly completion: Promise<RuntimeProcessResult>;
84
- on(event: "output" | "error" | "exit", listener: (...args: any[]) => void): this;
84
+ /**
85
+ * `output` and `error` carry stdout and stderr; `exit` the code. `rawmode`
86
+ * reports the program turning terminal raw mode on or off, which a terminal
87
+ * needs so that it stops echoing input the program is drawing itself.
88
+ */
89
+ on(event: "output" | "error" | "exit" | "rawmode", listener: (...args: any[]) => void): this;
85
90
  write(data: string): void;
86
91
  kill(signal?: string): void;
87
92
  }
@@ -191,7 +196,7 @@ interface DirEntry {
191
196
  /**
192
197
  * The container's virtual filesystem.
193
198
  *
194
- * Real file content lives in a Nodepod `MemoryVolume`, which is deliberately the
199
+ * Real file content lives in the RuntimePod's `MemoryVolume`, deliberately the
195
200
  * *same* volume the Node.js worker processes see — so a file written by `echo`
196
201
  * is readable by `require('fs')` inside a spawned script, and vice versa.
197
202
  *
@@ -390,6 +395,14 @@ declare class Pipe implements InputStream, OutputStream {
390
395
  isTTY: boolean;
391
396
  /** Set on pipes owned by an outside caller, who may never call `end()`. */
392
397
  interactive: boolean;
398
+ /**
399
+ * Set while the running program has put the terminal in raw mode.
400
+ *
401
+ * A program in raw mode draws its own input — a prompt library redraws the
402
+ * whole line on every keystroke — so the terminal must stop echoing, or
403
+ * every character appears twice.
404
+ */
405
+ rawMode: boolean;
393
406
  columns: number | undefined;
394
407
  rows: number | undefined;
395
408
  get closed(): boolean;
@@ -733,7 +746,7 @@ declare function createContext(init: ContextInit): ExecContext;
733
746
  * The container's network stack.
734
747
  *
735
748
  * There is no real socket layer: HTTP servers started inside the container are
736
- * registered with Nodepod's request proxy, and this module is the routing and
749
+ * registered with the RuntimePod's request proxy, and this module is the routing and
737
750
  * name-resolution layer on top — interfaces for `ip`/`ifconfig`, a hosts file
738
751
  * resolver, a listening-port table for `ss`/`netstat`, and an outbound policy
739
752
  * that decides whether `curl https://example.com` is allowed to touch the real
@@ -792,7 +805,7 @@ declare class NetworkStack {
792
805
  registerListener(port: number, info: Omit<ListeningPort, "port" | "since">): void;
793
806
  unregisterListener(port: number): void;
794
807
  listening(): ListeningPort[];
795
- /** Ports Nodepod's proxy has registered for this instance. */
808
+ /** Ports the pod's proxy has registered for this instance. */
796
809
  private knownPodPorts;
797
810
  /** True when something inside the container answers on `port`. */
798
811
  isPortOpen(port: number, timeoutMs?: number): Promise<boolean>;
@@ -1719,7 +1732,7 @@ declare class Container {
1719
1732
  /**
1720
1733
  * Deliver a request whose body is bytes, without letting them become text.
1721
1734
  *
1722
- * Nodepod's public `request()` runs the body through `toString("utf8")` on
1735
+ * A RuntimePod's public `request()` may run the body through `toString("utf8")` on
1723
1736
  * its way in, so anything above `0x7f` is replaced: a five-byte payload
1724
1737
  * containing `0x89` and `0xff` arrives as nine. That silently destroys every
1725
1738
  * upload — an image or a video reaches the server the wrong size and no
@@ -1759,7 +1772,7 @@ declare class Container {
1759
1772
  get cwd(): string;
1760
1773
  get env(): Record<string, string>;
1761
1774
  private assertActive;
1762
- /** Tear down every process and release the Nodepod instance. */
1775
+ /** Tear down every process and release the runtime pod. */
1763
1776
  dispose(): void;
1764
1777
  get isDisposed(): boolean;
1765
1778
  }
@@ -2101,17 +2114,16 @@ interface RootfsOptions {
2101
2114
  declare function buildRootfs(vfs: Vfs, opts?: RootfsOptions): void;
2102
2115
 
2103
2116
  /**
2104
- * The Node.js runtime, backed by Nodepod.
2117
+ * The Node.js command adapter, backed by the clean-room RuntimePod.
2105
2118
  *
2106
- * Nodepod runs the script in an isolated worker over the *same* memory volume
2119
+ * The pod runs the script over the *same* memory volume
2107
2120
  * the container's filesystem uses, so `require('fs')` inside a script sees the
2108
2121
  * files `echo` and `tar` created, and anything the script writes is visible to
2109
2122
  * the shell afterwards.
2110
2123
  *
2111
- * Two gaps in the underlying `spawn` are papered over here:
2124
+ * Two details of the underlying `spawn` are handled here:
2112
2125
  * - only `node` resolves as a command, so everything else is dispatched by our
2113
- * own kernel rather than being handed to Nodepod's shell (which hangs on an
2114
- * unknown command);
2126
+ * own kernel rather than being handed to the Node process runner;
2115
2127
  * - the worker's stdin has no end-of-stream signal, so when a pipeline feeds
2116
2128
  * a script we materialise stdin as a file and install a real stdin stream
2117
2129
  * over it before the script loads.
@@ -2262,6 +2274,16 @@ declare class CommonJsEngine {
2262
2274
  main: CommonJsModule | null;
2263
2275
  /** `package.json` per directory; resolution reads them constantly. */
2264
2276
  private readonly manifests;
2277
+ private evaluationDepth;
2278
+ /**
2279
+ * Is a module body running synchronously right now?
2280
+ *
2281
+ * `process.exit` unwinds by throwing, and that is only safe while one of
2282
+ * this engine's own frames is on the stack to catch it. Thrown from a later
2283
+ * callback — a stream handler, a timer — it would escape into whichever
2284
+ * library called that callback and surface as an unrelated crash.
2285
+ */
2286
+ get isEvaluating(): boolean;
2265
2287
  constructor(volume: RuntimeVolume, options?: Omit<CommonJsEngineOptions, "volume">);
2266
2288
  /**
2267
2289
  * Evaluate an entry point.
@@ -2426,12 +2448,31 @@ interface CoreModulesOptions {
2426
2448
  spawnChild?: SpawnChild;
2427
2449
  /** File holding the process's standard input, exposed as descriptor 0. */
2428
2450
  stdinPath?: string;
2451
+ /** Keep `process.stdin` open and fed by {@link writeStdin} rather than ending it. */
2452
+ interactiveStdin?: boolean;
2453
+ /** Report the standard streams as a terminal, which is what makes CLIs prompt. */
2454
+ tty?: boolean;
2455
+ /** Called when the program turns raw mode on or off. */
2456
+ onRawMode?: (enabled: boolean) => void;
2429
2457
  }
2430
2458
  /** Build the core-module table injected into each isolated JS worker. */
2431
2459
  declare function createCoreModules(options: CoreModulesOptions): {
2432
2460
  builtins: Record<string, unknown>;
2433
2461
  globals: Record<string, unknown>;
2434
2462
  process: Record<string, any>;
2463
+ /** Deliver a chunk of input to an interactive `process.stdin`. */
2464
+ writeStdin(data: string): void;
2465
+ /** Signal end-of-input to an interactive `process.stdin`. */
2466
+ endStdin(): void;
2467
+ /**
2468
+ * Is the program waiting on input?
2469
+ *
2470
+ * Node keeps a process alive for an open stdin only while something is
2471
+ * actually reading it, and that distinction matters here: a CLI sitting on a
2472
+ * prompt has no timers pending and would otherwise look finished, while a
2473
+ * program that never touches stdin must still be allowed to exit.
2474
+ */
2475
+ readingStdin(): boolean;
2435
2476
  /**
2436
2477
  * How many timers this process still has outstanding.
2437
2478
  *
@@ -2442,6 +2483,8 @@ declare function createCoreModules(options: CoreModulesOptions): {
2442
2483
  * script that has simply finished.
2443
2484
  */
2444
2485
  pendingHandles(): number;
2486
+ /** Active timers which called `unref()` and therefore only merit startup grace. */
2487
+ pendingUnrefed(): number;
2445
2488
  };
2446
2489
 
2447
2490
  interface EsmTransformResult {
@@ -2528,9 +2571,18 @@ declare class LocalRuntimePod implements RuntimePod {
2528
2571
  private readonly aliases;
2529
2572
  private readonly modules;
2530
2573
  private readonly esbuild;
2574
+ private rolldownBinding;
2531
2575
  private constructor();
2532
2576
  static boot(options?: LocalRuntimeOptions): Promise<LocalRuntimePod>;
2533
2577
  spawn(command: string, args?: string[], options?: Record<string, unknown>): Promise<RuntimeProcess>;
2578
+ /**
2579
+ * Rolldown's JavaScript API synchronously requires its compiled binding.
2580
+ * When a project contains Rolldown, preload the official WASI build in the
2581
+ * host and expose it through the module override table before evaluation.
2582
+ * Keeping this demand-driven avoids adding WASM startup cost to ordinary
2583
+ * shells and Node programs.
2584
+ */
2585
+ private prepareRolldown;
2534
2586
  /**
2535
2587
  * Wait until the process has either started serving or genuinely run out of
2536
2588
  * work.
@@ -2565,6 +2617,10 @@ interface RegistryManifest {
2565
2617
  };
2566
2618
  dependencies?: Record<string, string>;
2567
2619
  optionalDependencies?: Record<string, string>;
2620
+ os?: string[];
2621
+ cpu?: string[];
2622
+ libc?: string[];
2623
+ main?: string;
2568
2624
  bin?: string | Record<string, string>;
2569
2625
  }
2570
2626
  interface CleanInstallerOptions {