@scelar/nodepod 1.0.2 → 1.0.4

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.
Files changed (94) hide show
  1. package/dist/__sw__.js +642 -642
  2. package/dist/__tests__/bench/integration.bench.d.ts +1 -0
  3. package/dist/__tests__/bench/memory-volume.bench.d.ts +1 -0
  4. package/dist/__tests__/bench/polyfills.bench.d.ts +1 -0
  5. package/dist/__tests__/bench/script-engine.bench.d.ts +1 -0
  6. package/dist/__tests__/bench/shell.bench.d.ts +1 -0
  7. package/dist/__tests__/bench/syntax-transforms.bench.d.ts +1 -0
  8. package/dist/__tests__/bench/version-resolver.bench.d.ts +1 -0
  9. package/dist/__tests__/buffer.test.d.ts +1 -0
  10. package/dist/__tests__/byte-encoding.test.d.ts +1 -0
  11. package/dist/__tests__/digest.test.d.ts +1 -0
  12. package/dist/__tests__/events.test.d.ts +1 -0
  13. package/dist/__tests__/memory-volume.test.d.ts +1 -0
  14. package/dist/__tests__/path.test.d.ts +1 -0
  15. package/dist/__tests__/process.test.d.ts +1 -0
  16. package/dist/__tests__/script-engine.test.d.ts +1 -0
  17. package/dist/__tests__/shell-builtins.test.d.ts +1 -0
  18. package/dist/__tests__/shell-interpreter.test.d.ts +1 -0
  19. package/dist/__tests__/shell-parser.test.d.ts +1 -0
  20. package/dist/__tests__/stream.test.d.ts +1 -0
  21. package/dist/__tests__/syntax-transforms.test.d.ts +1 -0
  22. package/dist/__tests__/version-resolver.test.d.ts +1 -0
  23. package/dist/{child_process-Dopvyd-E.js → child_process-53fMkug_.js} +4 -4
  24. package/dist/child_process-53fMkug_.js.map +1 -0
  25. package/dist/{child_process-B38qoN6R.cjs → child_process-lxSKECHq.cjs} +5 -5
  26. package/dist/child_process-lxSKECHq.cjs.map +1 -0
  27. package/dist/{index--Qr8LVpQ.js → index-B8lyh_ti.js} +1316 -559
  28. package/dist/index-B8lyh_ti.js.map +1 -0
  29. package/dist/{index-cnitc68U.cjs → index-C-TQIrdG.cjs} +1422 -612
  30. package/dist/index-C-TQIrdG.cjs.map +1 -0
  31. package/dist/index.cjs +1 -1
  32. package/dist/index.mjs +1 -1
  33. package/dist/memory-volume.d.ts +1 -1
  34. package/dist/polyfills/wasi.d.ts +45 -4
  35. package/dist/script-engine.d.ts +2 -0
  36. package/dist/sdk/nodepod.d.ts +4 -3
  37. package/dist/sdk/types.d.ts +6 -0
  38. package/dist/syntax-transforms.d.ts +1 -0
  39. package/dist/threading/process-manager.d.ts +1 -1
  40. package/dist/threading/worker-protocol.d.ts +1 -1
  41. package/package.json +5 -3
  42. package/src/__tests__/bench/integration.bench.ts +117 -0
  43. package/src/__tests__/bench/memory-volume.bench.ts +115 -0
  44. package/src/__tests__/bench/polyfills.bench.ts +147 -0
  45. package/src/__tests__/bench/script-engine.bench.ts +104 -0
  46. package/src/__tests__/bench/shell.bench.ts +101 -0
  47. package/src/__tests__/bench/syntax-transforms.bench.ts +82 -0
  48. package/src/__tests__/bench/version-resolver.bench.ts +95 -0
  49. package/src/__tests__/buffer.test.ts +273 -0
  50. package/src/__tests__/byte-encoding.test.ts +98 -0
  51. package/src/__tests__/digest.test.ts +44 -0
  52. package/src/__tests__/events.test.ts +245 -0
  53. package/src/__tests__/memory-volume.test.ts +443 -0
  54. package/src/__tests__/path.test.ts +181 -0
  55. package/src/__tests__/process.test.ts +129 -0
  56. package/src/__tests__/script-engine.test.ts +229 -0
  57. package/src/__tests__/shell-builtins.test.ts +357 -0
  58. package/src/__tests__/shell-interpreter.test.ts +157 -0
  59. package/src/__tests__/shell-parser.test.ts +204 -0
  60. package/src/__tests__/stream.test.ts +142 -0
  61. package/src/__tests__/syntax-transforms.test.ts +158 -0
  62. package/src/__tests__/version-resolver.test.ts +184 -0
  63. package/src/constants/cdn-urls.ts +18 -18
  64. package/src/helpers/byte-encoding.ts +51 -39
  65. package/src/index.ts +192 -192
  66. package/src/memory-volume.ts +968 -941
  67. package/src/module-transformer.ts +368 -368
  68. package/src/packages/installer.ts +396 -396
  69. package/src/packages/version-resolver.ts +12 -2
  70. package/src/polyfills/buffer.ts +633 -628
  71. package/src/polyfills/child_process.ts +2288 -2288
  72. package/src/polyfills/esbuild.ts +854 -854
  73. package/src/polyfills/events.ts +282 -276
  74. package/src/polyfills/fs.ts +2888 -2888
  75. package/src/polyfills/http.ts +1450 -1449
  76. package/src/polyfills/process.ts +721 -690
  77. package/src/polyfills/readline.ts +692 -692
  78. package/src/polyfills/stream.ts +1620 -1620
  79. package/src/polyfills/tty.ts +71 -71
  80. package/src/polyfills/wasi.ts +1284 -22
  81. package/src/request-proxy.ts +716 -716
  82. package/src/script-engine.ts +465 -146
  83. package/src/sdk/nodepod.ts +525 -509
  84. package/src/sdk/types.ts +7 -0
  85. package/src/syntax-transforms.ts +543 -561
  86. package/src/threading/offload-worker.ts +383 -383
  87. package/src/threading/offload.ts +271 -271
  88. package/src/threading/process-manager.ts +956 -956
  89. package/src/threading/process-worker-entry.ts +858 -854
  90. package/src/threading/worker-protocol.ts +1 -1
  91. package/dist/child_process-B38qoN6R.cjs.map +0 -1
  92. package/dist/child_process-Dopvyd-E.js.map +0 -1
  93. package/dist/index--Qr8LVpQ.js.map +0 -1
  94. package/dist/index-cnitc68U.cjs.map +0 -1
@@ -1,18 +1,18 @@
1
- // Pinned CDN versions and URLs
2
-
3
- export const PINNED_ESBUILD_WASM = '0.20.0';
4
- export const PINNED_ROLLUP_BROWSER = '4.9.0';
5
- export const PINNED_BROTLI_WASM = '3.0.1';
6
- export const PINNED_LIGHTNINGCSS_WASM = '1.31.1';
7
-
8
- export const CDN_ESBUILD_ESM = `https://esm.sh/esbuild-wasm@${PINNED_ESBUILD_WASM}`;
9
- export const CDN_ESBUILD_BINARY = `https://esm.sh/esbuild-wasm@${PINNED_ESBUILD_WASM}/esbuild.wasm`;
10
- export const CDN_ESBUILD_BROWSER = `https://esm.sh/esbuild-wasm@${PINNED_ESBUILD_WASM}/esm/browser.min.js`;
11
- export const CDN_ROLLUP_BROWSER = `https://esm.sh/@rollup/browser@${PINNED_ROLLUP_BROWSER}`;
12
- export const CDN_BROTLI_WASM = `https://esm.sh/brotli-wasm@${PINNED_BROTLI_WASM}`;
13
- export const CDN_LIGHTNINGCSS_WASM = `https://esm.sh/lightningcss-wasm@${PINNED_LIGHTNINGCSS_WASM}`;
14
-
15
- // new Function hides import() from bundler static analysis so CDN URLs work at runtime
16
- // eslint-disable-next-line @typescript-eslint/no-implied-eval
17
- const _dynamicImport = new Function("url", "return import(url)") as (url: string) => Promise<any>;
18
- export { _dynamicImport as cdnImport };
1
+ // Pinned CDN versions and URLs
2
+
3
+ export const PINNED_ESBUILD_WASM = '0.20.0';
4
+ export const PINNED_ROLLUP_BROWSER = '4.9.0';
5
+ export const PINNED_BROTLI_WASM = '3.0.1';
6
+ export const PINNED_LIGHTNINGCSS_WASM = '1.31.1';
7
+
8
+ export const CDN_ESBUILD_ESM = `https://esm.sh/esbuild-wasm@${PINNED_ESBUILD_WASM}`;
9
+ export const CDN_ESBUILD_BINARY = `https://esm.sh/esbuild-wasm@${PINNED_ESBUILD_WASM}/esbuild.wasm`;
10
+ export const CDN_ESBUILD_BROWSER = `https://esm.sh/esbuild-wasm@${PINNED_ESBUILD_WASM}/esm/browser.min.js`;
11
+ export const CDN_ROLLUP_BROWSER = `https://esm.sh/@rollup/browser@${PINNED_ROLLUP_BROWSER}`;
12
+ export const CDN_BROTLI_WASM = `https://esm.sh/brotli-wasm@${PINNED_BROTLI_WASM}`;
13
+ export const CDN_LIGHTNINGCSS_WASM = `https://esm.sh/lightningcss-wasm@${PINNED_LIGHTNINGCSS_WASM}`;
14
+
15
+ // new Function hides import() from bundler static analysis so CDN URLs work at runtime
16
+ // eslint-disable-next-line @typescript-eslint/no-implied-eval
17
+ const _dynamicImport = new Function("url", "return import(url)") as (url: string) => Promise<any>;
18
+ export { _dynamicImport as cdnImport };
@@ -1,39 +1,51 @@
1
- // Chunked to avoid blowing the call stack on large buffers
2
- const SEGMENT_SIZE = 8192;
3
-
4
- export function bytesToBase64(data: Uint8Array): string {
5
- const segments: string[] = [];
6
- for (let offset = 0; offset < data.length; offset += SEGMENT_SIZE) {
7
- segments.push(
8
- String.fromCharCode.apply(null, Array.from(data.subarray(offset, offset + SEGMENT_SIZE)))
9
- );
10
- }
11
- return btoa(segments.join(''));
12
- }
13
-
14
- export function base64ToBytes(encoded: string): Uint8Array {
15
- const raw = atob(encoded);
16
- const result = new Uint8Array(raw.length);
17
- for (let i = 0; i < raw.length; i++) {
18
- result[i] = raw.charCodeAt(i);
19
- }
20
- return result;
21
- }
22
-
23
- export function bytesToHex(data: Uint8Array): string {
24
- const chars = new Array(data.length);
25
- for (let i = 0; i < data.length; i++) {
26
- chars[i] = data[i].toString(16).padStart(2, '0');
27
- }
28
- return chars.join('');
29
- }
30
-
31
- export function bytesToLatin1(data: Uint8Array): string {
32
- const segments: string[] = [];
33
- for (let offset = 0; offset < data.length; offset += SEGMENT_SIZE) {
34
- segments.push(
35
- String.fromCharCode.apply(null, Array.from(data.subarray(offset, offset + SEGMENT_SIZE)))
36
- );
37
- }
38
- return segments.join('');
39
- }
1
+ // Chunked to avoid blowing the call stack on large buffers
2
+ const SEGMENT_SIZE = 8192;
3
+
4
+ export function bytesToBase64(data: Uint8Array): string {
5
+ const segments: string[] = [];
6
+ for (let offset = 0; offset < data.length; offset += SEGMENT_SIZE) {
7
+ const end = Math.min(offset + SEGMENT_SIZE, data.length);
8
+ let chunk = '';
9
+ for (let i = offset; i < end; i++) {
10
+ chunk += String.fromCharCode(data[i]);
11
+ }
12
+ segments.push(chunk);
13
+ }
14
+ return btoa(segments.join(''));
15
+ }
16
+
17
+ export function base64ToBytes(encoded: string): Uint8Array {
18
+ const raw = atob(encoded);
19
+ const result = new Uint8Array(raw.length);
20
+ for (let i = 0; i < raw.length; i++) {
21
+ result[i] = raw.charCodeAt(i);
22
+ }
23
+ return result;
24
+ }
25
+
26
+ // Pre-computed hex lookup table
27
+ const HEX_TABLE: string[] = new Array(256);
28
+ for (let i = 0; i < 256; i++) {
29
+ HEX_TABLE[i] = (i < 16 ? '0' : '') + i.toString(16);
30
+ }
31
+
32
+ export function bytesToHex(data: Uint8Array): string {
33
+ const chars = new Array(data.length);
34
+ for (let i = 0; i < data.length; i++) {
35
+ chars[i] = HEX_TABLE[data[i]];
36
+ }
37
+ return chars.join('');
38
+ }
39
+
40
+ export function bytesToLatin1(data: Uint8Array): string {
41
+ const segments: string[] = [];
42
+ for (let offset = 0; offset < data.length; offset += SEGMENT_SIZE) {
43
+ const end = Math.min(offset + SEGMENT_SIZE, data.length);
44
+ let chunk = '';
45
+ for (let i = offset; i < end; i++) {
46
+ chunk += String.fromCharCode(data[i]);
47
+ }
48
+ segments.push(chunk);
49
+ }
50
+ return segments.join('');
51
+ }
package/src/index.ts CHANGED
@@ -1,192 +1,192 @@
1
- // nodepod - browser-native Node.js runtime environment
2
-
3
- export { MemoryVolume } from "./memory-volume";
4
- export type {
5
- VolumeNode,
6
- FileStat,
7
- FileWatchHandle,
8
- WatchCallback,
9
- WatchEventKind,
10
- SystemError,
11
- } from "./memory-volume";
12
- export { ScriptEngine, executeCode } from "./script-engine";
13
- export type { ModuleRecord, EngineOptions, ResolverFn } from "./script-engine";
14
- export { spawnEngine, WorkerSandbox, IframeSandbox, spawnProcessWorkerEngine, ProcessWorkerAdapter } from "./engine-factory";
15
- export type {
16
- IScriptEngine,
17
- ExecutionOutcome,
18
- SpawnEngineConfig,
19
- EngineConfig,
20
- VolumeSnapshot,
21
- } from "./engine-types";
22
- export {
23
- generateSandboxDeployment,
24
- getSandboxPageHtml,
25
- getSandboxHostingConfig,
26
- SANDBOX_DEPLOYMENT_GUIDE,
27
- } from "./isolation-helpers";
28
- export { buildFileSystemBridge } from "./polyfills/fs";
29
- export type { FsBridge } from "./polyfills/fs";
30
- export { buildProcessEnv } from "./polyfills/process";
31
- export type { ProcessObject, ProcessEnvVars } from "./polyfills/process";
32
- export * as path from "./polyfills/path";
33
- export * as http from "./polyfills/http";
34
- export * as net from "./polyfills/net";
35
- export * as events from "./polyfills/events";
36
- export * as stream from "./polyfills/stream";
37
- export * as url from "./polyfills/url";
38
- export * as querystring from "./polyfills/querystring";
39
- export * as util from "./polyfills/util";
40
- export * as npm from "./packages/installer";
41
- export { DependencyInstaller, install } from "./packages/installer";
42
- export { RequestProxy, getProxyInstance, resetProxy } from "./request-proxy";
43
- export type { ProxyOptions, ServiceWorkerConfig } from "./request-proxy";
44
- export * as chokidar from "./polyfills/chokidar";
45
- export * as ws from "./polyfills/ws";
46
- export * as fsevents from "./polyfills/fsevents";
47
- export * as readdirp from "./polyfills/readdirp";
48
- export * as module from "./polyfills/module";
49
- export * as perf_hooks from "./polyfills/perf_hooks";
50
- export * as worker_threads from "./polyfills/worker_threads";
51
- export * as esbuild from "./polyfills/esbuild";
52
- export * as rollup from "./polyfills/rollup";
53
- export * as assert from "./polyfills/assert";
54
-
55
- import { MemoryVolume } from "./memory-volume";
56
- import { ScriptEngine, EngineOptions } from "./script-engine";
57
- import { DependencyInstaller } from "./packages/installer";
58
- import { RequestProxy, getProxyInstance } from "./request-proxy";
59
- // lazy-load child_process to avoid pulling in the shell at module load time
60
- let _shellMod: typeof import("./polyfills/child_process") | null = null;
61
- async function getShellMod() {
62
- if (!_shellMod) _shellMod = await import("./polyfills/child_process");
63
- return _shellMod;
64
- }
65
-
66
- export interface CommandResult {
67
- stdout: string;
68
- stderr: string;
69
- exitCode: number;
70
- }
71
-
72
- export interface CommandOptions {
73
- cwd?: string;
74
- onStdout?: (data: string) => void;
75
- onStderr?: (data: string) => void;
76
- signal?: AbortSignal;
77
- }
78
-
79
- export interface WorkspaceConfig extends EngineOptions {
80
- baseUrl?: string;
81
- onServerReady?: (port: number, url: string) => void;
82
- }
83
-
84
- // create a fully-wired workspace (volume + engine + packages + proxy)
85
- export function createWorkspace(config?: WorkspaceConfig): {
86
- volume: MemoryVolume;
87
- engine: ScriptEngine;
88
- packages: DependencyInstaller;
89
- proxy: RequestProxy;
90
- execute: (code: string, filename?: string) => { exports: unknown };
91
- runFile: (filename: string) => { exports: unknown };
92
- run: (command: string, options?: CommandOptions) => Promise<CommandResult>;
93
- sendInput: (data: string) => Promise<void>;
94
- createREPL: () => { eval: (code: string) => unknown };
95
- on: (event: string, listener: (...args: unknown[]) => void) => void;
96
- } {
97
- const volume = new MemoryVolume();
98
- const engine = new ScriptEngine(volume, config);
99
- const packages = new DependencyInstaller(volume);
100
- const proxy = getProxyInstance({
101
- baseUrl: config?.baseUrl,
102
- onServerReady: config?.onServerReady,
103
- });
104
-
105
- // init shell lazily (SDK path uses Nodepod.boot() instead)
106
- getShellMod().then((mod) => mod.initShellExec(volume, { cwd: config?.cwd }));
107
-
108
- return {
109
- volume,
110
- engine,
111
- packages,
112
- proxy,
113
- execute: (code: string, filename?: string) =>
114
- engine.execute(code, filename),
115
- runFile: (filename: string) => engine.runFile(filename),
116
- run: async (
117
- command: string,
118
- runOpts?: CommandOptions,
119
- ): Promise<CommandResult> => {
120
- if (runOpts?.signal?.aborted) {
121
- return { stdout: "", stderr: "", exitCode: 130 };
122
- }
123
-
124
- const shell = await getShellMod();
125
- const hasStreaming =
126
- runOpts?.onStdout || runOpts?.onStderr || runOpts?.signal;
127
- if (hasStreaming) {
128
- shell.setStreamingCallbacks({
129
- onStdout: runOpts?.onStdout,
130
- onStderr: runOpts?.onStderr,
131
- signal: runOpts?.signal,
132
- });
133
- }
134
-
135
- return new Promise((resolve) => {
136
- shell.exec(command, { cwd: runOpts?.cwd }, (error, stdout, stderr) => {
137
- if (hasStreaming) shell.clearStreamingCallbacks();
138
- resolve({
139
- stdout: String(stdout),
140
- stderr: String(stderr),
141
- exitCode: error ? ((error as any).code ?? 1) : 0,
142
- });
143
- });
144
- });
145
- },
146
- sendInput: async (data: string) => {
147
- const shell = await getShellMod();
148
- shell.sendStdin(data);
149
- },
150
- createREPL: () => engine.createREPL(),
151
- on: (event: string, listener: (...args: unknown[]) => void) => {
152
- proxy.on(event, listener);
153
- },
154
- };
155
- }
156
-
157
- export default createWorkspace;
158
-
159
- /* ---- SDK (clean public API) ---- */
160
-
161
- export { Nodepod } from "./sdk/nodepod";
162
- export { NodepodTerminal } from "./sdk/nodepod-terminal";
163
- export { NodepodProcess } from "./sdk/nodepod-process";
164
- export { NodepodFS } from "./sdk/nodepod-fs";
165
- export type {
166
- NodepodOptions,
167
- TerminalOptions,
168
- TerminalTheme,
169
- StatResult,
170
- Snapshot,
171
- SpawnOptions,
172
- } from "./sdk/types";
173
-
174
- /* ---- Threading / Worker Infrastructure ---- */
175
-
176
- export { ProcessManager } from "./threading/process-manager";
177
- export { ProcessHandle } from "./threading/process-handle";
178
- export type { ProcessState } from "./threading/process-handle";
179
- export { VFSBridge } from "./threading/vfs-bridge";
180
- export { WorkerVFS } from "./threading/worker-vfs";
181
- export { SyncChannelController, SyncChannelWorker } from "./threading/sync-channel";
182
- export { SharedVFSController, SharedVFSReader, isSharedArrayBufferAvailable } from "./threading/shared-vfs";
183
- export { createProcessContext, getActiveContext, setActiveContext } from "./threading/process-context";
184
- export type { ProcessContext, ProcessWriter, ProcessReader, OpenFileEntry } from "./threading/process-context";
185
- export type {
186
- VFSBinarySnapshot,
187
- VFSSnapshotEntry,
188
- SpawnConfig,
189
- ProcessInfo,
190
- MainToWorkerMessage,
191
- WorkerToMainMessage,
192
- } from "./threading/worker-protocol";
1
+ // nodepod - browser-native Node.js runtime environment
2
+
3
+ export { MemoryVolume } from "./memory-volume";
4
+ export type {
5
+ VolumeNode,
6
+ FileStat,
7
+ FileWatchHandle,
8
+ WatchCallback,
9
+ WatchEventKind,
10
+ SystemError,
11
+ } from "./memory-volume";
12
+ export { ScriptEngine, executeCode } from "./script-engine";
13
+ export type { ModuleRecord, EngineOptions, ResolverFn } from "./script-engine";
14
+ export { spawnEngine, WorkerSandbox, IframeSandbox, spawnProcessWorkerEngine, ProcessWorkerAdapter } from "./engine-factory";
15
+ export type {
16
+ IScriptEngine,
17
+ ExecutionOutcome,
18
+ SpawnEngineConfig,
19
+ EngineConfig,
20
+ VolumeSnapshot,
21
+ } from "./engine-types";
22
+ export {
23
+ generateSandboxDeployment,
24
+ getSandboxPageHtml,
25
+ getSandboxHostingConfig,
26
+ SANDBOX_DEPLOYMENT_GUIDE,
27
+ } from "./isolation-helpers";
28
+ export { buildFileSystemBridge } from "./polyfills/fs";
29
+ export type { FsBridge } from "./polyfills/fs";
30
+ export { buildProcessEnv } from "./polyfills/process";
31
+ export type { ProcessObject, ProcessEnvVars } from "./polyfills/process";
32
+ export * as path from "./polyfills/path";
33
+ export * as http from "./polyfills/http";
34
+ export * as net from "./polyfills/net";
35
+ export * as events from "./polyfills/events";
36
+ export * as stream from "./polyfills/stream";
37
+ export * as url from "./polyfills/url";
38
+ export * as querystring from "./polyfills/querystring";
39
+ export * as util from "./polyfills/util";
40
+ export * as npm from "./packages/installer";
41
+ export { DependencyInstaller, install } from "./packages/installer";
42
+ export { RequestProxy, getProxyInstance, resetProxy } from "./request-proxy";
43
+ export type { ProxyOptions, ServiceWorkerConfig } from "./request-proxy";
44
+ export * as chokidar from "./polyfills/chokidar";
45
+ export * as ws from "./polyfills/ws";
46
+ export * as fsevents from "./polyfills/fsevents";
47
+ export * as readdirp from "./polyfills/readdirp";
48
+ export * as module from "./polyfills/module";
49
+ export * as perf_hooks from "./polyfills/perf_hooks";
50
+ export * as worker_threads from "./polyfills/worker_threads";
51
+ export * as esbuild from "./polyfills/esbuild";
52
+ export * as rollup from "./polyfills/rollup";
53
+ export * as assert from "./polyfills/assert";
54
+
55
+ import { MemoryVolume } from "./memory-volume";
56
+ import { ScriptEngine, EngineOptions } from "./script-engine";
57
+ import { DependencyInstaller } from "./packages/installer";
58
+ import { RequestProxy, getProxyInstance } from "./request-proxy";
59
+ // lazy-load child_process to avoid pulling in the shell at module load time
60
+ let _shellMod: typeof import("./polyfills/child_process") | null = null;
61
+ async function getShellMod() {
62
+ if (!_shellMod) _shellMod = await import("./polyfills/child_process");
63
+ return _shellMod;
64
+ }
65
+
66
+ export interface CommandResult {
67
+ stdout: string;
68
+ stderr: string;
69
+ exitCode: number;
70
+ }
71
+
72
+ export interface CommandOptions {
73
+ cwd?: string;
74
+ onStdout?: (data: string) => void;
75
+ onStderr?: (data: string) => void;
76
+ signal?: AbortSignal;
77
+ }
78
+
79
+ export interface WorkspaceConfig extends EngineOptions {
80
+ baseUrl?: string;
81
+ onServerReady?: (port: number, url: string) => void;
82
+ }
83
+
84
+ // create a fully-wired workspace (volume + engine + packages + proxy)
85
+ export function createWorkspace(config?: WorkspaceConfig): {
86
+ volume: MemoryVolume;
87
+ engine: ScriptEngine;
88
+ packages: DependencyInstaller;
89
+ proxy: RequestProxy;
90
+ execute: (code: string, filename?: string) => { exports: unknown };
91
+ runFile: (filename: string) => { exports: unknown };
92
+ run: (command: string, options?: CommandOptions) => Promise<CommandResult>;
93
+ sendInput: (data: string) => Promise<void>;
94
+ createREPL: () => { eval: (code: string) => unknown };
95
+ on: (event: string, listener: (...args: unknown[]) => void) => void;
96
+ } {
97
+ const volume = new MemoryVolume();
98
+ const engine = new ScriptEngine(volume, config);
99
+ const packages = new DependencyInstaller(volume);
100
+ const proxy = getProxyInstance({
101
+ baseUrl: config?.baseUrl,
102
+ onServerReady: config?.onServerReady,
103
+ });
104
+
105
+ // init shell lazily (SDK path uses Nodepod.boot() instead)
106
+ getShellMod().then((mod) => mod.initShellExec(volume, { cwd: config?.cwd }));
107
+
108
+ return {
109
+ volume,
110
+ engine,
111
+ packages,
112
+ proxy,
113
+ execute: (code: string, filename?: string) =>
114
+ engine.execute(code, filename),
115
+ runFile: (filename: string) => engine.runFile(filename),
116
+ run: async (
117
+ command: string,
118
+ runOpts?: CommandOptions,
119
+ ): Promise<CommandResult> => {
120
+ if (runOpts?.signal?.aborted) {
121
+ return { stdout: "", stderr: "", exitCode: 130 };
122
+ }
123
+
124
+ const shell = await getShellMod();
125
+ const hasStreaming =
126
+ runOpts?.onStdout || runOpts?.onStderr || runOpts?.signal;
127
+ if (hasStreaming) {
128
+ shell.setStreamingCallbacks({
129
+ onStdout: runOpts?.onStdout,
130
+ onStderr: runOpts?.onStderr,
131
+ signal: runOpts?.signal,
132
+ });
133
+ }
134
+
135
+ return new Promise((resolve) => {
136
+ shell.exec(command, { cwd: runOpts?.cwd }, (error, stdout, stderr) => {
137
+ if (hasStreaming) shell.clearStreamingCallbacks();
138
+ resolve({
139
+ stdout: String(stdout),
140
+ stderr: String(stderr),
141
+ exitCode: error ? ((error as any).code ?? 1) : 0,
142
+ });
143
+ });
144
+ });
145
+ },
146
+ sendInput: async (data: string) => {
147
+ const shell = await getShellMod();
148
+ shell.sendStdin(data);
149
+ },
150
+ createREPL: () => engine.createREPL(),
151
+ on: (event: string, listener: (...args: unknown[]) => void) => {
152
+ proxy.on(event, listener);
153
+ },
154
+ };
155
+ }
156
+
157
+ export default createWorkspace;
158
+
159
+ /* ---- SDK (clean public API) ---- */
160
+
161
+ export { Nodepod } from "./sdk/nodepod";
162
+ export { NodepodTerminal } from "./sdk/nodepod-terminal";
163
+ export { NodepodProcess } from "./sdk/nodepod-process";
164
+ export { NodepodFS } from "./sdk/nodepod-fs";
165
+ export type {
166
+ NodepodOptions,
167
+ TerminalOptions,
168
+ TerminalTheme,
169
+ StatResult,
170
+ Snapshot,
171
+ SpawnOptions,
172
+ } from "./sdk/types";
173
+
174
+ /* ---- Threading / Worker Infrastructure ---- */
175
+
176
+ export { ProcessManager } from "./threading/process-manager";
177
+ export { ProcessHandle } from "./threading/process-handle";
178
+ export type { ProcessState } from "./threading/process-handle";
179
+ export { VFSBridge } from "./threading/vfs-bridge";
180
+ export { WorkerVFS } from "./threading/worker-vfs";
181
+ export { SyncChannelController, SyncChannelWorker } from "./threading/sync-channel";
182
+ export { SharedVFSController, SharedVFSReader, isSharedArrayBufferAvailable } from "./threading/shared-vfs";
183
+ export { createProcessContext, getActiveContext, setActiveContext } from "./threading/process-context";
184
+ export type { ProcessContext, ProcessWriter, ProcessReader, OpenFileEntry } from "./threading/process-context";
185
+ export type {
186
+ VFSBinarySnapshot,
187
+ VFSSnapshotEntry,
188
+ SpawnConfig,
189
+ ProcessInfo,
190
+ MainToWorkerMessage,
191
+ WorkerToMainMessage,
192
+ } from "./threading/worker-protocol";