Relocate the ~6k-file docs tree from the repo root into plugins/document-navigator/docs/ so the plugin is self-contained and portable. docs/superpowers/ (specs and plans) stays at the repo root. Moved via cp+rm rather than git mv because directory rename() fails with EPERM on this WSL /mnt DrvFs mount; git rename-detection pairs the identical-content files at commit time. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
6.3 KiB
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 —
AskControlleris the request-side engine for the cross-tab request/response primitive layered on top of `TypedBroadca… - channel —
TypedBroadcastChannelis the high-level cross-tab messaging object exported by@suaveplan/broadcast-channel. It wra… - 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…
- codes — This module declares the stable error-code registry for
- id —
generateIdis the shared 16-character lowercase-hex identifier generator used for tab IDs and per-message IDs through…- id —
generateIdis the shared 16-character lowercase-hex identifier generator used for tab IDs and per-message IDs through…
- id —
- logger —
noopLoggeris a shared zero-costLoggerLikeimplementation used as the default byTypedBroadcastChanneland by `c…- noop —
noopLoggeris a shared zero-costLoggerLikeimplementation used as the default byTypedBroadcastChanneland by `c…
- noop —
- 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…
- metrics — This module declares the six OpenTelemetry counter instruments emitted by
- schemas — This module declares the Zod boundary schemas for
@suaveplan/broadcast-channel.ChannelMessageSchemavalidates ever…- messages — This module declares the Zod boundary schemas for
@suaveplan/broadcast-channel.ChannelMessageSchemavalidates ever…
- messages — This module declares the Zod boundary schemas for
- local-storage —
createLocalStorageChannelFactoryis theStorageLike-backed fallback transport. Use it whenever the host lacks a nat… - native —
nativeChannelFactoryis a thin adapter that exposes the globalBroadcastChannelconstructor as aChannelLike. 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
channel/channel—TypedBroadcastChannelcore.channel/heartbeat—HeartbeatRunnerinterval pair.errors/codes—BROADCAST_CHANNEL_CODESregistry + four typed error subclasses.id/id—generateId(rng)shared 16-hex id generator.logger/noop— shared no-opLoggerLikedefault.otel/metrics— six OpenTelemetry counters.schemas/messages— Zod boundary schemas.transport/native— nativeBroadcastChanneladapter.transport/local-storage—StorageLike-backed fallback.types/broadcast-channel— public shapes.
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