/** * @license * Copyright 2025 Steven Roussey * SPDX-License-Identifier: Apache-2.0 */ /** * Opaque cursor token returned by paginated queries. * * Encodes the position of the last seen row so the next page can resume * from there using keyset (a.k.a. seek) pagination. Callers must treat * cursors as opaque — the encoding is an implementation detail. * * Named `PageCursor` (not `Cursor`) to avoid colliding with the unrelated * generic iterator type `Cursor` exported from `@workglow/util`. */ export type PageCursor = string & { readonly __pageCursorBrand: unique symbol; }; /** * Internal cursor payload. * * - `v` is a format version (currently 1) so older cursors can be rejected * if the encoding ever changes. * - `n` (names), `d` (directions, `"a"` ASC / `"d"` DESC), and `c` (column * values) are parallel arrays describing the effective ordering of the * row the cursor is parked on: caller-supplied `orderBy` columns first, * then any primary-key columns not already covered by `orderBy` as a * tiebreaker. The flat layout means columns that appear in both the * user's `orderBy` and the primary key are stored exactly once (avoids * double-encoding the value in DESC-by-PK pagination). Storing column * names AND directions lets us reject cursors handed back with a * different ordering — same-name + flipped direction would otherwise * compute the next page with the wrong comparison operators. */ export interface CursorPayload { readonly v: 1; readonly n: ReadonlyArray; readonly d: ReadonlyArray<"a" | "d">; readonly c: ReadonlyArray; } /** Cursor format version recognised by this build. */ export declare const CURSOR_VERSION: 1; /** * Maximum accepted cursor length, in characters of the encoded form. * Real cursors are well under 1 KB (a few primary-key values plus an * orderBy worth of column names); the cap exists so a hostile client * can't force the server to base64-decode and JSON-parse arbitrarily * large strings, which would be cheap memory/CPU abuse. */ export declare const MAX_CURSOR_LENGTH: number; /** * Encodes a cursor payload as an opaque, URL-safe string. * * The transport is base64url(JSON(payload)). The format is intentionally * not documented for callers — it's an implementation detail and may be * tightened in future versions (signed, length-limited, etc.). */ export declare function encodeCursor(payload: CursorPayload): PageCursor; /** * Decodes an opaque cursor string into its payload. * * @throws {StorageValidationError} when the cursor is malformed, exceeds * {@link MAX_CURSOR_LENGTH}, or the format version is unknown. Callers * should surface these as 4xx errors rather than retrying. */ export declare function decodeCursor(cursor: PageCursor | string): CursorPayload; /** * Validates that a decoded cursor matches the effective ordering the * current request would use, by exact column-name AND direction sequence. * A mismatch usually means the caller switched ordering mid-iteration — * e.g. asked for `orderBy: [a ASC]` on page 1 and `orderBy: [a DESC]` on * page 2, or swapped to a different column entirely — both of which would * produce nonsensical results that arity matching alone can't catch. */ export declare function assertCursorMatches(payload: CursorPayload, effectiveOrder: ReadonlyArray<{ readonly column: string; readonly direction: "ASC" | "DESC"; }>): void; //# sourceMappingURL=Cursor.d.ts.map