diff --git a/scripts/whatsapp-bridge/outbound_ids.js b/scripts/whatsapp-bridge/outbound_ids.js index cb2b95fdcbb..56bf9b34b2e 100644 --- a/scripts/whatsapp-bridge/outbound_ids.js +++ b/scripts/whatsapp-bridge/outbound_ids.js @@ -1,10 +1,13 @@ /** - * Bounded LRU set of outbound message IDs. + * Bounded FIFO set of outbound message IDs. * * Used by the WhatsApp bridge to distinguish "echo of our own /send" from * "owner-typed message on the linked device" when forwarding `fromMe` * inbound events back to the Python adapter. * + * Eviction drops the oldest insertion-order entry when the cap is exceeded. + * Re-remembering an existing id is a no-op for ordering (not LRU refresh). + * * Heuristic limitation (intentional, documented for future debugging): * the set is in-memory only. On bridge restart it is empty, so for the * brief window between restart and the first new outbound, any in-flight @@ -15,6 +18,9 @@ */ export function createOutboundIdTracker(maxSize = 512) { + if (!Number.isInteger(maxSize) || maxSize < 1) { + throw new RangeError('createOutboundIdTracker: maxSize must be a positive integer'); + } const ids = new Set(); function remember(id) { diff --git a/scripts/whatsapp-bridge/outbound_ids.test.mjs b/scripts/whatsapp-bridge/outbound_ids.test.mjs index 7c88d5e58ae..54db18df390 100644 --- a/scripts/whatsapp-bridge/outbound_ids.test.mjs +++ b/scripts/whatsapp-bridge/outbound_ids.test.mjs @@ -46,11 +46,11 @@ test('cap holds across many inserts (bounded memory)', () => { assert.equal(tracker.has('id-99'), true); }); -test('re-remembering an existing id refreshes its position', () => { +test('re-remembering an existing id does not promote it (FIFO, not LRU)', () => { // Insertion-order semantics: re-adding doesn't move it forward in // Set iteration order. This is intentional — we don't need recency, // just bounded membership. Pin the actual behaviour so future - // refactors don't accidentally introduce LRU semantics. + // refactors don't accidentally introduce LRU refresh semantics. const tracker = createOutboundIdTracker(2); tracker.remember('a'); tracker.remember('b'); @@ -60,3 +60,9 @@ test('re-remembering an existing id refreshes its position', () => { assert.equal(tracker.has('b'), true); assert.equal(tracker.has('c'), true); }); + +test('rejects non-positive maxSize', () => { + assert.throws(() => createOutboundIdTracker(0), RangeError); + assert.throws(() => createOutboundIdTracker(-1), RangeError); + assert.throws(() => createOutboundIdTracker(1.5), RangeError); +});