@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.
- package/.cursor/BUGBOT.md +21 -0
- package/.cursor/rules/01-project-structure.mdc +77 -0
- package/.cursor/rules/02-typescript-standards.mdc +105 -0
- package/.cursor/rules/03-testing-standards.mdc +78 -0
- package/.cursor/rules/04-query-language.mdc +79 -0
- package/.cursor/rules/05-solid-principles.mdc +118 -0
- package/.cursor/rules/liqe-readme-docs.mdc +438 -0
- package/.devcontainer/devcontainer.json +25 -0
- package/.eslintignore +1 -0
- package/.eslintrc.js +39 -0
- package/.github/dependabot.yml +12 -0
- package/.github/workflows/ci.yml +114 -0
- package/.github/workflows/publish.yml +61 -0
- package/.husky/pre-commit +30 -0
- package/.prettierrc +10 -0
- package/CONTRIBUTING.md +187 -0
- package/LICENSE +674 -0
- package/README.md +237 -0
- package/dist/adapters/drizzle/index.d.ts +122 -0
- package/dist/adapters/drizzle/index.js +166 -0
- package/dist/adapters/index.d.ts +7 -0
- package/dist/adapters/index.js +25 -0
- package/dist/adapters/types.d.ts +60 -0
- package/dist/adapters/types.js +8 -0
- package/dist/index.d.ts +75 -0
- package/dist/index.js +118 -0
- package/dist/parser/index.d.ts +2 -0
- package/dist/parser/index.js +18 -0
- package/dist/parser/parser.d.ts +51 -0
- package/dist/parser/parser.js +201 -0
- package/dist/parser/types.d.ts +68 -0
- package/dist/parser/types.js +5 -0
- package/dist/query/builder.d.ts +61 -0
- package/dist/query/builder.js +188 -0
- package/dist/query/index.d.ts +2 -0
- package/dist/query/index.js +18 -0
- package/dist/query/types.d.ts +79 -0
- package/dist/query/types.js +2 -0
- package/dist/security/index.d.ts +2 -0
- package/dist/security/index.js +18 -0
- package/dist/security/types.d.ts +181 -0
- package/dist/security/types.js +43 -0
- package/dist/security/validator.d.ts +191 -0
- package/dist/security/validator.js +344 -0
- package/dist/translators/drizzle/index.d.ts +73 -0
- package/dist/translators/drizzle/index.js +260 -0
- package/dist/translators/index.d.ts +8 -0
- package/dist/translators/index.js +27 -0
- package/dist/translators/sql/index.d.ts +108 -0
- package/dist/translators/sql/index.js +252 -0
- package/dist/translators/types.d.ts +39 -0
- package/dist/translators/types.js +8 -0
- package/examples/qk-next/README.md +35 -0
- package/examples/qk-next/app/favicon.ico +0 -0
- package/examples/qk-next/app/globals.css +122 -0
- package/examples/qk-next/app/layout.tsx +121 -0
- package/examples/qk-next/app/page.tsx +813 -0
- package/examples/qk-next/app/providers.tsx +80 -0
- package/examples/qk-next/components/aurora-background.tsx +12 -0
- package/examples/qk-next/components/github-stars.tsx +51 -0
- package/examples/qk-next/components/mode-toggle.tsx +27 -0
- package/examples/qk-next/components/reactbits/blocks/Backgrounds/Aurora/Aurora.tsx +217 -0
- package/examples/qk-next/components/reactbits/blocks/Backgrounds/LightRays/LightRays.tsx +474 -0
- package/examples/qk-next/components/theme-provider.tsx +11 -0
- package/examples/qk-next/components/ui/card.tsx +92 -0
- package/examples/qk-next/components/ui/command.tsx +184 -0
- package/examples/qk-next/components/ui/dialog.tsx +143 -0
- package/examples/qk-next/components/ui/drawer.tsx +135 -0
- package/examples/qk-next/components/ui/hover-card.tsx +44 -0
- package/examples/qk-next/components/ui/icons.tsx +148 -0
- package/examples/qk-next/components/ui/sonner.tsx +26 -0
- package/examples/qk-next/components/ui/table.tsx +117 -0
- package/examples/qk-next/components.json +21 -0
- package/examples/qk-next/eslint.config.mjs +21 -0
- package/examples/qk-next/jsrepo.json +13 -0
- package/examples/qk-next/lib/utils.ts +6 -0
- package/examples/qk-next/next.config.ts +8 -0
- package/examples/qk-next/package.json +48 -0
- package/examples/qk-next/pnpm-lock.yaml +5558 -0
- package/examples/qk-next/postcss.config.mjs +5 -0
- package/examples/qk-next/public/file.svg +1 -0
- package/examples/qk-next/public/globe.svg +1 -0
- package/examples/qk-next/public/next.svg +1 -0
- package/examples/qk-next/public/vercel.svg +1 -0
- package/examples/qk-next/public/window.svg +1 -0
- package/examples/qk-next/tsconfig.json +42 -0
- package/examples/qk-next/types/sonner.d.ts +3 -0
- package/jest.config.js +26 -0
- package/package.json +51 -0
- package/src/adapters/drizzle/drizzle-adapter.test.ts +115 -0
- package/src/adapters/drizzle/index.ts +299 -0
- package/src/adapters/index.ts +11 -0
- package/src/adapters/types.ts +72 -0
- package/src/index.ts +194 -0
- package/src/integration.test.ts +202 -0
- package/src/parser/index.ts +2 -0
- package/src/parser/parser.test.ts +1056 -0
- package/src/parser/parser.ts +268 -0
- package/src/parser/types.ts +97 -0
- package/src/query/builder.test.ts +272 -0
- package/src/query/builder.ts +274 -0
- package/src/query/index.ts +2 -0
- package/src/query/types.ts +107 -0
- package/src/security/index.ts +2 -0
- package/src/security/types.ts +210 -0
- package/src/security/validator.test.ts +459 -0
- package/src/security/validator.ts +395 -0
- package/src/security.test.ts +366 -0
- package/src/translators/drizzle/drizzle-translator.test.ts +128 -0
- package/src/translators/drizzle/index.test.ts +45 -0
- package/src/translators/drizzle/index.ts +346 -0
- package/src/translators/index.ts +14 -0
- package/src/translators/sql/index.test.ts +45 -0
- package/src/translators/sql/index.ts +331 -0
- package/src/translators/sql/sql-translator.test.ts +419 -0
- package/src/translators/types.ts +44 -0
- package/src/types/sonner.d.ts +3 -0
- package/tsconfig.json +34 -0
|
@@ -0,0 +1,438 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: the readme of liqe; a poor man's documentation for liqe
|
|
3
|
+
globs:
|
|
4
|
+
alwaysApply: false
|
|
5
|
+
---
|
|
6
|
+
# liqe
|
|
7
|
+
|
|
8
|
+
[](https://coveralls.io/github/gajus/liqe)
|
|
9
|
+
[](https://www.npmjs.org/package/liqe)
|
|
10
|
+
[](https://github.com/gajus/canonical)
|
|
11
|
+
[](https://twitter.com/kuizinas)
|
|
12
|
+
|
|
13
|
+
Lightweight and performant Lucene-like parser, serializer and search engine.
|
|
14
|
+
|
|
15
|
+
* [Motivation](#motivation)
|
|
16
|
+
* [Usage](#usage)
|
|
17
|
+
* [Query Syntax](#query-syntax)
|
|
18
|
+
* [Liqe syntax cheat sheet](#liqe-syntax-cheat-sheet)
|
|
19
|
+
* [Keyword matching](#keyword-matching)
|
|
20
|
+
* [Number matching](#number-matching)
|
|
21
|
+
* [Range matching](#range-matching)
|
|
22
|
+
* [Wildcard matching](#wildcard-matching)
|
|
23
|
+
* [Boolean operators](#boolean-operators)
|
|
24
|
+
* [Serializer](#serializer)
|
|
25
|
+
* [AST](#ast)
|
|
26
|
+
* [Utilities](#utilities)
|
|
27
|
+
* [Compatibility with Lucene](#compatibility-with-lucene)
|
|
28
|
+
* [Recipes](#recipes)
|
|
29
|
+
* [Handling syntax errors](#handling-syntax-errors)
|
|
30
|
+
* [Highlighting matches](#highlighting-matches)
|
|
31
|
+
* [Development](#development)
|
|
32
|
+
* [Tutorials](#tutorials)
|
|
33
|
+
|
|
34
|
+
## Motivation
|
|
35
|
+
|
|
36
|
+
Originally built Liqe to enable [Roarr](https://github.com/gajus/roarr) log filtering via [cli](https://github.com/gajus/roarr-cli#filtering-logs). I have since been polishing this project as a hobby/intellectual exercise. I've seen it being adopted by [various](https://github.com/gajus/liqe/network/dependents) CLI and web applications that require advanced search. To my knowledge, it is currently the most complete Lucene-like syntax parser and serializer in JavaScript, as well as a compatible in-memory search engine.
|
|
37
|
+
|
|
38
|
+
Liqe use cases include:
|
|
39
|
+
|
|
40
|
+
* parsing search queries
|
|
41
|
+
* serializing parsed queries
|
|
42
|
+
* searching JSON documents using the Liqe query language (LQL)
|
|
43
|
+
|
|
44
|
+
Note that the [Liqe AST](#ast) is treated as a public API, i.e., one could implement their own search mechanism that uses Liqe query language (LQL).
|
|
45
|
+
|
|
46
|
+
## Usage
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
import {
|
|
50
|
+
filter,
|
|
51
|
+
highlight,
|
|
52
|
+
parse,
|
|
53
|
+
test,
|
|
54
|
+
} from 'liqe';
|
|
55
|
+
|
|
56
|
+
const persons = [
|
|
57
|
+
{
|
|
58
|
+
height: 180,
|
|
59
|
+
name: 'John Morton',
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
height: 175,
|
|
63
|
+
name: 'David Barker',
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
height: 170,
|
|
67
|
+
name: 'Thomas Castro',
|
|
68
|
+
},
|
|
69
|
+
];
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Filter a collection:
|
|
73
|
+
|
|
74
|
+
```ts
|
|
75
|
+
filter(parse('height:>170'), persons);
|
|
76
|
+
// [
|
|
77
|
+
// {
|
|
78
|
+
// height: 180,
|
|
79
|
+
// name: 'John Morton',
|
|
80
|
+
// },
|
|
81
|
+
// {
|
|
82
|
+
// height: 175,
|
|
83
|
+
// name: 'David Barker',
|
|
84
|
+
// },
|
|
85
|
+
// ]
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Test a single object:
|
|
89
|
+
|
|
90
|
+
```ts
|
|
91
|
+
test(parse('name:John'), persons[0]);
|
|
92
|
+
// true
|
|
93
|
+
test(parse('name:David'), persons[0]);
|
|
94
|
+
// false
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Highlight matching fields and substrings:
|
|
98
|
+
|
|
99
|
+
```ts
|
|
100
|
+
highlight(parse('name:john'), persons[0]);
|
|
101
|
+
// [
|
|
102
|
+
// {
|
|
103
|
+
// path: 'name',
|
|
104
|
+
// query: /(John)/,
|
|
105
|
+
// }
|
|
106
|
+
// ]
|
|
107
|
+
highlight(parse('height:180'), persons[0]);
|
|
108
|
+
// [
|
|
109
|
+
// {
|
|
110
|
+
// path: 'height',
|
|
111
|
+
// }
|
|
112
|
+
// ]
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Query Syntax
|
|
116
|
+
|
|
117
|
+
Liqe uses Liqe Query Language (LQL), which is heavily inspired by Lucene but extends it in various ways that allow a more powerful search experience.
|
|
118
|
+
|
|
119
|
+
### Liqe syntax cheat sheet
|
|
120
|
+
|
|
121
|
+
```rb
|
|
122
|
+
# search for "foo" term anywhere in the document (case insensitive)
|
|
123
|
+
foo
|
|
124
|
+
|
|
125
|
+
# search for "foo" term anywhere in the document (case sensitive)
|
|
126
|
+
'foo'
|
|
127
|
+
"foo"
|
|
128
|
+
|
|
129
|
+
# search for "foo" term in `name` field
|
|
130
|
+
name:foo
|
|
131
|
+
|
|
132
|
+
# search for "foo" term in `full name` field
|
|
133
|
+
'full name':foo
|
|
134
|
+
"full name":foo
|
|
135
|
+
|
|
136
|
+
# search for "foo" term in `first` field, member of `name`, i.e.
|
|
137
|
+
# matches {name: {first: 'foo'}}
|
|
138
|
+
name.first:foo
|
|
139
|
+
|
|
140
|
+
# search using regex
|
|
141
|
+
name:/foo/
|
|
142
|
+
name:/foo/o
|
|
143
|
+
|
|
144
|
+
# search using wildcard
|
|
145
|
+
name:foo*bar
|
|
146
|
+
name:foo?bar
|
|
147
|
+
|
|
148
|
+
# boolean search
|
|
149
|
+
member:true
|
|
150
|
+
member:false
|
|
151
|
+
|
|
152
|
+
# null search
|
|
153
|
+
member:null
|
|
154
|
+
|
|
155
|
+
# search for age =, >, >=, <, <=
|
|
156
|
+
height:=100
|
|
157
|
+
height:>100
|
|
158
|
+
height:>=100
|
|
159
|
+
height:<100
|
|
160
|
+
height:<=100
|
|
161
|
+
|
|
162
|
+
# search for height in range (inclusive, exclusive)
|
|
163
|
+
height:[100 TO 200]
|
|
164
|
+
height:{100 TO 200}
|
|
165
|
+
|
|
166
|
+
# boolean operators
|
|
167
|
+
name:foo AND height:=100
|
|
168
|
+
name:foo OR name:bar
|
|
169
|
+
|
|
170
|
+
# unary operators
|
|
171
|
+
NOT foo
|
|
172
|
+
-foo
|
|
173
|
+
NOT foo:bar
|
|
174
|
+
-foo:bar
|
|
175
|
+
name:foo AND NOT (bio:bar OR bio:baz)
|
|
176
|
+
|
|
177
|
+
# implicit AND boolean operator
|
|
178
|
+
name:foo height:=100
|
|
179
|
+
|
|
180
|
+
# grouping
|
|
181
|
+
name:foo AND (bio:bar OR bio:baz)
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### Keyword matching
|
|
185
|
+
|
|
186
|
+
Search for word "foo" in any field (case insensitive).
|
|
187
|
+
|
|
188
|
+
```rb
|
|
189
|
+
foo
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
Search for word "foo" in the `name` field.
|
|
193
|
+
|
|
194
|
+
```rb
|
|
195
|
+
name:foo
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Search for `name` field values matching `/foo/i` regex.
|
|
199
|
+
|
|
200
|
+
```rb
|
|
201
|
+
name:/foo/i
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
Search for `name` field values matching `f*o` wildcard pattern.
|
|
205
|
+
|
|
206
|
+
```rb
|
|
207
|
+
name:f*o
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
Search for `name` field values matching `f?o` wildcard pattern.
|
|
211
|
+
|
|
212
|
+
```rb
|
|
213
|
+
name:f?o
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
Search for phrase "foo bar" in the `name` field (case sensitive).
|
|
217
|
+
|
|
218
|
+
```rb
|
|
219
|
+
name:"foo bar"
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
### Number matching
|
|
223
|
+
|
|
224
|
+
Search for value equal to 100 in the `height` field.
|
|
225
|
+
|
|
226
|
+
```rb
|
|
227
|
+
height:=100
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
Search for value greater than 100 in the `height` field.
|
|
231
|
+
|
|
232
|
+
```rb
|
|
233
|
+
height:>100
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
Search for value greater than or equal to 100 in the `height` field.
|
|
237
|
+
|
|
238
|
+
```rb
|
|
239
|
+
height:>=100
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
### Range matching
|
|
243
|
+
|
|
244
|
+
Search for value greater or equal to 100 and lower or equal to 200 in the `height` field.
|
|
245
|
+
|
|
246
|
+
```rb
|
|
247
|
+
height:[100 TO 200]
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
Search for value greater than 100 and lower than 200 in the `height` field.
|
|
251
|
+
|
|
252
|
+
```rb
|
|
253
|
+
height:{100 TO 200}
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
### Wildcard matching
|
|
257
|
+
|
|
258
|
+
Search for any word that starts with "foo" in the `name` field.
|
|
259
|
+
|
|
260
|
+
```rb
|
|
261
|
+
name:foo*
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
Search for any word that starts with "foo" and ends with "bar" in the `name` field.
|
|
265
|
+
|
|
266
|
+
```rb
|
|
267
|
+
name:foo*bar
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
Search for any word that starts with "foo" in the `name` field, followed by a single arbitrary character.
|
|
271
|
+
|
|
272
|
+
```rb
|
|
273
|
+
name:foo?
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
Search for any word that starts with "foo", followed by a single arbitrary character and immediately ends with "bar" in the `name` field.
|
|
277
|
+
|
|
278
|
+
```rb
|
|
279
|
+
name:foo?bar
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
### Boolean operators
|
|
283
|
+
|
|
284
|
+
Search for phrase "foo bar" in the `name` field AND the phrase "quick fox" in the `bio` field.
|
|
285
|
+
|
|
286
|
+
```rb
|
|
287
|
+
name:"foo bar" AND bio:"quick fox"
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
Search for either the phrase "foo bar" in the `name` field AND the phrase "quick fox" in the `bio` field, or the word "fox" in the `name` field.
|
|
291
|
+
|
|
292
|
+
```rb
|
|
293
|
+
(name:"foo bar" AND bio:"quick fox") OR name:fox
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
## Serializer
|
|
297
|
+
|
|
298
|
+
Serializer allows to convert Liqe tokens back to the original search query.
|
|
299
|
+
|
|
300
|
+
```ts
|
|
301
|
+
import {
|
|
302
|
+
parse,
|
|
303
|
+
serialize,
|
|
304
|
+
} from 'liqe';
|
|
305
|
+
|
|
306
|
+
const tokens = parse('foo:bar');
|
|
307
|
+
|
|
308
|
+
// {
|
|
309
|
+
// expression: {
|
|
310
|
+
// location: {
|
|
311
|
+
// start: 4,
|
|
312
|
+
// },
|
|
313
|
+
// quoted: false,
|
|
314
|
+
// type: 'LiteralExpression',
|
|
315
|
+
// value: 'bar',
|
|
316
|
+
// },
|
|
317
|
+
// field: {
|
|
318
|
+
// location: {
|
|
319
|
+
// start: 0,
|
|
320
|
+
// },
|
|
321
|
+
// name: 'foo',
|
|
322
|
+
// path: ['foo'],
|
|
323
|
+
// quoted: false,
|
|
324
|
+
// type: 'Field',
|
|
325
|
+
// },
|
|
326
|
+
// location: {
|
|
327
|
+
// start: 0,
|
|
328
|
+
// },
|
|
329
|
+
// operator: {
|
|
330
|
+
// location: {
|
|
331
|
+
// start: 3,
|
|
332
|
+
// },
|
|
333
|
+
// operator: ':',
|
|
334
|
+
// type: 'ComparisonOperator',
|
|
335
|
+
// },
|
|
336
|
+
// type: 'Tag',
|
|
337
|
+
// }
|
|
338
|
+
|
|
339
|
+
serialize(tokens);
|
|
340
|
+
// 'foo:bar'
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
## AST
|
|
344
|
+
|
|
345
|
+
```ts
|
|
346
|
+
import {
|
|
347
|
+
type BooleanOperatorToken,
|
|
348
|
+
type ComparisonOperatorToken,
|
|
349
|
+
type EmptyExpression,
|
|
350
|
+
type FieldToken,
|
|
351
|
+
type ImplicitBooleanOperatorToken,
|
|
352
|
+
type ImplicitFieldToken,
|
|
353
|
+
type LiteralExpressionToken,
|
|
354
|
+
type LogicalExpressionToken,
|
|
355
|
+
type RangeExpressionToken,
|
|
356
|
+
type RegexExpressionToken,
|
|
357
|
+
type TagToken,
|
|
358
|
+
type UnaryOperatorToken,
|
|
359
|
+
} from 'liqe';
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
There are 11 AST tokens that describe a parsed Liqe query.
|
|
363
|
+
|
|
364
|
+
If you are building a serializer, then you must implement all of them for the complete coverage of all possible query inputs. Refer to the [built-in serializer](./src/serialize.ts) for an example.
|
|
365
|
+
|
|
366
|
+
## Utilities
|
|
367
|
+
|
|
368
|
+
```ts
|
|
369
|
+
import {
|
|
370
|
+
isSafeUnquotedExpression,
|
|
371
|
+
} from 'liqe';
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Determines if an expression requires quotes.
|
|
375
|
+
* Use this if you need to programmatically manipulate the AST
|
|
376
|
+
* before using a serializer to convert the query back to text.
|
|
377
|
+
*/
|
|
378
|
+
isSafeUnquotedExpression(expression: string): boolean;
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
## Compatibility with Lucene
|
|
382
|
+
|
|
383
|
+
The following Lucene abilities are not supported:
|
|
384
|
+
|
|
385
|
+
* [Fuzzy Searches](https://lucene.apache.org/core/2_9_4/queryparsersyntax.html#Fuzzy%20Searches)
|
|
386
|
+
* [Proximity Searches](https://lucene.apache.org/core/2_9_4/queryparsersyntax.html#Proximity%20Searches)
|
|
387
|
+
* [Boosting a Term](https://lucene.apache.org/core/2_9_4/queryparsersyntax.html#Boosting%20a%20Term)
|
|
388
|
+
|
|
389
|
+
## Recipes
|
|
390
|
+
|
|
391
|
+
### Handling syntax errors
|
|
392
|
+
|
|
393
|
+
In case of a syntax error, Liqe throws `SyntaxError`.
|
|
394
|
+
|
|
395
|
+
```ts
|
|
396
|
+
import {
|
|
397
|
+
parse,
|
|
398
|
+
SyntaxError,
|
|
399
|
+
} from 'liqe';
|
|
400
|
+
|
|
401
|
+
try {
|
|
402
|
+
parse('foo bar');
|
|
403
|
+
} catch (error) {
|
|
404
|
+
if (error instanceof SyntaxError) {
|
|
405
|
+
console.error({
|
|
406
|
+
// Syntax error at line 1 column 5
|
|
407
|
+
message: error.message,
|
|
408
|
+
// 4
|
|
409
|
+
offset: error.offset,
|
|
410
|
+
// 1
|
|
411
|
+
offset: error.line,
|
|
412
|
+
// 5
|
|
413
|
+
offset: error.column,
|
|
414
|
+
});
|
|
415
|
+
} else {
|
|
416
|
+
throw error;
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
### Highlighting matches
|
|
422
|
+
|
|
423
|
+
Consider using [`highlight-words`](https://github.com/tricinel/highlight-words) package to highlight Liqe matches.
|
|
424
|
+
|
|
425
|
+
## Development
|
|
426
|
+
|
|
427
|
+
### Compiling Parser
|
|
428
|
+
|
|
429
|
+
If you are going to modify parser, then use `npm run watch` to run compiler in watch mode.
|
|
430
|
+
|
|
431
|
+
### Benchmarking Changes
|
|
432
|
+
|
|
433
|
+
Before making any changes, capture the current benchmark on your machine using `npm run benchmark`. Run benchmark again after making any changes. Before committing changes, ensure that performance is not negatively impacted.
|
|
434
|
+
|
|
435
|
+
|
|
436
|
+
## Tutorials
|
|
437
|
+
|
|
438
|
+
* [Building advanced SQL search from a user text input](https://contra.com/p/WobOBob7-building-advanced-sql-search-from-a-user-text-input)
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
|
|
2
|
+
// README at: https://github.com/devcontainers/templates/tree/main/src/ubuntu
|
|
3
|
+
{
|
|
4
|
+
"name": "Ubuntu",
|
|
5
|
+
// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
|
|
6
|
+
"image": "mcr.microsoft.com/devcontainers/base:jammy",
|
|
7
|
+
"features": {
|
|
8
|
+
"ghcr.io/devcontainers/features/node:1": {}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// Features to add to the dev container. More info: https://containers.dev/features.
|
|
12
|
+
// "features": {},
|
|
13
|
+
|
|
14
|
+
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
|
15
|
+
// "forwardPorts": [],
|
|
16
|
+
|
|
17
|
+
// Use 'postCreateCommand' to run commands after the container is created.
|
|
18
|
+
// "postCreateCommand": "uname -a",
|
|
19
|
+
|
|
20
|
+
// Configure tool-specific properties.
|
|
21
|
+
// "customizations": {},
|
|
22
|
+
|
|
23
|
+
// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
|
|
24
|
+
// "remoteUser": "root"
|
|
25
|
+
}
|
package/.eslintignore
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
examples/qk-next/components/**
|
package/.eslintrc.js
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
module.exports = {
|
|
2
|
+
root: true,
|
|
3
|
+
parser: '@typescript-eslint/parser',
|
|
4
|
+
plugins: ['@typescript-eslint'],
|
|
5
|
+
extends: [
|
|
6
|
+
'eslint:recommended',
|
|
7
|
+
'plugin:@typescript-eslint/recommended',
|
|
8
|
+
'prettier'
|
|
9
|
+
],
|
|
10
|
+
env: {
|
|
11
|
+
node: true,
|
|
12
|
+
jest: true
|
|
13
|
+
},
|
|
14
|
+
rules: {
|
|
15
|
+
'@typescript-eslint/explicit-function-return-type': 'error',
|
|
16
|
+
'@typescript-eslint/no-explicit-any': 'error',
|
|
17
|
+
'@typescript-eslint/no-unused-vars': 'error',
|
|
18
|
+
'@typescript-eslint/naming-convention': [
|
|
19
|
+
'error',
|
|
20
|
+
{
|
|
21
|
+
selector: 'interface',
|
|
22
|
+
format: ['PascalCase'],
|
|
23
|
+
prefix: ['I']
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
selector: 'class',
|
|
27
|
+
format: ['PascalCase']
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
selector: 'typeAlias',
|
|
31
|
+
format: ['PascalCase']
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
selector: 'enum',
|
|
35
|
+
format: ['PascalCase']
|
|
36
|
+
}
|
|
37
|
+
]
|
|
38
|
+
}
|
|
39
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# To get started with Dependabot version updates, you'll need to specify which
|
|
2
|
+
# package ecosystems to update and where the package manifests are located.
|
|
3
|
+
# Please see the documentation for more information:
|
|
4
|
+
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
|
|
5
|
+
# https://containers.dev/guide/dependabot
|
|
6
|
+
|
|
7
|
+
version: 2
|
|
8
|
+
updates:
|
|
9
|
+
- package-ecosystem: "devcontainers"
|
|
10
|
+
directory: "/"
|
|
11
|
+
schedule:
|
|
12
|
+
interval: weekly
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
name: QueryKit CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main, staging]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main, staging]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
name: Test & Lint
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
steps:
|
|
14
|
+
- name: Checkout code
|
|
15
|
+
uses: actions/checkout@v4
|
|
16
|
+
|
|
17
|
+
- name: Setup Node.js
|
|
18
|
+
uses: actions/setup-node@v4
|
|
19
|
+
with:
|
|
20
|
+
node-version: '20'
|
|
21
|
+
|
|
22
|
+
- name: Setup pnpm
|
|
23
|
+
uses: pnpm/action-setup@v3
|
|
24
|
+
with:
|
|
25
|
+
version: 8
|
|
26
|
+
run_install: false
|
|
27
|
+
|
|
28
|
+
- name: Get pnpm store directory
|
|
29
|
+
id: pnpm-cache
|
|
30
|
+
shell: bash
|
|
31
|
+
run: |
|
|
32
|
+
echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT
|
|
33
|
+
|
|
34
|
+
- name: Setup pnpm cache
|
|
35
|
+
uses: actions/cache@v4
|
|
36
|
+
with:
|
|
37
|
+
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
|
|
38
|
+
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
|
39
|
+
restore-keys: |
|
|
40
|
+
${{ runner.os }}-pnpm-store-
|
|
41
|
+
|
|
42
|
+
- name: Install dependencies
|
|
43
|
+
run: pnpm i
|
|
44
|
+
|
|
45
|
+
- name: Lint code
|
|
46
|
+
run: pnpm run lint
|
|
47
|
+
|
|
48
|
+
- name: Check TypeScript compilation
|
|
49
|
+
run: pnpm exec tsc --noEmit
|
|
50
|
+
|
|
51
|
+
- name: Run tests
|
|
52
|
+
run: pnpm run test
|
|
53
|
+
|
|
54
|
+
build:
|
|
55
|
+
name: Build Package
|
|
56
|
+
needs: test
|
|
57
|
+
runs-on: ubuntu-latest
|
|
58
|
+
strategy:
|
|
59
|
+
matrix:
|
|
60
|
+
node-version: [18.x, 20.x]
|
|
61
|
+
steps:
|
|
62
|
+
- name: Checkout code
|
|
63
|
+
uses: actions/checkout@v4
|
|
64
|
+
|
|
65
|
+
- name: Setup Node.js ${{ matrix.node-version }}
|
|
66
|
+
uses: actions/setup-node@v4
|
|
67
|
+
with:
|
|
68
|
+
node-version: ${{ matrix.node-version }}
|
|
69
|
+
|
|
70
|
+
- name: Setup pnpm
|
|
71
|
+
uses: pnpm/action-setup@v3
|
|
72
|
+
with:
|
|
73
|
+
version: 8
|
|
74
|
+
run_install: false
|
|
75
|
+
|
|
76
|
+
- name: Get pnpm store directory
|
|
77
|
+
id: pnpm-cache
|
|
78
|
+
shell: bash
|
|
79
|
+
run: |
|
|
80
|
+
echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT
|
|
81
|
+
|
|
82
|
+
- name: Setup pnpm cache
|
|
83
|
+
uses: actions/cache@v4
|
|
84
|
+
with:
|
|
85
|
+
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
|
|
86
|
+
key: ${{ runner.os }}-pnpm-store-node-${{ matrix.node-version }}-${{ hashFiles('**/pnpm-lock.yaml') }}
|
|
87
|
+
restore-keys: |
|
|
88
|
+
${{ runner.os }}-pnpm-store-node-${{ matrix.node-version }}-
|
|
89
|
+
|
|
90
|
+
- name: Install dependencies
|
|
91
|
+
run: pnpm install
|
|
92
|
+
|
|
93
|
+
- name: Build package
|
|
94
|
+
run: pnpm run build
|
|
95
|
+
|
|
96
|
+
- name: Create test package
|
|
97
|
+
run: pnpm pack
|
|
98
|
+
|
|
99
|
+
- name: Verify package exports
|
|
100
|
+
run: |
|
|
101
|
+
# Ensure package can be imported
|
|
102
|
+
mkdir -p test-import
|
|
103
|
+
cd test-import
|
|
104
|
+
echo '{ "type": "module" }' > package.json
|
|
105
|
+
echo 'import * as querykit from "../dist/index.js"; console.log("Package import successful!")' > test.js
|
|
106
|
+
node test.js
|
|
107
|
+
|
|
108
|
+
- name: Upload artifact
|
|
109
|
+
uses: actions/upload-artifact@v4
|
|
110
|
+
with:
|
|
111
|
+
name: querykit-package-${{ matrix.node-version }}
|
|
112
|
+
path: querykit-*.tgz
|
|
113
|
+
|
|
114
|
+
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
name: Publish public release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
tags:
|
|
7
|
+
- 'v*'
|
|
8
|
+
workflow_dispatch:
|
|
9
|
+
inputs:
|
|
10
|
+
tag:
|
|
11
|
+
description: 'Tag name (e.g., v0.0.0)'
|
|
12
|
+
required: true
|
|
13
|
+
type: string
|
|
14
|
+
|
|
15
|
+
jobs:
|
|
16
|
+
publish-npm:
|
|
17
|
+
name: Publish package to npm registry
|
|
18
|
+
runs-on: ubuntu-latest
|
|
19
|
+
permissions:
|
|
20
|
+
contents: read
|
|
21
|
+
steps:
|
|
22
|
+
- name: Checkout code
|
|
23
|
+
uses: actions/checkout@v4
|
|
24
|
+
with:
|
|
25
|
+
ref: ${{ github.event.release.tag_name }}
|
|
26
|
+
|
|
27
|
+
- name: Setup Node.js
|
|
28
|
+
uses: actions/setup-node@v4
|
|
29
|
+
with:
|
|
30
|
+
node-version: '20'
|
|
31
|
+
registry-url: 'https://registry.npmjs.org'
|
|
32
|
+
always-auth: true
|
|
33
|
+
|
|
34
|
+
- name: Setup pnpm
|
|
35
|
+
uses: pnpm/action-setup@v3
|
|
36
|
+
with:
|
|
37
|
+
version: 8
|
|
38
|
+
run_install: false
|
|
39
|
+
|
|
40
|
+
- name: Install dependencies
|
|
41
|
+
run: pnpm install
|
|
42
|
+
|
|
43
|
+
- name: Build package
|
|
44
|
+
run: pnpm run build
|
|
45
|
+
|
|
46
|
+
- name: Verify tag is set
|
|
47
|
+
run: test -n "${TAG}" || { echo "TAG missing"; exit 1; }
|
|
48
|
+
env:
|
|
49
|
+
TAG: ${{ github.event.release.tag_name || inputs.tag || '' }}
|
|
50
|
+
|
|
51
|
+
- name: Verify tag matches package.json version
|
|
52
|
+
env:
|
|
53
|
+
TAG: ${{ github.event.release.tag_name || inputs.tag }}
|
|
54
|
+
run: node -e 'const v=require("./package.json").version; const tag=(process.env.TAG||"").replace(/^v/,""); if(v!==tag){console.error("package.json version "+v+" does not match tag "+tag); process.exit(1)} else {console.log("Version matches tag:", v)}'
|
|
55
|
+
|
|
56
|
+
- name: Publish package to npmjs
|
|
57
|
+
env:
|
|
58
|
+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
59
|
+
run: pnpm publish --no-git-checks --access public
|
|
60
|
+
|
|
61
|
+
|