@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,274 @@
1
+ import { QueryParser } from '../parser';
2
+ import { QueryExpression } from '../parser/types';
3
+ import {
4
+ ComparisonOperator,
5
+ IQueryBuilder,
6
+ IQueryBuilderOptions,
7
+ QueryField,
8
+ QueryValue,
9
+ SortDirection
10
+ } from './types';
11
+
12
+ /**
13
+ * Implementation of the type-safe query builder
14
+ */
15
+ export class QueryBuilder<T> implements IQueryBuilder<T> {
16
+ private parser: QueryParser;
17
+ private expression: string = '';
18
+ private orderByClause: string = '';
19
+ private limitClause: string = '';
20
+ private offsetClause: string = '';
21
+
22
+ constructor(options: IQueryBuilderOptions<T> = {}) {
23
+ this.parser = new QueryParser({
24
+ caseInsensitiveFields: options.caseInsensitiveFields,
25
+ fieldMappings: options.fieldMappings as Record<string, string>
26
+ });
27
+ }
28
+
29
+ /**
30
+ * Add a where clause to the query
31
+ */
32
+ public where(queryString: string): IQueryBuilder<T>;
33
+ public where(field: QueryField<T>, operator: ComparisonOperator, value: QueryValue): IQueryBuilder<T>;
34
+ public where(
35
+ fieldOrQueryString: QueryField<T> | string,
36
+ operator?: ComparisonOperator,
37
+ value?: QueryValue
38
+ ): IQueryBuilder<T> {
39
+ if (operator === undefined || value === undefined) {
40
+ // Handle direct query string format
41
+ this.expression = fieldOrQueryString as string;
42
+ } else {
43
+ // Handle field, operator, value format
44
+ this.expression = this.buildComparison(
45
+ fieldOrQueryString as QueryField<T>,
46
+ operator,
47
+ value
48
+ );
49
+ }
50
+ return this;
51
+ }
52
+
53
+ /**
54
+ * Add an AND where clause to the query
55
+ */
56
+ public andWhere(queryString: string): IQueryBuilder<T>;
57
+ public andWhere(field: QueryField<T>, operator: ComparisonOperator, value: QueryValue): IQueryBuilder<T>;
58
+ public andWhere(
59
+ fieldOrQueryString: QueryField<T> | string,
60
+ operator?: ComparisonOperator,
61
+ value?: QueryValue
62
+ ): IQueryBuilder<T> {
63
+ if (!this.expression) {
64
+ if (typeof fieldOrQueryString === 'string' && (operator === undefined || value === undefined)) {
65
+ // Handle direct query string
66
+ return this.where(fieldOrQueryString);
67
+ } else {
68
+ // Handle field, operator, value format
69
+ return this.where(
70
+ fieldOrQueryString as QueryField<T>,
71
+ operator as ComparisonOperator,
72
+ value as QueryValue
73
+ );
74
+ }
75
+ }
76
+
77
+ if (operator === undefined || value === undefined) {
78
+ // Handle direct query string format
79
+ this.expression = `(${this.expression}) AND ${fieldOrQueryString}`;
80
+ } else {
81
+ // Handle field, operator, value format
82
+ this.expression = `(${this.expression}) AND ${this.buildComparison(
83
+ fieldOrQueryString as QueryField<T>,
84
+ operator,
85
+ value
86
+ )}`;
87
+ }
88
+ return this;
89
+ }
90
+
91
+ /**
92
+ * Add an OR where clause to the query
93
+ */
94
+ public orWhere(queryString: string): IQueryBuilder<T>;
95
+ public orWhere(field: QueryField<T>, operator: ComparisonOperator, value: QueryValue): IQueryBuilder<T>;
96
+ public orWhere(
97
+ fieldOrQueryString: QueryField<T> | string,
98
+ operator?: ComparisonOperator,
99
+ value?: QueryValue
100
+ ): IQueryBuilder<T> {
101
+ if (!this.expression) {
102
+ if (typeof fieldOrQueryString === 'string' && (operator === undefined || value === undefined)) {
103
+ // Handle direct query string
104
+ return this.where(fieldOrQueryString);
105
+ } else {
106
+ // Handle field, operator, value format
107
+ return this.where(
108
+ fieldOrQueryString as QueryField<T>,
109
+ operator as ComparisonOperator,
110
+ value as QueryValue
111
+ );
112
+ }
113
+ }
114
+
115
+ if (operator === undefined || value === undefined) {
116
+ // Handle direct query string format
117
+ this.expression = `(${this.expression}) OR ${fieldOrQueryString}`;
118
+ } else {
119
+ // Handle field, operator, value format
120
+ this.expression = `(${this.expression}) OR ${this.buildComparison(
121
+ fieldOrQueryString as QueryField<T>,
122
+ operator,
123
+ value
124
+ )}`;
125
+ }
126
+ return this;
127
+ }
128
+
129
+ /**
130
+ * Add a NOT where clause to the query
131
+ */
132
+ public notWhere(queryString: string): IQueryBuilder<T>;
133
+ public notWhere(field: QueryField<T>, operator: ComparisonOperator, value: QueryValue): IQueryBuilder<T>;
134
+ public notWhere(
135
+ fieldOrQueryString: QueryField<T> | string,
136
+ operator?: ComparisonOperator,
137
+ value?: QueryValue
138
+ ): IQueryBuilder<T> {
139
+ if (!this.expression) {
140
+ if (operator === undefined || value === undefined) {
141
+ // Handle direct query string format
142
+ this.expression = `NOT ${fieldOrQueryString}`;
143
+ } else {
144
+ // Handle field, operator, value format
145
+ this.expression = `NOT ${this.buildComparison(
146
+ fieldOrQueryString as QueryField<T>,
147
+ operator,
148
+ value
149
+ )}`;
150
+ }
151
+ } else {
152
+ if (operator === undefined || value === undefined) {
153
+ // Handle direct query string format
154
+ this.expression = `(${this.expression}) AND NOT ${fieldOrQueryString}`;
155
+ } else {
156
+ // Handle field, operator, value format
157
+ this.expression = `(${this.expression}) AND NOT ${this.buildComparison(
158
+ fieldOrQueryString as QueryField<T>,
159
+ operator,
160
+ value
161
+ )}`;
162
+ }
163
+ }
164
+ return this;
165
+ }
166
+
167
+ /**
168
+ * Add an order by clause to the query
169
+ */
170
+ public orderBy(field: QueryField<T>, direction: SortDirection = 'asc'): IQueryBuilder<T> {
171
+ this.orderByClause = `ORDER BY ${field} ${direction.toUpperCase()}`;
172
+ return this;
173
+ }
174
+
175
+ /**
176
+ * Add a limit to the query
177
+ */
178
+ public limit(count: number): IQueryBuilder<T> {
179
+ this.limitClause = `LIMIT ${count}`;
180
+ return this;
181
+ }
182
+
183
+ /**
184
+ * Add an offset to the query
185
+ */
186
+ public offset(count: number): IQueryBuilder<T> {
187
+ this.offsetClause = `OFFSET ${count}`;
188
+ return this;
189
+ }
190
+
191
+ /**
192
+ * Get the current query expression
193
+ */
194
+ public getExpression(): QueryExpression {
195
+ return this.parser.parse(this.expression);
196
+ }
197
+
198
+ /**
199
+ * Get the current query as a string
200
+ */
201
+ public toString(): string {
202
+ const clauses = [
203
+ this.expression,
204
+ this.orderByClause,
205
+ this.limitClause,
206
+ this.offsetClause
207
+ ].filter(Boolean);
208
+
209
+ return clauses.join(' ');
210
+ }
211
+
212
+ /**
213
+ * Build a comparison expression
214
+ */
215
+ private buildComparison(field: QueryField<T>, operator: ComparisonOperator, value: QueryValue): string {
216
+ // Map QueryKit operators to Liqe operators
217
+ const operatorMap: Record<ComparisonOperator, string> = {
218
+ '==': ':',
219
+ '!=': '!=',
220
+ '>': '>',
221
+ '>=': '>=',
222
+ '<': '<',
223
+ '<=': '<=',
224
+ 'IN': 'in',
225
+ 'NOT IN': 'not in',
226
+ 'LIKE': ':'
227
+ };
228
+
229
+ const liqeOperator = operatorMap[operator];
230
+ const formattedValue = this.formatValue(value, operator);
231
+
232
+ // For equality and LIKE operators, use field:value format (simple colon)
233
+ if (operator === '==' || operator === 'LIKE') {
234
+ return `${field}${liqeOperator}${formattedValue}`;
235
+ }
236
+
237
+ // Based on Liqe docs, comparison operators are prefixed with colon
238
+ // e.g., 'height:>100', 'height:<100'
239
+ if (operator === '>' || operator === '>=' || operator === '<' || operator === '<=' || operator === '!=') {
240
+ return `${field}:${liqeOperator}${formattedValue}`;
241
+ }
242
+
243
+ // For array operators (IN, NOT IN), use the format field:operator[values]
244
+ if (operator === 'IN' || operator === 'NOT IN') {
245
+ return `${field}:${liqeOperator}${formattedValue}`;
246
+ }
247
+
248
+ // For other operators, use field:operator value format
249
+ return `${field}:${liqeOperator} ${formattedValue}`;
250
+ }
251
+
252
+ /**
253
+ * Format a value for use in a query
254
+ */
255
+ private formatValue(value: QueryValue, operator?: ComparisonOperator): string {
256
+ if (value === null) {
257
+ return 'null';
258
+ }
259
+
260
+ if (Array.isArray(value)) {
261
+ return `[${value.map(v => this.formatValue(v)).join(',')}]`;
262
+ }
263
+
264
+ if (typeof value === 'string') {
265
+ // For LIKE operator with wildcard patterns, don't add quotes to allow pattern matching
266
+ if (operator === 'LIKE' && (value.includes('*') || value.includes('?'))) {
267
+ return value;
268
+ }
269
+ return `"${value}"`;
270
+ }
271
+
272
+ return String(value);
273
+ }
274
+ }
@@ -0,0 +1,2 @@
1
+ export * from './types';
2
+ export * from './builder';
@@ -0,0 +1,107 @@
1
+ import { QueryExpression } from '../parser/types';
2
+
3
+ /**
4
+ * Represents a field in a query
5
+ */
6
+ export type QueryField<T> = keyof T & string;
7
+
8
+ /**
9
+ * Represents a value that can be used in a query
10
+ */
11
+ export type QueryValue = string | number | boolean | null | Array<string | number | boolean | null>;
12
+
13
+ /**
14
+ * Represents a comparison operator in a query
15
+ */
16
+ export type ComparisonOperator =
17
+ | '=='
18
+ | '!='
19
+ | '>'
20
+ | '>='
21
+ | '<'
22
+ | '<='
23
+ | 'IN'
24
+ | 'NOT IN'
25
+ | 'LIKE';
26
+
27
+ /**
28
+ * Represents a logical operator in a query
29
+ */
30
+ export type LogicalOperator =
31
+ | 'AND'
32
+ | 'OR'
33
+ | 'NOT';
34
+
35
+ /**
36
+ * Represents a sort direction
37
+ */
38
+ export type SortDirection = 'asc' | 'desc';
39
+
40
+ /**
41
+ * Configuration options for the query builder
42
+ */
43
+ export interface IQueryBuilderOptions<T> {
44
+ /**
45
+ * Whether to allow case-insensitive field names
46
+ */
47
+ caseInsensitiveFields?: boolean;
48
+
49
+ /**
50
+ * Custom field name mappings
51
+ */
52
+ fieldMappings?: Partial<Record<QueryField<T>, string>>;
53
+ }
54
+
55
+ /**
56
+ * Interface for a query builder
57
+ */
58
+ export interface IQueryBuilder<T> {
59
+ /**
60
+ * Add a where clause to the query
61
+ */
62
+ where(queryString: string): IQueryBuilder<T>;
63
+ where(field: QueryField<T>, operator: ComparisonOperator, value: QueryValue): IQueryBuilder<T>;
64
+
65
+ /**
66
+ * Add an AND where clause to the query
67
+ */
68
+ andWhere(queryString: string): IQueryBuilder<T>;
69
+ andWhere(field: QueryField<T>, operator: ComparisonOperator, value: QueryValue): IQueryBuilder<T>;
70
+
71
+ /**
72
+ * Add an OR where clause to the query
73
+ */
74
+ orWhere(queryString: string): IQueryBuilder<T>;
75
+ orWhere(field: QueryField<T>, operator: ComparisonOperator, value: QueryValue): IQueryBuilder<T>;
76
+
77
+ /**
78
+ * Add a NOT where clause to the query
79
+ */
80
+ notWhere(queryString: string): IQueryBuilder<T>;
81
+ notWhere(field: QueryField<T>, operator: ComparisonOperator, value: QueryValue): IQueryBuilder<T>;
82
+
83
+ /**
84
+ * Add an order by clause to the query
85
+ */
86
+ orderBy(field: QueryField<T>, direction?: SortDirection): IQueryBuilder<T>;
87
+
88
+ /**
89
+ * Add a limit to the query
90
+ */
91
+ limit(count: number): IQueryBuilder<T>;
92
+
93
+ /**
94
+ * Add an offset to the query
95
+ */
96
+ offset(count: number): IQueryBuilder<T>;
97
+
98
+ /**
99
+ * Get the current query expression
100
+ */
101
+ getExpression(): QueryExpression;
102
+
103
+ /**
104
+ * Get the current query as a string
105
+ */
106
+ toString(): string;
107
+ }
@@ -0,0 +1,2 @@
1
+ export * from './types';
2
+ export * from './validator';
@@ -0,0 +1,210 @@
1
+ /**
2
+ * Security configuration types for QueryKit
3
+ *
4
+ * This module defines the security configuration interface and default values
5
+ * used throughout QueryKit to enforce security boundaries and prevent abuse.
6
+ */
7
+
8
+ /**
9
+ * @interface ISecurityOptions
10
+ * @description Comprehensive security configuration options for QueryKit
11
+ *
12
+ * These options help protect your application from potential security issues,
13
+ * resource exhaustion, and performance problems when exposing QueryKit to users.
14
+ *
15
+ * @example
16
+ * ```typescript
17
+ * import { createQueryKit, type ISecurityOptions } from 'querykit';
18
+ *
19
+ * // Configure security options
20
+ * const securityOptions: ISecurityOptions = {
21
+ * allowedFields: ['id', 'name', 'createdAt'],
22
+ * denyFields: ['password', 'secretKey'],
23
+ * maxQueryDepth: 5,
24
+ * maxClauseCount: 20
25
+ * };
26
+ *
27
+ * // Create QueryKit instance with security options
28
+ * const queryKit = createQueryKit({
29
+ * // ...other options
30
+ * security: securityOptions
31
+ * });
32
+ * ```
33
+ */
34
+ export interface ISecurityOptions {
35
+ /**
36
+ * List of fields that are allowed to be queried.
37
+ * If empty, all fields in the schema are allowed by default.
38
+ *
39
+ * @example
40
+ * ```typescript
41
+ * // Only allow specific fields to be queried
42
+ * allowedFields: ['id', 'name', 'email', 'createdAt']
43
+ * ```
44
+ */
45
+ allowedFields?: string[];
46
+
47
+ /**
48
+ * List of fields that are explicitly denied from being queried.
49
+ * These fields will be blocked even if they appear in allowedFields.
50
+ * Use this to protect sensitive data fields.
51
+ *
52
+ * @example
53
+ * ```typescript
54
+ * // Prevent querying of sensitive fields
55
+ * denyFields: ['password', 'secretToken', 'ssn']
56
+ * ```
57
+ */
58
+ denyFields?: string[];
59
+
60
+ /**
61
+ * Maximum nesting depth of query expressions.
62
+ * Prevents deeply nested queries that could impact performance.
63
+ *
64
+ * @default 10
65
+ *
66
+ * @example
67
+ * ```typescript
68
+ * // Allow only simple queries with limited nesting
69
+ * maxQueryDepth: 3
70
+ *
71
+ * // This would allow queries like:
72
+ * // title:"Meeting notes" && (priority > 2 || completed == true)
73
+ * // But would reject more deeply nested expressions
74
+ * ```
75
+ */
76
+ maxQueryDepth?: number;
77
+
78
+ /**
79
+ * Maximum number of clauses (AND/OR operations) in a query.
80
+ * Prevents overly complex queries that could impact performance.
81
+ *
82
+ * @default 50
83
+ *
84
+ * @example
85
+ * ```typescript
86
+ * // Limit query complexity
87
+ * maxClauseCount: 20
88
+ *
89
+ * // This would allow queries with up to 20 conditions joined by AND/OR
90
+ * ```
91
+ */
92
+ maxClauseCount?: number;
93
+
94
+ /**
95
+ * Default limit for query results if none is specified by the client.
96
+ * Prevents unintentionally large result sets.
97
+ *
98
+ * @default 100
99
+ *
100
+ * @example
101
+ * ```typescript
102
+ * // Set conservative default limit
103
+ * defaultLimit: 50
104
+ * ```
105
+ */
106
+ defaultLimit?: number;
107
+
108
+ /**
109
+ * Maximum allowed limit for pagination.
110
+ * Prevents clients from requesting excessively large result sets.
111
+ *
112
+ * @default 1000
113
+ *
114
+ * @example
115
+ * ```typescript
116
+ * // Restrict maximum page size
117
+ * maxLimit: 500
118
+ *
119
+ * // Even if a client requests limit=10000, it will be capped at 500
120
+ * ```
121
+ */
122
+ maxLimit?: number;
123
+
124
+ /**
125
+ * Maximum string length for query values.
126
+ * Prevents memory exhaustion from extremely large string values.
127
+ *
128
+ * @default 1000
129
+ *
130
+ * @example
131
+ * ```typescript
132
+ * // Limit string length in query values
133
+ * maxValueLength: 500
134
+ *
135
+ * // Prevents attacks using extremely long strings in filters
136
+ * ```
137
+ */
138
+ maxValueLength?: number;
139
+
140
+ /**
141
+ * Whether to sanitize wildcard patterns in LIKE queries to prevent regex DoS.
142
+ * When enabled, excessive wildcard patterns are sanitized or rejected.
143
+ *
144
+ * @default true
145
+ *
146
+ * @example
147
+ * ```typescript
148
+ * // Enable wildcard sanitization
149
+ * sanitizeWildcards: true
150
+ *
151
+ * // Prevents regex DoS attacks like: name LIKE "%a%a%a%a%a%a%a%a%..."
152
+ * ```
153
+ */
154
+ sanitizeWildcards?: boolean;
155
+
156
+ /**
157
+ * Timeout in milliseconds for query execution.
158
+ * Prevents long-running queries from consuming excessive resources.
159
+ *
160
+ * @default 30000 (30 seconds)
161
+ *
162
+ * @example
163
+ * ```typescript
164
+ * // Set shorter timeout for API endpoints
165
+ * queryTimeout: 5000 // 5 seconds
166
+ *
167
+ * // Queries taking longer than 5 seconds will be terminated
168
+ * ```
169
+ */
170
+ queryTimeout?: number;
171
+ }
172
+
173
+ /**
174
+ * Default security configuration values
175
+ *
176
+ * These defaults provide a reasonable balance between functionality and security.
177
+ * It's recommended to review and adjust these settings based on your specific use case.
178
+ *
179
+ * @example
180
+ * ```typescript
181
+ * import { DEFAULT_SECURITY_OPTIONS } from 'querykit';
182
+ *
183
+ * // Use defaults but override specific options
184
+ * const securityOptions = {
185
+ * ...DEFAULT_SECURITY_OPTIONS,
186
+ * maxLimit: 500,
187
+ * queryTimeout: 10000
188
+ * };
189
+ * ```
190
+ */
191
+ export const DEFAULT_SECURITY_OPTIONS: Required<ISecurityOptions> = {
192
+ // Field restrictions - by default, all schema fields are allowed
193
+ allowedFields: [], // Empty means "use schema fields"
194
+ denyFields: [], // Empty means no denied fields
195
+
196
+ // Query complexity limits
197
+ maxQueryDepth: 10, // Maximum nesting level of expressions
198
+ maxClauseCount: 50, // Maximum number of clauses (AND/OR operations)
199
+
200
+ // Resource protection
201
+ defaultLimit: 100, // Default result limit if none specified
202
+ maxLimit: 1000, // Maximum allowed limit for pagination
203
+
204
+ // Value sanitization
205
+ maxValueLength: 1000, // Maximum string length for query values
206
+ sanitizeWildcards: true, // Prevent regex DoS with wildcards in LIKE queries
207
+
208
+ // Performance safeguards
209
+ queryTimeout: 30000 // 30 second timeout by default
210
+ };