Repository navigation
fix(events): use the WebSocket global and drop the ws dependency - #4799
georgeglarson wants to merge 3 commits into
Conversation
The package is emitted as ESM, and Node 22 provides a standards-compatible
globalThis.WebSocket. The conversation and bash event clients only checked
window.WebSocket, then attempted require('ws'), which is unavailable in
ESM — so starting either client in a Node ESM process reported "WebSocket
implementation not available" even though the global exists.
Every runtime this package supports supplies the global (browsers, and
Node.js since 22.4), so read globalThis.WebSocket directly and drop the ws
dependency. The "no implementation" condition stays deferred to connect()
time and is still surfaced through the existing onError callback, so
importing the package barrel never throws.
Co-authored-by: openhands <openhands@all-hands.dev>
|
🚦 CI is currently failing on this PR's latest commit. Please fix the failing checks before OpenHands reviews it - this is re-checked automatically once you push a new commit. (A maintainer can also request This is an automated check - no AI was used to generate this comment. |
all-hands-bot
left a comment
There was a problem hiding this comment.
This review was posted by an AI agent (OpenHands).
Review summary
The runtime-detection fix itself is correct and the reported bug is real: main still has the window.WebSocket / require('ws') block in both events/websocket-client.ts and events/bash-websocket-client.ts, which fails in Node ESM even when globalThis.WebSocket exists. Reading globalThis.WebSocket is equivalent to window.WebSocket in browsers and to the Node 22.4+ global in Node, so the change removes the crash and correctly keeps the "no implementation" condition deferred to connect() time. Locally I confirmed npm ci, npm run build (exit 0), npm run lint (0 errors), and npx jest src/__tests__/package-import.test.ts (8/8 pass) on this head.
However, the PR is not currently mergeable and its new tests will not run after a rebase:
-
Merge conflict with
main. GitHub reportsmergeable_state: "dirty"for headb39586a; a local merge-tree shows conflicts inclients/typescript/package-lock.jsonandclients/typescript/src/__tests__/package-import.test.ts. This branch is stale —mainhas moved on (the TS client Jest→Vitest migration #5075, dependency/image bumps, and a large Python-side delta) since this head's base. A rebase is required before merge. -
Tests target Jest, but
mainnow uses Vitest. The rewrittenpackage-import.test.tsusesjest.isolateModules/jest.resetModules, and the head still shipsjest.config.cjs. On currentmainthose Jest config files are gone andnpm testrunsvitest. After rebasing, these new cases must be ported to Vitest (vi.resetModules, dynamicimport,vi.spyOn(Module.prototype, 'require')style mocking asmainuses) or they will simply not execute. This is the same file that is conflicted, so the port should happen as part of resolving it. -
Stale
wsguidance in the deferred error. Withwsremoved fromdependenciesand the fallback deleted, theconnect()error still tells users toInstall thewspackage(events/websocket-client.tsline 83 andevents/bash-websocket-client.tsline 70 — both unchanged by this diff, so not inline-anchored). Installingwscan no longer fix anything; the message should point at the runtime requirement instead. Non-blocking, but the PR leaves the guidance inconsistent with the new behavior. -
Informational (acknowledged in the PR body): removing the
wsfallback is a silent behavior change for consumers on Node < 22.4, andpackage.jsondeclares noenginesbound while CI only exercises Node 22.12 / 24.x. Worth making the requirement explicit, but not a blocker given the mature-Node posture.
Nothing here changes the correctness of the fix, but the branch needs a rebase and a test-runner port before it can land.
🔄 CHANGES REQUESTED
| // undefined rather than throwing, and the "no implementation available" | ||
| // condition stays deferred to connect() time, where it is surfaced through | ||
| // the existing onError callback channel. | ||
| const WebSocketImpl: typeof WebSocket | undefined = globalThis.WebSocket; |
There was a problem hiding this comment.
Dropping the ws fallback is a silent breaking change for Node < 22.4: package.json declares no engines bound and CI only exercises Node 22.12 / 24.x, so older-Node consumers that relied on the require('ws') path now fall through to the onError "WebSocket implementation not available" at start(). Consider declaring engines.node (e.g. >=22.4) so the new runtime floor is explicit, or retain the fallback if older Node must stay supported. Same applies to events/bash-websocket-client.ts.
| describe('package imports do not crash when `ws` is unavailable', () => { | ||
| afterEach(() => { | ||
| globalThis.WebSocket = originalWebSocket; | ||
| jest.resetModules(); |
There was a problem hiding this comment.
This rewritten suite is Jest-based (jest.isolateModules / jest.resetModules), but main has migrated the TS client to Vitest (#5075): jest.config.cjs no longer exists and npm test runs vitest. After rebasing, these cases will not run as written — and this file is one of the two conflicted paths against main. Please port them to Vitest (vi.resetModules + dynamic import / vi.spyOn-based require interception, matching the current main version) while resolving the conflict.
HUMAN:
Human verified, Screenshot attached.
AGENT:
Ported from OpenHands/typescript-client#370 (closed unmerged when that repo was archived and the client moved here; the moved files are byte-identical to the pre-fix versions, so the bug crossed repos untouched). Verified end-to-end from
clients/typescript:mainfirst:npm ci && npm run build, then the repro from the linked issue —onErrorreceivesWebSocket implementation not availableon Node 22.22.2 despiteglobalThis.WebSocketexisting.npx jest src/__tests__/package-import.test.ts→ 8 passed, including new cases asserting each client opens the exact expected URL against a fakeglobalThis.WebSocket.npm test→ 311 passed, 18 suites.npm run lint→ 0 errors (and two fewer pre-existinganywarnings in these files).npm run format:check→ clean.npm run build→ clean.Why
The package is emitted as ESM, and Node 22 provides a standards-compatible
globalThis.WebSocket. The conversation and bash event clients only checkedwindow.WebSocket, then attemptedrequire('ws')— which is unavailable in ESM — so starting either client in a Node ESM process reportedWebSocket implementation not availableeven though the global exists. Full repro in the linked issue.Summary
globalThis.WebSocketdirectly in both event clients and drop thewsdependency (every supported runtime supplies the global: browsers, and Node.js since 22.4).connect()time and surfaced via the existingonErrorcallback, so importing the package barrel still never throws.ws, and add coverage pinning the URL each client opens.Issue Number
Fixes #4846.
How to Test
On
mainthis printsONERROR: ... WebSocket implementation not available; on this branch it does not. Thennpm testandnpx jest src/__tests__/package-import.test.ts.Video/Screenshots
Design Doc
N/A — small runtime-detection fix.
Type
Notes
The same change was previously reviewed as OpenHands/typescript-client#370 and went unmerged only because that repository was archived on 2026-08-30 when the client moved into this repo (OpenHands/typescript-client#371). Consumers on Node < 22.4 would lose the
wsfallback, but the package already requires Node 22-era tooling throughout its CI and the global has been unflagged since Node 22.4.Jev-Fast-Audit
⚡ Jev fast audit · estimates · 0.53s · commit b39586a⚠️ reduced context — partial coverage; 8/8 hunks, 5/5 files (missing context: 4).
Strongest signal: No primary concern selected.
Evidence: No primary concern to locate.
Coverage:
All estimates and evidence