reqSignIn sends a 303 redirect which breaks WebSocket upgrade; use the
same pattern as /user/events: register the route without middleware and
return 401 inside the handler when the user is not signed in.
Also fix copyright year to 2026 in all three new Go files and add a
console.warn for malformed JSON in the SharedWorker.
- Move /-/ws route inside reqSignIn middleware group; remove manual
ctx.IsSigned check from handler (auth is now enforced by the router)
- Fix scheduleReconnect() to schedule using current delay then double,
so first reconnect fires after 50ms not 100ms (reported by silverwind)
- Replace sourcesByPort.set(port, null) with delete() to prevent
MessagePort retention after tab close (memory leak fix)
- Centralize topic naming in pubsub.UserTopic() — removes duplication
between the notifier and the WebSocket handler
- Skip DB polling in notifier when broker has no active subscribers
to avoid unnecessary load on idle instances
- Hold RLock for the full Publish fan-out loop to prevent a race
where cancel() closes a channel between slice read and send
Replace timeutil.TimeStampNow() calls in the websocket notifier with a
nowTS() helper that reads time.Now().Unix() directly. TimeStampNow reads
a package-level mock variable that TestIncomingEmail writes concurrently,
causing a race detected by the race detector in test-pgsql CI.
Add a thin in-memory pubsub broker and a SharedWorker-based WebSocket
client to deliver real-time notification count updates. This replaces
the SSE path for notification-count events with a persistent WebSocket
connection shared across all tabs.
New files:
- services/pubsub/broker.go: fan-out pubsub broker (DefaultBroker singleton)
- services/websocket/notifier.go: polls notification counts, publishes to broker
- routers/web/websocket/websocket.go: /-/ws endpoint, per-user topic subscription
- web_src/js/features/websocket.sharedworker.ts: SharedWorker with exponential
backoff reconnect (50ms initial, 10s max, reconnect on close and error)
Modified files:
- routers/init.go: register websocket_service.Init()
- routers/web/web.go: add GET /-/ws route
- services/context/response.go: add Hijack() to forward http.Hijacker
so coder/websocket can upgrade the connection
- web_src/js/features/notification.ts: port from SSE SharedWorker to WS SharedWorker
- webpack.config.ts: add websocket.sharedworker entry point
Part of RFC #36942.