UNPKG

splitwise

Version:

A TypeScript SDK for the Splitwise API.

95 lines 4 kB
"use strict"; /** * Pagination helper for Splitwise list endpoints. * * Splitwise uses a simple `limit`/`offset` scheme with no continuation tokens, * so a "page" is exhausted when the server returns fewer rows than requested * (or none at all). `PagedResult` wraps that loop in a value that is: * - awaitable: `await result` resolves to the first page's array (sends the * user's `limit` as-is so the server's default applies when omitted) * - async-iterable: `for await (const item of result)` yields every item * - page-iterable: `for await (const page of result.byPage())` yields arrays * * Iteration needs a known page size to detect end-of-data, so when iterating * without an explicit `limit` the SDK uses `ITERATION_PAGE_SIZE` for batching. * * The first page fetched via `await` is cached, so repeated awaits don't * re-hit the network. Iteration always starts a fresh sequence from the * configured offset. */ Object.defineProperty(exports, "__esModule", { value: true }); exports.createPagedResult = createPagedResult; const ITERATION_PAGE_SIZE = 100; const DEFAULT_OFFSET = 0; function createPagedResult(http, path, unwrapKey, options) { const userLimit = options?.limit; const startOffset = options?.offset ?? DEFAULT_OFFSET; const extraQuery = options?.query ?? {}; // Reject nonsensical limits early. Without this guard, `limit: 0` would // produce an infinite loop in pageIterator (page.length < 0 is never true, // so the termination check never fires and we just keep requesting empty // pages from offset 0 forever). if (userLimit !== undefined && (!Number.isInteger(userLimit) || userLimit <= 0)) { throw new RangeError(`Pagination limit must be a positive integer, got ${userLimit}`); } if (!Number.isInteger(startOffset) || startOffset < 0) { throw new RangeError(`Pagination offset must be a non-negative integer, got ${startOffset}`); } const overrides = { ...(options?.signal !== undefined && { signal: options.signal }), ...(options?.timeout !== undefined && { timeout: options.timeout }), ...(options?.maxRetries !== undefined && { maxRetries: options.maxRetries }), ...(options?.baseUrl !== undefined && { baseUrl: options.baseUrl }), }; const fetchPage = (offset, limit) => http.get(path, { query: { ...extraQuery, ...(limit !== undefined && { limit }), offset, }, unwrapKey, ...overrides, }); // Cache the first page so repeated `await result` calls don't re-fetch. // The await path sends the user's limit as-is (no client-side default). let firstPagePromise = null; const getFirstPage = () => { if (firstPagePromise === null) { firstPagePromise = fetchPage(startOffset, userLimit); } return firstPagePromise; }; async function* pageIterator() { // Iteration needs a known page size to detect end-of-data. const pageSize = userLimit ?? ITERATION_PAGE_SIZE; let offset = startOffset; while (true) { const page = await fetchPage(offset, pageSize); if (page.length > 0) { yield page; } // Stop when the server returns a short or empty page; in either case // there's nothing more to fetch. if (page.length < pageSize) return; offset += pageSize; } } async function* itemIterator() { for await (const page of pageIterator()) { for (const item of page) { yield item; } } } return { then(onfulfilled, onrejected) { return getFirstPage().then(onfulfilled, onrejected); }, byPage() { return { [Symbol.asyncIterator]: pageIterator }; }, [Symbol.asyncIterator]: itemIterator, }; } //# sourceMappingURL=pagination.js.map