5.6 KiB
5.6 KiB
Tasks: enterprise-browser-broadcast-channel
Change ID: enterprise-browser-broadcast-channel
Package: @suaveplan/broadcast-channel
Target path: browser/platform/broadcast-channel
Prerequisite packages on Verdaccio: @suaveplan/events, @suaveplan/error, @suaveplan/crypto, @suaveplan/logger, @suaveplan/schema-registry
0. Pre-flight (non-negotiable)
- 0.1 Confirm clean working tree (
git statusreports nothing) and no in-flight worktrees with uncommitted work (git worktree listthengit statusin each) - 0.2 If any worktrees exist under
.claude/worktrees/, account for each one: still-active agent, work-pending-merge, or stale-and-prune. Stale worktrees MUST be removed (git worktree remove) before opening a new one — code that has been lost twice on main is the failure mode this guards against. - 0.3 Confirm the package set in
MANIFEST.mdmatchesfind packages -maxdepth 3 -name package.jsoncount. Drift here means a prior change archived without regenerating the manifest.
1. Scaffold
- Create directory tree per
proposal.md. package.jsonv0.0.0,"sideEffects": false.dependencies:@suaveplan/events workspace:^,@suaveplan/error workspace:^,@suaveplan/types workspace:^,@suaveplan/logger workspace:^,@suaveplan/crypto workspace:^,@suaveplan/schema-registry workspace:^,zod catalog:forms,@opentelemetry/api catalog:observability,tslib catalog:builddevDependencies:vitest catalog:testing,@vitest/coverage-istanbul catalog:testing,happy-dom catalog:testing,@playwright/test catalog:testing,@suaveplan/testing workspace:^- Subpath exports:
".","./transport","./leader","./types"
tsconfig.json,vitest.config.ts(pool: forks, happy-dom environment,fileParallelism: false).
2. Error codes
CHANNEL_CLOSED,MESSAGE_MALFORMED,PAYLOAD_TOO_LARGE.registerBroadcastChannelErrorCodes()— call eagerly.
3. Types (src/types/broadcast-channel.types.ts)
ChannelConfig,ChannelMessage,TabInfo,BroadcastChannelEventMap,ChannelHandler,ChannelLike(typedMessageEvent),ChannelTransport,AskOptions.- Write
broadcast-channel.types.md.
4. Internal ID generator (src/internal/id.ts)
generateId(): string— wraps@suaveplan/cryptorandom bytes → hex string.- Replaces duplicate
generateTabId/generateMsgIdthat existed in the original.
5. ChannelMessage schema (src/schema/channel-message-schema/)
channel-message-schema.ts— ZodChannelMessageSchema;registerChannelMessageSchema().- Write
channel-message-schema.md.
6. BroadcastTransport (src/transport/broadcast/)
broadcast.ts— wraps nativeBroadcastChannel; implementsChannelTransport.- Write
broadcast.test.ts(send, receive, close). Writebroadcast.md.
7. LocalStorageTransport (src/transport/local-storage/)
local-storage.ts—localStorage+storageevent fallback; sameChannelTransportinterface.- Auto-polyfill detection:
typeof BroadcastChannel === 'undefined'. - Write
local-storage.test.ts(send, receive, close). Writelocal-storage.md.
8. Leader strategies (src/leader/leader/)
leader.ts—LeaderStrategyinterface;LowestIdStrategy(lowest tab ID wins);ActiveTabStrategy(prefers visible tab viavisibilitychange, tie-break byjoinedAt).- Write
leader.test.ts(LowestId election, ActiveTab election, disconnect scenario). Writeleader.md.
9. ask primitive (src/ask/ask/)
ask.ts—ask<T, R>(channel, type, payload, options): Promise<R>using correlation IDs and timeout.- Reject with
CHANNEL_CLOSEDon close; reject withWORKER_REQUEST_TIMEOUTon timeout. - Write
ask.test.ts(success, timeout, channel-close rejection, collision). Writeask.md.
10. TypedBroadcastChannel (src/channel/channel/)
channel.ts— composing transport + leader + ask + schema validation.handleIncomingvalidates viaChannelMessageSchema; drops + emitserroron malformed.send()guards againstmaxMessageBytes; throwsPAYLOAD_TOO_LARGE.- Wire OTel metrics.
- Wire
@suaveplan/loggerfor dropped frames and stale-tab cleanup. - Write
channel.test.ts(send, receive, malformed drop, payload-too-large, leader election, stale cleanup). Writechannel.md.
11. Integration test (tests/integration.test.ts)
BroadcastTransport→TypedBroadcastChannelround-trip.LocalStorageTransportsame API round-trip.askwith timeout: verify rejection.- Leader election: two channels, close leader, verify re-election.
12. Playwright E2E (e2e/broadcast-channel.spec.ts)
- Chromium: two browser tabs on same origin; one sends, other receives.
- WebKit:
LocalStorageTransportfallback verified (inject config to skipBroadcastChannel). - Leader election: three tabs, close leader, verify re-election in remaining two.
13. Docs + verify
- Write
.mdper module (≥200 words, API reference, examples). - Write
README.mdwith subpath table, transport guide, leader-election guide,askexample. bunx turbo lint typecheck test --filter=@suaveplan/broadcast-channel— green.bunx turbo test:coverage— 100%.- No
@ts-expect-errorinsrc/or tests. NoanyinChannelLike. - Playwright E2E green on Chromium and WebKit.
bun scripts/publish.ts --package @suaveplan/broadcast-channel.- Archive to
openspec/archive/2026-04-24-enterprise-browser-broadcast-channel/.