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`. */