From 46de639ebcfdd5a80ad4e9ed300b17d2d80d9e3c Mon Sep 17 00:00:00 2001 From: 4gray Date: Mon, 28 Sep 2026 22:21:31 +0200 Subject: [PATCH] fix(stalker): keep a stopped watchdog interval stopped A worker error that arrived after stop() started the page fallback interval, which nothing cleared, so pings continued for an inactive playlist. stop() now marks the interval stopped, detaches the worker handlers, and the fallback refuses to start afterwards. Co-Authored-By: Claude Opus 5.5 --- .../src/lib/background-interval.spec.ts | 15 +++++++++++++++ .../data-access/src/lib/background-interval.ts | 8 ++++++++ 2 files changed, 23 insertions(+) diff --git a/libs/portal/stalker/data-access/src/lib/background-interval.spec.ts b/libs/portal/stalker/data-access/src/lib/background-interval.spec.ts index da95a2d05..dc05d6505 100644 --- a/libs/portal/stalker/data-access/src/lib/background-interval.spec.ts +++ b/libs/portal/stalker/data-access/src/lib/background-interval.spec.ts @@ -98,4 +98,19 @@ describe('createBackgroundInterval', () => { jest.advanceTimersByTime(60_000); expect(callback).toHaveBeenCalledTimes(1); }); + + it('stays stopped when a worker error arrives after stop', () => { + scope.Worker = FakeWorker; + const callback = jest.fn(); + + const stop = createBackgroundInterval(callback, 30_000); + const [worker] = FakeWorker.instances; + const lateError = worker.onerror; + stop(); + lateError?.(new Event('error')); + + jest.advanceTimersByTime(90_000); + expect(callback).not.toHaveBeenCalled(); + expect(jest.getTimerCount()).toBe(0); + }); }); diff --git a/libs/portal/stalker/data-access/src/lib/background-interval.ts b/libs/portal/stalker/data-access/src/lib/background-interval.ts index f10f30225..c51950eec 100644 --- a/libs/portal/stalker/data-access/src/lib/background-interval.ts +++ b/libs/portal/stalker/data-access/src/lib/background-interval.ts @@ -24,7 +24,10 @@ export function createBackgroundInterval( periodMs: number ): () => void { let fallback: ReturnType | undefined; + let stopped = false; const startFallback = () => { + // A worker error that arrives after stop() must not revive the tick. + if (stopped) return; fallback ??= setInterval(callback, periodMs); }; const ticker = startTickerWorker(); @@ -45,6 +48,11 @@ export function createBackgroundInterval( startFallback(); } return () => { + stopped = true; + if (ticker) { + ticker.worker.onmessage = null; + ticker.worker.onerror = null; + } stopWorker(); if (fallback !== undefined) clearInterval(fallback); };