nestjs-paginate 14.0.0 → 15.0.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/README.md CHANGED
@@ -41,11 +41,9 @@ updateGlobalConfig({
41
41
  defaultOrigin: undefined,
42
42
  defaultLimit: 20,
43
43
  defaultMaxLimit: 100,
44
- });
44
+ })
45
45
  ```
46
46
 
47
-
48
-
49
47
  ### Example
50
48
 
51
49
  The following code exposes a route that can be utilized like so:
@@ -270,12 +268,12 @@ const paginateConfig: PaginateConfig<CatEntity> {
270
268
  * Description: TypeORM partial selection. Limit selection further by using `select` query param.
271
269
  * https://typeorm.io/select-query-builder#partial-selection
272
270
  * Note: if you do not contain the primary key in the select array, primary key will be added automatically.
273
- *
271
+ *
274
272
  * Wildcard support:
275
273
  * - Use '*' to select all columns from the main entity.
276
274
  * - Use 'relation.*' to select all columns from a relation.
277
275
  * - Use 'relation.subrelation.*' to select all columns from nested relations.
278
- *
276
+ *
279
277
  * Examples:
280
278
  * select: ['*'] - Selects all columns from main entity
281
279
  * select: ['id', 'name', 'toys.*'] - Selects id, name from main entity and all columns from toys relation
@@ -330,7 +328,7 @@ const paginateConfig: PaginateConfig<CatEntity> {
330
328
  * Type: RelationColumn<CatEntity>
331
329
  * Description: Indicates what relations of entity should be loaded.
332
330
  */
333
- relations: [],
331
+ relations: {},
334
332
 
335
333
  /**
336
334
  * Required: false
@@ -495,7 +493,7 @@ http://localhost:3000/cats?filter.toys.name=$in:Mouse,String
495
493
 
496
494
  ```typescript
497
495
  const config: PaginateConfig<CatEntity> = {
498
- relations: ['toys'],
496
+ relations: { toys: true },
499
497
  sortableColumns: ['id', 'name', 'toys.name'],
500
498
  filterableColumns: {
501
499
  'toys.name': [FilterOperator.IN],
@@ -511,7 +509,7 @@ const result = await paginate<CatEntity>(query, catRepo, config)
511
509
  const config: PaginateConfig<CatEntity> = {
512
510
  sortableColumns: ['id', 'name', 'toys.(size.height)', 'toys.(size.width)'],
513
511
  searchableColumns: ['name'],
514
- relations: ['toys'],
512
+ relations: { toys: true },
515
513
  }
516
514
  ```
517
515
 
@@ -552,6 +550,7 @@ Quantifiers define how many related rows must satisfy the condition:
552
550
  - `$none`: no related rows match the condition
553
551
 
554
552
  ### Examples
553
+
555
554
  Assume `CatEntity` has a one‑to‑many relation `toys: CatToyEntity[]` where `CatToyEntity` has a string column `name`.
556
555
 
557
556
  - At least one toy named exactly "Ball":
@@ -584,46 +583,20 @@ Assume `CatEntity` has a one‑to‑many relation `toys: CatToyEntity[]` where `
584
583
  GET /cats?filter.toys.name=$any:$not:$eq:Squeaky
585
584
  ```
586
585
 
587
- ### AND-mode: entity must have ALL of the specified related values
586
+ ### Requiring ALL of several related values
588
587
 
589
- Use the `$and` comparator to require that a parent entity has **all** of the specified related values.
590
- Each `$and` value produces a separate correlated EXISTS subquery, ANDed on the outer query.
588
+ To require that a parent has **all** of several related values, use a [`filter=` expression](#filter-expressions-filter)
589
+ with one term per value. Each relation term is an independent `EXISTS`, so ANDing them means
590
+ "has a toy named Ball **and** has a toy named Mouse":
591
591
 
592
- Enable it in `filterableColumns`:
593
-
594
- ```typescript
595
- filterableColumns: {
596
- 'toys.name': [FilterOperator.EQ, FilterComparator.AND],
597
- }
592
+ ```url
593
+ GET /cats?filter=toys.name=$eq:Ball AND toys.name=$eq:Mouse
598
594
  ```
599
595
 
600
- - Cat must have **both** a toy named "Ball" **and** a toy named "Mouse":
601
-
602
- ```url
603
- GET /cats?filter.toys.name=$and:Ball&filter.toys.name=$and:Mouse
604
- ```
605
-
606
- - Cat must have a toy named "Ball" **and** be friends with a cat named "Garfield" (two independent to-many paths):
607
-
608
- ```url
609
- GET /cats?filter.toys.name=$and:Ball&filter.friends.name=$and:Garfield
610
- ```
611
-
612
- **Restrictions and performance notes**:
596
+ This also composes across independent relation paths, e.g. has a Ball toy and is friends with Garfield:
613
597
 
614
- - `$and` may only be used on to-many relationship columns (one-to-many or many-to-many).
615
- - `$and` values may not be mixed with non-`$and` values on the same sub-column.
616
- - `$and` may not be combined with `$none` or `$all` quantifiers.
617
- - Each `$and` value adds one correlated EXISTS subquery. For N values and a relation path of depth D, this produces N × D joins. The default cap is 20 values per sub-column; override with `maxAndValues` in `PaginateConfig`.
618
-
619
- ```typescript
620
- const config: PaginateConfig<CatEntity> = {
621
- sortableColumns: ['id'],
622
- filterableColumns: {
623
- 'toys.name': [FilterOperator.EQ, FilterComparator.AND],
624
- },
625
- maxAndValues: 10, // optional, default is 20
626
- }
598
+ ```url
599
+ GET /cats?filter=toys.name=$eq:Ball AND friends.name=$eq:Garfield
627
600
  ```
628
601
 
629
602
  ## Usage with Eager Loading
@@ -678,7 +651,7 @@ or its nemesis's age when it has no best friend.
678
651
  const config: PaginateConfig<CatEntity> = {
679
652
  // Every column used in a group must be listed in sortableColumns.
680
653
  sortableColumns: ['id', 'bestFriend.age', 'nemesis.age'],
681
- relations: ['bestFriend', 'nemesis'],
654
+ relations: { bestFriend: true, nemesis: true },
682
655
  }
683
656
  ```
684
657
 
@@ -739,27 +712,107 @@ const config: PaginateConfig<CatEntity> = {
739
712
 
740
713
  `?filter.roles=$contains:moderator,admin` where column `roles` is an array and contains the values `moderator` and `admin`
741
714
 
715
+ ### Filter expressions (`filter=`)
716
+
717
+ The per-column `filter.<column>=` parameters above are always combined with `AND`. For
718
+ arbitrary boolean logic, pass a single `filter=` expression instead. It uses the same
719
+ column and `$op:value` syntax, combined with `AND`, `OR`, `NOT` (case-insensitive,
720
+ precedence `NOT` > `AND` > `OR`) and parentheses:
721
+
722
+ ```
723
+ ?filter=color=$eq:black AND age=$gte:3
724
+ ?filter=(color=$eq:black OR color=$eq:white) AND NOT name=$eq:Leche
725
+ ?filter=home.name=$eq:House AND toys.name=$eq:String
726
+ ```
727
+
728
+ - The columns and operators are validated against `filterableColumns`, exactly like the
729
+ per-column form. An unknown column or disallowed operator always returns `400 Bad Request`.
730
+ - Relation columns are matched with correlated `EXISTS` subqueries, so they compose under
731
+ `OR`/`NOT` and never join the relation into the result set. `NOT toys.name=$eq:Ball` means
732
+ "no matching toy exists".
733
+ - Root, embedded, [JSONB key-path](#jsonb-support), and [polymorphic (`~`)](#polymorphic-columns-)
734
+ columns are all valid leaves and apply as direct conditions. Every operator, suffix and
735
+ quantifier from the per-column form works unchanged (e.g. `age=$btw:3,5`, `age=$not:$null`,
736
+ `toys.name=$none:$eq:Ball`); JSONB filtering keeps its PostgreSQL/CockroachDB-only limitation.
737
+ - A value containing whitespace or parentheses must be quoted with `"` or `'`:
738
+ `?filter=home.name=$eq:"Cat Mansion"`.
739
+
740
+ Inside a quoted value, a backslash escapes a following quote or backslash (`\"`, `\'`, `\\`),
741
+ so a value can contain either quote character:
742
+
743
+ ```
744
+ ?filter=name=$eq:"Milo \"the cat\"" # value: Milo "the cat"
745
+ ?filter=name=$eq:'it\'s mine' # value: it's mine
746
+ ?filter=name=$eq:"a\"b\'c\\d" # value: a"b'c\d
747
+ ```
748
+
749
+ Any other backslash is kept literal, so Windows paths and regexes need no doubling
750
+ (`?filter=path=$eq:"C:\Users"` → `C:\Users`). You can also switch quote styles instead of
751
+ escaping — `?filter=name=$eq:'O'"'"'Malley'`-style concatenation still works — but the
752
+ backslash form is usually clearer. Doubling a quote does **not** escape it
753
+ (`"say ""hi"""` yields `say hi`). Remember to URL-encode the `filter=` value; the examples
754
+ above are shown decoded for readability.
755
+
756
+ The value-level `$not` suffix (negating a single comparison, e.g. `color=$not:$eq:white`) is
757
+ distinct from the boolean `NOT` (negating a whole term or group).
758
+
759
+ Because the expression is parsed recursively, an unbounded expression is a denial-of-service
760
+ vector: a deeply nested or very wide payload can exhaust the call stack or blow up the
761
+ generated SQL. Every leaf, `AND`/`OR`/`NOT` operator, and parenthesised group counts as one
762
+ node, and the total is capped at **100** by default. Override the cap per endpoint with
763
+ `filterExpressionMaxComplexity` in the config, or globally via `updateGlobalConfig({ defaultFilterExpressionMaxComplexity })`.
764
+ An expression over the limit returns `400 Bad Request`.
765
+
766
+ ```typescript
767
+ const config: PaginateConfig<CatEntity> = {
768
+ sortableColumns: ['id'],
769
+ filterableColumns: { color: true, name: true },
770
+ filterExpressionMaxComplexity: 50, // reject filter= expressions with more than 50 nodes
771
+ }
772
+ ```
773
+
774
+ ### Polymorphic columns (`~`)
775
+
776
+ A filter column may group several columns with `~` to filter on their `COALESCE` — the first
777
+ non-null value per row (the same `~` syntax as polymorphic sorting). This works in both the
778
+ `filter=` expression and the per-column form:
779
+
780
+ ```url
781
+ ?filter=bestFriend.age~nemesis.age=$eq:4
782
+ ?filter.bestFriend.age~nemesis.age=$gte:5
783
+ ```
784
+
785
+ Each part must be a plain or **to-one** relation column (not embedded, virtual, JSONB, or to-many).
786
+ Relation parts are left-joined automatically and are not added to the result set. Parts may be
787
+ **nested** (`a.b.c.leaf`) as long as every segment before the leaf is a to-one relation; each hop is
788
+ joined step by step, and a prefix shared across parts (e.g. two parts both starting `a.b`) reuses the
789
+ same join rather than colliding:
790
+
791
+ ```url
792
+ ?filter=bestFriend.home.street~home.street=$eq:Downtown
793
+ ```
794
+
742
795
  ## JSONB Support
743
796
 
744
797
  You can sort, search, and filter on JSONB columns using dot notation to access nested fields.
745
798
 
746
799
  ### Database support matrix
747
800
 
748
- | Feature | PostgreSQL / CockroachDB | MySQL / MariaDB | SQLite |
749
- |---------|---|---|---|
750
- | **Sorting** (`sortableColumns`) | Yes (`#>>`) | Yes (`JSON_UNQUOTE(JSON_EXTRACT(...))`) | Yes (`json_extract`) |
751
- | **Searching** (`searchableColumns`) | Yes | Yes | Yes |
752
- | **Filtering** (`$eq`, `$in`, `$contains`) | Yes (`@>` containment) | No | No |
801
+ | Feature | PostgreSQL / CockroachDB | MySQL / MariaDB | SQLite |
802
+ | ----------------------------------------- | ------------------------ | --------------------------------------- | -------------------- |
803
+ | **Sorting** (`sortableColumns`) | Yes (`#>>`) | Yes (`JSON_UNQUOTE(JSON_EXTRACT(...))`) | Yes (`json_extract`) |
804
+ | **Searching** (`searchableColumns`) | Yes | Yes | Yes |
805
+ | **Filtering** (`$eq`, `$in`, `$contains`) | Yes (`@>` containment) | No | No |
753
806
 
754
807
  > **Note:** Sorting and searching on JSONB paths is supported across all database engines — the library automatically uses the correct JSON extraction function for your DB. Filtering via `$eq`, `$in`, and `$contains` uses PostgreSQL's `@>` containment operator (via TypeORM's `JsonContains`) and is only supported on **PostgreSQL** and **CockroachDB**.
755
808
 
756
809
  ### Filtering operators (PostgreSQL / CockroachDB only)
757
810
 
758
- | Operator | Description |
759
- |----------|-------------|
811
+ | Operator | Description |
812
+ | ----------- | -------------------------------------------------------- |
760
813
  | `$eq` | Exact match via containment (`col @> '{"key":"value"}'`) |
761
- | `$in` | Match any of a comma-separated list of values |
762
- | `$contains` | Match if a JSON array contains the value |
814
+ | `$in` | Match any of a comma-separated list of values |
815
+ | `$contains` | Match if a JSON array contains the value |
763
816
 
764
817
  ### Direct JSONB column
765
818
 
@@ -781,7 +834,7 @@ where `settings` is a relation whose JSONB column `theme` is filtered.
781
834
 
782
835
  ```typescript
783
836
  const config: PaginateConfig<UserEntity> = {
784
- relations: ['settings'],
837
+ relations: { settings: true },
785
838
  filterableColumns: {
786
839
  'settings.theme': [FilterOperator.EQ, FilterOperator.IN],
787
840
  },
@@ -817,29 +870,16 @@ Each value is expanded into its own `@>` condition joined with `OR`:
817
870
  ?filter.metadata.status=$not:$in:banned,suspended
818
871
  ```
819
872
 
820
- ## Multi Filters
821
-
822
- Multi filters are filters that can be applied to a single column with a comparator.
823
-
824
- ### Examples
825
-
826
- `?filter.createdAt=$gt:2022-02-02&filter.createdAt=$lt:2022-02-10` where column `createdAt` is after `2022-02-02` **and** before `2022-02-10`
873
+ ## Combining filters on one column
827
874
 
828
- `?filter.roles=$contains:moderator&filter.roles=$or:$contains:admin` where column `roles` is an array and contains `moderator` **or** `admin`
875
+ Repeating a `filter.<column>=` parameter applies all of its conditions with **AND**, e.g. a range:
829
876
 
830
- `?filter.id=$gt:3&filter.id=$and:$lt:5&filter.id=$or:$eq:7` where column `id` is greater than `3` **and** less than `5` **or** equal to `7`
877
+ `?filter.createdAt=$gt:2022-02-02&filter.createdAt=$lt:2022-02-10` `createdAt` after `2022-02-02` **and** before `2022-02-10`
831
878
 
832
- **Note:** The `$and` comparators are not required. The above example is equivalent to:
879
+ For **OR** (within a column or across columns) and arbitrary boolean logic, use a
880
+ [`filter=` expression](#filter-expressions-filter):
833
881
 
834
- `?filter.id=$gt:3&filter.id=$lt:5&filter.id=$or:$eq:7`
835
-
836
- **Note:** The first comparator on the the first filter is ignored because the filters are grouped by the column name and chained with an `$and` to other filters.
837
-
838
- `...&filter.id=5&filter.id=$or:7&filter.name=Milo&...`
839
-
840
- is resolved to:
841
-
842
- `WHERE ... AND (id = 5 OR id = 7) AND name = 'Milo' AND ...`
882
+ `?filter=id=$eq:5 OR id=$eq:7` — `id` equal to `5` **or** `7`
843
883
 
844
884
  ## Cursor-based Pagination
845
885
 
@@ -928,7 +968,6 @@ export function CustomSortBy(paginationConfig: PaginateConfig<any>) {
928
968
  Now you can create your version of the whole docs decorator and use it
929
969
 
930
970
  ```typescript
931
-
932
971
  const CustomApiPaginationQuery = (paginationConfig: PaginateConfig<any>) => {
933
972
  return applyDecorators(
934
973
  ...[
@@ -946,13 +985,10 @@ const CustomApiPaginationQuery = (paginationConfig: PaginateConfig<any>) => {
946
985
  function CustomPaginatedSwaggerDocs<DTO extends Type<unknown>>(dto: DTO, paginatedConfig: PaginateConfig<any>) {
947
986
  return applyDecorators(ApiOkPaginatedResponse(dto, paginatedConfig), CustomApiPaginationQuery(paginatedConfig))
948
987
  }
949
-
950
988
  ```
951
989
 
952
990
  You can use CustomPaginatedSwaggerDocs instead of default PaginatedSwaggerDocs
953
991
 
954
-
955
-
956
992
  ## Troubleshooting
957
993
 
958
994
  The package does not report error reasons in the response bodies. They are instead
@@ -7,6 +7,8 @@ export interface PaginateQuery {
7
7
  filter?: {
8
8
  [column: string]: string | string[];
9
9
  };
10
+ /** Boolean filter expression from a single `filter=` query parameter. */
11
+ filterExpression?: string;
10
12
  select?: string[];
11
13
  cursor?: string;
12
14
  withDeleted?: boolean;
package/lib/decorator.js CHANGED
@@ -83,6 +83,7 @@ exports.Paginate = (0, common_1.createParamDecorator)((_data, ctx) => {
83
83
  search: query.search ? query.search.toString() : undefined,
84
84
  searchBy,
85
85
  filter: Object.keys(filter).length ? filter : undefined,
86
+ filterExpression: (0, lodash_1.isString)(query.filter) ? query.filter : undefined,
86
87
  select,
87
88
  cursor: query.cursor ? query.cursor.toString() : undefined,
88
89
  withDeleted: query.withDeleted === 'true' ? true : query.withDeleted === 'false' ? false : undefined,
@@ -1 +1 @@
1
- {"version":3,"file":"decorator.js","sourceRoot":"","sources":["../src/decorator.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"decorator.js","sourceRoot":"","sources":["../src/decorator.ts"],"names":[],"mappings":"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"}
@@ -106,6 +106,7 @@ describe('Decorator', () => {
106
106
  sortBy: undefined,
107
107
  search: undefined,
108
108
  searchBy: undefined,
109
+ filterExpression: undefined,
109
110
  filter: undefined,
110
111
  select: undefined,
111
112
  cursor: undefined,
@@ -122,6 +123,7 @@ describe('Decorator', () => {
122
123
  sortBy: undefined,
123
124
  search: undefined,
124
125
  searchBy: undefined,
126
+ filterExpression: undefined,
125
127
  filter: undefined,
126
128
  select: undefined,
127
129
  cursor: undefined,
@@ -129,6 +131,12 @@ describe('Decorator', () => {
129
131
  path: 'http://localhost/items',
130
132
  });
131
133
  });
134
+ it('should parse a bare filter= param into filterExpression', () => {
135
+ const context = expressContextFactory({ filter: 'color=$eq:black AND age=$gte:3' });
136
+ const result = decoratorfactory(null, context);
137
+ expect(result.filterExpression).toBe('color=$eq:black AND age=$gte:3');
138
+ expect(result.filter).toBeUndefined();
139
+ });
132
140
  it('should handle express defined query fields', () => {
133
141
  const context = expressContextFactory({
134
142
  page: '1',
@@ -151,6 +159,7 @@ describe('Decorator', () => {
151
159
  ],
152
160
  search: 'white',
153
161
  searchBy: undefined,
162
+ filterExpression: undefined,
154
163
  withDeleted: true,
155
164
  select: ['name', 'createdAt'],
156
165
  path: 'http://localhost/items',
@@ -193,6 +202,7 @@ describe('Decorator', () => {
193
202
  ],
194
203
  search: 'white',
195
204
  searchBy: undefined,
205
+ filterExpression: undefined,
196
206
  withDeleted: false,
197
207
  path: 'http://localhost/items',
198
208
  filter: {
@@ -221,6 +231,7 @@ describe('Decorator', () => {
221
231
  sortBy: [['NOTEXISTEN', 'BLABLA']],
222
232
  search: 'white',
223
233
  searchBy: undefined,
234
+ filterExpression: undefined,
224
235
  withDeleted: undefined,
225
236
  path: 'http://localhost/items',
226
237
  filter: {
@@ -1 +1 @@
1
- {"version":3,"file":"decorator.spec.js","sourceRoot":"","sources":["../src/decorator.spec.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"decorator.spec.js","sourceRoot":"","sources":["../src/decorator.spec.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Boolean filter expression parsed from a single `filter=` query parameter.
3
+ *
4
+ * Grammar (precedence: NOT > AND > OR), case-insensitive keywords:
5
+ * expr := or
6
+ * or := and (OR and)*
7
+ * and := not (AND not)*
8
+ * not := NOT not | primary
9
+ * primary := '(' expr ')' | leaf
10
+ * leaf := column '=' value // value uses the existing $op:operand syntax
11
+ *
12
+ * Tokens are whitespace-delimited; `(` and `)` are punctuation. A value containing
13
+ * whitespace or a `(` `)` must be quoted with `"` or `'`. Inside a quoted value a backslash
14
+ * escapes a following quote or backslash (`\"`, `\'`, `\\`); any other backslash is literal.
15
+ */
16
+ export type FilterExpression = {
17
+ type: 'and';
18
+ children: FilterExpression[];
19
+ } | {
20
+ type: 'or';
21
+ children: FilterExpression[];
22
+ } | {
23
+ type: 'not';
24
+ child: FilterExpression;
25
+ } | {
26
+ type: 'leaf';
27
+ column: string;
28
+ value: string;
29
+ };
30
+ /**
31
+ * Default cap on the number of nodes (leaves, AND/OR/NOT operators, and parenthesised
32
+ * groups) a single `filter=` expression may contain. Because the parser is recursive, an
33
+ * unbounded expression is a denial-of-service vector: a deeply nested or very wide payload
34
+ * can exhaust the call stack or blow up the generated SQL. 100 nodes comfortably covers any
35
+ * realistic query while keeping the parse cheap. Override per-endpoint via
36
+ * `PaginateConfig.filterExpressionMaxComplexity`.
37
+ */
38
+ export declare const DEFAULT_FILTER_EXPRESSION_MAX_COMPLEXITY = 100;
39
+ /** Filter expression with negation pushed onto the leaves, so only AND/OR groups remain. */
40
+ export type NormalizedFilterExpression = {
41
+ type: 'and';
42
+ children: NormalizedFilterExpression[];
43
+ } | {
44
+ type: 'or';
45
+ children: NormalizedFilterExpression[];
46
+ } | {
47
+ type: 'leaf';
48
+ column: string;
49
+ value: string;
50
+ negated: boolean;
51
+ };
52
+ export declare function parseFilterExpression(input: string, maxComplexity?: number): FilterExpression;
53
+ /** Pushes every NOT down onto the leaves via De Morgan, leaving only AND/OR groups. */
54
+ export declare function normalizeFilterExpression(node: FilterExpression, negated?: boolean): NormalizedFilterExpression;
@@ -0,0 +1,180 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DEFAULT_FILTER_EXPRESSION_MAX_COMPLEXITY = void 0;
4
+ exports.parseFilterExpression = parseFilterExpression;
5
+ exports.normalizeFilterExpression = normalizeFilterExpression;
6
+ const common_1 = require("@nestjs/common");
7
+ /**
8
+ * Default cap on the number of nodes (leaves, AND/OR/NOT operators, and parenthesised
9
+ * groups) a single `filter=` expression may contain. Because the parser is recursive, an
10
+ * unbounded expression is a denial-of-service vector: a deeply nested or very wide payload
11
+ * can exhaust the call stack or blow up the generated SQL. 100 nodes comfortably covers any
12
+ * realistic query while keeping the parse cheap. Override per-endpoint via
13
+ * `PaginateConfig.filterExpressionMaxComplexity`.
14
+ */
15
+ exports.DEFAULT_FILTER_EXPRESSION_MAX_COMPLEXITY = 100;
16
+ const WHITESPACE = new Set([' ', '\t', '\n', '\r']);
17
+ function tokenize(input) {
18
+ const tokens = [];
19
+ let i = 0;
20
+ while (i < input.length) {
21
+ const c = input[i];
22
+ if (WHITESPACE.has(c)) {
23
+ i++;
24
+ continue;
25
+ }
26
+ if (c === '(') {
27
+ tokens.push({ type: 'lparen' });
28
+ i++;
29
+ continue;
30
+ }
31
+ if (c === ')') {
32
+ tokens.push({ type: 'rparen' });
33
+ i++;
34
+ continue;
35
+ }
36
+ let raw = '';
37
+ let quoted = false;
38
+ while (i < input.length) {
39
+ const ch = input[i];
40
+ if (WHITESPACE.has(ch) || ch === '(' || ch === ')')
41
+ break;
42
+ if (ch === '"' || ch === "'") {
43
+ quoted = true;
44
+ i++;
45
+ while (i < input.length && input[i] !== ch) {
46
+ // Inside a quoted span a backslash escapes a following quote or backslash,
47
+ // so either quote character (and a literal backslash) can appear without
48
+ // switching quote styles. Any other backslash is kept literal, leaving
49
+ // values such as Windows paths or regexes untouched.
50
+ if (input[i] === '\\' && i + 1 < input.length) {
51
+ const next = input[i + 1];
52
+ if (next === '"' || next === "'" || next === '\\') {
53
+ raw += next;
54
+ i += 2;
55
+ continue;
56
+ }
57
+ }
58
+ raw += input[i];
59
+ i++;
60
+ }
61
+ if (i >= input.length) {
62
+ throw new common_1.BadRequestException('Unterminated quote in filter expression');
63
+ }
64
+ i++;
65
+ continue;
66
+ }
67
+ raw += ch;
68
+ i++;
69
+ }
70
+ if (!quoted) {
71
+ const keyword = raw.toUpperCase();
72
+ if (keyword === 'AND' || keyword === 'OR' || keyword === 'NOT') {
73
+ tokens.push({ type: keyword.toLowerCase() });
74
+ continue;
75
+ }
76
+ }
77
+ tokens.push({ type: 'leaf', raw });
78
+ }
79
+ return tokens;
80
+ }
81
+ function parse(tokens, maxComplexity) {
82
+ let pos = 0;
83
+ const peek = () => tokens[pos];
84
+ // Every leaf, AND/OR/NOT operator, and parenthesised descent counts as one unit of
85
+ // complexity. Tallied as the tree is built and checked eagerly, so a hostile payload is
86
+ // rejected before it can nest deep enough to overflow the recursion stack.
87
+ let complexity = 0;
88
+ const spend = () => {
89
+ if (++complexity > maxComplexity) {
90
+ throw new common_1.BadRequestException(`Filter expression is too complex (max ${maxComplexity} nodes)`);
91
+ }
92
+ };
93
+ function parseOr() {
94
+ var _a;
95
+ const children = [parseAnd()];
96
+ while (((_a = peek()) === null || _a === void 0 ? void 0 : _a.type) === 'or') {
97
+ pos++;
98
+ spend();
99
+ children.push(parseAnd());
100
+ }
101
+ return children.length === 1 ? children[0] : { type: 'or', children };
102
+ }
103
+ function parseAnd() {
104
+ var _a;
105
+ const children = [parseNot()];
106
+ while (((_a = peek()) === null || _a === void 0 ? void 0 : _a.type) === 'and') {
107
+ pos++;
108
+ spend();
109
+ children.push(parseNot());
110
+ }
111
+ return children.length === 1 ? children[0] : { type: 'and', children };
112
+ }
113
+ function parseNot() {
114
+ var _a;
115
+ if (((_a = peek()) === null || _a === void 0 ? void 0 : _a.type) === 'not') {
116
+ pos++;
117
+ spend();
118
+ return { type: 'not', child: parseNot() };
119
+ }
120
+ return parsePrimary();
121
+ }
122
+ function parsePrimary() {
123
+ var _a;
124
+ const token = peek();
125
+ if (!token) {
126
+ throw new common_1.BadRequestException('Unexpected end of filter expression');
127
+ }
128
+ if (token.type === 'lparen') {
129
+ pos++;
130
+ spend();
131
+ const expr = parseOr();
132
+ if (((_a = peek()) === null || _a === void 0 ? void 0 : _a.type) !== 'rparen') {
133
+ throw new common_1.BadRequestException('Expected ")" in filter expression');
134
+ }
135
+ pos++;
136
+ return expr;
137
+ }
138
+ if (token.type === 'leaf') {
139
+ pos++;
140
+ spend();
141
+ const eq = token.raw.indexOf('=');
142
+ if (eq < 1) {
143
+ throw new common_1.BadRequestException(`Invalid filter expression term "${token.raw}", expected "column=value"`);
144
+ }
145
+ return { type: 'leaf', column: token.raw.slice(0, eq), value: token.raw.slice(eq + 1) };
146
+ }
147
+ throw new common_1.BadRequestException(`Unexpected "${token.type}" in filter expression`);
148
+ }
149
+ if (tokens.length === 0) {
150
+ throw new common_1.BadRequestException('Empty filter expression');
151
+ }
152
+ const expr = parseOr();
153
+ if (pos < tokens.length) {
154
+ throw new common_1.BadRequestException('Unexpected trailing tokens in filter expression');
155
+ }
156
+ return expr;
157
+ }
158
+ function parseFilterExpression(input, maxComplexity = exports.DEFAULT_FILTER_EXPRESSION_MAX_COMPLEXITY) {
159
+ return parse(tokenize(input), maxComplexity);
160
+ }
161
+ /** Pushes every NOT down onto the leaves via De Morgan, leaving only AND/OR groups. */
162
+ function normalizeFilterExpression(node, negated = false) {
163
+ switch (node.type) {
164
+ case 'leaf':
165
+ return { type: 'leaf', column: node.column, value: node.value, negated };
166
+ case 'not':
167
+ return normalizeFilterExpression(node.child, !negated);
168
+ case 'and':
169
+ return {
170
+ type: negated ? 'or' : 'and',
171
+ children: node.children.map((child) => normalizeFilterExpression(child, negated)),
172
+ };
173
+ case 'or':
174
+ return {
175
+ type: negated ? 'and' : 'or',
176
+ children: node.children.map((child) => normalizeFilterExpression(child, negated)),
177
+ };
178
+ }
179
+ }
180
+ //# sourceMappingURL=filter-expression.js.map