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.
- package/LICENSE +22 -0
- package/README.md +428 -0
- package/dist/adapters/prisma.d.ts +12 -0
- package/dist/adapters/prisma.d.ts.map +1 -0
- package/dist/adapters/prisma.js +132 -0
- package/dist/adapters/prisma.js.map +1 -0
- package/dist/adapters/sequelize.d.ts +37 -0
- package/dist/adapters/sequelize.d.ts.map +1 -0
- package/dist/adapters/sequelize.js +166 -0
- package/dist/adapters/sequelize.js.map +1 -0
- package/dist/adapters/typeorm.d.ts +18 -0
- package/dist/adapters/typeorm.d.ts.map +1 -0
- package/dist/adapters/typeorm.js +112 -0
- package/dist/adapters/typeorm.js.map +1 -0
- package/dist/errors.d.ts +35 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +61 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +38 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +78 -0
- package/dist/index.js.map +1 -0
- package/dist/parser/parser.d.ts +49 -0
- package/dist/parser/parser.d.ts.map +1 -0
- package/dist/parser/parser.js +148 -0
- package/dist/parser/parser.js.map +1 -0
- package/dist/parser/tokenizer.d.ts +73 -0
- package/dist/parser/tokenizer.d.ts.map +1 -0
- package/dist/parser/tokenizer.js +352 -0
- package/dist/parser/tokenizer.js.map +1 -0
- package/dist/parser/validator.d.ts +14 -0
- package/dist/parser/validator.d.ts.map +1 -0
- package/dist/parser/validator.js +94 -0
- package/dist/parser/validator.js.map +1 -0
- package/dist/types/ast.d.ts +72 -0
- package/dist/types/ast.d.ts.map +1 -0
- package/dist/types/ast.js +17 -0
- package/dist/types/ast.js.map +1 -0
- package/package.json +72 -0
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.toSequelizeFilter = toSequelizeFilter;
|
|
4
|
+
const ast_1 = require("../types/ast");
|
|
5
|
+
/**
|
|
6
|
+
* Transform a QAST AST node to a Sequelize filter
|
|
7
|
+
*
|
|
8
|
+
* IMPORTANT: Sequelize uses the Op object from 'sequelize', not $ operators.
|
|
9
|
+
* This adapter returns a structure with metadata that you need to transform.
|
|
10
|
+
*
|
|
11
|
+
* Example usage:
|
|
12
|
+
* ```ts
|
|
13
|
+
* import { Op } from 'sequelize';
|
|
14
|
+
* import { toSequelizeFilter } from 'qast';
|
|
15
|
+
*
|
|
16
|
+
* const filter = toSequelizeFilter(ast);
|
|
17
|
+
* // Returns a structure like:
|
|
18
|
+
* // { age: { __qast_operator__: 'gt', value: 25 } }
|
|
19
|
+
* // You need to transform it:
|
|
20
|
+
* // { age: { [Op.gt]: 25 } }
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* For simple equality (eq operator), you can use plain values directly.
|
|
24
|
+
*/
|
|
25
|
+
function toSequelizeFilter(ast) {
|
|
26
|
+
return transformNode(ast);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Transform a node to Sequelize format
|
|
30
|
+
*/
|
|
31
|
+
function transformNode(node) {
|
|
32
|
+
if ((0, ast_1.isComparisonNode)(node)) {
|
|
33
|
+
return transformComparisonNode(node);
|
|
34
|
+
}
|
|
35
|
+
else if ((0, ast_1.isLogicalNode)(node)) {
|
|
36
|
+
return transformLogicalNode(node);
|
|
37
|
+
}
|
|
38
|
+
throw new Error('Invalid node type');
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Transform a comparison node to Sequelize format
|
|
42
|
+
*
|
|
43
|
+
* Sequelize uses Op operators from 'sequelize' package.
|
|
44
|
+
* Since we cannot import Op (optional peer dependency), we return metadata
|
|
45
|
+
* that users can transform to use Op operators.
|
|
46
|
+
*
|
|
47
|
+
* For equality (eq), Sequelize accepts plain values, so we return them directly.
|
|
48
|
+
* For other operators, we return metadata that indicates the operator type.
|
|
49
|
+
*/
|
|
50
|
+
function transformComparisonNode(node) {
|
|
51
|
+
const { field, op, value } = node;
|
|
52
|
+
// For equality, Sequelize accepts plain values
|
|
53
|
+
if (op === 'eq') {
|
|
54
|
+
return { [field]: value };
|
|
55
|
+
}
|
|
56
|
+
// For other operators, return metadata that can be transformed to use Op
|
|
57
|
+
// Users need to import Op from 'sequelize' and transform:
|
|
58
|
+
// { __qast_operator__: 'gt', value: 25 } -> { [Op.gt]: 25 }
|
|
59
|
+
switch (op) {
|
|
60
|
+
case 'ne':
|
|
61
|
+
return { [field]: { __qast_operator__: 'ne', value } };
|
|
62
|
+
case 'gt':
|
|
63
|
+
return { [field]: { __qast_operator__: 'gt', value } };
|
|
64
|
+
case 'lt':
|
|
65
|
+
return { [field]: { __qast_operator__: 'lt', value } };
|
|
66
|
+
case 'gte':
|
|
67
|
+
return { [field]: { __qast_operator__: 'gte', value } };
|
|
68
|
+
case 'lte':
|
|
69
|
+
return { [field]: { __qast_operator__: 'lte', value } };
|
|
70
|
+
case 'in':
|
|
71
|
+
return { [field]: { __qast_operator__: 'in', value } };
|
|
72
|
+
case 'contains':
|
|
73
|
+
// Sequelize uses Op.like for contains with wildcards
|
|
74
|
+
return { [field]: { __qast_operator__: 'contains', value } };
|
|
75
|
+
default:
|
|
76
|
+
throw new Error(`Unsupported operator: ${op}`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Transform a logical node to Sequelize format
|
|
81
|
+
*
|
|
82
|
+
* Sequelize uses Op.and and Op.or for logical operations.
|
|
83
|
+
* Since we cannot import Op, we return metadata that indicates the logical operation.
|
|
84
|
+
*
|
|
85
|
+
* Users need to transform: { __qast_logical__: 'and', conditions: [...] }
|
|
86
|
+
* to: { [Op.and]: [...] }
|
|
87
|
+
*/
|
|
88
|
+
function transformLogicalNode(node) {
|
|
89
|
+
const leftFilter = transformNode(node.left);
|
|
90
|
+
const rightFilter = transformNode(node.right);
|
|
91
|
+
// Handle nested logical operations
|
|
92
|
+
// Check if either side already has logical operation metadata
|
|
93
|
+
const leftLogical = leftFilter.__qast_logical__;
|
|
94
|
+
const rightLogical = rightFilter.__qast_logical__;
|
|
95
|
+
if (node.type === 'AND') {
|
|
96
|
+
// For AND, combine conditions
|
|
97
|
+
if (leftLogical === 'and' && rightLogical === 'and') {
|
|
98
|
+
// Both have AND, merge their conditions
|
|
99
|
+
const leftConditions = leftFilter.conditions;
|
|
100
|
+
const rightConditions = rightFilter.conditions;
|
|
101
|
+
return {
|
|
102
|
+
__qast_logical__: 'and',
|
|
103
|
+
conditions: [...leftConditions, ...rightConditions],
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
else if (leftLogical === 'and') {
|
|
107
|
+
// Left has AND, add right to it
|
|
108
|
+
const leftConditions = leftFilter.conditions;
|
|
109
|
+
return {
|
|
110
|
+
__qast_logical__: 'and',
|
|
111
|
+
conditions: [...leftConditions, rightFilter],
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
else if (rightLogical === 'and') {
|
|
115
|
+
// Right has AND, add left to it
|
|
116
|
+
const rightConditions = rightFilter.conditions;
|
|
117
|
+
return {
|
|
118
|
+
__qast_logical__: 'and',
|
|
119
|
+
conditions: [leftFilter, ...rightConditions],
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
// Neither has AND, create new AND array
|
|
124
|
+
return {
|
|
125
|
+
__qast_logical__: 'and',
|
|
126
|
+
conditions: [leftFilter, rightFilter],
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
// For OR, combine conditions
|
|
132
|
+
if (leftLogical === 'or' && rightLogical === 'or') {
|
|
133
|
+
// Both have OR, merge their conditions
|
|
134
|
+
const leftConditions = leftFilter.conditions;
|
|
135
|
+
const rightConditions = rightFilter.conditions;
|
|
136
|
+
return {
|
|
137
|
+
__qast_logical__: 'or',
|
|
138
|
+
conditions: [...leftConditions, ...rightConditions],
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
else if (leftLogical === 'or') {
|
|
142
|
+
// Left has OR, add right to it
|
|
143
|
+
const leftConditions = leftFilter.conditions;
|
|
144
|
+
return {
|
|
145
|
+
__qast_logical__: 'or',
|
|
146
|
+
conditions: [...leftConditions, rightFilter],
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
else if (rightLogical === 'or') {
|
|
150
|
+
// Right has OR, add left to it
|
|
151
|
+
const rightConditions = rightFilter.conditions;
|
|
152
|
+
return {
|
|
153
|
+
__qast_logical__: 'or',
|
|
154
|
+
conditions: [leftFilter, ...rightConditions],
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
// Neither has OR, create new OR array
|
|
159
|
+
return {
|
|
160
|
+
__qast_logical__: 'or',
|
|
161
|
+
conditions: [leftFilter, rightFilter],
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
//# sourceMappingURL=sequelize.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sequelize.js","sourceRoot":"","sources":["../../src/adapters/sequelize.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { QastNode } from '../types/ast';
|
|
2
|
+
/**
|
|
3
|
+
* TypeORM filter type
|
|
4
|
+
* Note: This returns a plain object that can be used with TypeORM's FindOptions
|
|
5
|
+
* In practice, TypeORM users may need to import operators from 'typeorm'
|
|
6
|
+
*/
|
|
7
|
+
export type TypeORMFilter = {
|
|
8
|
+
where: Record<string, any> | Array<Record<string, any>>;
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Transform a QAST AST node to a TypeORM filter
|
|
12
|
+
*
|
|
13
|
+
* Note: TypeORM uses operator functions like MoreThan(), LessThan(), etc.
|
|
14
|
+
* This adapter returns a structure that can be used with TypeORM's FindOptions.
|
|
15
|
+
* For production use, users may need to wrap values with TypeORM operators.
|
|
16
|
+
*/
|
|
17
|
+
export declare function toTypeORMFilter(ast: QastNode): TypeORMFilter;
|
|
18
|
+
//# sourceMappingURL=typeorm.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"typeorm.d.ts","sourceRoot":"","sources":["../../src/adapters/typeorm.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAgE,MAAM,cAAc,CAAC;AAEtG;;;;GAIG;AACH,MAAM,MAAM,aAAa,GAAG;IAC1B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC;CACzD,CAAC;AAEF;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,QAAQ,GAAG,aAAa,CAG5D"}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.toTypeORMFilter = toTypeORMFilter;
|
|
4
|
+
const ast_1 = require("../types/ast");
|
|
5
|
+
/**
|
|
6
|
+
* Transform a QAST AST node to a TypeORM filter
|
|
7
|
+
*
|
|
8
|
+
* Note: TypeORM uses operator functions like MoreThan(), LessThan(), etc.
|
|
9
|
+
* This adapter returns a structure that can be used with TypeORM's FindOptions.
|
|
10
|
+
* For production use, users may need to wrap values with TypeORM operators.
|
|
11
|
+
*/
|
|
12
|
+
function toTypeORMFilter(ast) {
|
|
13
|
+
const where = transformNode(ast);
|
|
14
|
+
return { where };
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Transform a node to TypeORM format
|
|
18
|
+
*/
|
|
19
|
+
function transformNode(node) {
|
|
20
|
+
if ((0, ast_1.isComparisonNode)(node)) {
|
|
21
|
+
return transformComparisonNode(node);
|
|
22
|
+
}
|
|
23
|
+
else if ((0, ast_1.isLogicalNode)(node)) {
|
|
24
|
+
return transformLogicalNode(node);
|
|
25
|
+
}
|
|
26
|
+
throw new Error('Invalid node type');
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Transform a comparison node to TypeORM format
|
|
30
|
+
*
|
|
31
|
+
* TypeORM uses operator functions from 'typeorm' package.
|
|
32
|
+
* Since we don't want to import TypeORM directly (optional peer dependency),
|
|
33
|
+
* we return a structure with metadata that can be used to build TypeORM FindOptions.
|
|
34
|
+
*
|
|
35
|
+
* For simple equality (eq), TypeORM accepts plain values.
|
|
36
|
+
* For other operators, users should use TypeORM operators like MoreThan(), LessThan(), etc.
|
|
37
|
+
*
|
|
38
|
+
* This function returns an object with an __qast_operator__ property that indicates
|
|
39
|
+
* the operator type. Users can transform this to use TypeORM operators.
|
|
40
|
+
*/
|
|
41
|
+
function transformComparisonNode(node) {
|
|
42
|
+
const { field, op, value } = node;
|
|
43
|
+
// Map operators to TypeORM-compatible format
|
|
44
|
+
// For eq, use direct value (TypeORM supports this)
|
|
45
|
+
// For other operators, include metadata for transformation
|
|
46
|
+
switch (op) {
|
|
47
|
+
case 'eq':
|
|
48
|
+
// Direct equality - TypeORM accepts this
|
|
49
|
+
return { [field]: value };
|
|
50
|
+
case 'ne':
|
|
51
|
+
// Not equal - requires Not(Equal(value)) from TypeORM
|
|
52
|
+
return { [field]: { __qast_operator__: 'ne', value } };
|
|
53
|
+
case 'gt':
|
|
54
|
+
// More than - requires MoreThan(value) from TypeORM
|
|
55
|
+
return { [field]: { __qast_operator__: 'gt', value } };
|
|
56
|
+
case 'lt':
|
|
57
|
+
// Less than - requires LessThan(value) from TypeORM
|
|
58
|
+
return { [field]: { __qast_operator__: 'lt', value } };
|
|
59
|
+
case 'gte':
|
|
60
|
+
// More than or equal - requires MoreThanOrEqual(value) from TypeORM
|
|
61
|
+
return { [field]: { __qast_operator__: 'gte', value } };
|
|
62
|
+
case 'lte':
|
|
63
|
+
// Less than or equal - requires LessThanOrEqual(value) from TypeORM
|
|
64
|
+
return { [field]: { __qast_operator__: 'lte', value } };
|
|
65
|
+
case 'in':
|
|
66
|
+
// In array - requires In(value) from TypeORM
|
|
67
|
+
return { [field]: { __qast_operator__: 'in', value } };
|
|
68
|
+
case 'contains':
|
|
69
|
+
// Contains (like) - requires Like(`%${value}%`) from TypeORM
|
|
70
|
+
return { [field]: { __qast_operator__: 'contains', value } };
|
|
71
|
+
default:
|
|
72
|
+
throw new Error(`Unsupported operator: ${op}`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Transform a logical node to TypeORM format
|
|
77
|
+
*
|
|
78
|
+
* TypeORM handles AND/OR differently:
|
|
79
|
+
* - AND: Multiple conditions in same object (merged)
|
|
80
|
+
* - OR: Array of condition objects
|
|
81
|
+
*/
|
|
82
|
+
function transformLogicalNode(node) {
|
|
83
|
+
const leftFilter = transformNode(node.left);
|
|
84
|
+
const rightFilter = transformNode(node.right);
|
|
85
|
+
if (node.type === 'AND') {
|
|
86
|
+
// For AND, merge the filters into a single object
|
|
87
|
+
return mergeFilters(leftFilter, rightFilter);
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
// For OR, create an array of conditions
|
|
91
|
+
const leftArray = Array.isArray(leftFilter) ? leftFilter : [leftFilter];
|
|
92
|
+
const rightArray = Array.isArray(rightFilter) ? rightFilter : [rightFilter];
|
|
93
|
+
return [...leftArray, ...rightArray];
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Merge two TypeORM filters (for AND operations)
|
|
98
|
+
*/
|
|
99
|
+
function mergeFilters(left, right) {
|
|
100
|
+
// If either is an array, we need to flatten
|
|
101
|
+
// For AND operations in TypeORM, we merge objects
|
|
102
|
+
if (Array.isArray(left) || Array.isArray(right)) {
|
|
103
|
+
// If we have arrays in AND, we need to create a cartesian product
|
|
104
|
+
// For simplicity, we'll flatten and merge
|
|
105
|
+
const leftObj = Array.isArray(left) ? Object.assign({}, ...left) : left;
|
|
106
|
+
const rightObj = Array.isArray(right) ? Object.assign({}, ...right) : right;
|
|
107
|
+
return { ...leftObj, ...rightObj };
|
|
108
|
+
}
|
|
109
|
+
// Both are objects, merge them
|
|
110
|
+
return { ...left, ...right };
|
|
111
|
+
}
|
|
112
|
+
//# sourceMappingURL=typeorm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"typeorm.js","sourceRoot":"","sources":["../../src/adapters/typeorm.ts"],"names":[],"mappings":"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"}
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base error class for QAST library
|
|
3
|
+
*/
|
|
4
|
+
export declare class QastError extends Error {
|
|
5
|
+
constructor(message: string);
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Error thrown during tokenization when an invalid token is encountered
|
|
9
|
+
*/
|
|
10
|
+
export declare class TokenizationError extends QastError {
|
|
11
|
+
readonly position: number;
|
|
12
|
+
readonly query: string;
|
|
13
|
+
constructor(message: string, position: number, query: string);
|
|
14
|
+
/**
|
|
15
|
+
* Get a snippet of the query around the error position
|
|
16
|
+
*/
|
|
17
|
+
getSnippet(): string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Error thrown during parsing when the query syntax is invalid
|
|
21
|
+
*/
|
|
22
|
+
export declare class ParseError extends QastError {
|
|
23
|
+
readonly position: number;
|
|
24
|
+
readonly query: string;
|
|
25
|
+
constructor(message: string, position?: number, query?: string);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Error thrown during validation when a field or operator is not allowed
|
|
29
|
+
*/
|
|
30
|
+
export declare class ValidationError extends QastError {
|
|
31
|
+
readonly field?: string;
|
|
32
|
+
readonly operator?: string;
|
|
33
|
+
constructor(message: string, field?: string, operator?: string);
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,qBAAa,SAAU,SAAQ,KAAK;gBACtB,OAAO,EAAE,MAAM;CAK5B;AAED;;GAEG;AACH,qBAAa,iBAAkB,SAAQ,SAAS;IAC9C,SAAgB,QAAQ,EAAE,MAAM,CAAC;IACjC,SAAgB,KAAK,EAAE,MAAM,CAAC;gBAElB,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM;IAO5D;;OAEG;IACH,UAAU,IAAI,MAAM;CAOrB;AAED;;GAEG;AACH,qBAAa,UAAW,SAAQ,SAAS;IACvC,SAAgB,QAAQ,EAAE,MAAM,CAAC;IACjC,SAAgB,KAAK,EAAE,MAAM,CAAC;gBAElB,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM;CAM/D;AAED;;GAEG;AACH,qBAAa,eAAgB,SAAQ,SAAS;IAC5C,SAAgB,KAAK,CAAC,EAAE,MAAM,CAAC;IAC/B,SAAgB,QAAQ,CAAC,EAAE,MAAM,CAAC;gBAEtB,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM;CAM/D"}
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ValidationError = exports.ParseError = exports.TokenizationError = exports.QastError = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Base error class for QAST library
|
|
6
|
+
*/
|
|
7
|
+
class QastError extends Error {
|
|
8
|
+
constructor(message) {
|
|
9
|
+
super(message);
|
|
10
|
+
this.name = this.constructor.name;
|
|
11
|
+
Error.captureStackTrace(this, this.constructor);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
exports.QastError = QastError;
|
|
15
|
+
/**
|
|
16
|
+
* Error thrown during tokenization when an invalid token is encountered
|
|
17
|
+
*/
|
|
18
|
+
class TokenizationError extends QastError {
|
|
19
|
+
constructor(message, position, query) {
|
|
20
|
+
super(`${message} at position ${position}`);
|
|
21
|
+
this.position = position;
|
|
22
|
+
this.query = query;
|
|
23
|
+
this.name = 'TokenizationError';
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Get a snippet of the query around the error position
|
|
27
|
+
*/
|
|
28
|
+
getSnippet() {
|
|
29
|
+
const start = Math.max(0, this.position - 10);
|
|
30
|
+
const end = Math.min(this.query.length, this.position + 10);
|
|
31
|
+
const before = this.query.substring(start, this.position);
|
|
32
|
+
const after = this.query.substring(this.position, end);
|
|
33
|
+
return `${before}[ERROR]${after}`;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
exports.TokenizationError = TokenizationError;
|
|
37
|
+
/**
|
|
38
|
+
* Error thrown during parsing when the query syntax is invalid
|
|
39
|
+
*/
|
|
40
|
+
class ParseError extends QastError {
|
|
41
|
+
constructor(message, position, query) {
|
|
42
|
+
super(message);
|
|
43
|
+
this.position = position ?? -1;
|
|
44
|
+
this.query = query ?? '';
|
|
45
|
+
this.name = 'ParseError';
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
exports.ParseError = ParseError;
|
|
49
|
+
/**
|
|
50
|
+
* Error thrown during validation when a field or operator is not allowed
|
|
51
|
+
*/
|
|
52
|
+
class ValidationError extends QastError {
|
|
53
|
+
constructor(message, field, operator) {
|
|
54
|
+
super(message);
|
|
55
|
+
this.field = field;
|
|
56
|
+
this.operator = operator;
|
|
57
|
+
this.name = 'ValidationError';
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
exports.ValidationError = ValidationError;
|
|
61
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":";;;AAAA;;GAEG;AACH,MAAa,SAAU,SAAQ,KAAK;IAClC,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC;QAClC,KAAK,CAAC,iBAAiB,CAAC,IAAI,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;IAClD,CAAC;CACF;AAND,8BAMC;AAED;;GAEG;AACH,MAAa,iBAAkB,SAAQ,SAAS;IAI9C,YAAY,OAAe,EAAE,QAAgB,EAAE,KAAa;QAC1D,KAAK,CAAC,GAAG,OAAO,gBAAgB,QAAQ,EAAE,CAAC,CAAC;QAC5C,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,IAAI,GAAG,mBAAmB,CAAC;IAClC,CAAC;IAED;;OAEG;IACH,UAAU;QACR,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,QAAQ,GAAG,EAAE,CAAC,CAAC;QAC9C,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC,QAAQ,GAAG,EAAE,CAAC,CAAC;QAC5D,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC1D,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;QACvD,OAAO,GAAG,MAAM,UAAU,KAAK,EAAE,CAAC;IACpC,CAAC;CACF;AArBD,8CAqBC;AAED;;GAEG;AACH,MAAa,UAAW,SAAQ,SAAS;IAIvC,YAAY,OAAe,EAAE,QAAiB,EAAE,KAAc;QAC5D,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,QAAQ,GAAG,QAAQ,IAAI,CAAC,CAAC,CAAC;QAC/B,IAAI,CAAC,KAAK,GAAG,KAAK,IAAI,EAAE,CAAC;QACzB,IAAI,CAAC,IAAI,GAAG,YAAY,CAAC;IAC3B,CAAC;CACF;AAVD,gCAUC;AAED;;GAEG;AACH,MAAa,eAAgB,SAAQ,SAAS;IAI5C,YAAY,OAAe,EAAE,KAAc,EAAE,QAAiB;QAC5D,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,IAAI,GAAG,iBAAiB,CAAC;IAChC,CAAC;CACF;AAVD,0CAUC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* QAST - Query to AST to ORM
|
|
3
|
+
*
|
|
4
|
+
* A library that parses human-readable query strings into ASTs
|
|
5
|
+
* and transforms them into ORM-compatible filter objects.
|
|
6
|
+
*/
|
|
7
|
+
export { parseQueryString } from './parser/parser';
|
|
8
|
+
export { Tokenizer, Token, TokenType } from './parser/tokenizer';
|
|
9
|
+
export { validateQuery, extractFields, extractOperators } from './parser/validator';
|
|
10
|
+
export { toPrismaFilter, PrismaFilter } from './adapters/prisma';
|
|
11
|
+
export { toTypeORMFilter, TypeORMFilter } from './adapters/typeorm';
|
|
12
|
+
export { toSequelizeFilter, SequelizeFilter } from './adapters/sequelize';
|
|
13
|
+
export { QastNode, ComparisonNode, LogicalNode, LogicalOperator, Operator, QastValue, ParseOptions, WhitelistOptions, isComparisonNode, isLogicalNode, } from './types/ast';
|
|
14
|
+
export { QastError, ParseError, ValidationError, TokenizationError, } from './errors';
|
|
15
|
+
import { ParseOptions, QastNode } from './types/ast';
|
|
16
|
+
/**
|
|
17
|
+
* Parse a query string into an AST
|
|
18
|
+
*
|
|
19
|
+
* @param query - The query string to parse (e.g., 'age gt 25 and name eq "John"')
|
|
20
|
+
* @param options - Optional parsing options (whitelisting, validation)
|
|
21
|
+
* @returns The parsed AST node
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```ts
|
|
25
|
+
* const ast = parseQuery('age gt 25 and name eq "John"');
|
|
26
|
+
* ```
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```ts
|
|
30
|
+
* const ast = parseQuery('age gt 25', {
|
|
31
|
+
* allowedFields: ['age', 'name'],
|
|
32
|
+
* allowedOperators: ['gt', 'eq'],
|
|
33
|
+
* validate: true,
|
|
34
|
+
* });
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
export declare function parseQuery(query: string, options?: ParseOptions): QastNode;
|
|
38
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACnD,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAGjE,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAGpF,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjE,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACpE,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAG1E,OAAO,EACL,QAAQ,EACR,cAAc,EACd,WAAW,EACX,eAAe,EACf,QAAQ,EACR,SAAS,EACT,YAAY,EACZ,gBAAgB,EAChB,gBAAgB,EAChB,aAAa,GACd,MAAM,aAAa,CAAC;AAGrB,OAAO,EACL,SAAS,EACT,UAAU,EACV,eAAe,EACf,iBAAiB,GAClB,MAAM,UAAU,CAAC;AAKlB,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAoB,MAAM,aAAa,CAAC;AAEvE;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,QAAQ,CAiB1E"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* QAST - Query to AST to ORM
|
|
4
|
+
*
|
|
5
|
+
* A library that parses human-readable query strings into ASTs
|
|
6
|
+
* and transforms them into ORM-compatible filter objects.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.TokenizationError = exports.ValidationError = exports.ParseError = exports.QastError = exports.isLogicalNode = exports.isComparisonNode = exports.toSequelizeFilter = exports.toTypeORMFilter = exports.toPrismaFilter = exports.extractOperators = exports.extractFields = exports.validateQuery = exports.TokenType = exports.Tokenizer = exports.parseQueryString = void 0;
|
|
10
|
+
exports.parseQuery = parseQuery;
|
|
11
|
+
// Export parser
|
|
12
|
+
var parser_1 = require("./parser/parser");
|
|
13
|
+
Object.defineProperty(exports, "parseQueryString", { enumerable: true, get: function () { return parser_1.parseQueryString; } });
|
|
14
|
+
var tokenizer_1 = require("./parser/tokenizer");
|
|
15
|
+
Object.defineProperty(exports, "Tokenizer", { enumerable: true, get: function () { return tokenizer_1.Tokenizer; } });
|
|
16
|
+
Object.defineProperty(exports, "TokenType", { enumerable: true, get: function () { return tokenizer_1.TokenType; } });
|
|
17
|
+
// Export validators
|
|
18
|
+
var validator_1 = require("./parser/validator");
|
|
19
|
+
Object.defineProperty(exports, "validateQuery", { enumerable: true, get: function () { return validator_1.validateQuery; } });
|
|
20
|
+
Object.defineProperty(exports, "extractFields", { enumerable: true, get: function () { return validator_1.extractFields; } });
|
|
21
|
+
Object.defineProperty(exports, "extractOperators", { enumerable: true, get: function () { return validator_1.extractOperators; } });
|
|
22
|
+
// Export adapters
|
|
23
|
+
var prisma_1 = require("./adapters/prisma");
|
|
24
|
+
Object.defineProperty(exports, "toPrismaFilter", { enumerable: true, get: function () { return prisma_1.toPrismaFilter; } });
|
|
25
|
+
var typeorm_1 = require("./adapters/typeorm");
|
|
26
|
+
Object.defineProperty(exports, "toTypeORMFilter", { enumerable: true, get: function () { return typeorm_1.toTypeORMFilter; } });
|
|
27
|
+
var sequelize_1 = require("./adapters/sequelize");
|
|
28
|
+
Object.defineProperty(exports, "toSequelizeFilter", { enumerable: true, get: function () { return sequelize_1.toSequelizeFilter; } });
|
|
29
|
+
// Export types
|
|
30
|
+
var ast_1 = require("./types/ast");
|
|
31
|
+
Object.defineProperty(exports, "isComparisonNode", { enumerable: true, get: function () { return ast_1.isComparisonNode; } });
|
|
32
|
+
Object.defineProperty(exports, "isLogicalNode", { enumerable: true, get: function () { return ast_1.isLogicalNode; } });
|
|
33
|
+
// Export errors
|
|
34
|
+
var errors_1 = require("./errors");
|
|
35
|
+
Object.defineProperty(exports, "QastError", { enumerable: true, get: function () { return errors_1.QastError; } });
|
|
36
|
+
Object.defineProperty(exports, "ParseError", { enumerable: true, get: function () { return errors_1.ParseError; } });
|
|
37
|
+
Object.defineProperty(exports, "ValidationError", { enumerable: true, get: function () { return errors_1.ValidationError; } });
|
|
38
|
+
Object.defineProperty(exports, "TokenizationError", { enumerable: true, get: function () { return errors_1.TokenizationError; } });
|
|
39
|
+
// Main parse function with options
|
|
40
|
+
const parser_2 = require("./parser/parser");
|
|
41
|
+
const validator_2 = require("./parser/validator");
|
|
42
|
+
/**
|
|
43
|
+
* Parse a query string into an AST
|
|
44
|
+
*
|
|
45
|
+
* @param query - The query string to parse (e.g., 'age gt 25 and name eq "John"')
|
|
46
|
+
* @param options - Optional parsing options (whitelisting, validation)
|
|
47
|
+
* @returns The parsed AST node
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* ```ts
|
|
51
|
+
* const ast = parseQuery('age gt 25 and name eq "John"');
|
|
52
|
+
* ```
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* ```ts
|
|
56
|
+
* const ast = parseQuery('age gt 25', {
|
|
57
|
+
* allowedFields: ['age', 'name'],
|
|
58
|
+
* allowedOperators: ['gt', 'eq'],
|
|
59
|
+
* validate: true,
|
|
60
|
+
* });
|
|
61
|
+
* ```
|
|
62
|
+
*/
|
|
63
|
+
function parseQuery(query, options) {
|
|
64
|
+
const ast = (0, parser_2.parseQueryString)(query);
|
|
65
|
+
// Validate if options are provided and validation is enabled
|
|
66
|
+
if (options && options.validate !== false) {
|
|
67
|
+
const whitelist = {
|
|
68
|
+
allowedFields: options.allowedFields,
|
|
69
|
+
allowedOperators: options.allowedOperators,
|
|
70
|
+
};
|
|
71
|
+
// Only validate if whitelists are provided
|
|
72
|
+
if (whitelist.allowedFields || whitelist.allowedOperators) {
|
|
73
|
+
(0, validator_2.validateQuery)(ast, whitelist);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return ast;
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;;AA8DH,gCAiBC;AA7ED,gBAAgB;AAChB,0CAAmD;AAA1C,0GAAA,gBAAgB,OAAA;AACzB,gDAAiE;AAAxD,sGAAA,SAAS,OAAA;AAAS,sGAAA,SAAS,OAAA;AAEpC,oBAAoB;AACpB,gDAAoF;AAA3E,0GAAA,aAAa,OAAA;AAAE,0GAAA,aAAa,OAAA;AAAE,6GAAA,gBAAgB,OAAA;AAEvD,kBAAkB;AAClB,4CAAiE;AAAxD,wGAAA,cAAc,OAAA;AACvB,8CAAoE;AAA3D,0GAAA,eAAe,OAAA;AACxB,kDAA0E;AAAjE,8GAAA,iBAAiB,OAAA;AAE1B,eAAe;AACf,mCAWqB;AAFnB,uGAAA,gBAAgB,OAAA;AAChB,oGAAA,aAAa,OAAA;AAGf,gBAAgB;AAChB,mCAKkB;AAJhB,mGAAA,SAAS,OAAA;AACT,oGAAA,UAAU,OAAA;AACV,yGAAA,eAAe,OAAA;AACf,2GAAA,iBAAiB,OAAA;AAGnB,mCAAmC;AACnC,4CAAmD;AACnD,kDAAmD;AAGnD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,SAAgB,UAAU,CAAC,KAAa,EAAE,OAAsB;IAC9D,MAAM,GAAG,GAAG,IAAA,yBAAgB,EAAC,KAAK,CAAC,CAAC;IAEpC,6DAA6D;IAC7D,IAAI,OAAO,IAAI,OAAO,CAAC,QAAQ,KAAK,KAAK,EAAE,CAAC;QAC1C,MAAM,SAAS,GAAqB;YAClC,aAAa,EAAE,OAAO,CAAC,aAAa;YACpC,gBAAgB,EAAE,OAAO,CAAC,gBAAgB;SAC3C,CAAC;QAEF,2CAA2C;QAC3C,IAAI,SAAS,CAAC,aAAa,IAAI,SAAS,CAAC,gBAAgB,EAAE,CAAC;YAC1D,IAAA,yBAAa,EAAC,GAAG,EAAE,SAAS,CAAC,CAAC;QAChC,CAAC;IACH,CAAC;IAED,OAAO,GAAG,CAAC;AACb,CAAC"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { Token } from './tokenizer';
|
|
2
|
+
import { QastNode } from '../types/ast';
|
|
3
|
+
/**
|
|
4
|
+
* Parser for QAST query strings
|
|
5
|
+
* Grammar:
|
|
6
|
+
* expression -> term (("and" | "or") term)*
|
|
7
|
+
* term -> factor | "(" expression ")"
|
|
8
|
+
* factor -> IDENTIFIER OPERATOR VALUE
|
|
9
|
+
*/
|
|
10
|
+
export declare class Parser {
|
|
11
|
+
private tokens;
|
|
12
|
+
private position;
|
|
13
|
+
private currentToken;
|
|
14
|
+
constructor(tokens: Token[]);
|
|
15
|
+
/**
|
|
16
|
+
* Advance to the next token
|
|
17
|
+
*/
|
|
18
|
+
private advance;
|
|
19
|
+
/**
|
|
20
|
+
* Expect a token of a specific type, throw error if not found
|
|
21
|
+
*/
|
|
22
|
+
private expect;
|
|
23
|
+
/**
|
|
24
|
+
* Check if current token matches a type
|
|
25
|
+
*/
|
|
26
|
+
private check;
|
|
27
|
+
/**
|
|
28
|
+
* Parse a factor: IDENTIFIER OPERATOR VALUE
|
|
29
|
+
*/
|
|
30
|
+
private parseFactor;
|
|
31
|
+
/**
|
|
32
|
+
* Parse a term: factor | "(" expression ")"
|
|
33
|
+
*/
|
|
34
|
+
private parseTerm;
|
|
35
|
+
/**
|
|
36
|
+
* Parse an expression: term (("and" | "or") term)*
|
|
37
|
+
* Left-associative: a and b or c is parsed as (a and b) or c
|
|
38
|
+
*/
|
|
39
|
+
private parseExpression;
|
|
40
|
+
/**
|
|
41
|
+
* Parse the query and return the AST
|
|
42
|
+
*/
|
|
43
|
+
parse(): QastNode;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Parse a query string into an AST
|
|
47
|
+
*/
|
|
48
|
+
export declare function parseQueryString(query: string): QastNode;
|
|
49
|
+
//# sourceMappingURL=parser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../src/parser/parser.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,KAAK,EAAa,MAAM,aAAa,CAAC;AAC1D,OAAO,EAAE,QAAQ,EAA4B,MAAM,cAAc,CAAC;AAGlE;;;;;;GAMG;AACH,qBAAa,MAAM;IACjB,OAAO,CAAC,MAAM,CAAU;IACxB,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,YAAY,CAAQ;gBAEhB,MAAM,EAAE,KAAK,EAAE;IAM3B;;OAEG;IACH,OAAO,CAAC,OAAO;IASf;;OAEG;IACH,OAAO,CAAC,MAAM;IAYd;;OAEG;IACH,OAAO,CAAC,KAAK;IAIb;;OAEG;IACH,OAAO,CAAC,WAAW;IAoCnB;;OAEG;IACH,OAAO,CAAC,SAAS;IAajB;;;OAGG;IACH,OAAO,CAAC,eAAe;IAyBvB;;OAEG;IACI,KAAK,IAAI,QAAQ;CAmBzB;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,QAAQ,CAYxD"}
|