diff --git a/src/lib/event-feed.ts b/src/lib/event-feed.ts index 36ddd59..1bebbf2 100644 --- a/src/lib/event-feed.ts +++ b/src/lib/event-feed.ts @@ -8,6 +8,12 @@ export function decodeEvent(msg: SseMessage): FeedEvent | null { if (!outer.data) return null; const inner = JSON.parse(outer.data) as Record; + if (outer.type === "activity") { + const id = typeof inner.id === "number" ? inner.id : Number.NaN; + if (Number.isFinite(id)) return { type: "activity", id }; + return null; + } + if (outer.type === "inflight") { const operation = inner.operation as string; if (operation === "remove") { diff --git a/src/lib/inflight-tracker.ts b/src/lib/inflight-tracker.ts index 63ed038..5406626 100644 --- a/src/lib/inflight-tracker.ts +++ b/src/lib/inflight-tracker.ts @@ -15,6 +15,7 @@ export interface ModelState { export type FeedEvent = | { type: "inflight"; operation: "snapshot" | "add"; requests: InflightRequest[] } | { type: "inflight"; operation: "remove"; id: string } + | { type: "activity"; id: number } | { type: "modelStatus"; models: ModelState[] }; export type ModelRuntimeState = "stopped" | "loading" | "ready"; @@ -44,6 +45,10 @@ export class InflightTracker { return n; } + total(): number { + return this.requests.size; + } + state(modelId: string): ModelRuntimeState | undefined { return this.states.get(modelId); } diff --git a/tests/event-feed.test.ts b/tests/event-feed.test.ts index 2a91006..dc66157 100644 --- a/tests/event-feed.test.ts +++ b/tests/event-feed.test.ts @@ -117,3 +117,12 @@ test("decodeEvent handles inflight with a non-array requests field and no reques assert.deepEqual(ev.requests, []); } }); + +test("decodeEvent parses an activity event with an id", () => { + const msg: SseMessage = { + event: "message", + data: JSON.stringify({ type: "activity", data: JSON.stringify({ id: 817 }) }), + }; + const ev = decodeEvent(msg); + assert.deepEqual(ev, { type: "activity", id: 817 }); +}); diff --git a/tests/inflight-tracker.test.ts b/tests/inflight-tracker.test.ts index 3f4b443..fe43e18 100644 --- a/tests/inflight-tracker.test.ts +++ b/tests/inflight-tracker.test.ts @@ -63,3 +63,13 @@ test("modelStatus normalizes states", () => { assert.equal(tracker.state("D"), "stopped"); assert.equal(tracker.state("missing"), undefined); }); + +test("total sums in-flight requests across all models", () => { + const tracker = new InflightTracker(); + assert.equal(tracker.total(), 0); + tracker.apply({ type: "inflight", operation: "add", requests: [{ model: "A", id: "1" }, { model: "B", id: "2" }] }); + tracker.apply({ type: "inflight", operation: "add", requests: [{ model: "A", id: "3" }] }); + assert.equal(tracker.total(), 3); + tracker.apply({ type: "inflight", operation: "remove", id: "1" }); + assert.equal(tracker.total(), 2); +});