Skip to content

Commit aae24c1

Browse files
committed
feat: add resolvePagination helper and add defaults for perPage helper
1 parent c306e4f commit aae24c1

5 files changed

Lines changed: 102 additions & 20 deletions

File tree

docs/api.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -404,7 +404,8 @@ Decorating a controller method with `@Bind()` enables typed injection of `Reques
404404
- `Hook` — process-local hook registry with `set`, `has`, `get`, `getAll`, `unset`, and `clear`.
405405
- `Publisher` — registry for `ark publish`: `publishes(group)`, `publishables(filter?)`, `confirm(confirmation)`, `confirmables(package | true)`, and `clear()`. See the [publish guide](/guide/cli#publish).
406406
- `getModel(name)` — typed app model resolver.
407-
- `perPage(query)` — pagination limit helper.
407+
- `perPage(query, defaults?)` — pagination limit helper.
408+
- `resolvePagination(query, defaults?)` — pagination limit,page helper.
408409

409410
### Custom error responses
410411

docs/guide/utilities/helpers.md

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,30 @@
22

33
Small utility functions that can be used across the application for common tasks.
44

5-
## `perPage(query)`
5+
## `perPage(query, defaults?)`
66

7-
Extracts a safe pagination limit from a query object. Clamps the result between `1` and `50`, defaulting to `15`.
7+
Extracts a safe pagination limit from a query object. Clamps the result between `1` and `50` or the configured default `maxPerPage`, defaulting to `25` or the configured default `perPage`.
88

99
```ts
1010
import { perPage } from '@arkstack/common';
1111

1212
const limit = perPage({ limit: 100 }); // 50 (clamped)
13-
const limit2 = perPage({}); // 15 (default)
13+
const limit2 = perPage({ limit: 100 }, { maxPerPage: 100 }); // 100
14+
const limit3 = perPage({}); // 25 (default)
15+
const limit3 = perPage({}, { perPage: 100 }); // 100
16+
```
17+
18+
## `resolvePagination(query, defaults?)`
19+
20+
Extracts the current page and a safe pagination limit from a query object. Clamps the resulting `perPage` between `1` and `50` or the configured default `maxPerPage`, defaulting to `25` or the configured default `perPage`.
21+
22+
```ts
23+
import { resolvePagination } from '@arkstack/common';
24+
25+
const limit = resolvePagination({ limit: 100 }); // {perPage: 50, page: 1} (clamped)
26+
const limi2 = resolvePagination({ limit: 100 }, { maxPerPage: 100 }); // {perPage: 100, page: 1}
27+
const limit3 = resolvePagination({}); // {perPage: 50, page: 1} (default)
28+
const limit4 = resolvePagination({}, { perPage: 100, page: 2 }); // {perPage: 100, page: 2}
1429
```
1530

1631
## `getModel(modelName)`

packages/common/README.md

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -739,15 +739,30 @@ Key exported types from the package:
739739

740740
**`src/utils/helpers.ts`**
741741

742-
#### `perPage(query)`
742+
#### `perPage(query, defaults?)`
743743

744-
Extracts a safe pagination limit from a query object. Clamps the result between `1` and `50`, defaulting to `15`.
744+
Extracts a safe pagination limit from a query object. Clamps the result between `1` and `50` or the configured default `maxPerPage`, defaulting to `25` or the configured default `perPage`.
745745

746746
```ts
747747
import { perPage } from '@arkstack/common';
748748

749749
const limit = perPage({ limit: 100 }); // 50 (clamped)
750-
const limit2 = perPage({}); // 15 (default)
750+
const limit2 = perPage({ limit: 100 }, { maxPerPage: 100 }); // 100
751+
const limit3 = perPage({}); // 25 (default)
752+
const limit3 = perPage({}, { perPage: 100 }); // 100
753+
```
754+
755+
#### `resolvePagination(query, defaults?)`
756+
757+
Extracts the current page and a safe pagination limit from a query object. Clamps the resulting `perPage` between `1` and `50` or the configured default `maxPerPage`, defaulting to `25` or the configured default `perPage`.
758+
759+
```ts
760+
import { resolvePagination } from '@arkstack/common';
761+
762+
const limit = resolvePagination({ limit: 100 }); // {perPage: 50, page: 1} (clamped)
763+
const limi2 = resolvePagination({ limit: 100 }, { maxPerPage: 100 }); // {perPage: 100, page: 1}
764+
const limit3 = resolvePagination({}); // {perPage: 50, page: 1} (default)
765+
const limit4 = resolvePagination({}, { perPage: 100, page: 2 }); // {perPage: 100, page: 2}
751766
```
752767

753768
#### `getModel(modelName)`

packages/common/src/types.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,11 @@ export type Choice<Value> = {
293293

294294
export type Choices = readonly string[] | readonly Choice<string>[];
295295

296+
export type PaginationOptions = {
297+
page: number
298+
perPage: number
299+
}
300+
296301
/**
297302
* A single source → destination mapping a package wants to publish into the
298303
* consuming application.

packages/common/src/utils/helpers.ts

Lines changed: 59 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { Arkstack } from '@arkstack/contract'
55
import { RequestException } from '../Exceptions/RequestException'
66

77
import { createRequire } from 'node:module'
8+
import { PaginationOptions } from '../types'
89

910
export type AbstractModelConstructor<TModel = unknown> =
1011
abstract new (attributes?: Record<string, unknown>) => TModel
@@ -34,29 +35,74 @@ export const isClass = <T = unknown>(
3435
&& /^class\s/.test(Function.prototype.toString.call(target))
3536
}
3637

38+
export const normalizePositiveInteger = (value: unknown, fallback: number) => {
39+
const parsed = Number(value)
40+
41+
if (!Number.isInteger(parsed) || parsed < 1) {
42+
return fallback
43+
}
44+
45+
return parsed
46+
}
47+
3748
/**
38-
* Resolves the number of items to return per page based on the provided query parameters.
49+
* Extracts a safe pagination limit from a query object.
3950
*
4051
* @param query
52+
* @param defaults
53+
* @default const defaults = { pageSize: 25, maxPageSize: 50 }
4154
* @returns
4255
*/
43-
export const perPage = (query: {
44-
limit?: number;
45-
perPage?: number;
46-
per_page?: number;
47-
'per-page'?: number;
48-
}) => {
49-
50-
const requestedPerPage = Number(
56+
export const perPage = (
57+
query: {
58+
limit?: number;
59+
perPage?: number;
60+
per_page?: number;
61+
'per-page'?: number;
62+
},
63+
defaults: {
64+
perPage?: number;
65+
maxPerPage?: number
66+
}) => {
67+
68+
const requestedPerPage = normalizePositiveInteger(
5169
query.limit ??
5270
query.perPage ??
5371
query['per-page'] ??
54-
query.per_page ?? 15
72+
query.per_page,
73+
defaults.perPage ?? 25
5574
)
5675

57-
return Number.isFinite(requestedPerPage) && requestedPerPage > 0
58-
? Math.min(requestedPerPage, 50)
59-
: 15
76+
return Math.min(requestedPerPage, defaults.maxPerPage ?? 50)
77+
}
78+
79+
/**
80+
* Extracts the current page and a safe pagination limit from a query object.
81+
*
82+
* @param query
83+
* @param defaults
84+
* @default const defaults = { currentPage: 1, pageSize: 25, maxPageSize: 50 }
85+
* @returns
86+
*/
87+
export const resolvePagination = (
88+
query: {
89+
page?: number;
90+
limit?: number;
91+
perPage?: number;
92+
per_page?: number;
93+
'per-page'?: number;
94+
},
95+
defaults: {
96+
page?: number;
97+
perPage?: number;
98+
maxPerPage?: number
99+
}): PaginationOptions => {
100+
const page = normalizePositiveInteger(query.page, defaults.page ?? 1)
101+
102+
return {
103+
page,
104+
perPage: perPage(query, defaults)
105+
}
60106
}
61107

62108
/**

0 commit comments

Comments
 (0)