Bun
JavaScript runtime, bundler, test runner, and package manager.
Goals
Replace the Node/tsc/jest/npm stack with a single tool that runs TypeScript directly, runs tests, and compiles a self-contained binary for deployment as a scheduled task — without a build pipeline.
Effectiveness
Excellent. Runs TypeScript without configuration, compiles to a standalone binary that requires no runtime at the target machine, and provides a Jest-compatible test runner. Three tools replaced by one, with no compromises on the features we actually use.
What made it effective
bun build --compile --outfile dist/inertia-millproduces a single self-contained binary.ensure-deps.shrebuilds it automatically when the source is newer. Scheduled tasks run the binary — no Bun, Node, or npm required at runtime.- Native TypeScript support with
.tsextension imports means zero tsc configuration. Files just work. bun:testis Jest-compatible enough that all existing test patterns transferred unchanged.
Bonus utility
bunx runs CLI tools from npm without installation — useful for one-off wrangler invocations during deployment without adding it as a dependency.
Friction / pain points / surprises
spawnSync comes from Node's child_process, not Bun's native API. Bun exposes Node compatibility APIs — import { spawnSync } from "child_process" works — but it's not obvious whether you should prefer Bun's Bun.spawnSync or Node's. We use the Node import; it works fine but the duality is a minor cognitive overhead.
4MB maxBuffer limit on spawnSync. Large axe outputs could approach this. Hasn't been hit, but it's an invisible ceiling that will produce a cryptic error when it's hit.
Default fetch socket timeout (~30s) aborts long Ollama calls. Bun's fetch has an undocumented default socket timeout of roughly 30 seconds. The qwen3.5:27b model cold-start takes ~55 seconds to respond even to "hi". The result is an AbortError that looks like a network failure, not a timeout. Fix: pass signal: AbortSignal.timeout(300_000) explicitly on any long-running inference call. This was responsible for the podcast warmup failing on every cold-start nightly cron run.
bunx is the container-safe replacement for npx. In a Docker container built without npm, npx is not available. bunx is the drop-in replacement and ships with Bun. Any deployment or tooling script that uses npx wrangler, npx marss, etc. must use bunx instead. This caused three separate pipeline launch failures — the first was caught in deploy.ts, the second in feed.ts, and there were no more after a global grep. The lesson: on first occurrence of an environment-specific failure, grep for every instance of the pattern before closing the loop.