fix(reconnect): wire WS manual-reconnect helper now that htmx is actually loaded #88

Merged
nrupard merged 1 commit from fix/ws-reconnect-after-restart into main 2026-05-13 20:51:47 +02:00
Owner

Summary

  • htmx.defineExtension('lc-ws-reconnect', ...) and htmx.config.wsReconnectDelay were called from an inline body <script>, which executes during body parsing - before the deferred htmx.min.js / htmx-ext-ws.js in <head> have run. window.htmx was undefined, the if (window.htmx) guards silently skipped both setups, and window.__lcReconnectWS was never defined.
  • That hid the user-visible bug: htmx-ext-ws only auto-reconnects for close codes 1006/1012/1013. A docker stop / redeploy closes the socket cleanly with 1000/1001, so the extension does nothing AND the manual-reconnect fallback didn't exist. The banner sat on "Reconnecting..." forever and the page had to be reloaded manually.
  • Defer the htmx-dependent setup to DOMContentLoaded (fires after deferred scripts), drop the extension-mechanism dance, read htmx's htmx-internal-data property directly to call wrapper.init(), and split wsError from wsClose so only wsClose (which actually carries a code) decides whether to fire the manual reconnect.

Test plan

  • Open the app, then run just dev-web-down && just dev-web (graceful restart). Banner should show "Reconnecting...", then "Connected", and the page should soft-refresh without a manual reload.
  • Kill the container abruptly (docker kill) so the close code is 1006. Should also reconnect on backoff.
  • Pause the server long enough for the half-open watchdog (60s) to fire its close(4000, 'lc-half-open'). Reconnect should still kick in.
  • just check clean.
## Summary - `htmx.defineExtension('lc-ws-reconnect', ...)` and `htmx.config.wsReconnectDelay` were called from an inline body `<script>`, which executes during body parsing - before the deferred `htmx.min.js` / `htmx-ext-ws.js` in `<head>` have run. `window.htmx` was `undefined`, the `if (window.htmx)` guards silently skipped both setups, and `window.__lcReconnectWS` was never defined. - That hid the user-visible bug: htmx-ext-ws only auto-reconnects for close codes `1006/1012/1013`. A `docker stop` / redeploy closes the socket cleanly with `1000`/`1001`, so the extension does nothing AND the manual-reconnect fallback didn't exist. The banner sat on "Reconnecting..." forever and the page had to be reloaded manually. - Defer the htmx-dependent setup to `DOMContentLoaded` (fires after deferred scripts), drop the extension-mechanism dance, read htmx's `htmx-internal-data` property directly to call `wrapper.init()`, and split `wsError` from `wsClose` so only `wsClose` (which actually carries a code) decides whether to fire the manual reconnect. ## Test plan - [ ] Open the app, then run `just dev-web-down && just dev-web` (graceful restart). Banner should show "Reconnecting...", then "Connected", and the page should soft-refresh without a manual reload. - [ ] Kill the container abruptly (`docker kill`) so the close code is `1006`. Should also reconnect on backoff. - [ ] Pause the server long enough for the half-open watchdog (60s) to fire its `close(4000, 'lc-half-open')`. Reconnect should still kick in. - [ ] `just check` clean.
fix(reconnect): wire WS manual-reconnect helper now that htmx is actually loaded
Some checks failed
Check / clippy + fmt + tests (pull_request) Failing after 11s
5ada89d663
`htmx.defineExtension('lc-ws-reconnect', ...)` and `htmx.config.wsReconnectDelay` were called from an inline `<script>` in the body, which the parser executes *during* body parsing - before the deferred `htmx.min.js` and `htmx-ext-ws.js` in `<head>` have had a chance to run. `window.htmx` was therefore `undefined` at the point of those calls, the `if (window.htmx)` guards silently skipped both blocks, and `window.__lcReconnectWS` was never defined.

That hid a much more visible bug: the htmx-ext-ws extension only auto-reconnects for close codes `1006/1012/1013`. When the server is restarted gracefully (SIGTERM via `docker stop` / a redeploy), the socket closes with `1000`/`1001` instead, so the extension does nothing AND the manual-reconnect fallback was never wired up. The page would sit on "Connection lost. Reconnecting..." forever and the user had to reload to recover.

Defer the htmx-dependent setup to `DOMContentLoaded` (which fires after every deferred script in the document has executed, so `window.htmx` is guaranteed). Drop the extension-mechanism dance entirely: `__lcReconnectWS` now reads htmx's `htmx-internal-data` property on the `[ws-connect]` element directly to call `wrapper.init()`. Also separate the `wsError` and `wsClose` listeners so only `wsClose` (which carries the close code) decides whether to fire the manual reconnect; `wsError` arrives without a code and was previously triggering an immediate, backoff-bypassing reconnect that overlapped with the close-driven retry.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
nrupard deleted branch fix/ws-reconnect-after-restart 2026-05-13 20:51:48 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
psa-systems/lets-chat!88
No description provided.