Repository navigation
support stagehand in backend functions - #664
Merged
Merged
Conversation
Contributor
🚀 Package Preview Available!Install this PR's preview build with npm: npm i @base44-preview/cli@0.1.32-pr.664.f359bf9Prefer not to change any import paths? Install using npm alias so your code still imports npm i "base44@npm:@base44-preview/cli@0.1.32-pr.664.f359bf9"Or add it to your {
"dependencies": {
"base44": "npm:@base44-preview/cli@0.1.32-pr.664.f359bf9"
}
}
Preview published to npm registry — try new features instantly! |
netanelgilad
approved these changes
Oct 15, 2026
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.
Note
Description
Closes two workerd compatibility gaps in
@base44/functions-compilerthat prevented Node-targeted npm packages — Stagehand in particular — from running inside Base44 backend functions. Barews/npm:wsnow resolves to aws-shaped client built on workerd's nativeWebSocket(with afetch-upgrade path when the caller passes custom handshake headers), andAsyncLocalStorage.prototype.enterWith()is replaced with a no-op when workerd's throwing stub is detected. Both fixes apply to every compiled function and actor, sonpm:@browserbasehq/stagehandcan be imported, constructed, and driven from a backend function.Related Issue
None
Type of Change
Changes Made
src/esbuild/ws-native-virtual.ts— resolves barewsandnpm:ws[@version](from any namespace: user files and dependencies) to a single virtual CommonJS module implementing aws-compatible client over workerd's globalWebSocket:on/once/off/addListener/removeListener/removeAllListeners/emit,send,close,terminate,readyState,url,protocol, and theCONNECTING/OPEN/CLOSING/CLOSEDconstants. Messages are delivered asBufferwith theisBinaryflag, matchingws's event signature.ws/lib/*subpaths are deliberately left to resolve normally.WebSocketconstructor cannot send request headers, so whenoptions.headerscarries anything beyondUser-Agent(Stagehand'scdpHeaders, e.g. a remote browser's auth token) the socket is opened via afetchupgrade instead, which carries the headers to the server and follows the function's egress routing. A loneUser-Agentstays on the native path.wswithin what workerd allows — close codes pass through unchanged (invalid ones throw as in Node),close()during an in-flight upgrade is deferred until the socket lands, andterminate()emits a single1006closeimmediately (workerd has no abrupt close) while suppressing the server's close reply and anyopenfrom a pending upgrade. Repeatedterminate()calls andterminate()after close are no-ops.WebSocketServerthrows a clear error explaining that a function can open WebSocket connections but cannot listen for them.deno-bundle.ts, ahead ofnodeBuiltinRequirePlugin()and the Deno resolver, so it claims thewsimport before the package'sbrowser-condition stub (which throws on use) or itsnode:net-dependent Node build can be picked.src/shim/entry.ts— probesAsyncLocalStorage#enterWith()on a throwaway instance and, only if it throws, patches the prototype with a no-op, so a future native workerd implementation is left in place. Libraries that call it also pass their context explicitly and re-enter it viarun().packages/functions-compiler/README.mddocumenting both gaps and the adapter's limits (covers what Stagehand uses, not the wholewsAPI; other connection options ignored; no server support).Testing
npm test)Tests added in
packages/functions-compiler/test:workerd-runtime.e2e.test.ts— a parameterized end-to-end case that stands up a Miniflare echo server, bundles a function importingnpm:ws@8.18.0, and asserts the round-trip plus a cleanclose(1000)over both the native-constructor path and the header-carryingfetch-upgrade path (verifying theAuthorizationheader reaches the server). Aws: closingsuite covers close-code pass-through and rejection,terminate()emitting one1006,terminate()during an upgrade never firingopen, and repeated/post-closeterminate()being a no-op. A final case assertsAsyncLocalStorage.enterWith()is a no-op rather than a throw.package-matrix-data.ts— adds@browserbasehq/stagehand@3.7.3as anexpected: "pass"case in the package matrix, exercising both fixes (construction callsenterWith(), loading importsws).Checklist
docs/(AGENTS.md) if I made architectural changesAdditional Notes
The
wsadapter is client-only and scoped to what Stagehand exercises, not the fullwssurface. Connection options other thanprotocolsandheaders(agents, TLS settings, per-message deflate) are silently ignored, since neither workerd's nativeWebSocketnor thefetchupgrade accepts them. The package-matrix and workerd e2e suites pull real packages from npm and run Miniflare, so they need network access to execute.🤖 Generated by Claude | 2026-10-15 09:05 UTC | f359bf9