From 1363d360465cd9cfa46f44ec649510279bbf7ffc Mon Sep 17 00:00:00 2001 From: Nora Date: Thu, 2 Jul 2026 09:23:44 -0600 Subject: [PATCH] fix(sp25): typecheck fixups MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three small follow-ups after the live-tail triplet wired up: * evictOldest is now generic over (K extends string|number, V) so the addAck / addTa1Ack call sites don't need a type assertion. * Acks.test.tsx no longer spreads importOriginal() — TypeScript was choking on the inferred 'unknown' from the mock factory. Use the same ApiError / mock shape that Claims.test.tsx uses. * tail-stream.test.ts guards the optional gen.return() with so strict mode TS2722 stops complaining about possibly-undefined. --- src/lib/tail-stream.test.ts | 4 ++-- src/pages/Acks.test.tsx | 10 +++++++--- src/store/tail-store.ts | 14 +++++++------- 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/src/lib/tail-stream.test.ts b/src/lib/tail-stream.test.ts index 0f1424f..4638351 100644 --- a/src/lib/tail-stream.test.ts +++ b/src/lib/tail-stream.test.ts @@ -106,7 +106,7 @@ describe("streamTail", () => { // before we close the stream. await gen.next(); driver.close(); - await gen.return(undefined); + await gen.return?.(undefined); expect(fetchMock).toHaveBeenCalledTimes(1); const [calledUrl] = fetchMock.mock.calls[0] as [string]; expect(calledUrl).toBe("/api/acks/stream"); @@ -121,7 +121,7 @@ describe("streamTail", () => { const gen = streamTail("ta1_acks"); await gen.next(); driver.close(); - await gen.return(undefined); + await gen.return?.(undefined); expect(fetchMock).toHaveBeenCalledTimes(1); const [calledUrl] = fetchMock.mock.calls[0] as [string]; expect(calledUrl).toBe("/api/ta1-acks/stream"); diff --git a/src/pages/Acks.test.tsx b/src/pages/Acks.test.tsx index 4ca7eef..a7c726e 100644 --- a/src/pages/Acks.test.tsx +++ b/src/pages/Acks.test.tsx @@ -9,15 +9,19 @@ import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { Acks } from "./Acks"; import { api } from "@/lib/api"; -vi.mock("@/lib/api", async (importOriginal) => { - const actual = await importOriginal(); +vi.mock("@/lib/api", () => { + class ApiError extends Error { + constructor(public status: number, message: string) { + super(message); + } + } return { - ...actual, api: { isConfigured: true, listAcks: vi.fn(), getAck: vi.fn(), }, + ApiError, }; }); diff --git a/src/store/tail-store.ts b/src/store/tail-store.ts index d6b2630..f2bda85 100644 --- a/src/store/tail-store.ts +++ b/src/store/tail-store.ts @@ -79,11 +79,11 @@ interface TailStore { */ type RemitListItem = Remittance; -function evictOldest( - order: Array, - dict: Record, +function evictOldest( + order: K[], + dict: Record, batch: number, -): { order: Array; dict: Record } { +): { order: K[]; dict: Record } { if (order.length <= TAIL_CAP) return { order, dict }; // Drain down to the cap; never evict more than `batch` per call so a // 5-item overflow evicts 5, but a 1 000-item overflow evicts 100 in @@ -93,7 +93,7 @@ function evictOldest( const nextOrder = order.slice(toDrop); // Object.assign is consistently faster than `{ ...dict }` in V8 for // large dicts; we follow up with `delete` for each evicted id. - const nextDict: Record = Object.assign({}, dict); + const nextDict: Record = Object.assign({}, dict); for (const id of dropped) delete nextDict[String(id)]; return { order: nextOrder, dict: nextDict }; } @@ -172,7 +172,7 @@ export const useTailStore = create((set) => ({ const nextOrder = s.ackOrder.concat(a.id); if (nextOrder.length > TAIL_CAP) { const { order, dict } = evictOldest(nextOrder, nextAcks, EVICT_BATCH); - return { acks: dict, ackOrder: order as number[] }; + return { acks: dict, ackOrder: order }; } return { acks: nextAcks, ackOrder: nextOrder }; }), @@ -188,7 +188,7 @@ export const useTailStore = create((set) => ({ const nextOrder = s.ta1AckOrder.concat(a.id); if (nextOrder.length > TAIL_CAP) { const { order, dict } = evictOldest(nextOrder, nextTa1Acks, EVICT_BATCH); - return { ta1Acks: dict, ta1AckOrder: order as number[] }; + return { ta1Acks: dict, ta1AckOrder: order }; } return { ta1Acks: nextTa1Acks, ta1AckOrder: nextOrder }; }),