qast 1.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.
Files changed (39) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +428 -0
  3. package/dist/adapters/prisma.d.ts +12 -0
  4. package/dist/adapters/prisma.d.ts.map +1 -0
  5. package/dist/adapters/prisma.js +132 -0
  6. package/dist/adapters/prisma.js.map +1 -0
  7. package/dist/adapters/sequelize.d.ts +37 -0
  8. package/dist/adapters/sequelize.d.ts.map +1 -0
  9. package/dist/adapters/sequelize.js +166 -0
  10. package/dist/adapters/sequelize.js.map +1 -0
  11. package/dist/adapters/typeorm.d.ts +18 -0
  12. package/dist/adapters/typeorm.d.ts.map +1 -0
  13. package/dist/adapters/typeorm.js +112 -0
  14. package/dist/adapters/typeorm.js.map +1 -0
  15. package/dist/errors.d.ts +35 -0
  16. package/dist/errors.d.ts.map +1 -0
  17. package/dist/errors.js +61 -0
  18. package/dist/errors.js.map +1 -0
  19. package/dist/index.d.ts +38 -0
  20. package/dist/index.d.ts.map +1 -0
  21. package/dist/index.js +78 -0
  22. package/dist/index.js.map +1 -0
  23. package/dist/parser/parser.d.ts +49 -0
  24. package/dist/parser/parser.d.ts.map +1 -0
  25. package/dist/parser/parser.js +148 -0
  26. package/dist/parser/parser.js.map +1 -0
  27. package/dist/parser/tokenizer.d.ts +73 -0
  28. package/dist/parser/tokenizer.d.ts.map +1 -0
  29. package/dist/parser/tokenizer.js +352 -0
  30. package/dist/parser/tokenizer.js.map +1 -0
  31. package/dist/parser/validator.d.ts +14 -0
  32. package/dist/parser/validator.d.ts.map +1 -0
  33. package/dist/parser/validator.js +94 -0
  34. package/dist/parser/validator.js.map +1 -0
  35. package/dist/types/ast.d.ts +72 -0
  36. package/dist/types/ast.d.ts.map +1 -0
  37. package/dist/types/ast.js +17 -0
  38. package/dist/types/ast.js.map +1 -0
  39. package/package.json +72 -0
package/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 QAST Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
package/README.md ADDED
@@ -0,0 +1,428 @@
1
+ # QAST — Query to AST to ORM
2
+
3
+ **QAST** is a small, ORM-agnostic library that parses human-readable query strings (e.g. `age gt 25 and (name eq "John" or city eq "Paris")`) into an **Abstract Syntax Tree (AST)** and then transforms that AST into **ORM-compatible filter objects** such as Prisma or TypeORM filters.
4
+
5
+ It aims to provide a secure, declarative, and type-safe way to support advanced filtering in REST APIs — without falling into the pitfalls of raw string-based query patterns.
6
+
7
+ ## Features
8
+
9
+ - 🔒 **Safe**: Validates operators, values, and fields against whitelists
10
+ - 🎯 **Type-Safe**: Full TypeScript support for parsed ASTs and generated filters
11
+ - 🔌 **ORM-Agnostic**: Works with Prisma, TypeORM, Sequelize, and more via adapters
12
+ - 📝 **Simple Syntax**: Natural query expressions using logical operators
13
+ - 🚀 **Lightweight**: No dependencies, small bundle size
14
+
15
+ ## Installation
16
+
17
+ ```bash
18
+ npm install qast
19
+ ```
20
+
21
+ ## Quick Start
22
+
23
+ ### Basic Usage
24
+
25
+ ```typescript
26
+ import { parseQuery, toPrismaFilter } from 'qast';
27
+
28
+ const query = 'age gt 25 and (name eq "John" or city eq "Paris")';
29
+
30
+ const ast = parseQuery(query);
31
+ const prismaFilter = toPrismaFilter(ast);
32
+
33
+ await prisma.user.findMany(prismaFilter);
34
+ ```
35
+
36
+ ### With Validation
37
+
38
+ ```typescript
39
+ import { parseQuery, toPrismaFilter } from 'qast';
40
+
41
+ const query = 'age gt 25 and name eq "John"';
42
+
43
+ // Parse with whitelist validation
44
+ const ast = parseQuery(query, {
45
+ allowedFields: ['age', 'name', 'city'],
46
+ allowedOperators: ['gt', 'eq', 'lt'],
47
+ validate: true,
48
+ });
49
+
50
+ const prismaFilter = toPrismaFilter(ast);
51
+ await prisma.user.findMany(prismaFilter);
52
+ ```
53
+
54
+ ## Query Syntax
55
+
56
+ ### Operators
57
+
58
+ QAST supports the following comparison operators:
59
+
60
+ - `eq` - Equal
61
+ - `ne` - Not equal
62
+ - `gt` - Greater than
63
+ - `lt` - Less than
64
+ - `gte` - Greater than or equal
65
+ - `lte` - Less than or equal
66
+ - `in` - In array
67
+ - `contains` - Contains substring (string matching)
68
+
69
+ ### Logical Operators
70
+
71
+ - `and` - Logical AND
72
+ - `or` - Logical OR
73
+
74
+ ### Values
75
+
76
+ - **Strings**: Use single or double quotes: `"John"` or `'John'`
77
+ - **Numbers**: Integers or floats: `25`, `25.99`, `-10`
78
+ - **Booleans**: `true` or `false`
79
+ - **Arrays**: For `in` operator: `[1,2,3]` or `["John","Jane"]`
80
+
81
+ ### Examples
82
+
83
+ ```typescript
84
+ // Simple comparison
85
+ 'age gt 25'
86
+
87
+ // String comparison
88
+ 'name eq "John"'
89
+
90
+ // Boolean comparison
91
+ 'active eq true'
92
+
93
+ // Array (in operator)
94
+ 'age in [1,2,3]'
95
+
96
+ // AND operation
97
+ 'age gt 25 and name eq "John"'
98
+
99
+ // OR operation
100
+ 'name eq "John" or name eq "Jane"'
101
+
102
+ // Nested parentheses
103
+ 'age gt 25 and (name eq "John" or city eq "Paris")'
104
+
105
+ // Complex query
106
+ 'age gt 25 and (name eq "John" or city eq "Paris") and active eq true'
107
+ ```
108
+
109
+ ## ORM Adapters
110
+
111
+ ### Prisma
112
+
113
+ ```typescript
114
+ import { parseQuery, toPrismaFilter } from 'qast';
115
+
116
+ const query = 'age gt 25 and name eq "John"';
117
+ const ast = parseQuery(query);
118
+ const filter = toPrismaFilter(ast);
119
+
120
+ // filter = {
121
+ // where: {
122
+ // age: { gt: 25 },
123
+ // name: { equals: "John" }
124
+ // }
125
+ // }
126
+
127
+ await prisma.user.findMany(filter);
128
+ ```
129
+
130
+ ### TypeORM
131
+
132
+ ```typescript
133
+ import { parseQuery, toTypeORMFilter } from 'qast';
134
+ import { MoreThan, Equal } from 'typeorm';
135
+
136
+ const query = 'age gt 25 and name eq "John"';
137
+ const ast = parseQuery(query);
138
+ const filter = toTypeORMFilter(ast);
139
+
140
+ // Note: TypeORM requires operator functions for non-equality comparisons
141
+ // The adapter returns a structure that you can transform using TypeORM operators
142
+ // For equality, TypeORM accepts plain values directly
143
+
144
+ // filter.where = {
145
+ // age: { __qast_operator__: 'gt', value: 25 },
146
+ // name: "John"
147
+ // }
148
+
149
+ // Transform to use TypeORM operators:
150
+ // const transformed = {
151
+ // age: MoreThan(25),
152
+ // name: "John"
153
+ // }
154
+
155
+ await userRepository.find({ where: transformed });
156
+ ```
157
+
158
+ **Note**: TypeORM requires operator functions (`MoreThan`, `LessThan`, etc.) for non-equality comparisons. The adapter returns a structure with metadata that you can transform. For equality comparisons, TypeORM accepts plain values.
159
+
160
+ ### Sequelize
161
+
162
+ ```typescript
163
+ import { parseQuery, toSequelizeFilter } from 'qast';
164
+ import { Op } from 'sequelize';
165
+
166
+ const query = 'age gt 25 and name eq "John"';
167
+ const ast = parseQuery(query);
168
+ const filter = toSequelizeFilter(ast);
169
+
170
+ // filter = {
171
+ // __qast_logical__: 'and',
172
+ // conditions: [
173
+ // { age: { __qast_operator__: 'gt', value: 25 } },
174
+ // { name: 'John' }
175
+ // ]
176
+ // }
177
+
178
+ // Transform to use Sequelize Op operators:
179
+ function transformSequelizeFilter(filter: any): any {
180
+ if (filter.__qast_logical__) {
181
+ const op = filter.__qast_logical__ === 'and' ? Op.and : Op.or;
182
+ return {
183
+ [op]: filter.conditions.map(transformSequelizeFilter),
184
+ };
185
+ }
186
+
187
+ const result: any = {};
188
+ for (const [key, value] of Object.entries(filter)) {
189
+ if (value && typeof value === 'object' && '__qast_operator__' in value) {
190
+ const opKey = value.__qast_operator__;
191
+ const op = Op[opKey as keyof typeof Op];
192
+ if (opKey === 'contains') {
193
+ result[key] = { [Op.like]: `%${value.value}%` };
194
+ } else {
195
+ result[key] = { [op]: value.value };
196
+ }
197
+ } else {
198
+ result[key] = value;
199
+ }
200
+ }
201
+ return result;
202
+ }
203
+
204
+ const transformed = transformSequelizeFilter(filter);
205
+ // transformed = {
206
+ // [Op.and]: [
207
+ // { age: { [Op.gt]: 25 } },
208
+ // { name: 'John' }
209
+ // ]
210
+ // }
211
+
212
+ await User.findAll({ where: transformed });
213
+ ```
214
+
215
+ **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.
216
+
217
+ ## API Reference
218
+
219
+ ### `parseQuery(query: string, options?: ParseOptions): QastNode`
220
+
221
+ Parse a query string into an AST.
222
+
223
+ **Parameters:**
224
+ - `query` - The query string to parse
225
+ - `options` - Optional parsing options:
226
+ - `allowedFields?: string[]` - Whitelist of allowed field names
227
+ - `allowedOperators?: Operator[]` - Whitelist of allowed operators
228
+ - `validate?: boolean` - Whether to validate against whitelists (default: true if whitelists are provided)
229
+
230
+ **Returns:** The parsed AST node
231
+
232
+ **Example:**
233
+ ```typescript
234
+ const ast = parseQuery('age gt 25', {
235
+ allowedFields: ['age', 'name'],
236
+ allowedOperators: ['gt', 'eq'],
237
+ validate: true,
238
+ });
239
+ ```
240
+
241
+ ### `toPrismaFilter(ast: QastNode): PrismaFilter`
242
+
243
+ Transform an AST to a Prisma filter.
244
+
245
+ **Returns:** Prisma filter object with `where` property
246
+
247
+ ### `toTypeORMFilter(ast: QastNode): TypeORMFilter`
248
+
249
+ Transform an AST to a TypeORM filter.
250
+
251
+ **Returns:** TypeORM filter object with `where` property
252
+
253
+ **Note:** TypeORM requires operator functions for non-equality comparisons. You may need to transform the result.
254
+
255
+ ### `toSequelizeFilter(ast: QastNode): SequelizeFilter`
256
+
257
+ Transform an AST to a Sequelize filter.
258
+
259
+ **Returns:** Sequelize filter object
260
+
261
+ **Note:** Sequelize uses the `Op` object. You need to transform `$`-prefixed operators to use `Op` operators.
262
+
263
+ ### `validateQuery(ast: QastNode, whitelist: WhitelistOptions): void`
264
+
265
+ Validate an AST against whitelists.
266
+
267
+ **Parameters:**
268
+ - `ast` - The AST to validate
269
+ - `whitelist` - Whitelist options:
270
+ - `allowedFields?: string[]` - Allowed field names
271
+ - `allowedOperators?: Operator[]` - Allowed operators
272
+
273
+ **Throws:** `ValidationError` if validation fails
274
+
275
+ ### `extractFields(ast: QastNode): string[]`
276
+
277
+ Extract all field names used in an AST.
278
+
279
+ **Returns:** Array of unique field names
280
+
281
+ ### `extractOperators(ast: QastNode): Operator[]`
282
+
283
+ Extract all operators used in an AST.
284
+
285
+ **Returns:** Array of unique operators
286
+
287
+ ## Security Best Practices
288
+
289
+ 1. **Always use whitelists**: Restrict which fields and operators can be used in queries.
290
+
291
+ ```typescript
292
+ const ast = parseQuery(req.query.filter, {
293
+ allowedFields: ['age', 'name', 'city'],
294
+ allowedOperators: ['gt', 'eq', 'lt'],
295
+ validate: true,
296
+ });
297
+ ```
298
+
299
+ 2. **Validate user input**: Don't trust user-provided query strings without validation.
300
+
301
+ 3. **Limit query complexity**: Consider limiting the depth of nested queries to prevent DoS attacks.
302
+
303
+ 4. **Use type checking**: Ensure values match expected types for fields.
304
+
305
+ ## Error Handling
306
+
307
+ QAST provides custom error classes:
308
+
309
+ - `ParseError` - Syntax errors in query strings
310
+ - `ValidationError` - Validation failures (disallowed fields/operators)
311
+ - `TokenizationError` - Tokenization errors
312
+
313
+ ```typescript
314
+ import { parseQuery, ParseError, ValidationError } from 'qast';
315
+
316
+ try {
317
+ const ast = parseQuery(query, { allowedFields: ['age'], validate: true });
318
+ } catch (error) {
319
+ if (error instanceof ParseError) {
320
+ console.error('Parse error:', error.message);
321
+ } else if (error instanceof ValidationError) {
322
+ console.error('Validation error:', error.message);
323
+ console.error('Field:', error.field);
324
+ console.error('Operator:', error.operator);
325
+ }
326
+ }
327
+ ```
328
+
329
+ ## TypeScript Support
330
+
331
+ QAST is written in TypeScript and provides full type definitions:
332
+
333
+ ```typescript
334
+ import { QastNode, ComparisonNode, LogicalNode, Operator } from 'qast';
335
+
336
+ function processNode(node: QastNode): void {
337
+ if (node.type === 'COMPARISON') {
338
+ const comparison = node as ComparisonNode;
339
+ console.log(comparison.field, comparison.op, comparison.value);
340
+ } else if (node.type === 'AND' || node.type === 'OR') {
341
+ const logical = node as LogicalNode;
342
+ processNode(logical.left);
343
+ processNode(logical.right);
344
+ }
345
+ }
346
+ ```
347
+
348
+ ## Examples
349
+
350
+ ### REST API Endpoint
351
+
352
+ ```typescript
353
+ import { parseQuery, toPrismaFilter } from 'qast';
354
+ import { PrismaClient } from '@prisma/client';
355
+
356
+ const prisma = new PrismaClient();
357
+
358
+ app.get('/users', async (req, res) => {
359
+ try {
360
+ const query = req.query.filter as string;
361
+
362
+ // Parse and validate query
363
+ const ast = parseQuery(query, {
364
+ allowedFields: ['age', 'name', 'city', 'active'],
365
+ allowedOperators: ['gt', 'lt', 'eq', 'in'],
366
+ validate: true,
367
+ });
368
+
369
+ // Transform to Prisma filter
370
+ const filter = toPrismaFilter(ast);
371
+
372
+ // Query database
373
+ const users = await prisma.user.findMany(filter);
374
+
375
+ res.json(users);
376
+ } catch (error) {
377
+ if (error instanceof ValidationError) {
378
+ res.status(400).json({ error: error.message });
379
+ } else {
380
+ res.status(500).json({ error: 'Internal server error' });
381
+ }
382
+ }
383
+ });
384
+ ```
385
+
386
+ ### Express Middleware
387
+
388
+ ```typescript
389
+ import { parseQuery, toPrismaFilter, ValidationError } from 'qast';
390
+
391
+ function qastMiddleware(allowedFields: string[], allowedOperators: Operator[]) {
392
+ return (req, res, next) => {
393
+ try {
394
+ if (req.query.filter) {
395
+ const ast = parseQuery(req.query.filter, {
396
+ allowedFields,
397
+ allowedOperators,
398
+ validate: true,
399
+ });
400
+
401
+ req.qastFilter = toPrismaFilter(ast);
402
+ }
403
+ next();
404
+ } catch (error) {
405
+ if (error instanceof ValidationError) {
406
+ res.status(400).json({ error: error.message });
407
+ } else {
408
+ next(error);
409
+ }
410
+ }
411
+ };
412
+ }
413
+ ```
414
+
415
+ ## License
416
+
417
+ MIT © 2025
418
+
419
+ ## Contributing
420
+
421
+ Contributions are welcome! Please feel free to submit a Pull Request.
422
+
423
+ - GitHub Repository: https://github.com/hocestnonsatis/qast
424
+ - Issues: https://github.com/hocestnonsatis/qast/issues
425
+
426
+ ## Acknowledgments
427
+
428
+ QAST is inspired by the need for safe, type-safe query parsing in REST APIs. It aims to provide a lightweight alternative to complex query protocols while maintaining security and developer experience.
@@ -0,0 +1,12 @@
1
+ import { QastNode } from '../types/ast';
2
+ /**
3
+ * Prisma filter type
4
+ */
5
+ export type PrismaFilter = {
6
+ where: Record<string, any>;
7
+ };
8
+ /**
9
+ * Transform a QAST AST node to a Prisma filter
10
+ */
11
+ export declare function toPrismaFilter(ast: QastNode): PrismaFilter;
12
+ //# sourceMappingURL=prisma.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prisma.d.ts","sourceRoot":"","sources":["../../src/adapters/prisma.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAgE,MAAM,cAAc,CAAC;AAEtG;;GAEG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAC5B,CAAC;AAEF;;GAEG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,QAAQ,GAAG,YAAY,CAI1D"}
@@ -0,0 +1,132 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.toPrismaFilter = toPrismaFilter;
4
+ const ast_1 = require("../types/ast");
5
+ /**
6
+ * Transform a QAST AST node to a Prisma filter
7
+ */
8
+ function toPrismaFilter(ast) {
9
+ return {
10
+ where: transformNode(ast),
11
+ };
12
+ }
13
+ /**
14
+ * Transform a node to Prisma format
15
+ */
16
+ function transformNode(node) {
17
+ if ((0, ast_1.isComparisonNode)(node)) {
18
+ return transformComparisonNode(node);
19
+ }
20
+ else if ((0, ast_1.isLogicalNode)(node)) {
21
+ return transformLogicalNode(node);
22
+ }
23
+ throw new Error('Invalid node type');
24
+ }
25
+ /**
26
+ * Transform a comparison node to Prisma format
27
+ */
28
+ function transformComparisonNode(node) {
29
+ const { field, op, value } = node;
30
+ // Map operators to Prisma operators
31
+ switch (op) {
32
+ case 'eq':
33
+ return { [field]: { equals: value } };
34
+ case 'ne':
35
+ return { [field]: { not: value } };
36
+ case 'gt':
37
+ return { [field]: { gt: value } };
38
+ case 'lt':
39
+ return { [field]: { lt: value } };
40
+ case 'gte':
41
+ return { [field]: { gte: value } };
42
+ case 'lte':
43
+ return { [field]: { lte: value } };
44
+ case 'in':
45
+ return { [field]: { in: value } };
46
+ case 'contains':
47
+ return { [field]: { contains: value } };
48
+ default:
49
+ throw new Error(`Unsupported operator: ${op}`);
50
+ }
51
+ }
52
+ /**
53
+ * Transform a logical node to Prisma format
54
+ */
55
+ function transformLogicalNode(node) {
56
+ const leftFilter = transformNode(node.left);
57
+ const rightFilter = transformNode(node.right);
58
+ if (node.type === 'AND') {
59
+ // For AND, merge the filters
60
+ return mergeFilters(leftFilter, rightFilter);
61
+ }
62
+ else {
63
+ // For OR, create an OR array
64
+ // If either side already has an OR, we need to flatten
65
+ const leftOr = leftFilter.OR;
66
+ const rightOr = rightFilter.OR;
67
+ if (leftOr && rightOr) {
68
+ // Both have OR arrays, combine them
69
+ return {
70
+ OR: [...leftOr, ...rightOr],
71
+ };
72
+ }
73
+ else if (leftOr) {
74
+ // Left has OR, add right to it
75
+ return {
76
+ OR: [...leftOr, rightFilter],
77
+ };
78
+ }
79
+ else if (rightOr) {
80
+ // Right has OR, add left to it
81
+ return {
82
+ OR: [leftFilter, ...rightOr],
83
+ };
84
+ }
85
+ else {
86
+ // Neither has OR, create new OR array
87
+ return {
88
+ OR: [leftFilter, rightFilter],
89
+ };
90
+ }
91
+ }
92
+ }
93
+ /**
94
+ * Merge two Prisma filters (for AND operations)
95
+ */
96
+ function mergeFilters(left, right) {
97
+ const result = { ...left };
98
+ // Handle OR arrays - if both have OR, we need to combine them properly
99
+ // For AND with OR, Prisma requires: { field: value, OR: [...] }
100
+ if (left.OR || right.OR) {
101
+ // If both sides have OR, we need to distribute
102
+ // This is complex - for now, we'll merge non-OR fields and keep OR separate
103
+ const leftOr = left.OR;
104
+ const rightOr = right.OR;
105
+ // Remove OR from both objects
106
+ const leftWithoutOr = { ...left };
107
+ const rightWithoutOr = { ...right };
108
+ delete leftWithoutOr.OR;
109
+ delete rightWithoutOr.OR;
110
+ // Merge non-OR fields
111
+ Object.assign(result, leftWithoutOr);
112
+ Object.assign(result, rightWithoutOr);
113
+ // Handle OR arrays - in Prisma, when ANDing with OR, we need to be careful
114
+ if (leftOr && rightOr) {
115
+ // This is complex - we'll create nested conditions
116
+ // For simplicity, we'll merge all fields and combine ORs
117
+ result.OR = [...(leftOr || []), ...(rightOr || [])];
118
+ }
119
+ else if (leftOr) {
120
+ result.OR = leftOr;
121
+ }
122
+ else if (rightOr) {
123
+ result.OR = rightOr;
124
+ }
125
+ }
126
+ else {
127
+ // Simple merge - combine all fields
128
+ Object.assign(result, right);
129
+ }
130
+ return result;
131
+ }
132
+ //# sourceMappingURL=prisma.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prisma.js","sourceRoot":"","sources":["../../src/adapters/prisma.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,37 @@
1
+ import { QastNode } from '../types/ast';
2
+ /**
3
+ * Sequelize filter type
4
+ *
5
+ * Note: Sequelize uses the Op object from 'sequelize' package for operators.
6
+ * Since Sequelize is an optional peer dependency, we cannot import Op directly.
7
+ *
8
+ * This adapter returns a structure that represents the query logic, but you need
9
+ * to transform it to use Sequelize's Op operators.
10
+ *
11
+ * For simple equality, Sequelize accepts plain values: { age: 25 }
12
+ * For other operators, you need Op: { age: { [Op.gt]: 25 } }
13
+ * For logical operations, you need Op.and/Op.or: { [Op.and]: [...] }
14
+ */
15
+ export type SequelizeFilter = Record<string, any>;
16
+ /**
17
+ * Transform a QAST AST node to a Sequelize filter
18
+ *
19
+ * IMPORTANT: Sequelize uses the Op object from 'sequelize', not $ operators.
20
+ * This adapter returns a structure with metadata that you need to transform.
21
+ *
22
+ * Example usage:
23
+ * ```ts
24
+ * import { Op } from 'sequelize';
25
+ * import { toSequelizeFilter } from 'qast';
26
+ *
27
+ * const filter = toSequelizeFilter(ast);
28
+ * // Returns a structure like:
29
+ * // { age: { __qast_operator__: 'gt', value: 25 } }
30
+ * // You need to transform it:
31
+ * // { age: { [Op.gt]: 25 } }
32
+ * ```
33
+ *
34
+ * For simple equality (eq operator), you can use plain values directly.
35
+ */
36
+ export declare function toSequelizeFilter(ast: QastNode): SequelizeFilter;
37
+ //# sourceMappingURL=sequelize.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sequelize.d.ts","sourceRoot":"","sources":["../../src/adapters/sequelize.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAgE,MAAM,cAAc,CAAC;AAEtG;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AAElD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,QAAQ,GAAG,eAAe,CAEhE"}