/** * Sentry API pagination utilities. * * Sentry uses cursor-based pagination via HTTP Link headers. * These helpers make it ergonomic to paginate through results * returned by the generated SDK functions. */ import { type SdkResult } from "./sentry-errors"; export type UnwrappedResult = { data: TData; response: Response; }; export type PaginatedResponse = { data: T; /** Cursor for the next page. `undefined` when there are no more pages. */ nextCursor?: string; /** Cursor for the previous page. `undefined` on the first page. */ prevCursor?: string; }; export type PaginateAllOptions = { /** Hard cap on the number of pages fetched. Default: 50. */ maxPages?: number; }; export type PaginateUpToOptions = { /** Hard cap on the number of items returned. Required. */ limit: number; /** Safety cap on the number of pages fetched. Default: 50. */ maxPages?: number; /** Resume pagination from this cursor instead of starting from the beginning. */ startCursor?: string; /** Called after each page is fetched. Useful for progress indicators. */ onPage?: (fetched: number, limit: number) => void; /** * When true, preserve `nextCursor` even when the last page was trimmed * to fit `limit`. Default: false (the safe default — see body comment). * * Use this only for endpoints that have **no** server-side per-page * control (so the trimmed tail items remain reachable via the same * cursor on the next call). Sentry's `/issues/{id}/events/` is one * such endpoint: it has no `per_page` param, so dropping the cursor * on overshoot would orphan the items the helper trimmed. * * For endpoints that DO support `per_page` / `limit`, leave this * `false` — returning a cursor that points past trimmed items would * cause callers resuming pagination to skip records. */ keepCursorOnOvershoot?: boolean; }; export type PageFetcher = (cursor: string | undefined) => Promise>; /** * Parse Sentry's Link header to extract pagination cursors. * * Sentry returns Link headers in the format: * ; rel="previous"; results="true"; cursor="abc:0:1";, * ; rel="next"; results="true"; cursor="1234:0:0"; * * Returns `{ nextCursor?, prevCursor? }`: * - `nextCursor` set when there is a next page. * - `prevCursor` set when there is a previous page. * * The `results="true"` qualifier is required — Sentry includes a * `previous` rel even on the first page, but with `results="false"`. * We honor that signal so first-page callers don't see a bogus `prevCursor`. */ export declare const parseSentryLinkHeader: (header: string | null) => { nextCursor?: string; prevCursor?: string; }; /** * Internal: merge a managed `cursor` into an SDK call's `options.query` * and re-shape the result back to the SDK's `Options` type. * * Used exclusively by the auto-generated wrappers in `pagination.gen.ts` * (one call per wrapper kind, one `_withCursor` invocation per page). * Centralizes the cast chain — every wrapper used to inline its own * `as unknown as ...` quartet, which meant the same logic was repeated * once per generated wrapper (~115 places). This helper makes that * exactly one place. * * Type-erasure rationale: each SDK operation has its own `Options` * shape with operation-specific `query`, `path`, and `body` types. We * can't write a generic that's tight enough to satisfy all 200+ SDK * functions structurally without committing to a discriminated-union * encoding of every operation. The `_` prefix marks this as internal — * the typed wrappers in `pagination.gen.ts` are the supported public API. * * @internal */ export declare const _withCursor: (options: { query?: unknown; [k: string]: unknown; }, cursor: string | undefined) => TOptions; /** * Unwrap an SDK result, throwing on error. * * Returns `{ data, response }` so callers retain access to the * raw Response (and its headers) for pagination or other needs. * * The thrown value is a {@link SentryApiError}, so a `catch` block can still * read `err.status` / `err.body` to decide how to handle the failure. */ export declare const unwrapResult: (result: SdkResult, context: string) => UnwrappedResult; /** * Unwrap an SDK result and extract pagination cursors from the * Link header. Throws on error. * * Returns `{ data, nextCursor?, prevCursor? }`. Each cursor is * `undefined` when the corresponding rel does not exist or has * `results="false"`. */ export declare const unwrapPaginatedResult: (result: SdkResult, context: string) => PaginatedResponse; /** * Fetch a single page from a Sentry list endpoint and return both * the data and the pagination cursors. * * Thin wrapper over an SDK function call: invokes the fetcher with * an optional cursor, unwraps the result, and parses the Link header. * * Useful when you want manual control over pagination (e.g. exposing * a "next page" button in a UI) instead of fetching all pages eagerly. * * @example * ```ts * const { data, nextCursor } = await fetchPage( * (cursor) => listAnOrganization_sRepositories({ * path: { organization_id_or_slug: 'my-org' }, * query: { cursor }, * }), * 'listRepos', * ); * ``` */ export declare const fetchPage: (fetcher: PageFetcher, context: string, cursor?: string) => Promise>; /** * Automatically paginate through all pages of a Sentry list endpoint. * * Fetches pages sequentially until there is no next cursor or * `maxPages` is reached (default: 50). Returns all items concatenated. * * @example * ```ts * const allRepos = await paginateAll( * (cursor) => listAnOrganization_sRepositories({ * path: { organization_id_or_slug: 'my-org' }, * query: { cursor }, * }), * 'listRepos', * ); * ``` */ export declare const paginateAll: (fetcher: PageFetcher, TError>, context: string, options?: PaginateAllOptions) => Promise>; /** * Paginate up to a hard limit of items, suppressing the next-cursor * if the last fetched page had to be trimmed to fit. * * The trim-and-suppress behavior is intentional: returning a cursor * that points past the trimmed items would cause callers resuming * pagination to skip records. When the requested limit is reached * mid-page, no `nextCursor` is returned and the caller should treat * the result as the final page they're going to fetch. * * @example * ```ts * // Fetch up to 250 issues, in pages of 100 (Sentry's API max) * const { data, nextCursor } = await paginateUpTo( * (cursor) => listAnOrganization_sIssues({ * path: { organization_id_or_slug: 'my-org' }, * query: { cursor, limit: 100 }, * }), * { limit: 250 }, * 'listIssues', * ); * ``` */ export declare const paginateUpTo: (fetcher: PageFetcher, TError>, options: PaginateUpToOptions, context: string) => Promise<{ data: Array; nextCursor?: string; }>;