Gate live feed staleness and isolate Next dev artifacts

- delay stale status for paused live feeds before surfacing disconnects
- keep `next dev` output separate from production build artifacts
- add coverage for the new live-feed stale threshold
This commit is contained in:
dirtydishes 2026-05-06 22:14:11 -04:00
parent 07a9b91df7
commit 53eeb9e72f
8 changed files with 130 additions and 6 deletions

View file

@ -185,6 +185,11 @@ describe("live tape pausable helpers", () => {
expect(getLiveFeedStatus("disconnected", 1000, 500, 1601)).toBe("disconnected");
});
it("waits for an additional behind-delay before surfacing stale", () => {
expect(getLiveFeedStatus("connected", 1000, 500, 2000, 15_000)).toBe("connected");
expect(getLiveFeedStatus("connected", 1000, 500, 16_501, 15_000)).toBe("stale");
});
it("keeps visible history even when live status is stale", () => {
const projected = projectPausableTapeState([makeItem("stale", 7, 1000)], "stale", 2000);
expect(projected.items.map((item) => item.trace_id)).toEqual(["stale"]);

View file

@ -72,6 +72,7 @@ const LIVE_HOT_WINDOW_OPTIONS = parseBoundedInt(
const LIVE_OPTIONS_STALE_MS = 15_000;
const LIVE_NBBO_STALE_MS = 15_000;
const LIVE_EQUITIES_STALE_MS = 15_000;
const LIVE_FEED_BEHIND_DELAY_MS = 15_000;
const LIVE_EQUITIES_SILENT_WARNING_MS = parseBoundedInt(
process.env.NEXT_PUBLIC_LIVE_EQUITIES_SILENT_WARNING_MS,
25_000,
@ -491,7 +492,8 @@ export const getLiveFeedStatus = (
sourceStatus: WsStatus,
freshestTs: number | null,
thresholdMs: number,
now = Date.now()
now = Date.now(),
behindDelayMs = 0
): WsStatus => {
if (sourceStatus !== "connected") {
return sourceStatus;
@ -499,7 +501,14 @@ export const getLiveFeedStatus = (
if (freshestTs === null) {
return "connected";
}
return isFreshLiveItem(freshestTs, thresholdMs, now) ? "connected" : "stale";
const ageMs = now - freshestTs;
if (ageMs <= thresholdMs) {
return "connected";
}
const behindMs = ageMs - thresholdMs;
return behindMs > behindDelayMs ? "stale" : "connected";
};
type TapeState<T> = {
@ -945,8 +954,6 @@ export const countActiveFlowFilterGroups = (filters: OptionFlowFilters): number
return count;
};
const isFreshLiveItem = (ts: number, thresholdMs: number, now = Date.now()): boolean => now - ts <= thresholdMs;
export const toggleFilterValue = <T extends string>(
values: T[] | undefined,
value: T,
@ -1995,7 +2002,13 @@ const usePausableTapeView = <T extends SortableItem & { seq: number }>(
}, [config.sourceItems, getItemTs]);
const status = config.enabled
? getLiveFeedStatus(config.sourceStatus, freshestTs, config.freshnessMs, clock)
? getLiveFeedStatus(
config.sourceStatus,
freshestTs,
config.freshnessMs,
clock,
LIVE_FEED_BEHIND_DELAY_MS
)
: "disconnected";
const projected = projectPausableTapeState(data.visible, status, config.lastUpdate);