fix(sp25): typecheck fixups
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.
This commit is contained in:
@@ -106,7 +106,7 @@ describe("streamTail", () => {
|
|||||||
// before we close the stream.
|
// before we close the stream.
|
||||||
await gen.next();
|
await gen.next();
|
||||||
driver.close();
|
driver.close();
|
||||||
await gen.return(undefined);
|
await gen.return?.(undefined);
|
||||||
expect(fetchMock).toHaveBeenCalledTimes(1);
|
expect(fetchMock).toHaveBeenCalledTimes(1);
|
||||||
const [calledUrl] = fetchMock.mock.calls[0] as [string];
|
const [calledUrl] = fetchMock.mock.calls[0] as [string];
|
||||||
expect(calledUrl).toBe("/api/acks/stream");
|
expect(calledUrl).toBe("/api/acks/stream");
|
||||||
@@ -121,7 +121,7 @@ describe("streamTail", () => {
|
|||||||
const gen = streamTail("ta1_acks");
|
const gen = streamTail("ta1_acks");
|
||||||
await gen.next();
|
await gen.next();
|
||||||
driver.close();
|
driver.close();
|
||||||
await gen.return(undefined);
|
await gen.return?.(undefined);
|
||||||
expect(fetchMock).toHaveBeenCalledTimes(1);
|
expect(fetchMock).toHaveBeenCalledTimes(1);
|
||||||
const [calledUrl] = fetchMock.mock.calls[0] as [string];
|
const [calledUrl] = fetchMock.mock.calls[0] as [string];
|
||||||
expect(calledUrl).toBe("/api/ta1-acks/stream");
|
expect(calledUrl).toBe("/api/ta1-acks/stream");
|
||||||
|
|||||||
@@ -9,15 +9,19 @@ import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
|||||||
import { Acks } from "./Acks";
|
import { Acks } from "./Acks";
|
||||||
import { api } from "@/lib/api";
|
import { api } from "@/lib/api";
|
||||||
|
|
||||||
vi.mock("@/lib/api", async (importOriginal) => {
|
vi.mock("@/lib/api", () => {
|
||||||
const actual = await importOriginal();
|
class ApiError extends Error {
|
||||||
|
constructor(public status: number, message: string) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
...actual,
|
|
||||||
api: {
|
api: {
|
||||||
isConfigured: true,
|
isConfigured: true,
|
||||||
listAcks: vi.fn(),
|
listAcks: vi.fn(),
|
||||||
getAck: vi.fn(),
|
getAck: vi.fn(),
|
||||||
},
|
},
|
||||||
|
ApiError,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -79,11 +79,11 @@ interface TailStore {
|
|||||||
*/
|
*/
|
||||||
type RemitListItem = Remittance;
|
type RemitListItem = Remittance;
|
||||||
|
|
||||||
function evictOldest<T>(
|
function evictOldest<K extends string | number, V>(
|
||||||
order: Array<string | number>,
|
order: K[],
|
||||||
dict: Record<string, T>,
|
dict: Record<string, V>,
|
||||||
batch: number,
|
batch: number,
|
||||||
): { order: Array<string | number>; dict: Record<string, T> } {
|
): { order: K[]; dict: Record<string, V> } {
|
||||||
if (order.length <= TAIL_CAP) return { order, dict };
|
if (order.length <= TAIL_CAP) return { order, dict };
|
||||||
// Drain down to the cap; never evict more than `batch` per call so a
|
// 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
|
// 5-item overflow evicts 5, but a 1 000-item overflow evicts 100 in
|
||||||
@@ -93,7 +93,7 @@ function evictOldest<T>(
|
|||||||
const nextOrder = order.slice(toDrop);
|
const nextOrder = order.slice(toDrop);
|
||||||
// Object.assign is consistently faster than `{ ...dict }` in V8 for
|
// Object.assign is consistently faster than `{ ...dict }` in V8 for
|
||||||
// large dicts; we follow up with `delete` for each evicted id.
|
// large dicts; we follow up with `delete` for each evicted id.
|
||||||
const nextDict: Record<string, T> = Object.assign({}, dict);
|
const nextDict: Record<string, V> = Object.assign({}, dict);
|
||||||
for (const id of dropped) delete nextDict[String(id)];
|
for (const id of dropped) delete nextDict[String(id)];
|
||||||
return { order: nextOrder, dict: nextDict };
|
return { order: nextOrder, dict: nextDict };
|
||||||
}
|
}
|
||||||
@@ -172,7 +172,7 @@ export const useTailStore = create<TailStore>((set) => ({
|
|||||||
const nextOrder = s.ackOrder.concat(a.id);
|
const nextOrder = s.ackOrder.concat(a.id);
|
||||||
if (nextOrder.length > TAIL_CAP) {
|
if (nextOrder.length > TAIL_CAP) {
|
||||||
const { order, dict } = evictOldest(nextOrder, nextAcks, EVICT_BATCH);
|
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 };
|
return { acks: nextAcks, ackOrder: nextOrder };
|
||||||
}),
|
}),
|
||||||
@@ -188,7 +188,7 @@ export const useTailStore = create<TailStore>((set) => ({
|
|||||||
const nextOrder = s.ta1AckOrder.concat(a.id);
|
const nextOrder = s.ta1AckOrder.concat(a.id);
|
||||||
if (nextOrder.length > TAIL_CAP) {
|
if (nextOrder.length > TAIL_CAP) {
|
||||||
const { order, dict } = evictOldest(nextOrder, nextTa1Acks, EVICT_BATCH);
|
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 };
|
return { ta1Acks: nextTa1Acks, ta1AckOrder: nextOrder };
|
||||||
}),
|
}),
|
||||||
|
|||||||
Reference in New Issue
Block a user