@gblikas/querykit 0.2.0 → 0.4.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/.cursor/BUGBOT.md +65 -2
  2. package/.husky/pre-commit +3 -3
  3. package/README.md +510 -1
  4. package/dist/index.d.ts +36 -3
  5. package/dist/index.js +20 -3
  6. package/dist/parser/index.d.ts +1 -0
  7. package/dist/parser/index.js +1 -0
  8. package/dist/parser/input-parser.d.ts +215 -0
  9. package/dist/parser/input-parser.js +493 -0
  10. package/dist/parser/parser.d.ts +114 -1
  11. package/dist/parser/parser.js +716 -0
  12. package/dist/parser/types.d.ts +432 -0
  13. package/dist/virtual-fields/index.d.ts +5 -0
  14. package/dist/virtual-fields/index.js +21 -0
  15. package/dist/virtual-fields/resolver.d.ts +17 -0
  16. package/dist/virtual-fields/resolver.js +107 -0
  17. package/dist/virtual-fields/types.d.ts +160 -0
  18. package/dist/virtual-fields/types.js +5 -0
  19. package/examples/qk-next/app/page.tsx +190 -86
  20. package/examples/qk-next/package.json +1 -1
  21. package/package.json +2 -2
  22. package/src/adapters/drizzle/index.ts +3 -3
  23. package/src/index.ts +77 -8
  24. package/src/parser/divergence.test.ts +357 -0
  25. package/src/parser/index.ts +2 -1
  26. package/src/parser/input-parser.test.ts +770 -0
  27. package/src/parser/input-parser.ts +697 -0
  28. package/src/parser/parse-with-context-suggestions.test.ts +360 -0
  29. package/src/parser/parse-with-context-validation.test.ts +447 -0
  30. package/src/parser/parse-with-context.test.ts +325 -0
  31. package/src/parser/parser.ts +872 -0
  32. package/src/parser/token-consistency.test.ts +341 -0
  33. package/src/parser/types.ts +545 -23
  34. package/src/virtual-fields/index.ts +6 -0
  35. package/src/virtual-fields/integration.test.ts +338 -0
  36. package/src/virtual-fields/resolver.ts +165 -0
  37. package/src/virtual-fields/types.ts +203 -0
  38. package/src/virtual-fields/virtual-fields.test.ts +831 -0
  39. package/examples/qk-next/pnpm-lock.yaml +0 -5623
@@ -5,33 +5,30 @@
5
5
  /**
6
6
  * Represents a comparison operator in a query expression
7
7
  */
8
- export type ComparisonOperator =
9
- | '=='
10
- | '!='
11
- | '>'
12
- | '>='
13
- | '<'
14
- | '<='
15
- | 'IN'
8
+ export type ComparisonOperator =
9
+ | '=='
10
+ | '!='
11
+ | '>'
12
+ | '>='
13
+ | '<'
14
+ | '<='
15
+ | 'IN'
16
16
  | 'NOT IN'
17
17
  | 'LIKE';
18
18
 
19
19
  /**
20
20
  * Represents a logical operator in a query expression
21
21
  */
22
- export type LogicalOperator =
23
- | 'AND'
24
- | 'OR'
25
- | 'NOT';
22
+ export type LogicalOperator = 'AND' | 'OR' | 'NOT';
26
23
 
27
24
  /**
28
25
  * Represents a value that can be used in a query expression
29
26
  */
30
- export type QueryValue =
31
- | string
32
- | number
33
- | boolean
34
- | null
27
+ export type QueryValue =
28
+ | string
29
+ | number
30
+ | boolean
31
+ | null
35
32
  | Array<string | number | boolean | null>;
36
33
 
37
34
  /**
@@ -57,9 +54,7 @@ export interface ILogicalExpression {
57
54
  /**
58
55
  * Represents any valid query expression node
59
56
  */
60
- export type QueryExpression =
61
- | IComparisonExpression
62
- | ILogicalExpression;
57
+ export type QueryExpression = IComparisonExpression | ILogicalExpression;
63
58
 
64
59
  /**
65
60
  * Configuration options for the parser
@@ -69,7 +64,7 @@ export interface IParserOptions {
69
64
  * Whether to allow case-insensitive field names
70
65
  */
71
66
  caseInsensitiveFields?: boolean;
72
-
67
+
73
68
  /**
74
69
  * Custom field name mappings
75
70
  */
@@ -87,11 +82,538 @@ export interface IQueryParser {
87
82
  * @throws {QueryParseError} If the query is invalid
88
83
  */
89
84
  parse(query: string): QueryExpression;
90
-
85
+
91
86
  /**
92
87
  * Validate a query string without fully parsing it
93
88
  * @param query The query string to validate
94
89
  * @returns true if the query is valid, false otherwise
95
90
  */
96
91
  validate(query: string): boolean;
97
- }
92
+
93
+ /**
94
+ * Parse a query string with full context information
95
+ * @param query The query string to parse
96
+ * @param options Options for context parsing
97
+ * @returns Rich parse result with tokens, AST, structure, and more
98
+ */
99
+ parseWithContext(
100
+ query: string,
101
+ options?: IParseWithContextOptions
102
+ ): IQueryParseResult;
103
+ }
104
+
105
+ /**
106
+ * Options for parseWithContext
107
+ */
108
+ export interface IParseWithContextOptions {
109
+ /**
110
+ * Cursor position in the input (for cursor-aware features)
111
+ */
112
+ cursorPosition?: number;
113
+
114
+ /**
115
+ * Schema to validate fields against.
116
+ * Keys are field names, values describe the field type.
117
+ * When provided, enables field validation in the result.
118
+ */
119
+ schema?: Record<string, IFieldSchema>;
120
+
121
+ /**
122
+ * Security options for pre-validation.
123
+ * When provided, enables security pre-check in the result.
124
+ */
125
+ securityOptions?: ISecurityOptionsForContext;
126
+ }
127
+
128
+ /**
129
+ * Field schema definition for validation
130
+ */
131
+ export interface IFieldSchema {
132
+ /**
133
+ * The type of the field
134
+ */
135
+ type: 'string' | 'number' | 'boolean' | 'date' | 'array' | 'unknown';
136
+
137
+ /**
138
+ * Whether the field is required (for documentation purposes)
139
+ */
140
+ required?: boolean;
141
+
142
+ /**
143
+ * Allowed values for the field (for enums)
144
+ */
145
+ allowedValues?: Array<string | number | boolean>;
146
+
147
+ /**
148
+ * Human-readable description of the field
149
+ */
150
+ description?: string;
151
+ }
152
+
153
+ /**
154
+ * Security options for parseWithContext (subset of full security options)
155
+ */
156
+ export interface ISecurityOptionsForContext {
157
+ /**
158
+ * List of fields that are allowed to be queried
159
+ */
160
+ allowedFields?: string[];
161
+
162
+ /**
163
+ * List of fields that are denied from being queried
164
+ */
165
+ denyFields?: string[];
166
+
167
+ /**
168
+ * Maximum query depth allowed
169
+ */
170
+ maxQueryDepth?: number;
171
+
172
+ /**
173
+ * Maximum number of clauses allowed
174
+ */
175
+ maxClauseCount?: number;
176
+
177
+ /**
178
+ * Whether to allow dot notation in field names
179
+ */
180
+ allowDotNotation?: boolean;
181
+ }
182
+
183
+ /**
184
+ * Structural analysis of a query
185
+ */
186
+ export interface IQueryStructure {
187
+ /**
188
+ * Maximum nesting depth of the query
189
+ * e.g., "a:1 AND (b:2 OR c:3)" has depth 2
190
+ */
191
+ depth: number;
192
+
193
+ /**
194
+ * Total number of comparison clauses
195
+ * e.g., "a:1 AND b:2 OR c:3" has 3 clauses
196
+ */
197
+ clauseCount: number;
198
+
199
+ /**
200
+ * Number of logical operators (AND, OR, NOT)
201
+ */
202
+ operatorCount: number;
203
+
204
+ /**
205
+ * Whether parentheses are balanced
206
+ */
207
+ hasBalancedParentheses: boolean;
208
+
209
+ /**
210
+ * Whether all quotes are closed
211
+ */
212
+ hasBalancedQuotes: boolean;
213
+
214
+ /**
215
+ * Whether the query appears structurally complete
216
+ * (no trailing operators, no unclosed constructs)
217
+ */
218
+ isComplete: boolean;
219
+
220
+ /**
221
+ * List of fields referenced in the query
222
+ */
223
+ referencedFields: string[];
224
+
225
+ /**
226
+ * Complexity classification
227
+ */
228
+ complexity: 'simple' | 'moderate' | 'complex';
229
+ }
230
+
231
+ /**
232
+ * Error information when parsing fails
233
+ */
234
+ export interface IQueryParseErrorInfo {
235
+ /**
236
+ * Error message
237
+ */
238
+ message: string;
239
+
240
+ /**
241
+ * Position in the input where the error occurred (if known)
242
+ */
243
+ position?: number;
244
+
245
+ /**
246
+ * The portion of input that caused the error (if identifiable)
247
+ */
248
+ problematicText?: string;
249
+ }
250
+
251
+ /**
252
+ * Result of parseWithContext - provides rich context about the query
253
+ */
254
+ export interface IQueryParseResult {
255
+ /**
256
+ * Whether parsing succeeded
257
+ */
258
+ success: boolean;
259
+
260
+ /**
261
+ * The original input string
262
+ */
263
+ input: string;
264
+
265
+ /**
266
+ * Parsed AST (only present if success is true)
267
+ */
268
+ ast?: QueryExpression;
269
+
270
+ /**
271
+ * Error information (only present if success is false)
272
+ */
273
+ error?: IQueryParseErrorInfo;
274
+
275
+ /**
276
+ * Tokenized representation of the input
277
+ * Always present, even if parsing failed
278
+ */
279
+ tokens: IQueryToken[];
280
+
281
+ /**
282
+ * The token at the cursor position (if cursorPosition was provided)
283
+ */
284
+ activeToken?: IQueryToken;
285
+
286
+ /**
287
+ * Index of the active token (-1 if none)
288
+ */
289
+ activeTokenIndex: number;
290
+
291
+ /**
292
+ * Structural analysis of the query
293
+ */
294
+ structure: IQueryStructure;
295
+
296
+ /**
297
+ * Field validation results (only present if schema was provided in options)
298
+ */
299
+ fieldValidation?: IFieldValidationResult;
300
+
301
+ /**
302
+ * Security pre-check results (only present if securityOptions was provided)
303
+ */
304
+ security?: ISecurityCheckResult;
305
+
306
+ /**
307
+ * Autocomplete suggestions based on cursor position
308
+ * Only present if cursorPosition was provided in options
309
+ */
310
+ suggestions?: IAutocompleteSuggestions;
311
+
312
+ /**
313
+ * Error recovery hints (only present if parsing failed)
314
+ */
315
+ recovery?: IErrorRecovery;
316
+ }
317
+
318
+ /**
319
+ * Autocomplete suggestions based on cursor context
320
+ */
321
+ export interface IAutocompleteSuggestions {
322
+ /**
323
+ * The context where the cursor is positioned
324
+ */
325
+ context: 'field' | 'operator' | 'value' | 'logical_operator' | 'empty';
326
+
327
+ /**
328
+ * The current field being edited (if in value context)
329
+ */
330
+ currentField?: string;
331
+
332
+ /**
333
+ * Suggested field names (when in field context)
334
+ */
335
+ fields?: IFieldSuggestion[];
336
+
337
+ /**
338
+ * Suggested values (when in value context and schema has allowedValues)
339
+ */
340
+ values?: IValueSuggestion[];
341
+
342
+ /**
343
+ * Suggested operators (when in operator context)
344
+ */
345
+ operators?: IOperatorSuggestion[];
346
+
347
+ /**
348
+ * Suggested logical operators (AND, OR, NOT)
349
+ */
350
+ logicalOperators?: string[];
351
+
352
+ /**
353
+ * The text that would be replaced by the suggestion
354
+ */
355
+ replaceText?: string;
356
+
357
+ /**
358
+ * Position range that would be replaced
359
+ */
360
+ replaceRange?: { start: number; end: number };
361
+ }
362
+
363
+ /**
364
+ * A field suggestion for autocomplete
365
+ */
366
+ export interface IFieldSuggestion {
367
+ /**
368
+ * The field name
369
+ */
370
+ field: string;
371
+
372
+ /**
373
+ * The field type (from schema)
374
+ */
375
+ type?: string;
376
+
377
+ /**
378
+ * Description of the field (from schema)
379
+ */
380
+ description?: string;
381
+
382
+ /**
383
+ * Match score (higher is better match)
384
+ */
385
+ score: number;
386
+ }
387
+
388
+ /**
389
+ * A value suggestion for autocomplete
390
+ */
391
+ export interface IValueSuggestion {
392
+ /**
393
+ * The suggested value
394
+ */
395
+ value: string | number | boolean;
396
+
397
+ /**
398
+ * Display label (may differ from value)
399
+ */
400
+ label?: string;
401
+
402
+ /**
403
+ * Match score (higher is better match)
404
+ */
405
+ score: number;
406
+ }
407
+
408
+ /**
409
+ * An operator suggestion for autocomplete
410
+ */
411
+ export interface IOperatorSuggestion {
412
+ /**
413
+ * The operator symbol
414
+ */
415
+ operator: string;
416
+
417
+ /**
418
+ * Human-readable description
419
+ */
420
+ description: string;
421
+
422
+ /**
423
+ * Whether this operator is applicable to the current field type
424
+ */
425
+ applicable: boolean;
426
+ }
427
+
428
+ /**
429
+ * Error recovery suggestions
430
+ */
431
+ export interface IErrorRecovery {
432
+ /**
433
+ * The type of issue detected
434
+ */
435
+ issue:
436
+ | 'unclosed_quote'
437
+ | 'unclosed_parenthesis'
438
+ | 'trailing_operator'
439
+ | 'missing_value'
440
+ | 'missing_operator'
441
+ | 'syntax_error';
442
+
443
+ /**
444
+ * Human-readable description of the issue
445
+ */
446
+ message: string;
447
+
448
+ /**
449
+ * Suggested fix description
450
+ */
451
+ suggestion: string;
452
+
453
+ /**
454
+ * The corrected query (if auto-fix is possible)
455
+ */
456
+ autofix?: string;
457
+
458
+ /**
459
+ * Position where the issue was detected
460
+ */
461
+ position?: number;
462
+ }
463
+
464
+ /**
465
+ * Result of field validation against schema
466
+ */
467
+ export interface IFieldValidationResult {
468
+ /**
469
+ * Whether all fields passed validation
470
+ */
471
+ valid: boolean;
472
+
473
+ /**
474
+ * Validation details for each field
475
+ */
476
+ fields: IFieldValidationDetail[];
477
+
478
+ /**
479
+ * Fields that were referenced but not found in schema
480
+ */
481
+ unknownFields: string[];
482
+ }
483
+
484
+ /**
485
+ * Validation detail for a single field
486
+ */
487
+ export interface IFieldValidationDetail {
488
+ /**
489
+ * The field name
490
+ */
491
+ field: string;
492
+
493
+ /**
494
+ * Whether this field is valid
495
+ */
496
+ valid: boolean;
497
+
498
+ /**
499
+ * The expected type from schema (if known)
500
+ */
501
+ expectedType?: string;
502
+
503
+ /**
504
+ * Reason for validation failure (if invalid)
505
+ */
506
+ reason?: 'unknown_field' | 'type_mismatch' | 'invalid_value' | 'denied';
507
+
508
+ /**
509
+ * Suggested correction (for typos)
510
+ */
511
+ suggestion?: string;
512
+
513
+ /**
514
+ * Allowed values (if field has enum constraint)
515
+ */
516
+ allowedValues?: Array<string | number | boolean>;
517
+ }
518
+
519
+ /**
520
+ * Result of security pre-check
521
+ */
522
+ export interface ISecurityCheckResult {
523
+ /**
524
+ * Whether the query passes all security checks
525
+ */
526
+ passed: boolean;
527
+
528
+ /**
529
+ * List of security violations found
530
+ */
531
+ violations: ISecurityViolation[];
532
+
533
+ /**
534
+ * Warnings that don't block execution but should be noted
535
+ */
536
+ warnings: ISecurityWarning[];
537
+ }
538
+
539
+ /**
540
+ * A security violation that blocks query execution
541
+ */
542
+ export interface ISecurityViolation {
543
+ /**
544
+ * Type of violation
545
+ */
546
+ type:
547
+ | 'denied_field'
548
+ | 'depth_exceeded'
549
+ | 'clause_limit'
550
+ | 'dot_notation'
551
+ | 'field_not_allowed';
552
+
553
+ /**
554
+ * Human-readable message
555
+ */
556
+ message: string;
557
+
558
+ /**
559
+ * The field that caused the violation (if applicable)
560
+ */
561
+ field?: string;
562
+ }
563
+
564
+ /**
565
+ * A security warning (doesn't block execution)
566
+ */
567
+ export interface ISecurityWarning {
568
+ /**
569
+ * Type of warning
570
+ */
571
+ type:
572
+ | 'approaching_depth_limit'
573
+ | 'approaching_clause_limit'
574
+ | 'complex_query';
575
+
576
+ /**
577
+ * Human-readable message
578
+ */
579
+ message: string;
580
+
581
+ /**
582
+ * Current value
583
+ */
584
+ current?: number;
585
+
586
+ /**
587
+ * Limit value
588
+ */
589
+ limit?: number;
590
+ }
591
+
592
+ /**
593
+ * A token in the parsed query (term or operator)
594
+ */
595
+ export type IQueryToken = IQueryTermToken | IQueryOperatorToken;
596
+
597
+ /**
598
+ * A term token (field:value pair or bare value)
599
+ */
600
+ export interface IQueryTermToken {
601
+ type: 'term';
602
+ key: string | null;
603
+ operator: string | null;
604
+ value: string | null;
605
+ startPosition: number;
606
+ endPosition: number;
607
+ raw: string;
608
+ }
609
+
610
+ /**
611
+ * A logical operator token (AND, OR, NOT)
612
+ */
613
+ export interface IQueryOperatorToken {
614
+ type: 'operator';
615
+ operator: 'AND' | 'OR' | 'NOT';
616
+ startPosition: number;
617
+ endPosition: number;
618
+ raw: string;
619
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Virtual Fields module exports
3
+ */
4
+
5
+ export * from './types';
6
+ export * from './resolver';