Open source TypeScript toolkit
Type-safe errors, DI, and Result in plain async/await
The patterns you'd build from Effect and Rust — typed errors, dependency injection, Result types, retries, and more — in TypeScript you already know.
For teams using TypeScript in production who want Effect-level patterns without the learning curve.
import { Define, Result } from "within-ts"class NotFound extends Define.Error("NotFound")<{ id: string }>() {}class Db extends Define.Service("Db")<typeof postgres>() { static default() { return postgres }}class UserRepo extends Define.Entity<User>() { static async getById(id: string) { const user = await new Db().select(users).where(eq(users.id, id)).first() if (!user) return Result.err(new NotFound({ id })) return Result.ok(new UserRepo(user)) }}async function getUser(id: string) { return UserRepo.getById(id)}See the transformation
Four small changes. Completely different code.
Watch the same function evolve from a fragile try/catch to typed, testable, injectable code — one step at a time.
A typical TypeScript function
This is what most codebases look like. The function works — until it doesn't.
Testing
Swap any dependency in one line
Services use AsyncLocalStorage under the hood. Call Db.run(testDb, fn) to override any service for the duration of a callback — no mocking libraries, no DI frameworks.
it("returns NotFound for missing users", async () => { const result = await Db.run(testDb, () => UserRepo.getById("nonexistent") ) expect(result.ok).toBe(false) expect(result.error._tag).toBe("NotFound")})Batteries included
Everything else you'd build yourself
Each is a single import. No configuration.
Logger
Typed context that propagates through AsyncLocalStorage automatically.
log.with({ requestId }, fn)Cache
Memoize any async function with TTL. One import, one line.
Cache.memoize(fn, { ttl: "5m" })Schedule
Retry with exponential backoff, jitter, and conditional stop.
Schedule.retry(fn, { times: 3, delay: "1s" })Entity
Domain models with methods, getters, and direct service access.
Define.Entity<{ name: string }>()Start building better TypeScript
Pick the problem you're solving today. Each module stands alone — adopt one, adopt all, no lock-in.