BlueLibs Runner
Enterprise Application Runtime for TypeScript
Build apps from tasks and resources with explicit dependencies, predictable lifecycle, and first-class testing
Runner is a TypeScript-first toolkit for building an app out of small, typed building blocks. You can find more details and a visual overview at runner.bluelibs.com.
- Tasks: async business actions with explicit
dependencies, middleware, and input/output validation
- Resources: singletons with a four-phase lifecycle:
init, ready, cooldown, dispose
- Reliability Middleware: built-in
retry, timeout, circuitBreaker, cache, and rateLimit
- Remote Lanes: cross-process execution (the "Distributed Monolith") with zero call-site changes
- Durable Workflows: persistent, crash-recoverable async logic for Node.js
- Events & hooks: typed signals and subscribers for decoupling
- Runtime control: run, observe, test, pause, recover, and dispose your
app predictably
The goal is simple: keep dependencies explicit, keep lifecycle predictable, and make your runtime easy to control in production and in tests.
Versioning & Support
Runner follows a simple support policy so teams can plan upgrades without guesswork.
6.x: actively maintained. New features, improvements, bug fixes, and documentation updates land here first.
5.x: LTS through December 31, 2026. Critical fixes and important maintenance can continue there, but new development is focused on 6.x.
If you are starting a new project, use 6.x.
If you are on 5.x, you have a stable upgrade window through the end of 2026.
See Support & Release Policy for the full versioning and support policy.
import { r, run } from "@bluelibs/runner";
const userCreated = r
.event<{ id: string; email: string }>("userCreated")
.build();
const userStore = r
.resource("userStore")
.init(async () => new Map<string, { id: string; email: string }>())
.build();
const createUser = r
.task<{ email: string }>("createUser")
.dependencies({ userStore, userCreated })
.run(async (input, { userStore, userCreated }) => {
const user = { id: "user-1", email: input.email };
userStore.set(user.id, user);
await userCreated(user);
return user;
})
.build();
const sendWelcomeEmail = r
.hook("sendWelcomeEmail")
.on(userCreated)
.run(async (event) => {
console.log(`Welcome ${event.data.email}`);
})
.build();
const app = r
.resource("app")
.register([userStore, createUser, sendWelcomeEmail])
.build();
const runtime = await run(app);
await runtime.runTask(createUser, { email: "ada@example.com" });
await runtime.dispose();
This example is intentionally runnable with only @bluelibs/runner, typescript, and tsx.
Note: User-defined ids are local ids. Prefer createUser, userStore, and sendWelcomeEmail. Runner composes canonical ids such as app.tasks.createUser at runtime.
Choose Your Path
Platform Support (Quick Summary)
| Core runtime (tasks/resources/middleware/events/hooks) | Full | Full | Full | Platform adapters hide runtime differences |
Async Context (r.asyncContext) | Full | None | None | Requires AsyncLocalStorage; Bun/Deno may support it via the universal build when available |
Durable workflows (@bluelibs/runner/node) | Full | None | None | Node-only module |
Remote Lanes client (createHttpClient) | Full | Full | Full | Explicit universal client for fetch runtimes |
Remote Lanes server (@bluelibs/runner/node) | Full | None | None | Exposes tasks/events over HTTP |
Prerequisites
Use these minimums before starting:
| Node.js | 22.x+ | Enforced by package.json#engines.node |
| TypeScript | 5.6+ (recommended) | Required for typed DX and examples in this repository |
| Package manager | npm / pnpm / yarn / bun | Examples use npm, but any modern package manager works |
fetch runtime | Built-in or polyfilled | Required for explicit remote lane clients (createHttpClient) |
If you use the Node-only package (@bluelibs/runner/node) for durable workflows or exposure, stay on a supported Node LTS line.
Your First 5 Minutes
This page is the shortest path from "what is Runner?" to "I ran it once and I trust the shape of it."
New to Runner? Here's the absolute minimum you need to know:
- Tasks are your business logic functions with dependencies, middleware, and validation.
- Resources are shared services with a four-phase lifecycle:
init, ready, cooldown, dispose.
- You compose everything under an
app resource with .register([...]).
- You run it with
run(app) which gives you runTask() and dispose() first, then more runtime helpers as you grow.
Quick Start
This is the fastest way to run the TypeScript example at the top of this README.
- Confirm prerequisites from Prerequisites (Node
22+, TypeScript 5.6+ recommended).
- Install dependencies:
npm i @bluelibs/runner
npm i -D typescript tsx
- Copy the example above into
index.ts.
- Run it:
npx tsx index.ts
What you now have: a working runtime, explicit dependency wiring, and the smallest useful Runner execution path.
Tip: User-defined ids are local ids. Use createUser or userStore, not dotted ids like app.tasks.createUser.
Platform Note: Advanced features such as Durable Workflows and server-side Remote Lanes are Node-only.
Local Ids vs Canonical Runtime Ids
You write local ids in definitions:
task("createUser")
resource("userStore")
event("userCreated")
Runner composes canonical runtime ids from ownership:
app.tasks.createUser
app.userStore
app.events.userCreated
Prefer references such as runTask(createUser, input) over string ids whenever you can.
Runner Dev Tools Quick Start
@bluelibs/runner-dev gives you CLI scaffolding and runtime introspection.
- Install (or run without install):
npm install -g @bluelibs/runner-dev
npx @bluelibs/runner-dev --help
runner-dev new my-app --install
runner-dev query 'query { tasks { id } }' --entry-file ./src/main.ts
ENDPOINT=http://localhost:1337/graphql runner-dev overview --details 10
For full CLI and Dev UI docs, see Runner Dev Tools.
Real-World Examples
Where To Go Next
License
This project is licensed under the MIT License. See LICENSE.md.