Files
suaveplan/openspec/changes/enterprise-browser-broadcast-channel/tasks.md
2026-05-27 23:29:53 +01:00

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 status reports nothing) and no in-flight worktrees with uncommitted work (git worktree list then git status in 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.md matches find packages -maxdepth 3 -name package.json count. Drift here means a prior change archived without regenerating the manifest.

1. Scaffold

  • Create directory tree per proposal.md.
  • package.json v0.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:build
    • devDependencies: 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 (typed MessageEvent), ChannelTransport, AskOptions.
  • Write broadcast-channel.types.md.

4. Internal ID generator (src/internal/id.ts)

  • generateId(): string — wraps @suaveplan/crypto random bytes → hex string.
  • Replaces duplicate generateTabId/generateMsgId that existed in the original.

5. ChannelMessage schema (src/schema/channel-message-schema/)

  • channel-message-schema.ts — Zod ChannelMessageSchema; registerChannelMessageSchema().
  • Write channel-message-schema.md.

6. BroadcastTransport (src/transport/broadcast/)

  • broadcast.ts — wraps native BroadcastChannel; implements ChannelTransport.
  • Write broadcast.test.ts (send, receive, close). Write broadcast.md.

7. LocalStorageTransport (src/transport/local-storage/)

  • local-storage.ts — localStorage + storage event fallback; same ChannelTransport interface.
  • Auto-polyfill detection: typeof BroadcastChannel === 'undefined'.
  • Write local-storage.test.ts (send, receive, close). Write local-storage.md.

8. Leader strategies (src/leader/leader/)

  • leader.ts — LeaderStrategy interface; LowestIdStrategy (lowest tab ID wins); ActiveTabStrategy (prefers visible tab via visibilitychange, tie-break by joinedAt).
  • Write leader.test.ts (LowestId election, ActiveTab election, disconnect scenario). Write leader.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_CLOSED on close; reject with WORKER_REQUEST_TIMEOUT on timeout.
  • Write ask.test.ts (success, timeout, channel-close rejection, collision). Write ask.md.

10. TypedBroadcastChannel (src/channel/channel/)

  • channel.ts — composing transport + leader + ask + schema validation.
  • handleIncoming validates via ChannelMessageSchema; drops + emits error on malformed.
  • send() guards against maxMessageBytes; throws PAYLOAD_TOO_LARGE.
  • Wire OTel metrics.
  • Wire @suaveplan/logger for dropped frames and stale-tab cleanup.
  • Write channel.test.ts (send, receive, malformed drop, payload-too-large, leader election, stale cleanup). Write channel.md.

11. Integration test (tests/integration.test.ts)

  • BroadcastTransport → TypedBroadcastChannel round-trip.
  • LocalStorageTransport same API round-trip.
  • ask with 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: LocalStorageTransport fallback verified (inject config to skip BroadcastChannel).
  • Leader election: three tabs, close leader, verify re-election in remaining two.

13. Docs + verify

  • Write .md per module (≥200 words, API reference, examples).
  • Write README.md with subpath table, transport guide, leader-election guide, ask example.
  • bunx turbo lint typecheck test --filter=@suaveplan/broadcast-channel — green.
  • bunx turbo test:coverage — 100%.
  • No @ts-expect-error in src/ or tests. No any in ChannelLike.
  • 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/.