@dudousxd/nestjs-filter 1.7.0 → 1.8.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/CHANGELOG.md +22 -0
- package/dist/adapter/adapter.d.ts +119 -3
- package/dist/adapter/adapter.d.ts.map +1 -1
- package/dist/decorator/allowed.d.ts +30 -0
- package/dist/decorator/allowed.d.ts.map +1 -0
- package/dist/decorator/allowed.js +32 -0
- package/dist/decorator/allowed.js.map +1 -0
- package/dist/decorator/filter-for.decorator.d.ts.map +1 -1
- package/dist/decorator/filter-for.decorator.js +5 -0
- package/dist/decorator/filter-for.decorator.js.map +1 -1
- package/dist/decorator/filterable.decorator.d.ts.map +1 -1
- package/dist/decorator/filterable.decorator.js +3 -0
- package/dist/decorator/filterable.decorator.js.map +1 -1
- package/dist/decorator/relations.decorator.d.ts.map +1 -1
- package/dist/decorator/relations.decorator.js +25 -9
- package/dist/decorator/relations.decorator.js.map +1 -1
- package/dist/index.d.ts +7 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/input/spatie-parser.d.ts +40 -0
- package/dist/input/spatie-parser.d.ts.map +1 -0
- package/dist/input/spatie-parser.js +159 -0
- package/dist/input/spatie-parser.js.map +1 -0
- package/dist/pagination/cursor.d.ts +37 -0
- package/dist/pagination/cursor.d.ts.map +1 -0
- package/dist/pagination/cursor.js +69 -0
- package/dist/pagination/cursor.js.map +1 -0
- package/dist/runner.d.ts +78 -3
- package/dist/runner.d.ts.map +1 -1
- package/dist/runner.js +402 -49
- package/dist/runner.js.map +1 -1
- package/dist/types.d.ts +113 -3
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parses a spatie-laravel-query-builder / JSON:API-style query input into the
|
|
3
|
+
* library's internal {@link StructuredInput} model.
|
|
4
|
+
*
|
|
5
|
+
* This is an **opt-in** input format (enabled via the `inputFormat: 'spatie'`
|
|
6
|
+
* module option). The default `'native'` format is unchanged. The parser is a
|
|
7
|
+
* pure reshape — it does **not** enforce safety itself; its output flows through
|
|
8
|
+
* the same runner pipeline as native input, so the allowlist, per-field operator
|
|
9
|
+
* allowlist, and `throwOnInvalid` policy still apply.
|
|
10
|
+
*
|
|
11
|
+
* The input is the object produced after a query string has been decoded by
|
|
12
|
+
* Express + `qs` (bracket notation expanded into nested objects):
|
|
13
|
+
*
|
|
14
|
+
* | Query string | Decoded input | Mapped to |
|
|
15
|
+
* | ------------------------------------ | ----------------------------------------------- | ------------------------------------------ |
|
|
16
|
+
* | `filter[name]=Al` | `{ filter: { name: 'Al' } }` | `{ filter: { name: 'Al' } }` (equals) |
|
|
17
|
+
* | `filter[id]=1,2,3` | `{ filter: { id: '1,2,3' } }` | `{ filter: { id: ['1','2','3'] } }` (in) |
|
|
18
|
+
* | `filter[name][contains]=Al` | `{ filter: { name: { contains: 'Al' } } }` | operator object → existing operator path |
|
|
19
|
+
* | `sort=-createdAt,name` | `{ sort: '-createdAt,name' }` | `{ sort: '-createdAt,name' }` |
|
|
20
|
+
* | `include=posts,comments` | `{ include: 'posts,comments' }` | `{ include: 'posts,comments' }` |
|
|
21
|
+
* | `fields[users]=id,name` | `{ fields: { users: 'id,name' } }` | `{ select: ['id','name'] }` (sparse) |
|
|
22
|
+
* | `page[number]=2&page[size]=10` | `{ page: { number: '2', size: '10' } }` | `{ paginate: { page: 2, size: 10 } }` |
|
|
23
|
+
* | `page[after]=cur&page[size]=10` | `{ page: { after: 'cur', size: '10' } }` | `{ paginate: { after, first } }` (cursor) |
|
|
24
|
+
*
|
|
25
|
+
* Design notes:
|
|
26
|
+
* - **Filter values**: a bare scalar is interpreted by the adapter as `equals`;
|
|
27
|
+
* a comma-separated scalar (spatie's convention for multi-value filters) is
|
|
28
|
+
* split into an array (interpreted as `in`); an object whose keys are operators
|
|
29
|
+
* passes straight through to the existing operator-object mechanism.
|
|
30
|
+
* - **Sparse fieldsets** (`fields[resource]`): JSON:API keys these by resource
|
|
31
|
+
* type, but the runner applies a single SELECT to the primary entity, so the
|
|
32
|
+
* per-resource lists are flattened (deduped, order-preserving) into one
|
|
33
|
+
* `select` list and validated against the entity / allowlist downstream.
|
|
34
|
+
* - **Pagination**: JSON:API page-based (`page[number]`/`page[size]`) maps to
|
|
35
|
+
* offset pagination; cursor-style (`page[after]`/`page[before]`) maps to the
|
|
36
|
+
* library's keyset pagination input.
|
|
37
|
+
*/
|
|
38
|
+
export function parseSpatieInput(input) {
|
|
39
|
+
if (input == null || typeof input !== 'object')
|
|
40
|
+
return {};
|
|
41
|
+
const src = input;
|
|
42
|
+
const out = {};
|
|
43
|
+
if (src.filter != null && typeof src.filter === 'object' && !Array.isArray(src.filter)) {
|
|
44
|
+
out.filter = parseFilters(src.filter);
|
|
45
|
+
}
|
|
46
|
+
if (typeof src.sort === 'string') {
|
|
47
|
+
out.sort = src.sort;
|
|
48
|
+
}
|
|
49
|
+
else if (Array.isArray(src.sort)) {
|
|
50
|
+
// `sort[]=a&sort[]=-b` decodes to an array — join to the JSON:API string form.
|
|
51
|
+
out.sort = src.sort.filter((s) => typeof s === 'string').join(',');
|
|
52
|
+
}
|
|
53
|
+
if (typeof src.include === 'string') {
|
|
54
|
+
out.include = src.include;
|
|
55
|
+
}
|
|
56
|
+
else if (Array.isArray(src.include)) {
|
|
57
|
+
out.include = src.include.filter((s) => typeof s === 'string');
|
|
58
|
+
}
|
|
59
|
+
if (typeof src.search === 'string') {
|
|
60
|
+
out.search = src.search;
|
|
61
|
+
}
|
|
62
|
+
const select = parseSparseFieldsets(src.fields);
|
|
63
|
+
if (select.length > 0) {
|
|
64
|
+
out.select = select;
|
|
65
|
+
}
|
|
66
|
+
const paginate = parsePage(src.page);
|
|
67
|
+
if (paginate)
|
|
68
|
+
out.paginate = paginate;
|
|
69
|
+
return out;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Reshapes the `filter` map: scalar values become equals (with comma → array),
|
|
73
|
+
* operator objects pass through unchanged, and arrays are kept as-is.
|
|
74
|
+
*/
|
|
75
|
+
function parseFilters(filter) {
|
|
76
|
+
const out = {};
|
|
77
|
+
for (const [field, value] of Object.entries(filter)) {
|
|
78
|
+
if (Array.isArray(value)) {
|
|
79
|
+
out[field] = value;
|
|
80
|
+
}
|
|
81
|
+
else if (value != null && typeof value === 'object') {
|
|
82
|
+
// Operator object (`{ contains: 'x' }`) — passed straight through; the
|
|
83
|
+
// runner / adapter interprets and validates the operator keys.
|
|
84
|
+
out[field] = value;
|
|
85
|
+
}
|
|
86
|
+
else if (typeof value === 'string' && value.includes(',')) {
|
|
87
|
+
// spatie multi-value filter convention: comma-separated → array (IN).
|
|
88
|
+
out[field] = value
|
|
89
|
+
.split(',')
|
|
90
|
+
.map((s) => s.trim())
|
|
91
|
+
.filter((s) => s.length > 0);
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
out[field] = value;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return out;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Flattens JSON:API sparse fieldsets (`fields[resource]=a,b`) into a single
|
|
101
|
+
* order-preserving, de-duplicated list of column names. Per-resource keys are
|
|
102
|
+
* collapsed because the runner applies one SELECT to the primary entity; unknown
|
|
103
|
+
* columns are dropped downstream when validated against entity metadata.
|
|
104
|
+
*/
|
|
105
|
+
function parseSparseFieldsets(fields) {
|
|
106
|
+
if (fields == null || typeof fields !== 'object' || Array.isArray(fields))
|
|
107
|
+
return [];
|
|
108
|
+
const seen = new Set();
|
|
109
|
+
const out = [];
|
|
110
|
+
for (const value of Object.values(fields)) {
|
|
111
|
+
const cols = typeof value === 'string'
|
|
112
|
+
? value.split(',')
|
|
113
|
+
: Array.isArray(value)
|
|
114
|
+
? value.filter((s) => typeof s === 'string')
|
|
115
|
+
: [];
|
|
116
|
+
for (const raw of cols) {
|
|
117
|
+
const col = raw.trim();
|
|
118
|
+
if (col.length > 0 && !seen.has(col)) {
|
|
119
|
+
seen.add(col);
|
|
120
|
+
out.push(col);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return out;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Maps a JSON:API `page` object to the library's pagination input.
|
|
128
|
+
* `page[number]`/`page[size]` → offset; `page[after]`/`page[before]` → cursor.
|
|
129
|
+
*/
|
|
130
|
+
function parsePage(page) {
|
|
131
|
+
if (page == null || typeof page !== 'object' || Array.isArray(page))
|
|
132
|
+
return undefined;
|
|
133
|
+
const p = page;
|
|
134
|
+
const after = typeof p.after === 'string' ? p.after : undefined;
|
|
135
|
+
const before = typeof p.before === 'string' ? p.before : undefined;
|
|
136
|
+
const size = toInt(p.size);
|
|
137
|
+
if (after !== undefined || before !== undefined) {
|
|
138
|
+
if (after !== undefined) {
|
|
139
|
+
return { after, ...(size !== undefined && { first: size }) };
|
|
140
|
+
}
|
|
141
|
+
return { before: before, ...(size !== undefined && { last: size }) };
|
|
142
|
+
}
|
|
143
|
+
const number = toInt(p.number);
|
|
144
|
+
if (number !== undefined || size !== undefined) {
|
|
145
|
+
return { page: number ?? 0, size: size ?? 25 };
|
|
146
|
+
}
|
|
147
|
+
return undefined;
|
|
148
|
+
}
|
|
149
|
+
function toInt(value) {
|
|
150
|
+
if (typeof value === 'number' && Number.isFinite(value))
|
|
151
|
+
return value;
|
|
152
|
+
if (typeof value === 'string' && value.trim() !== '') {
|
|
153
|
+
const n = Number(value);
|
|
154
|
+
if (Number.isFinite(n))
|
|
155
|
+
return n;
|
|
156
|
+
}
|
|
157
|
+
return undefined;
|
|
158
|
+
}
|
|
159
|
+
//# sourceMappingURL=spatie-parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spatie-parser.js","sourceRoot":"","sources":["../../src/input/spatie-parser.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAAc;IAC7C,IAAI,KAAK,IAAI,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,EAAE,CAAC;IAC1D,MAAM,GAAG,GAAG,KAAgC,CAAC;IAC7C,MAAM,GAAG,GAAoB,EAAE,CAAC;IAEhC,IAAI,GAAG,CAAC,MAAM,IAAI,IAAI,IAAI,OAAO,GAAG,CAAC,MAAM,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;QACvF,GAAG,CAAC,MAAM,GAAG,YAAY,CAAC,GAAG,CAAC,MAAiC,CAAC,CAAC;IACnE,CAAC;IAED,IAAI,OAAO,GAAG,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QACjC,GAAG,CAAC,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC;IACtB,CAAC;SAAM,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;QACnC,+EAA+E;QAC/E,GAAG,CAAC,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACrE,CAAC;IAED,IAAI,OAAO,GAAG,CAAC,OAAO,KAAK,QAAQ,EAAE,CAAC;QACpC,GAAG,CAAC,OAAO,GAAG,GAAG,CAAC,OAAO,CAAC;IAC5B,CAAC;SAAM,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;QACtC,GAAG,CAAC,OAAO,GAAG,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAe,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC;IAC9E,CAAC;IAED,IAAI,OAAO,GAAG,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;QACnC,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC;IAC1B,CAAC;IAED,MAAM,MAAM,GAAG,oBAAoB,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAChD,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACrB,GAA+C,CAAC,MAAM,GAAG,MAAM,CAAC;IACnE,CAAC;IAED,MAAM,QAAQ,GAAG,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACrC,IAAI,QAAQ;QAAE,GAAG,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAEtC,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;GAGG;AACH,SAAS,YAAY,CAAC,MAA+B;IACnD,MAAM,GAAG,GAA4B,EAAE,CAAC;IACxC,KAAK,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QACpD,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YACzB,GAAG,CAAC,KAAK,CAAC,GAAG,KAAK,CAAC;QACrB,CAAC;aAAM,IAAI,KAAK,IAAI,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;YACtD,uEAAuE;YACvE,+DAA+D;YAC/D,GAAG,CAAC,KAAK,CAAC,GAAG,KAAK,CAAC;QACrB,CAAC;aAAM,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;YAC5D,sEAAsE;YACtE,GAAG,CAAC,KAAK,CAAC,GAAG,KAAK;iBACf,KAAK,CAAC,GAAG,CAAC;iBACV,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;iBACpB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QACjC,CAAC;aAAM,CAAC;YACN,GAAG,CAAC,KAAK,CAAC,GAAG,KAAK,CAAC;QACrB,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;;GAKG;AACH,SAAS,oBAAoB,CAAC,MAAe;IAC3C,IAAI,MAAM,IAAI,IAAI,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;QAAE,OAAO,EAAE,CAAC;IACrF,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,CAAC,MAAiC,CAAC,EAAE,CAAC;QACrE,MAAM,IAAI,GACR,OAAO,KAAK,KAAK,QAAQ;YACvB,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC;YAClB,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;gBACpB,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAe,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC;gBACzD,CAAC,CAAC,EAAE,CAAC;QACX,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,MAAM,GAAG,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;YACvB,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;gBACrC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;gBACd,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAChB,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;GAGG;AACH,SAAS,SAAS,CAAC,IAAa;IAC9B,IAAI,IAAI,IAAI,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC;QAAE,OAAO,SAAS,CAAC;IACtF,MAAM,CAAC,GAAG,IAA+B,CAAC;IAE1C,MAAM,KAAK,GAAG,OAAO,CAAC,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;IAChE,MAAM,MAAM,GAAG,OAAO,CAAC,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC;IACnE,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IAE3B,IAAI,KAAK,KAAK,SAAS,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QAChD,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxB,OAAO,EAAE,KAAK,EAAE,GAAG,CAAC,IAAI,KAAK,SAAS,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;QAC/D,CAAC;QACD,OAAO,EAAE,MAAM,EAAE,MAAO,EAAE,GAAG,CAAC,IAAI,KAAK,SAAS,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;IACxE,CAAC;IAED,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;IAC/B,IAAI,MAAM,KAAK,SAAS,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QAC/C,OAAO,EAAE,IAAI,EAAE,MAAM,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,IAAI,EAAE,EAAE,CAAC;IACjD,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,SAAS,KAAK,CAAC,KAAc;IAC3B,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IACtE,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QACrD,MAAM,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;QACxB,IAAI,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;YAAE,OAAO,CAAC,CAAC;IACnC,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { SortItem } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* A decoded keyset cursor: the ordered values of the keyset columns (the active
|
|
4
|
+
* sort columns plus a stable primary-key tiebreaker) captured from a boundary
|
|
5
|
+
* row. The values line up positionally with the keyset's `SortItem[]`.
|
|
6
|
+
*/
|
|
7
|
+
export type CursorValues = unknown[];
|
|
8
|
+
/**
|
|
9
|
+
* Encodes keyset cursor values into a compact, URL-safe opaque string
|
|
10
|
+
* (base64url of a JSON array). The shape is intentionally opaque to clients —
|
|
11
|
+
* only this module reads it back.
|
|
12
|
+
*
|
|
13
|
+
* `Date` values are encoded as `{ $d: <iso> }` so they round-trip to `Date`
|
|
14
|
+
* instances on decode (plain JSON would yield a string and break date keyset
|
|
15
|
+
* comparisons).
|
|
16
|
+
*/
|
|
17
|
+
export declare function encodeCursor(values: CursorValues): string;
|
|
18
|
+
/**
|
|
19
|
+
* Decodes an opaque cursor string back into its keyset values. Returns `null`
|
|
20
|
+
* when the cursor is malformed (bad base64, bad JSON, or not an array) so the
|
|
21
|
+
* caller can ignore an invalid cursor instead of crashing.
|
|
22
|
+
*/
|
|
23
|
+
export declare function decodeCursor(cursor: string): CursorValues | null;
|
|
24
|
+
/**
|
|
25
|
+
* Builds the keyset `SortItem[]` for cursor pagination: the caller's effective
|
|
26
|
+
* sorts, with a stable primary-key tiebreaker appended if it is not already
|
|
27
|
+
* present. The tiebreaker inherits the direction of the last sort column so the
|
|
28
|
+
* overall ordering stays monotonic (important for a correct `(cols, pk) > (...)`
|
|
29
|
+
* comparison).
|
|
30
|
+
*/
|
|
31
|
+
export declare function buildKeyset(sorts: SortItem[], primaryKey: string): SortItem[];
|
|
32
|
+
/**
|
|
33
|
+
* Extracts the keyset values from a fetched row, in keyset column order.
|
|
34
|
+
* Supports dotted relation paths (e.g. `author.name`) by walking the object.
|
|
35
|
+
*/
|
|
36
|
+
export declare function extractCursorValues(row: Record<string, unknown>, keyset: SortItem[]): CursorValues;
|
|
37
|
+
//# sourceMappingURL=cursor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cursor.d.ts","sourceRoot":"","sources":["../../src/pagination/cursor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAE5C;;;;GAIG;AACH,MAAM,MAAM,YAAY,GAAG,OAAO,EAAE,CAAC;AAErC;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CAOzD;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CAahE;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,QAAQ,EAAE,EAAE,UAAU,EAAE,MAAM,GAAG,QAAQ,EAAE,CAK7E;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC5B,MAAM,EAAE,QAAQ,EAAE,GACjB,YAAY,CAUd"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Encodes keyset cursor values into a compact, URL-safe opaque string
|
|
3
|
+
* (base64url of a JSON array). The shape is intentionally opaque to clients —
|
|
4
|
+
* only this module reads it back.
|
|
5
|
+
*
|
|
6
|
+
* `Date` values are encoded as `{ $d: <iso> }` so they round-trip to `Date`
|
|
7
|
+
* instances on decode (plain JSON would yield a string and break date keyset
|
|
8
|
+
* comparisons).
|
|
9
|
+
*/
|
|
10
|
+
export function encodeCursor(values) {
|
|
11
|
+
// Pre-map Date values to a tagged form. We cannot detect dates in the
|
|
12
|
+
// JSON.stringify replacer because Date.toJSON() has already converted them to
|
|
13
|
+
// strings by the time the replacer sees the value.
|
|
14
|
+
const tagged = values.map((v) => (v instanceof Date ? { $d: v.toISOString() } : v));
|
|
15
|
+
const json = JSON.stringify(tagged);
|
|
16
|
+
return Buffer.from(json, 'utf8').toString('base64url');
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Decodes an opaque cursor string back into its keyset values. Returns `null`
|
|
20
|
+
* when the cursor is malformed (bad base64, bad JSON, or not an array) so the
|
|
21
|
+
* caller can ignore an invalid cursor instead of crashing.
|
|
22
|
+
*/
|
|
23
|
+
export function decodeCursor(cursor) {
|
|
24
|
+
try {
|
|
25
|
+
const json = Buffer.from(cursor, 'base64url').toString('utf8');
|
|
26
|
+
const parsed = JSON.parse(json, (_key, value) => {
|
|
27
|
+
if (value && typeof value === 'object' && typeof value.$d === 'string') {
|
|
28
|
+
return new Date(value.$d);
|
|
29
|
+
}
|
|
30
|
+
return value;
|
|
31
|
+
});
|
|
32
|
+
return Array.isArray(parsed) ? parsed : null;
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Builds the keyset `SortItem[]` for cursor pagination: the caller's effective
|
|
40
|
+
* sorts, with a stable primary-key tiebreaker appended if it is not already
|
|
41
|
+
* present. The tiebreaker inherits the direction of the last sort column so the
|
|
42
|
+
* overall ordering stays monotonic (important for a correct `(cols, pk) > (...)`
|
|
43
|
+
* comparison).
|
|
44
|
+
*/
|
|
45
|
+
export function buildKeyset(sorts, primaryKey) {
|
|
46
|
+
const hasPk = sorts.some((s) => s.field === primaryKey);
|
|
47
|
+
if (hasPk)
|
|
48
|
+
return sorts;
|
|
49
|
+
const lastDirection = sorts[sorts.length - 1]?.direction ?? 'asc';
|
|
50
|
+
return [...sorts, { field: primaryKey, direction: lastDirection }];
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Extracts the keyset values from a fetched row, in keyset column order.
|
|
54
|
+
* Supports dotted relation paths (e.g. `author.name`) by walking the object.
|
|
55
|
+
*/
|
|
56
|
+
export function extractCursorValues(row, keyset) {
|
|
57
|
+
return keyset.map((s) => {
|
|
58
|
+
if (!s.field.includes('.'))
|
|
59
|
+
return row[s.field];
|
|
60
|
+
let current = row;
|
|
61
|
+
for (const segment of s.field.split('.')) {
|
|
62
|
+
if (current == null || typeof current !== 'object')
|
|
63
|
+
return undefined;
|
|
64
|
+
current = current[segment];
|
|
65
|
+
}
|
|
66
|
+
return current;
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=cursor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cursor.js","sourceRoot":"","sources":["../../src/pagination/cursor.ts"],"names":[],"mappings":"AASA;;;;;;;;GAQG;AACH,MAAM,UAAU,YAAY,CAAC,MAAoB;IAC/C,sEAAsE;IACtE,8EAA8E;IAC9E,mDAAmD;IACnD,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,YAAY,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACpF,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;IACpC,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;AACzD,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAAC,MAAc;IACzC,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QAC/D,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE;YAC9C,IAAI,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,OAAO,KAAK,CAAC,EAAE,KAAK,QAAQ,EAAE,CAAC;gBACvE,OAAO,IAAI,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;YAC5B,CAAC;YACD,OAAO,KAAK,CAAC;QACf,CAAC,CAAC,CAAC;QACH,OAAO,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC;IAC/C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,WAAW,CAAC,KAAiB,EAAE,UAAkB;IAC/D,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,UAAU,CAAC,CAAC;IACxD,IAAI,KAAK;QAAE,OAAO,KAAK,CAAC;IACxB,MAAM,aAAa,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,EAAE,SAAS,IAAI,KAAK,CAAC;IAClE,OAAO,CAAC,GAAG,KAAK,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,aAAa,EAAE,CAAC,CAAC;AACrE,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CACjC,GAA4B,EAC5B,MAAkB;IAElB,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;QACtB,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC;YAAE,OAAO,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;QAChD,IAAI,OAAO,GAAY,GAAG,CAAC;QAC3B,KAAK,MAAM,OAAO,IAAI,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;YACzC,IAAI,OAAO,IAAI,IAAI,IAAI,OAAO,OAAO,KAAK,QAAQ;gBAAE,OAAO,SAAS,CAAC;YACrE,OAAO,GAAI,OAAmC,CAAC,OAAO,CAAC,CAAC;QAC1D,CAAC;QACD,OAAO,OAAO,CAAC;IACjB,CAAC,CAAC,CAAC;AACL,CAAC"}
|
package/dist/runner.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { type Type } from '@nestjs/common';
|
|
|
2
2
|
import { ModuleRef } from '@nestjs/core';
|
|
3
3
|
import type { FilterAdapter } from './adapter/adapter.js';
|
|
4
4
|
import type { ContextAccessor } from './context-accessor.js';
|
|
5
|
-
import type { EntityDescription, FilterContext, FilterModuleOptions, SortItem } from './types.js';
|
|
5
|
+
import type { CursorPage, EntityDescription, FilterContext, FilterModuleOptions, SortItem } from './types.js';
|
|
6
6
|
export declare class FilterRunner {
|
|
7
7
|
private readonly moduleRef;
|
|
8
8
|
private readonly options;
|
|
@@ -46,7 +46,9 @@ export declare class FilterRunner {
|
|
|
46
46
|
* the `meta.fields` payload of generic, table-name-driven endpoints.
|
|
47
47
|
*/
|
|
48
48
|
describe(entity: Type<unknown>): EntityDescription;
|
|
49
|
-
apply<F extends object, Q>(FilterClass: Type<F>, input: unknown, qb: Q, context?: FilterContext
|
|
49
|
+
apply<F extends object, Q>(FilterClass: Type<F>, input: unknown, qb: Q, context?: FilterContext, internal?: {
|
|
50
|
+
native?: boolean;
|
|
51
|
+
}): Promise<Q>;
|
|
50
52
|
private resolveFilter;
|
|
51
53
|
private runSetup;
|
|
52
54
|
/**
|
|
@@ -125,7 +127,10 @@ export declare class FilterRunner {
|
|
|
125
127
|
* @param context - Optional filter context.
|
|
126
128
|
* @returns The query builder with filters applied.
|
|
127
129
|
*/
|
|
128
|
-
applyDynamic<Q>(entity: Type<unknown>, input: unknown, qb: Q, context?: FilterContext
|
|
130
|
+
applyDynamic<Q>(entity: Type<unknown>, input: unknown, qb: Q, context?: FilterContext, internal?: {
|
|
131
|
+
skipSortAndPagination?: boolean;
|
|
132
|
+
native?: boolean;
|
|
133
|
+
}): Promise<Q>;
|
|
129
134
|
/**
|
|
130
135
|
* Runs a dynamic query and **executes** it, returning the page of rows plus
|
|
131
136
|
* the total count — with pagination-safe relation loading. Unlike
|
|
@@ -146,6 +151,31 @@ export declare class FilterRunner {
|
|
|
146
151
|
rows: E[];
|
|
147
152
|
total: number;
|
|
148
153
|
}>;
|
|
154
|
+
/**
|
|
155
|
+
* Runs a dynamic query with **keyset (cursor) pagination** and executes it,
|
|
156
|
+
* returning a stable, non-overlapping page plus opaque forward/backward
|
|
157
|
+
* cursors. Unlike offset pagination (which drifts and re-scans as rows are
|
|
158
|
+
* inserted), keyset pagination seeks past a boundary row using a
|
|
159
|
+
* `WHERE (sortcols, pk) > (...)` predicate, so it is O(1) per page and stable
|
|
160
|
+
* under concurrent writes.
|
|
161
|
+
*
|
|
162
|
+
* The keyset is the request's effective sort (or `defaultSort`) plus the
|
|
163
|
+
* entity's primary key as a stable tiebreaker. Multi-column sort composes
|
|
164
|
+
* naturally. Direction is honored per column (asc → seek forward with `>`,
|
|
165
|
+
* desc → with `<`); for backward paging (`before`) the comparison and order
|
|
166
|
+
* are reversed internally and the result re-reversed so `items` is always in
|
|
167
|
+
* the requested order.
|
|
168
|
+
*
|
|
169
|
+
* Requires an adapter implementing `getResult`, `getPrimaryKey`,
|
|
170
|
+
* `applyKeysetPagination` and `applyKeysetOrderAndLimit`. Additive — does not
|
|
171
|
+
* change `apply`/`applyDynamic`/`findAndCount`.
|
|
172
|
+
*/
|
|
173
|
+
findPage<E>(entity: Type<E>, input: unknown, opts?: {
|
|
174
|
+
qb?: unknown;
|
|
175
|
+
context?: FilterContext;
|
|
176
|
+
}): Promise<CursorPage<E>>;
|
|
177
|
+
/** Flips every keyset column's direction (for backward cursor paging). */
|
|
178
|
+
private reverseKeyset;
|
|
149
179
|
/**
|
|
150
180
|
* Splits include paths into join-safe (to-one) and deferred (to-many) sets,
|
|
151
181
|
* by the cardinality of each path's first relation segment.
|
|
@@ -159,11 +189,47 @@ export declare class FilterRunner {
|
|
|
159
189
|
* No-op (pass-through) when the adapter exposes no metadata.
|
|
160
190
|
*/
|
|
161
191
|
private pruneUnknownColumnFilters;
|
|
192
|
+
/**
|
|
193
|
+
* Enforces per-field operator allowlists on a `where` ColumnFilter tree.
|
|
194
|
+
*
|
|
195
|
+
* For each clause whose `field` carries an operator restriction (declared as
|
|
196
|
+
* `{ field, operators }` in `@Filterable.allowed`), the clause's operator
|
|
197
|
+
* (after alias normalization) must be in the permitted set. A disallowed
|
|
198
|
+
* operator is dropped (default) or raises a `BadRequestException` when
|
|
199
|
+
* `throwOnInvalid` is set. Fields without a restriction (plain-string allowed
|
|
200
|
+
* entries, or no allowlist at all) pass through unchanged. Recurses AND/OR.
|
|
201
|
+
*/
|
|
202
|
+
private enforceOperatorAllowlist;
|
|
203
|
+
/**
|
|
204
|
+
* Enforces a per-field operator allowlist on an auto-field value, mirroring
|
|
205
|
+
* the adapter's value-shape interpretation:
|
|
206
|
+
*
|
|
207
|
+
* - scalar → `equals`
|
|
208
|
+
* - array → `in`
|
|
209
|
+
* - operator object (`{ gte, lte }`) → each key is an operator
|
|
210
|
+
*
|
|
211
|
+
* Returns the value to apply, or `undefined` when the whole auto-field should
|
|
212
|
+
* be skipped (e.g. a scalar whose implied `equals` is not permitted, or an
|
|
213
|
+
* operator object reduced to no permitted keys). For operator objects, only
|
|
214
|
+
* the disallowed keys are stripped. When `throwOnInvalid` is set, a
|
|
215
|
+
* disallowed operator raises instead of being dropped.
|
|
216
|
+
*/
|
|
217
|
+
private enforceAutoFieldOperators;
|
|
162
218
|
/**
|
|
163
219
|
* Applies global search for dynamic mode: auto-detects all string columns
|
|
164
220
|
* from entity metadata (no filter class with static search config).
|
|
165
221
|
*/
|
|
166
222
|
private applyGlobalSearchDynamic;
|
|
223
|
+
/**
|
|
224
|
+
* Resolves the effective `throwOnInvalid` policy: per-@Filterable wins over
|
|
225
|
+
* the module option, which defaults to `false` (silent-drop, legacy behavior).
|
|
226
|
+
*/
|
|
227
|
+
private resolveThrowOnInvalid;
|
|
228
|
+
/**
|
|
229
|
+
* Resolves the effective `defaultSort`: per-@Filterable wins over the module
|
|
230
|
+
* option. Returns undefined when neither is set.
|
|
231
|
+
*/
|
|
232
|
+
private resolveDefaultSort;
|
|
167
233
|
private handleUnknownKey;
|
|
168
234
|
/**
|
|
169
235
|
* Parses raw sort input into an array of SortItem objects.
|
|
@@ -181,6 +247,15 @@ export declare class FilterRunner {
|
|
|
181
247
|
* Silently skips invalid fields.
|
|
182
248
|
*/
|
|
183
249
|
private validateSorts;
|
|
250
|
+
/**
|
|
251
|
+
* Applies a list of sorts, routing computed/virtual aliases to
|
|
252
|
+
* `applyComputedSort` (their dev-provided SQL expression) and regular columns
|
|
253
|
+
* to `applySort`. Order is preserved across the mix so the resulting
|
|
254
|
+
* `ORDER BY` matches the requested column order. Regular columns are still
|
|
255
|
+
* validated against the allowlist / entity metadata; computed aliases bypass
|
|
256
|
+
* that check because they are dev-declared, not real columns.
|
|
257
|
+
*/
|
|
258
|
+
private applySortsWithComputed;
|
|
184
259
|
/**
|
|
185
260
|
* Parses raw distinct input into an array of field names.
|
|
186
261
|
*
|
package/dist/runner.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"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"}
|