Skip to content

feat(core): Add roWebSocket component with full Node and browser support - #1234

Merged
lvcabral merged 7 commits into
masterfrom
feat/rowebsocket-os16
Sep 14, 2026
Merged

lvcabral merged 7 commits into
masterfrom
feat/rowebsocket-os16

Conversation

@lvcabral

Copy link
Copy Markdown
Owner

Summary

  • Implements the new roWebSocket BrightScript component (Roku OS 16.0) — ifWebSocket, ifHttpAgent, and all 9 roWebSocketEvent types (Opened/Closed/Error/MsgSent/Text/DataReceived/Ping/PongReceived/Timer) matching the documented GetInfo() field tables.
  • Full parity on both platforms, unlike roStreamSocket/roDataGramSocket which stay browser-mocked:
    • Node/CLI: WebSocketBridge.ts, a per-socket helper process (mirroring StreamBridge.ts/DatagramBridge.ts) backed by the ws package — full protocol fidelity including real Ping/Pong control frames, custom headers, Basic-Auth, and TLS peer/host verification.
    • Browser: the real WebSocket lives on the main thread (src/api/webSocketHost.ts, shared by the app worker and every SceneGraph Task worker) since the interpreter's own worker thread busy-spins inside Wait() and can never run its own WebSocket callbacks. A new SharedEventQueue (src/core/SharedEventQueue.ts) — a lock-free-ish, lossless, ordered SharedArrayBuffer event log — carries events back to the worker. Documented browser platform gaps (no script control over handshake headers/auth/cert verification, no script-visible Ping/Pong) are noted in docs/limitations.md.
  • Socket lifecycle is cleaned up on app/Task termination via disposeSocketsForRealm, keyed by owning worker thread id, wired into src/api/index.ts and src/api/task.ts.

Test plan

  • npm run lint / npm run prettier clean
  • npm run build (browser + node + scenegraph) succeeds
  • Full test suite passes (229 files / ~3000 tests), including new coverage:
    • test/brsTypes/components/RoWebSocket.test.js — real loopback I/O against a separate child-process server (open/send/receive, binary messages, synchronous Open(wait_time), reconnect on the same object, timers, transport-start failure signaling)
    • test/api/webSocketHost.test.js — browser main-thread bridge logic (Node's native WebSocket standing in for the browser's), including per-realm socket disposal
    • test/core/SharedEventQueue.test.js — including a genuine cross-worker_threads delivery test

Co-Authored-By: Claude Sonnet 5 noreply@anthropic.com

🤖 Generated with Claude Code

https://claude.ai/code/session_01FBGL5HoJjAyCwbqpQQ9t4a

…ort (Roku OS 16.0)

Implements the new WebSocket BrightScript component (ifWebSocket/roWebSocketEvent) with
real network I/O on both platforms: a per-socket helper process backed by the `ws`
package on Node/CLI (full protocol fidelity, including Ping/Pong control frames, custom
headers, and Basic-Auth), and a main-thread-owned real WebSocket bridged to the
interpreter worker via a new SharedArrayBuffer-backed event queue on the browser build.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FBGL5HoJjAyCwbqpQQ9t4a
Comment thread src/core/brsTypes/interfaces/IfSocket.ts Fixed
@lvcabral lvcabral changed the title feat(core): Add roWebSocket component with full Node and browser support (Roku OS 16.0) feat(core): Add roWebSocket component with full Node and browser support Sep 13, 2026
lvcabral and others added 5 commits September 13, 2026 09:42
- Mark roWebSocket's port field readonly (only ever set externally via ifSetMessagePort)
- Suppress the Math.random() security-rating hit on generateUniqueId(): a non-cryptographic
  socket correlation id, same reasoning already reviewed as safe for the identical pattern
  in RoStreamSocket.ts/RoDataGramSocket.ts
- Add an explicit comment to WebSocketBrowserBridge's intentionally empty setAutoPingReply
  no-op

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FBGL5HoJjAyCwbqpQQ9t4a
…ER ifdef

Matches the existing convention (RoURLTransfer.ts's XMLHttpRequest import) instead of a
runtime typeof importScripts check. Each platform's bundle now excludes the other
platform's transport entirely (verified: brs-ws-queue is absent from the browser
bundle, webSocketCommand is absent from the node bundle), rather than relying on
webpack's resolve.fallback to stub out the unused one at runtime.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FBGL5HoJjAyCwbqpQQ9t4a
…nside one function

- WebSocketBrowserBridge.ts -> WebSocketBridge.ts (the browser transport)
- WebSocketBridge.ts (Node helper-process transport) -> WebSocketNodeBridge.ts
- Fix a typo introduced during the rename (WebSockeBridge -> WebSocketBridge) and update
  every doc-comment/prose reference to the new names across RoWebSocket.ts,
  WebSocketTransport.ts, webSocketHost.ts, and the test files

createTransport() keeps both platform branches' `return` inside one function body rather
than two separate per-branch function declarations: splitting it breaks the Node
package's cli/ecp webpack bundles with a real TS2393 "Duplicate function implementation"
(ts-loader sees both declarations in that configuration, even though only one survives
ifdef-loader's stripping) - a real build failure outranks the resulting SonarCloud
"unreachable code" false positive, which is suppressed with a NOSONAR comment instead.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FBGL5HoJjAyCwbqpQQ9t4a
SonarCloud flags the second return (unreachable from a plain parser's view after the
first branch's unconditional return), not the first one the comment was on.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FBGL5HoJjAyCwbqpQQ9t4a
@lvcabral lvcabral changed the title feat(core): Add roWebSocket component with full Node and browser support feat(core): Add roWebSocket component with full Node and browser support Sep 13, 2026
Sideloadable channel exercising every ifWebSocket method against a public
echo server, runnable identically on real Roku, the CLI, and the browser
simulator. Real-hardware testing found two BrightScript pitfalls along the
way: a function-name field observer runs on the thread that wrote the
field, not the thread that registered it (fixed by observing with a
roMessagePort instead), and a user sub/function named after a
BrightScript global (log vs Log()) is silently never called.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FBGL5HoJjAyCwbqpQQ9t4a
@sonarqubecloud

Copy link
Copy Markdown

@lvcabral
lvcabral merged commit d1a5bda into master Sep 14, 2026
3 checks passed
@lvcabral
lvcabral deleted the feat/rowebsocket-os16 branch September 14, 2026 05:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants