@rapidrest/service-core 1.8.0 → 2.1.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.
Files changed (87) hide show
  1. package/dist/lib/ApiErrors.js +10 -0
  2. package/dist/lib/ApiErrors.js.map +1 -1
  3. package/dist/lib/BackgroundServiceManager.js +51 -9
  4. package/dist/lib/BackgroundServiceManager.js.map +1 -1
  5. package/dist/lib/EventListenerManager.js +35 -2
  6. package/dist/lib/EventListenerManager.js.map +1 -1
  7. package/dist/lib/NetUtils.js +215 -21
  8. package/dist/lib/NetUtils.js.map +1 -1
  9. package/dist/lib/ObjectFactory.js +7 -2
  10. package/dist/lib/ObjectFactory.js.map +1 -1
  11. package/dist/lib/RateLimiter.js.map +1 -1
  12. package/dist/lib/Server.js +121 -80
  13. package/dist/lib/Server.js.map +1 -1
  14. package/dist/lib/auth/AuthMiddleware.js +160 -100
  15. package/dist/lib/auth/AuthMiddleware.js.map +1 -1
  16. package/dist/lib/auth/JWTStrategy.js +6 -2
  17. package/dist/lib/auth/JWTStrategy.js.map +1 -1
  18. package/dist/lib/database/ConnectionManager.js +47 -1
  19. package/dist/lib/database/ConnectionManager.js.map +1 -1
  20. package/dist/lib/database/DatabaseErrors.js +88 -0
  21. package/dist/lib/database/DatabaseErrors.js.map +1 -0
  22. package/dist/lib/database/MongoRepository.js +31 -3
  23. package/dist/lib/database/MongoRepository.js.map +1 -1
  24. package/dist/lib/database/MongoSchemaSync.js +7 -1
  25. package/dist/lib/database/MongoSchemaSync.js.map +1 -1
  26. package/dist/lib/database/TypeOrmSupport.js +56 -6
  27. package/dist/lib/database/TypeOrmSupport.js.map +1 -1
  28. package/dist/lib/database/index.js +1 -0
  29. package/dist/lib/database/index.js.map +1 -1
  30. package/dist/lib/decorators/PersistenceDecorators.js +23 -0
  31. package/dist/lib/decorators/PersistenceDecorators.js.map +1 -1
  32. package/dist/lib/decorators/RouteDecorators.js +4 -2
  33. package/dist/lib/decorators/RouteDecorators.js.map +1 -1
  34. package/dist/lib/http/bun/BunRouter.js +119 -9
  35. package/dist/lib/http/bun/BunRouter.js.map +1 -1
  36. package/dist/lib/http/index.js +1 -0
  37. package/dist/lib/http/index.js.map +1 -1
  38. package/dist/lib/http/session/sessionMiddleware.js +62 -14
  39. package/dist/lib/http/session/sessionMiddleware.js.map +1 -1
  40. package/dist/lib/http/types.js +10 -1
  41. package/dist/lib/http/types.js.map +1 -1
  42. package/dist/lib/http/uWS/Adapters.js +31 -13
  43. package/dist/lib/http/uWS/Adapters.js.map +1 -1
  44. package/dist/lib/http/uWS/Router.js +80 -16
  45. package/dist/lib/http/uWS/Router.js.map +1 -1
  46. package/dist/lib/http/uWS/WebSocket.js +4 -2
  47. package/dist/lib/http/uWS/WebSocket.js.map +1 -1
  48. package/dist/lib/models/ModelUtils.js +910 -227
  49. package/dist/lib/models/ModelUtils.js.map +1 -1
  50. package/dist/lib/models/RepoUtils.js +863 -266
  51. package/dist/lib/models/RepoUtils.js.map +1 -1
  52. package/dist/lib/routes/BaseAdminRoute.js +5 -4
  53. package/dist/lib/routes/BaseAdminRoute.js.map +1 -1
  54. package/dist/lib/routes/BasePushRoute.js +104 -40
  55. package/dist/lib/routes/BasePushRoute.js.map +1 -1
  56. package/dist/lib/routes/CRUDRoute.js +31 -17
  57. package/dist/lib/routes/CRUDRoute.js.map +1 -1
  58. package/dist/lib/routes/RouteUtils.js +120 -29
  59. package/dist/lib/routes/RouteUtils.js.map +1 -1
  60. package/dist/lib/security/ACLUtils.js +170 -34
  61. package/dist/lib/security/ACLUtils.js.map +1 -1
  62. package/dist/types/ApiErrors.d.ts +10 -0
  63. package/dist/types/BackgroundServiceManager.d.ts +6 -0
  64. package/dist/types/EventListenerManager.d.ts +4 -0
  65. package/dist/types/NetUtils.d.ts +65 -6
  66. package/dist/types/RateLimiter.d.ts +4 -3
  67. package/dist/types/Server.d.ts +33 -2
  68. package/dist/types/auth/AuthMiddleware.d.ts +35 -4
  69. package/dist/types/database/ConnectionManager.d.ts +18 -0
  70. package/dist/types/database/DatabaseErrors.d.ts +26 -0
  71. package/dist/types/database/MongoRepository.d.ts +21 -2
  72. package/dist/types/database/index.d.ts +1 -0
  73. package/dist/types/decorators/PersistenceDecorators.d.ts +31 -0
  74. package/dist/types/decorators/RouteDecorators.d.ts +4 -2
  75. package/dist/types/http/bun/BunRouter.d.ts +23 -2
  76. package/dist/types/http/index.d.ts +2 -1
  77. package/dist/types/http/session/sessionMiddleware.d.ts +14 -4
  78. package/dist/types/http/types.d.ts +40 -0
  79. package/dist/types/http/uWS/Adapters.d.ts +10 -1
  80. package/dist/types/http/uWS/Router.d.ts +15 -2
  81. package/dist/types/models/ModelUtils.d.ts +301 -47
  82. package/dist/types/models/RepoUtils.d.ts +240 -4
  83. package/dist/types/routes/BasePushRoute.d.ts +5 -0
  84. package/dist/types/routes/CRUDRoute.d.ts +10 -0
  85. package/dist/types/routes/RouteUtils.d.ts +37 -1
  86. package/dist/types/security/ACLUtils.d.ts +68 -7
  87. package/package.json +1 -1
@@ -1,6 +1,61 @@
1
1
  import type { Repository } from "typeorm";
2
2
  import { MongoRepository } from "../database/MongoRepository.js";
3
3
  import "reflect-metadata";
4
+ /** Default number of records returned by a search query when no `limit` is specified. Shared by both backends. */
5
+ export declare const DEFAULT_PAGE_SIZE = 100;
6
+ /** Maximum number of records a search query may request via `limit`, regardless of provider. Shared by both backends. */
7
+ export declare const MAX_PAGE_SIZE = 1000;
8
+ /**
9
+ * A single field comparison leaf in a search query AST (see `QueryNode`). `value` may be a raw string (in which
10
+ * case it is coerced the same way an `op(value)` query-parameter operand is - including `me` substitution and
11
+ * declared-type validation) or an already-typed JS value (used as-is, after an operator-injection check).
12
+ */
13
+ export interface PredicateNode {
14
+ kind: "predicate";
15
+ field: string;
16
+ op: "eq" | "ne" | "gt" | "gte" | "lt" | "lte" | "in" | "nin" | "range" | "like" | "regex" | "exists";
17
+ value: unknown;
18
+ /**
19
+ * Set to `true` to use a string `value` (or each string element of an `in`/`nin`/`range` array) exactly as
20
+ * given, skipping `me` substitution, the `null` literal and declared-type coercion. Only the hidden-operator
21
+ * check still applies. See `ModelUtils.literal`.
22
+ */
23
+ literal?: boolean;
24
+ }
25
+ /** The comparison operators a `QueryLiteral` may carry. */
26
+ export type LiteralOperator = "eq" | "ne" | "gt" | "gte" | "lt" | "lte" | "in" | "nin" | "range";
27
+ /**
28
+ * A search query value that is compared exactly as given rather than parsed as `op(value)` syntax. Create one via
29
+ * `ModelUtils.literal()`. Only code can produce one: a client's query string or `q` JSON can only ever yield plain
30
+ * strings, arrays and objects.
31
+ */
32
+ export declare class QueryLiteral {
33
+ readonly op: LiteralOperator;
34
+ readonly value: unknown;
35
+ constructor(value: unknown, op?: LiteralOperator);
36
+ /**
37
+ * Serializes under a `$`-prefixed key so a query containing a literal hashes (e.g. for `RepoUtils`' result
38
+ * cache) differently from any plain value, and so a client echoing the same JSON back is rejected by the
39
+ * query builders' operator-injection guard instead of being mistaken for a literal.
40
+ */
41
+ toJSON(): any;
42
+ }
43
+ /**
44
+ * A boolean grouping node in a search query AST: combines child nodes with `and`/`or`, optionally negated.
45
+ * Negation is supported when compiling to MongoDB (via `$nor`) but not against the SQL `find()`-based `where`
46
+ * (see `buildQueryFromNode`).
47
+ */
48
+ export interface GroupNode {
49
+ kind: "group";
50
+ op: "and" | "or";
51
+ negated?: boolean;
52
+ children: QueryNode[];
53
+ }
54
+ /**
55
+ * A tree-shaped search query, for boolean nesting the flat `op(value)` query-parameter form cannot express (e.g.
56
+ * `(a AND b) OR (c AND d)`). See `ModelUtils.buildQueryFromNode`.
57
+ */
58
+ export type QueryNode = GroupNode | PredicateNode;
4
59
  /**
5
60
  * Utility class for working with data model classes.
6
61
  *
@@ -11,6 +66,35 @@ export declare class ModelUtils {
11
66
  private static typeOrm;
12
67
  private static idPropertyCache;
13
68
  private static readOnlyPropertyCache;
69
+ private static columnTypeCache;
70
+ /** Sequence used to give every `Raw()` SQL expression's named parameter a unique name within one query. */
71
+ private static rawParamSeq;
72
+ /**
73
+ * Marks `value` as a literal search value for `buildSearchQuery()` (and so `RepoUtils.find/count/truncate`),
74
+ * so it is compared exactly as given instead of being parsed as `op(value)` syntax. Use this whenever a query
75
+ * value comes from outside the code (a client, an email header, an iCalendar UID, a display name): a raw string
76
+ * such as `ne(x)` would otherwise be read as an operator, `Support(EU)` would be rejected as an unknown
77
+ * operator, `me`/`null` would be substituted, and a comma inside an `in()` list would split the value.
78
+ *
79
+ * The value is not type-coerced, so pass it with the column's real type (number, boolean, `Date`, string).
80
+ * `null` still compiles to `IS NULL` on SQL. Object values are still checked for hidden `$`/dotted keys.
81
+ *
82
+ * ```
83
+ * repoUtils.find({ messageId: ModelUtils.literal(header) });
84
+ * repoUtils.find({ uid: ModelUtils.literal(["a,b", "c"], "in") });
85
+ * repoUtils.find({ name: ModelUtils.literal(displayName, "ne") });
86
+ * repoUtils.find({ size: ModelUtils.literal([10, 20], "range") });
87
+ * ```
88
+ *
89
+ * @param value The value to compare against. An array for `in`, `nin` and `range` (exactly two elements).
90
+ * @param op The comparison to apply. Defaults to `eq`.
91
+ */
92
+ static literal(value: unknown, op?: LiteralOperator): QueryLiteral;
93
+ /**
94
+ * Splits the operand of a list operator (`in()`, `nin()`, `range()`) on unescaped commas. `\,` yields a literal
95
+ * comma and `\\` a literal backslash; a backslash before any other character (or at the end) is kept as is.
96
+ */
97
+ private static splitListOperand;
14
98
  /**
15
99
  * Provides the `typeorm` module to use when building SQL queries. This is called automatically when a SQL
16
100
  * datasource connection is established.
@@ -36,6 +120,19 @@ export declare class ModelUtils {
36
120
  * @returns The list of all property names that have the @ReadOnly decorator applied.
37
121
  */
38
122
  static getReadOnlyPropertyNames(modelClass: any): string[];
123
+ /**
124
+ * Resolves the declared type of a model property, from an explicit `type` override on `@Column` or (falling
125
+ * back) the TypeScript design-time type reflected at decoration time. Returns `undefined` when `modelClass`
126
+ * is not provided or declares no column metadata for `property` - callers must fall back to a heuristic in
127
+ * that case, the same way `coerceOperand` does.
128
+ */
129
+ private static resolvePropertyType;
130
+ /**
131
+ * Returns the set of property names a `sort` query parameter may reference for `modelClass`, or `undefined`
132
+ * if `modelClass` declares no column metadata at all - in which case sort keys are accepted unvalidated
133
+ * (the same permissive fallback `coerceOperand` uses when no type metadata is available).
134
+ */
135
+ private static getSortablePropertyNames;
39
136
  /**
40
137
  * Builds a query object for use with `find` functions of the given repository for retrieving objects matching the
41
138
  * specified unique identifier.
@@ -74,45 +171,147 @@ export declare class ModelUtils {
74
171
  */
75
172
  static buildIdSearchQueryMongo(modelClass: any, id: any | any[], version?: number, includeDeleted?: boolean): any;
76
173
  /**
77
- * Given a string containing a parameter value and/or a comparison operation return a TypeORM compatible find value.
78
- * e.g.
79
- * Given the string "myvalue" will return an Eq("myvalue") object.
80
- * Given the string "Like(myvalue)" will return an Like("myvalue") object.
174
+ * Resolves a raw, single-value operand (already unwrapped from any `op(...)` syntax) to a properly-typed
175
+ * native value: `me` is substituted for the requesting user's uid, and the result is otherwise coerced
176
+ * according to `property`'s declared type on `modelClass` (falling back to a JSON/Date/string heuristic when
177
+ * no column metadata is available for it). Used for every scalar operand on both backends - including each
178
+ * element of `in()`/`nin()`/`range()` - so type coercion, `me` substitution and operator-injection rejection
179
+ * are applied uniformly everywhere a client-supplied value enters a query, on both backends.
81
180
  *
82
- * @param param
181
+ * @throws {ApiError} If `raw` is `me` with no authenticated user, if a typed column rejects an unparseable
182
+ * operand, or if the coerced value contains a hidden MongoDB operator/dotted key.
83
183
  */
84
- private static getQueryParamValue;
184
+ private static coerceOperand;
185
+ /**
186
+ * Coerces a raw operand string to `type` (an explicit `@Column({type})` override or a reflected TypeScript
187
+ * design type), rejecting operands that don't parse as that type rather than silently guessing.
188
+ */
189
+ private static coerceToType;
190
+ private static invalidOperandError;
191
+ /**
192
+ * Coerces an already-typed AST predicate value (see `QueryNode`): a string operand is routed through
193
+ * `coerceOperand` (type coercion, `me` substitution, injection guard) exactly like the flat `op(value)`
194
+ * form; any other value is assumed to already be correctly typed by the caller and is only checked for a
195
+ * hidden operator/dotted key. With `literal` set, a string operand is kept exactly as given too.
196
+ */
197
+ private static coerceNodeValue;
85
198
  /**
86
199
  * Recursively verifies that no key in the given value (at any depth, including keys of objects nested inside
87
200
  * arrays) is a MongoDB operator (starts with `$`) or uses dot-notation field addressing (contains `.`). Client
88
201
  * input is only ever meant to supply plain field values/comparison operands — never raw Mongo query operators —
89
202
  * so any such key indicates an attempt to inject arbitrary query behavior (e.g. `$where`, `$expr`, or reaching
90
- * into a field the API doesn't expose via dot-notation).
203
+ * into a field the API doesn't expose via dot-notation). Applied to every coerced operand on both backends -
204
+ * `Equal(JSON.parse(param))`-style SQL operators are constructed by TypeORM rather than interpreted from the
205
+ * operand directly, but a client-supplied object operand should still be rejected consistently on both
206
+ * backends rather than left to whatever TypeORM happens to do with it.
91
207
  *
92
208
  * @param value The value to check, typically a parsed query parameter.
93
209
  * @throws {ApiError} If an operator-like or dotted key is found anywhere in `value`.
94
210
  */
95
211
  private static assertNoOperatorInjection;
96
- /** Maximum accepted length of a client-supplied `like()` search pattern. */
97
- private static readonly MAX_LIKE_PATTERN_LENGTH;
212
+ /** Maximum accepted length of a client-supplied `like()`/`regex()` search pattern. */
213
+ private static readonly MAX_PATTERN_LENGTH;
98
214
  /**
99
- * Best-effort check for regex patterns vulnerable to catastrophic backtracking (ReDoS). Rejects patterns that
100
- * are unreasonably long, or that contain a quantified group whose contents are themselves quantified (e.g.
101
- * `(a+)+`, `(a*)*`) the classic shape that causes exponential backtracking in JS's regex engine. This is not
102
- * an exhaustive defense; it catches the common cases a client would realistically send.
215
+ * Best-effort check for regex patterns vulnerable to catastrophic backtracking (ReDoS): patterns that are
216
+ * unreasonably long, that contain a quantified group whose contents are themselves quantified (e.g. `(a+)+`,
217
+ * `(a*)*`), or a quantified group containing alternation (e.g. `(a|a)*`, `(a|ab)*`) - both classic shapes
218
+ * that cause exponential backtracking in JS's (and SQLite's, since the `regex()` SQL operator is backed by a
219
+ * JS `RegExp` - see `registerRegexpFunction` in `TypeOrmSupport.ts`) regex engine. This is not an exhaustive
220
+ * defense; it catches the common cases a client would realistically send. `like()` no longer accepts raw
221
+ * regex (it compiles glob syntax instead - see `globToRegExpSource`), so this now guards only the explicit
222
+ * `regex()` operator.
223
+ *
224
+ * Public so the SQLite `REGEXP` custom function (registered per-connection in `TypeOrmSupport.ts`) can apply
225
+ * the same guard at query-execution time, since a pattern reaching that function didn't necessarily pass
226
+ * through this class's own query builders (e.g. a raw `Raw()`/QueryBuilder use elsewhere).
227
+ */
228
+ static isUnsafeRegexPattern(pattern: string): boolean;
229
+ /**
230
+ * Translates a client-supplied glob pattern (`*` = any sequence, `?` = any single character) to a SQL
231
+ * `LIKE` pattern. Any `%`/`_` already present in the glob source is passed through unescaped (matching this
232
+ * operator's pre-existing behavior before glob support was added) - a client wanting to match a literal `%`
233
+ * or `_` cannot fully escape it, a narrow, documented limitation rather than a regression.
234
+ */
235
+ private static globToLike;
236
+ /**
237
+ * Translates a client-supplied glob pattern into a fully-escaped, anchored regular expression source string
238
+ * for use with MongoDB's `$regex`, so glob syntax behaves identically on both backends.
239
+ */
240
+ private static globToRegExpSource;
241
+ /**
242
+ * Compiles a validated `regex()` pattern to a driver-appropriate case-insensitive match expression. Only
243
+ * PostgreSQL (`~*`), MySQL/MariaDB (`REGEXP`) and the `better-sqlite3` driver (via a `REGEXP` function
244
+ * registered per-connection - see `registerRegexpFunction` in `TypeOrmSupport.ts`) are supported; any other
245
+ * driver rejects the operator outright rather than silently falling back to something incorrect.
246
+ */
247
+ private static compileSqlRegex;
248
+ /**
249
+ * Given a string containing a parameter value and/or a comparison operation return a TypeORM compatible find value.
250
+ * e.g.
251
+ * Given the string "myvalue" will return an Eq("myvalue") object.
252
+ * Given the string "Like(myvalue)" will return an Like("myvalue") object.
103
253
  *
104
- * @param pattern The user-supplied `like()` pattern.
254
+ * @param param
105
255
  */
106
- private static isUnsafeRegexPattern;
256
+ private static getQueryParamValue;
107
257
  /**
108
258
  * Given a string containing a parameter value and/or a comparison operation return a MongoDB compatible find value.
109
259
  * e.g.
110
260
  * Given the string "myvalue" will return an `"myvalue"` object.
111
- * Given the string "not(myvalue)" will return an `{ $not: "myvalue" }` object.
261
+ * Given the string "not(myvalue)" will return an `{ $ne: "myvalue" }` object.
112
262
  *
113
263
  * @param param
114
264
  */
115
265
  private static getQueryParamValueMongo;
266
+ /**
267
+ * Extracts the `$match` stage from either shape `buildSearchQueryMongo` can return (a pipeline array or a
268
+ * flattened `{$match, $sort}` object).
269
+ */
270
+ private static extractMatch;
271
+ /**
272
+ * Normalizes the return value of `buildSearchQueryMongo` to a single shape: a full aggregation pipeline.
273
+ * `buildSearchQueryMongo` itself still returns either a pipeline array or a flattened `{$match, $sort}`
274
+ * object depending on how many stages it produced (existing callers, e.g. `RepoUtils`, already branch on
275
+ * `Array.isArray()` to handle both) - use this instead at any new call site that wants one consistent shape.
276
+ */
277
+ static toFindQuery(pipelineOrObject: any): any[];
278
+ /**
279
+ * Resolves the `limit`/`page` reserved query parameters to a bounded `take`/`skip` pair, applying the same
280
+ * default (`DEFAULT_PAGE_SIZE`) and ceiling (`MAX_PAGE_SIZE`) that `buildSearchQuerySQL` already bakes into
281
+ * its own return value (as `take`/`page`). `buildSearchQueryMongo` does NOT bake pagination into its own
282
+ * pipeline - doing so would execute as `$skip`/`$limit` aggregation stages, which would double up with (and
283
+ * corrupt) any cursor-level `.skip()/.limit()` a caller applies on top, as `RepoUtils` already does for its
284
+ * own route-level pagination. A caller building a Mongo query directly - rather than going through
285
+ * `RepoUtils` - should call this explicitly to get the same bounded pagination the SQL path enforces
286
+ * automatically, rather than an unbounded result set.
287
+ */
288
+ static resolvePagination(query?: any): {
289
+ take: number;
290
+ page: number;
291
+ skip: number;
292
+ };
293
+ /** Returns `true` for the boolean grouping keys (`$or`, `$and`) accepted in a flat search query. */
294
+ private static isGroupKey;
295
+ /**
296
+ * Validates the value of a `$or`/`$and` key: a non-empty array (at most `MAX_QUERY_NODES` long) of plain query
297
+ * objects. Anything else is a 400 on both backends. In particular an empty array is rejected rather than
298
+ * compiled: on SQL it used to expand to zero branches, which dropped the whole `where` (every other condition
299
+ * included) and matched every row, while MongoDB rejects `$or: []` outright.
300
+ */
301
+ private static assertQueryGroup;
302
+ /**
303
+ * ANDs two lists of OR-ed SQL `where` branches: `(L1 OR L2) AND (R1 OR R2)` becomes the cross product
304
+ * `(L1 AND R1) OR (L1 AND R2) OR ...`. Throws before allocating if the product exceeds `MAX_QUERY_NODES`.
305
+ */
306
+ private static andBranches;
307
+ /**
308
+ * ANDs two SQL `where` branch objects. A key present on only one side is copied; a key present on both keeps
309
+ * both conditions via TypeORM's `And()` (a plain object spread would let the right side silently replace the
310
+ * left, e.g. a forced scope key being overridden by a `$or` branch). Nested plain objects (embedded entities or
311
+ * relations) are merged recursively.
312
+ */
313
+ private static mergeWhereBranches;
314
+ private static andWhereValues;
116
315
  /**
117
316
  * Builds a query object for the given criteria and repository. Query params can have a value containing a
118
317
  * conditional operator to apply for the search. The operator is encoded with the format `op(value)`. The following
@@ -121,13 +320,43 @@ export declare class ModelUtils {
121
320
  * * `gt` - Returns matches whose parameter is greater than the given value. e.g. `param > value`
122
321
  * * `gte` - Returns matches whose parameter is greater than or equal to the given value. e.g. `param >= value`
123
322
  * * `in` - Returns matches whose parameter includes one of the given values. e.g. `param in ('value1', 'value2', 'value3', ...)`
124
- * * `like` - Returns matches whose parameter is lexographically similar to the given value. `param like value`
323
+ * * `like` - Returns matches whose parameter matches the given glob pattern (`*` = any sequence, `?` = any single character), case-insensitively. e.g. `like(*.txt)`
324
+ * * `regex` - Returns matches whose parameter matches the given regular expression, case-insensitively.
125
325
  * * `lt` - Returns matches whose parameter is less than the given value. e.g. `param < value`
126
326
  * * `lte` - Returns matches whose parameter is less than or equal to than the given value. e.g. `param < value`
127
- * * `not` - Returns matches whose parameter is not equal to the given value. e.g. `param not value`
327
+ * * `not` / `ne` - Returns matches whose parameter is not equal to the given value. e.g. `param != value`
128
328
  * * `range` - Returns matches whose parameter is greater than or equal to first given value and less than or equal to the second. e.g. `param between(1,100)`
329
+ * * `exists` - Returns matches whose parameter is (`exists(true)`) or is not (`exists(false)`) set.
330
+ *
331
+ * When no operator is provided the comparison is evaluated as `eq`, unless `exactMatch` is `false`, in which
332
+ * case a string-valued parameter is instead matched as a case-insensitive "contains" search.
333
+ *
334
+ * Operand and escaping rules:
335
+ * * A value is only parsed as an operator when the WHOLE value has the shape `name(...)`. The operand is
336
+ * everything between the first `(` and the last `)`, verbatim: parentheses, commas, leading/trailing spaces,
337
+ * newlines and nested `op(...)` text included. So `eq(Support(EU))` matches `Support(EU)`, `eq( a,b )`
338
+ * matches ` a,b ` and `eq(ne(x))` matches `ne(x)`. The operator name is case-insensitive.
339
+ * * A bare value shaped like `name(...)` whose `name` is not a known operator is rejected with a 400; wrap it
340
+ * in `eq(...)` to match it literally.
341
+ * * `eq()`/`ne()` operands are then coerced like any operand: `me` resolves to the requesting user's uid, `null`
342
+ * matches a null value, and the value is converted to the column's declared type (number, boolean, date).
343
+ * With no column metadata a JSON/date heuristic is used instead.
344
+ * * `in()`, `nin()` and `range()` split their operand on commas. Write `\,` for a comma inside one value and
345
+ * `\\` for a backslash, e.g. `in(a\,b,c)` matches `a,b` or `c`. A backslash before any other character is
346
+ * kept as is.
347
+ * * `$or` and `$and` (only from programmatic queries or the `q` JSON parameter; a query string can't build them)
348
+ * take a non-empty array of sub-query objects that are ANDed with every other key, the same on both backends.
349
+ * A `$`-prefixed field name is rejected with a 400.
129
350
  *
130
- * When no operator is provided the comparison will always be evaluated as `eq`.
351
+ * Code that passes a value it doesn't control (an email header, an iCalendar UID, a display name, ...) should
352
+ * use `ModelUtils.literal(value)` instead of building an `eq(...)` string. A literal skips all of the parsing
353
+ * and coercion above. Other non-string values (numbers, booleans, `Date`, `null`) are also compared as given.
354
+ *
355
+ * A repeated query parameter name (e.g. `?a=1&a=2`) OR-combines its values, "zipped" positionally against
356
+ * every other repeated parameter rather than as a cartesian product: `?a=1&a=2&b=3&b=4` compiles to
357
+ * `(a=1 AND b=3) OR (a=2 AND b=4)`, not `a IN (1,2)` and not all four combinations. A shorter array is padded
358
+ * by repeating its own last value against the longer one(s), rather than leaving the key unset for the extra
359
+ * branches (which would match ANY value there, silently dropping that filter).
131
360
  *
132
361
  * NOTE: The result of this function is only compatible with the `aggregate()` function when MongoDB is used.
133
362
  *
@@ -140,43 +369,35 @@ export declare class ModelUtils {
140
369
  */
141
370
  static buildSearchQuery<T extends {}>(modelClass: any, repo: Repository<T> | MongoRepository<T> | undefined, query?: any, exactMatch?: boolean, user?: any): any;
142
371
  /**
143
- * Builds a TypeORM compatible query object for the given criteria. Query params can have a value containing a
144
- * conditional operator to apply for the search. The operator is encoded with the format `op(value)`. The following
145
- * operators are supported:
146
- * * `eq` - Returns matches whose parameter exactly matches of the given value. e.g. `param = value`
147
- * * `gt` - Returns matches whose parameter is greater than the given value. e.g. `param > value`
148
- * * `gte` - Returns matches whose parameter is greater than or equal to the given value. e.g. `param >= value`
149
- * * `in` - Returns matches whose parameter includes one of the given values. e.g. `param in ('value1', 'value2', 'value3', ...)`
150
- * * `like` - Returns matches whose parameter is lexographically similar to the given value. `param like value`
151
- * * `lt` - Returns matches whose parameter is less than the given value. e.g. `param < value`
152
- * * `lte` - Returns matches whose parameter is less than or equal to than the given value. e.g. `param < value`
153
- * * `not` - Returns matches whose parameter is not equal to the given value. e.g. `param not value`
154
- * * `range` - Returns matches whose parameter is greater than or equal to first given value and less than or equal to the second. e.g. `param between(1,100)`
372
+ * Builds a TypeORM compatible query object for the given criteria. See `buildSearchQuery` for the supported
373
+ * `op(value)` operators and multi-value "zip" semantics.
155
374
  *
156
- * When no operator is provided the comparison will always be evaluated as `eq`.
375
+ * Unlike `buildSearchQuery` (which always injects a `deleted: false` filter for a `RecoverableBaseEntity`
376
+ * before delegating here), this function applies no soft-delete filtering of its own - a caller invoking it
377
+ * directly, bypassing `buildSearchQuery`, will not get that default exclusion.
157
378
  *
158
379
  * @param modelClass The class definition of the data model to build a search query for.
159
380
  * @param {any} query The search query parameters to include.
160
381
  * @param {bool} exactMatch Set to true to create a query where parameters are to be matched exactly, otherwise set to false to use a 'contains' search.
161
382
  * @param {any} user The user that is performing the request.
383
+ * @param {string} driverType The TypeORM driver type (`connection.options.type`) of the target datasource, used
384
+ * to select a compatible SQL translation for the `regex()` operator. Only needed when `regex()` may appear in
385
+ * `query`.
386
+ * @param {number} depth Internal recursion-depth counter for nested `$or` groups - do not pass explicitly.
162
387
  * @returns {object} The TypeORM compatible query object.
163
388
  */
164
- static buildSearchQuerySQL(modelClass: any, query?: any, exactMatch?: boolean, user?: any): any;
389
+ static buildSearchQuerySQL(modelClass: any, query?: any, exactMatch?: boolean, user?: any, driverType?: string, depth?: number): any;
165
390
  /**
166
- * Builds a MongoDB compatible query object for the given criteria. Query params can have a value containing a
167
- * conditional operator to apply for the search. The operator is encoded with the format `op(value)`. The following
168
- * operators are supported:
169
- * * `eq` - Returns matches whose parameter exactly matches of the given value. e.g. `param = value`
170
- * * `gt` - Returns matches whose parameter is greater than the given value. e.g. `param > value`
171
- * * `gte` - Returns matches whose parameter is greater than or equal to the given value. e.g. `param >= value`
172
- * * `in` - Returns matches whose parameter includes one of the given values. e.g. `param in ('value1', 'value2', 'value3', ...)`
173
- * * `like` - Returns matches whose parameter is lexographically similar to the given value. `param like value`
174
- * * `lt` - Returns matches whose parameter is less than the given value. e.g. `param < value`
175
- * * `lte` - Returns matches whose parameter is less than or equal to than the given value. e.g. `param < value`
176
- * * `not` - Returns matches whose parameter is not equal to the given value. e.g. `param not value`
177
- * * `range` - Returns matches whose parameter is greater than or equal to first given value and less than or equal to the second. e.g. `param between(1,100)`
391
+ * Builds a MongoDB compatible query object for the given criteria. See `buildSearchQuery` for the supported
392
+ * `op(value)` operators and multi-value "zip" semantics.
393
+ *
394
+ * Unlike `buildSearchQuery` (which always injects a `deleted: false` filter for a `RecoverableBaseEntity`
395
+ * before delegating here), this function applies no soft-delete filtering of its own - a caller invoking it
396
+ * directly, bypassing `buildSearchQuery`, will not get that default exclusion.
178
397
  *
179
- * When no operator is provided the comparison will always be evaluated as `eq`.
398
+ * Does NOT bound `limit`/`page` into the returned pipeline (see `resolvePagination`) and returns either an
399
+ * aggregation pipeline array or a flattened `{$match, $sort}` object depending on how many stages it
400
+ * produced (see `toFindQuery` to normalize to one shape).
180
401
  *
181
402
  * NOTE: The result of this function is only compatible with the `aggregate()` function.
182
403
  *
@@ -184,9 +405,42 @@ export declare class ModelUtils {
184
405
  * @param {any} query The search query parameters to include.
185
406
  * @param {bool} exactMatch Set to true to create a query where parameters are to be matched exactly, otherwise set to false to use a 'contains' search.
186
407
  * @param {any} user The user that is performing the request.
408
+ * @param {number} depth Internal recursion-depth counter for nested `$or` groups - do not pass explicitly.
187
409
  * @returns {object} The MongoDB compatible query object.
188
410
  */
189
- static buildSearchQueryMongo(modelClass: any, query?: any, exactMatch?: boolean, user?: any): any;
411
+ static buildSearchQueryMongo(modelClass: any, query?: any, exactMatch?: boolean, user?: any, depth?: number): any;
412
+ /**
413
+ * Compiles a single `PredicateNode` leaf to a MongoDB filter fragment (`{field: ...}`).
414
+ */
415
+ private static compilePredicateMongo;
416
+ private static compileGroupMongo;
417
+ private static compileNodeMongo;
418
+ private static compilePredicateSQLOperator;
419
+ private static compileNodeSQL;
420
+ /**
421
+ * Compiles a `QueryNode` boolean tree into a query object for the given repository - the nested-condition
422
+ * counterpart to `buildSearchQuery()`'s flat `op(value)` query-parameter form, for boolean shapes the flat
423
+ * form can't express (e.g. `(a AND b) OR (c AND d)`, with no key forced into every branch). Reuses the same
424
+ * operand coercion, `me` substitution and operator-injection guard as the flat form. Bounded by the same
425
+ * `MAX_QUERY_DEPTH`/`MAX_QUERY_NODES` limits as `$or`. Negated groups are supported on MongoDB (via `$nor`)
426
+ * but rejected against the SQL `find()`-based `where` (see `compileNodeSQL`).
427
+ *
428
+ * @param modelClass The class definition of the data model to build a search query for.
429
+ * @param repo The repository to build a search query for.
430
+ * @param node The root of the query tree.
431
+ * @param user The user that is performing the request, resolved for any `field: "me"` predicate value.
432
+ */
433
+ static buildQueryFromNode<T extends {}>(modelClass: any, repo: Repository<T> | MongoRepository<T> | undefined, node: QueryNode, user?: any): any;
434
+ /**
435
+ * Converts a `QueryNode` boolean tree into a PostgreSQL `tsquery` expression string (`AND` -> `&`, `OR` -> `|`,
436
+ * negation -> `!`), so client input can drive full-text search without passing untrusted text straight to
437
+ * `to_tsquery` (which throws on malformed input) while still supporting the boolean grouping
438
+ * `websearch_to_tsquery` cannot express. Every predicate leaf's `value` is treated as a search term
439
+ * (lexeme/phrase) regardless of its `field`/`op` - this framework has no notion of a full-text-indexed column,
440
+ * so the caller is expected to route the resulting expression to whichever `tsvector` column it's searching,
441
+ * e.g. `to_tsquery(ModelUtils.toTsQuery(node))`.
442
+ */
443
+ static toTsQuery(node: QueryNode): string;
190
444
  /**
191
445
  * Loads all model schema files from the specified path and returns a map containing all the definitions.
192
446
  *