Skip to content

Improve singleton connection handling and prevent state recursion#69

Merged
MarcosBrendonDePaula merged 1 commit intomainfrom
claude/review-latest-pr-gxUMG
Feb 27, 2026
Merged

Improve singleton connection handling and prevent state recursion#69
MarcosBrendonDePaula merged 1 commit intomainfrom
claude/review-latest-pr-gxUMG

Conversation

@MarcosBrendonDePaula
Copy link
Copy Markdown
Collaborator

Summary

This PR improves the reliability and safety of LiveComponent singleton broadcasting and state management by fixing connection tracking, preventing infinite recursion in state change hooks, and enhancing error handling.

Key Changes

Singleton Connection Management

  • Unique connection IDs: Enhanced WebSocket connection ID generation from ws-${Date.now()} to ws-${Date.now()}-${Math.random().toString(36).slice(2, 8)} to prevent collisions when multiple connections are created in rapid succession
  • Dead connection cleanup: Added automatic detection and removal of dead connections when broadcasting to singleton instances, preventing memory leaks from failed sends
  • Refactored singleton cleanup: Extracted removeSingletonConnection() helper method to centralize singleton lifecycle logic across unmount and disconnect handlers, reducing code duplication

Emit Override Mechanism

  • Symbol-based access: Changed from method-based _setEmitOverride() to Symbol-keyed property EMIT_OVERRIDE_KEY to prevent accidental userland access while maintaining internal functionality
  • Updated tests: Modified test suite to use the new Symbol-based approach consistently

State Change Recursion Guard

  • Prevent infinite loops: Added _inStateChange guard flag to prevent infinite recursion when onStateChange() hook modifies state or calls setState()
  • Applied to both paths: Guard implemented in both the state proxy setter and the setState() method
  • Added test coverage: New tests verify recursion prevention works correctly with both direct property assignment and setState() calls

Error Handling

  • Mount failure notifications: Added error emission to client when onMount() fails during both initial mount and rehydration, improving debugging visibility
  • Consistent error messages: Distinguishes between initial mount and rehydration failures in error messages

Client-Side Optimization

  • Favicon caching: Added faviconUrlCache to prevent recreating blob URLs on every navigation, reducing memory churn from repeated URL.createObjectURL() calls
  • Removed cleanup: Removed URL.revokeObjectURL() from effect cleanup since cached URLs are reused across navigations

Security

  • Blocked internal methods: Updated publicActions blocklist to prevent client-side calls to _inStateChange and other internal methods

https://claude.ai/code/session_01898rxEV1yuwvJZCHnYjjUs

…n safety

- Add recursion guard for onStateChange to prevent infinite loops when
  hooks modify state (proxy and setState paths)
- Replace public _setEmitOverride with Symbol-keyed EMIT_OVERRIDE_KEY
  to prevent accidental access from userland code
- Fix silent catch in singleton broadcast — now logs and removes dead
  connections instead of swallowing errors
- Make connectionId collision-safe with random suffix fallback
- Notify client on onMount failure via ERROR emit instead of silent log
- Cache favicon blob URLs by hue to avoid recreating blobs per navigation
- Extract removeSingletonConnection() to deduplicate logic between
  unmountComponent and cleanupConnection
- Add 2 new tests for recursion guard (proxy + setState paths)

https://claude.ai/code/session_01898rxEV1yuwvJZCHnYjjUs
@MarcosBrendonDePaula MarcosBrendonDePaula merged commit fb866f7 into main Feb 27, 2026
11 checks passed
MarcosBrendonDePaula pushed a commit that referenced this pull request Feb 27, 2026
- onAction cancellation no longer emits ERROR to client (info leak)
- setState skips emit/hooks when values are unchanged (parity with proxy)
- Silent catch {} replaced with console.error in onStateChange, onRoomJoin, onRoomLeave
- Singleton broadcast now includes userId and room metadata
- Add onClientJoin/onClientLeave lifecycle hooks for singletons
- Singleton onDisconnect only fires when last client leaves
- New hooks added to BLOCKED_ACTIONS for security
- 19 regression tests covering all fixed bugs

https://claude.ai/code/session_01JEtihEZe9cThDAadXAp3Rp
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