Files
pubsub/docs/architecture
glm-5.1 8c025c3433 Set up project structure, source files, and architecture docs
- Copy core source from alkhub_ts/packages/core/pubsub/ with import path fixups
  (typed_event_target.ts → types.ts, .ts → .js extensions)
- Make PubSubPublishArgsByKey exported (was private type, needed by barrel)
- Add package.json with sub-path exports and optional peer deps (ioredis)
- Add tsup.config.ts with multi-entry + splitting for tree-shaking
- Add tsconfig.json, vitest.config.ts, .gitignore
- Add AGENTS.md with project conventions and adapter checklist
- Add architecture docs following taskgraph/alkhub pattern:
  docs/architecture/README.md, api-surface.md, event-targets.md,
  iroh-transport.md, build-distribution.md
- Add ADRs: 001-graphql-yoga-fork, 002-tree-shake-pattern
- Copy migration research doc to docs/research/migration.md
- Dual-license MIT OR Apache-2.0 (matching taskgraph)
2026-04-30 10:20:41 +00:00
..

status, last_updated
status last_updated
draft 2026-04-30

@alkdev/pubsub Architecture

Type-safe publish/subscribe with pluggable event target adapters. The core (createPubSub + TypedEventTarget + operators) has no transport dependency. Each adapter (Redis, WebSocket, Iroh) is an isolated module that only imports its own peer dependency.

Why This Exists

Extracted from @alkdev/alkhub_ts/packages/core/pubsub/, which itself was adapted from @graphql-yoga/subscription and @graphql-yoga/typed-event-target. The pubsub module was already self-contained within alkhub — zero cross-module imports from operations, config, logger, or MCP. Extracting it into a standalone package:

  1. Reduces coupling — alkhub depends on pubsub, not the other way around
  2. Enables reuse — multiple alkhub packages can share the same pubsub instance
  3. Isolates peer deps — Redis and Iroh are heavy native dependencies; consumers that don't need them shouldn't carry them
  4. Matches established pattern@alkdev/taskgraph and @alkdev/typemap already use the standalone-package pattern

Core Principle

The TypedEventTarget interface is the contract. All transports implement the same addEventListener / dispatchEvent / removeEventListener surface. createPubSub doesn't know or care which transport is in use — it just dispatches events to whatever TypedEventTarget it was given.

This means swapping from in-process to Redis to WebSocket to Iroh is a one-line config change:

const pubsub = createPubSub<MyEventMap>({
  eventTarget: createRedisEventTarget({ publishClient, subscribeClient }),
});

What This Package Provides

  • CorecreatePubSub, TypedEventTarget, TypedEvent, topic scoping, filter/map/pipe operators
  • Adapters (each is a peer-dep island, importable via sub-path export):
    • In-process (default EventTarget, no adapter needed)
    • Redis (@alkdev/pubsub/event-target-redis, peer dep: ioredis)
    • WebSocket (future: @alkdev/pubsub/event-target-websocket)
    • Iroh (future: @alkdev/pubsub/event-target-iroh, peer dep: @rayhanadev/iroh)

Consumer Context

alkhub (hub-spoke coordinator)

The hub uses pubsub for event routing between operations, runners, and the SSE interface. The event map is the call protocol — typed JSON events (call.requested, call.responded, session.status, etc.). Transport choice depends on deployment:

Deployment Transport
Single-process hub In-process (default)
Hub + worker processes Redis
Hub + remote spokes WebSocket or Iroh

Future: standalone spoke SDK

Spokes will import @alkdev/pubsub directly to create their event target (WebSocket or Iroh) and wire it into createPubSub. The call protocol types live in a separate @alkdev/call-protocol package (not yet extracted).

Threat Model

  • Fork provenance — core pubsub and typed event target are adapted from graphql-yoga (MIT). All original copyright notices are preserved in file headers. See ADR-001.
  • Peer dep isolation — Redis and Iroh are optional peer dependencies. A consumer that only needs in-process transport installs zero extra packages. A consumer using Redis but not Iroh installs ioredis only.
  • Type-only importsevent-target-redis.ts imports ioredis types only at compile time. At runtime, the consumer must provide the actual Redis/Cluster instances.

Architecture Documents

Document Content
api-surface.md createPubSub factory, PubSub types, operators, TypedEventTarget types
event-targets.md In-process, Redis, WebSocket adapters — interface, configuration, limitations
iroh-transport.md Iroh P2P QUIC transport — protocol, framing, identity, hub/spoke sides, reconnection
build-distribution.md Dependencies, project structure, tree-shaking, sub-path exports, targets

Document Lifecycle

Architecture documents use YAML frontmatter with status and last_updated fields:

---
status: draft | stable | deprecated
last_updated: YYYY-MM-DD
---
Status Meaning Transitions
draft Under active development. Content may change. stable when implementation is complete and tests verify API contract.
stable API contracts are locked. Changes require review cycle. deprecated when superseded.
deprecated Superseded. Kept for reference. Removed when no longer referenced.

References

  • Source: @alkdev/alkhub_ts/packages/core/pubsub/
  • Upstream: @graphql-yoga/subscription and @graphql-yoga/typed-event-target (MIT)
  • alkhub pubsub-redis doc: @alkdev/alkhub_ts/docs/architecture/pubsub-redis.md
  • alkhub spoke-runner doc: @alkdev/alkhub_ts/docs/architecture/spoke-runner.md
  • Migration research: docs/research/migration.md