@gblikas/querykit 0.0.0 → 0.2.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.
- package/.github/workflows/publish.yml +5 -7
- package/README.md +76 -0
- package/dist/parser/parser.d.ts +34 -0
- package/dist/parser/parser.js +164 -6
- package/dist/security/types.d.ts +48 -0
- package/dist/security/types.js +2 -0
- package/dist/security/validator.d.ts +35 -0
- package/dist/security/validator.js +108 -0
- package/examples/qk-next/app/globals.css +23 -0
- package/examples/qk-next/app/hooks/use-viewport-info.ts +89 -0
- package/examples/qk-next/app/layout.tsx +26 -7
- package/examples/qk-next/app/page.tsx +423 -121
- package/examples/qk-next/lib/utils.ts +74 -0
- package/examples/qk-next/package.json +5 -3
- package/examples/qk-next/pnpm-lock.yaml +112 -47
- package/package.json +5 -1
- package/src/parser/parser.test.ts +209 -1
- package/src/parser/parser.ts +234 -25
- package/src/security/types.ts +52 -0
- package/src/security/validator.test.ts +368 -0
- package/src/security/validator.ts +117 -0
|
@@ -1,6 +1,80 @@
|
|
|
1
1
|
import { clsx, type ClassValue } from 'clsx';
|
|
2
2
|
import { twMerge } from 'tailwind-merge';
|
|
3
|
+
// Use the signature from @vercel/analytics: Record<string, AllowedPropertyValues>
|
|
4
|
+
type AnalyticsEventProps = Record<string, string | number | boolean | null>;
|
|
3
5
|
|
|
4
6
|
export function cn(...inputs: ClassValue[]): string {
|
|
5
7
|
return twMerge(clsx(inputs));
|
|
6
8
|
}
|
|
9
|
+
|
|
10
|
+
// Lightweight telemetry helpers using Vercel Analytics custom events
|
|
11
|
+
// See: https://vercel.com/docs/analytics#events
|
|
12
|
+
export interface IQueryKitIssueEvent {
|
|
13
|
+
errorName: string;
|
|
14
|
+
errorMessage: string;
|
|
15
|
+
stage: 'parse' | 'translate' | 'execute' | 'explain' | 'seed' | 'search';
|
|
16
|
+
query?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export async function trackQueryKitIssue(event: IQueryKitIssueEvent): Promise<void> {
|
|
20
|
+
try {
|
|
21
|
+
const { track } = await import('@vercel/analytics');
|
|
22
|
+
const props: AnalyticsEventProps = {
|
|
23
|
+
errorName: event.errorName,
|
|
24
|
+
errorMessage: event.errorMessage,
|
|
25
|
+
stage: event.stage,
|
|
26
|
+
query: event.query ?? null
|
|
27
|
+
};
|
|
28
|
+
track('qk_issue', props);
|
|
29
|
+
} catch {
|
|
30
|
+
// no-op in dev or if analytics not available
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface IQueryKitUsageEvent {
|
|
35
|
+
usedQueryKit: boolean;
|
|
36
|
+
operators: string[];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export async function trackQueryKitUsage(event: IQueryKitUsageEvent): Promise<void> {
|
|
40
|
+
try {
|
|
41
|
+
const { track } = await import('@vercel/analytics');
|
|
42
|
+
const props: AnalyticsEventProps = {
|
|
43
|
+
usedQueryKit: event.usedQueryKit,
|
|
44
|
+
operators: (event.operators || []).join(',')
|
|
45
|
+
};
|
|
46
|
+
track('qk_usage', props);
|
|
47
|
+
} catch {
|
|
48
|
+
// no-op
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export interface IQueryKitSpeedEvent {
|
|
53
|
+
usedQueryKit: boolean;
|
|
54
|
+
baselineMs?: number | null;
|
|
55
|
+
parseTranslateMs?: number | null;
|
|
56
|
+
explainLatencyMs?: number | null;
|
|
57
|
+
dbExecutionMs?: number | null;
|
|
58
|
+
totalMs?: number | null;
|
|
59
|
+
rowsScanned?: number | null;
|
|
60
|
+
results?: number | null;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export async function trackQueryKitSpeed(event: IQueryKitSpeedEvent): Promise<void> {
|
|
64
|
+
try {
|
|
65
|
+
const { track } = await import('@vercel/analytics');
|
|
66
|
+
const props: AnalyticsEventProps = {
|
|
67
|
+
usedQueryKit: event.usedQueryKit,
|
|
68
|
+
baselineMs: event.baselineMs ?? null,
|
|
69
|
+
parseTranslateMs: event.parseTranslateMs ?? null,
|
|
70
|
+
explainLatencyMs: event.explainLatencyMs ?? null,
|
|
71
|
+
dbExecutionMs: event.dbExecutionMs ?? null,
|
|
72
|
+
totalMs: event.totalMs ?? null,
|
|
73
|
+
rowsScanned: event.rowsScanned ?? null,
|
|
74
|
+
results: event.results ?? null
|
|
75
|
+
};
|
|
76
|
+
track('qk_speed', props);
|
|
77
|
+
} catch {
|
|
78
|
+
// no-op
|
|
79
|
+
}
|
|
80
|
+
}
|
|
@@ -11,15 +11,17 @@
|
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@electric-sql/pglite": "^0.3.7",
|
|
13
13
|
"@electric-sql/pglite-react": "^0.2.25",
|
|
14
|
-
"@gblikas/querykit": "
|
|
14
|
+
"@gblikas/querykit": "^0.0.0",
|
|
15
15
|
"@radix-ui/react-dialog": "^1.1.15",
|
|
16
16
|
"@radix-ui/react-hover-card": "^1.1.15",
|
|
17
|
+
"@vercel/analytics": "^1.5.0",
|
|
18
|
+
"@vercel/speed-insights": "^1.2.0",
|
|
17
19
|
"class-variance-authority": "^0.7.1",
|
|
18
20
|
"clsx": "^2.1.1",
|
|
19
21
|
"cmdk": "^1.0.0",
|
|
20
22
|
"drizzle-orm": "^0.44.4",
|
|
21
23
|
"lucide-react": "^0.539.0",
|
|
22
|
-
"next": "15.4.
|
|
24
|
+
"next": "15.4.10",
|
|
23
25
|
"next-themes": "^0.4.6",
|
|
24
26
|
"ogl": "^1.0.11",
|
|
25
27
|
"prism-react-renderer": "^2.4.1",
|
|
@@ -45,4 +47,4 @@
|
|
|
45
47
|
"tw-animate-css": "^1.3.6",
|
|
46
48
|
"typescript": "^5"
|
|
47
49
|
}
|
|
48
|
-
}
|
|
50
|
+
}
|
|
@@ -15,14 +15,20 @@ importers:
|
|
|
15
15
|
specifier: ^0.2.25
|
|
16
16
|
version: 0.2.25(@electric-sql/pglite@0.3.7)(react@19.1.0)
|
|
17
17
|
'@gblikas/querykit':
|
|
18
|
-
specifier:
|
|
19
|
-
version:
|
|
18
|
+
specifier: ^0.0.0
|
|
19
|
+
version: 0.0.0(@electric-sql/pglite@0.3.7)(@types/react@19.1.10)(react@19.1.0)
|
|
20
20
|
'@radix-ui/react-dialog':
|
|
21
21
|
specifier: ^1.1.15
|
|
22
22
|
version: 1.1.15(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
|
23
23
|
'@radix-ui/react-hover-card':
|
|
24
24
|
specifier: ^1.1.15
|
|
25
25
|
version: 1.1.15(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
|
26
|
+
'@vercel/analytics':
|
|
27
|
+
specifier: ^1.5.0
|
|
28
|
+
version: 1.5.0(next@15.4.10(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0)
|
|
29
|
+
'@vercel/speed-insights':
|
|
30
|
+
specifier: ^1.2.0
|
|
31
|
+
version: 1.2.0(next@15.4.10(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0)
|
|
26
32
|
class-variance-authority:
|
|
27
33
|
specifier: ^0.7.1
|
|
28
34
|
version: 0.7.1
|
|
@@ -39,8 +45,8 @@ importers:
|
|
|
39
45
|
specifier: ^0.539.0
|
|
40
46
|
version: 0.539.0(react@19.1.0)
|
|
41
47
|
next:
|
|
42
|
-
specifier: 15.4.
|
|
43
|
-
version: 15.4.
|
|
48
|
+
specifier: 15.4.10
|
|
49
|
+
version: 15.4.10(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
|
44
50
|
next-themes:
|
|
45
51
|
specifier: ^0.4.6
|
|
46
52
|
version: 0.4.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
|
@@ -489,8 +495,8 @@ packages:
|
|
|
489
495
|
'@floating-ui/utils@0.2.10':
|
|
490
496
|
resolution: {integrity: sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==}
|
|
491
497
|
|
|
492
|
-
'@gblikas/querykit@
|
|
493
|
-
resolution: {
|
|
498
|
+
'@gblikas/querykit@0.0.0':
|
|
499
|
+
resolution: {integrity: sha512-kmCkPhc/YCmPJ3/WNGBEEbnHRMNeVrvWDvn3w6xi1cVOagN5Gff7s7sy6vqpXZ5fnhB3XSSnD0oj4KNDLZXdIA==}
|
|
494
500
|
|
|
495
501
|
'@humanfs/core@0.19.1':
|
|
496
502
|
resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==}
|
|
@@ -657,56 +663,56 @@ packages:
|
|
|
657
663
|
'@napi-rs/wasm-runtime@0.2.12':
|
|
658
664
|
resolution: {integrity: sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==}
|
|
659
665
|
|
|
660
|
-
'@next/env@15.4.
|
|
661
|
-
resolution: {integrity: sha512-
|
|
666
|
+
'@next/env@15.4.10':
|
|
667
|
+
resolution: {integrity: sha512-knhmoJ0Vv7VRf6pZEPSnciUG1S4bIhWx+qTYBW/AjxEtlzsiNORPk8sFDCEvqLfmKuey56UB9FL1UdHEV3uBrg==}
|
|
662
668
|
|
|
663
669
|
'@next/eslint-plugin-next@15.4.6':
|
|
664
670
|
resolution: {integrity: sha512-2NOu3ln+BTcpnbIDuxx6MNq+pRrCyey4WSXGaJIyt0D2TYicHeO9QrUENNjcf673n3B1s7hsiV5xBYRCK1Q8kA==}
|
|
665
671
|
|
|
666
|
-
'@next/swc-darwin-arm64@15.4.
|
|
667
|
-
resolution: {integrity: sha512-
|
|
672
|
+
'@next/swc-darwin-arm64@15.4.8':
|
|
673
|
+
resolution: {integrity: sha512-Pf6zXp7yyQEn7sqMxur6+kYcywx5up1J849psyET7/8pG2gQTVMjU3NzgIt8SeEP5to3If/SaWmaA6H6ysBr1A==}
|
|
668
674
|
engines: {node: '>= 10'}
|
|
669
675
|
cpu: [arm64]
|
|
670
676
|
os: [darwin]
|
|
671
677
|
|
|
672
|
-
'@next/swc-darwin-x64@15.4.
|
|
673
|
-
resolution: {integrity: sha512-
|
|
678
|
+
'@next/swc-darwin-x64@15.4.8':
|
|
679
|
+
resolution: {integrity: sha512-xla6AOfz68a6kq3gRQccWEvFC/VRGJmA/QuSLENSO7CZX5WIEkSz7r1FdXUjtGCQ1c2M+ndUAH7opdfLK1PQbw==}
|
|
674
680
|
engines: {node: '>= 10'}
|
|
675
681
|
cpu: [x64]
|
|
676
682
|
os: [darwin]
|
|
677
683
|
|
|
678
|
-
'@next/swc-linux-arm64-gnu@15.4.
|
|
679
|
-
resolution: {integrity: sha512-
|
|
684
|
+
'@next/swc-linux-arm64-gnu@15.4.8':
|
|
685
|
+
resolution: {integrity: sha512-y3fmp+1Px/SJD+5ntve5QLZnGLycsxsVPkTzAc3zUiXYSOlTPqT8ynfmt6tt4fSo1tAhDPmryXpYKEAcoAPDJw==}
|
|
680
686
|
engines: {node: '>= 10'}
|
|
681
687
|
cpu: [arm64]
|
|
682
688
|
os: [linux]
|
|
683
689
|
|
|
684
|
-
'@next/swc-linux-arm64-musl@15.4.
|
|
685
|
-
resolution: {integrity: sha512-
|
|
690
|
+
'@next/swc-linux-arm64-musl@15.4.8':
|
|
691
|
+
resolution: {integrity: sha512-DX/L8VHzrr1CfwaVjBQr3GWCqNNFgyWJbeQ10Lx/phzbQo3JNAxUok1DZ8JHRGcL6PgMRgj6HylnLNndxn4Z6A==}
|
|
686
692
|
engines: {node: '>= 10'}
|
|
687
693
|
cpu: [arm64]
|
|
688
694
|
os: [linux]
|
|
689
695
|
|
|
690
|
-
'@next/swc-linux-x64-gnu@15.4.
|
|
691
|
-
resolution: {integrity: sha512
|
|
696
|
+
'@next/swc-linux-x64-gnu@15.4.8':
|
|
697
|
+
resolution: {integrity: sha512-9fLAAXKAL3xEIFdKdzG5rUSvSiZTLLTCc6JKq1z04DR4zY7DbAPcRvNm3K1inVhTiQCs19ZRAgUerHiVKMZZIA==}
|
|
692
698
|
engines: {node: '>= 10'}
|
|
693
699
|
cpu: [x64]
|
|
694
700
|
os: [linux]
|
|
695
701
|
|
|
696
|
-
'@next/swc-linux-x64-musl@15.4.
|
|
697
|
-
resolution: {integrity: sha512-
|
|
702
|
+
'@next/swc-linux-x64-musl@15.4.8':
|
|
703
|
+
resolution: {integrity: sha512-s45V7nfb5g7dbS7JK6XZDcapicVrMMvX2uYgOHP16QuKH/JA285oy6HcxlKqwUNaFY/UC6EvQ8QZUOo19cBKSA==}
|
|
698
704
|
engines: {node: '>= 10'}
|
|
699
705
|
cpu: [x64]
|
|
700
706
|
os: [linux]
|
|
701
707
|
|
|
702
|
-
'@next/swc-win32-arm64-msvc@15.4.
|
|
703
|
-
resolution: {integrity: sha512-
|
|
708
|
+
'@next/swc-win32-arm64-msvc@15.4.8':
|
|
709
|
+
resolution: {integrity: sha512-KjgeQyOAq7t/HzAJcWPGA8X+4WY03uSCZ2Ekk98S9OgCFsb6lfBE3dbUzUuEQAN2THbwYgFfxX2yFTCMm8Kehw==}
|
|
704
710
|
engines: {node: '>= 10'}
|
|
705
711
|
cpu: [arm64]
|
|
706
712
|
os: [win32]
|
|
707
713
|
|
|
708
|
-
'@next/swc-win32-x64-msvc@15.4.
|
|
709
|
-
resolution: {integrity: sha512-
|
|
714
|
+
'@next/swc-win32-x64-msvc@15.4.8':
|
|
715
|
+
resolution: {integrity: sha512-Exsmf/+42fWVnLMaZHzshukTBxZrSwuuLKFvqhGHJ+mC1AokqieLY/XzAl3jc/CqhXLqLY3RRjkKJ9YnLPcRWg==}
|
|
710
716
|
engines: {node: '>= 10'}
|
|
711
717
|
cpu: [x64]
|
|
712
718
|
os: [win32]
|
|
@@ -1244,6 +1250,55 @@ packages:
|
|
|
1244
1250
|
cpu: [x64]
|
|
1245
1251
|
os: [win32]
|
|
1246
1252
|
|
|
1253
|
+
'@vercel/analytics@1.5.0':
|
|
1254
|
+
resolution: {integrity: sha512-MYsBzfPki4gthY5HnYN7jgInhAZ7Ac1cYDoRWFomwGHWEX7odTEzbtg9kf/QSo7XEsEAqlQugA6gJ2WS2DEa3g==}
|
|
1255
|
+
peerDependencies:
|
|
1256
|
+
'@remix-run/react': ^2
|
|
1257
|
+
'@sveltejs/kit': ^1 || ^2
|
|
1258
|
+
next: '>= 13'
|
|
1259
|
+
react: ^18 || ^19 || ^19.0.0-rc
|
|
1260
|
+
svelte: '>= 4'
|
|
1261
|
+
vue: ^3
|
|
1262
|
+
vue-router: ^4
|
|
1263
|
+
peerDependenciesMeta:
|
|
1264
|
+
'@remix-run/react':
|
|
1265
|
+
optional: true
|
|
1266
|
+
'@sveltejs/kit':
|
|
1267
|
+
optional: true
|
|
1268
|
+
next:
|
|
1269
|
+
optional: true
|
|
1270
|
+
react:
|
|
1271
|
+
optional: true
|
|
1272
|
+
svelte:
|
|
1273
|
+
optional: true
|
|
1274
|
+
vue:
|
|
1275
|
+
optional: true
|
|
1276
|
+
vue-router:
|
|
1277
|
+
optional: true
|
|
1278
|
+
|
|
1279
|
+
'@vercel/speed-insights@1.2.0':
|
|
1280
|
+
resolution: {integrity: sha512-y9GVzrUJ2xmgtQlzFP2KhVRoCglwfRQgjyfY607aU0hh0Un6d0OUyrJkjuAlsV18qR4zfoFPs/BiIj9YDS6Wzw==}
|
|
1281
|
+
peerDependencies:
|
|
1282
|
+
'@sveltejs/kit': ^1 || ^2
|
|
1283
|
+
next: '>= 13'
|
|
1284
|
+
react: ^18 || ^19 || ^19.0.0-rc
|
|
1285
|
+
svelte: '>= 4'
|
|
1286
|
+
vue: ^3
|
|
1287
|
+
vue-router: ^4
|
|
1288
|
+
peerDependenciesMeta:
|
|
1289
|
+
'@sveltejs/kit':
|
|
1290
|
+
optional: true
|
|
1291
|
+
next:
|
|
1292
|
+
optional: true
|
|
1293
|
+
react:
|
|
1294
|
+
optional: true
|
|
1295
|
+
svelte:
|
|
1296
|
+
optional: true
|
|
1297
|
+
vue:
|
|
1298
|
+
optional: true
|
|
1299
|
+
vue-router:
|
|
1300
|
+
optional: true
|
|
1301
|
+
|
|
1247
1302
|
acorn-jsx@5.3.2:
|
|
1248
1303
|
resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
|
|
1249
1304
|
peerDependencies:
|
|
@@ -2329,8 +2384,8 @@ packages:
|
|
|
2329
2384
|
react: ^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc
|
|
2330
2385
|
react-dom: ^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc
|
|
2331
2386
|
|
|
2332
|
-
next@15.4.
|
|
2333
|
-
resolution: {integrity: sha512-
|
|
2387
|
+
next@15.4.10:
|
|
2388
|
+
resolution: {integrity: sha512-itVlc79QjpKMFMRhP+kbGKaSG/gZM6RCvwhEbwmCNF06CdDiNaoHcbeg0PqkEa2GOcn8KJ0nnc7+yL7EjoYLHQ==}
|
|
2334
2389
|
engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0}
|
|
2335
2390
|
hasBin: true
|
|
2336
2391
|
peerDependencies:
|
|
@@ -2723,6 +2778,7 @@ packages:
|
|
|
2723
2778
|
tar@7.4.3:
|
|
2724
2779
|
resolution: {integrity: sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==}
|
|
2725
2780
|
engines: {node: '>=18'}
|
|
2781
|
+
deprecated: Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exhorbitant rates) by contacting i@izs.me
|
|
2726
2782
|
|
|
2727
2783
|
tinyglobby@0.2.14:
|
|
2728
2784
|
resolution: {integrity: sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==}
|
|
@@ -3097,11 +3153,10 @@ snapshots:
|
|
|
3097
3153
|
|
|
3098
3154
|
'@floating-ui/utils@0.2.10': {}
|
|
3099
3155
|
|
|
3100
|
-
'@gblikas/querykit@
|
|
3156
|
+
'@gblikas/querykit@0.0.0(@electric-sql/pglite@0.3.7)(@types/react@19.1.10)(react@19.1.0)':
|
|
3101
3157
|
dependencies:
|
|
3102
3158
|
drizzle-orm: 0.30.10(@electric-sql/pglite@0.3.7)(@types/react@19.1.10)(react@19.1.0)
|
|
3103
3159
|
liqe: 3.8.3
|
|
3104
|
-
ogl: 1.0.11
|
|
3105
3160
|
transitivePeerDependencies:
|
|
3106
3161
|
- '@aws-sdk/client-rds-data'
|
|
3107
3162
|
- '@cloudflare/workers-types'
|
|
@@ -3258,34 +3313,34 @@ snapshots:
|
|
|
3258
3313
|
'@tybys/wasm-util': 0.10.0
|
|
3259
3314
|
optional: true
|
|
3260
3315
|
|
|
3261
|
-
'@next/env@15.4.
|
|
3316
|
+
'@next/env@15.4.10': {}
|
|
3262
3317
|
|
|
3263
3318
|
'@next/eslint-plugin-next@15.4.6':
|
|
3264
3319
|
dependencies:
|
|
3265
3320
|
fast-glob: 3.3.1
|
|
3266
3321
|
|
|
3267
|
-
'@next/swc-darwin-arm64@15.4.
|
|
3322
|
+
'@next/swc-darwin-arm64@15.4.8':
|
|
3268
3323
|
optional: true
|
|
3269
3324
|
|
|
3270
|
-
'@next/swc-darwin-x64@15.4.
|
|
3325
|
+
'@next/swc-darwin-x64@15.4.8':
|
|
3271
3326
|
optional: true
|
|
3272
3327
|
|
|
3273
|
-
'@next/swc-linux-arm64-gnu@15.4.
|
|
3328
|
+
'@next/swc-linux-arm64-gnu@15.4.8':
|
|
3274
3329
|
optional: true
|
|
3275
3330
|
|
|
3276
|
-
'@next/swc-linux-arm64-musl@15.4.
|
|
3331
|
+
'@next/swc-linux-arm64-musl@15.4.8':
|
|
3277
3332
|
optional: true
|
|
3278
3333
|
|
|
3279
|
-
'@next/swc-linux-x64-gnu@15.4.
|
|
3334
|
+
'@next/swc-linux-x64-gnu@15.4.8':
|
|
3280
3335
|
optional: true
|
|
3281
3336
|
|
|
3282
|
-
'@next/swc-linux-x64-musl@15.4.
|
|
3337
|
+
'@next/swc-linux-x64-musl@15.4.8':
|
|
3283
3338
|
optional: true
|
|
3284
3339
|
|
|
3285
|
-
'@next/swc-win32-arm64-msvc@15.4.
|
|
3340
|
+
'@next/swc-win32-arm64-msvc@15.4.8':
|
|
3286
3341
|
optional: true
|
|
3287
3342
|
|
|
3288
|
-
'@next/swc-win32-x64-msvc@15.4.
|
|
3343
|
+
'@next/swc-win32-x64-msvc@15.4.8':
|
|
3289
3344
|
optional: true
|
|
3290
3345
|
|
|
3291
3346
|
'@nodelib/fs.scandir@2.1.5':
|
|
@@ -3772,6 +3827,16 @@ snapshots:
|
|
|
3772
3827
|
'@unrs/resolver-binding-win32-x64-msvc@1.11.1':
|
|
3773
3828
|
optional: true
|
|
3774
3829
|
|
|
3830
|
+
'@vercel/analytics@1.5.0(next@15.4.10(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0)':
|
|
3831
|
+
optionalDependencies:
|
|
3832
|
+
next: 15.4.10(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
|
3833
|
+
react: 19.1.0
|
|
3834
|
+
|
|
3835
|
+
'@vercel/speed-insights@1.2.0(next@15.4.10(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0)':
|
|
3836
|
+
optionalDependencies:
|
|
3837
|
+
next: 15.4.10(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
|
3838
|
+
react: 19.1.0
|
|
3839
|
+
|
|
3775
3840
|
acorn-jsx@5.3.2(acorn@8.15.0):
|
|
3776
3841
|
dependencies:
|
|
3777
3842
|
acorn: 8.15.0
|
|
@@ -4901,9 +4966,9 @@ snapshots:
|
|
|
4901
4966
|
react: 19.1.0
|
|
4902
4967
|
react-dom: 19.1.0(react@19.1.0)
|
|
4903
4968
|
|
|
4904
|
-
next@15.4.
|
|
4969
|
+
next@15.4.10(react-dom@19.1.0(react@19.1.0))(react@19.1.0):
|
|
4905
4970
|
dependencies:
|
|
4906
|
-
'@next/env': 15.4.
|
|
4971
|
+
'@next/env': 15.4.10
|
|
4907
4972
|
'@swc/helpers': 0.5.15
|
|
4908
4973
|
caniuse-lite: 1.0.30001735
|
|
4909
4974
|
postcss: 8.4.31
|
|
@@ -4911,14 +4976,14 @@ snapshots:
|
|
|
4911
4976
|
react-dom: 19.1.0(react@19.1.0)
|
|
4912
4977
|
styled-jsx: 5.1.6(react@19.1.0)
|
|
4913
4978
|
optionalDependencies:
|
|
4914
|
-
'@next/swc-darwin-arm64': 15.4.
|
|
4915
|
-
'@next/swc-darwin-x64': 15.4.
|
|
4916
|
-
'@next/swc-linux-arm64-gnu': 15.4.
|
|
4917
|
-
'@next/swc-linux-arm64-musl': 15.4.
|
|
4918
|
-
'@next/swc-linux-x64-gnu': 15.4.
|
|
4919
|
-
'@next/swc-linux-x64-musl': 15.4.
|
|
4920
|
-
'@next/swc-win32-arm64-msvc': 15.4.
|
|
4921
|
-
'@next/swc-win32-x64-msvc': 15.4.
|
|
4979
|
+
'@next/swc-darwin-arm64': 15.4.8
|
|
4980
|
+
'@next/swc-darwin-x64': 15.4.8
|
|
4981
|
+
'@next/swc-linux-arm64-gnu': 15.4.8
|
|
4982
|
+
'@next/swc-linux-arm64-musl': 15.4.8
|
|
4983
|
+
'@next/swc-linux-x64-gnu': 15.4.8
|
|
4984
|
+
'@next/swc-linux-x64-musl': 15.4.8
|
|
4985
|
+
'@next/swc-win32-arm64-msvc': 15.4.8
|
|
4986
|
+
'@next/swc-win32-x64-msvc': 15.4.8
|
|
4922
4987
|
sharp: 0.34.3
|
|
4923
4988
|
transitivePeerDependencies:
|
|
4924
4989
|
- '@babel/core'
|
package/package.json
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gblikas/querykit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "A comprehensive query toolkit for TypeScript that simplifies building and executing data queries across different environments",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "git+https://github.com/gblikas/querykit.git"
|
|
8
|
+
},
|
|
5
9
|
"main": "dist/index.js",
|
|
6
10
|
"types": "dist/index.d.ts",
|
|
7
11
|
"lint-staged": {
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { QueryParser, QueryParseError } from './parser';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
IComparisonExpression,
|
|
4
|
+
ILogicalExpression,
|
|
5
|
+
QueryExpression
|
|
6
|
+
} from './types';
|
|
3
7
|
|
|
4
8
|
// Replace the type definition with this approach
|
|
5
9
|
type QueryParserPrivate = {
|
|
@@ -863,6 +867,210 @@ describe('QueryParser', () => {
|
|
|
863
867
|
|
|
864
868
|
expect(parser.parse(query)).toEqual(expected);
|
|
865
869
|
});
|
|
870
|
+
|
|
871
|
+
// IN operator syntax tests using consistent key:[values] pattern
|
|
872
|
+
describe('IN operator syntax (key:[values])', () => {
|
|
873
|
+
it('should parse "field:[val1, val2, val3]" bracket array syntax', () => {
|
|
874
|
+
const query = 'status:[todo, doing, done]';
|
|
875
|
+
const expected: QueryExpression = {
|
|
876
|
+
type: 'logical',
|
|
877
|
+
operator: 'OR',
|
|
878
|
+
left: {
|
|
879
|
+
type: 'logical',
|
|
880
|
+
operator: 'OR',
|
|
881
|
+
left: {
|
|
882
|
+
type: 'comparison',
|
|
883
|
+
field: 'status',
|
|
884
|
+
operator: '==',
|
|
885
|
+
value: 'todo'
|
|
886
|
+
},
|
|
887
|
+
right: {
|
|
888
|
+
type: 'comparison',
|
|
889
|
+
field: 'status',
|
|
890
|
+
operator: '==',
|
|
891
|
+
value: 'doing'
|
|
892
|
+
}
|
|
893
|
+
},
|
|
894
|
+
right: {
|
|
895
|
+
type: 'comparison',
|
|
896
|
+
field: 'status',
|
|
897
|
+
operator: '==',
|
|
898
|
+
value: 'done'
|
|
899
|
+
}
|
|
900
|
+
};
|
|
901
|
+
|
|
902
|
+
expect(parser.parse(query)).toEqual(expected);
|
|
903
|
+
});
|
|
904
|
+
|
|
905
|
+
it('should parse numeric values in bracket syntax', () => {
|
|
906
|
+
const query = 'id:[2, 3]';
|
|
907
|
+
const expected: QueryExpression = {
|
|
908
|
+
type: 'logical',
|
|
909
|
+
operator: 'OR',
|
|
910
|
+
left: {
|
|
911
|
+
type: 'comparison',
|
|
912
|
+
field: 'id',
|
|
913
|
+
operator: '==',
|
|
914
|
+
value: 2
|
|
915
|
+
},
|
|
916
|
+
right: {
|
|
917
|
+
type: 'comparison',
|
|
918
|
+
field: 'id',
|
|
919
|
+
operator: '==',
|
|
920
|
+
value: 3
|
|
921
|
+
}
|
|
922
|
+
};
|
|
923
|
+
|
|
924
|
+
expect(parser.parse(query)).toEqual(expected);
|
|
925
|
+
});
|
|
926
|
+
|
|
927
|
+
it('should parse single value in bracket syntax', () => {
|
|
928
|
+
const query = 'status:[active]';
|
|
929
|
+
const expected: QueryExpression = {
|
|
930
|
+
type: 'comparison',
|
|
931
|
+
field: 'status',
|
|
932
|
+
operator: '==',
|
|
933
|
+
value: 'active'
|
|
934
|
+
};
|
|
935
|
+
|
|
936
|
+
expect(parser.parse(query)).toEqual(expected);
|
|
937
|
+
});
|
|
938
|
+
|
|
939
|
+
it('should preserve range syntax "field:[min TO max]"', () => {
|
|
940
|
+
const query = 'id:[2 TO 5]';
|
|
941
|
+
const expected: QueryExpression = {
|
|
942
|
+
type: 'logical',
|
|
943
|
+
operator: 'AND',
|
|
944
|
+
left: {
|
|
945
|
+
type: 'comparison',
|
|
946
|
+
field: 'id',
|
|
947
|
+
operator: '>=',
|
|
948
|
+
value: 2
|
|
949
|
+
},
|
|
950
|
+
right: {
|
|
951
|
+
type: 'comparison',
|
|
952
|
+
field: 'id',
|
|
953
|
+
operator: '<=',
|
|
954
|
+
value: 5
|
|
955
|
+
}
|
|
956
|
+
};
|
|
957
|
+
|
|
958
|
+
expect(parser.parse(query)).toEqual(expected);
|
|
959
|
+
});
|
|
960
|
+
|
|
961
|
+
it('should parse exclusive range syntax "field:{min TO max}"', () => {
|
|
962
|
+
const query = 'id:{2 TO 5}';
|
|
963
|
+
const expected: QueryExpression = {
|
|
964
|
+
type: 'logical',
|
|
965
|
+
operator: 'AND',
|
|
966
|
+
left: {
|
|
967
|
+
type: 'comparison',
|
|
968
|
+
field: 'id',
|
|
969
|
+
operator: '>',
|
|
970
|
+
value: 2
|
|
971
|
+
},
|
|
972
|
+
right: {
|
|
973
|
+
type: 'comparison',
|
|
974
|
+
field: 'id',
|
|
975
|
+
operator: '<',
|
|
976
|
+
value: 5
|
|
977
|
+
}
|
|
978
|
+
};
|
|
979
|
+
|
|
980
|
+
expect(parser.parse(query)).toEqual(expected);
|
|
981
|
+
});
|
|
982
|
+
|
|
983
|
+
it('should parse bracket syntax combined with other expressions', () => {
|
|
984
|
+
const query = 'status:[todo, doing] AND priority:>2';
|
|
985
|
+
const parsed = parser.parse(query);
|
|
986
|
+
|
|
987
|
+
// Verify it's a logical AND at the top level
|
|
988
|
+
expect(parsed.type).toBe('logical');
|
|
989
|
+
expect((parsed as ILogicalExpression).operator).toBe('AND');
|
|
990
|
+
|
|
991
|
+
// Left side should be OR of status values
|
|
992
|
+
const left = (parsed as ILogicalExpression).left as ILogicalExpression;
|
|
993
|
+
expect(left.type).toBe('logical');
|
|
994
|
+
expect(left.operator).toBe('OR');
|
|
995
|
+
});
|
|
996
|
+
|
|
997
|
+
it('should handle values with spaces using quotes', () => {
|
|
998
|
+
const query = 'name:[John, "Jane Doe"]';
|
|
999
|
+
const parsed = parser.parse(query);
|
|
1000
|
+
|
|
1001
|
+
expect(parsed.type).toBe('logical');
|
|
1002
|
+
expect((parsed as ILogicalExpression).operator).toBe('OR');
|
|
1003
|
+
|
|
1004
|
+
const right = (parsed as ILogicalExpression)
|
|
1005
|
+
.right as IComparisonExpression;
|
|
1006
|
+
expect(right.value).toBe('Jane Doe');
|
|
1007
|
+
});
|
|
1008
|
+
|
|
1009
|
+
it('should validate bracket syntax queries', () => {
|
|
1010
|
+
expect(parser.validate('status:[todo, doing, done]')).toBe(true);
|
|
1011
|
+
expect(parser.validate('id:[1, 2, 3]')).toBe(true);
|
|
1012
|
+
expect(parser.validate('id:[1 TO 10]')).toBe(true);
|
|
1013
|
+
});
|
|
1014
|
+
|
|
1015
|
+
it('should handle mixed types in bracket syntax', () => {
|
|
1016
|
+
const query = 'priority:[1, 2, 3]';
|
|
1017
|
+
const parsed = parser.parse(query);
|
|
1018
|
+
|
|
1019
|
+
expect(parsed.type).toBe('logical');
|
|
1020
|
+
// All values should be numbers
|
|
1021
|
+
const getLeftmost = (expr: QueryExpression): IComparisonExpression => {
|
|
1022
|
+
if (expr.type === 'comparison') return expr;
|
|
1023
|
+
return getLeftmost((expr as ILogicalExpression).left);
|
|
1024
|
+
};
|
|
1025
|
+
expect(typeof getLeftmost(parsed).value).toBe('number');
|
|
1026
|
+
});
|
|
1027
|
+
|
|
1028
|
+
it('should handle quoted values with commas inside', () => {
|
|
1029
|
+
const query = 'name:["John, Jr.", "Jane"]';
|
|
1030
|
+
const parsed = parser.parse(query);
|
|
1031
|
+
|
|
1032
|
+
expect(parsed.type).toBe('logical');
|
|
1033
|
+
expect((parsed as ILogicalExpression).operator).toBe('OR');
|
|
1034
|
+
|
|
1035
|
+
const left = (parsed as ILogicalExpression)
|
|
1036
|
+
.left as IComparisonExpression;
|
|
1037
|
+
const right = (parsed as ILogicalExpression)
|
|
1038
|
+
.right as IComparisonExpression;
|
|
1039
|
+
|
|
1040
|
+
expect(left.value).toBe('John, Jr.');
|
|
1041
|
+
expect(right.value).toBe('Jane');
|
|
1042
|
+
});
|
|
1043
|
+
|
|
1044
|
+
it('should handle single-quoted values with commas inside', () => {
|
|
1045
|
+
const query = "name:['Hello, World', 'test']";
|
|
1046
|
+
const parsed = parser.parse(query);
|
|
1047
|
+
|
|
1048
|
+
expect(parsed.type).toBe('logical');
|
|
1049
|
+
const left = (parsed as ILogicalExpression)
|
|
1050
|
+
.left as IComparisonExpression;
|
|
1051
|
+
expect(left.value).toBe('Hello, World');
|
|
1052
|
+
});
|
|
1053
|
+
|
|
1054
|
+
it('should handle mixed quoted and unquoted values with commas', () => {
|
|
1055
|
+
const query = 'tags:["a,b,c", simple, "x,y"]';
|
|
1056
|
+
const parsed = parser.parse(query);
|
|
1057
|
+
|
|
1058
|
+
expect(parsed.type).toBe('logical');
|
|
1059
|
+
// Should have 3 values: "a,b,c", "simple", "x,y"
|
|
1060
|
+
const getValues = (expr: QueryExpression): string[] => {
|
|
1061
|
+
if (expr.type === 'comparison') {
|
|
1062
|
+
return [String(expr.value)];
|
|
1063
|
+
}
|
|
1064
|
+
const logical = expr as ILogicalExpression;
|
|
1065
|
+
return [...getValues(logical.left), ...getValues(logical.right!)];
|
|
1066
|
+
};
|
|
1067
|
+
|
|
1068
|
+
const values = getValues(parsed);
|
|
1069
|
+
expect(values).toContain('a,b,c');
|
|
1070
|
+
expect(values).toContain('simple');
|
|
1071
|
+
expect(values).toContain('x,y');
|
|
1072
|
+
});
|
|
1073
|
+
});
|
|
866
1074
|
});
|
|
867
1075
|
|
|
868
1076
|
describe('validate', () => {
|