feat: add devframe/in-page-channel, server-free page script ↔ panel communication - #302
Merged
Conversation
…communication One typed, same-origin channel between a devframe's page script and its panels (dock iframe, popup, Document PiP), with no server involved: - panel-initiated postMessage handshake granting a dedicated MessageChannel port per panel — boot order never matters, reloads re-handshake, and a per-tab instance id keeps multi-tab apps from cross-talking - functions authored with defineChannelFunction (the defineRpcFunction shape narrowed to the browser); type 'event' fans out to every panel, query/action are request/response via birpc - a shared-state layer mirroring rpc.sharedState with the page script as authority: automatic replay to late-joining panels, syncId-deduplicated patches - explicit fallback/error surface: coded InPageChannelError (timeout, closed, not-serializable, not-cloneable, invalid-args, state-uninitialized), connecting/connected/closed status with buffering, whenConnected(ms) for no-page-script fallbacks, heartbeat + port-close recovery The a11y inspector's page script and panel now ride this channel instead of their hand-rolled BroadcastChannel protocol, which also fixes the two-tabs cross-talk it had.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Public API down to 13 exports (4 runtime + 9 types): drop the speculative setup() hook, post-creation register(), PanelPeer.callEvent, the page script's creation-time transport option (addPanelPort covers it), and stop exporting wire internals (handshake tag/version/message types, defaultHandshakeTargets) and derived type helpers. errors/registry/port merge into one internal module; defineChannelFunction moves into the index.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
A new core subpath,
devframe/in-page-channel, giving every devframe a first-class, typed, server-free communication path between its page script (in the user app's page) and its panels (dock iframe, popup, Document PiP) — the third communication path named on the terms page, now with a real API. The a11y inspector is migrated onto it, deleting its hand-rolledBroadcastChannelprotocol.Why
Tools whose live loop is inspect the page (a11y's scan/highlight, pinia-colada-style store devtools) need the page script and panel to talk without a server, so the loop works identically in dev and static builds. Until now each tool hand-rolled this (a11y's ready/replay dance over an origin-wide
BroadcastChannel, which also cross-talked between two tabs of the same app). This lands one well-defined primitive instead.Design
opener, retried with backoff) answered by a dedicatedMessageChannelport per panel — boot order never matters, reloads on either side just re-handshake, same-origin enforced both ways. A per-tab instance id (sessionStorage) scopes handshakes; panels can pin to one instance.defineChannelFunctionreuses thedefineRpcFunctionauthoring shape (name,type, Standard-Schemaargs/returns,jsonSerializable,handler/setup) narrowed to the browser.type: 'event'fans out to every panel;query/actionare request/response (birpc); peer handles give targeted page-script→panel calls plus presence.channel.sharedState.get(key)mirrorsrpc.sharedState— sameSharedState<T>handle — with the page script as authority: automatic replay to late-joining panels, syncId-deduplicated patches.InPageChannelErrors (timeoutwith status hints,closed,not-serializablenaming the offending path,not-cloneable,invalid-args,state-uninitialized); an explicitconnecting/connected/closedstatus machine with call queueing + bounded event buffering;whenConnected(ms)for "page script not loaded" fallback UIs; automatic recovery via portcloseevents + heartbeat.BroadcastChannel) slots in behind the same API;{ transport: MessagePort }bypasses discovery for tests and custom topologies.a11y migration
shared/protocol.tsis now a pure contract (A11yChannelProtocol+ constants); the page script registers channel functions and ownsA11yStateas channel shared state (scanningfolded in); the Solid panel mirrors it — the startup-race and replay code is gone, and the two-tabs cross-talk bug with it. Verified end-to-end in a browser against the demo (handshake, state replay, pinning).Docs
New guide (
/guide/in-page-channel) including the error/fallback surface and an in-page-channel-vs-RPC table; events reference and terms page updated in lockstep withDEVFRAME_EVENTS; devframe skill updated.Gate:
pnpm lint && pnpm knip && pnpm test && pnpm typecheck && pnpm buildall green (API snapshots regenerated).This PR was created with the help of an agent.