/** * Reads the page a list route's query string asks for, and cuts the rows a * service fetched into that page. The routes send the page as * `PaginatedData`. * * @module */ import { PAGINATION_DEFAULT_LIMIT, PAGINATION_MAX_LIMIT } from "../config.ts"; /** * Reads the `page` and `limit` query parameters of a list route. It never * fails: a missing or invalid `page` reads as 1, and `limit` is kept between 1 * and {@link PAGINATION_MAX_LIMIT}. */ export function parsePagination( params: URLSearchParams, defaultLimit = PAGINATION_DEFAULT_LIMIT, ): { page: number; limit: number } { const page = Math.max( 1, parseInt(params.get("page") ?? "1") || 1, ); const limit = Math.min( Math.max( 1, parseInt(params.get("limit") ?? String(defaultLimit)) || defaultLimit, ), PAGINATION_MAX_LIMIT, ); return { page, limit }; } /** * Returned to the routes by the services' list functions, and sent as * `PaginatedData`. */ export interface Page { items: T[]; hasMore: boolean; } /** * Turns rows fetched with `LIMIT limit + 1` into a page. The extra row, when it * comes back, is dropped and tells that another page follows, which spares a * COUNT(*) over the whole list. */ export function pageOf(rows: T[], limit: number): Page { return rows.length > limit ? { items: rows.slice(0, limit), hasMore: true } : { items: rows, hasMore: false }; }