TypeScript + Bun
Category: Tech Stack · Areas: all
Description
Category
tech-stack
Areas
all
Components
- Language: TypeScript (strict mode)
- Runtime: Bun 1.x — NOT Node.js
- Package manager: Bun (
bun install,bun add) — NOT npm, NOT yarn, NOT pnpm - Linter + Formatter: Biome — NOT ESLint, NOT Prettier
- Test runner:
bun:test— NOT Vitest, NOT Jest - Workspace layout: Bun workspaces (
workspacesin rootpackage.json) - TypeScript config: strict,
noUncheckedIndexedAccess,exactOptionalPropertyTypes
Constraints
- All code must pass
tsc --noEmit(orbun run typecheck) with strict config - All code must pass Biome lint + format check (
bun run lint) - Scripts use
bun run, notnpm run - Use Bun-native APIs:
Bun.serve()for HTTP (not@hono/node-serveror similar Node adapters),Bun.file()for file I/O,Bun.spawn()for subprocesses - Do not use
tsx,ts-node, or other TypeScript transpilers — Bun runs.tsnatively - Do not add an
engines.nodefield — this project targets Bun, not Node.js - No
package-lock.jsonoryarn.lock— usebun.lock - No
node dist/index.jsstart commands — usebun src/index.ts - Biome config: indent style tabs, line width 100,
noUnusedImports: error
Drift Signals (anti-patterns to reject in review)
npm runin scripts → must bebun runprettieroreslintdependencies → replace with Biomevitestorjest→ replace withbun:testtsxorts-node→ remove; Bun executes TypeScript natively@hono/node-serveror any*-node-*HTTP adapter → useBun.serve()node dist/start command → usebun src/engines.nodeconstraint → remove
When to use
TypeScript projects using Bun as the runtime and package manager. Applies to monorepos and single-package projects alike. If a project historically drifted to Node.js tooling (npm, tsx, prettier, vitest), the concern documents the target state and the drift signals above identify what needs correction.
ADR References
Practices by activity
Agents working in any of these activities inherit the practices below via the bead’s context digest.
Requirements (Frame activity)
- All user stories involving TypeScript must assume Bun as runtime and package manager
- If a library dependency requires a Node.js adapter, flag it as a concern at framing — it may require a Bun-compatible alternative
Design
- Use Bun workspaces for monorepos:
"workspaces": ["packages/*"]in rootpackage.json - Separate packages by concern:
shared(types/schemas),server(API),web(frontend) - Use workspace references (
workspace:*) for cross-package dependencies - HTTP servers:
Bun.serve()— not Express, Fastify with Node adapter, or@hono/node-server - For Hono: use
honodirectly withBun.serve()export, not the node-server adapter
Implementation
- Run TypeScript directly:
bun src/index.ts— no build step required for server/CLI - Scripts in
package.jsonmust usebun run/bun test/bun add, notnpm run - Use Bun-native APIs:
- File I/O:
Bun.file(),Bun.write() - Subprocesses:
Bun.spawn(),Bun.spawnSync() - HTTP:
Bun.serve() - Environment:
Bun.env
- File I/O:
- TypeScript config:
strict,noUncheckedIndexedAccess,exactOptionalPropertyTypes,verbatimModuleSyntax - No
any— TypeScript strict mode is enforced - Formatting: Biome with tabs, line width 100
- Linting: Biome recommended rules +
noUnusedImports: error,noUnusedVariables: warn - Imports: use
typekeyword for type-only imports (import type { Foo })
Testing
- Framework:
bun:test(built-in) - Run:
bun test - Use
mock()frombun:testfor module mocking - Fake data:
@faker-js/fakeror equivalent — not static fixtures - Prefer stubs to mocks; verify behavior, not call sequences
- Integration tests can use real databases via
docker compose up -dor testcontainers
Quality Gates (pre-commit / CI)
bun test— all tests passbun run typecheck—tsc --noEmitpasses for all packagesbun run lint— Biome lint + format check passes- No
package-lock.jsoncommitted (indicates npm was used) bun.lockcommitted and up to date
Dependency Management
- Add:
bun add <pkg>(notnpm install) - Dev deps:
bun add -d <pkg> - Workspace deps: reference with
"workspace:*"in package.json - Lock file:
bun.lock(text format, committed) - Audit:
bun auditfor known vulnerabilities