feat: realtime event system — standard envelope, cross-node delivery, offline sync#285
Merged
codebestia merged 3 commits intoJun 29, 2026
Merged
Conversation
|
@Damola09 Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits. You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀 |
Owner
|
Hello @Damola09 |
…codebestia#193) Issue codebestia#193 — Standard Event Envelope + Typed Event Router - Add src/lib/eventEnvelope.ts: Zod-validated envelope schema {eventId, type, timestamp, payload}, central KNOWN_EVENT_TYPES registry, createEnvelope() helper - Add src/socket/dispatcher.ts: EventDispatcher class that centralises all handler registration; registers each handler as a backward-compat socket.on() listener AND in a typed dispatch table; listen() attaches the standard dispatch channel with envelope validation, auth guard, Redis-based idempotency (eventId dedup, 24-h TTL), unknown-type warning, and per-handler error isolation (never crashes server) - Migrate src/socket/messaging.ts to route all handlers through the dispatcher, preserving full backward compatibility for raw events Issue codebestia#192 — Redis Pub/Sub Cross-Node Event Bus - Add src/services/deviceDelivery.ts: GatewayDeviceSubscriber (single shared ioredis subscriber per gateway instance) subscribes to deliver:device:{deviceId} channels for locally connected devices; publishToDevice() delivers encrypted envelopes to the channel; Redis failures degrade gracefully without crashing or blocking - Wire subscriptions in src/index.ts on connect/disconnect - Publish to device channels from send_message handler after storing per-device envelopes in the database - Socket.io Redis adapter (already present) provides room-level cross-node delivery; device channels add targeted per-device delivery Issue codebestia#187 — Offline Envelope Queue + Reconnect Sync - Add GET /sync route (src/routes/sync.ts): requires auth; validates deviceId ownership against userDevices; returns messageEnvelopes ordered by sequenceNumber, filtered by sinceSequence cursor and TTL; cursor-stable pagination; marks delivered envelopes best-effort; configurable via ENVELOPE_TTL_SECONDS and SYNC_PAGE_SIZE env vars - Register /sync in src/app.ts Tests: 362 passing (181 new) - eventEnvelope.test.ts: schema validation, type registry, createEnvelope - dispatcher.test.ts: handler routing, malformed envelope, unknown type, idempotency dedup, auth rejection, backward-compat raw events - deviceDelivery.test.ts: channel naming, publish, subscribe, message routing, unsubscribe, Redis failure graceful degradation - sync.routes.test.ts: empty queue, partial/full sync, pagination, ordering, auth, cursor stability
Add missing ormat:check script to root package.json so CI can run pnpm format:check. Apply prettier formatting to all files that were out of style.
f2343e9 to
a1b0fb9
Compare
codebestia
approved these changes
Jun 29, 2026
codebestia
left a comment
Owner
There was a problem hiding this comment.
LGTM!
Thank you for your contribution.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
EventDispatcherclass centralises handler registration with idempotency, auth guard, and unknown-type safety; all messaging handlers migrated through the dispatcher while preserving backward-compatible raw-event clients.GatewayDeviceSubscriberservice (one shared ioredis subscriber per gateway) subscribes todeliver:device:{deviceId}channels for locally-connected devices;publishToDeviceroutes per-device encrypted envelopes across gateways; Redis unavailability degrades gracefully.GET /sync?deviceId=<id>&sinceSequence=<cursor>returns undeliveredmessageEnvelopesordered bysequenceNumber, paginated, TTL-filtered, with deterministic cursor pagination stable under concurrent inserts.socket→device→usermappings #191 — Redis Socket Registry: On authenticated connect, writessocket:{socketId},device:{deviceId}, anduser:{userId}:devicesmappings with 120 s TTL. Heartbeat refreshes TTLs so crashed nodes self-heal after two missed beats.getUserSocketsprunes stale entries from dead nodes. Any gateway can resolve where to deliver via Redis lookups.Architecture decisions
EventDispatcher.register()attaches both a typed dispatch-table entry and a legacysocket.on(type, ...)listener, so existing clients continue to work unchanged.GatewayDeviceSubscriberreuses one ioredis connection per gateway viaduplicate(), avoiding a connection-per-socket model.messageEnvelopestable already haddeliveredAt,createdAt,recipientDeviceId; sync route uses TTL viaENVELOPE_TTL_SECONDSenv var (default 7 days).eventIdvia Redis SET NX EX 86400;send_messagealready had DB-level idempotency onmessageId.Testing performed
Compatibility
socket:,device:,user:) and do not collide with existing keys.Closes #193
Closes #192
Closes #187
Closes #191