Files
2026-05-27 23:47:53 +01:00

93 lines
12 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# @suaveplan/pagination - Universal pagination primitives: cursor, offset, keyset strategies with adapters, iterators, and type-safe request/response shapes.
## Summary
Universal pagination primitives for cursor-based, offset-based, and keyset-based (seek) pagination strategies. The package covers the full pagination surface: request normalisation, response envelope construction, opaque cursor and page-token encoding, async iterators that drain pages automatically, a pluggable adapter interface for any data source, and optional OpenTelemetry instrumentation. All three strategies share the same error hierarchy and Zod schemas so they compose cleanly in multi-strategy APIs.
<!-- begin:auto-docs -->
## Documentation tree
- [adapters](./src/adapters/database-adapter.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [database-adapter](./src/adapters/database-adapter.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [cursor](./src/cursor/cursor.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [cursor-pagination](./src/cursor/cursor-pagination.md) — The `cursor-pagination` module provides base64url cursor encoding/decoding and adapter-backed forward/backward fetch op…
- [cursor](./src/cursor/cursor.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [codes](./src/errors/codes.md) — Defines and registers all stable error codes for the `@suaveplan/pagination` package using `defineCodes` from `@suavepl…
- [pagination-error](./src/errors/pagination-error.md) — `PaginationError` is the structured error base class for all failures originating within `@suaveplan/pagination`. It ex…
- [helpers](./src/helpers/helpers.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [helpers](./src/helpers/helpers.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [keyset](./src/keyset/keyset-pagination.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [keyset-pagination](./src/keyset/keyset-pagination.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [offset](./src/offset/offset.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [offset](./src/offset/offset.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [otel](./src/otel/pagination-otel.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [pagination-otel](./src/otel/pagination-otel.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [tokens](./src/tokens/page-tokens.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [page-tokens](./src/tokens/page-tokens.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
<!-- end:auto-docs --> - [database-adapter](./src/adapters/database-adapter.md) — <!-- begin:auto-docs --> _(no submodules)_- [cursor](./src/cursor/cursor.md) — <!-- begin:auto-docs --> _(no submodules)_- [cursor-pagination](./src/cursor/cursor-pagination.md) — The `cursor-pagination` module provides base64url cursor encoding/decoding and adapter-backed forward/backward fetch op…
- [cursor](./src/cursor/cursor.md) — <!-- begin:auto-docs --> _(no submodules)_- [codes](./src/errors/codes.md) — Defines and registers all stable error codes for the `@suaveplan/pagination` package using `defineCodes` from `@suavepl…
- [pagination-error](./src/errors/pagination-error.md) — `PaginationError` is the structured error base class for all failures originating within `@suaveplan/pagination`. It ex…
- [helpers](./src/helpers/helpers.md) — <!-- begin:auto-docs --> _(no submodules)_- [helpers](./src/helpers/helpers.md) — <!-- begin:auto-docs --> _(no submodules)_- [keyset](./src/keyset/keyset-pagination.md) — <!-- begin:auto-docs --> _(no submodules)_- [keyset-pagination](./src/keyset/keyset-pagination.md) — <!-- begin:auto-docs --> _(no submodules)_- [offset](./src/offset/offset.md) — <!-- begin:auto-docs --> _(no submodules)_- [offset](./src/offset/offset.md) — <!-- begin:auto-docs --> _(no submodules)_- [otel](./src/otel/pagination-otel.md) — <!-- begin:auto-docs --> _(no submodules)_- [pagination-otel](./src/otel/pagination-otel.md) — <!-- begin:auto-docs --> _(no submodules)_- [tokens](./src/tokens/page-tokens.md) — <!-- begin:auto-docs --> _(no submodules)_- [page-tokens](./src/tokens/page-tokens.md) — <!-- begin:auto-docs --> _(no submodules)_- [database-adapter](./src/adapters/database-adapter.md) — <!-- begin:auto-docs --> _(no submodules)_- [cursor](./src/cursor/cursor.md) — <!-- begin:auto-docs --> _(no submodules)_- [cursor-pagination](./src/cursor/cursor-pagination.md) — The `cursor-pagination` module provides base64url cursor encoding/decoding and adapter-backed forward/backward fetch op…
- [cursor](./src/cursor/cursor.md) — <!-- begin:auto-docs --> _(no submodules)_- [codes](./src/errors/codes.md) — Defines and registers all stable error codes for the `@suaveplan/pagination` package using `defineCodes` from `@suavepl…
- [pagination-error](./src/errors/pagination-error.md) — `PaginationError` is the structured error base class for all failures originating within `@suaveplan/pagination`. It ex…
- [helpers](./src/helpers/helpers.md) — <!-- begin:auto-docs --> _(no submodules)_- [helpers](./src/helpers/helpers.md) — <!-- begin:auto-docs --> _(no submodules)_- [keyset](./src/keyset/keyset-pagination.md) — <!-- begin:auto-docs --> _(no submodules)_- [keyset-pagination](./src/keyset/keyset-pagination.md) — <!-- begin:auto-docs --> _(no submodules)_- [offset](./src/offset/offset.md) — <!-- begin:auto-docs --> _(no submodules)_- [offset](./src/offset/offset.md) — <!-- begin:auto-docs --> _(no submodules)_- [otel](./src/otel/pagination-otel.md) — <!-- begin:auto-docs --> _(no submodules)_- [pagination-otel](./src/otel/pagination-otel.md) — <!-- begin:auto-docs --> _(no submodules)_- [tokens](./src/tokens/page-tokens.md) — <!-- begin:auto-docs --> _(no submodules)_- [page-tokens](./src/tokens/page-tokens.md) — <!-- begin:auto-docs --> _(no submodules)_- [database-adapter](./src/adapters/database-adapter.md) — <!-- begin:auto-docs --> _(no submodules)_- [cursor](./src/cursor/cursor.md) — <!-- begin:auto-docs --> _(no submodules)_- [cursor-pagination](./src/cursor/cursor-pagination.md) — The `cursor-pagination` module provides base64url cursor encoding/decoding and adapter-backed forward/backward fetch op…
- [cursor](./src/cursor/cursor.md) — <!-- begin:auto-docs --> _(no submodules)_- [codes](./src/errors/codes.md) — Defines and registers all stable error codes for the `@suaveplan/pagination` package using `defineCodes` from `@suavepl…
- [pagination-error](./src/errors/pagination-error.md) — `PaginationError` is the structured error base class for all failures originating within `@suaveplan/pagination`. It ex…
- [helpers](./src/helpers/helpers.md) — <!-- begin:auto-docs --> _(no submodules)_- [helpers](./src/helpers/helpers.md) — <!-- begin:auto-docs --> _(no submodules)_- [keyset](./src/keyset/keyset-pagination.md) — <!-- begin:auto-docs --> _(no submodules)_- [keyset-pagination](./src/keyset/keyset-pagination.md) — <!-- begin:auto-docs --> _(no submodules)_- [offset](./src/offset/offset.md) — <!-- begin:auto-docs --> _(no submodules)_- [offset](./src/offset/offset.md) — <!-- begin:auto-docs --> _(no submodules)_- [otel](./src/otel/pagination-otel.md) — <!-- begin:auto-docs --> _(no submodules)_- [pagination-otel](./src/otel/pagination-otel.md) — <!-- begin:auto-docs --> _(no submodules)_- [tokens](./src/tokens/page-tokens.md) — <!-- begin:auto-docs --> _(no submodules)_- [page-tokens](./src/tokens/page-tokens.md) — <!-- begin:auto-docs --> _(no submodules)_- [database-adapter](./src/adapters/database-adapter.md) — <!-- begin:auto-docs --> _(no submodules)_- [cursor](./src/cursor/cursor.md) — <!-- begin:auto-docs --> _(no submodules)_ - [cursor-pagination](./src/cursor/cursor-pagination.md) — The `cursor-pagination` module provides base64url cursor encoding/decoding and adapter-backed forward/backward fetch op…
- [cursor](./src/cursor/cursor.md) — <!-- begin:auto-docs --> _(no submodules)_- [codes](./src/errors/codes.md) — Defines and registers all stable error codes for the `@suaveplan/pagination` package using `defineCodes` from `@suavepl…
- [pagination-error](./src/errors/pagination-error.md) — `PaginationError` is the structured error base class for all failures originating within `@suaveplan/pagination`. It ex…
- [helpers](./src/helpers/helpers.md) — <!-- begin:auto-docs --> _(no submodules)_ - [helpers](./src/helpers/helpers.md) — <!-- begin:auto-docs --> _(no submodules)_- [keyset](./src/keyset/keyset-pagination.md) — <!-- begin:auto-docs --> _(no submodules)_ - [keyset-pagination](./src/keyset/keyset-pagination.md) — <!-- begin:auto-docs --> _(no submodules)_- [offset](./src/offset/offset.md) — <!-- begin:auto-docs --> _(no submodules)_ - [offset](./src/offset/offset.md) — <!-- begin:auto-docs --> _(no submodules)_- [otel](./src/otel/pagination-otel.md) — <!-- begin:auto-docs --> _(no submodules)_ - [pagination-otel](./src/otel/pagination-otel.md) — <!-- begin:auto-docs --> _(no submodules)_- [tokens](./src/tokens/page-tokens.md) — <!-- begin:auto-docs --> _(no submodules)_ - [page-tokens](./src/tokens/page-tokens.md) — <!-- begin:auto-docs --> _(no submodules)_
## Install
```bash
bun add @suaveplan/pagination
```
## Quick start
```ts
import { paginateArray } from "@suaveplan/pagination/offset";
import { cursorPaginateArray } from "@suaveplan/pagination/cursor";
import { createOffsetIterator, collectAll } from "@suaveplan/pagination/helpers";
// Offset: slice page 2, 10 items per page
const items = Array.from({ length: 50 }, (_, i) => ({ id: i + 1 }));
const page2 = paginateArray(items, 2, 10);
// page2.data → items 11–20
// page2.meta.totalPages → 5, page2.meta.hasNext → true
// Cursor: paginate an in-memory array by item.id
const users = [{ id: "a" }, { id: "b" }, { id: "c" }, { id: "d" }];
const first = cursorPaginateArray(users, { limit: 2 }, (u) => u.id);
// first.meta.endCursor → "b", first.meta.hasNextPage → true
const second = cursorPaginateArray(
users,
{ cursor: first.meta.endCursor ?? undefined, limit: 2 },
(u) => u.id
);
// second.data → [{ id: "c" }, { id: "d" }]
// Helpers: drain all pages into a flat array
const iter = createOffsetIterator(
(page, limit) => Promise.resolve(paginateArray(items, page, limit)),
{ limit: 10 }
);
const all = await collectAll(iter); // → all 50 items
```
## Modules
| Module | Description |
|--------|-------------|
| [src/cursor/cursor.md](src/cursor/cursor.md) | In-memory cursor pagination building blocks: `cursorPaginateArray`, `createEdges`, `createConnection`, `calculateCursorMeta`, `normalizeCursorRequest` |
| [src/cursor/cursor-pagination.md](src/cursor/cursor-pagination.md) | Adapter-backed cursor pagination: `encodeCursor`, `decodeCursor`, `validateCursor`, `fetchAfter`, `fetchBefore` |
| [src/offset/offset.md](src/offset/offset.md) | Offset/page-number pagination: `paginateArray`, `normalizeOffsetRequest`, `calculateOffsetMeta`, `pageToOffset`, `offsetToPage`, `getPageRange`, `estimateTotalCount` |
| [src/keyset/keyset-pagination.md](src/keyset/keyset-pagination.md) | Seek-based keyset pagination: `keysetPaginate`, `fetchKeysetPage`, `encodeKeysetCursor`, `decodeKeysetCursor`, `extractSortKeys` |
| [src/helpers/helpers.md](src/helpers/helpers.md) | Higher-order iteration utilities: `createOffsetIterator`, `createCursorIterator`, `collectAll`, `collectUpTo`, `mapPaginated`, `mergeOffsetResponses` |
| [src/adapters/database-adapter.md](src/adapters/database-adapter.md) | `PaginationAdapter` interface, `MemoryPaginationAdapter`, and `createMemoryAdapter` factory |
| [src/tokens/page-tokens.md](src/tokens/page-tokens.md) | Opaque page tokens with sort and filter metadata: `createCursorToken`, `createOffsetToken`, `encodePageToken`, `decodePageToken`, `hashFilters`, `validateTokenFilter` |
| [src/otel/pagination-otel.md](src/otel/pagination-otel.md) | OTel instrumentation: `getPaginationInstruments` with request counter, page-size histogram, and encode/decode histogram; graceful no-op fallback |
| [src/errors/pagination-error.md](src/errors/pagination-error.md) | `PaginationError` — structured error class extending `SuaveplanError` with `PAGINATION.*` codes |
| [src/errors/codes.md](src/errors/codes.md) | `PAGINATION_CODES` constant and `PaginationCode` union type |