diff --git a/tests/page-unit.js b/tests/page-unit.js index 84c3481..6a45113 100644 --- a/tests/page-unit.js +++ b/tests/page-unit.js @@ -242,3 +242,10 @@ test('a tap is its trace\'s root span, reported with its ids once it has an answ assert.equal(run(`sent.at(-1)[0]`), '/api/client'); assert.deepEqual([body.ev, body.ms, body.msg, body.span.length, body.trace.length], ['tap', 250, 'keydown', 16, 32]); }); + +test('a tap ended over a minute after its last request reports once, without recursing (#378)', () => { + run(`globalThis.sent = []; fetch = (url, o) => { sent.push([url, o]); return Promise.resolve({ ok: true, status: 204 }); }`); + run(`newTrace('pointerdown'); answeredAt = rootAt + 100; traceAt = Date.now() - 120_000`); + run('endTap()'); + assert.equal(run(`sent.filter(([u, o]) => JSON.parse(o.body).ev === 'tap').length`), 1); +}); diff --git a/web/CHANGELOG.md b/web/CHANGELOG.md index 07e3871..d055736 100644 --- a/web/CHANGELOG.md +++ b/web/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Fixed +- Switching away from the page a minute after using it no longer stops it with "Maximum call stack size exceeded" (#378). - Two items from different feeds that share an id, as a blog's RSS and Atom feeds' do, are told apart: in All Subscriptions the second can be opened and marked read, where the first was marked whichever was clicked (#361). diff --git a/web/src/util.ts b/web/src/util.ts index 2ff4cc6..7115b21 100644 --- a/web/src/util.ts +++ b/web/src/util.ts @@ -109,9 +109,12 @@ function spanText(secs: number){ let traceId='', traceAt=0, rootId='', rootAt=0, rootKind='', answeredAt=0; const hex=(n: number)=>Array.from(crypto.getRandomValues(new Uint8Array(n)),b=>b.toString(16).padStart(2,'0')).join(''); function endTap(){ - if(rootKind && answeredAt>rootAt) - report('tap',{trace:traceId,span:rootId,start:rootAt,ms:Math.min(answeredAt-rootAt,60_000),msg:rootKind}); + const kind=rootKind; + // Cleared first: report's traceparent can start a new trace, whose newTrace calls endTap again, + // and with the tap still open that recursed until the stack ran out (#378). rootKind=''; + if(kind && answeredAt>rootAt) + report('tap',{trace:traceId,span:rootId,start:rootAt,ms:Math.min(answeredAt-rootAt,60_000),msg:kind}); } function newTrace(kind: string){ endTap();