@scelar/nodepod 1.0.0

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 (134) hide show
  1. package/LICENSE +43 -0
  2. package/README.md +240 -0
  3. package/dist/child_process-BJOMsZje.js +8233 -0
  4. package/dist/child_process-BJOMsZje.js.map +1 -0
  5. package/dist/child_process-Cj8vOcuc.cjs +7434 -0
  6. package/dist/child_process-Cj8vOcuc.cjs.map +1 -0
  7. package/dist/index-Cb1Cgdnd.js +35308 -0
  8. package/dist/index-Cb1Cgdnd.js.map +1 -0
  9. package/dist/index-DsMGS-xc.cjs +37195 -0
  10. package/dist/index-DsMGS-xc.cjs.map +1 -0
  11. package/dist/index.cjs +65 -0
  12. package/dist/index.cjs.map +1 -0
  13. package/dist/index.mjs +59 -0
  14. package/dist/index.mjs.map +1 -0
  15. package/package.json +95 -0
  16. package/src/__tests__/smoke.test.ts +11 -0
  17. package/src/constants/cdn-urls.ts +18 -0
  18. package/src/constants/config.ts +236 -0
  19. package/src/cross-origin.ts +26 -0
  20. package/src/engine-factory.ts +176 -0
  21. package/src/engine-types.ts +56 -0
  22. package/src/helpers/byte-encoding.ts +39 -0
  23. package/src/helpers/digest.ts +9 -0
  24. package/src/helpers/event-loop.ts +96 -0
  25. package/src/helpers/wasm-cache.ts +133 -0
  26. package/src/iframe-sandbox.ts +141 -0
  27. package/src/index.ts +192 -0
  28. package/src/isolation-helpers.ts +148 -0
  29. package/src/memory-volume.ts +941 -0
  30. package/src/module-transformer.ts +368 -0
  31. package/src/packages/archive-extractor.ts +248 -0
  32. package/src/packages/browser-bundler.ts +284 -0
  33. package/src/packages/installer.ts +396 -0
  34. package/src/packages/registry-client.ts +131 -0
  35. package/src/packages/version-resolver.ts +411 -0
  36. package/src/polyfills/assert.ts +384 -0
  37. package/src/polyfills/async_hooks.ts +144 -0
  38. package/src/polyfills/buffer.ts +628 -0
  39. package/src/polyfills/child_process.ts +2288 -0
  40. package/src/polyfills/chokidar.ts +336 -0
  41. package/src/polyfills/cluster.ts +106 -0
  42. package/src/polyfills/console.ts +136 -0
  43. package/src/polyfills/constants.ts +123 -0
  44. package/src/polyfills/crypto.ts +885 -0
  45. package/src/polyfills/dgram.ts +87 -0
  46. package/src/polyfills/diagnostics_channel.ts +76 -0
  47. package/src/polyfills/dns.ts +134 -0
  48. package/src/polyfills/domain.ts +68 -0
  49. package/src/polyfills/esbuild.ts +854 -0
  50. package/src/polyfills/events.ts +276 -0
  51. package/src/polyfills/fs.ts +2888 -0
  52. package/src/polyfills/fsevents.ts +79 -0
  53. package/src/polyfills/http.ts +1449 -0
  54. package/src/polyfills/http2.ts +199 -0
  55. package/src/polyfills/https.ts +76 -0
  56. package/src/polyfills/inspector.ts +62 -0
  57. package/src/polyfills/lightningcss.ts +105 -0
  58. package/src/polyfills/module.ts +191 -0
  59. package/src/polyfills/net.ts +353 -0
  60. package/src/polyfills/os.ts +238 -0
  61. package/src/polyfills/path.ts +206 -0
  62. package/src/polyfills/perf_hooks.ts +102 -0
  63. package/src/polyfills/process.ts +690 -0
  64. package/src/polyfills/punycode.ts +159 -0
  65. package/src/polyfills/querystring.ts +93 -0
  66. package/src/polyfills/quic.ts +118 -0
  67. package/src/polyfills/readdirp.ts +229 -0
  68. package/src/polyfills/readline.ts +692 -0
  69. package/src/polyfills/repl.ts +134 -0
  70. package/src/polyfills/rollup.ts +119 -0
  71. package/src/polyfills/sea.ts +33 -0
  72. package/src/polyfills/sqlite.ts +78 -0
  73. package/src/polyfills/stream.ts +1620 -0
  74. package/src/polyfills/string_decoder.ts +25 -0
  75. package/src/polyfills/tailwindcss-oxide.ts +309 -0
  76. package/src/polyfills/test.ts +197 -0
  77. package/src/polyfills/timers.ts +32 -0
  78. package/src/polyfills/tls.ts +105 -0
  79. package/src/polyfills/trace_events.ts +50 -0
  80. package/src/polyfills/tty.ts +71 -0
  81. package/src/polyfills/url.ts +174 -0
  82. package/src/polyfills/util.ts +559 -0
  83. package/src/polyfills/v8.ts +126 -0
  84. package/src/polyfills/vm.ts +132 -0
  85. package/src/polyfills/volume-registry.ts +15 -0
  86. package/src/polyfills/wasi.ts +44 -0
  87. package/src/polyfills/worker_threads.ts +326 -0
  88. package/src/polyfills/ws.ts +595 -0
  89. package/src/polyfills/zlib.ts +881 -0
  90. package/src/request-proxy.ts +716 -0
  91. package/src/script-engine.ts +3375 -0
  92. package/src/sdk/nodepod-fs.ts +93 -0
  93. package/src/sdk/nodepod-process.ts +86 -0
  94. package/src/sdk/nodepod-terminal.ts +350 -0
  95. package/src/sdk/nodepod.ts +509 -0
  96. package/src/sdk/types.ts +70 -0
  97. package/src/shell/commands/bun.ts +121 -0
  98. package/src/shell/commands/directory.ts +297 -0
  99. package/src/shell/commands/file-ops.ts +525 -0
  100. package/src/shell/commands/git.ts +2142 -0
  101. package/src/shell/commands/node.ts +80 -0
  102. package/src/shell/commands/npm.ts +198 -0
  103. package/src/shell/commands/pm-types.ts +45 -0
  104. package/src/shell/commands/pnpm.ts +82 -0
  105. package/src/shell/commands/search.ts +264 -0
  106. package/src/shell/commands/shell-env.ts +352 -0
  107. package/src/shell/commands/text-processing.ts +1152 -0
  108. package/src/shell/commands/yarn.ts +84 -0
  109. package/src/shell/shell-builtins.ts +19 -0
  110. package/src/shell/shell-helpers.ts +250 -0
  111. package/src/shell/shell-interpreter.ts +514 -0
  112. package/src/shell/shell-parser.ts +429 -0
  113. package/src/shell/shell-types.ts +85 -0
  114. package/src/syntax-transforms.ts +561 -0
  115. package/src/threading/engine-worker.ts +64 -0
  116. package/src/threading/inline-worker.ts +372 -0
  117. package/src/threading/offload-types.ts +112 -0
  118. package/src/threading/offload-worker.ts +383 -0
  119. package/src/threading/offload.ts +271 -0
  120. package/src/threading/process-context.ts +92 -0
  121. package/src/threading/process-handle.ts +275 -0
  122. package/src/threading/process-manager.ts +956 -0
  123. package/src/threading/process-worker-entry.ts +854 -0
  124. package/src/threading/shared-vfs.ts +352 -0
  125. package/src/threading/sync-channel.ts +135 -0
  126. package/src/threading/task-queue.ts +177 -0
  127. package/src/threading/vfs-bridge.ts +231 -0
  128. package/src/threading/worker-pool.ts +233 -0
  129. package/src/threading/worker-protocol.ts +358 -0
  130. package/src/threading/worker-vfs.ts +218 -0
  131. package/src/types/externals.d.ts +38 -0
  132. package/src/types/fs-streams.ts +142 -0
  133. package/src/types/manifest.ts +17 -0
  134. package/src/worker-sandbox.ts +90 -0
@@ -0,0 +1,429 @@
1
+ // Tokenizer + recursive-descent parser.
2
+ // Converts a shell command string into an AST (ListNode).
3
+
4
+ import type {
5
+ Token,
6
+ TokenType,
7
+ ListNode,
8
+ ListEntry,
9
+ ListOperator,
10
+ PipelineNode,
11
+ CommandNode,
12
+ RedirectNode,
13
+ } from "./shell-types";
14
+ import type { MemoryVolume } from "../memory-volume";
15
+
16
+ /* ------------------------------------------------------------------ */
17
+ /* Variable & substitution expansion */
18
+ /* ------------------------------------------------------------------ */
19
+
20
+ // Expand $VAR, ${VAR}, $?, $$, $0, and tilde.
21
+ // Single-quote handling is the caller's job.
22
+ export function expandVariables(
23
+ raw: string,
24
+ env: Record<string, string>,
25
+ lastExit: number,
26
+ ): string {
27
+ let result = "";
28
+ let i = 0;
29
+
30
+ if (raw === "~" || raw.startsWith("~/")) {
31
+ const home = env.HOME || "/home/user";
32
+ return home + raw.slice(1);
33
+ }
34
+
35
+ while (i < raw.length) {
36
+ if (raw[i] === "\\") {
37
+ i++;
38
+ if (i < raw.length) result += raw[i++];
39
+ continue;
40
+ }
41
+
42
+ if (raw[i] === "$") {
43
+ i++;
44
+ if (i >= raw.length) {
45
+ result += "$";
46
+ break;
47
+ }
48
+
49
+ if (raw[i] === "?") {
50
+ result += String(lastExit);
51
+ i++;
52
+ continue;
53
+ }
54
+ if (raw[i] === "$") {
55
+ result += "1"; // stub PID
56
+ i++;
57
+ continue;
58
+ }
59
+ if (raw[i] === "0") {
60
+ result += "nodepod";
61
+ i++;
62
+ continue;
63
+ }
64
+ if (raw[i] === "#") {
65
+ result += "0"; // $# stub
66
+ i++;
67
+ continue;
68
+ }
69
+
70
+ if (raw[i] === "{") {
71
+ i++;
72
+ let name = "";
73
+ while (i < raw.length && raw[i] !== "}" && raw[i] !== ":" && raw[i] !== "-" && raw[i] !== "=") {
74
+ name += raw[i++];
75
+ }
76
+ let defaultVal = "";
77
+ let useDefault = false;
78
+ if (i < raw.length && (raw[i] === ":" || raw[i] === "-")) {
79
+ useDefault = true;
80
+ if (raw[i] === ":") i++;
81
+ if (i < raw.length && (raw[i] === "-" || raw[i] === "=")) i++;
82
+ while (i < raw.length && raw[i] !== "}") {
83
+ defaultVal += raw[i++];
84
+ }
85
+ }
86
+ if (i < raw.length && raw[i] === "}") i++;
87
+
88
+ const val = env[name];
89
+ if (val !== undefined && val !== "") {
90
+ result += val;
91
+ } else if (useDefault) {
92
+ result += defaultVal;
93
+ }
94
+ continue;
95
+ }
96
+
97
+ let name = "";
98
+ while (i < raw.length && /[a-zA-Z0-9_]/.test(raw[i])) {
99
+ name += raw[i++];
100
+ }
101
+ if (name) {
102
+ result += env[name] ?? "";
103
+ } else {
104
+ result += "$";
105
+ }
106
+ continue;
107
+ }
108
+
109
+ result += raw[i++];
110
+ }
111
+
112
+ return result;
113
+ }
114
+
115
+ /* ------------------------------------------------------------------ */
116
+ /* Glob expansion */
117
+ /* ------------------------------------------------------------------ */
118
+
119
+ export function expandGlob(
120
+ pattern: string,
121
+ cwd: string,
122
+ volume: MemoryVolume,
123
+ ): string[] {
124
+ if (!pattern.includes("*") && !pattern.includes("?")) return [pattern];
125
+
126
+ const lastSlash = pattern.lastIndexOf("/");
127
+ let dir: string;
128
+ let filePattern: string;
129
+
130
+ if (lastSlash === -1) {
131
+ dir = cwd;
132
+ filePattern = pattern;
133
+ } else {
134
+ dir = pattern.slice(0, lastSlash) || "/";
135
+ if (!dir.startsWith("/")) dir = `${cwd}/${dir}`.replace(/\/+/g, "/");
136
+ filePattern = pattern.slice(lastSlash + 1);
137
+ }
138
+
139
+ try {
140
+ const entries = volume.readdirSync(dir);
141
+ const regex = globToRegex(filePattern);
142
+ const matches = entries.filter((e) => regex.test(e));
143
+
144
+ if (matches.length === 0) return [pattern];
145
+
146
+ return matches.sort().map((m) =>
147
+ lastSlash === -1 ? m : `${dir}/${m}`.replace(/\/+/g, "/"),
148
+ );
149
+ } catch {
150
+ return [pattern];
151
+ }
152
+ }
153
+
154
+ function globToRegex(pattern: string): RegExp {
155
+ let regex = "^";
156
+ for (const ch of pattern) {
157
+ if (ch === "*") regex += ".*";
158
+ else if (ch === "?") regex += ".";
159
+ else if (".+^${}()|[]\\".includes(ch)) regex += "\\" + ch;
160
+ else regex += ch;
161
+ }
162
+ regex += "$";
163
+ return new RegExp(regex);
164
+ }
165
+
166
+ /* ------------------------------------------------------------------ */
167
+ /* Tokenizer */
168
+ /* ------------------------------------------------------------------ */
169
+
170
+ export function tokenize(
171
+ input: string,
172
+ env: Record<string, string>,
173
+ lastExit: number,
174
+ ): Token[] {
175
+ const tokens: Token[] = [];
176
+ let i = 0;
177
+
178
+ while (i < input.length) {
179
+ if (input[i] === " " || input[i] === "\t") {
180
+ i++;
181
+ continue;
182
+ }
183
+
184
+ if (input[i] === "\n") {
185
+ tokens.push({ type: "newline", value: "\n" });
186
+ i++;
187
+ continue;
188
+ }
189
+
190
+ if (input[i] === "#") {
191
+ while (i < input.length && input[i] !== "\n") i++;
192
+ continue;
193
+ }
194
+
195
+ if (input.slice(i, i + 4) === "2>&1") {
196
+ tokens.push({ type: "redirect-2to1", value: "2>&1" });
197
+ i += 4;
198
+ continue;
199
+ }
200
+
201
+ if (input[i] === ">" && input[i + 1] === ">") {
202
+ tokens.push({ type: "redirect-app", value: ">>" });
203
+ i += 2;
204
+ continue;
205
+ }
206
+
207
+ if (input[i] === ">") {
208
+ tokens.push({ type: "redirect-out", value: ">" });
209
+ i++;
210
+ continue;
211
+ }
212
+
213
+ if (input[i] === "<") {
214
+ tokens.push({ type: "redirect-in", value: "<" });
215
+ i++;
216
+ continue;
217
+ }
218
+
219
+ if (input[i] === "&" && input[i + 1] === "&") {
220
+ tokens.push({ type: "and", value: "&&" });
221
+ i += 2;
222
+ continue;
223
+ }
224
+
225
+ if (input[i] === "|" && input[i + 1] === "|") {
226
+ tokens.push({ type: "or", value: "||" });
227
+ i += 2;
228
+ continue;
229
+ }
230
+
231
+ if (input[i] === "|") {
232
+ tokens.push({ type: "pipe", value: "|" });
233
+ i++;
234
+ continue;
235
+ }
236
+
237
+ if (input[i] === ";") {
238
+ tokens.push({ type: "semi", value: ";" });
239
+ i++;
240
+ continue;
241
+ }
242
+
243
+ let word = "";
244
+ while (i < input.length) {
245
+ const ch = input[i];
246
+
247
+ if (ch === " " || ch === "\t" || ch === "\n") break;
248
+ if (ch === "|" || ch === "&" || ch === ";" || ch === ">" || ch === "<") break;
249
+ if (ch === "2" && input.slice(i, i + 4) === "2>&1") break;
250
+
251
+ if (ch === "\\") {
252
+ i++;
253
+ if (i < input.length) word += input[i++];
254
+ continue;
255
+ }
256
+
257
+ // single quotes: no expansion
258
+ if (ch === "'") {
259
+ i++;
260
+ while (i < input.length && input[i] !== "'") {
261
+ word += input[i++];
262
+ }
263
+ if (i < input.length) i++;
264
+ continue;
265
+ }
266
+
267
+ // double quotes: expand variables
268
+ if (ch === '"') {
269
+ i++;
270
+ let dqContent = "";
271
+ while (i < input.length && input[i] !== '"') {
272
+ if (input[i] === "\\" && i + 1 < input.length) {
273
+ const next = input[i + 1];
274
+ if (next === '"' || next === "\\" || next === "$" || next === "`") {
275
+ dqContent += next;
276
+ i += 2;
277
+ continue;
278
+ }
279
+ }
280
+ dqContent += input[i++];
281
+ }
282
+ if (i < input.length) i++;
283
+ word += expandVariables(dqContent, env, lastExit);
284
+ continue;
285
+ }
286
+
287
+ word += ch;
288
+ i++;
289
+ }
290
+
291
+ if (word.length > 0) {
292
+ const expanded = expandVariables(word, env, lastExit);
293
+ tokens.push({ type: "word", value: expanded });
294
+ }
295
+ }
296
+
297
+ tokens.push({ type: "eof", value: "" });
298
+ return tokens;
299
+ }
300
+
301
+ /* ------------------------------------------------------------------ */
302
+ /* Recursive-descent parser */
303
+ /* ------------------------------------------------------------------ */
304
+
305
+ class Parser {
306
+ private tokens: Token[];
307
+ private pos = 0;
308
+
309
+ constructor(tokens: Token[]) {
310
+ this.tokens = tokens;
311
+ }
312
+
313
+ private peek(): Token {
314
+ return this.tokens[this.pos] ?? { type: "eof", value: "" };
315
+ }
316
+
317
+ private advance(): Token {
318
+ return this.tokens[this.pos++] ?? { type: "eof", value: "" };
319
+ }
320
+
321
+ private skipNewlines(): void {
322
+ while (this.peek().type === "newline") this.advance();
323
+ }
324
+
325
+ parseList(): ListNode {
326
+ this.skipNewlines();
327
+ const entries: ListEntry[] = [];
328
+
329
+ while (this.peek().type !== "eof") {
330
+ this.skipNewlines();
331
+ if (this.peek().type === "eof") break;
332
+
333
+ const pipeline = this.parsePipeline();
334
+ const op = this.peek();
335
+
336
+ if (op.type === "and" || op.type === "or" || op.type === "semi") {
337
+ this.advance();
338
+ const operator: ListOperator =
339
+ op.type === "and" ? "&&" : op.type === "or" ? "||" : ";";
340
+ entries.push({ pipeline, next: operator });
341
+ } else {
342
+ entries.push({ pipeline });
343
+ break;
344
+ }
345
+ }
346
+
347
+ return { kind: "list", entries };
348
+ }
349
+
350
+ private parsePipeline(): PipelineNode {
351
+ const commands: CommandNode[] = [];
352
+ commands.push(this.parseCommand());
353
+
354
+ while (this.peek().type === "pipe") {
355
+ this.advance();
356
+ commands.push(this.parseCommand());
357
+ }
358
+
359
+ return { kind: "pipeline", commands };
360
+ }
361
+
362
+ private parseCommand(): CommandNode {
363
+ const args: string[] = [];
364
+ const redirects: RedirectNode[] = [];
365
+ const assignments: Record<string, string> = {};
366
+
367
+ // leading KEY=value assignments (only before any regular args)
368
+ while (this.peek().type === "word") {
369
+ const val = this.peek().value;
370
+ const eqIdx = val.indexOf("=");
371
+ if (eqIdx > 0 && args.length === 0 && /^[a-zA-Z_]/.test(val)) {
372
+ this.advance();
373
+ assignments[val.slice(0, eqIdx)] = val.slice(eqIdx + 1);
374
+ } else {
375
+ break;
376
+ }
377
+ }
378
+
379
+ while (true) {
380
+ const tok = this.peek();
381
+
382
+ if (tok.type === "word") {
383
+ this.advance();
384
+ args.push(tok.value);
385
+ continue;
386
+ }
387
+
388
+ if (tok.type === "redirect-out" || tok.type === "redirect-app" || tok.type === "redirect-in") {
389
+ this.advance();
390
+ const target = this.peek();
391
+ if (target.type === "word") {
392
+ this.advance();
393
+ const rtype =
394
+ tok.type === "redirect-out" ? "write" as const :
395
+ tok.type === "redirect-app" ? "append" as const :
396
+ "read" as const;
397
+ redirects.push({ type: rtype, target: target.value });
398
+ }
399
+ continue;
400
+ }
401
+
402
+ if (tok.type === "redirect-2to1") {
403
+ this.advance();
404
+ redirects.push({ type: "stderr-to-stdout", target: "" });
405
+ continue;
406
+ }
407
+
408
+ break;
409
+ }
410
+
411
+ return { kind: "command", args, redirects, assignments };
412
+ }
413
+ }
414
+
415
+ /* ------------------------------------------------------------------ */
416
+ /* Public API */
417
+ /* ------------------------------------------------------------------ */
418
+
419
+ // Glob expansion happens in the interpreter (needs volume access),
420
+ // but variable expansion and quoting are done here at tokenize time.
421
+ export function parse(
422
+ input: string,
423
+ env: Record<string, string>,
424
+ lastExit: number = 0,
425
+ ): ListNode {
426
+ const tokens = tokenize(input, env, lastExit);
427
+ const parser = new Parser(tokens);
428
+ return parser.parseList();
429
+ }
@@ -0,0 +1,85 @@
1
+ import type { MemoryVolume } from "../memory-volume";
2
+
3
+ /* ------------------------------------------------------------------ */
4
+ /* Result & context */
5
+ /* ------------------------------------------------------------------ */
6
+
7
+ export interface ShellResult {
8
+ stdout: string;
9
+ stderr: string;
10
+ exitCode: number;
11
+ }
12
+
13
+ export interface ShellContext {
14
+ cwd: string;
15
+ env: Record<string, string>;
16
+ volume: MemoryVolume;
17
+ // run a sub-command through the shell (used by npm run, etc.)
18
+ exec: (cmd: string, opts?: { cwd?: string; env?: Record<string, string> }) => Promise<ShellResult>;
19
+ }
20
+
21
+ export interface ShellCommand {
22
+ name: string;
23
+ execute(args: string[], ctx: ShellContext): Promise<ShellResult>;
24
+ }
25
+
26
+ /* ------------------------------------------------------------------ */
27
+ /* AST nodes */
28
+ /* ------------------------------------------------------------------ */
29
+
30
+ export interface RedirectNode {
31
+ type: "write" | "append" | "read" | "stderr-to-stdout";
32
+ target: string; // file path (empty for 2>&1)
33
+ }
34
+
35
+ export interface CommandNode {
36
+ kind: "command";
37
+ args: string[];
38
+ redirects: RedirectNode[];
39
+ assignments: Record<string, string>; // KEY=value before the command
40
+ }
41
+
42
+ export interface PipelineNode {
43
+ kind: "pipeline";
44
+ commands: CommandNode[];
45
+ }
46
+
47
+ export type ListOperator = "&&" | "||" | ";";
48
+
49
+ export interface ListEntry {
50
+ pipeline: PipelineNode;
51
+ next?: ListOperator; // operator after this pipeline (undefined for last)
52
+ }
53
+
54
+ export interface ListNode {
55
+ kind: "list";
56
+ entries: ListEntry[];
57
+ }
58
+
59
+ /* ------------------------------------------------------------------ */
60
+ /* Token types */
61
+ /* ------------------------------------------------------------------ */
62
+
63
+ export type TokenType =
64
+ | "word"
65
+ | "pipe" // |
66
+ | "and" // &&
67
+ | "or" // ||
68
+ | "semi" // ;
69
+ | "redirect-out" // >
70
+ | "redirect-app" // >>
71
+ | "redirect-in" // <
72
+ | "redirect-2to1" // 2>&1
73
+ | "newline"
74
+ | "eof";
75
+
76
+ export interface Token {
77
+ type: TokenType;
78
+ value: string;
79
+ }
80
+
81
+ export type BuiltinFn = (
82
+ args: string[],
83
+ ctx: ShellContext,
84
+ stdin?: string,
85
+ ) => Promise<ShellResult> | ShellResult;