A small experimental runtime inspired by ZIO 2, implemented in vanilla TypeScript and intentionally built without using Promise / async/await as the primary semantic primitive.
brass-runtime is the foundation: it provides an effect system, fibers, scheduler, scopes, and streams.
Higher-level modules (HTTP, streaming utilities, integrations) are built on top of the runtime, not baked into it.
You can still interop with the outside world (timers, fetch, Node APIs) via explicit, cancellable bridges such as
fromPromiseAbortable.
- Effects are values โ lazy, composable, referentially transparent
- Async is explicit โ no hidden Promise semantics
- Concurrency is structured โ fibers, scopes, finalizers
- Side effects are interpreted โ not executed eagerly
- Higher-level APIs are libraries, not magic
If you like ZIOโs separation between zio-core, zio-streams, and zio-http, this project follows the same spirit.
- Sync core effect:
Effect<R, E, A>andExit<E, A> - Algebraic async representation:
Async<R, E, A> - Cooperative
Scheduler(observable / testable) - Lightweight
Fibers with interruption & finalizers - Structured
Scopes for resource safety - ZStream-style streams with backpressure
npm i brass-runtimeimport { succeed } from "brass-runtime";
import { Runtime, toPromise } from "brass-runtime/runtime";
const runtime = new Runtime({ env: {} });
const value = await toPromise(succeed(123), runtime.env);
console.log(value); // 123import { withScope } from "brass-runtime/scope";
import { Runtime } from "brass-runtime/runtime";
const runtime = new Runtime({ env: {} });
withScope(runtime, (scope) => {
const f = scope.fork(/* Async effect */);
// later...
scope.close(); // interrupts child fibers + runs finalizers
});
toPromiseis just a convenience bridge for examples/DX. The runtime semantics remain explicit.
These are optional layers, implemented using the runtime primitives.
A ZIO-style HTTP client built on top of fibers and Async.
- Lazy & cancelable HTTP requests
- Explicit wire/content separation
- Middleware-friendly (logging, retry, timeout, etc.)
- Integrated with fiber interruption via
AbortController
๐ Docs: HTTP module
Example:
import { httpClientStream } from "brass-runtime/http";
import { toPromise, Runtime } from "brass-runtime/runtime";
type Post = { id: number; title: string; body: string };
const runtime = new Runtime({ env: {} });
const client = httpClientStream({ baseUrl: "https://jsonplaceholder.typicode.com" });
const res = await toPromise(client.getJson<Post>("/posts/1"), runtime.env);
console.log(res.status, res.value.title);Pull-based, resource-aware streams with backpressure.
ZStream<R, E, A>Pullsemantics- Bounded buffers
- Deterministic resource cleanup
Examples:
src/examples/fromPromise.tssrc/examples/mergeStreamSync.ts
- Getting Started
- Architecture
- Cancellation & Interruption
- Observability: Hooks & Tracing
- HTTP module
- Modules overview
- Stream buffering with backpressure (
buffer) - Abortable async integration (
fromPromiseAbortable) - Fiber-safe
toPromisefor examples & DX - HTTP client module built on top of the runtime
- Sync core:
Effect - Async algebra:
Async - Cooperative
Scheduler - Fibers with interruption & finalizers
- Structured
Scope - Resource safety (
acquireRelease)
-
race,zipPar,collectAllPar - ZStream-like core
- Bounded buffers & backpressure
- Stream merge / zip
- Hubs / Broadcast
- Pipelines (
ZPipeline-style)
- HTTP client
- Retry / timeout middleware
- Logging / metrics layers
- No hidden Promises: async is always modeled explicitly
- Deterministic execution: scheduler is observable & testable
- Resource safety is structural: scopes guarantee cleanup
- Libraries compose via functions: middleware, not inheritance
- Runtime invariants matter โ avoid sneaking Promises into semantics
- Prefer libraries on top of the runtime over changes in the core
- Small, focused PRs are welcome (your repo may enforce PR-only changes)
MIT License ยฉ 2025