-
Notifications
You must be signed in to change notification settings - Fork 2
Home
A suite of independent, cross-runtime TypeScript libraries — every
package works identically on Deno, Bun and Node.js, and most
also run on Cloudflare Workers and in the browser (see
Runtime support). Published to
JSR under the @tundralibs scope.
They cover databases (an ORM, a query language, and connection drivers for PostgreSQL, MariaDB, SQLite, MongoDB, Redis, and Memcached), schema validation, structured logging, distributed tracing, Prometheus metrics, authentication, caching, dependency injection, HTTP routing, an application framework that binds them together, cryptography, and ID generation — each independent and dependency-light.
-
Ambient
— Cross-runtime request-scoped context over AsyncLocalStorage — correlation/trace ids and custom fields that survive await, no threading
-
Cacher
— Cross-runtime caching with a unified API over Memory, Redis, Memcached and Cloudflare Workers KV engines
-
compat
— Compatibility layer smoothing API differences across Deno, Bun, and Node.js
-
Cronus
— Cross-runtime minute-resolution cron scheduler — tick-and-match (impossible expressions never crash), per-job overlap prevention, cron/run-once/run-now triggers.
-
crypt
— Cross-runtime cryptography — hashing, AES/RSA encryption, HMAC/RSA/ECDSA/Ed25519 signing, JWT, OTP, key derivation, and secure random
-
Doctor
— Lightweight dependency injection with Singleton, Scoped, and Transient vial lifecycles — TC39 decorators, typed inject() tokens, no reflect-metadata
-
drivers
— Cross-runtime connection drivers for SQL (PostgreSQL, MariaDB/MySQL, SQLite), MongoDB, Redis, and Memcached — plus edge/serverless HTTP dialects (Neon, Turso, Cloudflare D1)
-
Guardian
— Schema validation for TypeScript — strict at compile time, forgiving at API boundaries
-
ID
— Cross-runtime ID generators — NanoID, CUID/CUID2, ULID, MongoDB ObjectID, and sequential/simple IDs
-
MetroMan
— Prometheus-compatible in-process metrics: Counter, Gauge, Histogram, Summary, and a central registry (MetroMan).
-
NORM
— Typed, cross-runtime ORM over OQL and drivers — one schema drives types, validation, relations, migrations, and at-rest column encryption
-
OQL
— Object Query Language — type-safe, database-agnostic query definitions
-
Pact
— Permissions, Authentication, Control & Tokens — a transport-agnostic authentication & authorization toolkit: RBAC BigInt-bitmask permissions, JWT & opaque sessions with refresh-token rotation, passkeys (WebAuthn/FIDO2), four credential schemes (password, bearer, API keys, HMAC), TOTP MFA, an OAuth2/OIDC client, and drop-in middleware for express/fastify/oak/hono — all over flat bring-your-own-storage hooks
-
RadRouter
— Compressed radix-tree HTTP router — typed parameters, greedy patterns, versioned endpoints, generic middleware
-
Rapid
— rAPId — config-driven application framework: HTTP routes, WebSocket commands and cron jobs behind one middleware onion, decorated modules, an HTML-over-the-wire UI layer, native observability (slogger, tracer, ambient correlation) and pact authentication.
-
RESTler
— Cross-runtime REST API client base class for building typed per-vendor SDKs on Deno, Bun, and Node.js
-
RPC
— Remote Procedure Call + pub/sub framework over WebSocket — typed request/response, channels, middleware, and pluggable adapters
-
Slogger
— Cross-runtime structured logging that fans one record out to many formats in-process — console, JSON, syslog, file, HTTP, TCP, or any custom handler
-
Tracer
— Cross-runtime distributed tracing — W3C Trace Context propagation, automatic span nesting via ambient async context, pluggable samplers and exporters
-
utils
— Core TypeScript building blocks — the typed Options + Events base class, BaseError, Singleton, and shared helpers (config/env, memoize, IP/subnet, free-port)
Each package's README is its main documentation; deeper guides live in the wiki.
Coming from another ecosystem? Here's the TundraLibs equivalent of tools you may already know — every one is cross-runtime (Deno, Bun, Node).
| If you want… | Reach for |
|---|---|
| A Zod / Yup / Joi alternative (schema validation) | @tundralibs/guardian |
| A Prisma / Drizzle / TypeORM alternative (ORM) | @tundralibs/norm |
| A Knex-style query builder (SQL + MongoDB) | @tundralibs/oql |
| Unified pg / mysql2 / ioredis / mongodb connection drivers | @tundralibs/drivers |
| An InversifyJS / tsyringe alternative (dependency injection) | @tundralibs/doctor |
| A Winston / Pino alternative (structured logging) | @tundralibs/slogger |
| An OpenTelemetry-style tracing SDK | @tundralibs/tracer |
| A prom-client alternative (Prometheus metrics) | @tundralibs/metro-man |
| A node-cron / croner alternative (cron scheduler) | @tundralibs/cronus |
| nanoid / uuid / cuid / ulid / ObjectID in one library | @tundralibs/id |
| A Keyv / node-cache alternative (Memory/Redis/Memcached) | @tundralibs/cacher |
| A jose / bcrypt / node:crypto toolkit (JWT, OTP, AES, hashing) | @tundralibs/crypt |
| A Passport / Lucia / Auth.js alternative (auth) | @tundralibs/pact |
| An Axios / Ky-style base for building typed API SDKs | @tundralibs/restler |
| A socket.io-style typed RPC + pub/sub over WebSocket | @tundralibs/rpc |
| A find-my-way-style radix-tree HTTP router | @tundralibs/radrouter |
| An Express / Fastify / Hono / NestJS alternative (app framework) | @tundralibs/rapid |
| An AsyncLocalStorage / cls-hooked request context | @tundralibs/ambient |
Every package runs on Deno, Bun and Node.js. Most also run on Cloudflare Workers and in a browser — the exceptions are listed below, along with exactly what is unavailable and why.
Verified by running each package's real operations on
workerd 1.20260811.1 (nodejs_compat, compatibility date 2026-08-04)
and in Chrome via Vite, not by inspecting imports.
| Package | Deno | Bun | Node | Workers | Browser | Not available |
|---|---|---|---|---|---|---|
| ambient | ✅ | ✅ | ✅ | ✅ | ❌ |
Browser: no AsyncLocalStorage, so createContext() throws. ambient.get() outside a scope returns undefined rather than throwing. |
| cacher | ✅ | ✅ | ✅ | Memory engine works everywhere. Workers: Redis and Memcached connect directly on cloudflare:sockets via compat/net — no nodejs_compat flag needed. Browser: Memory only — Redis and Memcached need real TCP, which a browser does not have. |
||
| compat | ✅ | ✅ | ✅ |
Workers (since 2.6.0): outbound TCP/TLS (net.connect/upgradeTls) runs directly on cloudflare:sockets — no nodejs_compat flag needed. File I/O under /tmp runs on node:fs, which does need nodejs_compat (makeTempFile/makeTempDir also need { allowEphemeral: true }). WebSocketServer.handleUpgrade() works either way. Still unsupported: net.listen(), ./udp, ./watch, WebServer.start()/.listen(), ./cli's subprocess spawning, and file's directory/copy/move/rename/realPath/openFile ops. Browser: all of the above throw, plus ./websocket entirely; ./runtime, ./fetch, ./http, ./path, ./common and ./permissions work. |
||
| cronus | ✅ | ✅ | ✅ | ✅ | ✅ | — |
| crypt | ✅ | ✅ | ✅ | ✅ | ✅ | — |
| doctor | ✅ | ✅ | ✅ | ✅ | ✅ | — |
| drivers | ✅ | ✅ | ✅ | Import engines by subpath. Workers: Postgres, Redis and Memcached connect directly on cloudflare:sockets via compat/net — no nodejs_compat flag needed. MariaDB also connects, but via a different path: nodejs_compat shims node:net for the third-party mariadb driver. The fetch-based ./d1, ./neon and ./turso work unchanged. ./sqlite needs a native binding (unavailable); ./mongo is unverified — never tested on workerd. Browser: only ./d1, ./neon and ./turso — the rest need real TCP or Node globals a browser doesn't have. |
||
| guardian | ✅ | ✅ | ✅ | ✅ | ✅ | — |
| id | ✅ | ✅ | ✅ | ✅ | ✅ | — |
| metro-man | ✅ | ✅ | ✅ | ✅ | ✅ | — |
| norm | ✅ | ✅ | ✅ | The root barrel registers six of the seven dialects — every one but sqlite, which needs its own import '@tundralibs/norm/engines/sqlite'. Workers: ./postgres and ./maria genuinely connect (same mechanisms as drivers, above), and so do the fetch-only ./d1/./neon/./turso; ./mongo is unverified, ./sqlite never runs there. Browser: the barrel itself bundles fine (verified with a real esbuild build) — only the fetch-only trio can actually connect, since a browser has no raw sockets for postgres/maria to use. |
||
| oql | ✅ | ✅ | ✅ | ✅ | ✅ | — |
| pact | ✅ | ✅ | ✅ | ✅ | ✅ | — |
| radrouter | ✅ | ✅ | ✅ | ✅ | ✅ | — |
| rapid | ✅ | ✅ | ✅ | ❌ |
Workers: serve through app.fetch(request) instead of app.start() — HTTP routes, middleware and the UI layer work; there is no listening socket, filesystem or scheduler, so socket commands error if registered, jobs are not scheduled (fire them from a Cron Trigger with app.triggerJob(name)), file uploads are rejected with a typed 501, and the live bridge cannot connect. Browser: the target rapid serves pages to, not one it runs in — no server socket, so nothing to mount. |
|
| restler | ✅ | ✅ | ✅ | ✅ | ✅ | — |
| rpc | ✅ | ✅ | ✅ |
Client works everywhere. Workers: Server serves connections via handleUpgrade(request) from a fetch handler — commands, middleware and channels all work; cross-connection publish() fan-out does not (each connection is pinned to its own request's I/O context — the drop now surfaces via onSendError instead of vanishing silently), and real fan-out needs a Durable Object this package doesn't provide. listen()/handlers() still need a host runtime. Browser: Server cannot run at all — Client is what a browser uses. ./conformance is test-only and never bundles. |
||
| slogger | ✅ | ✅ | ✅ | Console, Memory and HTTP handlers work everywhere. Workers: TCPHandler and SyslogHandler's TCP transport connect via compat/net's cloudflare:sockets — no nodejs_compat flag needed. FileHandler also works — reads/writes land in workerd's /tmp — but a record is gone by the very next request, not merely when the isolate eventually recycles; the handler detects this at open and warns once per instance. SyslogHandler's UDP transport doesn't work (Workers has no UDP), and neither does its UNIX transport (compat/net disallows UNIX sockets on Workers). Browser: no filesystem or raw socket — File, TCP and Syslog all throw. |
||
| tracer | ✅ | ✅ | ✅ | ✅ |
Browser: manual startSpan + export and the read-only active/get degrade fine, but the ergonomic auto-nesting path — startActiveSpan / run and the wrap / wrapClient witnesses — throws, since a plain browser has no AsyncLocalStorage. |
|
| utils | ✅ | ✅ | ✅ | ✅ | ✅ | — |
✅ everything works ·
Two things worth knowing. Outbound TCP/TLS genuinely works on Cloudflare
Workers, confirmed two independent ways, with two different flag requirements:
compat/net connects directly on cloudflare:sockets (since 2.6.0) — no
nodejs_compat flag needed — which is what postgres/redis/memcached use;
a driver that manages its own sockets instead, like drivers/maria's
third-party mariadb client, reaches the same result through Cloudflare's
nodejs_compat node:net shim, entirely independent of compat. Neither path
exists in a browser: no filesystem, no TCP and no UDP there — anything built on
those throws UnsupportedRuntimeError rather than failing silently.
Packages are consumed from JSR in any runtime:
deno add @tundralibs/<package> # Deno
bunx jsr add @tundralibs/<package> # Bun
npx jsr add @tundralibs/<package> # Node.jsgit clone https://github.com/TundraSoft/TundraLibs.git
cd TundraLibs
bun install # node_modules for the Bun/Node test runs
deno task test # Deno test suite
bun test packages/ # Bun
node --import tsx --test 'packages/**/*.test.ts' # Node.js
deno task fmt && deno task lint && deno task checkCreate or remove packages with the workspace tool (it also regenerates every config that enumerates packages):
deno task workspace:add MyPkg
deno task workspace:remove mypkg
deno task workspace:syncSee CONTRIBUTING.md for the PR workflow (conventional titles, one package per PR, squash merge) and SECURITY.md for reporting vulnerabilities.
Versioning, changelogs, tags, and JSR publishing are fully automated from conventional commits (release-please maintains one release PR per package). Contributors never bump versions by hand.