@zvoove/broadcast-channels
v1.2.0
Published
Shared Broadcast Channel messaging contract for PDL micro-frontend architecture
Readme
@zvoove/broadcast-channels
Typed, validated event bus for the PDL micro-frontend architecture. Provides safe,
structured communication between the Shell app and fragment applications over the
browser's native BroadcastChannel API.
Installation
pnpm add @zvoove/broadcast-channelsor
npm add @zvoove/broadcast-channelsTwo-channel model
Communication is split into two directional channels:
| Channel | Name | Direction | Events |
| ---------------- | ------------------ | ------------------------- | ------------------------------------- |
| Shell channel | pdl:shell | Shell → Fragments | AUTH_CHANGED, THEME_CHANGED |
| Fragment channel | pdl:{fragmentId} | Fragment → Shell / Others | FRAGMENT_READY, BREADCRUMB_UPDATE |
Shell app
import {
type AuthUser,
CONTRACT_VERSION,
createFragmentChannel,
createShellChannel,
onVersionMismatch,
} from '@zvoove/broadcast-channels';
// ── Outbound: emit state changes to all fragments ──────────────────────────
const shell = createShellChannel();
function onLogin(user: AuthUser) {
shell.emit('AUTH_CHANGED', { user });
}
function onLogout() {
shell.emit('AUTH_CHANGED', { user: null });
}
function onThemeChange(theme: 'light' | 'dark') {
shell.emit('THEME_CHANGED', { theme });
}
// ── Inbound: listen to a specific fragment ─────────────────────────────────
const zain = createFragmentChannel('zain');
zain.on('BREADCRUMB_UPDATE', ({ items }) => {
updateBreadcrumbs(items); // items is BreadcrumbItem[], compatible with unity-ui
});
onVersionMismatch(zain, CONTRACT_VERSION, (fragmentId, version) => {
console.warn(`Fragment "${fragmentId}" is on contract v${version} — reload may be required`);
});
// ── Cleanup ────────────────────────────────────────────────────────────────
shell.destroy();
zain.destroy();Fragment app (e.g. zain)
import {
type BreadcrumbItem,
type Unsubscribe,
announceFragment,
createFragmentChannel,
createShellChannel,
} from '@zvoove/broadcast-channels';
const shell = createShellChannel();
const frag = createFragmentChannel('zain');
const subscriptions: Unsubscribe[] = [];
// ── On mount ───────────────────────────────────────────────────────────────
subscriptions.push(
shell.on('AUTH_CHANGED', ({ user }) => {
if (user === null) return clearAuth();
setAuth(user); // user is typed as AuthUser
}),
shell.on('THEME_CHANGED', ({ theme }) => {
document.documentElement.dataset.theme = theme;
}),
);
announceFragment(frag, 'zain'); // emits FRAGMENT_READY with CONTRACT_VERSION
// ── On route change ────────────────────────────────────────────────────────
function onRouteChange(items: BreadcrumbItem[]) {
frag.emit('BREADCRUMB_UPDATE', { items });
}
// ── On unmount ─────────────────────────────────────────────────────────────
subscriptions.forEach((u) => u());
shell.destroy();
frag.destroy();Event types
Shell events (pdl:shell)
type ShellEvent =
| { type: 'AUTH_CHANGED'; payload: { user: AuthUser | null } }
| { type: 'THEME_CHANGED'; payload: { theme: 'light' | 'dark' } };
interface AuthUser {
id: string;
name: string;
email: string;
role: string;
department: string;
}Fragment events (pdl:{fragmentId})
type FragmentEvent =
| { type: 'FRAGMENT_READY'; payload: { fragmentId: string; version: string } }
| { type: 'BREADCRUMB_UPDATE'; payload: { items: BreadcrumbItem[] } };
// BreadcrumbItem is structurally compatible with BreadcrumbsItem from @zvoove/unity-ui
type BreadcrumbItem = { label: string; href: string };API reference
| Function | Returns | Description |
| ----------------------------------------- | ----------------------------- | --------------------------------------------------------- |
| createShellChannel(options?) | TypedChannel<ShellEvent> | Channel on pdl:shell. Shell emits; fragments subscribe. |
| createFragmentChannel(id, options?) | TypedChannel<FragmentEvent> | Channel on pdl:{id}. Fragment emits; shell subscribes. |
| fragmentChannelName(id) | string | Returns pdl:{id}. |
| announceFragment(channel, id, version?) | void | Emits FRAGMENT_READY with CONTRACT_VERSION (default). |
| onVersionMismatch(channel, version, cb) | Unsubscribe | Calls cb(fragmentId, actualVersion) on version skew. |
| CHANNEL_NAMES | { SHELL: 'pdl:shell' } | Shell channel name constant. |
| CONTRACT_VERSION | string | Runtime contract version for the version handshake. |
Development
pnpm install
pnpm build # ESM + CJS + .d.ts
pnpm test # Vitest
pnpm test:watch # Vitest watch mode
pnpm lint # ESLint
pnpm format # Prettier
pnpm docs # TypeDoc → docs/api-reference/See CONTRIBUTING.md for architecture details, how to add events, and the release process.
