Agnostic JavaScript & TypeScript runtime to swap engines — QuickJS (default), JavaScriptCore, V8
2.0K
Pick your JS engine. Ship secure code. Zero compromises.
v1.x · Written in D · QuickJS · JavaScriptCore · V8
One image. Three battle-tested JS engines. A permission model that says no by default. Run JavaScript and TypeScript the same way you'd run any trusted CLI tool — explicit, auditable, fast.
# JavaScript
docker run --rm -v "$PWD":/app -w /app kassany/dunjs run server.js
# TypeScript
docker run --rm -v "$PWD":/app -w /app kassany/dunjs run app.ts
Need more power? Swap the engine with one env var:
-e DUN_BACKEND=jsc # JavaScriptCore — 0.99× Bun throughput
-e DUN_BACKEND=v8 # V8 — matches Node.js startup
# default: QuickJS — 9.8 MB binary, interpreter-fast
Every I/O operation is blocked until you say otherwise. No silent network calls. No surprise file reads. Just what you permit, nothing more.
# Read-only access to one directory
docker run --rm -v "$PWD":/app -w /app kassany/dunjs run --allow-read=./data app.js
# Network + read for a real server
docker run --rm -v "$PWD":/app -w /app kassany/dunjs run --allow-net --allow-read server.js
# Full trust — your call
docker run --rm -v "$PWD":/app -w /app kassany/dunjs run --allow-all app.js
Scoped to paths and hosts: --allow-read=./data, --allow-net=api.example.com
Runtimes you already know, APIs you already use:
// Node.js style
const fs = require('fs')
const http = require('http')
const { Buffer } = require('buffer')
// Deno style
await Deno.readTextFile('./config.json')
await Deno.serve({ port: 3000 }, handler)
// Web platform
const res = await fetch('https://api.example.com/data')
const hash = await crypto.subtle.digest('SHA-256', data)
Built-in batteries:
| What | How |
|---|---|
| TypeScript | Strip-transpiled on the fly — .ts, .tsx, no install |
| npm packages | dun install lodash + CVE audit via OSV.dev |
| HTTP/2 | HPACK, ALPN/TLS, stream multiplexing |
| HTTP/3 | QUIC via lsquic — dun:http3 |
| SQLite | Vendored, zero external deps — dun:sqlite |
| Workers | SharedArrayBuffer + Atomics on all 3 engines |
| 30+ Node modules | fs, path, crypto, events, stream, zlib, dns, ws, child_process, … |
| 30+ Deno APIs | Deno.env, Deno.stat, Deno.serve, Deno.permissions, … |
run <file> Run a JavaScript/TypeScript file
test [files] Run tests (*.test.{js,ts}, *_test.{js,ts})
bench [files] Run benchmarks (*.bench.{js,ts})
repl Start interactive REPL
eval <code> Evaluate JavaScript code
install <pkg> Install npm package to node_modules/
audit Scan node_modules/ for known CVEs
init [name] Initialize a new project
fmt [files] Format JavaScript/TypeScript files
bundle <entry> Bundle JS/TS modules (--outdir DIR)
clean Remove cache and build artifacts
version Print version
help Show this help
Throughput — feature_bench (25 tests, release build):
| ops/sec | ||
|---|---|---|
| Bun 1.3 | 491 M | baseline |
| DunJS JSC | 488 M | 0.99× Bun |
| Node 22 | 172 M | 0.35× |
| DunJS V8 | 160 M | 0.33× |
| DunJS QuickJS | 22 M | interpreter — no JIT |
Cold start (process spawn, eval ';'):
| Bun | 11 ms |
| DunJS V8 | 22 ms |
| DunJS QJS / JSC | 27 ms |
| Node 22 | 27 ms |
| Deno 2.7 | 42 ms |
HTTP throughput (V8, /plaintext): 62,900 req/s — +41% over Node.js v22
Binary sizes (stripped, release):
| DunJS QuickJS | 9.8 MB |
| DunJS V8 | 56 MB |
| DunJS JSC | 60 MB |
| Bun | 89 MB |
| Node.js / Deno | 119 MB |
The Dockerfile pass CIS Docker Benchmark §4 out of the box:
debian:bookworm-slim@sha256:…, no floating tagstini as PID 1 — proper SIGCHLD, no zombie workersUSER 1001:1001 — Kubernetes runAsNonRoot compliantsha256sum -c at build timeHEALTHCHECK runs dun eval "1+1" on every startSTOPSIGNAL SIGTERM for graceful shutdownHarden further at runtime (CIS §5):
docker run --read-only --tmpfs /tmp \
--cap-drop=ALL --security-opt no-new-privileges:true \
--pids-limit 256 --memory=512m \
kassany/dunjs run script.js
Content type
Image
Digest
sha256:c8516c70d…
Size
134.6 MB
Last updated
about 2 months ago
docker pull kassany/dunjs