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
@@ -0,0 +1,94 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.validateQuery = validateQuery;
4
+ exports.extractFields = extractFields;
5
+ exports.extractOperators = extractOperators;
6
+ const errors_1 = require("../errors");
7
+ const ast_1 = require("../types/ast");
8
+ /**
9
+ * Validate an AST against whitelist options
10
+ */
11
+ function validateQuery(ast, whitelist) {
12
+ validateNode(ast, whitelist);
13
+ }
14
+ /**
15
+ * Recursively validate a node
16
+ */
17
+ function validateNode(node, whitelist) {
18
+ if ((0, ast_1.isComparisonNode)(node)) {
19
+ validateComparisonNode(node, whitelist);
20
+ }
21
+ else if ((0, ast_1.isLogicalNode)(node)) {
22
+ validateLogicalNode(node, whitelist);
23
+ }
24
+ }
25
+ /**
26
+ * Validate a comparison node
27
+ */
28
+ function validateComparisonNode(node, whitelist) {
29
+ // Validate field
30
+ if (whitelist.allowedFields && whitelist.allowedFields.length > 0) {
31
+ if (!whitelist.allowedFields.includes(node.field)) {
32
+ throw new errors_1.ValidationError(`Field '${node.field}' is not allowed. Allowed fields: ${whitelist.allowedFields.join(', ')}`, node.field);
33
+ }
34
+ }
35
+ // Validate operator
36
+ if (whitelist.allowedOperators && whitelist.allowedOperators.length > 0) {
37
+ if (!whitelist.allowedOperators.includes(node.op)) {
38
+ throw new errors_1.ValidationError(`Operator '${node.op}' is not allowed. Allowed operators: ${whitelist.allowedOperators.join(', ')}`, node.field, node.op);
39
+ }
40
+ }
41
+ // Validate operator-value combination
42
+ if (node.op === 'in' && !Array.isArray(node.value)) {
43
+ throw new errors_1.ValidationError(`Operator 'in' requires an array value, got ${typeof node.value}`, node.field, node.op);
44
+ }
45
+ }
46
+ /**
47
+ * Validate a logical node
48
+ */
49
+ function validateLogicalNode(node, whitelist) {
50
+ // Recursively validate left and right children
51
+ validateNode(node.left, whitelist);
52
+ validateNode(node.right, whitelist);
53
+ }
54
+ /**
55
+ * Extract all fields used in an AST
56
+ */
57
+ function extractFields(ast) {
58
+ const fields = [];
59
+ extractFieldsRecursive(ast, fields);
60
+ return [...new Set(fields)]; // Return unique fields
61
+ }
62
+ /**
63
+ * Recursively extract fields from a node
64
+ */
65
+ function extractFieldsRecursive(node, fields) {
66
+ if ((0, ast_1.isComparisonNode)(node)) {
67
+ fields.push(node.field);
68
+ }
69
+ else if ((0, ast_1.isLogicalNode)(node)) {
70
+ extractFieldsRecursive(node.left, fields);
71
+ extractFieldsRecursive(node.right, fields);
72
+ }
73
+ }
74
+ /**
75
+ * Extract all operators used in an AST
76
+ */
77
+ function extractOperators(ast) {
78
+ const operators = [];
79
+ extractOperatorsRecursive(ast, operators);
80
+ return [...new Set(operators)]; // Return unique operators
81
+ }
82
+ /**
83
+ * Recursively extract operators from a node
84
+ */
85
+ function extractOperatorsRecursive(node, operators) {
86
+ if ((0, ast_1.isComparisonNode)(node)) {
87
+ operators.push(node.op);
88
+ }
89
+ else if ((0, ast_1.isLogicalNode)(node)) {
90
+ extractOperatorsRecursive(node.left, operators);
91
+ extractOperatorsRecursive(node.right, operators);
92
+ }
93
+ }
94
+ //# sourceMappingURL=validator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validator.js","sourceRoot":"","sources":["../../src/parser/validator.ts"],"names":[],"mappings":";;AAOA,sCAEC;AA4DD,sCAIC;AAiBD,4CAIC;AA7FD,sCAA4C;AAC5C,sCAA+D;AAE/D;;GAEG;AACH,SAAgB,aAAa,CAAC,GAAa,EAAE,SAA2B;IACtE,YAAY,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;AAC/B,CAAC;AAED;;GAEG;AACH,SAAS,YAAY,CAAC,IAAc,EAAE,SAA2B;IAC/D,IAAI,IAAA,sBAAgB,EAAC,IAAI,CAAC,EAAE,CAAC;QAC3B,sBAAsB,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;IAC1C,CAAC;SAAM,IAAI,IAAA,mBAAa,EAAC,IAAI,CAAC,EAAE,CAAC;QAC/B,mBAAmB,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;IACvC,CAAC;AACH,CAAC;AAED;;GAEG;AACH,SAAS,sBAAsB,CAAC,IAAoB,EAAE,SAA2B;IAC/E,iBAAiB;IACjB,IAAI,SAAS,CAAC,aAAa,IAAI,SAAS,CAAC,aAAa,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAClE,IAAI,CAAC,SAAS,CAAC,aAAa,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;YAClD,MAAM,IAAI,wBAAe,CACvB,UAAU,IAAI,CAAC,KAAK,qCAAqC,SAAS,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EAC7F,IAAI,CAAC,KAAK,CACX,CAAC;QACJ,CAAC;IACH,CAAC;IAED,oBAAoB;IACpB,IAAI,SAAS,CAAC,gBAAgB,IAAI,SAAS,CAAC,gBAAgB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACxE,IAAI,CAAC,SAAS,CAAC,gBAAgB,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC;YAClD,MAAM,IAAI,wBAAe,CACvB,aAAa,IAAI,CAAC,EAAE,wCAAwC,SAAS,CAAC,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EACnG,IAAI,CAAC,KAAK,EACV,IAAI,CAAC,EAAE,CACR,CAAC;QACJ,CAAC;IACH,CAAC;IAED,sCAAsC;IACtC,IAAI,IAAI,CAAC,EAAE,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QACnD,MAAM,IAAI,wBAAe,CACvB,8CAA8C,OAAO,IAAI,CAAC,KAAK,EAAE,EACjE,IAAI,CAAC,KAAK,EACV,IAAI,CAAC,EAAE,CACR,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;GAEG;AACH,SAAS,mBAAmB,CAAC,IAAiB,EAAE,SAA2B;IACzE,+CAA+C;IAC/C,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;IACnC,YAAY,CAAC,IAAI,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;AACtC,CAAC;AAED;;GAEG;AACH,SAAgB,aAAa,CAAC,GAAa;IACzC,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,sBAAsB,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;IACpC,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,uBAAuB;AACtD,CAAC;AAED;;GAEG;AACH,SAAS,sBAAsB,CAAC,IAAc,EAAE,MAAgB;IAC9D,IAAI,IAAA,sBAAgB,EAAC,IAAI,CAAC,EAAE,CAAC;QAC3B,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC1B,CAAC;SAAM,IAAI,IAAA,mBAAa,EAAC,IAAI,CAAC,EAAE,CAAC;QAC/B,sBAAsB,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QAC1C,sBAAsB,CAAC,IAAI,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;IAC7C,CAAC;AACH,CAAC;AAED;;GAEG;AACH,SAAgB,gBAAgB,CAAC,GAAa;IAC5C,MAAM,SAAS,GAAe,EAAE,CAAC;IACjC,yBAAyB,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;IAC1C,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,0BAA0B;AAC5D,CAAC;AAED;;GAEG;AACH,SAAS,yBAAyB,CAAC,IAAc,EAAE,SAAqB;IACtE,IAAI,IAAA,sBAAgB,EAAC,IAAI,CAAC,EAAE,CAAC;QAC3B,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC1B,CAAC;SAAM,IAAI,IAAA,mBAAa,EAAC,IAAI,CAAC,EAAE,CAAC;QAC/B,yBAAyB,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;QAChD,yBAAyB,CAAC,IAAI,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;IACnD,CAAC;AACH,CAAC"}
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Supported comparison operators
3
+ */
4
+ export type Operator = 'eq' | 'ne' | 'gt' | 'lt' | 'gte' | 'lte' | 'in' | 'contains';
5
+ /**
6
+ * Supported value types in comparisons
7
+ */
8
+ export type QastValue = string | number | boolean | string[] | number[];
9
+ /**
10
+ * Logical operator type
11
+ */
12
+ export type LogicalOperator = 'AND' | 'OR';
13
+ /**
14
+ * Comparison node representing a field comparison operation
15
+ */
16
+ export interface ComparisonNode {
17
+ type: 'COMPARISON';
18
+ field: string;
19
+ op: Operator;
20
+ value: QastValue;
21
+ }
22
+ /**
23
+ * Logical node representing a logical operation (AND/OR) between two nodes
24
+ */
25
+ export interface LogicalNode {
26
+ type: LogicalOperator;
27
+ left: QastNode;
28
+ right: QastNode;
29
+ }
30
+ /**
31
+ * Root AST node type - can be either a comparison or a logical operation
32
+ */
33
+ export type QastNode = LogicalNode | ComparisonNode;
34
+ /**
35
+ * Type guard to check if a node is a ComparisonNode
36
+ */
37
+ export declare function isComparisonNode(node: QastNode): node is ComparisonNode;
38
+ /**
39
+ * Type guard to check if a node is a LogicalNode
40
+ */
41
+ export declare function isLogicalNode(node: QastNode): node is LogicalNode;
42
+ /**
43
+ * Options for parsing queries
44
+ */
45
+ export interface ParseOptions {
46
+ /**
47
+ * List of allowed field names. If provided, only these fields can be used in queries.
48
+ */
49
+ allowedFields?: string[];
50
+ /**
51
+ * List of allowed operators. If provided, only these operators can be used in queries.
52
+ */
53
+ allowedOperators?: Operator[];
54
+ /**
55
+ * Whether to validate the query against whitelists after parsing
56
+ */
57
+ validate?: boolean;
58
+ }
59
+ /**
60
+ * Options for query validation
61
+ */
62
+ export interface WhitelistOptions {
63
+ /**
64
+ * List of allowed field names
65
+ */
66
+ allowedFields?: string[];
67
+ /**
68
+ * List of allowed operators
69
+ */
70
+ allowedOperators?: Operator[];
71
+ }
72
+ //# sourceMappingURL=ast.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ast.d.ts","sourceRoot":"","sources":["../../src/types/ast.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,MAAM,QAAQ,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,KAAK,GAAG,KAAK,GAAG,IAAI,GAAG,UAAU,CAAC;AAErF;;GAEG;AACH,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,EAAE,GAAG,MAAM,EAAE,CAAC;AAExE;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG,KAAK,GAAG,IAAI,CAAC;AAE3C;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,YAAY,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,EAAE,EAAE,QAAQ,CAAC;IACb,KAAK,EAAE,SAAS,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,eAAe,CAAC;IACtB,IAAI,EAAE,QAAQ,CAAC;IACf,KAAK,EAAE,QAAQ,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,MAAM,QAAQ,GAAG,WAAW,GAAG,cAAc,CAAC;AAEpD;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,QAAQ,GAAG,IAAI,IAAI,cAAc,CAEvE;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,QAAQ,GAAG,IAAI,IAAI,WAAW,CAEjE;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B;;OAEG;IACH,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IAEzB;;OAEG;IACH,gBAAgB,CAAC,EAAE,QAAQ,EAAE,CAAC;IAE9B;;OAEG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B;;OAEG;IACH,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IAEzB;;OAEG;IACH,gBAAgB,CAAC,EAAE,QAAQ,EAAE,CAAC;CAC/B"}
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isComparisonNode = isComparisonNode;
4
+ exports.isLogicalNode = isLogicalNode;
5
+ /**
6
+ * Type guard to check if a node is a ComparisonNode
7
+ */
8
+ function isComparisonNode(node) {
9
+ return node.type === 'COMPARISON';
10
+ }
11
+ /**
12
+ * Type guard to check if a node is a LogicalNode
13
+ */
14
+ function isLogicalNode(node) {
15
+ return node.type === 'AND' || node.type === 'OR';
16
+ }
17
+ //# sourceMappingURL=ast.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ast.js","sourceRoot":"","sources":["../../src/types/ast.ts"],"names":[],"mappings":";;AA0CA,4CAEC;AAKD,sCAEC;AAZD;;GAEG;AACH,SAAgB,gBAAgB,CAAC,IAAc;IAC7C,OAAO,IAAI,CAAC,IAAI,KAAK,YAAY,CAAC;AACpC,CAAC;AAED;;GAEG;AACH,SAAgB,aAAa,CAAC,IAAc;IAC1C,OAAO,IAAI,CAAC,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC;AACnD,CAAC"}
package/package.json ADDED
@@ -0,0 +1,72 @@
1
+ {
2
+ "name": "qast",
3
+ "version": "1.0.0",
4
+ "description": "Query to AST to ORM - Parse human-readable query strings into AST and transform them into ORM-compatible filters",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "files": [
8
+ "dist",
9
+ "README.md",
10
+ "LICENSE"
11
+ ],
12
+ "scripts": {
13
+ "build": "tsc",
14
+ "test": "jest",
15
+ "test:watch": "jest --watch",
16
+ "test:coverage": "jest --coverage",
17
+ "prepare": "npm run build",
18
+ "prepublishOnly": "npm run build && npm test"
19
+ },
20
+ "keywords": [
21
+ "query",
22
+ "parser",
23
+ "ast",
24
+ "orm",
25
+ "prisma",
26
+ "typeorm",
27
+ "sequelize",
28
+ "filter",
29
+ "typescript",
30
+ "query-string",
31
+ "filtering",
32
+ "api"
33
+ ],
34
+ "author": "https://x.com/hocestnonsatis",
35
+ "license": "MIT",
36
+ "repository": {
37
+ "type": "git",
38
+ "url": "https://github.com/hocestnonsatis/qast.git"
39
+ },
40
+ "bugs": {
41
+ "url": "https://github.com/hocestnonsatis/qast/issues"
42
+ },
43
+ "homepage": "https://github.com/hocestnonsatis/qast#readme",
44
+ "engines": {
45
+ "node": ">=14.0.0"
46
+ },
47
+ "devDependencies": {
48
+ "@types/jest": "^29.5.0",
49
+ "@types/node": "^20.0.0",
50
+ "jest": "^29.5.0",
51
+ "ts-jest": "^29.1.0",
52
+ "typescript": "^5.0.0"
53
+ },
54
+ "dependencies": {},
55
+ "peerDependencies": {
56
+ "@prisma/client": "*",
57
+ "typeorm": "*",
58
+ "sequelize": "*"
59
+ },
60
+ "peerDependenciesMeta": {
61
+ "@prisma/client": {
62
+ "optional": true
63
+ },
64
+ "typeorm": {
65
+ "optional": true
66
+ },
67
+ "sequelize": {
68
+ "optional": true
69
+ }
70
+ }
71
+ }
72
+