Skip to content

fix: stable WebSocket object identity across open/message/close hooks#1737

Open
MarcelOlsen wants to merge 2 commits intoelysiajs:mainfrom
MarcelOlsen:cache-websocket
Open

fix: stable WebSocket object identity across open/message/close hooks#1737
MarcelOlsen wants to merge 2 commits intoelysiajs:mainfrom
MarcelOlsen:cache-websocket

Conversation

@MarcelOlsen
Copy link
Contributor

@MarcelOlsen MarcelOlsen commented Feb 13, 2026

Summary

Fixes WebSocket (ElysiaWS) object identity not being stable across open, message, drain, and close lifecycle hooks.

Previously, every handler created a new ElysiaWS(ws, context) wrapping the same raw
Bun ServerWebSocket, meaning ws in open !== ws in message !== ws in close. This broke identity-based patterns like storing sockets in a Set/Map or attaching per-socket state via properties.

Changes

  • src/adapter/bun/index.ts: Cache the ElysiaWS wrapper on ws.data._elysiaWS in the open handler and reuse it in message, drain, and close. Each handler falls back to creating a new instance (??=) as a defensive measure.
  • test/ws/connection.test.ts: Added test asserting strict reference equality (===) of the ws object across open, message, and close.

Test plan

  • New test: should maintain ws object identity across open/message/close
  • All 33 existing WebSocket tests pass (bun test test/ws/)

This resolves #1716

Summary by CodeRabbit

  • Chores
    • Optimized WebSocket handling for improved connection stability, resource usage, and consistent per-connection state.
  • Tests
    • Added tests verifying socket identity across events, tracking of active sockets, and per-socket state propagation and cleanup.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 13, 2026

Walkthrough

The Bun adapter's WebSocket handling now creates and stores a single ElysiaWS wrapper per native socket on ws.data._elysiaWS, reusing it across open, message, drain, and close events; tests were added to verify socket identity and per-socket state persistence across lifecycle events.

Changes

Cohort / File(s) Summary
WebSocket Adapter Implementation
src/adapter/bun/index.ts
Create and reuse a single ElysiaWS wrapper stored on ws.data._elysiaWS; pass that instance to all WS event handlers; set elysiaWS.body on incoming messages; lazy initialization to avoid recreating wrappers per event.
WebSocket Connection Tests
test/ws/connection.test.ts
Add tests asserting the same ws object is passed to open/message/close handlers, that sockets can be tracked in a Set across open/close, and that per-socket state in a Map persists across handlers and cleans up on close.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant BunAdapter as Bun WS
  participant ElysiaWS as Wrapper

  Client->>BunAdapter: connect (open)
  BunAdapter->>ElysiaWS: create if missing -> store on ws.data._elysiaWS
  BunAdapter->>ElysiaWS: call open(handler) with same wrapper

  Client->>BunAdapter: send message
  BunAdapter->>ElysiaWS: set body, call message(handler) with same wrapper

  Client->>BunAdapter: close
  BunAdapter->>ElysiaWS: call close(handler) with same wrapper
  BunAdapter-->>ElysiaWS: optional cleanup (remove from ws.data)
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related issues

Poem

🐰
A single wrap for every hop and close,
One faithful friend wherever data flows.
Open, message, close — the same small stew,
I nibble bugs and hop; the socket's true. 🥕✨

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main fix: ensuring stable WebSocket object identity across lifecycle hooks, which is the central change in this PR.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Merge Conflict Detection ✅ Passed ✅ No merge conflicts detected when merging into main
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@MarcelOlsen
Copy link
Contributor Author

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 13, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@src/adapter/bun/index.ts`:
- Around line 560-566: The TS2345 arises because storing new ElysiaWS(ws,
context as any) in elysiaWS loses the inline any-cast used previously when
calling options.open; to fix, keep the local elysiaWS but cast it back to any
when invoking options.open (e.g., pass options.open?.(elysiaWS as any)), or
alternatively cast at declaration (const elysiaWS = new ElysiaWS(ws, context as
any) as any); update the call site in the handleResponse invocation so
options.open receives the same any-typed value; relevant symbols: ElysiaWS,
elysiaWS, ws, options.open, handleResponse.
🧹 Nitpick comments (1)
src/adapter/bun/index.ts (1)

560-561: The _elysiaWS property on ws.data is untyped.

ws.data._elysiaWS is set dynamically without any type declaration. This works because ws is typed as ServerWebSocket<any>, but it would be cleaner to add _elysiaWS to the data shape if a type exists for it. Not blocking, but worth noting for future maintainability.

@pkg-pr-new
Copy link

pkg-pr-new bot commented Feb 13, 2026

Open in StackBlitz

npm i https://pkg.pr.new/elysiajs/elysia@1737

commit: c9d6c09

@MarcelOlsen
Copy link
Contributor Author

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 13, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@MarcelOlsen MarcelOlsen marked this pull request as ready for review February 13, 2026 22:22
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.

Websockets object identity is not stable across open/message/close hooks

1 participant