/** * @db4/core - Cursor-Based Pagination * * Implements Relay-style cursor-based pagination with support for: * - Forward pagination (first/after) * - Backward pagination (last/before) * - Multi-field compound cursors * - Cursor validation and security * - URL-safe base64 encoding * - Cryptographic HMAC-SHA256 signing * * @packageDocumentation */ import type { StoredDocument, QueryOptions } from './types.js'; import { CursorError, CursorErrorCode } from './errors.js'; export { CursorError, CursorErrorCode }; /** * Cursor position representing a point in the dataset. */ export interface CursorPosition { /** Sort field values at this position */ sortValues: (string | number | boolean | null)[]; /** Document ID for tie-breaking */ id: string; /** Pagination direction */ direction: 'forward' | 'backward'; /** Cursor version for schema migration (accessed via hidden property) */ readonly version?: number; } /** * Result of cursor validation. */ export interface CursorValidation { /** Whether the cursor is valid */ isValid: boolean; /** Decoded position if valid */ position?: CursorPosition; /** Error details if invalid */ error?: { code: CursorErrorCode; message: string; }; } /** * Page information for paginated results. */ export interface PageInfo { /** Whether there are more items after */ hasNextPage: boolean; /** Whether there are more items before */ hasPreviousPage: boolean; /** Cursor of the first item */ startCursor: string | null; /** Cursor of the last item */ endCursor: string | null; /** Total count (only if requested) */ totalCount?: number | undefined; } /** * Edge in the connection. */ export interface Edge { /** The document node */ node: T; /** Cursor for this edge */ cursor: string; } /** * Paginated result following Relay spec. */ export interface PaginatedResult { /** Edges containing nodes and cursors */ edges: Edge[]; /** Page information */ pageInfo: PageInfo; } /** * Options for cursor pagination. */ export interface CursorOptions { /** Version for cursor schema migration */ version?: number; /** TTL in milliseconds for cursor expiry */ cursorTTL?: number; /** Secret for cursor signing */ secret?: string; /** * Field name used for document ID. * Defaults to 'id' for @db4/core documents. * Set to '$id' for IceType documents. */ idField?: string; } /** * Options for pagination request. */ export interface PaginateOptions { /** Number of items to fetch (forward) */ first?: number | undefined; /** Number of items to fetch (backward) */ last?: number | undefined; /** Cursor to fetch after */ after?: string | undefined; /** Cursor to fetch before */ before?: string | undefined; /** Fields to sort by */ sortFields: string[]; /** Sort directions (defaults to 'asc' for all) */ sortDirections?: ('asc' | 'desc')[] | undefined; /** Include total count in response */ includeTotalCount?: boolean | undefined; } /** * Options for building query options from cursor. */ export interface BuildQueryOptionsInput { /** Cursor to continue from */ cursor: string; /** Sort fields */ sortFields: string[]; /** Sort directions */ sortDirections: ('asc' | 'desc')[]; /** Number of items to fetch */ limit: number; } /** * Cursor pagination implementation. * * @example * ```typescript * // Basic usage (no signing) * const pagination = new CursorPagination(); * * // Paginate documents * const result = pagination.paginate(documents, { * first: 10, * sortFields: ['createdAt'], * sortDirections: ['desc'], * }); * * // Use cursors for next page * const nextPage = pagination.paginate(documents, { * first: 10, * after: result.pageInfo.endCursor, * sortFields: ['createdAt'], * sortDirections: ['desc'], * }); * * // Secure usage with HMAC-SHA256 signing * const securePagination = await CursorPagination.create({ secret: 'your-secret' }); * const cursor = await securePagination.encodeAsync(position); * const validation = await securePagination.validateAsync(cursor); * ``` */ export declare class CursorPagination { private version; private cursorTTL; private secret; private hmacKey; private idField; constructor(options?: CursorOptions); /** * Create a CursorPagination instance with cryptographic HMAC-SHA256 signing. * Use this factory method when you need secure cursor signing. * * @example * ```typescript * const pagination = await CursorPagination.create({ secret: 'your-secret-key' }); * const cursor = await pagination.encodeAsync(position); * const validation = await pagination.validateAsync(cursor); * ``` */ static create(options?: CursorOptions): Promise; /** * Encode a cursor position to a URL-safe string. * For cryptographic security, use encodeAsync() instead. */ encode(position: CursorPosition): string; /** * Encode a cursor position to a URL-safe string with cryptographic HMAC-SHA256 signing. * Requires the instance to be created with CursorPagination.create(). */ encodeAsync(position: CursorPosition): Promise; /** * Decode a cursor string back to a position. */ decode(cursor: string): CursorPosition & { version: number; }; /** * Validate a cursor string. * For cryptographic security, use validateAsync() instead. */ validate(cursor: string, options?: { checkExpiry?: boolean; currentTime?: number; }): CursorValidation; /** * Validate a cursor string with cryptographic HMAC-SHA256 verification. * Requires the instance to be created with CursorPagination.create(). */ validateAsync(cursor: string, options?: { checkExpiry?: boolean; currentTime?: number; }): Promise; /** * Paginate a list of documents. */ paginate(documents: T[], options: PaginateOptions): PaginatedResult; /** * Build query options from cursor for database queries. */ buildQueryOptions(input: BuildQueryOptionsInput): QueryOptions; private validatePaginateOptions; private buildComparator; private compareValues; private getFieldValue; /** * Get the document ID using the configured idField. * Supports both 'id' (core) and '$id' (IceType) conventions. */ private getDocId; private filterAfter; private filterBefore; private buildCursorFilters; private toBase64Url; private fromBase64Url; private btoa; private atob; private manualBtoa; private manualAtob; private maybeCompress; private maybeDecompress; private simpleCompress; private simpleDecompress; /** * Compute a cryptographic HMAC-SHA256 signature for cursor data. * This provides strong protection against cursor tampering. */ private computeHmacSignature; /** * Legacy synchronous checksum using FNV-1a hash. * @deprecated Use computeHmacSignature for cryptographic security. */ private computeChecksum; } /** * Creates a new cursor pagination instance. */ export declare function createCursorPagination(options?: CursorOptions): CursorPagination; //# sourceMappingURL=cursor.d.ts.map