perf(web): stream dashboard tile events
This commit is contained in:
parent
a66f1fdd57
commit
41fb964ebb
7 changed files with 315 additions and 0 deletions
|
|
@ -13,6 +13,7 @@ import {
|
|||
runViewportAwareDashboardHydrationQueue,
|
||||
shouldPauseDashboardRefresh,
|
||||
splitDashboardHydrationItemsByVisibility,
|
||||
subscribeToDashboardTileEvents,
|
||||
waitForDashboardHydrationIdle,
|
||||
type DashboardIntersectionEntry,
|
||||
} from "./dashboard-refresh";
|
||||
|
|
@ -302,6 +303,69 @@ describe("dashboard refresh lifecycle", () => {
|
|||
]);
|
||||
});
|
||||
|
||||
test("subscribes to dashboard tile events", () => {
|
||||
const received: unknown[] = [];
|
||||
let listener: ((event: MessageEvent<string>) => void) | undefined;
|
||||
let closed = false;
|
||||
|
||||
const unsubscribe = subscribeToDashboardTileEvents({
|
||||
createEventSource: (url) => {
|
||||
expect(url).toBe("/api/dashboard/events");
|
||||
return {
|
||||
addEventListener(_type, eventListener) {
|
||||
listener = eventListener;
|
||||
},
|
||||
close() {
|
||||
closed = true;
|
||||
},
|
||||
onerror: null,
|
||||
};
|
||||
},
|
||||
onTile: (tile) => received.push(tile),
|
||||
});
|
||||
|
||||
listener?.({ data: JSON.stringify({ state: "ready" }) } as MessageEvent<string>);
|
||||
expect(received).toEqual([{ state: "ready" }]);
|
||||
unsubscribe();
|
||||
expect(closed).toBe(true);
|
||||
});
|
||||
|
||||
test("falls back when dashboard tile events are unavailable or fail", () => {
|
||||
let unavailableCount = 0;
|
||||
|
||||
subscribeToDashboardTileEvents({
|
||||
createEventSource: undefined,
|
||||
onTile: () => undefined,
|
||||
onUnavailable: () => {
|
||||
unavailableCount += 1;
|
||||
},
|
||||
});
|
||||
|
||||
let errorHandler: (() => void) | null = null;
|
||||
const unsubscribe = subscribeToDashboardTileEvents({
|
||||
createEventSource: () => ({
|
||||
addEventListener: () => undefined,
|
||||
close: () => undefined,
|
||||
get onerror() {
|
||||
return errorHandler;
|
||||
},
|
||||
set onerror(handler) {
|
||||
errorHandler = handler;
|
||||
},
|
||||
}),
|
||||
onTile: () => undefined,
|
||||
onUnavailable: () => {
|
||||
unavailableCount += 1;
|
||||
},
|
||||
});
|
||||
|
||||
if (!errorHandler) throw new Error("expected error handler");
|
||||
const triggerError = errorHandler as unknown as () => void;
|
||||
triggerError();
|
||||
unsubscribe();
|
||||
expect(unavailableCount).toBe(2);
|
||||
});
|
||||
|
||||
test("backs off failed tile keys and resets after success", () => {
|
||||
const backoff = createDashboardTileBackoff();
|
||||
|
||||
|
|
|
|||
|
|
@ -489,6 +489,51 @@ export function createDashboardPerformanceMarks(
|
|||
};
|
||||
}
|
||||
|
||||
export interface DashboardTileEventSource {
|
||||
addEventListener(
|
||||
type: "dashboard-tile",
|
||||
listener: (event: MessageEvent<string>) => void,
|
||||
): void;
|
||||
close(): void;
|
||||
onerror: (() => void) | null;
|
||||
}
|
||||
|
||||
export interface DashboardTileEventSubscriptionOptions {
|
||||
createEventSource?: (url: string) => DashboardTileEventSource;
|
||||
onTile: (data: unknown) => void;
|
||||
onUnavailable?: () => void;
|
||||
url?: string;
|
||||
}
|
||||
|
||||
export function subscribeToDashboardTileEvents(
|
||||
options: DashboardTileEventSubscriptionOptions,
|
||||
): () => void {
|
||||
const createEventSource = options.createEventSource ||
|
||||
(typeof globalThis.EventSource !== "undefined"
|
||||
? (url: string) => new globalThis.EventSource(url)
|
||||
: undefined);
|
||||
|
||||
if (!createEventSource) {
|
||||
options.onUnavailable?.();
|
||||
return () => undefined;
|
||||
}
|
||||
|
||||
const source = createEventSource(options.url || "/api/dashboard/events");
|
||||
source.addEventListener("dashboard-tile", (event) => {
|
||||
try {
|
||||
options.onTile(JSON.parse(event.data));
|
||||
} catch {
|
||||
// Ignore malformed diagnostics from an optional live transport.
|
||||
}
|
||||
});
|
||||
source.onerror = () => {
|
||||
source.close();
|
||||
options.onUnavailable?.();
|
||||
};
|
||||
|
||||
return () => source.close();
|
||||
}
|
||||
|
||||
const dashboardTileBackoffDelaysMs = [15_000, 30_000, 60_000, 120_000];
|
||||
|
||||
export function createDashboardTileBackoff() {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue