$_ bashkit

TypeScript in Bashkit

Bashkit includes an embedded TypeScript interpreter powered by ZapCode, a pure-Rust TypeScript runtime with ~2µs cold start and zero V8 dependency. TypeScript runs entirely in-memory alongside bash — files written by bash are readable from TypeScript and vice versa.

Getting started

Add the typescript feature to your Cargo.toml:

[dependencies]
bashkit = { version = "0.1", features = ["typescript"] }

Enable TypeScript in the builder:

use bashkit::Bash;

let mut bash = Bash::builder().typescript().build();

// Run TypeScript inline
let r = bash.exec("ts -c \"console.log('hello')\"").await?;
assert_eq!(r.stdout, "hello\n");

Command aliases

Five commands are registered, all running the same ZapCode interpreter:

CommandInline flagExample
ts-cts -c "console.log('hi')"
typescript-ctypescript -c "1 + 2"
node-enode -e "console.log('hi')"
deno-edeno -e "console.log('hi')"
bun-ebun -e "console.log('hi')"

Both -c and -e are accepted by all aliases.

Running TypeScript files

Write a .ts file to the VFS and execute it:

cat > /tmp/hello.ts << 'EOF'
const greet = (name: string): string => `Hello, ${name}!`;
console.log(greet("world"));
EOF
ts /tmp/hello.ts

Output: Hello, world!

Scripts can also be piped via stdin:

echo "console.log(2 ** 10)" | ts

Working with the virtual filesystem

TypeScript code can read and write files through async VFS functions that are automatically available as globals:

# Write from bash
echo "important data" > /tmp/config.txt

# Read from TypeScript
ts -c "await readFile('/tmp/config.txt')"
# Output: important data

# Write from TypeScript
ts -c "await writeFile('/tmp/output.txt', 'computed result\n')"

# Read from bash
cat /tmp/output.txt
# Output: computed result

Available VFS functions

FunctionDescription
readFile(path)Read file contents as string
writeFile(path, content)Write string to file
exists(path)Check if path exists (returns boolean)
readDir(path)List directory entries (returns string[])
mkdir(path)Create directory (recursive)
remove(path)Delete file or directory
stat(path)Get file metadata (returns JSON string)

Example: data processing pipeline

Bash and TypeScript can work together in a single session, each using their strengths:

# Step 1: Bash generates raw data
for i in $(seq 1 5); do
    echo "$i,$((i * 10)),$((RANDOM % 100))" >> /tmp/data.csv
done

# Step 2: TypeScript processes it
ts -c "
const csv = await readFile('/tmp/data.csv');
const rows = csv.trim().split('\n').map(r => r.split(',').map(Number));
const total = rows.reduce((sum, [_id, value, _score]) => sum + value, 0);
await writeFile('/tmp/summary.txt', 'Total: ' + total + '\n');
"

# Step 3: Bash uses the result
cat /tmp/summary.txt
# Output: Total: 150

Example: JSON transformation

# Create JSON with bash
cat > /tmp/users.json << 'EOF'
[
  {"name": "Alice", "age": 30},
  {"name": "Bob", "age": 25},
  {"name": "Charlie", "age": 35}
]
EOF

# Transform with TypeScript
ts -c "
const data = JSON.parse(await readFile('/tmp/users.json'));
const names = data.map(u => u.name).join(', ');
console.log('Users: ' + names);
console.log('Average age: ' + (data.reduce((s, u) => s + u.age, 0) / data.length));
"

Example: script file with VFS

# Write a TypeScript utility script
cat > /tmp/analyze.ts << 'TSEOF'
const content = await readFile('/tmp/numbers.txt');
const nums = content.trim().split('\n').map(Number);
const sum = nums.reduce((a, b) => a + b, 0);
const avg = sum / nums.length;
console.log('Count: ' + nums.length);
console.log('Sum:   ' + sum);
console.log('Avg:   ' + avg.toFixed(2));
console.log('Min:   ' + Math.min(...nums));
console.log('Max:   ' + Math.max(...nums));
TSEOF

# Generate data and run the script
seq 1 10 > /tmp/numbers.txt
ts /tmp/analyze.ts

Configuration

Resource limits

use bashkit::{Bash, TypeScriptLimits};
use std::time::Duration;

let bash = Bash::builder()
    .typescript_with_limits(
        TypeScriptLimits::default()
            .max_duration(Duration::from_secs(5))
            .max_memory(16 * 1024 * 1024)  // 16 MB
            .max_stack_depth(100)
            .max_allocations(100_000)
    )
    .build();

Disabling compat aliases

If you only want ts/typescript and not node/deno/bun:

use bashkit::{Bash, TypeScriptConfig};

let bash = Bash::builder()
    .typescript_with_config(TypeScriptConfig::default().compat_aliases(false))
    .build();

// ts works:
bash.exec("ts -c \"console.log('ok')\"").await?;

// node does NOT work:
let r = bash.exec("node -e \"console.log('ok')\"").await?;
assert_ne!(r.exit_code, 0);

Disabling unsupported-mode hints

By default, using Node/Deno/Bun-specific flags shows helpful guidance. Disable this for cleaner error output:

use bashkit::{Bash, TypeScriptConfig};

let bash = Bash::builder()
    .typescript_with_config(TypeScriptConfig::default().unsupported_mode_hint(false))
    .build();

Supported TypeScript features

FeatureExample
Variableslet x = 10; const y = 20;
Arrow functionsconst add = (a: number, b: number) => a + b;
Template literals`hello ${name}`
Destructuringconst { x, y } = obj; const [a, ...rest] = arr;
Async/awaitconst data = await readFile('/tmp/f.txt');
Array methods.map(), .filter(), .reduce(), .forEach(), .find()
For loopsfor, for...of, for...in, while, do...while
Conditionalsif/else, ternary, switch/case
Type annotationsParsed and accepted but not enforced at runtime
MathMath.floor(), Math.min(), Math.max(), Math.round() etc.
JSONJSON.parse(), JSON.stringify()
ClosuresFull lexical scoping with closure capture
Generatorsfunction* with yield

What’s NOT supported

FeatureReason
import/requireNo module system
eval()/Function()Blocked for security
fetch/XMLHttpRequestNo network access
process/Deno/Bun globalsNo runtime APIs
npm packagesNo package manager
DOM APIsNo browser environment

Security

TypeScript execution is fully sandboxed:

  • All file I/O goes through the virtual filesystem (no host access)
  • No network, no process spawning, no dynamic code evaluation
  • Independent resource limits (time, memory, stack, allocations)
  • Opt-in only: requires both typescript Cargo feature AND .typescript() builder call
  • Path traversal (../../../etc/passwd) is blocked by VFS normalization

See TM-TS threat entries for the full security analysis.