@gblikas/querykit 0.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 (118) hide show
  1. package/.cursor/BUGBOT.md +21 -0
  2. package/.cursor/rules/01-project-structure.mdc +77 -0
  3. package/.cursor/rules/02-typescript-standards.mdc +105 -0
  4. package/.cursor/rules/03-testing-standards.mdc +78 -0
  5. package/.cursor/rules/04-query-language.mdc +79 -0
  6. package/.cursor/rules/05-solid-principles.mdc +118 -0
  7. package/.cursor/rules/liqe-readme-docs.mdc +438 -0
  8. package/.devcontainer/devcontainer.json +25 -0
  9. package/.eslintignore +1 -0
  10. package/.eslintrc.js +39 -0
  11. package/.github/dependabot.yml +12 -0
  12. package/.github/workflows/ci.yml +114 -0
  13. package/.github/workflows/publish.yml +61 -0
  14. package/.husky/pre-commit +30 -0
  15. package/.prettierrc +10 -0
  16. package/CONTRIBUTING.md +187 -0
  17. package/LICENSE +674 -0
  18. package/README.md +237 -0
  19. package/dist/adapters/drizzle/index.d.ts +122 -0
  20. package/dist/adapters/drizzle/index.js +166 -0
  21. package/dist/adapters/index.d.ts +7 -0
  22. package/dist/adapters/index.js +25 -0
  23. package/dist/adapters/types.d.ts +60 -0
  24. package/dist/adapters/types.js +8 -0
  25. package/dist/index.d.ts +75 -0
  26. package/dist/index.js +118 -0
  27. package/dist/parser/index.d.ts +2 -0
  28. package/dist/parser/index.js +18 -0
  29. package/dist/parser/parser.d.ts +51 -0
  30. package/dist/parser/parser.js +201 -0
  31. package/dist/parser/types.d.ts +68 -0
  32. package/dist/parser/types.js +5 -0
  33. package/dist/query/builder.d.ts +61 -0
  34. package/dist/query/builder.js +188 -0
  35. package/dist/query/index.d.ts +2 -0
  36. package/dist/query/index.js +18 -0
  37. package/dist/query/types.d.ts +79 -0
  38. package/dist/query/types.js +2 -0
  39. package/dist/security/index.d.ts +2 -0
  40. package/dist/security/index.js +18 -0
  41. package/dist/security/types.d.ts +181 -0
  42. package/dist/security/types.js +43 -0
  43. package/dist/security/validator.d.ts +191 -0
  44. package/dist/security/validator.js +344 -0
  45. package/dist/translators/drizzle/index.d.ts +73 -0
  46. package/dist/translators/drizzle/index.js +260 -0
  47. package/dist/translators/index.d.ts +8 -0
  48. package/dist/translators/index.js +27 -0
  49. package/dist/translators/sql/index.d.ts +108 -0
  50. package/dist/translators/sql/index.js +252 -0
  51. package/dist/translators/types.d.ts +39 -0
  52. package/dist/translators/types.js +8 -0
  53. package/examples/qk-next/README.md +35 -0
  54. package/examples/qk-next/app/favicon.ico +0 -0
  55. package/examples/qk-next/app/globals.css +122 -0
  56. package/examples/qk-next/app/layout.tsx +121 -0
  57. package/examples/qk-next/app/page.tsx +813 -0
  58. package/examples/qk-next/app/providers.tsx +80 -0
  59. package/examples/qk-next/components/aurora-background.tsx +12 -0
  60. package/examples/qk-next/components/github-stars.tsx +51 -0
  61. package/examples/qk-next/components/mode-toggle.tsx +27 -0
  62. package/examples/qk-next/components/reactbits/blocks/Backgrounds/Aurora/Aurora.tsx +217 -0
  63. package/examples/qk-next/components/reactbits/blocks/Backgrounds/LightRays/LightRays.tsx +474 -0
  64. package/examples/qk-next/components/theme-provider.tsx +11 -0
  65. package/examples/qk-next/components/ui/card.tsx +92 -0
  66. package/examples/qk-next/components/ui/command.tsx +184 -0
  67. package/examples/qk-next/components/ui/dialog.tsx +143 -0
  68. package/examples/qk-next/components/ui/drawer.tsx +135 -0
  69. package/examples/qk-next/components/ui/hover-card.tsx +44 -0
  70. package/examples/qk-next/components/ui/icons.tsx +148 -0
  71. package/examples/qk-next/components/ui/sonner.tsx +26 -0
  72. package/examples/qk-next/components/ui/table.tsx +117 -0
  73. package/examples/qk-next/components.json +21 -0
  74. package/examples/qk-next/eslint.config.mjs +21 -0
  75. package/examples/qk-next/jsrepo.json +13 -0
  76. package/examples/qk-next/lib/utils.ts +6 -0
  77. package/examples/qk-next/next.config.ts +8 -0
  78. package/examples/qk-next/package.json +48 -0
  79. package/examples/qk-next/pnpm-lock.yaml +5558 -0
  80. package/examples/qk-next/postcss.config.mjs +5 -0
  81. package/examples/qk-next/public/file.svg +1 -0
  82. package/examples/qk-next/public/globe.svg +1 -0
  83. package/examples/qk-next/public/next.svg +1 -0
  84. package/examples/qk-next/public/vercel.svg +1 -0
  85. package/examples/qk-next/public/window.svg +1 -0
  86. package/examples/qk-next/tsconfig.json +42 -0
  87. package/examples/qk-next/types/sonner.d.ts +3 -0
  88. package/jest.config.js +26 -0
  89. package/package.json +51 -0
  90. package/src/adapters/drizzle/drizzle-adapter.test.ts +115 -0
  91. package/src/adapters/drizzle/index.ts +299 -0
  92. package/src/adapters/index.ts +11 -0
  93. package/src/adapters/types.ts +72 -0
  94. package/src/index.ts +194 -0
  95. package/src/integration.test.ts +202 -0
  96. package/src/parser/index.ts +2 -0
  97. package/src/parser/parser.test.ts +1056 -0
  98. package/src/parser/parser.ts +268 -0
  99. package/src/parser/types.ts +97 -0
  100. package/src/query/builder.test.ts +272 -0
  101. package/src/query/builder.ts +274 -0
  102. package/src/query/index.ts +2 -0
  103. package/src/query/types.ts +107 -0
  104. package/src/security/index.ts +2 -0
  105. package/src/security/types.ts +210 -0
  106. package/src/security/validator.test.ts +459 -0
  107. package/src/security/validator.ts +395 -0
  108. package/src/security.test.ts +366 -0
  109. package/src/translators/drizzle/drizzle-translator.test.ts +128 -0
  110. package/src/translators/drizzle/index.test.ts +45 -0
  111. package/src/translators/drizzle/index.ts +346 -0
  112. package/src/translators/index.ts +14 -0
  113. package/src/translators/sql/index.test.ts +45 -0
  114. package/src/translators/sql/index.ts +331 -0
  115. package/src/translators/sql/sql-translator.test.ts +419 -0
  116. package/src/translators/types.ts +44 -0
  117. package/src/types/sonner.d.ts +3 -0
  118. package/tsconfig.json +34 -0
@@ -0,0 +1,75 @@
1
+ /**
2
+ * QueryKit - A comprehensive query toolkit for TypeScript
3
+ *
4
+ * QueryKit simplifies how you build and execute data queries across different
5
+ * environments with a unified, intuitive syntax for filtering, sorting, and
6
+ * transforming data.
7
+ */
8
+ import { QueryBuilder, IQueryBuilderOptions } from './query';
9
+ import { QueryParser, IParserOptions } from './parser';
10
+ import { SqlTranslator } from './translators/sql';
11
+ import { ISecurityOptions } from './security';
12
+ import { IAdapter, IAdapterOptions } from './adapters';
13
+ export { QueryParser, IParserOptions, QueryBuilder, IQueryBuilderOptions, SqlTranslator };
14
+ export * from './translators';
15
+ export * from './adapters';
16
+ /**
17
+ * Create a new QueryBuilder instance
18
+ */
19
+ export declare function createQueryBuilder<T>(options?: IQueryBuilderOptions<T>): QueryBuilder<T>;
20
+ /**
21
+ * Create a new QueryParser instance
22
+ */
23
+ export declare function createQueryParser(options?: IParserOptions): QueryParser;
24
+ /**
25
+ * Options for creating a new QueryKit instance
26
+ */
27
+ export interface IQueryKitOptions<TSchema extends Record<string, object> = Record<string, Record<string, unknown>>> {
28
+ /**
29
+ * The adapter to use for database connections
30
+ */
31
+ adapter: IAdapter;
32
+ /**
33
+ * The schema to use for query validation
34
+ */
35
+ schema: TSchema;
36
+ /**
37
+ * Security options for query validation
38
+ */
39
+ security?: ISecurityOptions;
40
+ /**
41
+ * Options to initialize the provided adapter
42
+ */
43
+ adapterOptions?: IAdapterOptions & {
44
+ [key: string]: unknown;
45
+ };
46
+ }
47
+ export interface IQueryExecutor<TResult> {
48
+ execute(): Promise<TResult[]>;
49
+ orderBy(field: string, direction?: 'asc' | 'desc'): IQueryExecutor<TResult>;
50
+ limit(count: number): IQueryExecutor<TResult>;
51
+ offset(count: number): IQueryExecutor<TResult>;
52
+ }
53
+ export interface IWhereClause<TResult> {
54
+ where(queryString: string): IQueryExecutor<TResult>;
55
+ }
56
+ /**
57
+ * Public QueryKit type
58
+ */
59
+ export type QueryKit<TSchema extends Record<string, object>, TRows extends {
60
+ [K in keyof TSchema & string]: unknown;
61
+ } = {
62
+ [K in keyof TSchema & string]: unknown;
63
+ }> = {
64
+ query<K extends keyof TSchema & string>(table: K): IWhereClause<TRows[K]>;
65
+ };
66
+ /**
67
+ * Create a new QueryKit instance
68
+ */
69
+ export declare function createQueryKit<TSchema extends Record<string, object>, TRows extends {
70
+ [K in keyof TSchema & string]: unknown;
71
+ } = {
72
+ [K in keyof TSchema & string]: unknown;
73
+ }>(options: IQueryKitOptions<TSchema>): QueryKit<TSchema, TRows>;
74
+ export * from './parser';
75
+ export * from './security';
package/dist/index.js ADDED
@@ -0,0 +1,118 @@
1
+ "use strict";
2
+ /**
3
+ * QueryKit - A comprehensive query toolkit for TypeScript
4
+ *
5
+ * QueryKit simplifies how you build and execute data queries across different
6
+ * environments with a unified, intuitive syntax for filtering, sorting, and
7
+ * transforming data.
8
+ */
9
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ var desc = Object.getOwnPropertyDescriptor(m, k);
12
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
13
+ desc = { enumerable: true, get: function() { return m[k]; } };
14
+ }
15
+ Object.defineProperty(o, k2, desc);
16
+ }) : (function(o, m, k, k2) {
17
+ if (k2 === undefined) k2 = k;
18
+ o[k2] = m[k];
19
+ }));
20
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
21
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
22
+ };
23
+ Object.defineProperty(exports, "__esModule", { value: true });
24
+ exports.SqlTranslator = exports.QueryBuilder = exports.QueryParser = void 0;
25
+ exports.createQueryBuilder = createQueryBuilder;
26
+ exports.createQueryParser = createQueryParser;
27
+ exports.createQueryKit = createQueryKit;
28
+ // Core exports
29
+ const query_1 = require("./query");
30
+ Object.defineProperty(exports, "QueryBuilder", { enumerable: true, get: function () { return query_1.QueryBuilder; } });
31
+ const parser_1 = require("./parser");
32
+ Object.defineProperty(exports, "QueryParser", { enumerable: true, get: function () { return parser_1.QueryParser; } });
33
+ const sql_1 = require("./translators/sql");
34
+ Object.defineProperty(exports, "SqlTranslator", { enumerable: true, get: function () { return sql_1.SqlTranslator; } });
35
+ const security_1 = require("./security");
36
+ // Re-export from modules
37
+ __exportStar(require("./translators"), exports);
38
+ __exportStar(require("./adapters"), exports);
39
+ /**
40
+ * Create a new QueryBuilder instance
41
+ */
42
+ function createQueryBuilder(options) {
43
+ return new query_1.QueryBuilder(options);
44
+ }
45
+ /**
46
+ * Create a new QueryParser instance
47
+ */
48
+ function createQueryParser(options) {
49
+ return new parser_1.QueryParser(options);
50
+ }
51
+ /**
52
+ * Create a new QueryKit instance
53
+ */
54
+ function createQueryKit(options) {
55
+ const parser = new parser_1.QueryParser();
56
+ const securityValidator = new security_1.QuerySecurityValidator(options.security);
57
+ // Initialize adapter if options provided. If adapter is already initialized,
58
+ // calling initialize again with the same options should be a no-op for most adapters.
59
+ if (options.adapterOptions) {
60
+ const mergedAdapterOptions = {
61
+ // Ensure adapter receives schema information if not already provided
62
+ schema: options.adapterOptions.schema ?? options.schema,
63
+ ...options.adapterOptions
64
+ };
65
+ try {
66
+ options.adapter.initialize(mergedAdapterOptions);
67
+ }
68
+ catch {
69
+ // If initialization fails here, the adapter might already be initialized
70
+ // or require a different init path; we'll let execute-time errors surface.
71
+ }
72
+ }
73
+ // This function would be expanded to include all QueryKit functionality
74
+ return {
75
+ query: (table) => {
76
+ return {
77
+ where: (queryString) => {
78
+ // Parse and validate the query
79
+ const expressionAst = parser.parse(queryString);
80
+ securityValidator.validate(expressionAst, options.schema);
81
+ // Execution state accumulated via fluent calls
82
+ let orderByState = {};
83
+ let limitState;
84
+ let offsetState;
85
+ const executor = {
86
+ orderBy: (field, direction = 'asc') => {
87
+ orderByState = { ...orderByState, [field]: direction };
88
+ return executor;
89
+ },
90
+ limit: (count) => {
91
+ limitState = count;
92
+ return executor;
93
+ },
94
+ offset: (count) => {
95
+ offsetState = count;
96
+ return executor;
97
+ },
98
+ execute: async () => {
99
+ // Delegate to adapter
100
+ const results = await options.adapter.execute(table, expressionAst, {
101
+ orderBy: Object.keys(orderByState).length > 0
102
+ ? orderByState
103
+ : undefined,
104
+ limit: limitState,
105
+ offset: offsetState
106
+ });
107
+ return results;
108
+ }
109
+ };
110
+ return executor;
111
+ }
112
+ };
113
+ }
114
+ };
115
+ }
116
+ // Export all public APIs
117
+ __exportStar(require("./parser"), exports);
118
+ __exportStar(require("./security"), exports);
@@ -0,0 +1,2 @@
1
+ export * from './types';
2
+ export * from './parser';
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./types"), exports);
18
+ __exportStar(require("./parser"), exports);
@@ -0,0 +1,51 @@
1
+ import { IParserOptions, IQueryParser, QueryExpression } from './types';
2
+ /**
3
+ * Error thrown when query parsing fails
4
+ */
5
+ export declare class QueryParseError extends Error {
6
+ constructor(message: string);
7
+ }
8
+ /**
9
+ * Implementation of the QueryKit parser using Liqe
10
+ */
11
+ export declare class QueryParser implements IQueryParser {
12
+ private options;
13
+ constructor(options?: IParserOptions);
14
+ /**
15
+ * Parse a query string into a QueryKit AST
16
+ */
17
+ parse(query: string): QueryExpression;
18
+ /**
19
+ * Validate a query string
20
+ */
21
+ validate(query: string): boolean;
22
+ /**
23
+ * Convert a Liqe AST node to a QueryKit expression
24
+ */
25
+ private convertLiqeAst;
26
+ /**
27
+ * Convert a Liqe logical operator to a QueryKit operator
28
+ */
29
+ private convertLogicalOperator;
30
+ /**
31
+ * Create a logical expression from Liqe nodes
32
+ */
33
+ private createLogicalExpression;
34
+ /**
35
+ * Create a comparison expression
36
+ */
37
+ private createComparisonExpression;
38
+ /**
39
+ * Convert a Liqe operator to a QueryKit operator
40
+ */
41
+ private convertLiqeOperator;
42
+ /**
43
+ * Convert a Liqe value to a QueryKit value
44
+ * Security: Strict type checking to prevent NoSQL injection via objects
45
+ */
46
+ private convertLiqeValue;
47
+ /**
48
+ * Normalize a field name based on parser options
49
+ */
50
+ private normalizeFieldName;
51
+ }
@@ -0,0 +1,201 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.QueryParser = exports.QueryParseError = void 0;
4
+ const liqe_1 = require("liqe");
5
+ /**
6
+ * Error thrown when query parsing fails
7
+ */
8
+ class QueryParseError extends Error {
9
+ constructor(message) {
10
+ super(message);
11
+ this.name = 'QueryParseError';
12
+ }
13
+ }
14
+ exports.QueryParseError = QueryParseError;
15
+ /**
16
+ * Implementation of the QueryKit parser using Liqe
17
+ */
18
+ class QueryParser {
19
+ constructor(options = {}) {
20
+ this.options = {
21
+ caseInsensitiveFields: options.caseInsensitiveFields ?? false,
22
+ fieldMappings: options.fieldMappings ?? {}
23
+ };
24
+ }
25
+ /**
26
+ * Parse a query string into a QueryKit AST
27
+ */
28
+ parse(query) {
29
+ try {
30
+ const liqeAst = (0, liqe_1.parse)(query);
31
+ return this.convertLiqeAst(liqeAst);
32
+ }
33
+ catch (error) {
34
+ throw new QueryParseError(`Failed to parse query: ${error instanceof Error ? error.message : String(error)}`);
35
+ }
36
+ }
37
+ /**
38
+ * Validate a query string
39
+ */
40
+ validate(query) {
41
+ try {
42
+ const ast = (0, liqe_1.parse)(query);
43
+ this.convertLiqeAst(ast);
44
+ return true;
45
+ }
46
+ catch {
47
+ return false;
48
+ }
49
+ }
50
+ /**
51
+ * Convert a Liqe AST node to a QueryKit expression
52
+ */
53
+ convertLiqeAst(node) {
54
+ if (!node || typeof node !== 'object') {
55
+ throw new QueryParseError('Invalid AST node');
56
+ }
57
+ switch (node.type) {
58
+ case 'LogicalExpression': {
59
+ const logicalNode = node;
60
+ const operator = logicalNode.operator.operator;
61
+ return this.createLogicalExpression(this.convertLogicalOperator(operator), logicalNode.left, logicalNode.right);
62
+ }
63
+ case 'UnaryOperator': {
64
+ const unaryNode = node;
65
+ return this.createLogicalExpression('NOT', unaryNode.operand);
66
+ }
67
+ case 'Tag': {
68
+ const tagNode = node;
69
+ const field = tagNode.field;
70
+ const expression = tagNode.expression;
71
+ if (!field || !expression) {
72
+ throw new QueryParseError('Invalid field or expression in Tag node');
73
+ }
74
+ const fieldName = this.normalizeFieldName(field.name);
75
+ const operator = this.convertLiqeOperator(tagNode.operator.operator);
76
+ const value = this.convertLiqeValue(expression.value);
77
+ // Check for wildcard patterns in string values
78
+ if (operator === '==' && typeof value === 'string' && (value.includes('*') || value.includes('?'))) {
79
+ return this.createComparisonExpression(fieldName, 'LIKE', value);
80
+ }
81
+ return this.createComparisonExpression(fieldName, operator, value);
82
+ }
83
+ case 'EmptyExpression':
84
+ if ('left' in node && node.left) {
85
+ return this.convertLiqeAst(node.left);
86
+ }
87
+ throw new QueryParseError('Invalid empty expression');
88
+ case 'ParenthesizedExpression': {
89
+ const parenNode = node;
90
+ if (parenNode.expression) {
91
+ return this.convertLiqeAst(parenNode.expression);
92
+ }
93
+ throw new QueryParseError('Invalid parenthesized expression');
94
+ }
95
+ default:
96
+ throw new QueryParseError(`Unsupported node type: ${node.type}`);
97
+ }
98
+ }
99
+ /**
100
+ * Convert a Liqe logical operator to a QueryKit operator
101
+ */
102
+ convertLogicalOperator(operator) {
103
+ switch (operator.toLowerCase()) {
104
+ case 'and':
105
+ return 'AND';
106
+ case 'or':
107
+ return 'OR';
108
+ case 'not':
109
+ return 'NOT';
110
+ default:
111
+ throw new QueryParseError(`Unsupported logical operator: ${operator}`);
112
+ }
113
+ }
114
+ /**
115
+ * Create a logical expression from Liqe nodes
116
+ */
117
+ createLogicalExpression(operator, left, right) {
118
+ return {
119
+ type: 'logical',
120
+ operator,
121
+ left: this.convertLiqeAst(left),
122
+ ...(right && { right: this.convertLiqeAst(right) })
123
+ };
124
+ }
125
+ /**
126
+ * Create a comparison expression
127
+ */
128
+ createComparisonExpression(field, operator, value) {
129
+ return {
130
+ type: 'comparison',
131
+ field,
132
+ operator,
133
+ value
134
+ };
135
+ }
136
+ /**
137
+ * Convert a Liqe operator to a QueryKit operator
138
+ */
139
+ convertLiqeOperator(operator) {
140
+ // Handle the case where operator is part of the value for comparison operators
141
+ if (operator === ':') {
142
+ return '==';
143
+ }
144
+ // Check if the operator is prefixed with a colon
145
+ const actualOperator = operator.startsWith(':') ? operator.substring(1) : operator;
146
+ // Map Liqe operators to QueryKit operators
147
+ const operatorMap = {
148
+ '=': '==',
149
+ '!=': '!=',
150
+ '>': '>',
151
+ '>=': '>=',
152
+ '<': '<',
153
+ '<=': '<=',
154
+ 'in': 'IN',
155
+ 'not in': 'NOT IN'
156
+ };
157
+ const queryKitOperator = operatorMap[actualOperator.toLowerCase()];
158
+ if (!queryKitOperator) {
159
+ throw new QueryParseError(`Unsupported operator: ${operator}`);
160
+ }
161
+ return queryKitOperator;
162
+ }
163
+ /**
164
+ * Convert a Liqe value to a QueryKit value
165
+ * Security: Strict type checking to prevent NoSQL injection via objects
166
+ */
167
+ convertLiqeValue(value) {
168
+ // Security fix: Strict type checking to prevent object injection
169
+ if (value === null) {
170
+ return null;
171
+ }
172
+ if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
173
+ return value;
174
+ }
175
+ if (Array.isArray(value)) {
176
+ // Security fix: Recursively validate array elements
177
+ const validatedArray = value.map(item => {
178
+ if (typeof item === 'object' && item !== null) {
179
+ throw new QueryParseError('Object values are not allowed in arrays');
180
+ }
181
+ return this.convertLiqeValue(item);
182
+ });
183
+ return validatedArray;
184
+ }
185
+ // Security fix: Reject all object types to prevent NoSQL injection
186
+ if (typeof value === 'object') {
187
+ throw new QueryParseError('Object values are not supported for security reasons');
188
+ }
189
+ throw new QueryParseError(`Unsupported value type: ${typeof value}`);
190
+ }
191
+ /**
192
+ * Normalize a field name based on parser options
193
+ */
194
+ normalizeFieldName(field) {
195
+ const normalizedField = this.options.caseInsensitiveFields
196
+ ? field.toLowerCase()
197
+ : field;
198
+ return this.options.fieldMappings[normalizedField] ?? normalizedField;
199
+ }
200
+ }
201
+ exports.QueryParser = QueryParser;
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Core AST types for QueryKit's parser
3
+ */
4
+ /**
5
+ * Represents a comparison operator in a query expression
6
+ */
7
+ export type ComparisonOperator = '==' | '!=' | '>' | '>=' | '<' | '<=' | 'IN' | 'NOT IN' | 'LIKE';
8
+ /**
9
+ * Represents a logical operator in a query expression
10
+ */
11
+ export type LogicalOperator = 'AND' | 'OR' | 'NOT';
12
+ /**
13
+ * Represents a value that can be used in a query expression
14
+ */
15
+ export type QueryValue = string | number | boolean | null | Array<string | number | boolean | null>;
16
+ /**
17
+ * Represents a comparison expression node in the AST
18
+ */
19
+ export interface IComparisonExpression {
20
+ type: 'comparison';
21
+ field: string;
22
+ operator: ComparisonOperator;
23
+ value: QueryValue;
24
+ }
25
+ /**
26
+ * Represents a logical expression node in the AST
27
+ */
28
+ export interface ILogicalExpression {
29
+ type: 'logical';
30
+ operator: LogicalOperator;
31
+ left: QueryExpression;
32
+ right?: QueryExpression;
33
+ }
34
+ /**
35
+ * Represents any valid query expression node
36
+ */
37
+ export type QueryExpression = IComparisonExpression | ILogicalExpression;
38
+ /**
39
+ * Configuration options for the parser
40
+ */
41
+ export interface IParserOptions {
42
+ /**
43
+ * Whether to allow case-insensitive field names
44
+ */
45
+ caseInsensitiveFields?: boolean;
46
+ /**
47
+ * Custom field name mappings
48
+ */
49
+ fieldMappings?: Record<string, string>;
50
+ }
51
+ /**
52
+ * Interface for the query parser
53
+ */
54
+ export interface IQueryParser {
55
+ /**
56
+ * Parse a query string into an AST
57
+ * @param query The query string to parse
58
+ * @returns The parsed AST
59
+ * @throws {QueryParseError} If the query is invalid
60
+ */
61
+ parse(query: string): QueryExpression;
62
+ /**
63
+ * Validate a query string without fully parsing it
64
+ * @param query The query string to validate
65
+ * @returns true if the query is valid, false otherwise
66
+ */
67
+ validate(query: string): boolean;
68
+ }
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ /**
3
+ * Core AST types for QueryKit's parser
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,61 @@
1
+ import { QueryExpression } from '../parser/types';
2
+ import { ComparisonOperator, IQueryBuilder, IQueryBuilderOptions, QueryField, QueryValue, SortDirection } from './types';
3
+ /**
4
+ * Implementation of the type-safe query builder
5
+ */
6
+ export declare class QueryBuilder<T> implements IQueryBuilder<T> {
7
+ private parser;
8
+ private expression;
9
+ private orderByClause;
10
+ private limitClause;
11
+ private offsetClause;
12
+ constructor(options?: IQueryBuilderOptions<T>);
13
+ /**
14
+ * Add a where clause to the query
15
+ */
16
+ where(queryString: string): IQueryBuilder<T>;
17
+ where(field: QueryField<T>, operator: ComparisonOperator, value: QueryValue): IQueryBuilder<T>;
18
+ /**
19
+ * Add an AND where clause to the query
20
+ */
21
+ andWhere(queryString: string): IQueryBuilder<T>;
22
+ andWhere(field: QueryField<T>, operator: ComparisonOperator, value: QueryValue): IQueryBuilder<T>;
23
+ /**
24
+ * Add an OR where clause to the query
25
+ */
26
+ orWhere(queryString: string): IQueryBuilder<T>;
27
+ orWhere(field: QueryField<T>, operator: ComparisonOperator, value: QueryValue): IQueryBuilder<T>;
28
+ /**
29
+ * Add a NOT where clause to the query
30
+ */
31
+ notWhere(queryString: string): IQueryBuilder<T>;
32
+ notWhere(field: QueryField<T>, operator: ComparisonOperator, value: QueryValue): IQueryBuilder<T>;
33
+ /**
34
+ * Add an order by clause to the query
35
+ */
36
+ orderBy(field: QueryField<T>, direction?: SortDirection): IQueryBuilder<T>;
37
+ /**
38
+ * Add a limit to the query
39
+ */
40
+ limit(count: number): IQueryBuilder<T>;
41
+ /**
42
+ * Add an offset to the query
43
+ */
44
+ offset(count: number): IQueryBuilder<T>;
45
+ /**
46
+ * Get the current query expression
47
+ */
48
+ getExpression(): QueryExpression;
49
+ /**
50
+ * Get the current query as a string
51
+ */
52
+ toString(): string;
53
+ /**
54
+ * Build a comparison expression
55
+ */
56
+ private buildComparison;
57
+ /**
58
+ * Format a value for use in a query
59
+ */
60
+ private formatValue;
61
+ }