keryx 0.18.0 → 0.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.ts +2 -0
- package/package.json +1 -1
- package/util/pagination.ts +54 -0
- package/util/zodMixins.ts +33 -0
package/index.ts
CHANGED
|
@@ -34,11 +34,13 @@ export type { WebServer } from "./servers/web";
|
|
|
34
34
|
export { buildProgram } from "./util/cli";
|
|
35
35
|
export { deepMerge, loadFromEnvIfSet } from "./util/config";
|
|
36
36
|
export { globLoader } from "./util/glob";
|
|
37
|
+
export { type PaginatedResult, paginate } from "./util/pagination";
|
|
37
38
|
export { toMarkdown } from "./util/toMarkdown";
|
|
38
39
|
export type { DbOrTransaction, Transaction } from "./util/transaction";
|
|
39
40
|
export { withTransaction } from "./util/transaction";
|
|
40
41
|
export {
|
|
41
42
|
isSecret,
|
|
43
|
+
paginationInputs,
|
|
42
44
|
secret,
|
|
43
45
|
zBooleanFromString,
|
|
44
46
|
zIdOrModel,
|
package/package.json
CHANGED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Standardized pagination response envelope.
|
|
3
|
+
*/
|
|
4
|
+
export interface PaginatedResult<T> {
|
|
5
|
+
data: T[];
|
|
6
|
+
pagination: {
|
|
7
|
+
page: number;
|
|
8
|
+
limit: number;
|
|
9
|
+
total: number;
|
|
10
|
+
pages: number;
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Applies pagination to a Drizzle select query and returns a standardized envelope.
|
|
16
|
+
*
|
|
17
|
+
* Runs the data query (with LIMIT/OFFSET) and a COUNT query in parallel for efficiency.
|
|
18
|
+
* The caller provides both queries separately so they can use different joins or WHERE
|
|
19
|
+
* clauses for the count (e.g., skipping expensive JOINs that don't affect the total).
|
|
20
|
+
*
|
|
21
|
+
* @param query - A Drizzle select query builder that has not yet had `.limit()` or
|
|
22
|
+
* `.offset()` applied. Must support chaining `.limit(n).offset(n)`.
|
|
23
|
+
* @param countQuery - A promise resolving to `[{ count: number }]`. Typically built with
|
|
24
|
+
* `db.select({ count: count() }).from(table).where(...)`.
|
|
25
|
+
* @param params - Object with `page` (1-indexed) and `limit`, matching the output of
|
|
26
|
+
* `paginationInputs()`.
|
|
27
|
+
* @returns A `PaginatedResult<T>` containing the `data` array and `pagination` metadata.
|
|
28
|
+
*/
|
|
29
|
+
export async function paginate<T>(
|
|
30
|
+
query: {
|
|
31
|
+
limit: (n: number) => { offset: (n: number) => PromiseLike<T[]> };
|
|
32
|
+
},
|
|
33
|
+
countQuery: PromiseLike<{ count: number }[]>,
|
|
34
|
+
params: { page: number; limit: number },
|
|
35
|
+
): Promise<PaginatedResult<T>> {
|
|
36
|
+
const offset = (params.page - 1) * params.limit;
|
|
37
|
+
|
|
38
|
+
const [data, countResult] = await Promise.all([
|
|
39
|
+
query.limit(params.limit).offset(offset),
|
|
40
|
+
countQuery,
|
|
41
|
+
]);
|
|
42
|
+
|
|
43
|
+
const total = Number(countResult[0]?.count ?? 0);
|
|
44
|
+
|
|
45
|
+
return {
|
|
46
|
+
data,
|
|
47
|
+
pagination: {
|
|
48
|
+
page: params.page,
|
|
49
|
+
limit: params.limit,
|
|
50
|
+
total,
|
|
51
|
+
pages: Math.ceil(total / params.limit),
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
}
|
package/util/zodMixins.ts
CHANGED
|
@@ -46,6 +46,39 @@ export function zBooleanFromString() {
|
|
|
46
46
|
});
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
+
/**
|
|
50
|
+
* Creates a Zod schema for pagination inputs with sensible defaults.
|
|
51
|
+
* Returns `{ page, limit }` where `page` is 1-indexed.
|
|
52
|
+
*
|
|
53
|
+
* @param options - Optional overrides for default values and bounds.
|
|
54
|
+
* @param options.defaultLimit - Default number of items per page (default: 25).
|
|
55
|
+
* @param options.maxLimit - Maximum allowed items per page (default: 100).
|
|
56
|
+
* @returns A Zod object schema with `page` and `limit` fields.
|
|
57
|
+
*/
|
|
58
|
+
export function paginationInputs(options?: {
|
|
59
|
+
defaultLimit?: number;
|
|
60
|
+
maxLimit?: number;
|
|
61
|
+
}) {
|
|
62
|
+
const defaultLimit = options?.defaultLimit ?? 25;
|
|
63
|
+
const maxLimit = options?.maxLimit ?? 100;
|
|
64
|
+
|
|
65
|
+
return z.object({
|
|
66
|
+
page: z.coerce
|
|
67
|
+
.number()
|
|
68
|
+
.int()
|
|
69
|
+
.min(1)
|
|
70
|
+
.default(1)
|
|
71
|
+
.describe("Page number (1-indexed)"),
|
|
72
|
+
limit: z.coerce
|
|
73
|
+
.number()
|
|
74
|
+
.int()
|
|
75
|
+
.min(1)
|
|
76
|
+
.max(maxLimit)
|
|
77
|
+
.default(defaultLimit)
|
|
78
|
+
.describe("Number of items per page"),
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
49
82
|
// Type for Drizzle tables with an id column
|
|
50
83
|
type TableWithId = { id: any; $inferSelect: any };
|
|
51
84
|
|