@orpc-ws/shared
Shared helper package. Holds the seam interfaces every other package in
this monorepo depends on, so the client core and server core can agree on
Logger / Clock / Rng / heartbeat wire types without taking a
dependency on each other. It's published because the cores depend on
it at runtime — but it's an implementation-detail dependency, not a
package you import directly.
If you're a consumer of @orpc-ws/client or @orpc-ws/server,
you don't import from here directly — Logger and HeartbeatEvent are
re-exported from the public packages. This README exists for library
contributors.
Why a separate package
The client core, the server core, and the NestJS adapter all need the
same Logger shape and the same HEARTBEAT_PATH constant. Inlining them
in either core would force a client → server (or server → client)
dependency just for type definitions. A shared leaf package keeps the
dependency graph honest: each core depends on a small shared package
rather than on the other core.
What's in it
Logger— Pino-compatible structured-args shape (debug/info/warn/errorwith an optionalRecord<string, unknown>meta arg).noopLoggeris the default the cores fall back to. Source:src/logger.ts.Clock+TimerHandle—now()plus the foursetTimeout/setInterval/clear*proxies.systemClockis theDate.now-and-globals default. Tests inject fakes so jitter and storm-guard windows are deterministic. The library will not callDate.now()orsetTimeoutoutside this seam. Source:src/clock.ts.Rng—next(): numberreturning[0, 1).defaultRngwrapsMath.random; seeded fakes make jitter assertions exact. Source:src/rng.ts.HeartbeatEvent,HEARTBEAT_NAMESPACE,HEARTBEAT_PATH— wire shape and library-reserved path for the stealth heartbeat procedure. Server core publishes against this path; client core subscribes against this path. One source of truth. Source:src/heartbeat.ts.
Logger bridges
Tiny adapter factories for plugging common logger shapes into the Logger
seam without writing a 4-line object literal at every call site.
Consumers don't import from this package — the bridges surface on the public packages they already depend on:
| Bridge | Re-exported from |
|---|---|
consoleLogger |
@orpc-ws/client, @orpc-ws/server, @orpc-ws/server-nestjs |
fromPinoShape |
@orpc-ws/client, @orpc-ws/server, @orpc-ws/server-nestjs |
fromNestShape |
@orpc-ws/server-nestjs only (Nest is a server-side concept) |
Usage:
consoleLogger(prefix?)— wraps the globalconsole. Universal, zero-dep, fine for SPAs and quick scripts.import { consoleLogger, createOrpcWsClient } from "@orpc-ws/client"; createOrpcWsClient({ logger: consoleLogger("orpc-ws"), ... });fromPinoShape(pino)— adapts a Pino-style logger. Swaps to Pino's native(obj, msg)arg order when meta is present.import pino from "pino"; import { fromPinoShape } from "@orpc-ws/server"; // pass to OrpcWsServer / OrpcWsModule.forRoot({ logger: ... })fromNestShape(nestLogger)— adapts a NestJSLogger. Routesinfoto Nest'slog(Nest has noinfo) and merges meta into a single{ msg, ...meta }payload.import { Logger } from "@nestjs/common"; import { fromNestShape, OrpcWsModule } from "@orpc-ws/server-nestjs"; OrpcWsModule.forRoot({ logger: fromNestShape(new Logger("OrpcWs")), ... });
Input shapes (PinoShape, NestShape) are duck-typed interfaces — this
package takes no runtime dependency on Pino or @nestjs/common. The
consumer brings their own logger instance.
Adding a new shared type
The bar is "two cores need to agree on this and neither owns the concept." A type that only one package consumes belongs in that package, not here.
See also
- Top-level README
- Implementation plan §"Shared types pinned in Phase 0"