fix(diagnostics): deep-review round 1 — a send that cannot race, lose or repeat itself
The send, the draft and the screenshots take the session's row lock, so an autosave or a paste during a send waits for it and then sees it, and two sends cannot open two issues. A save keeps the delivery's progress; a failed edit that shows the screenshots is finished by the retry; an answer that is not the tracker's API (a redirect, a proxy page) is refused, not a crash that loses the issue's record. Only an attempt that opened an issue counts toward the pace. Screenshots: deleting one after the send is refused, a user's storage is capped across sessions, new sessions and zip exports are paced, images are stored uncompressed in the zip. One liveness rule, one session view, one image type table. Logging: a session's elevation is tracked apart from the user's own, so the browser's own requests (no header) no longer lower the floor a session needs; Stop ends it at once. Composer: an image pasted anywhere is taken; what is still unsaved is saved on close, and "saved" is shown only when it is; the draft is held while it is sent; a failed screenshot read says so instead of reading as none; focus moves to what stays; the preview follows the text size. Pill: the step prompt and a running Stop survive the pill moving between hosts, the prompt keeps the dialog's Tab trap, the toast region lets clicks through, other tabs hear of a Start or Stop, the last seconds are sent before the cap, a capture with no frame is an error.
This commit is contained in:
@@ -52,6 +52,10 @@ const DETAIL = { INFO: 0, DEBUG: 1, TRACE: 2 } as const;
|
||||
|
||||
let applied: string | null = null;
|
||||
let lapseTimer: number | null = null;
|
||||
// A recording's last seconds are sent BEFORE its cap: at the cap the server stops tagging (or, at the
|
||||
// base level, taking) them, and the regular flush could be up to FLUSH_MS behind.
|
||||
const CAP_FLUSH_LEAD_MS = 1_500;
|
||||
let capFlushTimer: number | null = null;
|
||||
let onLapsed: (() => void) | null = null;
|
||||
let lastState: DiagnosticsState | null | undefined = null;
|
||||
let lastSession: DebugSession | null | undefined = null;
|
||||
@@ -79,6 +83,8 @@ export function applyDiagnostics(
|
||||
applied = key;
|
||||
if (lapseTimer !== null) window.clearTimeout(lapseTimer);
|
||||
lapseTimer = null;
|
||||
if (capFlushTimer !== null) window.clearTimeout(capFlushTimer);
|
||||
capFlushTimer = null;
|
||||
if (level === "INFO") {
|
||||
stopStreaming();
|
||||
setLevel("INFO");
|
||||
@@ -99,6 +105,10 @@ export function applyDiagnostics(
|
||||
const ends = [until, live?.until].filter((t): t is string => !!t).map((t) => Date.parse(t));
|
||||
const wait = Math.min(Math.min(...ends) - Date.now(), 2 ** 31 - 1);
|
||||
lapseTimer = window.setTimeout(lapse, wait);
|
||||
if (live) {
|
||||
const lead = Date.parse(live.until) - CAP_FLUSH_LEAD_MS - Date.now();
|
||||
if (lead > 0 && lead < wait) capFlushTimer = window.setTimeout(() => void flush(), lead);
|
||||
}
|
||||
}
|
||||
|
||||
/** Something in force ran out: apply what is left of it (the clock decides), then let `me` catch up. */
|
||||
|
||||
Reference in New Issue
Block a user