feat: support portable connections for external viewers - #150
Conversation
✅ Deploy Preview for devfra ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
There was a problem hiding this comment.
Pull request overview
Adds a portable, serializable DevframeConnection model to decouple connection preparation from RPC client creation, enabling external viewers (e.g. browser extensions / cross-realm contexts) to carry full connection metadata (including meta source URL and auth token) across origins and frames.
Changes:
- Introduces
DevframeConnection,setupDevframeConnection(), andgetDevframeConnection()and wires them intogetDevframeRpcClient()/connectDevframe(). - Exposes the active connection via
rpc.connectionand synchronizes newly issued auth tokens back into that connection. - Updates WebSocket URL resolution to anchor legacy numeric-port/host inference to the metadata origin (not the viewer origin), and documents the new flow.
Reviewed changes
Copilot reviewed 10 out of 12 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/snapshots/tsnapi/devframe/constants.snapshot.js | Updates public API snapshot for newly exported constant. |
| tests/snapshots/tsnapi/devframe/constants.snapshot.d.ts | Updates public API types snapshot for newly exported constant. |
| tests/snapshots/tsnapi/devframe/client.snapshot.js | Updates public API snapshot for newly exported connection helpers. |
| tests/snapshots/tsnapi/devframe/client.snapshot.d.ts | Updates public API types snapshot to include DevframeConnection, new options shape, and rpc.connection. |
| packages/devframe/src/constants.ts | Adds DEVFRAME_CONNECTION_KEY constant for cross-realm connection sharing. |
| packages/devframe/src/client/rpc.ts | Refactors client creation to use setupDevframeConnection() and exposes rpc.connection with token synchronization. |
| packages/devframe/src/client/rpc-ws.ts | Anchors WS URL resolution (protocol/hostname) to the connection metadata base URL for external viewers. |
| packages/devframe/src/client/rpc-ws.test.ts | Adds coverage for extension-style locations and metadata-anchored numeric port handling. |
| packages/devframe/src/client/connection.ts | Implements DevframeConnection, getDevframeConnection(), and setupDevframeConnection() including meta-base URL tracking. |
| packages/devframe/src/client/connection.test.ts | Adds tests for connection preparation, RPC client exposure, token precedence, and fallback base behavior. |
| packages/devframe/src/client/connection-storage.ts | Centralizes cross-realm/global/localStorage storage and synchronization for connection + token. |
| docs/guide/client.md | Documents external viewer connection sharing, rpc.connection, and the new connection option. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
813bb48 to
1df20d0
Compare
1df20d0 to
526e06a
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 11 out of 13 changed files in this pull request and generated no new comments.
Suppressed comments (2)
docs/guide/client.md:64
- The text implies
DEVFRAME_CONNECTION_KEYis directly readable, but it’s just the string key. To make cross-realm usage unambiguous, document that the prepared connection is stored onglobalThis[DEVFRAME_CONNECTION_KEY](and that this is what external viewers should read).
`getDevframeConnection()` returns the prepared connection in the current
window or an accessible parent window. Cross-realm viewers can read the
serializable value through `DEVFRAME_CONNECTION_KEY` from
`devframe/constants`.
docs/guide/client.md:83
- The
authTokenoption description is inaccurate: the code defaults to the locally persisted token (and may also use a token embedded in connection metadata), not a “human-readable id”.
| `authToken` | Override the auth token. Defaults to a locally-persisted human-readable id. |
8b4f51f to
526e06a
Compare
Background
I am currently refactoring the Vite DevTools Web Extension so that it can reuse the same DevTools UI and Devframe integrations as the embedded and standalone clients.
The extension panel runs under a
chrome-extension://origin, while the inspected application, Vite development server, and individual DevTools iframe applications run under the development server origin.This exposed a limitation in the existing connection model: it works well when the client can access a same-origin parent window, but an external viewer cannot reliably carry the complete connection into its own context or into cross-origin child frames.
Summary
This PR adds a portable, serializable Devframe connection model that can be passed between JavaScript realms and external viewers.
It introduces:
setupDevframeConnection()to prepare connection metadata independently from RPC client creationgetDevframeConnection()to retrieve a previously prepared connectionconnectionoption forconnectDevframe()/getDevframeRpcClient()rpc.connection, exposing the complete active connectionDEVFRAME_CONNECTION_KEYas the shared cross-realm keyrpc.connectionMotivation
The Web Extension should behave as another Devframe viewer, rather than implementing a separate connection and authentication system.
However, the previous connection setup implicitly depended on the current page:
In practice, this caused Web Extension code to reconstruct connection details, interact with internal globals, and resolve WebSocket addresses itself. It also meant each child DevTools iframe could become unauthorized even when the extension host was already trusted.
Approach
A
DevframeConnectionnow represents everything required to establish a client connection:metaBaseUrlrecords the absolute URL from which__connection.jsonwas loaded. Relative WebSocket paths and sidecar ports can therefore be resolved against the Devframe server instead of the external viewer’s origin.A host can prepare the connection once:
It can then pass that serializable value to another viewer:
The resulting RPC client also exposes the active connection:
When authorization returns a new token,
rpc.connectionis updated so the host can pass the trusted connection to child viewers without requiring another authorization step.For Vite DevTools, this allows the extension panel to obtain the inspected application’s connection, connect as a standalone viewer, and pass that connection to DevTools iframe applications through the existing remote connection mechanism.
Compatibility
This is additive and does not change the existing connection flow.
Existing calls continue to work:
The existing metadata and authentication globals remain synchronized for compatibility with current integrations. Explicit
connectionMeta,baseURL, andauthTokenoptions are still supported.Security
The connection may contain an authentication token, so it is intentionally represented as an explicit value. The consuming host decides which viewer receives it.
This PR does not automatically expose the connection to arbitrary cross-origin frames. In the Web Extension integration, Vite DevTools only forwards it to child applications served by the same Devframe server origin.