@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
@@ -0,0 +1,160 @@
1
+ /**
2
+ * Type definitions for Virtual Fields support
3
+ */
4
+ import { QueryExpression, IComparisonExpression, ComparisonOperator } from '../parser/types';
5
+ /**
6
+ * Base interface for query context.
7
+ * Users can extend this interface with their own context properties.
8
+ */
9
+ export interface IQueryContext {
10
+ [key: string]: unknown;
11
+ }
12
+ /**
13
+ * Input provided to a virtual field resolver.
14
+ * Contains the parsed field, operator, and value from the query.
15
+ */
16
+ export interface IVirtualFieldInput {
17
+ /**
18
+ * The virtual field name (e.g., "my")
19
+ */
20
+ field: string;
21
+ /**
22
+ * The comparison operator used (e.g., ":", ">", "<", etc.)
23
+ * Maps to ComparisonOperator type
24
+ */
25
+ operator: string;
26
+ /**
27
+ * The value provided in the query
28
+ */
29
+ value: string;
30
+ }
31
+ /**
32
+ * Helper type to filter out index signatures from a type
33
+ */
34
+ type KnownKeys<T> = {
35
+ [K in keyof T]: string extends K ? never : number extends K ? never : K;
36
+ } extends {
37
+ [_ in keyof T]: infer U;
38
+ } ? U : never;
39
+ /**
40
+ * Utility type to extract all field names from a schema.
41
+ * Recursively extracts field names from nested tables, excluding index signatures.
42
+ */
43
+ export type AllSchemaFields<TSchema extends Record<string, object>> = {
44
+ [K in KnownKeys<TSchema>]: TSchema[K] extends {
45
+ [key: string]: unknown;
46
+ } ? keyof TSchema[K] & string : never;
47
+ }[KnownKeys<TSchema>];
48
+ /**
49
+ * Type-safe mapping from allowed values to schema fields.
50
+ * Ensures all keys in TKeys map to valid fields in the schema.
51
+ */
52
+ export type SchemaFieldMap<TKeys extends string, TSchema extends Record<string, object>> = Record<TKeys, AllSchemaFields<TSchema>>;
53
+ /**
54
+ * Helper functions provided to virtual field resolvers.
55
+ *
56
+ * Note: The fields() method is generic at the method level, not the interface level.
57
+ * This allows TypeScript to infer TValues from the mapping object passed at call-time,
58
+ * eliminating the need for type assertions while maintaining full type safety.
59
+ */
60
+ export interface IResolverHelpers<TSchema extends Record<string, object>> {
61
+ /**
62
+ * Type-safe field mapping helper.
63
+ * Ensures all allowedValues are mapped to valid schema fields.
64
+ *
65
+ * The generic TValues parameter is inferred from the keys in the mapping object,
66
+ * providing full type safety without requiring explicit type annotations.
67
+ *
68
+ * @example
69
+ * const fieldMap = fields({
70
+ * assigned: 'assignee_id',
71
+ * created: 'creator_id'
72
+ * });
73
+ * // TypeScript infers TValues as 'assigned' | 'created'
74
+ */
75
+ fields: <TValues extends string>(mapping: SchemaFieldMap<TValues, TSchema>) => SchemaFieldMap<TValues, TSchema>;
76
+ }
77
+ /**
78
+ * Schema-constrained comparison expression.
79
+ * Ensures field names are valid schema fields.
80
+ */
81
+ export interface ITypedComparisonExpression<TFields extends string = string> extends Omit<IComparisonExpression, 'field'> {
82
+ type: 'comparison';
83
+ field: TFields;
84
+ operator: ComparisonOperator;
85
+ value: string | number | boolean | null | Array<string | number | boolean | null>;
86
+ }
87
+ /**
88
+ * Schema-constrained query expression.
89
+ * Can be a comparison or logical expression with typed fields.
90
+ */
91
+ export type ITypedQueryExpression<TFields extends string = string> = ITypedComparisonExpression<TFields> | QueryExpression;
92
+ /**
93
+ * Definition for a virtual field.
94
+ * Configures how a virtual field should be resolved at query execution time.
95
+ */
96
+ export interface IVirtualFieldDefinition<TSchema extends Record<string, object>, TContext extends IQueryContext = IQueryContext, TValues extends string = string> {
97
+ /**
98
+ * Allowed values for this virtual field.
99
+ * Use `as const` for type inference.
100
+ *
101
+ * @example
102
+ * allowedValues: ['assigned', 'created', 'watching'] as const
103
+ */
104
+ allowedValues: readonly TValues[];
105
+ /**
106
+ * Whether to allow comparison operators beyond `:` (equality).
107
+ * If false, only `:` is allowed. If true, `:>`, `:<`, etc. are permitted.
108
+ *
109
+ * @default false
110
+ */
111
+ allowOperators?: boolean;
112
+ /**
113
+ * Resolve the virtual field to a real query expression.
114
+ * The `fields` helper ensures type-safe field references.
115
+ *
116
+ * @param input - The parsed virtual field input (field, operator, value)
117
+ * @param context - Runtime context provided by createContext()
118
+ * @param helpers - Helper functions including type-safe fields() helper
119
+ * @returns A query expression that replaces the virtual field
120
+ *
121
+ * @example
122
+ * resolve: (input, ctx, { fields }) => {
123
+ * const fieldMap = fields({
124
+ * assigned: 'assignee_id',
125
+ * created: 'creator_id'
126
+ * });
127
+ * return {
128
+ * type: 'comparison',
129
+ * field: fieldMap[input.value],
130
+ * operator: '==',
131
+ * value: ctx.currentUserId
132
+ * };
133
+ * }
134
+ */
135
+ resolve: (input: IVirtualFieldInput & {
136
+ value: TValues;
137
+ }, context: TContext, helpers: IResolverHelpers<TSchema>) => ITypedQueryExpression<AllSchemaFields<TSchema>>;
138
+ /**
139
+ * Human-readable description (for autocomplete UI).
140
+ * Optional metadata for documentation and tooling.
141
+ */
142
+ description?: string;
143
+ /**
144
+ * Descriptions for each allowed value (for autocomplete UI).
145
+ * Optional metadata for documentation and tooling.
146
+ */
147
+ valueDescriptions?: Partial<Record<TValues, string>>;
148
+ }
149
+ /**
150
+ * Configuration for all virtual fields in a QueryKit instance.
151
+ *
152
+ * Note: Uses a flexible type for the values to allow each virtual field definition
153
+ * to have its own specific TValues type (e.g., 'assigned' | 'created' for one field,
154
+ * 'high' | 'low' for another). The IResolverHelpers.fields() method infers these
155
+ * types at call-time, maintaining type safety without needing explicit annotations.
156
+ */
157
+ export type VirtualFieldsConfig<TSchema extends Record<string, object> = Record<string, object>, TContext extends IQueryContext = IQueryContext> = {
158
+ [fieldName: string]: IVirtualFieldDefinition<TSchema, TContext, string>;
159
+ };
160
+ export {};
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ /**
3
+ * Type definitions for Virtual Fields support
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -4,7 +4,7 @@ import { useEffect, useMemo, useState, useCallback, useRef, JSX } from 'react';
4
4
  import { drizzle } from 'drizzle-orm/pglite';
5
5
  import { usePGlite } from '@electric-sql/pglite-react';
6
6
  import { pgTable, serial, text, integer, boolean } from 'drizzle-orm/pg-core';
7
- import { InferSelectModel, sql, SQLWrapper } from 'drizzle-orm';
7
+ import { InferSelectModel, sql } from 'drizzle-orm';
8
8
  import { Card, CardContent } from '@/components/ui/card';
9
9
  import {
10
10
  Table,
@@ -17,9 +17,9 @@ import {
17
17
  import {
18
18
  QueryParser,
19
19
  SqlTranslator,
20
- DrizzleAdapter,
21
- createQueryKit,
22
- IDrizzleAdapterOptions
20
+ createDrizzleQueryKit,
21
+ ISecurityOptions,
22
+ IQueryStructure
23
23
  } from '@gblikas/querykit';
24
24
  import { Copy, Check, Search, ChevronUp, FileCode, X } from 'lucide-react';
25
25
  import { Light as SyntaxHighlighter } from 'react-syntax-highlighter';
@@ -32,7 +32,12 @@ import { toast } from 'sonner';
32
32
  import Aurora from '@/components/reactbits/blocks/Backgrounds/Aurora/Aurora';
33
33
  import { PGlite } from '@electric-sql/pglite';
34
34
  import { useViewportInfo } from './hooks/use-viewport-info';
35
- import { cn, trackQueryKitIssue, trackQueryKitUsage, trackQueryKitSpeed } from '@/lib/utils';
35
+ import {
36
+ cn,
37
+ trackQueryKitIssue,
38
+ trackQueryKitUsage,
39
+ trackQueryKitSpeed
40
+ } from '@/lib/utils';
36
41
  import {
37
42
  Drawer,
38
43
  DrawerTrigger,
@@ -70,7 +75,7 @@ const highlightQueryHtml = (input: string): string => {
70
75
  .join('');
71
76
  };
72
77
 
73
- const INSTALL_SNIPPET = `pnpm i @gblikas/querykit drizzle-orm`;
78
+ const INSTALL_SNIPPET = `pnpm i @gblikas/querykit@0.3.0 drizzle-orm`;
74
79
 
75
80
  const SCHEMA_SNIPPET = `// schema.ts
76
81
  import { serial, text, pgTable } from 'drizzle-orm/pg-core';
@@ -86,25 +91,30 @@ export type SelectUser = InferSelectModel<typeof users>;
86
91
  `;
87
92
 
88
93
  const QUERYKIT_SNIPPET = `// querykit.ts
89
- import { createQueryKit } from 'querykit';
90
- import { drizzleAdapter } from 'querykit/adapters/drizzle';
94
+ import { createDrizzleQueryKit } from '@gblikas/querykit';
95
+ import { db } from './db';
91
96
  import { users } from './schema';
92
97
 
93
- export const qk = createQueryKit({
94
- adapter: drizzleAdapter,
98
+ // New 0.3.0: createDrizzleQueryKit combines adapter & security config
99
+ export const qk = createDrizzleQueryKit({
100
+ db,
95
101
  schema: { users },
102
+ security: {
103
+ maxQueryDepth: 5,
104
+ maxClauseCount: 20,
105
+ sanitizeWildcards: true,
106
+ },
96
107
  });
97
108
 
98
109
  // example.ts
99
110
  import { qk } from './querykit';
100
111
 
101
- const query = qk
112
+ const results = await qk
102
113
  .query('users')
103
114
  .where('status:done AND name:"John *"')
104
115
  .orderBy('name', 'asc')
105
- .limit(10);
106
-
107
- const results = await query.execute();
116
+ .limit(10)
117
+ .execute();
108
118
  `;
109
119
 
110
120
  const tasks = pgTable('tasks', {
@@ -132,6 +142,9 @@ export default function Home(): JSX.Element {
132
142
  const [, setLastExecutionMs] = useState<number | null>(null);
133
143
  const [rowsScanned, setRowsScanned] = useState<number | null>(null);
134
144
  const [operatorsUsed, setOperatorsUsed] = useState<string[]>([]);
145
+ const [queryStructure, setQueryStructure] = useState<IQueryStructure | null>(
146
+ null
147
+ );
135
148
  const [usedQueryKit, setUsedQueryKit] = useState<boolean>(false);
136
149
  const [, setExplainJson] = useState<string | null>(null);
137
150
  const [, setPlanningTimeMs] = useState<number | null>(null);
@@ -309,23 +322,33 @@ export default function Home(): JSX.Element {
309
322
  void seed();
310
323
  }, [db]);
311
324
 
325
+ // Security configuration for QueryKit 0.3.0
326
+ const securityOptions: ISecurityOptions = useMemo(
327
+ () => ({
328
+ maxQueryDepth: 5,
329
+ maxClauseCount: 20,
330
+ allowDotNotation: false, // Disable dot notation for simple flat schema
331
+ sanitizeWildcards: true
332
+ }),
333
+ []
334
+ );
335
+
312
336
  const parser = useMemo(() => new QueryParser(), []);
313
337
  const sqlTranslator = useMemo(
314
338
  () => new SqlTranslator({ useParameters: false }),
315
339
  []
316
340
  );
317
- const qk = useMemo(() => {
318
- const adapter = new DrizzleAdapter();
319
- const iDrizzleAdataperOptions: IDrizzleAdapterOptions = {
320
- db: db as unknown as PGlite,
321
- schema: { tasks } as unknown as Record<string, Record<string, SQLWrapper>>
322
- };
323
- adapter.initialize(iDrizzleAdataperOptions);
324
- return createQueryKit({
325
- adapter,
326
- schema: { tasks } as unknown as Record<string, Record<string, SQLWrapper>>
327
- });
328
- }, [db]);
341
+
342
+ // Use the new createDrizzleQueryKit factory (0.3.0 feature)
343
+ const qk = useMemo(
344
+ () =>
345
+ createDrizzleQueryKit({
346
+ db,
347
+ schema: { tasks },
348
+ security: securityOptions
349
+ }),
350
+ [db, securityOptions]
351
+ );
329
352
 
330
353
  // Note: Execute via QueryKit fluent API (Drizzle adapter under the hood)
331
354
 
@@ -341,6 +364,7 @@ export default function Home(): JSX.Element {
341
364
  setIsInputFocused(false);
342
365
  inputRef.current?.blur();
343
366
  setOperatorsUsed([]);
367
+ setQueryStructure(null);
344
368
  setExplainJson(null);
345
369
  setPlanningTimeMs(null);
346
370
  setExecutionTimeMs(null);
@@ -402,49 +426,77 @@ export default function Home(): JSX.Element {
402
426
  if (searchQuery.trim()) {
403
427
  try {
404
428
  const parseStart = performance.now();
405
- const ast = parser.parse(searchQuery);
406
- const translated = sqlTranslator.translate(ast) as
407
- | string
408
- | { sql: string; params: unknown[] };
409
- localParseTranslateMs = performance.now() - parseStart;
410
- setParseTranslateMs(localParseTranslateMs);
411
- whereSql =
412
- typeof translated === 'string' ? translated : translated.sql;
413
- mockSQL += ` WHERE ${whereSql}`;
414
- // Robust operator detection with word boundaries and precedence
415
- const extractOperators = (sqlText: string): string[] => {
416
- const found = new Set<string>();
417
- const upper = sqlText.toUpperCase();
418
- // Keyword operators (use word boundaries)
419
- const keywordOps: Array<[string, RegExp]> = [
420
- ['ILIKE', /\bILIKE\b/i],
421
- ['LIKE', /\bLIKE\b/i],
422
- ['AND', /\bAND\b/i],
423
- ['OR', /\bOR\b/i],
424
- ['NOT', /\bNOT\b/i],
425
- ['IN', /\bIN\b/i],
426
- ['BETWEEN', /\bBETWEEN\b/i]
427
- ];
428
- for (const [name, re] of keywordOps) {
429
- if (re.test(sqlText)) found.add(name);
429
+ // Use parseWithContext for enhanced query analysis (0.3.0 feature)
430
+ const parseResult = parser.parseWithContext(searchQuery, {
431
+ schema: {
432
+ title: { type: 'string', description: 'Task title' },
433
+ status: {
434
+ type: 'string',
435
+ description: 'Task status',
436
+ allowedValues: ['todo', 'doing', 'done']
437
+ },
438
+ priority: { type: 'number', description: 'Priority level' },
439
+ completed: { type: 'boolean', description: 'Is completed' }
430
440
  }
431
- // Symbol operators: match longest first and remove before shorter matches
432
- let temp = upper;
433
- const consume = (re: RegExp, label: string): void => {
434
- if (re.test(temp)) {
435
- found.add(label);
436
- temp = temp.replace(re, ' ');
437
- }
438
- };
439
- consume(/>=/g, '>=');
440
- consume(/<=/g, '<=');
441
- consume(/!=/g, '!=');
442
- consume(/=/g, '=');
443
- consume(/>/g, '>');
444
- consume(/</g, '<');
445
- return Array.from(found);
446
- };
447
- detectedOperators = extractOperators(whereSql);
441
+ });
442
+
443
+ // Set query structure for UI display
444
+ setQueryStructure(parseResult.structure);
445
+
446
+ if (parseResult.success && parseResult.ast) {
447
+ const translated = sqlTranslator.translate(parseResult.ast) as
448
+ | string
449
+ | { sql: string; params: unknown[] };
450
+ localParseTranslateMs = performance.now() - parseStart;
451
+ setParseTranslateMs(localParseTranslateMs);
452
+ whereSql =
453
+ typeof translated === 'string' ? translated : translated.sql;
454
+ mockSQL += ` WHERE ${whereSql}`;
455
+
456
+ // Use referenced fields from structure (0.3.0 feature)
457
+ detectedOperators = [];
458
+ if (parseResult.structure.operatorCount > 0) {
459
+ // Extract operators from the SQL for display
460
+ const extractOperators = (sqlText: string): string[] => {
461
+ const found = new Set<string>();
462
+ const keywordOps: Array<[string, RegExp]> = [
463
+ ['ILIKE', /\bILIKE\b/i],
464
+ ['LIKE', /\bLIKE\b/i],
465
+ ['AND', /\bAND\b/i],
466
+ ['OR', /\bOR\b/i],
467
+ ['NOT', /\bNOT\b/i],
468
+ ['IN', /\bIN\b/i],
469
+ ['BETWEEN', /\bBETWEEN\b/i]
470
+ ];
471
+ for (const [name, re] of keywordOps) {
472
+ if (re.test(sqlText)) found.add(name);
473
+ }
474
+ let temp = sqlText.toUpperCase();
475
+ const consume = (re: RegExp, label: string): void => {
476
+ if (re.test(temp)) {
477
+ found.add(label);
478
+ temp = temp.replace(re, ' ');
479
+ }
480
+ };
481
+ consume(/>=/g, '>=');
482
+ consume(/<=/g, '<=');
483
+ consume(/!=/g, '!=');
484
+ consume(/=/g, '=');
485
+ consume(/>/g, '>');
486
+ consume(/</g, '<');
487
+ return Array.from(found);
488
+ };
489
+ detectedOperators = extractOperators(whereSql);
490
+ } else {
491
+ // Simple query - just detect from SQL
492
+ if (/ILIKE/i.test(whereSql)) detectedOperators.push('ILIKE');
493
+ if (/=/i.test(whereSql) && !/!=/i.test(whereSql))
494
+ detectedOperators.push('=');
495
+ }
496
+ } else {
497
+ // Parse failed - fall back to ILIKE search
498
+ throw new Error(parseResult.error?.message || 'Parse failed');
499
+ }
448
500
  } catch (error) {
449
501
  void trackQueryKitIssue({
450
502
  errorName: (error as Error)?.name ?? 'UnknownError',
@@ -1008,25 +1060,77 @@ export default function Home(): JSX.Element {
1008
1060
  )}
1009
1061
  </div>
1010
1062
  {!isShortViewport ? (
1011
- <div className="mt-3">
1012
- <div className="text-xs text-muted-foreground mb-1">
1013
- Detected operators
1014
- </div>
1015
- {operatorsUsed.length ? (
1016
- <div className="flex flex-wrap gap-2">
1017
- {operatorsUsed.map(op => (
1018
- <span
1019
- key={op}
1020
- className="inline-flex items-center rounded-full border bg-muted px-2 py-0.5 text-xs font-medium"
1021
- >
1022
- {op}
1023
- </span>
1024
- ))}
1063
+ <>
1064
+ <div className="mt-3 grid grid-cols-2 gap-3">
1065
+ <div>
1066
+ <div className="text-xs text-muted-foreground mb-1">
1067
+ Detected operators
1068
+ </div>
1069
+ {operatorsUsed.length ? (
1070
+ <div className="flex flex-wrap gap-2">
1071
+ {operatorsUsed.map(op => (
1072
+ <span
1073
+ key={op}
1074
+ className="inline-flex items-center rounded-full border bg-muted px-2 py-0.5 text-xs font-medium"
1075
+ >
1076
+ {op}
1077
+ </span>
1078
+ ))}
1079
+ </div>
1080
+ ) : (
1081
+ <div className="text-xs text-muted-foreground">
1082
+ -
1083
+ </div>
1084
+ )}
1025
1085
  </div>
1026
- ) : (
1027
- <div className="text-xs text-muted-foreground">-</div>
1028
- )}
1029
- </div>
1086
+ {queryStructure && (
1087
+ <div>
1088
+ <div className="text-xs text-muted-foreground mb-1">
1089
+ Query complexity (0.3.0)
1090
+ </div>
1091
+ <div className="flex flex-wrap gap-2">
1092
+ <span
1093
+ className={cn(
1094
+ 'inline-flex items-center rounded-full border px-2 py-0.5 text-xs font-medium',
1095
+ queryStructure.complexity === 'simple' &&
1096
+ 'bg-green-500/10 text-green-600 border-green-500/30',
1097
+ queryStructure.complexity === 'moderate' &&
1098
+ 'bg-yellow-500/10 text-yellow-600 border-yellow-500/30',
1099
+ queryStructure.complexity === 'complex' &&
1100
+ 'bg-red-500/10 text-red-600 border-red-500/30'
1101
+ )}
1102
+ >
1103
+ {queryStructure.complexity}
1104
+ </span>
1105
+ <span className="inline-flex items-center rounded-full border bg-muted px-2 py-0.5 text-xs font-medium">
1106
+ depth: {queryStructure.depth}
1107
+ </span>
1108
+ <span className="inline-flex items-center rounded-full border bg-muted px-2 py-0.5 text-xs font-medium">
1109
+ clauses: {queryStructure.clauseCount}
1110
+ </span>
1111
+ </div>
1112
+ </div>
1113
+ )}
1114
+ </div>
1115
+ {queryStructure &&
1116
+ queryStructure.referencedFields.length > 0 && (
1117
+ <div className="mt-3">
1118
+ <div className="text-xs text-muted-foreground mb-1">
1119
+ Referenced fields
1120
+ </div>
1121
+ <div className="flex flex-wrap gap-2">
1122
+ {queryStructure.referencedFields.map(field => (
1123
+ <span
1124
+ key={field}
1125
+ className="inline-flex items-center rounded-full border bg-blue-500/10 text-blue-600 border-blue-500/30 px-2 py-0.5 text-xs font-medium"
1126
+ >
1127
+ {field}
1128
+ </span>
1129
+ ))}
1130
+ </div>
1131
+ </div>
1132
+ )}
1133
+ </>
1030
1134
  ) : (
1031
1135
  <div className="mt-3 text-xs text-muted-foreground">
1032
1136
  View on larger screen for more details
@@ -11,7 +11,7 @@
11
11
  "dependencies": {
12
12
  "@electric-sql/pglite": "^0.3.7",
13
13
  "@electric-sql/pglite-react": "^0.2.25",
14
- "@gblikas/querykit": "^0.0.0",
14
+ "@gblikas/querykit": "^0.3.0",
15
15
  "@radix-ui/react-dialog": "^1.1.15",
16
16
  "@radix-ui/react-hover-card": "^1.1.15",
17
17
  "@vercel/analytics": "^1.5.0",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gblikas/querykit",
3
- "version": "0.2.0",
3
+ "version": "0.4.0",
4
4
  "description": "A comprehensive query toolkit for TypeScript that simplifies building and executing data queries across different environments",
5
5
  "repository": {
6
6
  "type": "git",
@@ -43,7 +43,7 @@
43
43
  "lint-staged": "^15.5.1",
44
44
  "prettier": "^3.2.5",
45
45
  "ts-jest": "^29.1.2",
46
- "typescript": "^5.3.3"
46
+ "typescript": "^5.9.3"
47
47
  },
48
48
  "scripts": {
49
49
  "build": "tsc --outDir dist",
@@ -9,6 +9,7 @@ import { IAdapter, IAdapterOptions, IQueryExecutionOptions } from '../types';
9
9
  import { QueryExpression } from '../../parser/types';
10
10
  import { SQL, SQLWrapper, asc, desc, sql } from 'drizzle-orm';
11
11
  import { createQueryKit, QueryKit } from '../../index';
12
+ import { IQueryContext } from '../../virtual-fields';
12
13
  /**
13
14
  * Type for Drizzle ORM database instance
14
15
  */
@@ -91,8 +92,7 @@ export class DrizzleAdapterError extends Error {
91
92
  */
92
93
  export class DrizzleAdapter<
93
94
  TSchema extends Record<string, unknown> = Record<string, unknown>
94
- > implements IAdapter<IDrizzleAdapterOptions<TSchema>>
95
- {
95
+ > implements IAdapter<IDrizzleAdapterOptions<TSchema>> {
96
96
  private db!: unknown;
97
97
  private schema!: TSchema;
98
98
  private translator!: DrizzleTranslator;
@@ -291,7 +291,7 @@ export function createDrizzleQueryKit<
291
291
 
292
292
  type RowMap = RowMapFromDrizzleSchema<TSchema>;
293
293
 
294
- return createQueryKit<TSchema, RowMap>({
294
+ return createQueryKit<TSchema, IQueryContext, RowMap>({
295
295
  adapter,
296
296
  schema: args.schema as unknown as TSchema,
297
297
  security: args.security