feat: support Expo DevTools plugin context.upgrade(hooks) WebSocket upgrades - #26
Draft
krystofwoldrich wants to merge 4 commits into
Draft
feat: support Expo DevTools plugin context.upgrade(hooks) WebSocket upgrades#26krystofwoldrich wants to merge 4 commits into
context.upgrade(hooks) WebSocket upgrades#26krystofwoldrich wants to merge 4 commits into
Conversation
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This makes vendoring the license asier for expo-device-hub
## Summary - replace the GitHub Actions simulator, lint, and typecheck workflows with EAS Workflow custom jobs - install dependencies with `eas/install_node_modules` - start the iOS Simulator with `eas/start_ios_simulator` - preserve serialized tests and the one-time simulator reboot retry ## Why The repository's CI checks should run on EAS infrastructure. This also lets the simulator tests use the built-in EAS simulator lifecycle instead of maintaining runner-specific simulator discovery logic. ## Impact Pull requests run lint and typecheck on parallel EAS Linux workers. Changes under `packages/serve-sim/**` or to the simulator workflow also run the build and simulator-backed test suite on an EAS macOS worker. Superseded runs on the same branch remain canceled. EAS project: https://expo.dev/accounts/expo/projects/serve-sim ## Validation - validated both EAS workflow files against the current EAS Workflows JSON schema - `bun run lint` - `bun run typecheck` - EAS `Checks / Lint` and `Checks / Typecheck` jobs - `git diff --check`
…pgrades Expo CLI's DevTools plugin request handlers receive a per-request context whose upgrade(hooks) commits WebSocket handshakes on dynamic paths (expo/expo@a6ca932ee5a). simMiddleware now accepts that context as an optional second argument, so all three of its WebSocket routes work when mounted as a DevTools plugin — /exec-ws plus the dynamic /helper/<device>/ws and /devtools/page/<id> proxy paths that static exact-path webSocketHandlers mounts can't express. New src/upgrade-context.ts mirrors the CLI contract structurally (no @expo/cli dependency) and adapts hook-based peers onto the existing socket consumers: ExecWebSocket for the exec channel, HidSocket for HID input (attached from onopen, once the peer can take the seeded config frame), and a frame-type-preserving relay to the inspect-webkit DevTools upstream. The host's upgrade() throws for plain HTTP requests, so the middleware gates on the Upgrade header before calling it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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.
Why
Expo CLI's DevTools plugin request handlers now receive a per-request
contextwhoseupgrade(hooks)commits WebSocket handshakes on dynamic paths (expo/expo@a6ca932ee5a). Until now, only serve-sim's static/exec-wsroute could work under Expo CLI (via exact-pathwebSocketHandlersmounts in expo-device-hub); the dynamic/helper/<device>/ws(HID input) and/devtools/page/<id>(WebKit DevTools proxy) routes required a host that forwards raw Nodeupgradeevents, which Expo CLI doesn't do.What
simMiddleware's fetch handler now takes an optional secondcontextargument. For WebSocket Upgrade requests it routes through a hook-based twin ofhandleUpgradeand commits the handshake by returningcontext.upgrade(hooks):src/upgrade-context.ts— structural mirror of the CLI contract (no@expo/clidependency), with three adapters onto the existing socket consumers:execSocketFromUpgrade→ExecWebSocketfor the exec/control channel (handles pre-openclose(), e.g. the origin check, by closing the peer once it appears)hidSocketFromUpgrade→HidSocket, attached to theDeviceSessionfromonopenso the synchronously-seeded config frame has a live peerbridgeUpgradeToWebSocket→ relay to the loopback-only inspect-webkit upstream, queueing frames until the upstream opens and preserving text/binary frame types across the hopsrc/middleware.ts— routing: devtools proxy → helper HID (plainResponse404/400 rejects the handshake) → exec-ws →undefineddeclines. The host'supgrade()exists on plain HTTP requests but throws, so the middleware gates on theUpgradeheader before calling it.src/runtime-utils.ts—WebMiddlewaresignature carries the optional context (backwards compatible; existing hosts pass one argument).Existing transports are untouched: raw-socket
handleUpgrade(standalone serve-sim) andhandleWebSocket(staticwebSocketHandlersmounts) work as before; the protocol logic stays single-sourced inexec-ws.ts/device-session.ts.Follow-up (separate repo)
expo-device-hub's server entry still calls
middleware(request)with one argument; forwarding the context (handler(request, context)→middleware(rewritten, context)) lights this up and makesproxyHelpers: trueviable for single-port remote viewing.Test plan
src/__tests__/upgrade-context.test.ts(8 tests) drives the middleware with a fake context mirroring the CLI semantics: exec auth + command round-trip, bad-token close, cross-origin close-before-open, unknown-path decline without committing, helper-404 reject, plain-HTTP passthrough (assertingupgrade()is never called — it throws in the CLI for HTTP), HID adapter framing both directions, and a live DevTools bridge against a Bun WebSocket upstream asserting queued frames survive and text frames stay text.bun run typecheck,bun run lint, and the fullbun testsuite (315 pass) are green.🤖 Generated with Claude Code