typeorm-extensions 2.2.0-rc.10 → 2.2.0-rc.11
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/README.md +2 -2
- package/dist/extensions/cursor-pagination.extension.d.ts +13 -1
- package/dist/extensions/cursor-pagination.extension.js +83 -0
- package/dist/extensions/get-many-with-totals.extension.js +2 -2
- package/dist/extensions/pagination.extension.d.ts +6 -0
- package/dist/extensions/pagination.extension.js +5 -12
- package/dist/types/extensions/cursor-pagination.types.d.ts +12 -2
- package/dist/types/interfaces/cursor-pagination.interface.d.ts +11 -0
- package/dist/utils/cursor.utils.d.ts +5 -0
- package/dist/utils/cursor.utils.js +14 -1
- package/dist/utils/pagination.utils.d.ts +3 -0
- package/dist/utils/pagination.utils.js +8 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -57,8 +57,8 @@ npm install typeorm-extensions
|
|
|
57
57
|
|
|
58
58
|
**Pagination** ([docs](https://koenigstag.github.io/typeorm-extensions/docs/api/pagination/))
|
|
59
59
|
- `getManyWithTotals` and `getRawManyWithTotals`: the rows of a page, the total, `page` / `pageSize`, `hasMore` and the order it was read in
|
|
60
|
-
- `getManyWithCursor`: keyset pagination with an opaque cursor
|
|
61
|
-
- `
|
|
60
|
+
- `getManyWithCursor` and `getRawManyWithCursor`: keyset pagination with an opaque cursor, by columns or by SQL expressions
|
|
61
|
+
- `applyPagination` for a page of any query
|
|
62
62
|
|
|
63
63
|
**Filters and ordering** ([docs](https://koenigstag.github.io/typeorm-extensions/docs/api/where/))
|
|
64
64
|
- `whereIsIn` / `whereIsNotIn` that handle empty arrays, `whereIsAny`, null-safe `whereIsDistinctFrom`
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ObjectLiteral } from 'typeorm';
|
|
2
2
|
import type { CursorPaginationFilter } from '../types/interfaces/cursor-pagination.interface';
|
|
3
|
-
import type { GetManyWithCursorOptions, ListWithCursor } from '../types/extensions/cursor-pagination.types';
|
|
3
|
+
import type { GetManyWithCursorOptions, GetRawManyWithCursorOptions, ListWithCursor } from '../types/extensions/cursor-pagination.types';
|
|
4
4
|
export { InvalidCursorError } from '../utils/cursor.utils';
|
|
5
5
|
declare module 'typeorm/query-builder/SelectQueryBuilder' {
|
|
6
6
|
interface SelectQueryBuilder<Entity> {
|
|
@@ -16,5 +16,17 @@ declare module 'typeorm/query-builder/SelectQueryBuilder' {
|
|
|
16
16
|
* be decoded or was made for another order.
|
|
17
17
|
*/
|
|
18
18
|
getManyWithCursor<CursorEntity extends ObjectLiteral = Entity>(this: SelectQueryBuilder<CursorEntity>, cursorFilter: CursorPaginationFilter | null | undefined, options: GetManyWithCursorOptions<CursorEntity>): Promise<ListWithCursor<CursorEntity>>;
|
|
19
|
+
/**
|
|
20
|
+
* `getManyWithCursor` for raw rows (`getRawMany()`), ordered by SQL expressions, so a computed
|
|
21
|
+
* value can be a key too: `CASE "member"."role" WHEN 'owner' THEN 0 ELSE 1 END`.
|
|
22
|
+
*
|
|
23
|
+
* Orders the query by `options.orderBy`, replacing its ORDER BY, and appends the primary key of
|
|
24
|
+
* the main alias when the query is on an entity and has no GROUP BY or DISTINCT. Loads
|
|
25
|
+
* `pageSize + 1` rows with LIMIT and returns `nextCursor` together with `hasMore`. The cursor
|
|
26
|
+
* names the keys by a hash of their SQL, not the SQL itself.
|
|
27
|
+
* The query builder itself is not changed. Throws `InvalidCursorError` for a cursor that can't
|
|
28
|
+
* be decoded or was made for another order.
|
|
29
|
+
*/
|
|
30
|
+
getRawManyWithCursor<Row = ObjectLiteral>(cursorFilter: CursorPaginationFilter | null | undefined, options: GetRawManyWithCursorOptions): Promise<ListWithCursor<Row>>;
|
|
19
31
|
}
|
|
20
32
|
}
|
|
@@ -8,6 +8,7 @@ const order_utils_1 = require("../utils/order.utils");
|
|
|
8
8
|
const where_utils_1 = require("../utils/where.utils");
|
|
9
9
|
const sql_utils_1 = require("../utils/sql.utils");
|
|
10
10
|
const query_builder_utils_1 = require("../utils/query-builder.utils");
|
|
11
|
+
const common_utils_1 = require("../utils/common.utils");
|
|
11
12
|
const cursor_utils_1 = require("../utils/cursor.utils");
|
|
12
13
|
var cursor_utils_2 = require("../utils/cursor.utils");
|
|
13
14
|
Object.defineProperty(exports, "InvalidCursorError", { enumerable: true, get: function () { return cursor_utils_2.InvalidCursorError; } });
|
|
@@ -17,6 +18,9 @@ const extension = {
|
|
|
17
18
|
getManyWithCursor(cursorFilter, options) {
|
|
18
19
|
return getManyWithCursor(this, cursorFilter, options);
|
|
19
20
|
},
|
|
21
|
+
getRawManyWithCursor(cursorFilter, options) {
|
|
22
|
+
return getRawManyWithCursor(this, cursorFilter, options);
|
|
23
|
+
},
|
|
20
24
|
},
|
|
21
25
|
};
|
|
22
26
|
(0, prototype_utils_1.patchPrototype)(SelectQueryBuilder_1.SelectQueryBuilder, extension);
|
|
@@ -101,3 +105,82 @@ async function getManyWithCursor(builder, cursorFilter, options) {
|
|
|
101
105
|
});
|
|
102
106
|
return { list, nextCursor: (0, cursor_utils_1.encodeCursor)(fields, values), hasMore: true, pageSize, orderBy: appliedOrderBy };
|
|
103
107
|
}
|
|
108
|
+
/** The expressions of `orderBy`, followed by the primary key of the main alias when it identifies a row. */
|
|
109
|
+
function getExpressionKeys(builder, orderBy) {
|
|
110
|
+
var _a;
|
|
111
|
+
const keys = (orderBy !== null && orderBy !== void 0 ? orderBy : []).map(({ selector, direction }) => {
|
|
112
|
+
if (typeof selector !== 'string' || !selector.trim()) {
|
|
113
|
+
throw new TypeError('getRawManyWithCursor: every orderBy key needs a selector');
|
|
114
|
+
}
|
|
115
|
+
return { selector, direction: (0, order_utils_1.toSqlOrderDirection)(direction) };
|
|
116
|
+
});
|
|
117
|
+
const { expressionMap } = builder;
|
|
118
|
+
const mainAlias = expressionMap.mainAlias;
|
|
119
|
+
// grouped or distinct rows are not one per entity, and their primary key may not even be selectable
|
|
120
|
+
const isRowPerEntity = expressionMap.groupBys.length === 0 &&
|
|
121
|
+
!expressionMap.selectDistinct &&
|
|
122
|
+
!((_a = expressionMap.selectDistinctOn) === null || _a === void 0 ? void 0 : _a.length);
|
|
123
|
+
if ((mainAlias === null || mainAlias === void 0 ? void 0 : mainAlias.hasMetadata) && isRowPerEntity) {
|
|
124
|
+
const lastDirection = keys.length ? keys[keys.length - 1].direction : 'ASC';
|
|
125
|
+
for (const column of mainAlias.metadata.primaryColumns) {
|
|
126
|
+
const quoted = `${builder.escape(mainAlias.name)}.${builder.escape(column.databaseName)}`;
|
|
127
|
+
const path = `${mainAlias.name}.${column.propertyPath}`;
|
|
128
|
+
if (!keys.some((key) => key.selector === quoted || key.selector === path)) {
|
|
129
|
+
keys.push({ selector: quoted, direction: lastDirection });
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
if (!keys.length) {
|
|
134
|
+
throw new TypeError('getRawManyWithCursor: orderBy is empty and the query has no primary key to order by');
|
|
135
|
+
}
|
|
136
|
+
return keys;
|
|
137
|
+
}
|
|
138
|
+
async function getRawManyWithCursor(builder, cursorFilter, options) {
|
|
139
|
+
const keys = getExpressionKeys(builder, options === null || options === void 0 ? void 0 : options.orderBy);
|
|
140
|
+
const fields = keys.map((key) => (0, cursor_utils_1.getExpressionKeyName)(key.selector));
|
|
141
|
+
const pageSize = (0, pagination_utils_1.normalizePageSize)(cursorFilter === null || cursorFilter === void 0 ? void 0 : cursorFilter.pageSize);
|
|
142
|
+
const query = builder.clone();
|
|
143
|
+
const driver = query.connection.driver.options.type;
|
|
144
|
+
const textCastType = (0, cursor_utils_1.getTextCastType)(driver);
|
|
145
|
+
if (cursorFilter === null || cursorFilter === void 0 ? void 0 : cursorFilter.cursor) {
|
|
146
|
+
const values = (0, cursor_utils_1.decodeCursor)(cursorFilter.cursor, fields);
|
|
147
|
+
const keysetColumns = keys.map((key, i) => {
|
|
148
|
+
const paramName = (0, sql_utils_1.createUniqueParameterName)(`cursor${i}`);
|
|
149
|
+
query.setParameter(paramName, values[i]);
|
|
150
|
+
// in parentheses: an expression may bind looser than the comparison
|
|
151
|
+
return { column: `(${key.selector})`, direction: key.direction, paramName };
|
|
152
|
+
});
|
|
153
|
+
(0, where_utils_1.andWhereNarrowing)(query, (0, cursor_utils_1.buildKeysetCondition)(keysetColumns, (0, cursor_utils_1.supportsRowValueComparison)(driver)));
|
|
154
|
+
}
|
|
155
|
+
query.orderBy();
|
|
156
|
+
keys.forEach((key) => query.addOrderBy(key.selector, key.direction));
|
|
157
|
+
const appliedOrderBy = (0, query_builder_utils_1.getAppliedOrderBy)(query);
|
|
158
|
+
// the type of an expression is unknown here: each key is also read as text, which the cursor
|
|
159
|
+
// takes instead when the driver returned a Date, so timestamps keep the database's precision
|
|
160
|
+
const keyAliases = keys.map((key, i) => {
|
|
161
|
+
const valueAlias = `cursor_key_${i}`;
|
|
162
|
+
const textAlias = textCastType ? `cursor_text_${i}` : undefined;
|
|
163
|
+
query.addSelect(key.selector, valueAlias);
|
|
164
|
+
if (textAlias) {
|
|
165
|
+
query.addSelect(`CAST((${key.selector}) AS ${textCastType})`, textAlias);
|
|
166
|
+
}
|
|
167
|
+
return { valueAlias, textAlias };
|
|
168
|
+
});
|
|
169
|
+
const addedAliases = new Set(keyAliases.flatMap(({ valueAlias, textAlias }) => (textAlias ? [valueAlias, textAlias] : [valueAlias])));
|
|
170
|
+
query.limit(pageSize + 1);
|
|
171
|
+
const rows = await query.getRawMany();
|
|
172
|
+
const page = rows.slice(0, pageSize);
|
|
173
|
+
const list = page.map((row) => Object.fromEntries(Object.entries(row).filter(([column]) => !addedAliases.has(column))));
|
|
174
|
+
if (rows.length <= pageSize) {
|
|
175
|
+
return { list, nextCursor: null, hasMore: false, pageSize, orderBy: appliedOrderBy };
|
|
176
|
+
}
|
|
177
|
+
const last = page[page.length - 1];
|
|
178
|
+
const values = keyAliases.map(({ valueAlias, textAlias }, i) => {
|
|
179
|
+
const value = last[valueAlias];
|
|
180
|
+
if (value === null || value === undefined) {
|
|
181
|
+
throw new TypeError(`getRawManyWithCursor: "${keys[i].selector}" is NULL in the last row; cursor keys must not be NULL`);
|
|
182
|
+
}
|
|
183
|
+
return (0, common_utils_1.isDate)(value) && textAlias ? last[textAlias] : value;
|
|
184
|
+
});
|
|
185
|
+
return { list, nextCursor: (0, cursor_utils_1.encodeCursor)(fields, values), hasMore: true, pageSize, orderBy: appliedOrderBy };
|
|
186
|
+
}
|
|
@@ -67,10 +67,10 @@ async function readWithTotals(builder, paginationFilter, options, read) {
|
|
|
67
67
|
if (loadAll) {
|
|
68
68
|
return read.rows(builder);
|
|
69
69
|
}
|
|
70
|
-
const paged =
|
|
70
|
+
const paged = (0, pagination_utils_1.paginateQuery)(builder, paginationFilter, paginationOptions);
|
|
71
71
|
if (probeForMore) {
|
|
72
72
|
const { useTakeAndSkip = constants_1.defaultUseTakeAndSkip } = paginationOptions;
|
|
73
|
-
// replaces the limit
|
|
73
|
+
// replaces the limit paginateQuery just set, keeping its offset
|
|
74
74
|
(0, pagination_utils_1.setPageLimit)(paged, limit + 1, useTakeAndSkip);
|
|
75
75
|
}
|
|
76
76
|
return read.rows(paged);
|
|
@@ -3,6 +3,12 @@ import { ObjectLiteral } from 'typeorm';
|
|
|
3
3
|
import { ApplyPaginationOptions } from '../types/extensions/pagination.types';
|
|
4
4
|
declare module 'typeorm/query-builder/SelectQueryBuilder' {
|
|
5
5
|
interface SelectQueryBuilder<Entity extends ObjectLiteral> {
|
|
6
|
+
/**
|
|
7
|
+
* Pages the query by `page` and `pageSize` (page 1 and 10 rows by default): LIMIT / OFFSET, or
|
|
8
|
+
* `take` / `skip` with `useTakeAndSkip`, which a query joining a collection needs.
|
|
9
|
+
*/
|
|
10
|
+
applyPagination(paginationFilter: PaginationFilter, options?: ApplyPaginationOptions): this;
|
|
11
|
+
/** @deprecated Use `applyPagination`, which takes the same arguments. Will be removed in 3.0. */
|
|
6
12
|
applyPaginationFilter(paginationFilter: PaginationFilter, options?: ApplyPaginationOptions): this;
|
|
7
13
|
}
|
|
8
14
|
}
|
|
@@ -1,25 +1,18 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
4
3
|
const SelectQueryBuilder_1 = require("typeorm/query-builder/SelectQueryBuilder");
|
|
5
|
-
const constants_1 = require("../constants");
|
|
6
4
|
const pagination_utils_1 = require("../utils/pagination.utils");
|
|
7
5
|
const prototype_utils_1 = require("../utils/prototype.utils");
|
|
8
6
|
// patching
|
|
9
7
|
const extension = {
|
|
10
8
|
prototype: {
|
|
9
|
+
applyPagination(paginationFilter, options) {
|
|
10
|
+
return (0, pagination_utils_1.paginateQuery)(this, paginationFilter, options);
|
|
11
|
+
},
|
|
12
|
+
// calls the implementation, not this.applyPagination: an application may patch a method of that name itself
|
|
11
13
|
applyPaginationFilter(paginationFilter, options) {
|
|
12
|
-
return
|
|
14
|
+
return (0, pagination_utils_1.paginateQuery)(this, paginationFilter, options);
|
|
13
15
|
},
|
|
14
16
|
},
|
|
15
17
|
};
|
|
16
18
|
(0, prototype_utils_1.patchPrototype)(SelectQueryBuilder_1.SelectQueryBuilder, extension);
|
|
17
|
-
// implementation
|
|
18
|
-
function applyPaginationFilter(builder, paginationFilter, options) {
|
|
19
|
-
const { useTakeAndSkip = constants_1.defaultUseTakeAndSkip } = options || {};
|
|
20
|
-
const { limit, offset } = (0, pagination_utils_1.getLimitAndOffset)(paginationFilter);
|
|
21
|
-
if (useTakeAndSkip) {
|
|
22
|
-
return builder.take(limit).skip(offset);
|
|
23
|
-
}
|
|
24
|
-
return builder.limit(limit).offset(offset);
|
|
25
|
-
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { CursorOrderParam } from '../interfaces/cursor-pagination.interface';
|
|
1
|
+
import type { CursorOrderParam, RawCursorOrderParam } from '../interfaces/cursor-pagination.interface';
|
|
2
2
|
import type { OrderType } from '../interfaces/ordering.interface';
|
|
3
3
|
export type GetManyWithCursorOptions<Entity> = {
|
|
4
4
|
/**
|
|
@@ -10,7 +10,17 @@ export type GetManyWithCursorOptions<Entity> = {
|
|
|
10
10
|
/** Use `take` instead of `limit`, as needed for queries with joined collections. Defaults to `false`. */
|
|
11
11
|
useTakeAndSkip?: boolean;
|
|
12
12
|
};
|
|
13
|
+
export type GetRawManyWithCursorOptions = {
|
|
14
|
+
/**
|
|
15
|
+
* SQL expressions that order the rows. On a query of an entity without GROUP BY or DISTINCT the
|
|
16
|
+
* primary key of the main alias is appended when it isn't listed; otherwise the last key has to be
|
|
17
|
+
* unique per row, or rows with equal keys may be skipped or repeated between pages.
|
|
18
|
+
* It replaces any ORDER BY of the query.
|
|
19
|
+
*/
|
|
20
|
+
orderBy: RawCursorOrderParam[];
|
|
21
|
+
};
|
|
13
22
|
export interface ListWithCursor<Entity> {
|
|
23
|
+
/** The entities of the page, or with `getRawManyWithCursor` its raw rows. */
|
|
14
24
|
list: Entity[];
|
|
15
25
|
/** Cursor of the next page, `null` on the last page. */
|
|
16
26
|
nextCursor: string | null;
|
|
@@ -21,6 +31,6 @@ export interface ListWithCursor<Entity> {
|
|
|
21
31
|
hasMore: boolean;
|
|
22
32
|
/** The page size used: the `pageSize` given, rounded down, or `10` when it was missing or below 1. */
|
|
23
33
|
pageSize: number;
|
|
24
|
-
/** The ORDER BY the page was read in: the `orderBy`
|
|
34
|
+
/** The ORDER BY the page was read in: the `orderBy` keys, then the primary key columns not among them. */
|
|
25
35
|
orderBy: OrderType[];
|
|
26
36
|
}
|
|
@@ -14,3 +14,14 @@ export type CursorOrderParam<Entity> = {
|
|
|
14
14
|
/** `'ASC'` by default. */
|
|
15
15
|
direction?: OrderDirection;
|
|
16
16
|
};
|
|
17
|
+
/** An SQL expression that orders the rows of `getRawManyWithCursor`. */
|
|
18
|
+
export type RawCursorOrderParam = {
|
|
19
|
+
/**
|
|
20
|
+
* A column (`user.createdAt`, `"user"."created_at"`) or a computed value
|
|
21
|
+
* (`CASE "member"."role" WHEN 'owner' THEN 0 ELSE 1 END`). It must be valid in WHERE, so not an
|
|
22
|
+
* aggregate, and must not be `NULL`.
|
|
23
|
+
*/
|
|
24
|
+
selector: string;
|
|
25
|
+
/** `'ASC'` by default. */
|
|
26
|
+
direction?: OrderDirection;
|
|
27
|
+
};
|
|
@@ -5,6 +5,11 @@ export declare class InvalidCursorError extends TypeError {
|
|
|
5
5
|
}
|
|
6
6
|
/** A key value in a cursor: as read from the database, or a JS Date tagged as `{ $date }`. */
|
|
7
7
|
export type CursorValue = string | number | boolean;
|
|
8
|
+
/**
|
|
9
|
+
* Short stable name of an SQL expression key, for the cursor to recognise its order by: a cursor
|
|
10
|
+
* carries no SQL. 32-bit FNV-1a; telling orders apart doesn't need more.
|
|
11
|
+
*/
|
|
12
|
+
export declare function getExpressionKeyName(selector: string): string;
|
|
8
13
|
/** base64url-encoded JSON of the key fields and the key values of the last row. */
|
|
9
14
|
export declare function encodeCursor(fields: string[], values: unknown[]): string;
|
|
10
15
|
/** Key values of a cursor made for `fields`; throws `InvalidCursorError` otherwise. */
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.buildKeysetCondition = exports.getTextCastType = exports.supportsRowValueComparison = exports.decodeCursor = exports.encodeCursor = exports.InvalidCursorError = void 0;
|
|
3
|
+
exports.buildKeysetCondition = exports.getTextCastType = exports.supportsRowValueComparison = exports.decodeCursor = exports.encodeCursor = exports.getExpressionKeyName = exports.InvalidCursorError = void 0;
|
|
4
4
|
const sql_utils_1 = require("./sql.utils");
|
|
5
5
|
const common_utils_1 = require("./common.utils");
|
|
6
6
|
/** A cursor that can't be decoded or doesn't belong to the query's order (e.g. tampered with). */
|
|
@@ -34,6 +34,19 @@ const fromBase64Url = (value) => {
|
|
|
34
34
|
const binary = atob(base64 + '='.repeat((4 - (base64.length % 4)) % 4));
|
|
35
35
|
return new TextDecoder('utf-8', { fatal: true }).decode(Uint8Array.from(binary, (char) => char.charCodeAt(0)));
|
|
36
36
|
};
|
|
37
|
+
/**
|
|
38
|
+
* Short stable name of an SQL expression key, for the cursor to recognise its order by: a cursor
|
|
39
|
+
* carries no SQL. 32-bit FNV-1a; telling orders apart doesn't need more.
|
|
40
|
+
*/
|
|
41
|
+
function getExpressionKeyName(selector) {
|
|
42
|
+
let hash = 0x811c9dc5;
|
|
43
|
+
for (let i = 0; i < selector.length; i++) {
|
|
44
|
+
hash ^= selector.charCodeAt(i);
|
|
45
|
+
hash = Math.imul(hash, 0x01000193);
|
|
46
|
+
}
|
|
47
|
+
return `sql:${(hash >>> 0).toString(16).padStart(8, '0')}`;
|
|
48
|
+
}
|
|
49
|
+
exports.getExpressionKeyName = getExpressionKeyName;
|
|
37
50
|
/** base64url-encoded JSON of the key fields and the key values of the last row. */
|
|
38
51
|
function encodeCursor(fields, values) {
|
|
39
52
|
const payload = { k: fields, v: values.map(toCursorValue) };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ObjectLiteral, SelectQueryBuilder } from "typeorm";
|
|
2
|
+
import type { ApplyPaginationOptions } from "../types/extensions/pagination.types";
|
|
2
3
|
import { PaginationFilter } from "../types/interfaces/pagination.interface";
|
|
3
4
|
/** The page size to read: a number or a numeric string of at least 1, rounded down; `defaultPageSize` otherwise. */
|
|
4
5
|
export declare const normalizePageSize: (pageSize: unknown) => number;
|
|
@@ -10,3 +11,5 @@ export declare const getLimitAndOffset: ({ page, pageSize }: PaginationFilter) =
|
|
|
10
11
|
page: number;
|
|
11
12
|
pageSize: number;
|
|
12
13
|
};
|
|
14
|
+
/** What `applyPagination` does, for code that must not depend on what is patched under that name. */
|
|
15
|
+
export declare const paginateQuery: <Entity extends ObjectLiteral>(builder: SelectQueryBuilder<Entity>, paginationFilter: PaginationFilter, options?: ApplyPaginationOptions) => SelectQueryBuilder<Entity>;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getLimitAndOffset = exports.setPageLimit = exports.normalizePageSize = void 0;
|
|
3
|
+
exports.paginateQuery = exports.getLimitAndOffset = exports.setPageLimit = exports.normalizePageSize = void 0;
|
|
4
4
|
const constants_1 = require("../constants");
|
|
5
5
|
// an unparsed query parameter is a string: "20" + 1 would be "201"
|
|
6
6
|
const toPositiveInteger = (value, fallback) => {
|
|
@@ -24,3 +24,10 @@ const getLimitAndOffset = ({ page, pageSize }) => {
|
|
|
24
24
|
};
|
|
25
25
|
};
|
|
26
26
|
exports.getLimitAndOffset = getLimitAndOffset;
|
|
27
|
+
/** What `applyPagination` does, for code that must not depend on what is patched under that name. */
|
|
28
|
+
const paginateQuery = (builder, paginationFilter, options) => {
|
|
29
|
+
const { useTakeAndSkip = constants_1.defaultUseTakeAndSkip } = options || {};
|
|
30
|
+
const { limit, offset } = (0, exports.getLimitAndOffset)(paginationFilter);
|
|
31
|
+
return useTakeAndSkip ? builder.take(limit).skip(offset) : builder.limit(limit).offset(offset);
|
|
32
|
+
};
|
|
33
|
+
exports.paginateQuery = paginateQuery;
|