# @wrnexus/pubsub > Topic-based publish/subscribe with a pluggable driver — in-process by default, Redis for cross-process messaging. Part of the **WrNexus** framework — an SSR-first, Bun-native full-stack web framework. ## Overview `@wrnexus/pubsub` is a small server-side pub/sub bus. You publish messages to a topic and subscribe with topic patterns; handlers fire for matching topics. The default driver keeps everything in-process, and you can swap in the Redis driver (`@wrnexus/pubsub/redis`) to fan messages out across processes or hosts. It also backs `@wrnexus/core`'s realtime bridge for horizontal scaling. ## Installation ```bash bun add @wrnexus/pubsub ``` > Private package — the machine must be authenticated to the `wrnexus` npm org > (a read token in `~/.npmrc`). Requires **Bun** (Node is not supported). ## API ### `createPubSub(driver?): PubSub` Creates a bus over a driver. Defaults to `memoryDriver()` (in-process). ```ts interface PubSub { publish(topic: string, message: T): Promise; subscribe(pattern: string, handler: Handler): () => void; close(): Promise; } type Handler = (message: T, topic: string) => void | Promise; ``` - `publish(topic, message)` — resolves once the driver and in-memory async handlers finish. - `subscribe(pattern, handler)` — returns an unsubscribe function. - `close()` — idempotently rejects new work, clears local subscriptions, and closes the driver. ### Pattern matching Subscription patterns match in three ways: - **Exact** — `"order:created"` matches only that topic. - **Prefix** — `"order:*"` matches any topic starting with `"order:"`. - **Everything** — `"*"` matches all topics. ### `memoryDriver(): PubSubDriver` The default in-process driver. Handlers are invoked synchronously (fire-and-forget for async handlers) whenever a published topic matches a registered pattern. ```ts interface PubSubDriver { publish(topic: string, message: unknown): void | Promise; subscribe(pattern: string, handler: Handler): () => void; } ``` ### `@wrnexus/pubsub/redis` — `redisDriver(url?)` A cross-process driver backed by Redis. It speaks RESP over a raw TCP socket via `Bun.connect`, so it adds **no npm dependency**. `url` defaults to `$REDIS_URL`, then `redis://localhost:6379`. The URL may carry a password and a database index (e.g. `redis://:secret@host:6379/2`). ```ts function redisDriver(url?: string, options?: RedisDriverOptions): PubSubDriver & { close(): void }; ``` - Exact topics use Redis `SUBSCRIBE`; wildcard patterns (`ns:*`, `*`) use `PSUBSCRIBE`, whose glob semantics line up with this library's matching. - Messages are JSON-stringified on publish and `JSON.parse`d on receipt; a payload that isn't valid JSON is delivered as the raw string. - `close()` tears down both the subscriber and publisher connections. - Lost sockets reconnect with bounded exponential backoff and active subscriptions are replayed. `maxPending` bounds unavailable-connection writes (default 1000); `reconnectDelayMs` and `reconnectMaxDelayMs` tune recovery (100ms/5000ms). ### RESP codec (internal) `redis.ts` uses a minimal RESP implementation exported from `resp.ts` (`encodeCommand`, `parseReply`, `concat`, and the `RespValue` type). These are implementation details of the Redis driver, not part of the public package entry. ## Usage In-process (default): ```ts import { createPubSub } from "@wrnexus/pubsub"; const bus = createPubSub(); const off = bus.subscribe("order:*", (msg, topic) => { console.log(topic, msg); }); await bus.publish("order:created", { id: 7 }); off(); // unsubscribe ``` Cross-process with Redis: ```ts import { createPubSub } from "@wrnexus/pubsub"; import { redisDriver } from "@wrnexus/pubsub/redis"; const driver = redisDriver("redis://localhost:6379"); const bus = createPubSub(driver); bus.subscribe("order:*", (msg, topic) => { // received on any app process subscribed to this pattern }); await bus.publish("order:created", { id: 7 }); // on shutdown (also closes the driver) await bus.close(); ``` ## Requirements / Notes - **Bun-only.** The Redis driver depends on `Bun.connect`; it throws `redisDriver requires the Bun runtime (Bun.connect).` outside Bun. The default in-memory driver has no runtime dependencies. - The Redis driver reads `REDIS_URL` from the environment when no `url` is passed. - Backs [`@wrnexus/core`](../core)'s realtime bridge for horizontal scaling. - No external npm dependencies — the Redis client is a self-contained RESP codec.