fix(hmr): reconnect WebSocket after sleep/wake (#91416)
## Summary
When a computer sleeps and wakes, the `/_next/hmr` WebSocket connection
can silently become stale. The current code relies entirely on
`onclose`/`onerror` events, but:
- `onclose` may fire late or not at all after sleep/wake — the TCP
connection is dead but the browser hasn't detected it yet
- `setTimeout` timers freeze during sleep, so queued retries can fire at
once after wake, rapidly exhausting the 12-reconnect limit and
triggering a spurious full-page reload
This PR adds `visibilitychange` and `online` event listeners to both the
App Router and Pages Router HMR WebSocket clients. When the page becomes
visible again or the browser regains connectivity, the WebSocket state
is checked and a reconnection is triggered immediately if it's no longer
open. The reconnection counter is reset since sleep-induced
disconnections are not indicative of the dev server being down.
## How it works
- **`visibilitychange`**: When the document becomes `visible`, check
`webSocket.readyState`. If not `OPEN`, reset the reconnection counter,
clear any pending retry timer, and call `init()` to reconnect
immediately.
- **`online`**: Same logic — when the browser fires the `online` event
after regaining network connectivity.
This matches the approach recommended by the Socket.IO team and other
WebSocket libraries for handling sleep/wake reconnection.
## Test plan
- Manual: open a Next.js dev app, put the computer to sleep (or simulate
by disabling/re-enabling the network interface), verify the WebSocket
reconnects and logs `[HMR] connected` when the tab is re-focused
- Verified: `pnpm --filter=next types` passes with no errors