From 99dcb39509643b85ce9512b313247d0bdb6e2c6f Mon Sep 17 00:00:00 2001 From: David Nguyen Date: Tue, 10 Nov 2026 15:05:53 +0700 Subject: [PATCH 1/2] fix(perf): checkpoint online presence on activity after delayed timer --- utils/online-duration.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/online-duration.mjs b/utils/online-duration.mjs index 0ce18a0e..cf066efe 100644 --- a/utils/online-duration.mjs +++ b/utils/online-duration.mjs @@ -102,7 +102,7 @@ export function createOnlineDurationTracker({ const current = now(); // An input event after the idle deadline must not retroactively make // the entire unattended gap count as foreground activity. - if (active && current >= lastActivityAt + idleMs) flush(); + if (active && (current >= lastActivityAt + idleMs || current - lastAt >= MAX_ONLINE_CHECKPOINT_MS)) flush(); if (!active) { active = true; lastAt = current; } lastActivityAt = current; }, From d8f8ce5a8d0bcf552e1728f4ead1b8d9d4e46d84 Mon Sep 17 00:00:00 2001 From: David Nguyen Date: Tue, 10 Nov 2026 15:06:00 +0700 Subject: [PATCH 2/2] test(perf): cover activity during delayed online checkpoint --- tests/online-duration.test.mjs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/online-duration.test.mjs b/tests/online-duration.test.mjs index 3575a30e..1047f518 100644 --- a/tests/online-duration.test.mjs +++ b/tests/online-duration.test.mjs @@ -152,3 +152,15 @@ test('all seven locales explain online time and independent listening metrics', } } }); + +test('foreground activity checkpoints a delayed timer before later time is lost', () => { + let now = 0; + const commits = []; + const tracker = createOnlineDurationTracker({ now: () => now, onDuration: ms => commits.push(ms) }); + tracker.start(); + now = MAX_ONLINE_CHECKPOINT_MS; + tracker.activity(); // Interval callback has not fired yet. + now += 5_000; + tracker.stop(); + assert.deepEqual(commits, [MAX_ONLINE_CHECKPOINT_MS, 5_000]); +});