Files
pubsub/docs/architecture
glm-5.1 04b3464c36 Add call protocol module with streaming support
New sub-path export @alkdev/pubsub/call providing:
- CallEventSchema (TypeBox schemas) for call.requested/responded/part/completed/aborted/error
- PendingRequestMap with call() (request/response) and subscribe() (streaming via Repeater)
- CallError class and CallErrorCode constants
- Scoped topic subscriptions (call.responded:{requestId}) to avoid O(n) fanout
- subscribe() yields call.part events until call.completed or call.error,
  with automatic call.aborted on consumer break

Also adds @alkdev/typebox as runtime dependency and architecture doc.
2026-04-30 13:46:39 +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, Repeater (inlined from @repeaterjs/repeater)
  • Call protocol (@alkdev/pubsub/call) — PendingRequestMap, CallEventSchema, CallError, event types for request/response and streaming operations
  • 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. Call protocol types and PendingRequestMap are available from @alkdev/pubsub/call.

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
call-protocol.md Call/subscribe protocol — event types, PendingRequestMap, streaming, error model, transport mapping
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