Files
2026-05-27 23:47:53 +01:00

6.3 KiB

@suaveplan/broadcast-channel - Typed cross-tab messaging built on the BroadcastChannel API — leader election, tab presence, Zod-validated boundaries, OpenTelemetry-instrumented hot paths, StorageLike-backed fallback for Safari < 15.4. Browser-tier.

Typed cross-tab messaging built on the Web BroadcastChannel API. Browser-tier package — leader election, tab presence and heartbeats, message-type handler registry, Zod-validated boundaries, OpenTelemetry-instrumented hot paths, deterministic Clock / Rng / LoggerLike injection, and a StorageLike-backed fallback transport for Safari < 15.4 (and any sandbox where the native BroadcastChannel is unavailable).

Documentation tree

  • ask — AskController is the request-side engine for the cross-tab request/response primitive layered on top of `TypedBroadca…
    • ask — AskController is the request-side engine for the cross-tab request/response primitive layered on top of `TypedBroadca…
    • responder — AskResponderRegistry is the response-side counterpart to AskController. It maps an inbound handlerKey to the sing…
  • channel — TypedBroadcastChannel is the high-level cross-tab messaging object exported by @suaveplan/broadcast-channel. It wra…
    • channel — TypedBroadcastChannel is the high-level cross-tab messaging object exported by @suaveplan/broadcast-channel. It wra…
    • heartbeat — HeartbeatRunner is a tiny lifecycle helper that owns the pair of setInterval timers a TypedBroadcastChannel needs…
  • errors — This module declares the stable error-code registry for @suaveplan/broadcast-channel. Every error the package throws…
    • codes — This module declares the stable error-code registry for @suaveplan/broadcast-channel. Every error the package throws…
  • id — generateId is the shared 16-character lowercase-hex identifier generator used for tab IDs and per-message IDs through…
    • id — generateId is the shared 16-character lowercase-hex identifier generator used for tab IDs and per-message IDs through…
  • logger — noopLogger is a shared zero-cost LoggerLike implementation used as the default by TypedBroadcastChannel and by `c…
    • noop — noopLogger is a shared zero-cost LoggerLike implementation used as the default by TypedBroadcastChannel and by `c…
  • otel — This module declares the six OpenTelemetry counter instruments emitted by @suaveplan/broadcast-channel. Every counter…
    • metrics — This module declares the six OpenTelemetry counter instruments emitted by @suaveplan/broadcast-channel. Every counter…
  • schemas — This module declares the Zod boundary schemas for @suaveplan/broadcast-channel. ChannelMessageSchema validates ever…
    • messages — This module declares the Zod boundary schemas for @suaveplan/broadcast-channel. ChannelMessageSchema validates ever…
  • local-storage — createLocalStorageChannelFactory is the StorageLike-backed fallback transport. Use it whenever the host lacks a nat…
  • native — nativeChannelFactory is a thin adapter that exposes the global BroadcastChannel constructor as a ChannelLike. Use…

Install

bun add @suaveplan/broadcast-channel

Quick start

import {
    nativeChannelFactory,
    TypedBroadcastChannel,
} from "@suaveplan/broadcast-channel";

const channel = new TypedBroadcastChannel({
    config: { name: "app-bus" },
    factory: nativeChannelFactory,
});

channel.open();
channel.on<{ userId: string }>("user:signed-in", (msg) => {
    console.log(`peer tab ${msg.senderId} signed in as ${msg.payload.userId}`);
});
channel.send("user:signed-in", { userId: "u_123" });

Fallback transport (Safari < 15.4)

import {
    createLocalStorageChannelFactory,
    isNativeBroadcastChannelAvailable,
    nativeChannelFactory,
    TypedBroadcastChannel,
} from "@suaveplan/broadcast-channel";
import { yourStorageLike } from "./storage";

const factory = isNativeBroadcastChannelAvailable()
    ? nativeChannelFactory
    : createLocalStorageChannelFactory(yourStorageLike, {
          pollIntervalMs: 50,
          retentionMs: 30_000,
      });

const channel = new TypedBroadcastChannel({
    config: { name: "app-bus" },
    factory,
});

Modules

Error codes

Code When
BROADCAST_CHANNEL.CHANNEL_CLOSED send() called before open() or after close()
BROADCAST_CHANNEL.MESSAGE_MALFORMED inbound frame fails ChannelMessageSchema
BROADCAST_CHANNEL.PAYLOAD_TOO_LARGE outbound payload exceeds config.maxMessageBytes (default 1 MiB)
BROADCAST_CHANNEL.STORAGE_FAILED fallback-transport StorageLike get/set/list/delete rejected

OpenTelemetry

Six counters under meter @suaveplan/broadcast-channel@0.1.0:

  • channel.message.sent ({ type, channel })
  • channel.message.received ({ type, channel })
  • channel.message.dropped ({ reason, channel })
  • channel.tab.joined ({ channel })
  • channel.tab.left ({ channel })
  • channel.leader.elected ({ channel })

License

MIT