The page stops recursing when a tap ends long after its answer (#378)
endTap reported the tap before clearing it; report's traceparent starts a new trace when the last is over a minute old, and newTrace ends the open tap again, so hiding the page (switching apps on the phone) a minute after using it recursed until the stack ran out. Timings are never throttled, so nothing broke the loop. Introduced in #376. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -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.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]);
|
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);
|
||||||
|
});
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
### Fixed
|
### 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).
|
- 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).
|
||||||
|
|
||||||
|
|||||||
@@ -109,9 +109,12 @@ function spanText(secs: number){
|
|||||||
let traceId='', traceAt=0, rootId='', rootAt=0, rootKind='', answeredAt=0;
|
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('');
|
const hex=(n: number)=>Array.from(crypto.getRandomValues(new Uint8Array(n)),b=>b.toString(16).padStart(2,'0')).join('');
|
||||||
function endTap(){
|
function endTap(){
|
||||||
if(rootKind && answeredAt>rootAt)
|
const kind=rootKind;
|
||||||
report('tap',{trace:traceId,span:rootId,start:rootAt,ms:Math.min(answeredAt-rootAt,60_000),msg: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='';
|
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){
|
function newTrace(kind: string){
|
||||||
endTap();
|
endTap();
|
||||||
|
|||||||
Reference in New Issue
Block a user