shell-dsl 0.0.2 → 0.0.3
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/README.md +6 -2
- package/dist/cjs/package.json +1 -1
- package/dist/cjs/src/interpreter/interpreter.cjs +61 -1
- package/dist/cjs/src/interpreter/interpreter.cjs.map +3 -3
- package/dist/cjs/src/io/stdout.cjs +47 -2
- package/dist/cjs/src/io/stdout.cjs.map +3 -3
- package/dist/cjs/src/shell-dsl.cjs +28 -3
- package/dist/cjs/src/shell-dsl.cjs.map +3 -3
- package/dist/cjs/src/types.cjs +5 -1
- package/dist/cjs/src/types.cjs.map +3 -3
- package/dist/mjs/package.json +1 -1
- package/dist/mjs/src/interpreter/interpreter.mjs +62 -2
- package/dist/mjs/src/interpreter/interpreter.mjs.map +3 -3
- package/dist/mjs/src/io/stdout.mjs +47 -2
- package/dist/mjs/src/io/stdout.mjs.map +3 -3
- package/dist/mjs/src/shell-dsl.mjs +29 -4
- package/dist/mjs/src/shell-dsl.mjs.map +3 -3
- package/dist/mjs/src/types.mjs +5 -1
- package/dist/mjs/src/types.mjs.map +3 -3
- package/dist/types/commands/cp.d.ts +2 -0
- package/dist/types/commands/index.d.ts +4 -0
- package/dist/types/commands/mv.d.ts +2 -0
- package/dist/types/commands/tee.d.ts +2 -0
- package/dist/types/commands/touch.d.ts +2 -0
- package/dist/types/src/interpreter/interpreter.d.ts +5 -1
- package/dist/types/src/io/stdout.d.ts +13 -0
- package/dist/types/src/shell-dsl.d.ts +1 -0
- package/dist/types/src/types.d.ts +5 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -440,14 +440,14 @@ import { builtinCommands } from "shell-dsl/commands";
|
|
|
440
440
|
Or import individually:
|
|
441
441
|
|
|
442
442
|
```ts
|
|
443
|
-
import { echo, cat, grep, wc } from "shell-dsl/commands";
|
|
443
|
+
import { echo, cat, grep, wc, cp, mv, touch, tee } from "shell-dsl/commands";
|
|
444
444
|
```
|
|
445
445
|
|
|
446
446
|
| Command | Description |
|
|
447
447
|
|---------|-------------|
|
|
448
448
|
| `echo` | Print arguments to stdout |
|
|
449
449
|
| `cat` | Concatenate files or stdin to stdout |
|
|
450
|
-
| `grep` |
|
|
450
|
+
| `grep` | Linux-compatible pattern search |
|
|
451
451
|
| `wc` | Count lines, words, or characters (`-l`, `-w`, `-c`) |
|
|
452
452
|
| `head` | Output first lines (`-n`) |
|
|
453
453
|
| `tail` | Output last lines (`-n`) |
|
|
@@ -457,6 +457,10 @@ import { echo, cat, grep, wc } from "shell-dsl/commands";
|
|
|
457
457
|
| `ls` | List directory contents |
|
|
458
458
|
| `mkdir` | Create directories (`-p` parents) |
|
|
459
459
|
| `rm` | Remove files/directories (`-r` recursive, `-f` force) |
|
|
460
|
+
| `cp` | Copy files/directories (`-r` recursive, `-n` no-clobber) |
|
|
461
|
+
| `mv` | Move/rename files/directories (`-n` no-clobber) |
|
|
462
|
+
| `touch` | Create empty files or update timestamps (`-c` no-create) |
|
|
463
|
+
| `tee` | Duplicate stdin to stdout and files (`-a` append) |
|
|
460
464
|
| `test` / `[` | File and string tests (`-f`, `-d`, `-e`, `-z`, `-n`, `=`, `!=`) |
|
|
461
465
|
| `true` | Exit with code 0 |
|
|
462
466
|
| `false` | Exit with code 1 |
|
package/dist/cjs/package.json
CHANGED
|
@@ -41,11 +41,13 @@ class Interpreter {
|
|
|
41
41
|
cwd;
|
|
42
42
|
env;
|
|
43
43
|
commands;
|
|
44
|
+
redirectObjects;
|
|
44
45
|
constructor(options) {
|
|
45
46
|
this.fs = options.fs;
|
|
46
47
|
this.cwd = options.cwd;
|
|
47
48
|
this.env = { ...options.env };
|
|
48
49
|
this.commands = options.commands;
|
|
50
|
+
this.redirectObjects = options.redirectObjects ?? {};
|
|
49
51
|
}
|
|
50
52
|
async execute(ast) {
|
|
51
53
|
const stdout = import_stdout.createStdout();
|
|
@@ -159,6 +161,9 @@ class Interpreter {
|
|
|
159
161
|
}
|
|
160
162
|
async handleRedirect(redirect, stdin, stdout, stderr) {
|
|
161
163
|
const target = await this.evaluateNode(redirect.target);
|
|
164
|
+
if (target in this.redirectObjects) {
|
|
165
|
+
return this.handleObjectRedirect(redirect.mode, this.redirectObjects[target], stdin, stdout, stderr);
|
|
166
|
+
}
|
|
162
167
|
switch (redirect.mode) {
|
|
163
168
|
case "<": {
|
|
164
169
|
const path = this.fs.resolve(this.cwd, target);
|
|
@@ -233,6 +238,61 @@ class Interpreter {
|
|
|
233
238
|
return { stdin, stdout, stderr };
|
|
234
239
|
}
|
|
235
240
|
}
|
|
241
|
+
async handleObjectRedirect(mode, obj, stdin, stdout, stderr) {
|
|
242
|
+
switch (mode) {
|
|
243
|
+
case "<": {
|
|
244
|
+
const data = await this.readFromObject(obj);
|
|
245
|
+
return {
|
|
246
|
+
stdin: async function* () {
|
|
247
|
+
yield data;
|
|
248
|
+
}(),
|
|
249
|
+
stdout,
|
|
250
|
+
stderr
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
case ">":
|
|
254
|
+
case ">>": {
|
|
255
|
+
if (!Buffer.isBuffer(obj)) {
|
|
256
|
+
throw new Error("Output redirection only supports Buffer targets");
|
|
257
|
+
}
|
|
258
|
+
const collector = import_stdout.createBufferTargetCollector(obj);
|
|
259
|
+
return { stdin, stdout: collector, stderr };
|
|
260
|
+
}
|
|
261
|
+
case "2>":
|
|
262
|
+
case "2>>": {
|
|
263
|
+
if (!Buffer.isBuffer(obj)) {
|
|
264
|
+
throw new Error("Stderr redirection only supports Buffer targets");
|
|
265
|
+
}
|
|
266
|
+
const collector = import_stdout.createBufferTargetCollector(obj);
|
|
267
|
+
return { stdin, stdout, stderr: collector };
|
|
268
|
+
}
|
|
269
|
+
case "&>":
|
|
270
|
+
case "&>>": {
|
|
271
|
+
if (!Buffer.isBuffer(obj)) {
|
|
272
|
+
throw new Error("Combined redirection only supports Buffer targets");
|
|
273
|
+
}
|
|
274
|
+
const collector = import_stdout.createBufferTargetCollector(obj);
|
|
275
|
+
return { stdin, stdout: collector, stderr: collector };
|
|
276
|
+
}
|
|
277
|
+
default:
|
|
278
|
+
return { stdin, stdout, stderr };
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
async readFromObject(obj) {
|
|
282
|
+
if (Buffer.isBuffer(obj)) {
|
|
283
|
+
return new Uint8Array(obj);
|
|
284
|
+
}
|
|
285
|
+
if (obj instanceof Blob) {
|
|
286
|
+
return new Uint8Array(await obj.arrayBuffer());
|
|
287
|
+
}
|
|
288
|
+
if (obj instanceof Response) {
|
|
289
|
+
return new Uint8Array(await obj.arrayBuffer());
|
|
290
|
+
}
|
|
291
|
+
if (typeof obj === "string") {
|
|
292
|
+
return new TextEncoder().encode(obj);
|
|
293
|
+
}
|
|
294
|
+
throw new Error("Unsupported redirect object type");
|
|
295
|
+
}
|
|
236
296
|
async executePipeline(commands, stdinSource, stdout, stderr) {
|
|
237
297
|
if (commands.length === 0)
|
|
238
298
|
return 0;
|
|
@@ -318,4 +378,4 @@ class Interpreter {
|
|
|
318
378
|
}
|
|
319
379
|
}
|
|
320
380
|
|
|
321
|
-
//# debugId=
|
|
381
|
+
//# debugId=672068A850D821FB64756E2164756E21
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/interpreter/interpreter.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"import type { ASTNode, CommandNode, Redirect } from \"../parser/ast.cjs\";\nimport type { Command, VirtualFS, ExecResult, OutputCollector } from \"../types.cjs\";\nimport { createCommandContext } from \"./context.cjs\";\nimport { createStdin } from \"../io/stdin.cjs\";\nimport { createStdout, createStderr, createPipe, PipeBuffer } from \"../io/stdout.cjs\";\nimport { Lexer } from \"../lexer/lexer.cjs\";\nimport { Parser } from \"../parser/parser.cjs\";\n\nexport interface InterpreterOptions {\n fs: VirtualFS;\n cwd: string;\n env: Record<string, string>;\n commands: Record<string, Command>;\n}\n\nexport class Interpreter {\n private fs: VirtualFS;\n private cwd: string;\n private env: Record<string, string>;\n private commands: Record<string, Command>;\n\n constructor(options: InterpreterOptions) {\n this.fs = options.fs;\n this.cwd = options.cwd;\n this.env = { ...options.env };\n this.commands = options.commands;\n }\n\n async execute(ast: ASTNode): Promise<ExecResult> {\n const stdout = createStdout();\n const stderr = createStderr();\n\n const exitCode = await this.executeNode(ast, null, stdout, stderr);\n\n stdout.close();\n stderr.close();\n\n return {\n stdout: await stdout.collect(),\n stderr: await stderr.collect(),\n exitCode,\n };\n }\n\n private async executeNode(\n node: ASTNode,\n stdinSource: AsyncIterable<Uint8Array> | null,\n stdout: OutputCollector,\n stderr: OutputCollector\n ): Promise<number> {\n switch (node.type) {\n case \"command\":\n return this.executeCommand(node, stdinSource, stdout, stderr);\n case \"pipeline\":\n return this.executePipeline(node.commands, stdinSource, stdout, stderr);\n case \"sequence\":\n return this.executeSequence(node.commands, stdinSource, stdout, stderr);\n case \"and\":\n return this.executeAnd(node.left, node.right, stdinSource, stdout, stderr);\n case \"or\":\n return this.executeOr(node.left, node.right, stdinSource, stdout, stderr);\n default:\n throw new Error(`Cannot execute node type: ${node.type}`);\n }\n }\n\n private async executeCommand(\n node: CommandNode,\n stdinSource: AsyncIterable<Uint8Array> | null,\n stdout: OutputCollector,\n stderr: OutputCollector\n ): Promise<number> {\n // Create local env with assignments\n const localEnv = { ...this.env };\n for (const assignment of node.assignments) {\n localEnv[assignment.name] = await this.evaluateNode(assignment.value);\n }\n\n // If there's no command name but there are assignments, just update env\n const name = await this.evaluateNode(node.name);\n if (name === \"\" && node.assignments.length > 0) {\n for (const assignment of node.assignments) {\n this.env[assignment.name] = await this.evaluateNode(assignment.value);\n }\n return 0;\n }\n\n // Evaluate arguments using localEnv for scoped variable expansion\n const args: string[] = [];\n for (const arg of node.args) {\n const evaluated = await this.evaluateNode(arg, localEnv);\n // Glob expansion returns multiple values\n if (arg.type === \"glob\") {\n const matches = await this.fs.glob(evaluated, { cwd: this.cwd });\n if (matches.length > 0) {\n args.push(...matches);\n } else {\n // No matches - use pattern as-is\n args.push(evaluated);\n }\n } else {\n args.push(evaluated);\n }\n }\n\n // Handle redirects\n let actualStdin = stdinSource;\n let actualStdout: OutputCollector = stdout;\n let actualStderr: OutputCollector = stderr;\n let stderrToStdout = false;\n let stdoutToStderr = false;\n const fileWritePromises: Promise<void>[] = [];\n\n for (const redirect of node.redirects) {\n const result = await this.handleRedirect(\n redirect,\n actualStdin,\n actualStdout,\n actualStderr\n );\n actualStdin = result.stdin;\n actualStdout = result.stdout;\n actualStderr = result.stderr;\n stderrToStdout = result.stderrToStdout || stderrToStdout;\n stdoutToStderr = result.stdoutToStderr || stdoutToStderr;\n if (result.fileWritePromise) {\n fileWritePromises.push(result.fileWritePromise);\n }\n }\n\n // Handle stderr->stdout redirect\n if (stderrToStdout) {\n actualStderr = actualStdout;\n }\n if (stdoutToStderr) {\n actualStdout = actualStderr;\n }\n\n // Look up command\n const command = this.commands[name];\n if (!command) {\n await stderr.writeText(`${name}: command not found\\n`);\n return 127;\n }\n\n // Create context and execute\n const ctx = createCommandContext({\n args,\n stdin: createStdin(actualStdin),\n stdout: actualStdout,\n stderr: actualStderr,\n fs: this.fs,\n cwd: this.cwd,\n env: localEnv,\n });\n\n let exitCode: number;\n try {\n exitCode = await command(ctx);\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n await stderr.writeText(`${name}: ${message}\\n`);\n exitCode = 1;\n }\n\n // Close redirect collectors and wait for file writes\n if (actualStdout !== stdout) {\n actualStdout.close();\n }\n if (actualStderr !== stderr && actualStderr !== actualStdout) {\n actualStderr.close();\n }\n\n // Wait for all file write operations to complete\n await Promise.all(fileWritePromises);\n\n return exitCode;\n }\n\n private async handleRedirect(\n redirect: Redirect,\n stdin: AsyncIterable<Uint8Array> | null,\n stdout: OutputCollector,\n stderr: OutputCollector\n ): Promise<{\n stdin: AsyncIterable<Uint8Array> | null;\n stdout: OutputCollector;\n stderr: OutputCollector;\n stderrToStdout?: boolean;\n stdoutToStderr?: boolean;\n fileWritePromise?: Promise<void>;\n }> {\n const target = await this.evaluateNode(redirect.target);\n\n switch (redirect.mode) {\n case \"<\": {\n // Input redirect\n const path = this.fs.resolve(this.cwd, target);\n const content = await this.fs.readFile(path);\n return {\n stdin: (async function* () {\n yield new Uint8Array(content);\n })(),\n stdout,\n stderr,\n };\n }\n case \">\": {\n // Output redirect (overwrite)\n const path = this.fs.resolve(this.cwd, target);\n const collector = createStdout();\n const fileWritePromise = (async () => {\n const data = await collector.collect();\n await this.fs.writeFile(path, data);\n })();\n return { stdin, stdout: collector, stderr, fileWritePromise };\n }\n case \">>\": {\n // Output redirect (append)\n const path = this.fs.resolve(this.cwd, target);\n const collector = createStdout();\n const fileWritePromise = (async () => {\n const data = await collector.collect();\n await this.fs.appendFile(path, data);\n })();\n return { stdin, stdout: collector, stderr, fileWritePromise };\n }\n case \"2>\": {\n // Stderr redirect (overwrite)\n const path = this.fs.resolve(this.cwd, target);\n const collector = createStderr();\n const fileWritePromise = (async () => {\n const data = await collector.collect();\n await this.fs.writeFile(path, data);\n })();\n return { stdin, stdout, stderr: collector, fileWritePromise };\n }\n case \"2>>\": {\n // Stderr redirect (append)\n const path = this.fs.resolve(this.cwd, target);\n const collector = createStderr();\n const fileWritePromise = (async () => {\n const data = await collector.collect();\n await this.fs.appendFile(path, data);\n })();\n return { stdin, stdout, stderr: collector, fileWritePromise };\n }\n case \"&>\": {\n // Both to file (overwrite)\n const path = this.fs.resolve(this.cwd, target);\n const collector = createStdout();\n const fileWritePromise = (async () => {\n const data = await collector.collect();\n await this.fs.writeFile(path, data);\n })();\n return { stdin, stdout: collector, stderr: collector, fileWritePromise };\n }\n case \"&>>\": {\n // Both to file (append)\n const path = this.fs.resolve(this.cwd, target);\n const collector = createStdout();\n const fileWritePromise = (async () => {\n const data = await collector.collect();\n await this.fs.appendFile(path, data);\n })();\n return { stdin, stdout: collector, stderr: collector, fileWritePromise };\n }\n case \"2>&1\":\n return { stdin, stdout, stderr, stderrToStdout: true };\n case \"1>&2\":\n return { stdin, stdout, stderr, stdoutToStderr: true };\n default:\n return { stdin, stdout, stderr };\n }\n }\n\n private async executePipeline(\n commands: ASTNode[],\n stdinSource: AsyncIterable<Uint8Array> | null,\n stdout: OutputCollector,\n stderr: OutputCollector\n ): Promise<number> {\n if (commands.length === 0) return 0;\n if (commands.length === 1) {\n return this.executeNode(commands[0]!, stdinSource, stdout, stderr);\n }\n\n // Create pipes between commands\n const pipes: PipeBuffer[] = [];\n for (let i = 0; i < commands.length - 1; i++) {\n pipes.push(createPipe());\n }\n\n // Execute all commands concurrently\n const promises: Promise<number>[] = [];\n\n for (let i = 0; i < commands.length; i++) {\n const command = commands[i]!;\n const cmdStdin = i === 0 ? stdinSource : pipes[i - 1]!.getReadableStream();\n const cmdStdout = i === commands.length - 1 ? stdout : pipes[i]!;\n\n promises.push(\n this.executeNode(command, cmdStdin, cmdStdout, stderr).then((code) => {\n // Close pipe when command finishes\n if (i < commands.length - 1) {\n pipes[i]!.close();\n }\n return code;\n })\n );\n }\n\n // Wait for all commands and return last exit code\n const results = await Promise.all(promises);\n return results[results.length - 1]!;\n }\n\n private async executeSequence(\n commands: ASTNode[],\n stdinSource: AsyncIterable<Uint8Array> | null,\n stdout: OutputCollector,\n stderr: OutputCollector\n ): Promise<number> {\n let lastExitCode = 0;\n\n for (const command of commands) {\n lastExitCode = await this.executeNode(command, stdinSource, stdout, stderr);\n }\n\n return lastExitCode;\n }\n\n private async executeAnd(\n left: ASTNode,\n right: ASTNode,\n stdinSource: AsyncIterable<Uint8Array> | null,\n stdout: OutputCollector,\n stderr: OutputCollector\n ): Promise<number> {\n const leftCode = await this.executeNode(left, stdinSource, stdout, stderr);\n if (leftCode !== 0) {\n return leftCode;\n }\n return this.executeNode(right, stdinSource, stdout, stderr);\n }\n\n private async executeOr(\n left: ASTNode,\n right: ASTNode,\n stdinSource: AsyncIterable<Uint8Array> | null,\n stdout: OutputCollector,\n stderr: OutputCollector\n ): Promise<number> {\n const leftCode = await this.executeNode(left, stdinSource, stdout, stderr);\n if (leftCode === 0) {\n return 0;\n }\n return this.executeNode(right, stdinSource, stdout, stderr);\n }\n\n private async evaluateNode(node: ASTNode, localEnv?: Record<string, string>): Promise<string> {\n const env = localEnv ?? this.env;\n switch (node.type) {\n case \"literal\":\n return node.value;\n case \"variable\":\n return env[node.name] ?? \"\";\n case \"glob\":\n return node.pattern;\n case \"concat\": {\n const parts = await Promise.all(node.parts.map((p) => this.evaluateNode(p, localEnv)));\n return parts.join(\"\");\n }\n case \"substitution\": {\n // Execute the command and capture output\n const subStdout = createStdout();\n const subStderr = createStderr();\n await this.executeNode(node.command, null, subStdout, subStderr);\n subStdout.close();\n const output = await subStdout.collect();\n // Trim trailing newlines\n return output.toString(\"utf-8\").replace(/\\n+$/, \"\");\n }\n default:\n throw new Error(`Cannot evaluate node type: ${node.type}`);\n }\n }\n\n setCwd(cwd: string): void {\n this.cwd = cwd;\n }\n\n setEnv(vars: Record<string, string>): void {\n Object.assign(this.env, vars);\n }\n\n getCwd(): string {\n return this.cwd;\n }\n\n getEnv(): Record<string, string> {\n return { ...this.env };\n }\n}\n"
|
|
5
|
+
"import type { ASTNode, CommandNode, Redirect } from \"../parser/ast.cjs\";\nimport type { Command, VirtualFS, ExecResult, OutputCollector, RedirectObjectMap } from \"../types.cjs\";\nimport { createCommandContext } from \"./context.cjs\";\nimport { createStdin } from \"../io/stdin.cjs\";\nimport { createStdout, createStderr, createPipe, PipeBuffer, createBufferTargetCollector } from \"../io/stdout.cjs\";\nimport { Lexer } from \"../lexer/lexer.cjs\";\nimport { Parser } from \"../parser/parser.cjs\";\n\nexport interface InterpreterOptions {\n fs: VirtualFS;\n cwd: string;\n env: Record<string, string>;\n commands: Record<string, Command>;\n redirectObjects?: RedirectObjectMap;\n}\n\nexport class Interpreter {\n private fs: VirtualFS;\n private cwd: string;\n private env: Record<string, string>;\n private commands: Record<string, Command>;\n private redirectObjects: RedirectObjectMap;\n\n constructor(options: InterpreterOptions) {\n this.fs = options.fs;\n this.cwd = options.cwd;\n this.env = { ...options.env };\n this.commands = options.commands;\n this.redirectObjects = options.redirectObjects ?? {};\n }\n\n async execute(ast: ASTNode): Promise<ExecResult> {\n const stdout = createStdout();\n const stderr = createStderr();\n\n const exitCode = await this.executeNode(ast, null, stdout, stderr);\n\n stdout.close();\n stderr.close();\n\n return {\n stdout: await stdout.collect(),\n stderr: await stderr.collect(),\n exitCode,\n };\n }\n\n private async executeNode(\n node: ASTNode,\n stdinSource: AsyncIterable<Uint8Array> | null,\n stdout: OutputCollector,\n stderr: OutputCollector\n ): Promise<number> {\n switch (node.type) {\n case \"command\":\n return this.executeCommand(node, stdinSource, stdout, stderr);\n case \"pipeline\":\n return this.executePipeline(node.commands, stdinSource, stdout, stderr);\n case \"sequence\":\n return this.executeSequence(node.commands, stdinSource, stdout, stderr);\n case \"and\":\n return this.executeAnd(node.left, node.right, stdinSource, stdout, stderr);\n case \"or\":\n return this.executeOr(node.left, node.right, stdinSource, stdout, stderr);\n default:\n throw new Error(`Cannot execute node type: ${node.type}`);\n }\n }\n\n private async executeCommand(\n node: CommandNode,\n stdinSource: AsyncIterable<Uint8Array> | null,\n stdout: OutputCollector,\n stderr: OutputCollector\n ): Promise<number> {\n // Create local env with assignments\n const localEnv = { ...this.env };\n for (const assignment of node.assignments) {\n localEnv[assignment.name] = await this.evaluateNode(assignment.value);\n }\n\n // If there's no command name but there are assignments, just update env\n const name = await this.evaluateNode(node.name);\n if (name === \"\" && node.assignments.length > 0) {\n for (const assignment of node.assignments) {\n this.env[assignment.name] = await this.evaluateNode(assignment.value);\n }\n return 0;\n }\n\n // Evaluate arguments using localEnv for scoped variable expansion\n const args: string[] = [];\n for (const arg of node.args) {\n const evaluated = await this.evaluateNode(arg, localEnv);\n // Glob expansion returns multiple values\n if (arg.type === \"glob\") {\n const matches = await this.fs.glob(evaluated, { cwd: this.cwd });\n if (matches.length > 0) {\n args.push(...matches);\n } else {\n // No matches - use pattern as-is\n args.push(evaluated);\n }\n } else {\n args.push(evaluated);\n }\n }\n\n // Handle redirects\n let actualStdin = stdinSource;\n let actualStdout: OutputCollector = stdout;\n let actualStderr: OutputCollector = stderr;\n let stderrToStdout = false;\n let stdoutToStderr = false;\n const fileWritePromises: Promise<void>[] = [];\n\n for (const redirect of node.redirects) {\n const result = await this.handleRedirect(\n redirect,\n actualStdin,\n actualStdout,\n actualStderr\n );\n actualStdin = result.stdin;\n actualStdout = result.stdout;\n actualStderr = result.stderr;\n stderrToStdout = result.stderrToStdout || stderrToStdout;\n stdoutToStderr = result.stdoutToStderr || stdoutToStderr;\n if (result.fileWritePromise) {\n fileWritePromises.push(result.fileWritePromise);\n }\n }\n\n // Handle stderr->stdout redirect\n if (stderrToStdout) {\n actualStderr = actualStdout;\n }\n if (stdoutToStderr) {\n actualStdout = actualStderr;\n }\n\n // Look up command\n const command = this.commands[name];\n if (!command) {\n await stderr.writeText(`${name}: command not found\\n`);\n return 127;\n }\n\n // Create context and execute\n const ctx = createCommandContext({\n args,\n stdin: createStdin(actualStdin),\n stdout: actualStdout,\n stderr: actualStderr,\n fs: this.fs,\n cwd: this.cwd,\n env: localEnv,\n });\n\n let exitCode: number;\n try {\n exitCode = await command(ctx);\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n await stderr.writeText(`${name}: ${message}\\n`);\n exitCode = 1;\n }\n\n // Close redirect collectors and wait for file writes\n if (actualStdout !== stdout) {\n actualStdout.close();\n }\n if (actualStderr !== stderr && actualStderr !== actualStdout) {\n actualStderr.close();\n }\n\n // Wait for all file write operations to complete\n await Promise.all(fileWritePromises);\n\n return exitCode;\n }\n\n private async handleRedirect(\n redirect: Redirect,\n stdin: AsyncIterable<Uint8Array> | null,\n stdout: OutputCollector,\n stderr: OutputCollector\n ): Promise<{\n stdin: AsyncIterable<Uint8Array> | null;\n stdout: OutputCollector;\n stderr: OutputCollector;\n stderrToStdout?: boolean;\n stdoutToStderr?: boolean;\n fileWritePromise?: Promise<void>;\n }> {\n const target = await this.evaluateNode(redirect.target);\n\n // Check if target is a redirect object marker\n if (target in this.redirectObjects) {\n return this.handleObjectRedirect(redirect.mode, this.redirectObjects[target]!, stdin, stdout, stderr);\n }\n\n switch (redirect.mode) {\n case \"<\": {\n // Input redirect\n const path = this.fs.resolve(this.cwd, target);\n const content = await this.fs.readFile(path);\n return {\n stdin: (async function* () {\n yield new Uint8Array(content);\n })(),\n stdout,\n stderr,\n };\n }\n case \">\": {\n // Output redirect (overwrite)\n const path = this.fs.resolve(this.cwd, target);\n const collector = createStdout();\n const fileWritePromise = (async () => {\n const data = await collector.collect();\n await this.fs.writeFile(path, data);\n })();\n return { stdin, stdout: collector, stderr, fileWritePromise };\n }\n case \">>\": {\n // Output redirect (append)\n const path = this.fs.resolve(this.cwd, target);\n const collector = createStdout();\n const fileWritePromise = (async () => {\n const data = await collector.collect();\n await this.fs.appendFile(path, data);\n })();\n return { stdin, stdout: collector, stderr, fileWritePromise };\n }\n case \"2>\": {\n // Stderr redirect (overwrite)\n const path = this.fs.resolve(this.cwd, target);\n const collector = createStderr();\n const fileWritePromise = (async () => {\n const data = await collector.collect();\n await this.fs.writeFile(path, data);\n })();\n return { stdin, stdout, stderr: collector, fileWritePromise };\n }\n case \"2>>\": {\n // Stderr redirect (append)\n const path = this.fs.resolve(this.cwd, target);\n const collector = createStderr();\n const fileWritePromise = (async () => {\n const data = await collector.collect();\n await this.fs.appendFile(path, data);\n })();\n return { stdin, stdout, stderr: collector, fileWritePromise };\n }\n case \"&>\": {\n // Both to file (overwrite)\n const path = this.fs.resolve(this.cwd, target);\n const collector = createStdout();\n const fileWritePromise = (async () => {\n const data = await collector.collect();\n await this.fs.writeFile(path, data);\n })();\n return { stdin, stdout: collector, stderr: collector, fileWritePromise };\n }\n case \"&>>\": {\n // Both to file (append)\n const path = this.fs.resolve(this.cwd, target);\n const collector = createStdout();\n const fileWritePromise = (async () => {\n const data = await collector.collect();\n await this.fs.appendFile(path, data);\n })();\n return { stdin, stdout: collector, stderr: collector, fileWritePromise };\n }\n case \"2>&1\":\n return { stdin, stdout, stderr, stderrToStdout: true };\n case \"1>&2\":\n return { stdin, stdout, stderr, stdoutToStderr: true };\n default:\n return { stdin, stdout, stderr };\n }\n }\n\n private async handleObjectRedirect(\n mode: string,\n obj: Buffer | Blob | Response | string,\n stdin: AsyncIterable<Uint8Array> | null,\n stdout: OutputCollector,\n stderr: OutputCollector\n ): Promise<{\n stdin: AsyncIterable<Uint8Array> | null;\n stdout: OutputCollector;\n stderr: OutputCollector;\n stderrToStdout?: boolean;\n stdoutToStderr?: boolean;\n fileWritePromise?: Promise<void>;\n }> {\n switch (mode) {\n case \"<\": {\n // Input from object\n const data = await this.readFromObject(obj);\n return {\n stdin: (async function* () {\n yield data;\n })(),\n stdout,\n stderr,\n };\n }\n case \">\":\n case \">>\": {\n // Output to object (only Buffer supported)\n if (!Buffer.isBuffer(obj)) {\n throw new Error(\"Output redirection only supports Buffer targets\");\n }\n const collector = createBufferTargetCollector(obj);\n return { stdin, stdout: collector, stderr };\n }\n case \"2>\":\n case \"2>>\": {\n // Stderr to object (only Buffer supported)\n if (!Buffer.isBuffer(obj)) {\n throw new Error(\"Stderr redirection only supports Buffer targets\");\n }\n const collector = createBufferTargetCollector(obj);\n return { stdin, stdout, stderr: collector };\n }\n case \"&>\":\n case \"&>>\": {\n // Both to object (only Buffer supported)\n if (!Buffer.isBuffer(obj)) {\n throw new Error(\"Combined redirection only supports Buffer targets\");\n }\n const collector = createBufferTargetCollector(obj);\n return { stdin, stdout: collector, stderr: collector };\n }\n default:\n return { stdin, stdout, stderr };\n }\n }\n\n private async readFromObject(obj: Buffer | Blob | Response | string): Promise<Uint8Array> {\n if (Buffer.isBuffer(obj)) {\n return new Uint8Array(obj);\n }\n if (obj instanceof Blob) {\n return new Uint8Array(await obj.arrayBuffer());\n }\n if (obj instanceof Response) {\n return new Uint8Array(await obj.arrayBuffer());\n }\n if (typeof obj === \"string\") {\n return new TextEncoder().encode(obj);\n }\n throw new Error(\"Unsupported redirect object type\");\n }\n\n private async executePipeline(\n commands: ASTNode[],\n stdinSource: AsyncIterable<Uint8Array> | null,\n stdout: OutputCollector,\n stderr: OutputCollector\n ): Promise<number> {\n if (commands.length === 0) return 0;\n if (commands.length === 1) {\n return this.executeNode(commands[0]!, stdinSource, stdout, stderr);\n }\n\n // Create pipes between commands\n const pipes: PipeBuffer[] = [];\n for (let i = 0; i < commands.length - 1; i++) {\n pipes.push(createPipe());\n }\n\n // Execute all commands concurrently\n const promises: Promise<number>[] = [];\n\n for (let i = 0; i < commands.length; i++) {\n const command = commands[i]!;\n const cmdStdin = i === 0 ? stdinSource : pipes[i - 1]!.getReadableStream();\n const cmdStdout = i === commands.length - 1 ? stdout : pipes[i]!;\n\n promises.push(\n this.executeNode(command, cmdStdin, cmdStdout, stderr).then((code) => {\n // Close pipe when command finishes\n if (i < commands.length - 1) {\n pipes[i]!.close();\n }\n return code;\n })\n );\n }\n\n // Wait for all commands and return last exit code\n const results = await Promise.all(promises);\n return results[results.length - 1]!;\n }\n\n private async executeSequence(\n commands: ASTNode[],\n stdinSource: AsyncIterable<Uint8Array> | null,\n stdout: OutputCollector,\n stderr: OutputCollector\n ): Promise<number> {\n let lastExitCode = 0;\n\n for (const command of commands) {\n lastExitCode = await this.executeNode(command, stdinSource, stdout, stderr);\n }\n\n return lastExitCode;\n }\n\n private async executeAnd(\n left: ASTNode,\n right: ASTNode,\n stdinSource: AsyncIterable<Uint8Array> | null,\n stdout: OutputCollector,\n stderr: OutputCollector\n ): Promise<number> {\n const leftCode = await this.executeNode(left, stdinSource, stdout, stderr);\n if (leftCode !== 0) {\n return leftCode;\n }\n return this.executeNode(right, stdinSource, stdout, stderr);\n }\n\n private async executeOr(\n left: ASTNode,\n right: ASTNode,\n stdinSource: AsyncIterable<Uint8Array> | null,\n stdout: OutputCollector,\n stderr: OutputCollector\n ): Promise<number> {\n const leftCode = await this.executeNode(left, stdinSource, stdout, stderr);\n if (leftCode === 0) {\n return 0;\n }\n return this.executeNode(right, stdinSource, stdout, stderr);\n }\n\n private async evaluateNode(node: ASTNode, localEnv?: Record<string, string>): Promise<string> {\n const env = localEnv ?? this.env;\n switch (node.type) {\n case \"literal\":\n return node.value;\n case \"variable\":\n return env[node.name] ?? \"\";\n case \"glob\":\n return node.pattern;\n case \"concat\": {\n const parts = await Promise.all(node.parts.map((p) => this.evaluateNode(p, localEnv)));\n return parts.join(\"\");\n }\n case \"substitution\": {\n // Execute the command and capture output\n const subStdout = createStdout();\n const subStderr = createStderr();\n await this.executeNode(node.command, null, subStdout, subStderr);\n subStdout.close();\n const output = await subStdout.collect();\n // Trim trailing newlines\n return output.toString(\"utf-8\").replace(/\\n+$/, \"\");\n }\n default:\n throw new Error(`Cannot evaluate node type: ${node.type}`);\n }\n }\n\n setCwd(cwd: string): void {\n this.cwd = cwd;\n }\n\n setEnv(vars: Record<string, string>): void {\n Object.assign(this.env, vars);\n }\n\n getCwd(): string {\n return this.cwd;\n }\n\n getEnv(): Record<string, string> {\n return { ...this.env };\n }\n}\n"
|
|
6
6
|
],
|
|
7
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEqC,IAArC;AAC4B,IAA5B;
|
|
8
|
-
"debugId": "
|
|
7
|
+
"mappings": "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",
|
|
8
|
+
"debugId": "672068A850D821FB64756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
|
@@ -32,8 +32,10 @@ __export(exports_stdout, {
|
|
|
32
32
|
createStdout: () => createStdout,
|
|
33
33
|
createStderr: () => createStderr,
|
|
34
34
|
createPipe: () => createPipe,
|
|
35
|
+
createBufferTargetCollector: () => createBufferTargetCollector,
|
|
35
36
|
PipeBuffer: () => PipeBuffer,
|
|
36
|
-
OutputCollectorImpl: () => OutputCollectorImpl
|
|
37
|
+
OutputCollectorImpl: () => OutputCollectorImpl,
|
|
38
|
+
BufferTargetCollector: () => BufferTargetCollector
|
|
37
39
|
});
|
|
38
40
|
module.exports = __toCommonJS(exports_stdout);
|
|
39
41
|
|
|
@@ -155,4 +157,47 @@ function createPipe() {
|
|
|
155
157
|
return new PipeBuffer;
|
|
156
158
|
}
|
|
157
159
|
|
|
158
|
-
|
|
160
|
+
class BufferTargetCollector {
|
|
161
|
+
target;
|
|
162
|
+
offset = 0;
|
|
163
|
+
closed = false;
|
|
164
|
+
closeResolvers = [];
|
|
165
|
+
constructor(target) {
|
|
166
|
+
this.target = target;
|
|
167
|
+
}
|
|
168
|
+
async write(chunk) {
|
|
169
|
+
if (this.closed) {
|
|
170
|
+
throw new Error("Output stream is closed");
|
|
171
|
+
}
|
|
172
|
+
for (let i = 0;i < chunk.length && this.offset < this.target.length; i++) {
|
|
173
|
+
this.target[this.offset++] = chunk[i];
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
async writeText(str) {
|
|
177
|
+
const bytes = new TextEncoder().encode(str);
|
|
178
|
+
await this.write(bytes);
|
|
179
|
+
}
|
|
180
|
+
close() {
|
|
181
|
+
this.closed = true;
|
|
182
|
+
for (const resolve of this.closeResolvers) {
|
|
183
|
+
resolve();
|
|
184
|
+
}
|
|
185
|
+
this.closeResolvers = [];
|
|
186
|
+
}
|
|
187
|
+
async collect() {
|
|
188
|
+
while (!this.closed) {
|
|
189
|
+
await new Promise((resolve) => {
|
|
190
|
+
this.closeResolvers.push(resolve);
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
return this.target.subarray(0, this.offset);
|
|
194
|
+
}
|
|
195
|
+
async* getReadableStream() {
|
|
196
|
+
yield this.target.subarray(0, this.offset);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
function createBufferTargetCollector(target) {
|
|
200
|
+
return new BufferTargetCollector(target);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
//# debugId=D2F8163FA2DAF10364756E2164756E21
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/io/stdout.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"import type { Stdout, Stderr, OutputCollector } from \"../types.cjs\";\n\nexport class OutputCollectorImpl implements OutputCollector {\n private chunks: Uint8Array[] = [];\n private closed: boolean = false;\n private closeResolvers: Array<() => void> = [];\n private resolveWait: (() => void) | null = null;\n private waitPromise: Promise<void> | null = null;\n\n async write(chunk: Uint8Array): Promise<void> {\n if (this.closed) {\n throw new Error(\"Output stream is closed\");\n }\n this.chunks.push(chunk);\n if (this.resolveWait) {\n this.resolveWait();\n this.resolveWait = null;\n this.waitPromise = null;\n }\n }\n\n async writeText(str: string): Promise<void> {\n await this.write(new TextEncoder().encode(str));\n }\n\n close(): void {\n this.closed = true;\n if (this.resolveWait) {\n this.resolveWait();\n this.resolveWait = null;\n this.waitPromise = null;\n }\n // Wake up anyone waiting for close\n for (const resolve of this.closeResolvers) {\n resolve();\n }\n this.closeResolvers = [];\n }\n\n async collect(): Promise<Buffer> {\n // Wait until closed\n while (!this.closed) {\n await new Promise<void>((resolve) => {\n this.closeResolvers.push(resolve);\n });\n }\n return Buffer.concat(this.chunks);\n }\n\n async *getReadableStream(): AsyncIterable<Uint8Array> {\n let index = 0;\n\n while (true) {\n while (index < this.chunks.length) {\n yield this.chunks[index]!;\n index++;\n }\n\n if (this.closed) {\n break;\n }\n\n // Wait for more data or close\n if (!this.waitPromise) {\n this.waitPromise = new Promise<void>((resolve) => {\n this.resolveWait = resolve;\n });\n }\n await this.waitPromise;\n }\n }\n}\n\nexport class PipeBuffer implements OutputCollector, Stdout {\n private chunks: Uint8Array[] = [];\n private closed: boolean = false;\n private waitingReaders: Array<() => void> = [];\n private readIndex: number = 0;\n\n async write(chunk: Uint8Array): Promise<void> {\n if (this.closed) {\n throw new Error(\"Pipe is closed\");\n }\n this.chunks.push(chunk);\n // Wake up any waiting readers\n for (const resolve of this.waitingReaders) {\n resolve();\n }\n this.waitingReaders = [];\n }\n\n async writeText(str: string): Promise<void> {\n await this.write(new TextEncoder().encode(str));\n }\n\n close(): void {\n this.closed = true;\n // Wake up any waiting readers\n for (const resolve of this.waitingReaders) {\n resolve();\n }\n this.waitingReaders = [];\n }\n\n async collect(): Promise<Buffer> {\n // Wait until closed\n while (!this.closed) {\n await new Promise<void>((resolve) => {\n this.waitingReaders.push(resolve);\n });\n }\n return Buffer.concat(this.chunks);\n }\n\n async *getReadableStream(): AsyncIterable<Uint8Array> {\n while (true) {\n // Yield any available chunks\n while (this.readIndex < this.chunks.length) {\n yield this.chunks[this.readIndex]!;\n this.readIndex++;\n }\n\n if (this.closed) {\n break;\n }\n\n // Wait for more data\n await new Promise<void>((resolve) => {\n this.waitingReaders.push(resolve);\n });\n }\n }\n}\n\nexport function createStdout(): OutputCollector {\n return new OutputCollectorImpl();\n}\n\nexport function createStderr(): OutputCollector {\n return new OutputCollectorImpl();\n}\n\nexport function createPipe(): PipeBuffer {\n return new PipeBuffer();\n}\n"
|
|
5
|
+
"import type { Stdout, Stderr, OutputCollector } from \"../types.cjs\";\n\nexport class OutputCollectorImpl implements OutputCollector {\n private chunks: Uint8Array[] = [];\n private closed: boolean = false;\n private closeResolvers: Array<() => void> = [];\n private resolveWait: (() => void) | null = null;\n private waitPromise: Promise<void> | null = null;\n\n async write(chunk: Uint8Array): Promise<void> {\n if (this.closed) {\n throw new Error(\"Output stream is closed\");\n }\n this.chunks.push(chunk);\n if (this.resolveWait) {\n this.resolveWait();\n this.resolveWait = null;\n this.waitPromise = null;\n }\n }\n\n async writeText(str: string): Promise<void> {\n await this.write(new TextEncoder().encode(str));\n }\n\n close(): void {\n this.closed = true;\n if (this.resolveWait) {\n this.resolveWait();\n this.resolveWait = null;\n this.waitPromise = null;\n }\n // Wake up anyone waiting for close\n for (const resolve of this.closeResolvers) {\n resolve();\n }\n this.closeResolvers = [];\n }\n\n async collect(): Promise<Buffer> {\n // Wait until closed\n while (!this.closed) {\n await new Promise<void>((resolve) => {\n this.closeResolvers.push(resolve);\n });\n }\n return Buffer.concat(this.chunks);\n }\n\n async *getReadableStream(): AsyncIterable<Uint8Array> {\n let index = 0;\n\n while (true) {\n while (index < this.chunks.length) {\n yield this.chunks[index]!;\n index++;\n }\n\n if (this.closed) {\n break;\n }\n\n // Wait for more data or close\n if (!this.waitPromise) {\n this.waitPromise = new Promise<void>((resolve) => {\n this.resolveWait = resolve;\n });\n }\n await this.waitPromise;\n }\n }\n}\n\nexport class PipeBuffer implements OutputCollector, Stdout {\n private chunks: Uint8Array[] = [];\n private closed: boolean = false;\n private waitingReaders: Array<() => void> = [];\n private readIndex: number = 0;\n\n async write(chunk: Uint8Array): Promise<void> {\n if (this.closed) {\n throw new Error(\"Pipe is closed\");\n }\n this.chunks.push(chunk);\n // Wake up any waiting readers\n for (const resolve of this.waitingReaders) {\n resolve();\n }\n this.waitingReaders = [];\n }\n\n async writeText(str: string): Promise<void> {\n await this.write(new TextEncoder().encode(str));\n }\n\n close(): void {\n this.closed = true;\n // Wake up any waiting readers\n for (const resolve of this.waitingReaders) {\n resolve();\n }\n this.waitingReaders = [];\n }\n\n async collect(): Promise<Buffer> {\n // Wait until closed\n while (!this.closed) {\n await new Promise<void>((resolve) => {\n this.waitingReaders.push(resolve);\n });\n }\n return Buffer.concat(this.chunks);\n }\n\n async *getReadableStream(): AsyncIterable<Uint8Array> {\n while (true) {\n // Yield any available chunks\n while (this.readIndex < this.chunks.length) {\n yield this.chunks[this.readIndex]!;\n this.readIndex++;\n }\n\n if (this.closed) {\n break;\n }\n\n // Wait for more data\n await new Promise<void>((resolve) => {\n this.waitingReaders.push(resolve);\n });\n }\n }\n}\n\nexport function createStdout(): OutputCollector {\n return new OutputCollectorImpl();\n}\n\nexport function createStderr(): OutputCollector {\n return new OutputCollectorImpl();\n}\n\nexport function createPipe(): PipeBuffer {\n return new PipeBuffer();\n}\n\nexport class BufferTargetCollector implements OutputCollector {\n private target: Buffer;\n private offset: number = 0;\n private closed: boolean = false;\n private closeResolvers: Array<() => void> = [];\n\n constructor(target: Buffer) {\n this.target = target;\n }\n\n async write(chunk: Uint8Array): Promise<void> {\n if (this.closed) {\n throw new Error(\"Output stream is closed\");\n }\n for (let i = 0; i < chunk.length && this.offset < this.target.length; i++) {\n this.target[this.offset++] = chunk[i]!;\n }\n }\n\n async writeText(str: string): Promise<void> {\n const bytes = new TextEncoder().encode(str);\n await this.write(bytes);\n }\n\n close(): void {\n this.closed = true;\n for (const resolve of this.closeResolvers) {\n resolve();\n }\n this.closeResolvers = [];\n }\n\n async collect(): Promise<Buffer> {\n while (!this.closed) {\n await new Promise<void>((resolve) => {\n this.closeResolvers.push(resolve);\n });\n }\n return this.target.subarray(0, this.offset) as Buffer;\n }\n\n async *getReadableStream(): AsyncIterable<Uint8Array> {\n yield this.target.subarray(0, this.offset);\n }\n}\n\nexport function createBufferTargetCollector(target: Buffer): OutputCollector {\n return new BufferTargetCollector(target);\n}\n"
|
|
6
6
|
],
|
|
7
|
-
"mappings": "
|
|
8
|
-
"debugId": "
|
|
7
|
+
"mappings": "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",
|
|
8
|
+
"debugId": "D2F8163FA2DAF10364756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
|
@@ -58,16 +58,40 @@ class ShellDSL {
|
|
|
58
58
|
}
|
|
59
59
|
tag(strings, ...values) {
|
|
60
60
|
let source = strings[0] ?? "";
|
|
61
|
+
const redirectObjects = {};
|
|
62
|
+
let objIndex = 0;
|
|
61
63
|
for (let i = 0;i < values.length; i++) {
|
|
62
64
|
const value = values[i];
|
|
65
|
+
const precedingString = strings[i] ?? "";
|
|
63
66
|
if (import_types.isRawValue(value)) {
|
|
64
67
|
source += value.raw;
|
|
68
|
+
} else if (this.isRedirectTarget(precedingString, value)) {
|
|
69
|
+
const marker = `__REDIR_OBJ_${objIndex++}__`;
|
|
70
|
+
redirectObjects[marker] = value;
|
|
71
|
+
source += marker;
|
|
65
72
|
} else {
|
|
66
73
|
source += import_escape.escapeForInterpolation(value);
|
|
67
74
|
}
|
|
68
75
|
source += strings[i + 1] ?? "";
|
|
69
76
|
}
|
|
70
|
-
return this.createPromise(source);
|
|
77
|
+
return this.createPromise(source, { redirectObjects });
|
|
78
|
+
}
|
|
79
|
+
isRedirectTarget(precedingString, value) {
|
|
80
|
+
if (!import_types.isRedirectObject(value) || import_types.isRawValue(value)) {
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
const trimmed = precedingString.trimEnd();
|
|
84
|
+
const afterRedirectOp = /(<|>|>>|2>|2>>|&>|&>>)\s*$/.test(trimmed);
|
|
85
|
+
if (!afterRedirectOp) {
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
if (Buffer.isBuffer(value) || value instanceof Blob || value instanceof Response) {
|
|
89
|
+
return true;
|
|
90
|
+
}
|
|
91
|
+
if (typeof value === "string") {
|
|
92
|
+
return /<\s*$/.test(trimmed);
|
|
93
|
+
}
|
|
94
|
+
return false;
|
|
71
95
|
}
|
|
72
96
|
createPromise(source, options) {
|
|
73
97
|
const shell = this;
|
|
@@ -79,7 +103,8 @@ class ShellDSL {
|
|
|
79
103
|
fs: shell.fs,
|
|
80
104
|
cwd,
|
|
81
105
|
env,
|
|
82
|
-
commands: shell.commands
|
|
106
|
+
commands: shell.commands,
|
|
107
|
+
redirectObjects: options?.redirectObjects
|
|
83
108
|
});
|
|
84
109
|
const tokens = shell.lex(source);
|
|
85
110
|
const ast = shell.parse(tokens);
|
|
@@ -159,4 +184,4 @@ function createShellDSL(config) {
|
|
|
159
184
|
return tag;
|
|
160
185
|
}
|
|
161
186
|
|
|
162
|
-
//# debugId=
|
|
187
|
+
//# debugId=35D814EA2D605E7064756E2164756E21
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/shell-dsl.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"import type { ShellConfig, Command, VirtualFS, ExecResult } from \"./types.cjs\";\nimport { isRawValue } from \"./types.cjs\";\nimport type { Token } from \"./lexer/tokens.cjs\";\nimport type { ASTNode } from \"./parser/ast.cjs\";\nimport { Lexer } from \"./lexer/lexer.cjs\";\nimport { Parser } from \"./parser/parser.cjs\";\nimport { Interpreter } from \"./interpreter/interpreter.cjs\";\nimport { ShellPromise } from \"./shell-promise.cjs\";\nimport { escape, escapeForInterpolation } from \"./utils/escape.cjs\";\n\nexport interface Program {\n ast: ASTNode;\n source: string;\n}\n\nexport class ShellDSL {\n private fs: VirtualFS;\n private initialCwd: string;\n private initialEnv: Record<string, string>;\n private currentCwd: string;\n private currentEnv: Record<string, string>;\n private commands: Record<string, Command>;\n private shouldThrow: boolean = true;\n\n constructor(config: ShellConfig) {\n this.fs = config.fs;\n this.initialCwd = config.cwd;\n this.initialEnv = { ...config.env };\n this.currentCwd = config.cwd;\n this.currentEnv = { ...config.env };\n this.commands = config.commands;\n }\n\n // Template tag function\n tag(strings: TemplateStringsArray, ...values: unknown[]): ShellPromise {\n // Build the command string with escaped interpolations\n let source = strings[0] ?? \"\";\n for (let i = 0; i < values.length; i++) {\n const value = values[i];\n if (isRawValue(value)) {\n source += value.raw;\n } else {\n source += escapeForInterpolation(value);\n }\n source += strings[i + 1] ?? \"\";\n }\n\n return this.createPromise(source);\n }\n\n private createPromise(source: string, options?: { cwd?: string; env?: Record<string, string>; shouldThrow?: boolean }): ShellPromise {\n const shell = this;\n\n return new ShellPromise({\n execute: async (overrides) => {\n const cwd = overrides?.cwd ?? options?.cwd ?? shell.currentCwd;\n const env = { ...shell.currentEnv, ...options?.env, ...overrides?.env };\n\n const interpreter = new Interpreter({\n fs: shell.fs,\n cwd,\n env,\n commands: shell.commands,\n });\n\n const tokens = shell.lex(source);\n const ast = shell.parse(tokens);\n return interpreter.execute(ast);\n },\n cwdOverride: options?.cwd,\n envOverride: options?.env,\n shouldThrow: options?.shouldThrow ?? this.shouldThrow,\n });\n }\n\n // Global defaults\n cwd(path: string): void {\n this.currentCwd = path;\n }\n\n env(vars: Record<string, string>): void {\n Object.assign(this.currentEnv, vars);\n }\n\n throws(enable: boolean): void {\n this.shouldThrow = enable;\n }\n\n resetCwd(): void {\n this.currentCwd = this.initialCwd;\n }\n\n resetEnv(): void {\n this.currentEnv = { ...this.initialEnv };\n }\n\n // Utility\n escape(str: string): string {\n return escape(str);\n }\n\n // Low-level API\n lex(source: string): Token[] {\n return new Lexer(source).tokenize();\n }\n\n parse(tokens: Token[]): ASTNode {\n return new Parser(tokens).parse();\n }\n\n compile(ast: ASTNode): Program {\n // For now, the \"program\" is just the AST with source reconstruction\n return {\n ast,\n source: \"\", // Could reconstruct source from AST if needed\n };\n }\n\n async run(program: Program): Promise<ExecResult> {\n const interpreter = new Interpreter({\n fs: this.fs,\n cwd: this.currentCwd,\n env: this.currentEnv,\n commands: this.commands,\n });\n\n return interpreter.execute(program.ast);\n }\n}\n\n// Factory function that returns a callable template tag\nexport function createShellDSL(config: ShellConfig): ShellDSL & ((strings: TemplateStringsArray, ...values: unknown[]) => ShellPromise) {\n const shell = new ShellDSL(config);\n\n // Create a function that acts as both tag and shell instance\n const tag = (strings: TemplateStringsArray, ...values: unknown[]) => {\n return shell.tag(strings, ...values);\n };\n\n // Copy all properties and methods from shell to tag function\n Object.setPrototypeOf(tag, ShellDSL.prototype);\n Object.assign(tag, {\n fs: (shell as any).fs,\n initialCwd: (shell as any).initialCwd,\n initialEnv: (shell as any).initialEnv,\n currentCwd: (shell as any).currentCwd,\n currentEnv: (shell as any).currentEnv,\n commands: (shell as any).commands,\n shouldThrow: (shell as any).shouldThrow,\n });\n\n // Bind methods\n (tag as any).cwd = shell.cwd.bind(shell);\n (tag as any).env = shell.env.bind(shell);\n (tag as any).throws = shell.throws.bind(shell);\n (tag as any).resetCwd = shell.resetCwd.bind(shell);\n (tag as any).resetEnv = shell.resetEnv.bind(shell);\n (tag as any).escape = shell.escape.bind(shell);\n (tag as any).lex = shell.lex.bind(shell);\n (tag as any).parse = shell.parse.bind(shell);\n (tag as any).compile = shell.compile.bind(shell);\n (tag as any).run = shell.run.bind(shell);\n (tag as any).tag = shell.tag.bind(shell);\n\n return tag as ShellDSL & ((strings: TemplateStringsArray, ...values: unknown[]) => ShellPromise);\n}\n"
|
|
5
|
+
"import type { ShellConfig, Command, VirtualFS, ExecResult, RedirectObjectMap } from \"./types.cjs\";\nimport { isRawValue, isRedirectObject } from \"./types.cjs\";\nimport type { Token } from \"./lexer/tokens.cjs\";\nimport type { ASTNode } from \"./parser/ast.cjs\";\nimport { Lexer } from \"./lexer/lexer.cjs\";\nimport { Parser } from \"./parser/parser.cjs\";\nimport { Interpreter } from \"./interpreter/interpreter.cjs\";\nimport { ShellPromise } from \"./shell-promise.cjs\";\nimport { escape, escapeForInterpolation } from \"./utils/escape.cjs\";\n\nexport interface Program {\n ast: ASTNode;\n source: string;\n}\n\nexport class ShellDSL {\n private fs: VirtualFS;\n private initialCwd: string;\n private initialEnv: Record<string, string>;\n private currentCwd: string;\n private currentEnv: Record<string, string>;\n private commands: Record<string, Command>;\n private shouldThrow: boolean = true;\n\n constructor(config: ShellConfig) {\n this.fs = config.fs;\n this.initialCwd = config.cwd;\n this.initialEnv = { ...config.env };\n this.currentCwd = config.cwd;\n this.currentEnv = { ...config.env };\n this.commands = config.commands;\n }\n\n // Template tag function\n tag(strings: TemplateStringsArray, ...values: unknown[]): ShellPromise {\n // Build the command string with escaped interpolations\n let source = strings[0] ?? \"\";\n const redirectObjects: RedirectObjectMap = {};\n let objIndex = 0;\n\n for (let i = 0; i < values.length; i++) {\n const value = values[i];\n const precedingString = strings[i] ?? \"\";\n\n if (isRawValue(value)) {\n source += value.raw;\n } else if (this.isRedirectTarget(precedingString, value)) {\n // Value appears after a redirect operator - store as redirect object\n const marker = `__REDIR_OBJ_${objIndex++}__`;\n redirectObjects[marker] = value as Buffer | Blob | Response | string;\n source += marker;\n } else {\n source += escapeForInterpolation(value);\n }\n source += strings[i + 1] ?? \"\";\n }\n\n return this.createPromise(source, { redirectObjects });\n }\n\n private isRedirectTarget(precedingString: string, value: unknown): boolean {\n // Check if value is a redirect object type AND appears after redirect operator\n if (!isRedirectObject(value) || isRawValue(value)) {\n return false;\n }\n // Check if preceding string ends with redirect operator\n const trimmed = precedingString.trimEnd();\n const afterRedirectOp = /(<|>|>>|2>|2>>|&>|&>>)\\s*$/.test(trimmed);\n\n if (!afterRedirectOp) {\n return false;\n }\n\n // Buffer, Blob, Response are always treated as redirect objects\n if (Buffer.isBuffer(value) || value instanceof Blob || value instanceof Response) {\n return true;\n }\n\n // For strings after input redirect (<), treat as content per spec\n // For strings after output redirect (>), they must be Buffers\n if (typeof value === \"string\") {\n // Only input redirection supports string content\n return /<\\s*$/.test(trimmed);\n }\n\n return false;\n }\n\n private createPromise(source: string, options?: { cwd?: string; env?: Record<string, string>; shouldThrow?: boolean; redirectObjects?: RedirectObjectMap }): ShellPromise {\n const shell = this;\n\n return new ShellPromise({\n execute: async (overrides) => {\n const cwd = overrides?.cwd ?? options?.cwd ?? shell.currentCwd;\n const env = { ...shell.currentEnv, ...options?.env, ...overrides?.env };\n\n const interpreter = new Interpreter({\n fs: shell.fs,\n cwd,\n env,\n commands: shell.commands,\n redirectObjects: options?.redirectObjects,\n });\n\n const tokens = shell.lex(source);\n const ast = shell.parse(tokens);\n return interpreter.execute(ast);\n },\n cwdOverride: options?.cwd,\n envOverride: options?.env,\n shouldThrow: options?.shouldThrow ?? this.shouldThrow,\n });\n }\n\n // Global defaults\n cwd(path: string): void {\n this.currentCwd = path;\n }\n\n env(vars: Record<string, string>): void {\n Object.assign(this.currentEnv, vars);\n }\n\n throws(enable: boolean): void {\n this.shouldThrow = enable;\n }\n\n resetCwd(): void {\n this.currentCwd = this.initialCwd;\n }\n\n resetEnv(): void {\n this.currentEnv = { ...this.initialEnv };\n }\n\n // Utility\n escape(str: string): string {\n return escape(str);\n }\n\n // Low-level API\n lex(source: string): Token[] {\n return new Lexer(source).tokenize();\n }\n\n parse(tokens: Token[]): ASTNode {\n return new Parser(tokens).parse();\n }\n\n compile(ast: ASTNode): Program {\n // For now, the \"program\" is just the AST with source reconstruction\n return {\n ast,\n source: \"\", // Could reconstruct source from AST if needed\n };\n }\n\n async run(program: Program): Promise<ExecResult> {\n const interpreter = new Interpreter({\n fs: this.fs,\n cwd: this.currentCwd,\n env: this.currentEnv,\n commands: this.commands,\n });\n\n return interpreter.execute(program.ast);\n }\n}\n\n// Factory function that returns a callable template tag\nexport function createShellDSL(config: ShellConfig): ShellDSL & ((strings: TemplateStringsArray, ...values: unknown[]) => ShellPromise) {\n const shell = new ShellDSL(config);\n\n // Create a function that acts as both tag and shell instance\n const tag = (strings: TemplateStringsArray, ...values: unknown[]) => {\n return shell.tag(strings, ...values);\n };\n\n // Copy all properties and methods from shell to tag function\n Object.setPrototypeOf(tag, ShellDSL.prototype);\n Object.assign(tag, {\n fs: (shell as any).fs,\n initialCwd: (shell as any).initialCwd,\n initialEnv: (shell as any).initialEnv,\n currentCwd: (shell as any).currentCwd,\n currentEnv: (shell as any).currentEnv,\n commands: (shell as any).commands,\n shouldThrow: (shell as any).shouldThrow,\n });\n\n // Bind methods\n (tag as any).cwd = shell.cwd.bind(shell);\n (tag as any).env = shell.env.bind(shell);\n (tag as any).throws = shell.throws.bind(shell);\n (tag as any).resetCwd = shell.resetCwd.bind(shell);\n (tag as any).resetEnv = shell.resetEnv.bind(shell);\n (tag as any).escape = shell.escape.bind(shell);\n (tag as any).lex = shell.lex.bind(shell);\n (tag as any).parse = shell.parse.bind(shell);\n (tag as any).compile = shell.compile.bind(shell);\n (tag as any).run = shell.run.bind(shell);\n (tag as any).tag = shell.tag.bind(shell);\n\n return tag as ShellDSL & ((strings: TemplateStringsArray, ...values: unknown[]) => ShellPromise);\n}\n"
|
|
6
6
|
],
|
|
7
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
8
|
-
"debugId": "
|
|
7
|
+
"mappings": "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",
|
|
8
|
+
"debugId": "35D814EA2D605E7064756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
package/dist/cjs/src/types.cjs
CHANGED
|
@@ -29,11 +29,15 @@ var __export = (target, all) => {
|
|
|
29
29
|
// src/types.ts
|
|
30
30
|
var exports_types = {};
|
|
31
31
|
__export(exports_types, {
|
|
32
|
+
isRedirectObject: () => isRedirectObject,
|
|
32
33
|
isRawValue: () => isRawValue
|
|
33
34
|
});
|
|
34
35
|
module.exports = __toCommonJS(exports_types);
|
|
35
36
|
function isRawValue(value) {
|
|
36
37
|
return typeof value === "object" && value !== null && "raw" in value && typeof value.raw === "string";
|
|
37
38
|
}
|
|
39
|
+
function isRedirectObject(value) {
|
|
40
|
+
return Buffer.isBuffer(value) || value instanceof Blob || value instanceof Response || typeof value === "string";
|
|
41
|
+
}
|
|
38
42
|
|
|
39
|
-
//# debugId=
|
|
43
|
+
//# debugId=A099C0F37A42BA3264756E2164756E21
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/types.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"// Virtual Filesystem Interface\nexport interface VirtualFS {\n readFile(path: string): Promise<Buffer>;\n readdir(path: string): Promise<string[]>;\n stat(path: string): Promise<FileStat>;\n exists(path: string): Promise<boolean>;\n\n writeFile(path: string, data: Buffer | string): Promise<void>;\n appendFile(path: string, data: Buffer | string): Promise<void>;\n mkdir(path: string, opts?: { recursive?: boolean }): Promise<void>;\n\n rm(path: string, opts?: { recursive?: boolean; force?: boolean }): Promise<void>;\n\n resolve(...paths: string[]): string;\n dirname(path: string): string;\n basename(path: string): string;\n glob(pattern: string, opts?: { cwd?: string }): Promise<string[]>;\n}\n\nexport interface FileStat {\n isFile(): boolean;\n isDirectory(): boolean;\n size: number;\n mtime: Date;\n}\n\n// Command Interfaces\nexport type Command = (ctx: CommandContext) => Promise<number>;\n\nexport interface CommandContext {\n args: string[];\n stdin: Stdin;\n stdout: Stdout;\n stderr: Stderr;\n fs: VirtualFS;\n cwd: string;\n env: Record<string, string>;\n}\n\nexport interface Stdin {\n stream(): AsyncIterable<Uint8Array>;\n buffer(): Promise<Buffer>;\n text(): Promise<string>;\n lines(): AsyncIterable<string>;\n}\n\nexport interface Stdout {\n write(chunk: Uint8Array): Promise<void>;\n writeText(str: string): Promise<void>;\n}\n\nexport interface Stderr {\n write(chunk: Uint8Array): Promise<void>;\n writeText(str: string): Promise<void>;\n}\n\nexport interface OutputCollector extends Stdout {\n close(): void;\n collect(): Promise<Buffer>;\n getReadableStream(): AsyncIterable<Uint8Array>;\n}\n\n// Execution Result\nexport interface ExecResult {\n stdout: Buffer;\n stderr: Buffer;\n exitCode: number;\n}\n\n// Shell Configuration\nexport interface ShellConfig {\n fs: VirtualFS;\n cwd: string;\n env: Record<string, string>;\n commands: Record<string, Command>;\n}\n\n// Raw escape hatch type\nexport interface RawValue {\n raw: string;\n}\n\nexport function isRawValue(value: unknown): value is RawValue {\n return (\n typeof value === \"object\" &&\n value !== null &&\n \"raw\" in value &&\n typeof (value as RawValue).raw === \"string\"\n );\n}\n"
|
|
5
|
+
"// Virtual Filesystem Interface\nexport interface VirtualFS {\n readFile(path: string): Promise<Buffer>;\n readdir(path: string): Promise<string[]>;\n stat(path: string): Promise<FileStat>;\n exists(path: string): Promise<boolean>;\n\n writeFile(path: string, data: Buffer | string): Promise<void>;\n appendFile(path: string, data: Buffer | string): Promise<void>;\n mkdir(path: string, opts?: { recursive?: boolean }): Promise<void>;\n\n rm(path: string, opts?: { recursive?: boolean; force?: boolean }): Promise<void>;\n\n resolve(...paths: string[]): string;\n dirname(path: string): string;\n basename(path: string): string;\n glob(pattern: string, opts?: { cwd?: string }): Promise<string[]>;\n}\n\nexport interface FileStat {\n isFile(): boolean;\n isDirectory(): boolean;\n size: number;\n mtime: Date;\n}\n\n// Command Interfaces\nexport type Command = (ctx: CommandContext) => Promise<number>;\n\nexport interface CommandContext {\n args: string[];\n stdin: Stdin;\n stdout: Stdout;\n stderr: Stderr;\n fs: VirtualFS;\n cwd: string;\n env: Record<string, string>;\n}\n\nexport interface Stdin {\n stream(): AsyncIterable<Uint8Array>;\n buffer(): Promise<Buffer>;\n text(): Promise<string>;\n lines(): AsyncIterable<string>;\n}\n\nexport interface Stdout {\n write(chunk: Uint8Array): Promise<void>;\n writeText(str: string): Promise<void>;\n}\n\nexport interface Stderr {\n write(chunk: Uint8Array): Promise<void>;\n writeText(str: string): Promise<void>;\n}\n\nexport interface OutputCollector extends Stdout {\n close(): void;\n collect(): Promise<Buffer>;\n getReadableStream(): AsyncIterable<Uint8Array>;\n}\n\n// Execution Result\nexport interface ExecResult {\n stdout: Buffer;\n stderr: Buffer;\n exitCode: number;\n}\n\n// Shell Configuration\nexport interface ShellConfig {\n fs: VirtualFS;\n cwd: string;\n env: Record<string, string>;\n commands: Record<string, Command>;\n}\n\n// Raw escape hatch type\nexport interface RawValue {\n raw: string;\n}\n\nexport function isRawValue(value: unknown): value is RawValue {\n return (\n typeof value === \"object\" &&\n value !== null &&\n \"raw\" in value &&\n typeof (value as RawValue).raw === \"string\"\n );\n}\n\n// JS Object Redirection types\nexport type RedirectObject = Buffer | Blob | Response | string;\n\nexport interface RedirectObjectMap {\n [marker: string]: RedirectObject;\n}\n\nexport function isRedirectObject(value: unknown): value is RedirectObject {\n return (\n Buffer.isBuffer(value) ||\n value instanceof Blob ||\n value instanceof Response ||\n typeof value === \"string\"\n );\n}\n"
|
|
6
6
|
],
|
|
7
|
-
"mappings": "
|
|
8
|
-
"debugId": "
|
|
7
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkFO,SAAS,UAAU,CAAC,OAAmC;AAAA,EAC5D,OACE,OAAO,UAAU,YACjB,UAAU,QACV,SAAS,SACT,OAAQ,MAAmB,QAAQ;AAAA;AAWhC,SAAS,gBAAgB,CAAC,OAAyC;AAAA,EACxE,OACE,OAAO,SAAS,KAAK,KACrB,iBAAiB,QACjB,iBAAiB,YACjB,OAAO,UAAU;AAAA;",
|
|
8
|
+
"debugId": "A099C0F37A42BA3264756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
package/dist/mjs/package.json
CHANGED
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
// src/interpreter/interpreter.ts
|
|
2
2
|
import { createCommandContext } from "./context.mjs";
|
|
3
3
|
import { createStdin } from "../io/stdin.mjs";
|
|
4
|
-
import { createStdout, createStderr, createPipe } from "../io/stdout.mjs";
|
|
4
|
+
import { createStdout, createStderr, createPipe, createBufferTargetCollector } from "../io/stdout.mjs";
|
|
5
5
|
|
|
6
6
|
class Interpreter {
|
|
7
7
|
fs;
|
|
8
8
|
cwd;
|
|
9
9
|
env;
|
|
10
10
|
commands;
|
|
11
|
+
redirectObjects;
|
|
11
12
|
constructor(options) {
|
|
12
13
|
this.fs = options.fs;
|
|
13
14
|
this.cwd = options.cwd;
|
|
14
15
|
this.env = { ...options.env };
|
|
15
16
|
this.commands = options.commands;
|
|
17
|
+
this.redirectObjects = options.redirectObjects ?? {};
|
|
16
18
|
}
|
|
17
19
|
async execute(ast) {
|
|
18
20
|
const stdout = createStdout();
|
|
@@ -126,6 +128,9 @@ class Interpreter {
|
|
|
126
128
|
}
|
|
127
129
|
async handleRedirect(redirect, stdin, stdout, stderr) {
|
|
128
130
|
const target = await this.evaluateNode(redirect.target);
|
|
131
|
+
if (target in this.redirectObjects) {
|
|
132
|
+
return this.handleObjectRedirect(redirect.mode, this.redirectObjects[target], stdin, stdout, stderr);
|
|
133
|
+
}
|
|
129
134
|
switch (redirect.mode) {
|
|
130
135
|
case "<": {
|
|
131
136
|
const path = this.fs.resolve(this.cwd, target);
|
|
@@ -200,6 +205,61 @@ class Interpreter {
|
|
|
200
205
|
return { stdin, stdout, stderr };
|
|
201
206
|
}
|
|
202
207
|
}
|
|
208
|
+
async handleObjectRedirect(mode, obj, stdin, stdout, stderr) {
|
|
209
|
+
switch (mode) {
|
|
210
|
+
case "<": {
|
|
211
|
+
const data = await this.readFromObject(obj);
|
|
212
|
+
return {
|
|
213
|
+
stdin: async function* () {
|
|
214
|
+
yield data;
|
|
215
|
+
}(),
|
|
216
|
+
stdout,
|
|
217
|
+
stderr
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
case ">":
|
|
221
|
+
case ">>": {
|
|
222
|
+
if (!Buffer.isBuffer(obj)) {
|
|
223
|
+
throw new Error("Output redirection only supports Buffer targets");
|
|
224
|
+
}
|
|
225
|
+
const collector = createBufferTargetCollector(obj);
|
|
226
|
+
return { stdin, stdout: collector, stderr };
|
|
227
|
+
}
|
|
228
|
+
case "2>":
|
|
229
|
+
case "2>>": {
|
|
230
|
+
if (!Buffer.isBuffer(obj)) {
|
|
231
|
+
throw new Error("Stderr redirection only supports Buffer targets");
|
|
232
|
+
}
|
|
233
|
+
const collector = createBufferTargetCollector(obj);
|
|
234
|
+
return { stdin, stdout, stderr: collector };
|
|
235
|
+
}
|
|
236
|
+
case "&>":
|
|
237
|
+
case "&>>": {
|
|
238
|
+
if (!Buffer.isBuffer(obj)) {
|
|
239
|
+
throw new Error("Combined redirection only supports Buffer targets");
|
|
240
|
+
}
|
|
241
|
+
const collector = createBufferTargetCollector(obj);
|
|
242
|
+
return { stdin, stdout: collector, stderr: collector };
|
|
243
|
+
}
|
|
244
|
+
default:
|
|
245
|
+
return { stdin, stdout, stderr };
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
async readFromObject(obj) {
|
|
249
|
+
if (Buffer.isBuffer(obj)) {
|
|
250
|
+
return new Uint8Array(obj);
|
|
251
|
+
}
|
|
252
|
+
if (obj instanceof Blob) {
|
|
253
|
+
return new Uint8Array(await obj.arrayBuffer());
|
|
254
|
+
}
|
|
255
|
+
if (obj instanceof Response) {
|
|
256
|
+
return new Uint8Array(await obj.arrayBuffer());
|
|
257
|
+
}
|
|
258
|
+
if (typeof obj === "string") {
|
|
259
|
+
return new TextEncoder().encode(obj);
|
|
260
|
+
}
|
|
261
|
+
throw new Error("Unsupported redirect object type");
|
|
262
|
+
}
|
|
203
263
|
async executePipeline(commands, stdinSource, stdout, stderr) {
|
|
204
264
|
if (commands.length === 0)
|
|
205
265
|
return 0;
|
|
@@ -288,4 +348,4 @@ export {
|
|
|
288
348
|
Interpreter
|
|
289
349
|
};
|
|
290
350
|
|
|
291
|
-
//# debugId=
|
|
351
|
+
//# debugId=600AE415CC91B77B64756E2164756E21
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/interpreter/interpreter.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"import type { ASTNode, CommandNode, Redirect } from \"../parser/ast.mjs\";\nimport type { Command, VirtualFS, ExecResult, OutputCollector } from \"../types.mjs\";\nimport { createCommandContext } from \"./context.mjs\";\nimport { createStdin } from \"../io/stdin.mjs\";\nimport { createStdout, createStderr, createPipe, PipeBuffer } from \"../io/stdout.mjs\";\nimport { Lexer } from \"../lexer/lexer.mjs\";\nimport { Parser } from \"../parser/parser.mjs\";\n\nexport interface InterpreterOptions {\n fs: VirtualFS;\n cwd: string;\n env: Record<string, string>;\n commands: Record<string, Command>;\n}\n\nexport class Interpreter {\n private fs: VirtualFS;\n private cwd: string;\n private env: Record<string, string>;\n private commands: Record<string, Command>;\n\n constructor(options: InterpreterOptions) {\n this.fs = options.fs;\n this.cwd = options.cwd;\n this.env = { ...options.env };\n this.commands = options.commands;\n }\n\n async execute(ast: ASTNode): Promise<ExecResult> {\n const stdout = createStdout();\n const stderr = createStderr();\n\n const exitCode = await this.executeNode(ast, null, stdout, stderr);\n\n stdout.close();\n stderr.close();\n\n return {\n stdout: await stdout.collect(),\n stderr: await stderr.collect(),\n exitCode,\n };\n }\n\n private async executeNode(\n node: ASTNode,\n stdinSource: AsyncIterable<Uint8Array> | null,\n stdout: OutputCollector,\n stderr: OutputCollector\n ): Promise<number> {\n switch (node.type) {\n case \"command\":\n return this.executeCommand(node, stdinSource, stdout, stderr);\n case \"pipeline\":\n return this.executePipeline(node.commands, stdinSource, stdout, stderr);\n case \"sequence\":\n return this.executeSequence(node.commands, stdinSource, stdout, stderr);\n case \"and\":\n return this.executeAnd(node.left, node.right, stdinSource, stdout, stderr);\n case \"or\":\n return this.executeOr(node.left, node.right, stdinSource, stdout, stderr);\n default:\n throw new Error(`Cannot execute node type: ${node.type}`);\n }\n }\n\n private async executeCommand(\n node: CommandNode,\n stdinSource: AsyncIterable<Uint8Array> | null,\n stdout: OutputCollector,\n stderr: OutputCollector\n ): Promise<number> {\n // Create local env with assignments\n const localEnv = { ...this.env };\n for (const assignment of node.assignments) {\n localEnv[assignment.name] = await this.evaluateNode(assignment.value);\n }\n\n // If there's no command name but there are assignments, just update env\n const name = await this.evaluateNode(node.name);\n if (name === \"\" && node.assignments.length > 0) {\n for (const assignment of node.assignments) {\n this.env[assignment.name] = await this.evaluateNode(assignment.value);\n }\n return 0;\n }\n\n // Evaluate arguments using localEnv for scoped variable expansion\n const args: string[] = [];\n for (const arg of node.args) {\n const evaluated = await this.evaluateNode(arg, localEnv);\n // Glob expansion returns multiple values\n if (arg.type === \"glob\") {\n const matches = await this.fs.glob(evaluated, { cwd: this.cwd });\n if (matches.length > 0) {\n args.push(...matches);\n } else {\n // No matches - use pattern as-is\n args.push(evaluated);\n }\n } else {\n args.push(evaluated);\n }\n }\n\n // Handle redirects\n let actualStdin = stdinSource;\n let actualStdout: OutputCollector = stdout;\n let actualStderr: OutputCollector = stderr;\n let stderrToStdout = false;\n let stdoutToStderr = false;\n const fileWritePromises: Promise<void>[] = [];\n\n for (const redirect of node.redirects) {\n const result = await this.handleRedirect(\n redirect,\n actualStdin,\n actualStdout,\n actualStderr\n );\n actualStdin = result.stdin;\n actualStdout = result.stdout;\n actualStderr = result.stderr;\n stderrToStdout = result.stderrToStdout || stderrToStdout;\n stdoutToStderr = result.stdoutToStderr || stdoutToStderr;\n if (result.fileWritePromise) {\n fileWritePromises.push(result.fileWritePromise);\n }\n }\n\n // Handle stderr->stdout redirect\n if (stderrToStdout) {\n actualStderr = actualStdout;\n }\n if (stdoutToStderr) {\n actualStdout = actualStderr;\n }\n\n // Look up command\n const command = this.commands[name];\n if (!command) {\n await stderr.writeText(`${name}: command not found\\n`);\n return 127;\n }\n\n // Create context and execute\n const ctx = createCommandContext({\n args,\n stdin: createStdin(actualStdin),\n stdout: actualStdout,\n stderr: actualStderr,\n fs: this.fs,\n cwd: this.cwd,\n env: localEnv,\n });\n\n let exitCode: number;\n try {\n exitCode = await command(ctx);\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n await stderr.writeText(`${name}: ${message}\\n`);\n exitCode = 1;\n }\n\n // Close redirect collectors and wait for file writes\n if (actualStdout !== stdout) {\n actualStdout.close();\n }\n if (actualStderr !== stderr && actualStderr !== actualStdout) {\n actualStderr.close();\n }\n\n // Wait for all file write operations to complete\n await Promise.all(fileWritePromises);\n\n return exitCode;\n }\n\n private async handleRedirect(\n redirect: Redirect,\n stdin: AsyncIterable<Uint8Array> | null,\n stdout: OutputCollector,\n stderr: OutputCollector\n ): Promise<{\n stdin: AsyncIterable<Uint8Array> | null;\n stdout: OutputCollector;\n stderr: OutputCollector;\n stderrToStdout?: boolean;\n stdoutToStderr?: boolean;\n fileWritePromise?: Promise<void>;\n }> {\n const target = await this.evaluateNode(redirect.target);\n\n switch (redirect.mode) {\n case \"<\": {\n // Input redirect\n const path = this.fs.resolve(this.cwd, target);\n const content = await this.fs.readFile(path);\n return {\n stdin: (async function* () {\n yield new Uint8Array(content);\n })(),\n stdout,\n stderr,\n };\n }\n case \">\": {\n // Output redirect (overwrite)\n const path = this.fs.resolve(this.cwd, target);\n const collector = createStdout();\n const fileWritePromise = (async () => {\n const data = await collector.collect();\n await this.fs.writeFile(path, data);\n })();\n return { stdin, stdout: collector, stderr, fileWritePromise };\n }\n case \">>\": {\n // Output redirect (append)\n const path = this.fs.resolve(this.cwd, target);\n const collector = createStdout();\n const fileWritePromise = (async () => {\n const data = await collector.collect();\n await this.fs.appendFile(path, data);\n })();\n return { stdin, stdout: collector, stderr, fileWritePromise };\n }\n case \"2>\": {\n // Stderr redirect (overwrite)\n const path = this.fs.resolve(this.cwd, target);\n const collector = createStderr();\n const fileWritePromise = (async () => {\n const data = await collector.collect();\n await this.fs.writeFile(path, data);\n })();\n return { stdin, stdout, stderr: collector, fileWritePromise };\n }\n case \"2>>\": {\n // Stderr redirect (append)\n const path = this.fs.resolve(this.cwd, target);\n const collector = createStderr();\n const fileWritePromise = (async () => {\n const data = await collector.collect();\n await this.fs.appendFile(path, data);\n })();\n return { stdin, stdout, stderr: collector, fileWritePromise };\n }\n case \"&>\": {\n // Both to file (overwrite)\n const path = this.fs.resolve(this.cwd, target);\n const collector = createStdout();\n const fileWritePromise = (async () => {\n const data = await collector.collect();\n await this.fs.writeFile(path, data);\n })();\n return { stdin, stdout: collector, stderr: collector, fileWritePromise };\n }\n case \"&>>\": {\n // Both to file (append)\n const path = this.fs.resolve(this.cwd, target);\n const collector = createStdout();\n const fileWritePromise = (async () => {\n const data = await collector.collect();\n await this.fs.appendFile(path, data);\n })();\n return { stdin, stdout: collector, stderr: collector, fileWritePromise };\n }\n case \"2>&1\":\n return { stdin, stdout, stderr, stderrToStdout: true };\n case \"1>&2\":\n return { stdin, stdout, stderr, stdoutToStderr: true };\n default:\n return { stdin, stdout, stderr };\n }\n }\n\n private async executePipeline(\n commands: ASTNode[],\n stdinSource: AsyncIterable<Uint8Array> | null,\n stdout: OutputCollector,\n stderr: OutputCollector\n ): Promise<number> {\n if (commands.length === 0) return 0;\n if (commands.length === 1) {\n return this.executeNode(commands[0]!, stdinSource, stdout, stderr);\n }\n\n // Create pipes between commands\n const pipes: PipeBuffer[] = [];\n for (let i = 0; i < commands.length - 1; i++) {\n pipes.push(createPipe());\n }\n\n // Execute all commands concurrently\n const promises: Promise<number>[] = [];\n\n for (let i = 0; i < commands.length; i++) {\n const command = commands[i]!;\n const cmdStdin = i === 0 ? stdinSource : pipes[i - 1]!.getReadableStream();\n const cmdStdout = i === commands.length - 1 ? stdout : pipes[i]!;\n\n promises.push(\n this.executeNode(command, cmdStdin, cmdStdout, stderr).then((code) => {\n // Close pipe when command finishes\n if (i < commands.length - 1) {\n pipes[i]!.close();\n }\n return code;\n })\n );\n }\n\n // Wait for all commands and return last exit code\n const results = await Promise.all(promises);\n return results[results.length - 1]!;\n }\n\n private async executeSequence(\n commands: ASTNode[],\n stdinSource: AsyncIterable<Uint8Array> | null,\n stdout: OutputCollector,\n stderr: OutputCollector\n ): Promise<number> {\n let lastExitCode = 0;\n\n for (const command of commands) {\n lastExitCode = await this.executeNode(command, stdinSource, stdout, stderr);\n }\n\n return lastExitCode;\n }\n\n private async executeAnd(\n left: ASTNode,\n right: ASTNode,\n stdinSource: AsyncIterable<Uint8Array> | null,\n stdout: OutputCollector,\n stderr: OutputCollector\n ): Promise<number> {\n const leftCode = await this.executeNode(left, stdinSource, stdout, stderr);\n if (leftCode !== 0) {\n return leftCode;\n }\n return this.executeNode(right, stdinSource, stdout, stderr);\n }\n\n private async executeOr(\n left: ASTNode,\n right: ASTNode,\n stdinSource: AsyncIterable<Uint8Array> | null,\n stdout: OutputCollector,\n stderr: OutputCollector\n ): Promise<number> {\n const leftCode = await this.executeNode(left, stdinSource, stdout, stderr);\n if (leftCode === 0) {\n return 0;\n }\n return this.executeNode(right, stdinSource, stdout, stderr);\n }\n\n private async evaluateNode(node: ASTNode, localEnv?: Record<string, string>): Promise<string> {\n const env = localEnv ?? this.env;\n switch (node.type) {\n case \"literal\":\n return node.value;\n case \"variable\":\n return env[node.name] ?? \"\";\n case \"glob\":\n return node.pattern;\n case \"concat\": {\n const parts = await Promise.all(node.parts.map((p) => this.evaluateNode(p, localEnv)));\n return parts.join(\"\");\n }\n case \"substitution\": {\n // Execute the command and capture output\n const subStdout = createStdout();\n const subStderr = createStderr();\n await this.executeNode(node.command, null, subStdout, subStderr);\n subStdout.close();\n const output = await subStdout.collect();\n // Trim trailing newlines\n return output.toString(\"utf-8\").replace(/\\n+$/, \"\");\n }\n default:\n throw new Error(`Cannot evaluate node type: ${node.type}`);\n }\n }\n\n setCwd(cwd: string): void {\n this.cwd = cwd;\n }\n\n setEnv(vars: Record<string, string>): void {\n Object.assign(this.env, vars);\n }\n\n getCwd(): string {\n return this.cwd;\n }\n\n getEnv(): Record<string, string> {\n return { ...this.env };\n }\n}\n"
|
|
5
|
+
"import type { ASTNode, CommandNode, Redirect } from \"../parser/ast.mjs\";\nimport type { Command, VirtualFS, ExecResult, OutputCollector, RedirectObjectMap } from \"../types.mjs\";\nimport { createCommandContext } from \"./context.mjs\";\nimport { createStdin } from \"../io/stdin.mjs\";\nimport { createStdout, createStderr, createPipe, PipeBuffer, createBufferTargetCollector } from \"../io/stdout.mjs\";\nimport { Lexer } from \"../lexer/lexer.mjs\";\nimport { Parser } from \"../parser/parser.mjs\";\n\nexport interface InterpreterOptions {\n fs: VirtualFS;\n cwd: string;\n env: Record<string, string>;\n commands: Record<string, Command>;\n redirectObjects?: RedirectObjectMap;\n}\n\nexport class Interpreter {\n private fs: VirtualFS;\n private cwd: string;\n private env: Record<string, string>;\n private commands: Record<string, Command>;\n private redirectObjects: RedirectObjectMap;\n\n constructor(options: InterpreterOptions) {\n this.fs = options.fs;\n this.cwd = options.cwd;\n this.env = { ...options.env };\n this.commands = options.commands;\n this.redirectObjects = options.redirectObjects ?? {};\n }\n\n async execute(ast: ASTNode): Promise<ExecResult> {\n const stdout = createStdout();\n const stderr = createStderr();\n\n const exitCode = await this.executeNode(ast, null, stdout, stderr);\n\n stdout.close();\n stderr.close();\n\n return {\n stdout: await stdout.collect(),\n stderr: await stderr.collect(),\n exitCode,\n };\n }\n\n private async executeNode(\n node: ASTNode,\n stdinSource: AsyncIterable<Uint8Array> | null,\n stdout: OutputCollector,\n stderr: OutputCollector\n ): Promise<number> {\n switch (node.type) {\n case \"command\":\n return this.executeCommand(node, stdinSource, stdout, stderr);\n case \"pipeline\":\n return this.executePipeline(node.commands, stdinSource, stdout, stderr);\n case \"sequence\":\n return this.executeSequence(node.commands, stdinSource, stdout, stderr);\n case \"and\":\n return this.executeAnd(node.left, node.right, stdinSource, stdout, stderr);\n case \"or\":\n return this.executeOr(node.left, node.right, stdinSource, stdout, stderr);\n default:\n throw new Error(`Cannot execute node type: ${node.type}`);\n }\n }\n\n private async executeCommand(\n node: CommandNode,\n stdinSource: AsyncIterable<Uint8Array> | null,\n stdout: OutputCollector,\n stderr: OutputCollector\n ): Promise<number> {\n // Create local env with assignments\n const localEnv = { ...this.env };\n for (const assignment of node.assignments) {\n localEnv[assignment.name] = await this.evaluateNode(assignment.value);\n }\n\n // If there's no command name but there are assignments, just update env\n const name = await this.evaluateNode(node.name);\n if (name === \"\" && node.assignments.length > 0) {\n for (const assignment of node.assignments) {\n this.env[assignment.name] = await this.evaluateNode(assignment.value);\n }\n return 0;\n }\n\n // Evaluate arguments using localEnv for scoped variable expansion\n const args: string[] = [];\n for (const arg of node.args) {\n const evaluated = await this.evaluateNode(arg, localEnv);\n // Glob expansion returns multiple values\n if (arg.type === \"glob\") {\n const matches = await this.fs.glob(evaluated, { cwd: this.cwd });\n if (matches.length > 0) {\n args.push(...matches);\n } else {\n // No matches - use pattern as-is\n args.push(evaluated);\n }\n } else {\n args.push(evaluated);\n }\n }\n\n // Handle redirects\n let actualStdin = stdinSource;\n let actualStdout: OutputCollector = stdout;\n let actualStderr: OutputCollector = stderr;\n let stderrToStdout = false;\n let stdoutToStderr = false;\n const fileWritePromises: Promise<void>[] = [];\n\n for (const redirect of node.redirects) {\n const result = await this.handleRedirect(\n redirect,\n actualStdin,\n actualStdout,\n actualStderr\n );\n actualStdin = result.stdin;\n actualStdout = result.stdout;\n actualStderr = result.stderr;\n stderrToStdout = result.stderrToStdout || stderrToStdout;\n stdoutToStderr = result.stdoutToStderr || stdoutToStderr;\n if (result.fileWritePromise) {\n fileWritePromises.push(result.fileWritePromise);\n }\n }\n\n // Handle stderr->stdout redirect\n if (stderrToStdout) {\n actualStderr = actualStdout;\n }\n if (stdoutToStderr) {\n actualStdout = actualStderr;\n }\n\n // Look up command\n const command = this.commands[name];\n if (!command) {\n await stderr.writeText(`${name}: command not found\\n`);\n return 127;\n }\n\n // Create context and execute\n const ctx = createCommandContext({\n args,\n stdin: createStdin(actualStdin),\n stdout: actualStdout,\n stderr: actualStderr,\n fs: this.fs,\n cwd: this.cwd,\n env: localEnv,\n });\n\n let exitCode: number;\n try {\n exitCode = await command(ctx);\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n await stderr.writeText(`${name}: ${message}\\n`);\n exitCode = 1;\n }\n\n // Close redirect collectors and wait for file writes\n if (actualStdout !== stdout) {\n actualStdout.close();\n }\n if (actualStderr !== stderr && actualStderr !== actualStdout) {\n actualStderr.close();\n }\n\n // Wait for all file write operations to complete\n await Promise.all(fileWritePromises);\n\n return exitCode;\n }\n\n private async handleRedirect(\n redirect: Redirect,\n stdin: AsyncIterable<Uint8Array> | null,\n stdout: OutputCollector,\n stderr: OutputCollector\n ): Promise<{\n stdin: AsyncIterable<Uint8Array> | null;\n stdout: OutputCollector;\n stderr: OutputCollector;\n stderrToStdout?: boolean;\n stdoutToStderr?: boolean;\n fileWritePromise?: Promise<void>;\n }> {\n const target = await this.evaluateNode(redirect.target);\n\n // Check if target is a redirect object marker\n if (target in this.redirectObjects) {\n return this.handleObjectRedirect(redirect.mode, this.redirectObjects[target]!, stdin, stdout, stderr);\n }\n\n switch (redirect.mode) {\n case \"<\": {\n // Input redirect\n const path = this.fs.resolve(this.cwd, target);\n const content = await this.fs.readFile(path);\n return {\n stdin: (async function* () {\n yield new Uint8Array(content);\n })(),\n stdout,\n stderr,\n };\n }\n case \">\": {\n // Output redirect (overwrite)\n const path = this.fs.resolve(this.cwd, target);\n const collector = createStdout();\n const fileWritePromise = (async () => {\n const data = await collector.collect();\n await this.fs.writeFile(path, data);\n })();\n return { stdin, stdout: collector, stderr, fileWritePromise };\n }\n case \">>\": {\n // Output redirect (append)\n const path = this.fs.resolve(this.cwd, target);\n const collector = createStdout();\n const fileWritePromise = (async () => {\n const data = await collector.collect();\n await this.fs.appendFile(path, data);\n })();\n return { stdin, stdout: collector, stderr, fileWritePromise };\n }\n case \"2>\": {\n // Stderr redirect (overwrite)\n const path = this.fs.resolve(this.cwd, target);\n const collector = createStderr();\n const fileWritePromise = (async () => {\n const data = await collector.collect();\n await this.fs.writeFile(path, data);\n })();\n return { stdin, stdout, stderr: collector, fileWritePromise };\n }\n case \"2>>\": {\n // Stderr redirect (append)\n const path = this.fs.resolve(this.cwd, target);\n const collector = createStderr();\n const fileWritePromise = (async () => {\n const data = await collector.collect();\n await this.fs.appendFile(path, data);\n })();\n return { stdin, stdout, stderr: collector, fileWritePromise };\n }\n case \"&>\": {\n // Both to file (overwrite)\n const path = this.fs.resolve(this.cwd, target);\n const collector = createStdout();\n const fileWritePromise = (async () => {\n const data = await collector.collect();\n await this.fs.writeFile(path, data);\n })();\n return { stdin, stdout: collector, stderr: collector, fileWritePromise };\n }\n case \"&>>\": {\n // Both to file (append)\n const path = this.fs.resolve(this.cwd, target);\n const collector = createStdout();\n const fileWritePromise = (async () => {\n const data = await collector.collect();\n await this.fs.appendFile(path, data);\n })();\n return { stdin, stdout: collector, stderr: collector, fileWritePromise };\n }\n case \"2>&1\":\n return { stdin, stdout, stderr, stderrToStdout: true };\n case \"1>&2\":\n return { stdin, stdout, stderr, stdoutToStderr: true };\n default:\n return { stdin, stdout, stderr };\n }\n }\n\n private async handleObjectRedirect(\n mode: string,\n obj: Buffer | Blob | Response | string,\n stdin: AsyncIterable<Uint8Array> | null,\n stdout: OutputCollector,\n stderr: OutputCollector\n ): Promise<{\n stdin: AsyncIterable<Uint8Array> | null;\n stdout: OutputCollector;\n stderr: OutputCollector;\n stderrToStdout?: boolean;\n stdoutToStderr?: boolean;\n fileWritePromise?: Promise<void>;\n }> {\n switch (mode) {\n case \"<\": {\n // Input from object\n const data = await this.readFromObject(obj);\n return {\n stdin: (async function* () {\n yield data;\n })(),\n stdout,\n stderr,\n };\n }\n case \">\":\n case \">>\": {\n // Output to object (only Buffer supported)\n if (!Buffer.isBuffer(obj)) {\n throw new Error(\"Output redirection only supports Buffer targets\");\n }\n const collector = createBufferTargetCollector(obj);\n return { stdin, stdout: collector, stderr };\n }\n case \"2>\":\n case \"2>>\": {\n // Stderr to object (only Buffer supported)\n if (!Buffer.isBuffer(obj)) {\n throw new Error(\"Stderr redirection only supports Buffer targets\");\n }\n const collector = createBufferTargetCollector(obj);\n return { stdin, stdout, stderr: collector };\n }\n case \"&>\":\n case \"&>>\": {\n // Both to object (only Buffer supported)\n if (!Buffer.isBuffer(obj)) {\n throw new Error(\"Combined redirection only supports Buffer targets\");\n }\n const collector = createBufferTargetCollector(obj);\n return { stdin, stdout: collector, stderr: collector };\n }\n default:\n return { stdin, stdout, stderr };\n }\n }\n\n private async readFromObject(obj: Buffer | Blob | Response | string): Promise<Uint8Array> {\n if (Buffer.isBuffer(obj)) {\n return new Uint8Array(obj);\n }\n if (obj instanceof Blob) {\n return new Uint8Array(await obj.arrayBuffer());\n }\n if (obj instanceof Response) {\n return new Uint8Array(await obj.arrayBuffer());\n }\n if (typeof obj === \"string\") {\n return new TextEncoder().encode(obj);\n }\n throw new Error(\"Unsupported redirect object type\");\n }\n\n private async executePipeline(\n commands: ASTNode[],\n stdinSource: AsyncIterable<Uint8Array> | null,\n stdout: OutputCollector,\n stderr: OutputCollector\n ): Promise<number> {\n if (commands.length === 0) return 0;\n if (commands.length === 1) {\n return this.executeNode(commands[0]!, stdinSource, stdout, stderr);\n }\n\n // Create pipes between commands\n const pipes: PipeBuffer[] = [];\n for (let i = 0; i < commands.length - 1; i++) {\n pipes.push(createPipe());\n }\n\n // Execute all commands concurrently\n const promises: Promise<number>[] = [];\n\n for (let i = 0; i < commands.length; i++) {\n const command = commands[i]!;\n const cmdStdin = i === 0 ? stdinSource : pipes[i - 1]!.getReadableStream();\n const cmdStdout = i === commands.length - 1 ? stdout : pipes[i]!;\n\n promises.push(\n this.executeNode(command, cmdStdin, cmdStdout, stderr).then((code) => {\n // Close pipe when command finishes\n if (i < commands.length - 1) {\n pipes[i]!.close();\n }\n return code;\n })\n );\n }\n\n // Wait for all commands and return last exit code\n const results = await Promise.all(promises);\n return results[results.length - 1]!;\n }\n\n private async executeSequence(\n commands: ASTNode[],\n stdinSource: AsyncIterable<Uint8Array> | null,\n stdout: OutputCollector,\n stderr: OutputCollector\n ): Promise<number> {\n let lastExitCode = 0;\n\n for (const command of commands) {\n lastExitCode = await this.executeNode(command, stdinSource, stdout, stderr);\n }\n\n return lastExitCode;\n }\n\n private async executeAnd(\n left: ASTNode,\n right: ASTNode,\n stdinSource: AsyncIterable<Uint8Array> | null,\n stdout: OutputCollector,\n stderr: OutputCollector\n ): Promise<number> {\n const leftCode = await this.executeNode(left, stdinSource, stdout, stderr);\n if (leftCode !== 0) {\n return leftCode;\n }\n return this.executeNode(right, stdinSource, stdout, stderr);\n }\n\n private async executeOr(\n left: ASTNode,\n right: ASTNode,\n stdinSource: AsyncIterable<Uint8Array> | null,\n stdout: OutputCollector,\n stderr: OutputCollector\n ): Promise<number> {\n const leftCode = await this.executeNode(left, stdinSource, stdout, stderr);\n if (leftCode === 0) {\n return 0;\n }\n return this.executeNode(right, stdinSource, stdout, stderr);\n }\n\n private async evaluateNode(node: ASTNode, localEnv?: Record<string, string>): Promise<string> {\n const env = localEnv ?? this.env;\n switch (node.type) {\n case \"literal\":\n return node.value;\n case \"variable\":\n return env[node.name] ?? \"\";\n case \"glob\":\n return node.pattern;\n case \"concat\": {\n const parts = await Promise.all(node.parts.map((p) => this.evaluateNode(p, localEnv)));\n return parts.join(\"\");\n }\n case \"substitution\": {\n // Execute the command and capture output\n const subStdout = createStdout();\n const subStderr = createStderr();\n await this.executeNode(node.command, null, subStdout, subStderr);\n subStdout.close();\n const output = await subStdout.collect();\n // Trim trailing newlines\n return output.toString(\"utf-8\").replace(/\\n+$/, \"\");\n }\n default:\n throw new Error(`Cannot evaluate node type: ${node.type}`);\n }\n }\n\n setCwd(cwd: string): void {\n this.cwd = cwd;\n }\n\n setEnv(vars: Record<string, string>): void {\n Object.assign(this.env, vars);\n }\n\n getCwd(): string {\n return this.cwd;\n }\n\n getEnv(): Record<string, string> {\n return { ...this.env };\n }\n}\n"
|
|
6
6
|
],
|
|
7
|
-
"mappings": ";AAEA;AACA;AACA;AAAA;
|
|
8
|
-
"debugId": "
|
|
7
|
+
"mappings": "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",
|
|
8
|
+
"debugId": "600AE415CC91B77B64756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
|
@@ -116,12 +116,57 @@ function createStderr() {
|
|
|
116
116
|
function createPipe() {
|
|
117
117
|
return new PipeBuffer;
|
|
118
118
|
}
|
|
119
|
+
|
|
120
|
+
class BufferTargetCollector {
|
|
121
|
+
target;
|
|
122
|
+
offset = 0;
|
|
123
|
+
closed = false;
|
|
124
|
+
closeResolvers = [];
|
|
125
|
+
constructor(target) {
|
|
126
|
+
this.target = target;
|
|
127
|
+
}
|
|
128
|
+
async write(chunk) {
|
|
129
|
+
if (this.closed) {
|
|
130
|
+
throw new Error("Output stream is closed");
|
|
131
|
+
}
|
|
132
|
+
for (let i = 0;i < chunk.length && this.offset < this.target.length; i++) {
|
|
133
|
+
this.target[this.offset++] = chunk[i];
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
async writeText(str) {
|
|
137
|
+
const bytes = new TextEncoder().encode(str);
|
|
138
|
+
await this.write(bytes);
|
|
139
|
+
}
|
|
140
|
+
close() {
|
|
141
|
+
this.closed = true;
|
|
142
|
+
for (const resolve of this.closeResolvers) {
|
|
143
|
+
resolve();
|
|
144
|
+
}
|
|
145
|
+
this.closeResolvers = [];
|
|
146
|
+
}
|
|
147
|
+
async collect() {
|
|
148
|
+
while (!this.closed) {
|
|
149
|
+
await new Promise((resolve) => {
|
|
150
|
+
this.closeResolvers.push(resolve);
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
return this.target.subarray(0, this.offset);
|
|
154
|
+
}
|
|
155
|
+
async* getReadableStream() {
|
|
156
|
+
yield this.target.subarray(0, this.offset);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
function createBufferTargetCollector(target) {
|
|
160
|
+
return new BufferTargetCollector(target);
|
|
161
|
+
}
|
|
119
162
|
export {
|
|
120
163
|
createStdout,
|
|
121
164
|
createStderr,
|
|
122
165
|
createPipe,
|
|
166
|
+
createBufferTargetCollector,
|
|
123
167
|
PipeBuffer,
|
|
124
|
-
OutputCollectorImpl
|
|
168
|
+
OutputCollectorImpl,
|
|
169
|
+
BufferTargetCollector
|
|
125
170
|
};
|
|
126
171
|
|
|
127
|
-
//# debugId=
|
|
172
|
+
//# debugId=8AA51761A00AF04864756E2164756E21
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/io/stdout.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"import type { Stdout, Stderr, OutputCollector } from \"../types.mjs\";\n\nexport class OutputCollectorImpl implements OutputCollector {\n private chunks: Uint8Array[] = [];\n private closed: boolean = false;\n private closeResolvers: Array<() => void> = [];\n private resolveWait: (() => void) | null = null;\n private waitPromise: Promise<void> | null = null;\n\n async write(chunk: Uint8Array): Promise<void> {\n if (this.closed) {\n throw new Error(\"Output stream is closed\");\n }\n this.chunks.push(chunk);\n if (this.resolveWait) {\n this.resolveWait();\n this.resolveWait = null;\n this.waitPromise = null;\n }\n }\n\n async writeText(str: string): Promise<void> {\n await this.write(new TextEncoder().encode(str));\n }\n\n close(): void {\n this.closed = true;\n if (this.resolveWait) {\n this.resolveWait();\n this.resolveWait = null;\n this.waitPromise = null;\n }\n // Wake up anyone waiting for close\n for (const resolve of this.closeResolvers) {\n resolve();\n }\n this.closeResolvers = [];\n }\n\n async collect(): Promise<Buffer> {\n // Wait until closed\n while (!this.closed) {\n await new Promise<void>((resolve) => {\n this.closeResolvers.push(resolve);\n });\n }\n return Buffer.concat(this.chunks);\n }\n\n async *getReadableStream(): AsyncIterable<Uint8Array> {\n let index = 0;\n\n while (true) {\n while (index < this.chunks.length) {\n yield this.chunks[index]!;\n index++;\n }\n\n if (this.closed) {\n break;\n }\n\n // Wait for more data or close\n if (!this.waitPromise) {\n this.waitPromise = new Promise<void>((resolve) => {\n this.resolveWait = resolve;\n });\n }\n await this.waitPromise;\n }\n }\n}\n\nexport class PipeBuffer implements OutputCollector, Stdout {\n private chunks: Uint8Array[] = [];\n private closed: boolean = false;\n private waitingReaders: Array<() => void> = [];\n private readIndex: number = 0;\n\n async write(chunk: Uint8Array): Promise<void> {\n if (this.closed) {\n throw new Error(\"Pipe is closed\");\n }\n this.chunks.push(chunk);\n // Wake up any waiting readers\n for (const resolve of this.waitingReaders) {\n resolve();\n }\n this.waitingReaders = [];\n }\n\n async writeText(str: string): Promise<void> {\n await this.write(new TextEncoder().encode(str));\n }\n\n close(): void {\n this.closed = true;\n // Wake up any waiting readers\n for (const resolve of this.waitingReaders) {\n resolve();\n }\n this.waitingReaders = [];\n }\n\n async collect(): Promise<Buffer> {\n // Wait until closed\n while (!this.closed) {\n await new Promise<void>((resolve) => {\n this.waitingReaders.push(resolve);\n });\n }\n return Buffer.concat(this.chunks);\n }\n\n async *getReadableStream(): AsyncIterable<Uint8Array> {\n while (true) {\n // Yield any available chunks\n while (this.readIndex < this.chunks.length) {\n yield this.chunks[this.readIndex]!;\n this.readIndex++;\n }\n\n if (this.closed) {\n break;\n }\n\n // Wait for more data\n await new Promise<void>((resolve) => {\n this.waitingReaders.push(resolve);\n });\n }\n }\n}\n\nexport function createStdout(): OutputCollector {\n return new OutputCollectorImpl();\n}\n\nexport function createStderr(): OutputCollector {\n return new OutputCollectorImpl();\n}\n\nexport function createPipe(): PipeBuffer {\n return new PipeBuffer();\n}\n"
|
|
5
|
+
"import type { Stdout, Stderr, OutputCollector } from \"../types.mjs\";\n\nexport class OutputCollectorImpl implements OutputCollector {\n private chunks: Uint8Array[] = [];\n private closed: boolean = false;\n private closeResolvers: Array<() => void> = [];\n private resolveWait: (() => void) | null = null;\n private waitPromise: Promise<void> | null = null;\n\n async write(chunk: Uint8Array): Promise<void> {\n if (this.closed) {\n throw new Error(\"Output stream is closed\");\n }\n this.chunks.push(chunk);\n if (this.resolveWait) {\n this.resolveWait();\n this.resolveWait = null;\n this.waitPromise = null;\n }\n }\n\n async writeText(str: string): Promise<void> {\n await this.write(new TextEncoder().encode(str));\n }\n\n close(): void {\n this.closed = true;\n if (this.resolveWait) {\n this.resolveWait();\n this.resolveWait = null;\n this.waitPromise = null;\n }\n // Wake up anyone waiting for close\n for (const resolve of this.closeResolvers) {\n resolve();\n }\n this.closeResolvers = [];\n }\n\n async collect(): Promise<Buffer> {\n // Wait until closed\n while (!this.closed) {\n await new Promise<void>((resolve) => {\n this.closeResolvers.push(resolve);\n });\n }\n return Buffer.concat(this.chunks);\n }\n\n async *getReadableStream(): AsyncIterable<Uint8Array> {\n let index = 0;\n\n while (true) {\n while (index < this.chunks.length) {\n yield this.chunks[index]!;\n index++;\n }\n\n if (this.closed) {\n break;\n }\n\n // Wait for more data or close\n if (!this.waitPromise) {\n this.waitPromise = new Promise<void>((resolve) => {\n this.resolveWait = resolve;\n });\n }\n await this.waitPromise;\n }\n }\n}\n\nexport class PipeBuffer implements OutputCollector, Stdout {\n private chunks: Uint8Array[] = [];\n private closed: boolean = false;\n private waitingReaders: Array<() => void> = [];\n private readIndex: number = 0;\n\n async write(chunk: Uint8Array): Promise<void> {\n if (this.closed) {\n throw new Error(\"Pipe is closed\");\n }\n this.chunks.push(chunk);\n // Wake up any waiting readers\n for (const resolve of this.waitingReaders) {\n resolve();\n }\n this.waitingReaders = [];\n }\n\n async writeText(str: string): Promise<void> {\n await this.write(new TextEncoder().encode(str));\n }\n\n close(): void {\n this.closed = true;\n // Wake up any waiting readers\n for (const resolve of this.waitingReaders) {\n resolve();\n }\n this.waitingReaders = [];\n }\n\n async collect(): Promise<Buffer> {\n // Wait until closed\n while (!this.closed) {\n await new Promise<void>((resolve) => {\n this.waitingReaders.push(resolve);\n });\n }\n return Buffer.concat(this.chunks);\n }\n\n async *getReadableStream(): AsyncIterable<Uint8Array> {\n while (true) {\n // Yield any available chunks\n while (this.readIndex < this.chunks.length) {\n yield this.chunks[this.readIndex]!;\n this.readIndex++;\n }\n\n if (this.closed) {\n break;\n }\n\n // Wait for more data\n await new Promise<void>((resolve) => {\n this.waitingReaders.push(resolve);\n });\n }\n }\n}\n\nexport function createStdout(): OutputCollector {\n return new OutputCollectorImpl();\n}\n\nexport function createStderr(): OutputCollector {\n return new OutputCollectorImpl();\n}\n\nexport function createPipe(): PipeBuffer {\n return new PipeBuffer();\n}\n\nexport class BufferTargetCollector implements OutputCollector {\n private target: Buffer;\n private offset: number = 0;\n private closed: boolean = false;\n private closeResolvers: Array<() => void> = [];\n\n constructor(target: Buffer) {\n this.target = target;\n }\n\n async write(chunk: Uint8Array): Promise<void> {\n if (this.closed) {\n throw new Error(\"Output stream is closed\");\n }\n for (let i = 0; i < chunk.length && this.offset < this.target.length; i++) {\n this.target[this.offset++] = chunk[i]!;\n }\n }\n\n async writeText(str: string): Promise<void> {\n const bytes = new TextEncoder().encode(str);\n await this.write(bytes);\n }\n\n close(): void {\n this.closed = true;\n for (const resolve of this.closeResolvers) {\n resolve();\n }\n this.closeResolvers = [];\n }\n\n async collect(): Promise<Buffer> {\n while (!this.closed) {\n await new Promise<void>((resolve) => {\n this.closeResolvers.push(resolve);\n });\n }\n return this.target.subarray(0, this.offset) as Buffer;\n }\n\n async *getReadableStream(): AsyncIterable<Uint8Array> {\n yield this.target.subarray(0, this.offset);\n }\n}\n\nexport function createBufferTargetCollector(target: Buffer): OutputCollector {\n return new BufferTargetCollector(target);\n}\n"
|
|
6
6
|
],
|
|
7
|
-
"mappings": "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",
|
|
8
|
-
"debugId": "
|
|
7
|
+
"mappings": "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",
|
|
8
|
+
"debugId": "8AA51761A00AF04864756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/shell-dsl.ts
|
|
2
|
-
import { isRawValue } from "./types.mjs";
|
|
2
|
+
import { isRawValue, isRedirectObject } from "./types.mjs";
|
|
3
3
|
import { Lexer } from "./lexer/lexer.mjs";
|
|
4
4
|
import { Parser } from "./parser/parser.mjs";
|
|
5
5
|
import { Interpreter } from "./interpreter/interpreter.mjs";
|
|
@@ -24,16 +24,40 @@ class ShellDSL {
|
|
|
24
24
|
}
|
|
25
25
|
tag(strings, ...values) {
|
|
26
26
|
let source = strings[0] ?? "";
|
|
27
|
+
const redirectObjects = {};
|
|
28
|
+
let objIndex = 0;
|
|
27
29
|
for (let i = 0;i < values.length; i++) {
|
|
28
30
|
const value = values[i];
|
|
31
|
+
const precedingString = strings[i] ?? "";
|
|
29
32
|
if (isRawValue(value)) {
|
|
30
33
|
source += value.raw;
|
|
34
|
+
} else if (this.isRedirectTarget(precedingString, value)) {
|
|
35
|
+
const marker = `__REDIR_OBJ_${objIndex++}__`;
|
|
36
|
+
redirectObjects[marker] = value;
|
|
37
|
+
source += marker;
|
|
31
38
|
} else {
|
|
32
39
|
source += escapeForInterpolation(value);
|
|
33
40
|
}
|
|
34
41
|
source += strings[i + 1] ?? "";
|
|
35
42
|
}
|
|
36
|
-
return this.createPromise(source);
|
|
43
|
+
return this.createPromise(source, { redirectObjects });
|
|
44
|
+
}
|
|
45
|
+
isRedirectTarget(precedingString, value) {
|
|
46
|
+
if (!isRedirectObject(value) || isRawValue(value)) {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
const trimmed = precedingString.trimEnd();
|
|
50
|
+
const afterRedirectOp = /(<|>|>>|2>|2>>|&>|&>>)\s*$/.test(trimmed);
|
|
51
|
+
if (!afterRedirectOp) {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
if (Buffer.isBuffer(value) || value instanceof Blob || value instanceof Response) {
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
if (typeof value === "string") {
|
|
58
|
+
return /<\s*$/.test(trimmed);
|
|
59
|
+
}
|
|
60
|
+
return false;
|
|
37
61
|
}
|
|
38
62
|
createPromise(source, options) {
|
|
39
63
|
const shell = this;
|
|
@@ -45,7 +69,8 @@ class ShellDSL {
|
|
|
45
69
|
fs: shell.fs,
|
|
46
70
|
cwd,
|
|
47
71
|
env,
|
|
48
|
-
commands: shell.commands
|
|
72
|
+
commands: shell.commands,
|
|
73
|
+
redirectObjects: options?.redirectObjects
|
|
49
74
|
});
|
|
50
75
|
const tokens = shell.lex(source);
|
|
51
76
|
const ast = shell.parse(tokens);
|
|
@@ -129,4 +154,4 @@ export {
|
|
|
129
154
|
ShellDSL
|
|
130
155
|
};
|
|
131
156
|
|
|
132
|
-
//# debugId=
|
|
157
|
+
//# debugId=8D1028F11D1C104A64756E2164756E21
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/shell-dsl.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"import type { ShellConfig, Command, VirtualFS, ExecResult } from \"./types.mjs\";\nimport { isRawValue } from \"./types.mjs\";\nimport type { Token } from \"./lexer/tokens.mjs\";\nimport type { ASTNode } from \"./parser/ast.mjs\";\nimport { Lexer } from \"./lexer/lexer.mjs\";\nimport { Parser } from \"./parser/parser.mjs\";\nimport { Interpreter } from \"./interpreter/interpreter.mjs\";\nimport { ShellPromise } from \"./shell-promise.mjs\";\nimport { escape, escapeForInterpolation } from \"./utils/escape.mjs\";\n\nexport interface Program {\n ast: ASTNode;\n source: string;\n}\n\nexport class ShellDSL {\n private fs: VirtualFS;\n private initialCwd: string;\n private initialEnv: Record<string, string>;\n private currentCwd: string;\n private currentEnv: Record<string, string>;\n private commands: Record<string, Command>;\n private shouldThrow: boolean = true;\n\n constructor(config: ShellConfig) {\n this.fs = config.fs;\n this.initialCwd = config.cwd;\n this.initialEnv = { ...config.env };\n this.currentCwd = config.cwd;\n this.currentEnv = { ...config.env };\n this.commands = config.commands;\n }\n\n // Template tag function\n tag(strings: TemplateStringsArray, ...values: unknown[]): ShellPromise {\n // Build the command string with escaped interpolations\n let source = strings[0] ?? \"\";\n for (let i = 0; i < values.length; i++) {\n const value = values[i];\n if (isRawValue(value)) {\n source += value.raw;\n } else {\n source += escapeForInterpolation(value);\n }\n source += strings[i + 1] ?? \"\";\n }\n\n return this.createPromise(source);\n }\n\n private createPromise(source: string, options?: { cwd?: string; env?: Record<string, string>; shouldThrow?: boolean }): ShellPromise {\n const shell = this;\n\n return new ShellPromise({\n execute: async (overrides) => {\n const cwd = overrides?.cwd ?? options?.cwd ?? shell.currentCwd;\n const env = { ...shell.currentEnv, ...options?.env, ...overrides?.env };\n\n const interpreter = new Interpreter({\n fs: shell.fs,\n cwd,\n env,\n commands: shell.commands,\n });\n\n const tokens = shell.lex(source);\n const ast = shell.parse(tokens);\n return interpreter.execute(ast);\n },\n cwdOverride: options?.cwd,\n envOverride: options?.env,\n shouldThrow: options?.shouldThrow ?? this.shouldThrow,\n });\n }\n\n // Global defaults\n cwd(path: string): void {\n this.currentCwd = path;\n }\n\n env(vars: Record<string, string>): void {\n Object.assign(this.currentEnv, vars);\n }\n\n throws(enable: boolean): void {\n this.shouldThrow = enable;\n }\n\n resetCwd(): void {\n this.currentCwd = this.initialCwd;\n }\n\n resetEnv(): void {\n this.currentEnv = { ...this.initialEnv };\n }\n\n // Utility\n escape(str: string): string {\n return escape(str);\n }\n\n // Low-level API\n lex(source: string): Token[] {\n return new Lexer(source).tokenize();\n }\n\n parse(tokens: Token[]): ASTNode {\n return new Parser(tokens).parse();\n }\n\n compile(ast: ASTNode): Program {\n // For now, the \"program\" is just the AST with source reconstruction\n return {\n ast,\n source: \"\", // Could reconstruct source from AST if needed\n };\n }\n\n async run(program: Program): Promise<ExecResult> {\n const interpreter = new Interpreter({\n fs: this.fs,\n cwd: this.currentCwd,\n env: this.currentEnv,\n commands: this.commands,\n });\n\n return interpreter.execute(program.ast);\n }\n}\n\n// Factory function that returns a callable template tag\nexport function createShellDSL(config: ShellConfig): ShellDSL & ((strings: TemplateStringsArray, ...values: unknown[]) => ShellPromise) {\n const shell = new ShellDSL(config);\n\n // Create a function that acts as both tag and shell instance\n const tag = (strings: TemplateStringsArray, ...values: unknown[]) => {\n return shell.tag(strings, ...values);\n };\n\n // Copy all properties and methods from shell to tag function\n Object.setPrototypeOf(tag, ShellDSL.prototype);\n Object.assign(tag, {\n fs: (shell as any).fs,\n initialCwd: (shell as any).initialCwd,\n initialEnv: (shell as any).initialEnv,\n currentCwd: (shell as any).currentCwd,\n currentEnv: (shell as any).currentEnv,\n commands: (shell as any).commands,\n shouldThrow: (shell as any).shouldThrow,\n });\n\n // Bind methods\n (tag as any).cwd = shell.cwd.bind(shell);\n (tag as any).env = shell.env.bind(shell);\n (tag as any).throws = shell.throws.bind(shell);\n (tag as any).resetCwd = shell.resetCwd.bind(shell);\n (tag as any).resetEnv = shell.resetEnv.bind(shell);\n (tag as any).escape = shell.escape.bind(shell);\n (tag as any).lex = shell.lex.bind(shell);\n (tag as any).parse = shell.parse.bind(shell);\n (tag as any).compile = shell.compile.bind(shell);\n (tag as any).run = shell.run.bind(shell);\n (tag as any).tag = shell.tag.bind(shell);\n\n return tag as ShellDSL & ((strings: TemplateStringsArray, ...values: unknown[]) => ShellPromise);\n}\n"
|
|
5
|
+
"import type { ShellConfig, Command, VirtualFS, ExecResult, RedirectObjectMap } from \"./types.mjs\";\nimport { isRawValue, isRedirectObject } from \"./types.mjs\";\nimport type { Token } from \"./lexer/tokens.mjs\";\nimport type { ASTNode } from \"./parser/ast.mjs\";\nimport { Lexer } from \"./lexer/lexer.mjs\";\nimport { Parser } from \"./parser/parser.mjs\";\nimport { Interpreter } from \"./interpreter/interpreter.mjs\";\nimport { ShellPromise } from \"./shell-promise.mjs\";\nimport { escape, escapeForInterpolation } from \"./utils/escape.mjs\";\n\nexport interface Program {\n ast: ASTNode;\n source: string;\n}\n\nexport class ShellDSL {\n private fs: VirtualFS;\n private initialCwd: string;\n private initialEnv: Record<string, string>;\n private currentCwd: string;\n private currentEnv: Record<string, string>;\n private commands: Record<string, Command>;\n private shouldThrow: boolean = true;\n\n constructor(config: ShellConfig) {\n this.fs = config.fs;\n this.initialCwd = config.cwd;\n this.initialEnv = { ...config.env };\n this.currentCwd = config.cwd;\n this.currentEnv = { ...config.env };\n this.commands = config.commands;\n }\n\n // Template tag function\n tag(strings: TemplateStringsArray, ...values: unknown[]): ShellPromise {\n // Build the command string with escaped interpolations\n let source = strings[0] ?? \"\";\n const redirectObjects: RedirectObjectMap = {};\n let objIndex = 0;\n\n for (let i = 0; i < values.length; i++) {\n const value = values[i];\n const precedingString = strings[i] ?? \"\";\n\n if (isRawValue(value)) {\n source += value.raw;\n } else if (this.isRedirectTarget(precedingString, value)) {\n // Value appears after a redirect operator - store as redirect object\n const marker = `__REDIR_OBJ_${objIndex++}__`;\n redirectObjects[marker] = value as Buffer | Blob | Response | string;\n source += marker;\n } else {\n source += escapeForInterpolation(value);\n }\n source += strings[i + 1] ?? \"\";\n }\n\n return this.createPromise(source, { redirectObjects });\n }\n\n private isRedirectTarget(precedingString: string, value: unknown): boolean {\n // Check if value is a redirect object type AND appears after redirect operator\n if (!isRedirectObject(value) || isRawValue(value)) {\n return false;\n }\n // Check if preceding string ends with redirect operator\n const trimmed = precedingString.trimEnd();\n const afterRedirectOp = /(<|>|>>|2>|2>>|&>|&>>)\\s*$/.test(trimmed);\n\n if (!afterRedirectOp) {\n return false;\n }\n\n // Buffer, Blob, Response are always treated as redirect objects\n if (Buffer.isBuffer(value) || value instanceof Blob || value instanceof Response) {\n return true;\n }\n\n // For strings after input redirect (<), treat as content per spec\n // For strings after output redirect (>), they must be Buffers\n if (typeof value === \"string\") {\n // Only input redirection supports string content\n return /<\\s*$/.test(trimmed);\n }\n\n return false;\n }\n\n private createPromise(source: string, options?: { cwd?: string; env?: Record<string, string>; shouldThrow?: boolean; redirectObjects?: RedirectObjectMap }): ShellPromise {\n const shell = this;\n\n return new ShellPromise({\n execute: async (overrides) => {\n const cwd = overrides?.cwd ?? options?.cwd ?? shell.currentCwd;\n const env = { ...shell.currentEnv, ...options?.env, ...overrides?.env };\n\n const interpreter = new Interpreter({\n fs: shell.fs,\n cwd,\n env,\n commands: shell.commands,\n redirectObjects: options?.redirectObjects,\n });\n\n const tokens = shell.lex(source);\n const ast = shell.parse(tokens);\n return interpreter.execute(ast);\n },\n cwdOverride: options?.cwd,\n envOverride: options?.env,\n shouldThrow: options?.shouldThrow ?? this.shouldThrow,\n });\n }\n\n // Global defaults\n cwd(path: string): void {\n this.currentCwd = path;\n }\n\n env(vars: Record<string, string>): void {\n Object.assign(this.currentEnv, vars);\n }\n\n throws(enable: boolean): void {\n this.shouldThrow = enable;\n }\n\n resetCwd(): void {\n this.currentCwd = this.initialCwd;\n }\n\n resetEnv(): void {\n this.currentEnv = { ...this.initialEnv };\n }\n\n // Utility\n escape(str: string): string {\n return escape(str);\n }\n\n // Low-level API\n lex(source: string): Token[] {\n return new Lexer(source).tokenize();\n }\n\n parse(tokens: Token[]): ASTNode {\n return new Parser(tokens).parse();\n }\n\n compile(ast: ASTNode): Program {\n // For now, the \"program\" is just the AST with source reconstruction\n return {\n ast,\n source: \"\", // Could reconstruct source from AST if needed\n };\n }\n\n async run(program: Program): Promise<ExecResult> {\n const interpreter = new Interpreter({\n fs: this.fs,\n cwd: this.currentCwd,\n env: this.currentEnv,\n commands: this.commands,\n });\n\n return interpreter.execute(program.ast);\n }\n}\n\n// Factory function that returns a callable template tag\nexport function createShellDSL(config: ShellConfig): ShellDSL & ((strings: TemplateStringsArray, ...values: unknown[]) => ShellPromise) {\n const shell = new ShellDSL(config);\n\n // Create a function that acts as both tag and shell instance\n const tag = (strings: TemplateStringsArray, ...values: unknown[]) => {\n return shell.tag(strings, ...values);\n };\n\n // Copy all properties and methods from shell to tag function\n Object.setPrototypeOf(tag, ShellDSL.prototype);\n Object.assign(tag, {\n fs: (shell as any).fs,\n initialCwd: (shell as any).initialCwd,\n initialEnv: (shell as any).initialEnv,\n currentCwd: (shell as any).currentCwd,\n currentEnv: (shell as any).currentEnv,\n commands: (shell as any).commands,\n shouldThrow: (shell as any).shouldThrow,\n });\n\n // Bind methods\n (tag as any).cwd = shell.cwd.bind(shell);\n (tag as any).env = shell.env.bind(shell);\n (tag as any).throws = shell.throws.bind(shell);\n (tag as any).resetCwd = shell.resetCwd.bind(shell);\n (tag as any).resetEnv = shell.resetEnv.bind(shell);\n (tag as any).escape = shell.escape.bind(shell);\n (tag as any).lex = shell.lex.bind(shell);\n (tag as any).parse = shell.parse.bind(shell);\n (tag as any).compile = shell.compile.bind(shell);\n (tag as any).run = shell.run.bind(shell);\n (tag as any).tag = shell.tag.bind(shell);\n\n return tag as ShellDSL & ((strings: TemplateStringsArray, ...values: unknown[]) => ShellPromise);\n}\n"
|
|
6
6
|
],
|
|
7
|
-
"mappings": ";AACA;AAGA;AACA;AACA;AACA;AACA;AAAA;AAOO,MAAM,SAAS;AAAA,EACZ;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,cAAuB;AAAA,EAE/B,WAAW,CAAC,QAAqB;AAAA,IAC/B,KAAK,KAAK,OAAO;AAAA,IACjB,KAAK,aAAa,OAAO;AAAA,IACzB,KAAK,aAAa,KAAK,OAAO,IAAI;AAAA,IAClC,KAAK,aAAa,OAAO;AAAA,IACzB,KAAK,aAAa,KAAK,OAAO,IAAI;AAAA,IAClC,KAAK,WAAW,OAAO;AAAA;AAAA,EAIzB,GAAG,CAAC,YAAkC,QAAiC;AAAA,IAErE,IAAI,SAAS,QAAQ,MAAM;AAAA,IAC3B,SAAS,IAAI,EAAG,IAAI,OAAO,QAAQ,KAAK;AAAA,MACtC,MAAM,QAAQ,OAAO;AAAA,MACrB,IAAI,WAAW,KAAK,GAAG;AAAA,QACrB,UAAU,MAAM;AAAA,MAClB,EAAO;AAAA,QACL,UAAU,uBAAuB,KAAK;AAAA;AAAA,MAExC,UAAU,QAAQ,IAAI,MAAM;AAAA,IAC9B;AAAA,IAEA,OAAO,KAAK,cAAc,MAAM;AAAA;AAAA,
|
|
8
|
-
"debugId": "
|
|
7
|
+
"mappings": "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",
|
|
8
|
+
"debugId": "8D1028F11D1C104A64756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
package/dist/mjs/src/types.mjs
CHANGED
|
@@ -2,8 +2,12 @@
|
|
|
2
2
|
function isRawValue(value) {
|
|
3
3
|
return typeof value === "object" && value !== null && "raw" in value && typeof value.raw === "string";
|
|
4
4
|
}
|
|
5
|
+
function isRedirectObject(value) {
|
|
6
|
+
return Buffer.isBuffer(value) || value instanceof Blob || value instanceof Response || typeof value === "string";
|
|
7
|
+
}
|
|
5
8
|
export {
|
|
9
|
+
isRedirectObject,
|
|
6
10
|
isRawValue
|
|
7
11
|
};
|
|
8
12
|
|
|
9
|
-
//# debugId=
|
|
13
|
+
//# debugId=9C8E04A91CC7802764756E2164756E21
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/types.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"// Virtual Filesystem Interface\nexport interface VirtualFS {\n readFile(path: string): Promise<Buffer>;\n readdir(path: string): Promise<string[]>;\n stat(path: string): Promise<FileStat>;\n exists(path: string): Promise<boolean>;\n\n writeFile(path: string, data: Buffer | string): Promise<void>;\n appendFile(path: string, data: Buffer | string): Promise<void>;\n mkdir(path: string, opts?: { recursive?: boolean }): Promise<void>;\n\n rm(path: string, opts?: { recursive?: boolean; force?: boolean }): Promise<void>;\n\n resolve(...paths: string[]): string;\n dirname(path: string): string;\n basename(path: string): string;\n glob(pattern: string, opts?: { cwd?: string }): Promise<string[]>;\n}\n\nexport interface FileStat {\n isFile(): boolean;\n isDirectory(): boolean;\n size: number;\n mtime: Date;\n}\n\n// Command Interfaces\nexport type Command = (ctx: CommandContext) => Promise<number>;\n\nexport interface CommandContext {\n args: string[];\n stdin: Stdin;\n stdout: Stdout;\n stderr: Stderr;\n fs: VirtualFS;\n cwd: string;\n env: Record<string, string>;\n}\n\nexport interface Stdin {\n stream(): AsyncIterable<Uint8Array>;\n buffer(): Promise<Buffer>;\n text(): Promise<string>;\n lines(): AsyncIterable<string>;\n}\n\nexport interface Stdout {\n write(chunk: Uint8Array): Promise<void>;\n writeText(str: string): Promise<void>;\n}\n\nexport interface Stderr {\n write(chunk: Uint8Array): Promise<void>;\n writeText(str: string): Promise<void>;\n}\n\nexport interface OutputCollector extends Stdout {\n close(): void;\n collect(): Promise<Buffer>;\n getReadableStream(): AsyncIterable<Uint8Array>;\n}\n\n// Execution Result\nexport interface ExecResult {\n stdout: Buffer;\n stderr: Buffer;\n exitCode: number;\n}\n\n// Shell Configuration\nexport interface ShellConfig {\n fs: VirtualFS;\n cwd: string;\n env: Record<string, string>;\n commands: Record<string, Command>;\n}\n\n// Raw escape hatch type\nexport interface RawValue {\n raw: string;\n}\n\nexport function isRawValue(value: unknown): value is RawValue {\n return (\n typeof value === \"object\" &&\n value !== null &&\n \"raw\" in value &&\n typeof (value as RawValue).raw === \"string\"\n );\n}\n"
|
|
5
|
+
"// Virtual Filesystem Interface\nexport interface VirtualFS {\n readFile(path: string): Promise<Buffer>;\n readdir(path: string): Promise<string[]>;\n stat(path: string): Promise<FileStat>;\n exists(path: string): Promise<boolean>;\n\n writeFile(path: string, data: Buffer | string): Promise<void>;\n appendFile(path: string, data: Buffer | string): Promise<void>;\n mkdir(path: string, opts?: { recursive?: boolean }): Promise<void>;\n\n rm(path: string, opts?: { recursive?: boolean; force?: boolean }): Promise<void>;\n\n resolve(...paths: string[]): string;\n dirname(path: string): string;\n basename(path: string): string;\n glob(pattern: string, opts?: { cwd?: string }): Promise<string[]>;\n}\n\nexport interface FileStat {\n isFile(): boolean;\n isDirectory(): boolean;\n size: number;\n mtime: Date;\n}\n\n// Command Interfaces\nexport type Command = (ctx: CommandContext) => Promise<number>;\n\nexport interface CommandContext {\n args: string[];\n stdin: Stdin;\n stdout: Stdout;\n stderr: Stderr;\n fs: VirtualFS;\n cwd: string;\n env: Record<string, string>;\n}\n\nexport interface Stdin {\n stream(): AsyncIterable<Uint8Array>;\n buffer(): Promise<Buffer>;\n text(): Promise<string>;\n lines(): AsyncIterable<string>;\n}\n\nexport interface Stdout {\n write(chunk: Uint8Array): Promise<void>;\n writeText(str: string): Promise<void>;\n}\n\nexport interface Stderr {\n write(chunk: Uint8Array): Promise<void>;\n writeText(str: string): Promise<void>;\n}\n\nexport interface OutputCollector extends Stdout {\n close(): void;\n collect(): Promise<Buffer>;\n getReadableStream(): AsyncIterable<Uint8Array>;\n}\n\n// Execution Result\nexport interface ExecResult {\n stdout: Buffer;\n stderr: Buffer;\n exitCode: number;\n}\n\n// Shell Configuration\nexport interface ShellConfig {\n fs: VirtualFS;\n cwd: string;\n env: Record<string, string>;\n commands: Record<string, Command>;\n}\n\n// Raw escape hatch type\nexport interface RawValue {\n raw: string;\n}\n\nexport function isRawValue(value: unknown): value is RawValue {\n return (\n typeof value === \"object\" &&\n value !== null &&\n \"raw\" in value &&\n typeof (value as RawValue).raw === \"string\"\n );\n}\n\n// JS Object Redirection types\nexport type RedirectObject = Buffer | Blob | Response | string;\n\nexport interface RedirectObjectMap {\n [marker: string]: RedirectObject;\n}\n\nexport function isRedirectObject(value: unknown): value is RedirectObject {\n return (\n Buffer.isBuffer(value) ||\n value instanceof Blob ||\n value instanceof Response ||\n typeof value === \"string\"\n );\n}\n"
|
|
6
6
|
],
|
|
7
|
-
"mappings": ";AAkFO,SAAS,UAAU,CAAC,OAAmC;AAAA,EAC5D,OACE,OAAO,UAAU,YACjB,UAAU,QACV,SAAS,SACT,OAAQ,MAAmB,QAAQ;AAAA;",
|
|
8
|
-
"debugId": "
|
|
7
|
+
"mappings": ";AAkFO,SAAS,UAAU,CAAC,OAAmC;AAAA,EAC5D,OACE,OAAO,UAAU,YACjB,UAAU,QACV,SAAS,SACT,OAAQ,MAAmB,QAAQ;AAAA;AAWhC,SAAS,gBAAgB,CAAC,OAAyC;AAAA,EACxE,OACE,OAAO,SAAS,KAAK,KACrB,iBAAiB,QACjB,iBAAiB,YACjB,OAAO,UAAU;AAAA;",
|
|
8
|
+
"debugId": "9C8E04A91CC7802764756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
|
@@ -13,4 +13,8 @@ export { mkdir } from "./mkdir.ts";
|
|
|
13
13
|
export { rm } from "./rm.ts";
|
|
14
14
|
export { test, bracket } from "./test.ts";
|
|
15
15
|
export { trueCmd, falseCmd } from "./true-false.ts";
|
|
16
|
+
export { touch } from "./touch.ts";
|
|
17
|
+
export { cp } from "./cp.ts";
|
|
18
|
+
export { mv } from "./mv.ts";
|
|
19
|
+
export { tee } from "./tee.ts";
|
|
16
20
|
export declare const builtinCommands: Record<string, Command>;
|
|
@@ -1,21 +1,25 @@
|
|
|
1
1
|
import type { ASTNode } from "../parser/ast.ts";
|
|
2
|
-
import type { Command, VirtualFS, ExecResult } from "../types.ts";
|
|
2
|
+
import type { Command, VirtualFS, ExecResult, RedirectObjectMap } from "../types.ts";
|
|
3
3
|
export interface InterpreterOptions {
|
|
4
4
|
fs: VirtualFS;
|
|
5
5
|
cwd: string;
|
|
6
6
|
env: Record<string, string>;
|
|
7
7
|
commands: Record<string, Command>;
|
|
8
|
+
redirectObjects?: RedirectObjectMap;
|
|
8
9
|
}
|
|
9
10
|
export declare class Interpreter {
|
|
10
11
|
private fs;
|
|
11
12
|
private cwd;
|
|
12
13
|
private env;
|
|
13
14
|
private commands;
|
|
15
|
+
private redirectObjects;
|
|
14
16
|
constructor(options: InterpreterOptions);
|
|
15
17
|
execute(ast: ASTNode): Promise<ExecResult>;
|
|
16
18
|
private executeNode;
|
|
17
19
|
private executeCommand;
|
|
18
20
|
private handleRedirect;
|
|
21
|
+
private handleObjectRedirect;
|
|
22
|
+
private readFromObject;
|
|
19
23
|
private executePipeline;
|
|
20
24
|
private executeSequence;
|
|
21
25
|
private executeAnd;
|
|
@@ -25,3 +25,16 @@ export declare class PipeBuffer implements OutputCollector, Stdout {
|
|
|
25
25
|
export declare function createStdout(): OutputCollector;
|
|
26
26
|
export declare function createStderr(): OutputCollector;
|
|
27
27
|
export declare function createPipe(): PipeBuffer;
|
|
28
|
+
export declare class BufferTargetCollector implements OutputCollector {
|
|
29
|
+
private target;
|
|
30
|
+
private offset;
|
|
31
|
+
private closed;
|
|
32
|
+
private closeResolvers;
|
|
33
|
+
constructor(target: Buffer);
|
|
34
|
+
write(chunk: Uint8Array): Promise<void>;
|
|
35
|
+
writeText(str: string): Promise<void>;
|
|
36
|
+
close(): void;
|
|
37
|
+
collect(): Promise<Buffer>;
|
|
38
|
+
getReadableStream(): AsyncIterable<Uint8Array>;
|
|
39
|
+
}
|
|
40
|
+
export declare function createBufferTargetCollector(target: Buffer): OutputCollector;
|
|
@@ -16,6 +16,7 @@ export declare class ShellDSL {
|
|
|
16
16
|
private shouldThrow;
|
|
17
17
|
constructor(config: ShellConfig);
|
|
18
18
|
tag(strings: TemplateStringsArray, ...values: unknown[]): ShellPromise;
|
|
19
|
+
private isRedirectTarget;
|
|
19
20
|
private createPromise;
|
|
20
21
|
cwd(path: string): void;
|
|
21
22
|
env(vars: Record<string, string>): void;
|
|
@@ -69,3 +69,8 @@ export interface RawValue {
|
|
|
69
69
|
raw: string;
|
|
70
70
|
}
|
|
71
71
|
export declare function isRawValue(value: unknown): value is RawValue;
|
|
72
|
+
export type RedirectObject = Buffer | Blob | Response | string;
|
|
73
|
+
export interface RedirectObjectMap {
|
|
74
|
+
[marker: string]: RedirectObject;
|
|
75
|
+
}
|
|
76
|
+
export declare function isRedirectObject(value: unknown): value is RedirectObject;
|
package/package.json
CHANGED