Initial Commit
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
# Pagination Adapters
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
## Purpose
|
||||
|
||||
The adapters module defines the `PaginationAdapter<T>` interface that all three pagination strategies — cursor, offset, and keyset — consume for data access. It also ships `MemoryPaginationAdapter<T>`, a pure in-memory reference implementation that stores items in a plain array and supports equality-based filtering and single-field sorting. The adapter abstraction decouples pagination logic from the underlying data layer, so swapping from an in-memory store to a SQL database or HTTP API requires implementing just two methods.
|
||||
|
||||
## Features
|
||||
|
||||
- `PaginationAdapter<T>` interface — two methods: `fetchPage` (paginated fetch) and `count` (total matching items).
|
||||
- `MemoryPaginationAdapter<T>` — in-memory implementation with `addItems`, `setItems`, `getAll`, `fetchPage`, and `count`.
|
||||
- `createMemoryAdapter` — factory function that creates a pre-populated `MemoryPaginationAdapter`.
|
||||
- `createSortConfig` — convenience builder for `SortConfig` values.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```typescript
|
||||
import { createMemoryAdapter, createSortConfig } from "@suaveplan/pagination/adapters";
|
||||
|
||||
const adapter = createMemoryAdapter([
|
||||
{ id: 1, name: "Alice", age: 30 },
|
||||
{ id: 2, name: "Bob", age: 25 },
|
||||
{ id: 3, name: "Carol", age: 35 },
|
||||
]);
|
||||
|
||||
const sort = createSortConfig("name", "asc");
|
||||
const page = await adapter.fetchPage(0, 2, sort);
|
||||
// page.items → [{ id: 1, name: "Alice" }, { id: 2, name: "Bob" }]
|
||||
// page.total → 3
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```typescript
|
||||
import { createMemoryAdapter } from "@suaveplan/pagination/adapters";
|
||||
import { fetchAfter, encodeCursor } from "@suaveplan/pagination/cursor";
|
||||
|
||||
const adapter = createMemoryAdapter(
|
||||
Array.from({ length: 50 }, (_, i) => ({ id: i + 1, score: i * 2 }))
|
||||
);
|
||||
|
||||
// Count with filter
|
||||
const activeCount = await adapter.count({ score: 10 });
|
||||
|
||||
// Use with cursor pagination
|
||||
const page = await fetchAfter(adapter, undefined, { limit: 5, sortBy: "id" }, (i) => i.id);
|
||||
// page.items → first 5 items sorted by id
|
||||
|
||||
// Replace dataset at runtime
|
||||
adapter.setItems([{ id: 99, score: 0 }]);
|
||||
const count = await adapter.count();
|
||||
// → 1
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `interface PaginationAdapter<T>`
|
||||
|
||||
| Method | Signature |
|
||||
|-------------|------------------------------------------------------------------------------|
|
||||
| `fetchPage` | `(offset, limit, sort?, filter?) => Promise<PageResult<T>>` |
|
||||
| `count` | `(filter?) => Promise<number>` |
|
||||
|
||||
`fetchPage` must return items in the requested sort order and the total matching count in `PageResult.total`.
|
||||
|
||||
### `class MemoryPaginationAdapter<T>`
|
||||
|
||||
| Method | Description |
|
||||
|-------------|-----------------------------------------------------|
|
||||
| `constructor(items?)` | Initialises with a shallow copy of `items`. |
|
||||
| `addItems(items)` | Appends items to the store. |
|
||||
| `setItems(items)` | Replaces the entire store. |
|
||||
| `getAll()` | Returns a shallow copy of all items. |
|
||||
| `fetchPage(...)` | Applies filter, sorts, slices, returns `PageResult`. |
|
||||
| `count(filter?)` | Returns the count of items matching the filter. |
|
||||
|
||||
### `createMemoryAdapter<T>(items?): MemoryPaginationAdapter<T>`
|
||||
|
||||
Factory that creates a `MemoryPaginationAdapter` pre-populated with `items` (default: empty array).
|
||||
|
||||
### `createSortConfig(field, direction?): SortConfig`
|
||||
|
||||
Builds a `{ field, direction }` sort config. Direction defaults to `"asc"`.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
Filtering uses strict equality (`===`) on top-level fields only — nested field access is not supported in the reference implementation. Sorting uses `localeCompare` for strings and arithmetic comparison for numbers; null values sort last. The `items` array is always stored as a shallow copy to prevent external mutation from affecting the adapter state. Class members are `protected` (not `private`) to enable subclassing in tests without re-implementing the full interface.
|
||||
Reference in New Issue
Block a user