qast 1.1.0 → 2.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 +312 -6
- package/README.zh-CN.md +520 -0
- package/dist/adapters/drizzle.d.ts +66 -0
- package/dist/adapters/drizzle.d.ts.map +1 -0
- package/dist/adapters/drizzle.js +222 -0
- package/dist/adapters/drizzle.js.map +1 -0
- package/dist/adapters/knex.d.ts +22 -0
- package/dist/adapters/knex.d.ts.map +1 -0
- package/dist/adapters/knex.js +158 -0
- package/dist/adapters/knex.js.map +1 -0
- package/dist/adapters/mongoose.d.ts +15 -0
- package/dist/adapters/mongoose.d.ts.map +1 -0
- package/dist/adapters/mongoose.js +152 -0
- package/dist/adapters/mongoose.js.map +1 -0
- package/dist/adapters/prisma.d.ts +5 -2
- package/dist/adapters/prisma.d.ts.map +1 -1
- package/dist/adapters/prisma.js +103 -4
- package/dist/adapters/prisma.js.map +1 -1
- package/dist/adapters/sequelize.d.ts +26 -8
- package/dist/adapters/sequelize.d.ts.map +1 -1
- package/dist/adapters/sequelize.js +168 -7
- package/dist/adapters/sequelize.js.map +1 -1
- package/dist/adapters/typeorm.d.ts +32 -2
- package/dist/adapters/typeorm.d.ts.map +1 -1
- package/dist/adapters/typeorm.js +169 -3
- package/dist/adapters/typeorm.js.map +1 -1
- package/dist/builder/query-builder.d.ts +139 -0
- package/dist/builder/query-builder.d.ts.map +1 -0
- package/dist/builder/query-builder.js +320 -0
- package/dist/builder/query-builder.js.map +1 -0
- package/dist/errors.d.ts +18 -3
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +127 -9
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +90 -10
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +142 -16
- package/dist/index.js.map +1 -1
- package/dist/integrations/express.d.ts +14 -0
- package/dist/integrations/express.d.ts.map +1 -0
- package/dist/integrations/express.js +33 -0
- package/dist/integrations/express.js.map +1 -0
- package/dist/integrations/fastify.d.ts +17 -0
- package/dist/integrations/fastify.d.ts.map +1 -0
- package/dist/integrations/fastify.js +35 -0
- package/dist/integrations/fastify.js.map +1 -0
- package/dist/integrations/hono.d.ts +14 -0
- package/dist/integrations/hono.d.ts.map +1 -0
- package/dist/integrations/hono.js +30 -0
- package/dist/integrations/hono.js.map +1 -0
- package/dist/integrations/nestjs.d.ts +23 -0
- package/dist/integrations/nestjs.d.ts.map +1 -0
- package/dist/integrations/nestjs.js +137 -0
- package/dist/integrations/nestjs.js.map +1 -0
- package/dist/parser/parser.d.ts +15 -7
- package/dist/parser/parser.d.ts.map +1 -1
- package/dist/parser/parser.js +117 -15
- package/dist/parser/parser.js.map +1 -1
- package/dist/parser/tokenizer.d.ts +11 -1
- package/dist/parser/tokenizer.d.ts.map +1 -1
- package/dist/parser/tokenizer.js +67 -10
- package/dist/parser/tokenizer.js.map +1 -1
- package/dist/parser/validator.d.ts +44 -1
- package/dist/parser/validator.d.ts.map +1 -1
- package/dist/parser/validator.js +210 -2
- package/dist/parser/validator.js.map +1 -1
- package/dist/types/ast.d.ts +102 -3
- package/dist/types/ast.d.ts.map +1 -1
- package/dist/types/ast.js +21 -0
- package/dist/types/ast.js.map +1 -1
- package/dist/utils/ast-utils.d.ts +43 -0
- package/dist/utils/ast-utils.d.ts.map +1 -0
- package/dist/utils/ast-utils.js +205 -0
- package/dist/utils/ast-utils.js.map +1 -0
- package/dist/utils/cache.d.ts +47 -0
- package/dist/utils/cache.d.ts.map +1 -0
- package/dist/utils/cache.js +132 -0
- package/dist/utils/cache.js.map +1 -0
- package/dist/utils/serializer.d.ts +6 -0
- package/dist/utils/serializer.d.ts.map +1 -0
- package/dist/utils/serializer.js +140 -0
- package/dist/utils/serializer.js.map +1 -0
- package/package.json +31 -8
package/README.md
CHANGED
|
@@ -70,19 +70,35 @@ QAST supports the following comparison operators:
|
|
|
70
70
|
- `gte` - Greater than or equal
|
|
71
71
|
- `lte` - Less than or equal
|
|
72
72
|
- `in` - In array
|
|
73
|
+
- `notIn` - Not in array
|
|
73
74
|
- `contains` - Contains substring (string matching)
|
|
75
|
+
- `startsWith` - String starts with pattern
|
|
76
|
+
- `endsWith` - String ends with pattern
|
|
77
|
+
- `like` - Pattern matching with wildcards (`%` and `_`)
|
|
78
|
+
- `regex` / `matches` - Regex pattern matching
|
|
79
|
+
- `between` - Range check: `age between 18 and 65`
|
|
80
|
+
- `isNull` - Null check: `email isNull`
|
|
81
|
+
- `isNotNull` - Not null check: `email isNotNull`
|
|
74
82
|
|
|
75
83
|
### Logical Operators
|
|
76
84
|
|
|
77
85
|
- `and` - Logical AND
|
|
78
86
|
- `or` - Logical OR
|
|
87
|
+
- `not` - Logical NOT: `not (age gt 25)`
|
|
88
|
+
|
|
89
|
+
### Query Clauses
|
|
90
|
+
|
|
91
|
+
- **Sorting**: `orderBy age desc, name asc` - Sort by multiple fields
|
|
92
|
+
- **Pagination**: `limit 10 offset 20` - Limit and offset results
|
|
93
|
+
- **Nested fields**: `user.profile.name eq "John"` - Access nested fields with dot notation
|
|
79
94
|
|
|
80
95
|
### Values
|
|
81
96
|
|
|
82
97
|
- **Strings**: Use single or double quotes: `"John"` or `'John'`
|
|
83
98
|
- **Numbers**: Integers or floats: `25`, `25.99`, `-10`
|
|
84
99
|
- **Booleans**: `true` or `false`
|
|
85
|
-
- **
|
|
100
|
+
- **Null**: `null` - For null checks
|
|
101
|
+
- **Arrays**: For `in` and `notIn` operators: `[1,2,3]` or `["John","Jane"]`
|
|
86
102
|
|
|
87
103
|
### Examples
|
|
88
104
|
|
|
@@ -110,6 +126,19 @@ QAST supports the following comparison operators:
|
|
|
110
126
|
|
|
111
127
|
// Complex query
|
|
112
128
|
'age gt 25 and (name eq "John" or city eq "Paris") and active eq true'
|
|
129
|
+
|
|
130
|
+
// New operators
|
|
131
|
+
'age between 18 and 65'
|
|
132
|
+
'age notIn [1,2,3]'
|
|
133
|
+
'name like "John%"'
|
|
134
|
+
'email matches "^[a-z]+@example\.com$"'
|
|
135
|
+
'email isNull'
|
|
136
|
+
'email isNotNull'
|
|
137
|
+
'not (age gt 25)'
|
|
138
|
+
|
|
139
|
+
// Sorting and pagination
|
|
140
|
+
'age gt 25 orderBy age desc limit 10 offset 20'
|
|
141
|
+
'name eq "John" orderBy name asc, age desc limit 5'
|
|
113
142
|
```
|
|
114
143
|
|
|
115
144
|
## ORM Adapters
|
|
@@ -220,6 +249,192 @@ await User.findAll({ where: transformed });
|
|
|
220
249
|
|
|
221
250
|
**Note**: Sequelize uses the `Op` object from 'sequelize'. Since Sequelize is an optional peer dependency, the adapter returns a structure with metadata (`__qast_operator__` and `__qast_logical__`) that you need to transform to use `Op` operators. For simple equality (`eq`), the adapter returns plain values which Sequelize accepts directly.
|
|
222
251
|
|
|
252
|
+
### Mongoose
|
|
253
|
+
|
|
254
|
+
```typescript
|
|
255
|
+
import { parseQueryFull, toMongooseFilter } from 'qast';
|
|
256
|
+
|
|
257
|
+
const query = 'age gt 25 orderBy age desc limit 10';
|
|
258
|
+
const queryAST = parseQueryFull(query);
|
|
259
|
+
const filter = toMongooseFilter(queryAST);
|
|
260
|
+
|
|
261
|
+
// filter = {
|
|
262
|
+
// filter: { age: { $gt: 25 } },
|
|
263
|
+
// sort: { age: -1 },
|
|
264
|
+
// limit: 10
|
|
265
|
+
// }
|
|
266
|
+
|
|
267
|
+
await User.find(filter.filter)
|
|
268
|
+
.sort(filter.sort)
|
|
269
|
+
.limit(filter.limit);
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
### Knex.js
|
|
273
|
+
|
|
274
|
+
```typescript
|
|
275
|
+
import { parseQueryFull, toKnexFilter } from 'qast';
|
|
276
|
+
|
|
277
|
+
const query = 'age gt 25 orderBy age desc limit 10';
|
|
278
|
+
const queryAST = parseQueryFull(query);
|
|
279
|
+
const filter = toKnexFilter(queryAST);
|
|
280
|
+
|
|
281
|
+
// Use with Knex query builder
|
|
282
|
+
knex('users')
|
|
283
|
+
.where(filter.whereCallback)
|
|
284
|
+
.orderBy(filter.orderBy!)
|
|
285
|
+
.limit(filter.limit!)
|
|
286
|
+
.offset(filter.offset);
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
### Drizzle ORM
|
|
290
|
+
|
|
291
|
+
```typescript
|
|
292
|
+
import { parseQueryFull, toDrizzleFilter, transformDrizzleConditions } from 'qast';
|
|
293
|
+
import { and, gt, eq, desc } from 'drizzle-orm';
|
|
294
|
+
|
|
295
|
+
const query = 'age gt 25 and name eq "John"';
|
|
296
|
+
const queryAST = parseQueryFull(query);
|
|
297
|
+
const filter = toDrizzleFilter(queryAST);
|
|
298
|
+
|
|
299
|
+
// Transform to Drizzle conditions
|
|
300
|
+
const columnMap = { age: users.age, name: users.name };
|
|
301
|
+
const where = transformDrizzleConditions(filter.where, { and, gt, eq }, columnMap);
|
|
302
|
+
|
|
303
|
+
await db.select().from(users).where(where);
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
## Query Builder API
|
|
307
|
+
|
|
308
|
+
Build queries programmatically using a fluent API:
|
|
309
|
+
|
|
310
|
+
```typescript
|
|
311
|
+
import { queryBuilder } from 'qast';
|
|
312
|
+
|
|
313
|
+
const query = queryBuilder()
|
|
314
|
+
.field('age').gt(25)
|
|
315
|
+
.and(queryBuilder().field('name').eq('John'))
|
|
316
|
+
.orderBy('age', 'desc')
|
|
317
|
+
.limit(10)
|
|
318
|
+
.offset(20)
|
|
319
|
+
.build();
|
|
320
|
+
|
|
321
|
+
// query.filter - the filter AST
|
|
322
|
+
// query.orderBy - sorting specifications
|
|
323
|
+
// query.limit - limit value
|
|
324
|
+
// query.offset - offset value
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
## AST Serialization
|
|
328
|
+
|
|
329
|
+
Convert AST back to query string:
|
|
330
|
+
|
|
331
|
+
```typescript
|
|
332
|
+
import { parseQuery, serializeQuery } from 'qast';
|
|
333
|
+
|
|
334
|
+
const query = 'age gt 25 and name eq "John"';
|
|
335
|
+
const ast = parseQuery(query);
|
|
336
|
+
const serialized = serializeQuery(ast);
|
|
337
|
+
// serialized = 'age gt 25 and name eq "John"'
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
## AST Utilities
|
|
341
|
+
|
|
342
|
+
### Clone AST
|
|
343
|
+
|
|
344
|
+
```typescript
|
|
345
|
+
import { parseQuery, cloneAST } from 'qast';
|
|
346
|
+
|
|
347
|
+
const ast = parseQuery('age gt 25');
|
|
348
|
+
const cloned = cloneAST(ast);
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
### Merge Queries
|
|
352
|
+
|
|
353
|
+
```typescript
|
|
354
|
+
import { parseQuery, mergeQueries } from 'qast';
|
|
355
|
+
|
|
356
|
+
const query1 = parseQuery('age gt 25');
|
|
357
|
+
const query2 = parseQuery('name eq "John"');
|
|
358
|
+
const merged = mergeQueries(query1, query2, 'AND');
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
### Get Query Statistics
|
|
362
|
+
|
|
363
|
+
```typescript
|
|
364
|
+
import { parseQuery, getQueryStats } from 'qast';
|
|
365
|
+
|
|
366
|
+
const ast = parseQuery('age gt 25 and name eq "John"');
|
|
367
|
+
const stats = getQueryStats(ast);
|
|
368
|
+
// stats.depth, stats.nodeCount, stats.fields, etc.
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
### Optimize AST
|
|
372
|
+
|
|
373
|
+
```typescript
|
|
374
|
+
import { parseQuery, optimizeAST } from 'qast';
|
|
375
|
+
|
|
376
|
+
const ast = parseQuery('age gt 25 and age gt 25'); // Redundant
|
|
377
|
+
const optimized = optimizeAST(ast);
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
### Compare Queries
|
|
381
|
+
|
|
382
|
+
```typescript
|
|
383
|
+
import { parseQuery, diffQueries } from 'qast';
|
|
384
|
+
|
|
385
|
+
const query1 = parseQuery('age gt 25');
|
|
386
|
+
const query2 = parseQuery('age gt 30');
|
|
387
|
+
const diff = diffQueries(query1, query2);
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
## Performance Features
|
|
391
|
+
|
|
392
|
+
### Query Caching
|
|
393
|
+
|
|
394
|
+
```typescript
|
|
395
|
+
import { QueryCache, createCachedParser, parseQuery } from 'qast';
|
|
396
|
+
|
|
397
|
+
// Create a cached parser
|
|
398
|
+
const cachedParse = createCachedParser(parseQuery, 100, 60000); // 100 items, 60s TTL
|
|
399
|
+
|
|
400
|
+
// Use cached parser
|
|
401
|
+
const ast = cachedParse('age gt 25'); // Cached on subsequent calls
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
## Security Features
|
|
405
|
+
|
|
406
|
+
### Field Sanitization
|
|
407
|
+
|
|
408
|
+
```typescript
|
|
409
|
+
import { sanitizeFieldName } from 'qast';
|
|
410
|
+
|
|
411
|
+
const safeField = sanitizeFieldName('user.name'); // Removes dangerous characters
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
### Query Cost Estimation
|
|
415
|
+
|
|
416
|
+
```typescript
|
|
417
|
+
import { parseQuery, estimateQueryCost } from 'qast';
|
|
418
|
+
|
|
419
|
+
const ast = parseQuery('age gt 25 and name eq "John"');
|
|
420
|
+
const cost = estimateQueryCost(ast);
|
|
421
|
+
// cost.estimatedComplexity, cost.depth, cost.nodeCount, etc.
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
### Rate Limiting
|
|
425
|
+
|
|
426
|
+
```typescript
|
|
427
|
+
import { RateLimiter } from 'qast';
|
|
428
|
+
|
|
429
|
+
const limiter = new RateLimiter(100, 60000); // 100 requests per 60 seconds
|
|
430
|
+
|
|
431
|
+
if (limiter.isAllowed(userId)) {
|
|
432
|
+
// Process query
|
|
433
|
+
} else {
|
|
434
|
+
// Rate limit exceeded
|
|
435
|
+
}
|
|
436
|
+
```
|
|
437
|
+
|
|
223
438
|
## API Reference
|
|
224
439
|
|
|
225
440
|
### `parseQuery(query: string, options?: ParseOptions): QastNode`
|
|
@@ -232,6 +447,11 @@ Parse a query string into an AST.
|
|
|
232
447
|
- `allowedFields?: string[]` - Whitelist of allowed field names
|
|
233
448
|
- `allowedOperators?: Operator[]` - Whitelist of allowed operators
|
|
234
449
|
- `validate?: boolean` - Whether to validate against whitelists (default: true if whitelists are provided)
|
|
450
|
+
- `maxDepth?: number` - Maximum allowed AST depth (to limit nested logical expressions)
|
|
451
|
+
- `maxNodes?: number` - Maximum allowed number of AST nodes (to limit overall query complexity)
|
|
452
|
+
- `maxQueryLength?: number` - Maximum allowed length of the raw query string (checked before parsing)
|
|
453
|
+
- `maxArrayLength?: number` - Maximum allowed length of array values (for `in` operator)
|
|
454
|
+
- `maxStringLength?: number` - Maximum allowed length of string values
|
|
235
455
|
|
|
236
456
|
**Returns:** The parsed AST node
|
|
237
457
|
|
|
@@ -241,6 +461,11 @@ const ast = parseQuery('age gt 25', {
|
|
|
241
461
|
allowedFields: ['age', 'name'],
|
|
242
462
|
allowedOperators: ['gt', 'eq'],
|
|
243
463
|
validate: true,
|
|
464
|
+
maxDepth: 5,
|
|
465
|
+
maxNodes: 50,
|
|
466
|
+
maxQueryLength: 1000,
|
|
467
|
+
maxArrayLength: 100,
|
|
468
|
+
maxStringLength: 200,
|
|
244
469
|
});
|
|
245
470
|
```
|
|
246
471
|
|
|
@@ -258,13 +483,53 @@ Transform an AST to a TypeORM filter.
|
|
|
258
483
|
|
|
259
484
|
**Note:** TypeORM requires operator functions for non-equality comparisons. You may need to transform the result.
|
|
260
485
|
|
|
261
|
-
### `
|
|
486
|
+
### `parseQueryFull(query: string, options?: ParseOptions): QueryAST`
|
|
487
|
+
|
|
488
|
+
Parse a query string into a full QueryAST (includes filter, sorting, pagination).
|
|
489
|
+
|
|
490
|
+
**Returns:** QueryAST with filter, orderBy, limit, and offset
|
|
491
|
+
|
|
492
|
+
### `toPrismaFilter(ast: QastNode | QueryAST): PrismaFilter`
|
|
493
|
+
|
|
494
|
+
Transform an AST or QueryAST to a Prisma filter.
|
|
495
|
+
|
|
496
|
+
**Returns:** Prisma filter object with `where`, `orderBy`, `take`, and `skip` properties
|
|
497
|
+
|
|
498
|
+
### `toTypeORMFilter(ast: QastNode | QueryAST): TypeORMFilter`
|
|
499
|
+
|
|
500
|
+
Transform an AST or QueryAST to a TypeORM filter.
|
|
262
501
|
|
|
263
|
-
|
|
502
|
+
**Returns:** TypeORM filter object with `where`, `order`, `take`, and `skip` properties
|
|
264
503
|
|
|
265
|
-
**
|
|
504
|
+
**Note:** TypeORM requires operator functions for non-equality comparisons. Use `transformTypeORMOperators()` helper function.
|
|
266
505
|
|
|
267
|
-
|
|
506
|
+
### `toSequelizeFilter(ast: QastNode | QueryAST): SequelizeFilter`
|
|
507
|
+
|
|
508
|
+
Transform an AST or QueryAST to a Sequelize filter.
|
|
509
|
+
|
|
510
|
+
**Returns:** Sequelize filter object with `where`, `order`, `limit`, and `offset` properties
|
|
511
|
+
|
|
512
|
+
**Note:** Sequelize uses the `Op` object. Use `transformSequelizeOperators()` helper function.
|
|
513
|
+
|
|
514
|
+
### `toMongooseFilter(ast: QastNode | QueryAST): MongooseFilter`
|
|
515
|
+
|
|
516
|
+
Transform an AST or QueryAST to a Mongoose filter.
|
|
517
|
+
|
|
518
|
+
**Returns:** Mongoose filter object with `filter`, `sort`, `limit`, and `skip` properties
|
|
519
|
+
|
|
520
|
+
### `toKnexFilter(ast: QastNode | QueryAST): KnexFilter`
|
|
521
|
+
|
|
522
|
+
Transform an AST or QueryAST to a Knex filter.
|
|
523
|
+
|
|
524
|
+
**Returns:** Knex filter object with `whereCallback`, `orderBy`, `limit`, and `offset` properties
|
|
525
|
+
|
|
526
|
+
### `toDrizzleFilter(ast: QastNode | QueryAST): DrizzleFilter`
|
|
527
|
+
|
|
528
|
+
Transform an AST or QueryAST to a Drizzle filter.
|
|
529
|
+
|
|
530
|
+
**Returns:** Drizzle filter object with `where`, `orderBy`, `limit`, and `offset` properties
|
|
531
|
+
|
|
532
|
+
**Note:** Use `transformDrizzleConditions()` helper function to convert to actual Drizzle conditions.
|
|
268
533
|
|
|
269
534
|
### `validateQuery(ast: QastNode, whitelist: WhitelistOptions): void`
|
|
270
535
|
|
|
@@ -290,6 +555,33 @@ Extract all operators used in an AST.
|
|
|
290
555
|
|
|
291
556
|
**Returns:** Array of unique operators
|
|
292
557
|
|
|
558
|
+
### `validateQueryComplexity(ast: QastNode, options: ComplexityOptions): void`
|
|
559
|
+
|
|
560
|
+
Validate an AST against complexity limits.
|
|
561
|
+
|
|
562
|
+
**Parameters:**
|
|
563
|
+
- `ast` - The AST to validate
|
|
564
|
+
- `options` - Complexity options:
|
|
565
|
+
- `maxDepth?: number` - Maximum allowed AST depth
|
|
566
|
+
- `maxNodes?: number` - Maximum allowed number of nodes
|
|
567
|
+
- `maxArrayLength?: number` - Maximum allowed array length
|
|
568
|
+
- `maxStringLength?: number` - Maximum allowed string length
|
|
569
|
+
|
|
570
|
+
**Throws:** `ValidationError` if any limit is exceeded
|
|
571
|
+
|
|
572
|
+
**Example:**
|
|
573
|
+
```typescript
|
|
574
|
+
import { parseQuery, validateQueryComplexity } from 'qast';
|
|
575
|
+
|
|
576
|
+
const ast = parseQuery('age in [1,2,3,4,5]');
|
|
577
|
+
validateQueryComplexity(ast, {
|
|
578
|
+
maxDepth: 5,
|
|
579
|
+
maxNodes: 20,
|
|
580
|
+
maxArrayLength: 100,
|
|
581
|
+
maxStringLength: 200,
|
|
582
|
+
});
|
|
583
|
+
```
|
|
584
|
+
|
|
293
585
|
## Security Best Practices
|
|
294
586
|
|
|
295
587
|
1. **Always use whitelists**: Restrict which fields and operators can be used in queries.
|
|
@@ -304,7 +596,21 @@ const ast = parseQuery(req.query.filter, {
|
|
|
304
596
|
|
|
305
597
|
2. **Validate user input**: Don't trust user-provided query strings without validation.
|
|
306
598
|
|
|
307
|
-
3. **Limit query complexity**:
|
|
599
|
+
3. **Limit query complexity**: Use complexity limits to prevent DoS attacks.
|
|
600
|
+
|
|
601
|
+
```typescript
|
|
602
|
+
const ast = parseQuery(req.query.filter, {
|
|
603
|
+
allowedFields: ['age', 'name', 'city'],
|
|
604
|
+
allowedOperators: ['gt', 'eq', 'lt', 'in'],
|
|
605
|
+
validate: true,
|
|
606
|
+
// Complexity limits
|
|
607
|
+
maxQueryLength: 1000, // Reject queries longer than 1000 chars
|
|
608
|
+
maxDepth: 5, // Max 5 levels of nesting
|
|
609
|
+
maxNodes: 20, // Max 20 conditions
|
|
610
|
+
maxArrayLength: 100, // Max 100 items in 'in' arrays
|
|
611
|
+
maxStringLength: 200, // Max 200 chars per string value
|
|
612
|
+
});
|
|
613
|
+
```
|
|
308
614
|
|
|
309
615
|
4. **Use type checking**: Ensure values match expected types for fields.
|
|
310
616
|
|