From 1a3c405345f04df4d37bbcd119aa6242dece7115 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 9 Feb 2026 21:39:38 +0000 Subject: [PATCH] fix: batch merge notifications and add storage failure warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes Copilot audit issues #2 and #4 (code we added): Issue #2 - Multiple Event Emissions (MEDIUM severity): - Changed: Collect all merge notifications in array - Emit: Single batched event instead of multiple separate events - Impact: Better performance, fewer DB transactions, no UI flickering Issue #4 - Storage Validation (MEDIUM severity): - Added: Warning log when LID-PN mappings fail to store - Tracks: errors count vs notifications sent for debugging - Improves: Observability of partial storage failures Technical changes: - Declared mergeNotifications array before loop - Compute mergedAt timestamp once (not per iteration) - Push notifications to array instead of emitting in loop - Emit single chats.update with all notifications - Log warning with detailed counts if result.errors > 0 Benefits: ✅ 100x fewer events for 100 mappings (1 vs 100) ✅ Better consumer performance (ZPRO) ✅ Improved observability of storage failures ✅ Zero breaking changes (backward compatible) Note: Did NOT fix Issue #3 (Prototype Pollution) as it's in pre-existing code (event-buffer.ts), not code we added. https://claude.ai/code/session_01SoNUGBEWbJwWWws3F2fuzh --- src/Socket/chats.ts | 38 ++++++++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/src/Socket/chats.ts b/src/Socket/chats.ts index c8d35641..b0a0b0dc 100644 --- a/src/Socket/chats.ts +++ b/src/Socket/chats.ts @@ -1309,6 +1309,10 @@ export const makeChatsSocket = (config: SocketConfig) => { // Automatic chat merge: notify consumers about LID→PN mapping // This allows ZPRO and other consumers to merge/rename chats accordingly + // Collect all merge notifications to emit in a single batch + const mergeNotifications: ChatUpdate[] = [] + const mergedAt = Date.now() + for (const mapping of mappings) { const lidUser = jidNormalizedUser(mapping.lid) const pnUser = jidNormalizedUser(mapping.pn) @@ -1316,20 +1320,34 @@ export const makeChatsSocket = (config: SocketConfig) => { if (lidUser && pnUser && lidUser !== pnUser) { logger.debug( { lid: lidUser, pn: pnUser }, - 'emitting chat update for LID→PN merge notification' + 'collected chat update for LID→PN merge notification' ) - // Emit chat update indicating this chat should be merged/renamed from LID to PN - ev.emit('chats.update', [ - { - id: pnUser, - merged: true, - previousId: lidUser, - mergedAt: Date.now() - } - ]) + mergeNotifications.push({ + id: pnUser, + merged: true, + previousId: lidUser, + mergedAt + }) } } + + // Emit single batch of merge notifications for better performance + if (mergeNotifications.length > 0) { + logger.debug( + { count: mergeNotifications.length }, + 'emitting batch of chat merge notifications' + ) + ev.emit('chats.update', mergeNotifications) + } + + // Log warning if some mappings failed to store + if (result.errors > 0) { + logger.warn( + { errors: result.errors, total: mappings.length, notified: mergeNotifications.length }, + 'some LID-PN mappings failed to store, but merge notifications were sent' + ) + } } catch (error) { logger.warn({ count: mappings.length, error }, 'Failed to store LID-PN mappings') }