|
1 | 1 | import type { PaginatedResponse } from "../types" |
2 | 2 |
|
| 3 | +function toPositiveInt(value: number): number { |
| 4 | + if (!Number.isFinite(value)) return 1 |
| 5 | + const floored = Math.floor(value) |
| 6 | + return floored < 1 ? 1 : floored |
| 7 | +} |
| 8 | + |
3 | 9 | /** |
4 | 10 | * Paginate an array of items. |
5 | 11 | * |
6 | | - * @param items Full array of items |
7 | | - * @param page 1-indexed page number |
8 | | - * @param size Number of items per page |
| 12 | + * `page` and `size` are truncated to integers and clamped to a minimum of 1. |
| 13 | + * Non-finite values (`NaN`, `Infinity`, `-Infinity`) and values below 1 fall |
| 14 | + * back to 1, so untrusted input such as `Number(query.page)` can never leak |
| 15 | + * `NaN` into the response. |
9 | 16 | * |
10 | | - * TODO: implement this function — it is currently a stub. |
11 | | - * The test in packages/shared/test/pagination.test.ts exercises the full contract. |
| 17 | + * @param items Full array of items |
| 18 | + * @param page 1-indexed page number; defaults to 1 if non-finite or < 1 |
| 19 | + * @param size Number of items per page; defaults to 1 if non-finite or < 1 |
12 | 20 | */ |
13 | 21 | export function paginate<T>(items: T[], page: number, size: number): PaginatedResponse<T> { |
14 | | - throw new Error("not implemented") |
| 22 | + const total = items.length |
| 23 | + const pageSize = toPositiveInt(size) |
| 24 | + const currentPage = toPositiveInt(page) |
| 25 | + const totalPages = Math.ceil(total / pageSize) |
| 26 | + const start = (currentPage - 1) * pageSize |
| 27 | + |
| 28 | + return { |
| 29 | + data: items.slice(start, start + pageSize), |
| 30 | + page: currentPage, |
| 31 | + pageSize, |
| 32 | + total, |
| 33 | + totalPages, |
| 34 | + } |
15 | 35 | } |
0 commit comments