From 332ea2659ce94bd1e10baf2ec789422e621880d5 Mon Sep 17 00:00:00 2001 From: caballeto Date: Sun, 23 Aug 2026 13:06:04 +0200 Subject: [PATCH] fix: accept nextCursor on TableValueResult list envelopes Offset list commands were using a strict page parser that rejected the additive nextCursor field the API now always sends. Co-authored-by: Cursor --- src/lib/response-validation.ts | 7 ++++++- src/lib/typed-api.ts | 4 ++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/lib/response-validation.ts b/src/lib/response-validation.ts index 1c13aa3..e6e573e 100644 --- a/src/lib/response-validation.ts +++ b/src/lib/response-validation.ts @@ -52,9 +52,12 @@ export interface Page { hasPrev: boolean totalElements: number | null totalPages: number | null + nextCursor: string | null } export function parsePage(schema: ZodType, data: unknown, context = 'Response'): Page { + // nextCursor is additive on TableValueResult. Passthrough so a new + // envelope field cannot break list commands (Postel's Law). const envelope = z .object({ data: z.array(schema), @@ -62,8 +65,9 @@ export function parsePage(schema: ZodType, data: unknown, context = 'Respo hasPrev: z.boolean(), totalElements: z.number().int().nullable().optional(), totalPages: z.number().int().nullable().optional(), + nextCursor: z.string().nullable().optional(), }) - .strict() + .passthrough() const out = parse(envelope, data, `${context}: invalid TableValueResult envelope`) return { data: out.data, @@ -71,6 +75,7 @@ export function parsePage(schema: ZodType, data: unknown, context = 'Respo hasPrev: out.hasPrev, totalElements: out.totalElements ?? null, totalPages: out.totalPages ?? null, + nextCursor: out.nextCursor ?? null, } } diff --git a/src/lib/typed-api.ts b/src/lib/typed-api.ts index 6452248..5255795 100644 --- a/src/lib/typed-api.ts +++ b/src/lib/typed-api.ts @@ -38,8 +38,8 @@ export async function fetchPaginated( /** * Schema-validated variant of `fetchPaginated`. * - * Each page is parsed through `parsePage` (envelope `.strict()` — P1) so an - * unknown top-level field on the `TableValueResult` envelope, or a per-item + * Each page is parsed through `parsePage` (envelope `.passthrough()` so + * additive fields like `nextCursor` cannot break list commands; per-item * shape mismatch, raises a typed `ValidationError` rather than flowing * silently into `display()`. Continues paginating until `hasNext === false`. */