@witchcraft/expressit 0.2.0 → 0.2.2

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 (60) hide show
  1. package/README.md +5 -2
  2. package/dist/Lexer.d.ts +3 -5
  3. package/dist/Lexer.d.ts.map +1 -1
  4. package/dist/Lexer.js +21 -40
  5. package/dist/Parser.d.ts +1 -1
  6. package/dist/Parser.d.ts.map +1 -1
  7. package/dist/Parser.js +35 -64
  8. package/dist/ast/builders/condition.js +2 -4
  9. package/dist/ast/builders/delim.d.ts +1 -1
  10. package/dist/ast/builders/delim.d.ts.map +1 -1
  11. package/dist/ast/builders/delim.js +2 -4
  12. package/dist/ast/builders/expression.js +1 -2
  13. package/dist/ast/builders/pos.js +3 -6
  14. package/dist/ast/builders/variable.js +2 -4
  15. package/dist/ast/error.js +1 -2
  16. package/dist/ast/handlers.d.ts +2 -2
  17. package/dist/ast/handlers.d.ts.map +1 -1
  18. package/dist/ast/handlers.js +7 -11
  19. package/dist/defaults/defaultKeyParser.js +1 -2
  20. package/dist/examples/ParserWithSqlSupport.d.ts +62 -0
  21. package/dist/examples/ParserWithSqlSupport.d.ts.map +1 -0
  22. package/dist/examples/ParserWithSqlSupport.js +269 -0
  23. package/dist/examples/{shortcutContextParser.d.ts → ShortcutContextParser.d.ts} +5 -5
  24. package/dist/examples/ShortcutContextParser.d.ts.map +1 -0
  25. package/dist/examples/{shortcutContextParser.js → ShortcutContextParser.js} +6 -12
  26. package/dist/examples/index.d.ts +2 -1
  27. package/dist/examples/index.d.ts.map +1 -1
  28. package/dist/examples/index.js +3 -1
  29. package/dist/internal/ExpressitError.js +3 -5
  30. package/dist/internal/checkParserOpts.d.ts +1 -1
  31. package/dist/internal/checkParserOpts.d.ts.map +1 -1
  32. package/dist/internal/checkParserOpts.js +8 -16
  33. package/dist/internal/escapeVariableOrPrefix.js +4 -8
  34. package/dist/internal/parseParserOptions.d.ts +1 -1
  35. package/dist/internal/parseParserOptions.d.ts.map +1 -1
  36. package/dist/package.json.js +14 -147
  37. package/dist/types/parser.d.ts +6 -6
  38. package/dist/types/parser.d.ts.map +1 -1
  39. package/dist/utils/generateParentsMap.js +20 -40
  40. package/dist/utils/getCursorInfo.js +9 -17
  41. package/dist/utils/getOppositeDelimiter.js +3 -6
  42. package/dist/utils/prettyAst.js +1 -2
  43. package/package.json +25 -23
  44. package/src/Lexer.ts +5 -4
  45. package/src/Parser.ts +9 -3
  46. package/src/ast/builders/delim.ts +1 -1
  47. package/src/ast/handlers.ts +3 -3
  48. package/src/examples/ParserWithSqlSupport.ts +365 -0
  49. package/src/examples/{shortcutContextParser.ts → ShortcutContextParser.ts} +14 -14
  50. package/src/examples/index.ts +2 -1
  51. package/src/internal/ExpressitError.ts +2 -2
  52. package/src/internal/checkParserOpts.ts +3 -3
  53. package/src/internal/parseParserOptions.ts +2 -2
  54. package/src/types/parser.ts +6 -6
  55. package/src/utils/getCursorInfo.ts +1 -1
  56. package/dist/examples/shortcutContextParser.d.ts.map +0 -1
  57. package/dist/global.d.js +0 -1
  58. package/dist/package.js +0 -7
  59. package/src/global.d.ts +0 -4
  60. package/src/package.js +0 -11
@@ -0,0 +1,365 @@
1
+ import { unreachable } from "@alanscodelog/utils/unreachable.js"
2
+
3
+ import { Parser } from "../Parser.js"
4
+ import { AST_TYPE, type NormalizedCondition, type NormalizedExpression, type Position,TOKEN_TYPE } from "../types/ast.js"
5
+
6
+ export interface BasePropertyDefinition {
7
+ name: string
8
+ /** Supported types are: string, boolean, int, float, date. */
9
+ type: string
10
+ /** Return how to access the column in the WHERE condition of the SQL query, useful for json properties. It is up to you to properly quote the value if needed. */
11
+ transformToColumn?: (key: string, name: string) => string
12
+ /**
13
+ * A function that can be used to transform the value before it is inserted into the SQL query. Useful for types like arrays. It is up to you to properly escape values if you use the second parameter which contains the unescaped value.
14
+ */
15
+ transformValue?: (value: any, unescapedValue: any) => any | any[]
16
+ isArray?: boolean
17
+ /** If undefined, it's assumed all operators are supported. This should only include the final operator (see {@link BaseOperatorDefinition.operator}), it doesn't need to include it's aliases. */
18
+ supportedOperators?: string[]
19
+ /** Further transform the value after the basic parsing has been done. Useful to, for example, parse other strings (e.g. now, today, tomorrow) as dates. */
20
+ postParse?: (value: any) => any
21
+ }
22
+ export interface BaseOperatorDefinition {
23
+ /** The final operator to use in the SQL query and while evaluating. */
24
+ operator: string
25
+ /** All aliases the user can use to specify the operator. They need not include the real final operator. */
26
+ operators: string[]
27
+ /** All negated aliases to the operator. If an operator is listed here, it will be used to "invert" a condition when normalizing. e.g. < can list >= as a negated operator. This greatly simplifies queries. */
28
+ negatedOperators?: string[]
29
+ /** How to compare the value when evualuating a condition. This is only used if using `evaluate`. */
30
+ valueComparer: (condition: any, contextValue: any) => boolean
31
+ }
32
+ export type BaseErrorTokenTypes =
33
+ | "invalidKey"
34
+ | "unknownProperty"
35
+ | "unknownOperator"
36
+ | "unknownOperatorForType"
37
+ | "invalidValueType"
38
+ /*
39
+ * Creates an example parser with a `toSql` method that can be used to convert an AST to a a safe SQL query.
40
+ *
41
+ * The parser assumes all column names (i.e. the propertyDefinitions) and operators (i.e. operatorDefinitions) are vetted and safe to use in the SQL queries. They are double quoted (unless transformToColumn is specificed) so you can use special characters if needed. You can use the `transformToColumn` option to specify a custom name (e.g. for json columns) that will be left as is.
42
+ *
43
+ * Values are escaped with the given `sqlEscapeValue`. If using drizzle, for simple tables and types, you can just do and it'll be safe. More complex types, such as arrays, or json properties are more comlicated and you'll probably want to specify the definition's transformValue option. See the examples tests for examples.
44
+ * ```ts
45
+ * const parser = new ParserWithSqlSupport(
46
+ * propertyDefinitions,
47
+ * {
48
+ * sqlEscapeValue: (value: string) => sql`${value}`
49
+ * }
50
+ * )
51
+ * ```
52
+ *
53
+ */
54
+ export class ParserWithSqlSupport<TErrorToken extends
55
+ Position & {
56
+ type: BaseErrorTokenTypes
57
+ message?: string
58
+ } =
59
+ Position & {
60
+ type: BaseErrorTokenTypes
61
+ message?: string
62
+ },
63
+ TPropertyDefinition extends BasePropertyDefinition = BasePropertyDefinition,
64
+ TPropertyDefinitions extends Record<string, TPropertyDefinition> = Record<string, TPropertyDefinition>,
65
+ TOperatorDefinition extends BaseOperatorDefinition = BaseOperatorDefinition,
66
+ TOperatorDefinitions extends Record<string, TOperatorDefinition> = Record<string, TOperatorDefinition>,
67
+ TSqlEscapeValue extends (value: string) => any | ReturnType<TSqlEscapeValue> = (value: string) => any,
68
+ > extends Parser<TErrorToken> {
69
+ sqlEscapeValue: TSqlEscapeValue
70
+
71
+ operatorMap: Record<string, string>
72
+
73
+ constructor(
74
+ public propertyDefinitions: TPropertyDefinitions,
75
+ public operatorDefinitions: TOperatorDefinitions,
76
+ { sqlEscapeValue }: { sqlEscapeValue: TSqlEscapeValue }
77
+ ) {
78
+ const operators = []
79
+ const operatorMap: Record<string, string> = {}
80
+ for (const value of Object.values(operatorDefinitions)) {
81
+ for (const operator of value.operators) {
82
+ operatorMap[operator] = value.operator
83
+ operators.push(operator)
84
+ }
85
+ if (value.negatedOperators) {
86
+ for (const operator of value.negatedOperators) {
87
+ operatorMap[operator] = value.operator
88
+ operators.push(operator)
89
+ }
90
+ }
91
+ }
92
+ super({
93
+ arrayValues: true,
94
+ regexValues: false,
95
+ keywords: {
96
+ and: [{ isSymbol: true, value: "&&" }],
97
+ or: [{ isSymbol: true, value: "||" }],
98
+ not: [{ isSymbol: true, value: "!" }],
99
+ },
100
+ customPropertyOperators: operators,
101
+ prefixableGroups: false,
102
+ valueComparer: (condition, contextValue, _context) => {
103
+ if (typeof condition.value !== typeof contextValue) {
104
+ throw new Error(`Expected type of property ${condition.property[0]} to be the same type as the context value ${contextValue} (${typeof contextValue}). If the ast has been validated this is likely because the type of the context value is incorrect.`)
105
+ }
106
+
107
+ const prop = condition.property[0]
108
+ if (!prop) unreachable("Did you validate the ast before evaluating it?")
109
+
110
+ const propDefinition = propertyDefinitions[prop]
111
+ const operatorDefinition = condition.operator && operatorDefinitions[condition.operator]
112
+ if (!operatorDefinition) unreachable("Did you validate the ast before evaluating it?")
113
+
114
+ const isSupported = !propDefinition.supportedOperators?.includes(condition.operator!)
115
+ if (!isSupported) unreachable("Did you validate the ast before evaluating it?")
116
+
117
+ const res = operatorDefinition.valueComparer(condition, contextValue)
118
+ return res
119
+ },
120
+ valueValidator: (_contextValue, query): TErrorToken[] | void => {
121
+ const prop = query.propertyKeys[0]
122
+ let tokens: TErrorToken[] = []
123
+ const propDefinition = propertyDefinitions[prop]
124
+ if (!propDefinition) {
125
+ tokens = tokens.concat(query.property.map(token => ({
126
+ start: token.start,
127
+ end: token.end,
128
+ type: "unknownProperty",
129
+ })) as TErrorToken[])
130
+ return tokens
131
+ }
132
+ const op = query.operator
133
+ const opKey = op && operatorMap[op.value]
134
+ if (!op || !opKey) {
135
+ tokens.push({
136
+ start: (op ?? query.condition)?.start,
137
+ end: (op ?? query.condition)?.end,
138
+ type: "unknownOperator",
139
+ } as TErrorToken)
140
+ } else {
141
+ if (propDefinition.supportedOperators && !propDefinition.supportedOperators?.includes(opKey)) {
142
+ tokens.push({
143
+ start: query.condition.start,
144
+ end: query.condition.end,
145
+ type: "unknownOperatorForType",
146
+ } as TErrorToken)
147
+ }
148
+ }
149
+
150
+ const val = query.value
151
+ if (Array.isArray(val)) {
152
+ for (const v of val) {
153
+ if (v.type !== "VARIABLE") unreachable()
154
+ const res = convertAndValidateValue(query.isQuoted, v.value.value, prop, propertyDefinitions, { isArray: true })
155
+ if (res instanceof Error) {
156
+ if (v.type !== "VARIABLE") unreachable()
157
+ const token = v
158
+ tokens.push({
159
+ start: token.start,
160
+ end: token.end,
161
+ type: "invalidValueType",
162
+ message: res.message,
163
+ } as TErrorToken)
164
+ }
165
+ }
166
+ if (tokens.length > 0) return tokens
167
+ return
168
+ }
169
+
170
+ if (val?.type !== "VARIABLE") unreachable()
171
+ const value = val.value.value
172
+ const res = convertAndValidateValue(query.isQuoted, value, prop, propertyDefinitions)
173
+ if (res instanceof Error) {
174
+ if (!query.value || query.value.type !== "VARIABLE") unreachable()
175
+ const token = query.value.value
176
+ tokens.push({
177
+ start: token.start,
178
+ end: token.end,
179
+ type: "invalidValueType",
180
+ message: res.message,
181
+ } as TErrorToken)
182
+ }
183
+
184
+ if (tokens.length > 0) return tokens
185
+ },
186
+ conditionNormalizer(query) {
187
+ const prop = query.property?.[0]
188
+ if (!prop) unreachable("Did you validate the ast before normalizing it?")
189
+ const propDefinition = propertyDefinitions[prop]
190
+
191
+ let finalValue
192
+ if (Array.isArray(query.value)) {
193
+ const values = []
194
+ if (query.condition.value.type !== AST_TYPE.ARRAY) unreachable()
195
+ const raw = query.condition.value.values
196
+ for (let i = 0; i < query.value.length; i += 1) {
197
+ const token = raw[i]
198
+ const val = query.value[i]
199
+ const isQuoted = !!token.quote
200
+ const res = convertAndValidateValue(isQuoted, val, prop, propertyDefinitions, { isArray: true })
201
+ if (res instanceof Error) throw res
202
+ values.push(res)
203
+ }
204
+ finalValue = values
205
+ } else {
206
+ finalValue = convertAndValidateValue(query.isQuoted, query.value, prop, propertyDefinitions)
207
+ if (propDefinition.isArray) {
208
+ finalValue = [finalValue]
209
+ }
210
+ }
211
+
212
+ let finalOperator: any = query.operator
213
+ if (finalValue instanceof Error) throw finalValue
214
+ const opKey = query.operator && operatorMap[query.operator]
215
+ if (!opKey) unreachable("Did you validate the ast before normalizing it?")
216
+
217
+ const operatorDefinition = opKey && operatorDefinitions[opKey]
218
+ if (!operatorDefinition) unreachable("Did you validate the ast before normalizing it?")
219
+
220
+
221
+ const isNegatableOperator = operatorDefinition.negatedOperators?.includes(query.operator!)
222
+
223
+ finalOperator = operatorDefinition.operator
224
+ let isNegated = query.isNegated
225
+ if (isNegatableOperator) {
226
+ isNegated = !isNegated
227
+ }
228
+
229
+ return { value: finalValue, operator: finalOperator, negate: isNegated }
230
+ },
231
+ })
232
+ this.operatorMap = operatorMap
233
+ this.sqlEscapeValue = sqlEscapeValue
234
+ }
235
+
236
+ toSql<T>(
237
+ ast: NormalizedExpression<any, any> | NormalizedCondition<any, any>,
238
+ /**
239
+ * Optionally convert the raw strings to something else. If uding drizzle, you can pass sql.raw here. So later you can just sql.join the return of the function:
240
+ *
241
+ * ```ts
242
+ * sql.join([
243
+ * sql.raw(db.select().from(someTable).toSQL().sql),
244
+ * sql.raw(`where`),
245
+ * ...parser.toSql(ast, sql.raw)
246
+ * ], sql` `)
247
+ * ```
248
+ */
249
+ wrapStrings?: (value: string) => T,
250
+ ): (ReturnType<TSqlEscapeValue> | typeof wrapStrings extends undefined ? string : T)[] {
251
+ this._checkEvaluationOptions()
252
+ const chunks = []
253
+
254
+ if (ast.type === AST_TYPE.NORMALIZED_CONDITION) {
255
+ const prop = ast.property?.[0]
256
+ const definition = this.propertyDefinitions[prop]
257
+ const value = ast.value
258
+ const col = definition.transformToColumn?.(prop, definition.name) ?? `"${prop}"`
259
+ const op = ast.operator
260
+ if (ast.negate) {
261
+ chunks.push(wrapStrings?.(`NOT(`) ?? `NOT(`)
262
+ }
263
+ chunks.push(wrapStrings?.(`${col} `) ?? `${col} `)
264
+ chunks.push(wrapStrings?.(`${op} `) ?? `${op} `)
265
+ const val = this.sqlEscapeValue(value)
266
+ if (definition.transformValue) {
267
+ const transformed = definition.transformValue(val, value)
268
+ if (Array.isArray(transformed)) {
269
+ chunks.push(...transformed)
270
+ } else {
271
+ chunks.push(transformed)
272
+ }
273
+ } else {
274
+ chunks.push(val)
275
+ }
276
+ if (ast.negate) {
277
+ chunks.push(wrapStrings?.(`)`) ?? `)`)
278
+ }
279
+ return chunks
280
+ }
281
+ if (ast.type === AST_TYPE.NORMALIZED_EXPRESSION) {
282
+ const left = this.toSql(ast.left, wrapStrings)
283
+ const right = this.toSql(ast.right, wrapStrings)
284
+ const op = ast.operator === TOKEN_TYPE.AND ? "AND" : "OR"
285
+ chunks.push(wrapStrings?.(`(`) ?? `(`)
286
+ chunks.push(...left)
287
+ chunks.push(wrapStrings?.(` ${op} `) ?? ` ${op} `)
288
+ chunks.push(...right)
289
+ chunks.push(wrapStrings?.(`)`) ?? `)`)
290
+ return chunks as any
291
+ }
292
+
293
+ return unreachable()
294
+ }
295
+ }
296
+ export function createTypeError(prop: string, type: string, isArray: boolean): Error {
297
+ if (isArray) {
298
+ return new Error(`Property ${prop} must contain items of type ${type}.`)
299
+ }
300
+ return new Error(`Property ${prop} must be of type ${type}.`)
301
+ }
302
+
303
+ export function convertAndValidateValue(
304
+ isQuoted: boolean,
305
+ value: any,
306
+ prop: string,
307
+ propertyDefinitions: Record<string, BasePropertyDefinition>,
308
+ { isArray = false }: { isArray?: boolean } = {},
309
+ ): any {
310
+ let finalValue: any = value
311
+ let isFloat = false
312
+ const propDefinition = propertyDefinitions[prop]
313
+
314
+ if (typeof value === "string" && !isQuoted) {
315
+ if (finalValue === "true") {
316
+ finalValue = true
317
+ } else if (finalValue === "false") {
318
+ finalValue = false
319
+ } else {
320
+ const asNum = parseInt(value, 10)
321
+ if (!isNaN(asNum)) {
322
+ finalValue = asNum
323
+ } else {
324
+ const asFloat = parseFloat(value)
325
+ if (!isNaN(asFloat)) {
326
+ finalValue = asFloat
327
+ isFloat = true
328
+ }
329
+ }
330
+ }
331
+ }
332
+ const type = propDefinition.type
333
+ finalValue = propDefinition.postParse?.(finalValue) ?? finalValue
334
+
335
+ switch (type) {
336
+ case "integer":
337
+ case "float": {
338
+ if (typeof finalValue !== "number" || (type === "float" && !isFloat) || (type === "integer" && isFloat)) {
339
+ return createTypeError(prop, type, isArray)
340
+ }
341
+ break
342
+ }
343
+ case "string":
344
+ case "boolean": {
345
+ if (typeof finalValue !== propDefinition.type) {
346
+ return createTypeError(prop, type, isArray)
347
+ }
348
+ break
349
+ }
350
+ case "date": {
351
+ if (finalValue instanceof Date) {
352
+ break
353
+ }
354
+ const maybeDate = new Date(finalValue)
355
+ if (isNaN(maybeDate.getTime())) {
356
+ return createTypeError(prop, "date", isArray)
357
+ } else {
358
+ finalValue = maybeDate
359
+ }
360
+ break
361
+ }
362
+ }
363
+
364
+ return finalValue
365
+ }
@@ -1,3 +1,7 @@
1
+ import { Parser } from "../Parser.js"
2
+ import type { Position } from "../types/ast.js"
3
+ import type { ValueQuery } from "../types/parser.js"
4
+
1
5
  /* TODO TOUPDATE */
2
6
  /**
3
7
  * A pre-configured parser for parsing shortcut contexts (similar to VSCode's [when clause contexts](https://code.visualstudio.com/docs/getstarted/keybindings#_when-clause-contexts)).
@@ -9,15 +13,11 @@
9
13
  * The validate function will return a list of positions with a list of errors which includes handling invalid or duplicate regex flags.
10
14
  */
11
15
 
12
- import { Parser } from "../Parser.js"
13
- import type { Position } from "../types/ast.js"
14
- import type { ValueQuery } from "../types/parser.js"
15
-
16
16
 
17
- export class ShortcutContextParser<T extends
17
+ export class ShortcutContextParser<TErrorTokens extends
18
18
  Position & { type: ("invalidKey" | "unregexableKey" | "invalidRegexFlag" | "duplicateRegexFlag") } =
19
19
  Position & { type: ("invalidKey" | "unregexableKey" | "invalidRegexFlag" | "duplicateRegexFlag") },
20
- > extends Parser<T> {
20
+ > extends Parser<TErrorTokens> {
21
21
  validKeys: string[] = []
22
22
 
23
23
  regexablekeys: string[] = []
@@ -49,21 +49,21 @@ export class ShortcutContextParser<T extends
49
49
  }
50
50
  return contextValue === condition.value
51
51
  },
52
- valueValidator: (_contextValue, query): T[] | void => {
53
- let tokens: T[] = []
52
+ valueValidator: (_contextValue, query): TErrorTokens[] | void => {
53
+ let tokens: TErrorTokens[] = []
54
54
  if (!this.validKeys.includes(query.propertyName!)) {
55
55
  tokens = tokens.concat(query.property.map(token => ({
56
56
  start: token.start,
57
57
  end: token.end,
58
58
  type: "invalidKey",
59
- })) as T[])
59
+ })) as TErrorTokens[])
60
60
  }
61
61
  if (query.isRegex && !this.regexablekeys.includes(query.propertyName!)) {
62
62
  tokens = tokens.concat(query.property.map(token => ({
63
63
  start: token.start,
64
64
  end: token.end,
65
65
  type: "unregexableKey",
66
- })) as T[])
66
+ })) as TErrorTokens[])
67
67
  }
68
68
  if (query.regexFlags) {
69
69
  const chars = query.regexFlags.value.split("")
@@ -75,14 +75,14 @@ export class ShortcutContextParser<T extends
75
75
  start: start + i,
76
76
  end: start + i + 1,
77
77
  type: "duplicateRegexFlag",
78
- } as T)
78
+ } as TErrorTokens)
79
79
  }
80
80
  if (!validRegexFlags.includes(char)) {
81
81
  tokens.push({
82
82
  start: start + i,
83
83
  end: start + i + 1,
84
84
  type: "invalidRegexFlag",
85
- } as T)
85
+ } as TErrorTokens)
86
86
  }
87
87
  }
88
88
  }
@@ -93,7 +93,7 @@ export class ShortcutContextParser<T extends
93
93
  let finalOperator: any = operator
94
94
  // another way to allow special unquoted value types is something like this:
95
95
  if (typeof value === "string" && !isQuoted) {
96
- const asNum = parseInt(value, 2)
96
+ const asNum = parseInt(value, 10)
97
97
  if (!isNaN(asNum)) finalValue = asNum
98
98
  if (["true", "false"].includes(value)) {
99
99
  finalValue = value === "true"
@@ -132,7 +132,7 @@ export class ShortcutContextParser<T extends
132
132
  for (const key of Object.keys(context)) {
133
133
  if (typeof context[key] === "boolean") {
134
134
  this.validKeys.push(prev ? `${prev}.${key}` : key)
135
- if (context[key] === true) {
135
+ if (context[key]) {
136
136
  this.regexablekeys.push(prev ? `${prev}.${key}` : key)
137
137
  }
138
138
  } else {
@@ -1,3 +1,4 @@
1
1
  /* Autogenerated Index */
2
2
 
3
- export { ShortcutContextParser } from "./shortcutContextParser.js"
3
+ export { ShortcutContextParser } from "./ShortcutContextParser.js"
4
+ export { ParserWithSqlSupport } from "./ParserWithSqlSupport.js"
@@ -3,8 +3,8 @@ import { indent } from "@alanscodelog/utils/indent.js"
3
3
  import { pretty } from "@alanscodelog/utils/pretty.js"
4
4
  import type { Keys } from "@alanscodelog/utils/types"
5
5
 
6
- // @ts-expect-error todo
7
- import { repository, version } from "../package.js"
6
+ import packageJson from "../../package.json" assert { type: "json" }
7
+ const { version, repository } = packageJson
8
8
  import type { ERROR_CODES, ErrorInfo } from "../types/errors.js"
9
9
 
10
10
  /** @internal */
@@ -9,7 +9,7 @@ import { ERROR_CODES } from "../types/errors.js"
9
9
  import type { FullParserOptions, ParserOptions } from "../types/parser.js"
10
10
 
11
11
  /** @internal */
12
- export function checkParserOpts<T extends {}>(opts: FullParserOptions<T>, evaluatorChecks: boolean = false, validatorChecks: boolean = false): void {
12
+ export function checkParserOpts<T>(opts: FullParserOptions<T>, evaluatorChecks: boolean = false, validatorChecks: boolean = false): void {
13
13
  if (!evaluatorChecks) {
14
14
  const keywordsList = [...opts.keywords.and, ...opts.keywords.or, ...opts.keywords.not].map(keyword => keyword.value)
15
15
  const symNots = opts.keywords.not.filter(op => op.isSymbol).map(op => op.value)
@@ -48,8 +48,8 @@ export function checkParserOpts<T extends {}>(opts: FullParserOptions<T>, evalua
48
48
  `prefixableStrings cannot contain blank entries`,
49
49
  )
50
50
  }
51
- for (const key of ["and", "or", "not"]) {
52
- const invalid = opts.keywords[key as keyof FullParserOptions["keywords"]]
51
+ for (const key of ["and", "or", "not"] as const) {
52
+ const invalid = opts.keywords[key]
53
53
  ?.find(_ => isBlank(_.value))
54
54
  ?.value
55
55
  if (invalid !== undefined) {
@@ -5,10 +5,10 @@ import { defaultValueComparer } from "../defaults/defaultValueComparer.js"
5
5
  import type { FullParserOptions, ParserOptions } from "../types/parser.js"
6
6
 
7
7
  /** @internal */
8
- export function parseParserOptions<T extends {} = {}>(
8
+ export function parseParserOptions<T>(
9
9
  options: ParserOptions<T>,
10
10
  ): FullParserOptions<T> {
11
- const opts: ParserOptions = {
11
+ const opts: ParserOptions<T> = {
12
12
  prefixApplier: defaultPrefixApplier,
13
13
  keyParser: defaultKeyParser,
14
14
  valueComparer: defaultValueComparer,
@@ -4,7 +4,7 @@ import type { ArrayNode, ConditionNode, NormalizedCondition, Position, TOKEN_TYP
4
4
 
5
5
 
6
6
  // #partially-synced
7
- export type FullParserOptions<T extends {} = {}> = MakeRequired<
7
+ export type FullParserOptions<T = any> = MakeRequired<
8
8
  ParserOptions<T>,
9
9
  // makes required all except:
10
10
  Exclude<keyof ParserOptions<T>,
@@ -18,7 +18,7 @@ export type FullParserOptions<T extends {} = {}> = MakeRequired<
18
18
  // overrides
19
19
  keywords: DeepRequired<KeywordOptions>
20
20
  }
21
- export type ParserOptions<T extends {} = {}> = {
21
+ export type ParserOptions<T = any> = {
22
22
  /**
23
23
  * Allows any conditions (i.e. a variable or negated variable) to precede groups and append themselves to all variables inside them. Regular use of groups for changing precedence (e.g. `(a || b) && c` ) or negating expressions `!(a || b)` is still supported even if `prefixableGroups` is false.
24
24
  *
@@ -116,7 +116,7 @@ export type ParserOptions<T extends {} = {}> = {
116
116
  /**
117
117
  * Enables regex strings as values. The value is treated as if it was quoted by forward slashes. Any repetition of lowercase characters (even if there are multiple) attached to the end are assumed to be flags and added as a single token to the value's `quotes.mode` property.
118
118
  *
119
- * Can be passed a custom function to determine when to use the regex value or not (it is converted to a regular value). The function is passed the property, the operator, and whether it's an expanded operator. If their is an error token for the property or operator, an empty string is passed.
119
+ * Can be passed a custom function to determine when to use the regex value or not (it is converted to a regular value). The function is passed the property, the operator, and whether it's an expanded operator. If there is an error token for the property or operator, an empty string is passed.
120
120
  *
121
121
  * ```ts
122
122
  * // allow anything (`prop=/val/`, `prop:op:/val`, `prop=(/val/)`, `prop:op(/val/)`) but the value alone (`/regex/`)
@@ -272,7 +272,7 @@ export type ParserOptions<T extends {} = {}> = {
272
272
  * ```ts
273
273
  * type Operators = "contains"
274
274
  * function valueComparer(condition: Omit<Condition, "negate">, contextValue: any, context: any): boolean {
275
- * switch (operator as Operators) {
275
+ * switch (condition.operator as Operators) {
276
276
  * case "contains": return (contextValue as string[]).includes(condition.value as string)
277
277
  * // ...
278
278
  * }
@@ -307,13 +307,13 @@ export type ParserOptions<T extends {} = {}> = {
307
307
  * if (prefix) {
308
308
  * const val = value as string // it's always a string if prefixed
309
309
  * switch (prefix as RawPrefixes) {
310
- * case "num": finalValue = parseInt(val, 2); break
310
+ * case "num": finalValue = parseInt(val, 10); break
311
311
  * // ...
312
312
  * }
313
313
  * }
314
314
  * // another way to allow special unquoted value types is something like this:
315
315
  * if (typeof value === "string" && !isQuoted) {
316
- * const asNum = parseInt(value, 2)
316
+ * const asNum = parseInt(value, 10)
317
317
  * if (!isNaN(asNum)) finalValue = asNum
318
318
  * if (["true","false"].includes(value)) {
319
319
  * finalValue = value === "true" ? true : false
@@ -56,7 +56,7 @@ export function getCursorInfo(
56
56
  }
57
57
  }
58
58
  if (token.start >= index) {
59
- info.next ||= token
59
+ info.next ??= token
60
60
  if (token.valid && !info.valid.next) {
61
61
  info.valid.next = token
62
62
  break
@@ -1 +0,0 @@
1
- {"version":3,"file":"shortcutContextParser.d.ts","sourceRoot":"","sources":["../../src/examples/shortcutContextParser.ts"],"names":[],"mappings":"AACA;;;;;;;;GAQG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AACrC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AAI/C,qBAAa,qBAAqB,CAAC,CAAC,SACnC,QAAQ,GAAG;IAAE,IAAI,EAAE,CAAC,YAAY,GAAG,gBAAgB,GAAG,kBAAkB,GAAG,oBAAoB,CAAC,CAAA;CAAE,GAClG,QAAQ,GAAG;IAAE,IAAI,EAAE,CAAC,YAAY,GAAG,gBAAgB,GAAG,kBAAkB,GAAG,oBAAoB,CAAC,CAAA;CAAE,CACjG,SAAQ,MAAM,CAAC,CAAC,CAAC;IAClB,SAAS,EAAE,MAAM,EAAE,CAAK;IAExB,aAAa,EAAE,MAAM,EAAE,CAAK;gBAG3B,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACjC,eAAe,GAAE,MAAM,EAAoB;IAmG5C,UAAU,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI;IAK9C,OAAO,CAAC,uBAAuB;CAa/B"}
package/dist/global.d.js DELETED
@@ -1 +0,0 @@
1
-
package/dist/package.js DELETED
@@ -1,7 +0,0 @@
1
- import pkg from "./package.json.js";
2
- const version = pkg.version;
3
- const repository = pkg.repository;
4
- export {
5
- repository,
6
- version
7
- };
package/src/global.d.ts DELETED
@@ -1,4 +0,0 @@
1
- declare module "./package.js" {
2
- export const version: string
3
- export const repository: string
4
- }
package/src/package.js DELETED
@@ -1,11 +0,0 @@
1
- import pkg from "../package.json"
2
-
3
- /**
4
- * This library imports it's own package.json for inserting the version number into errors. Doing this is not a problem normally (because babel is used to transpile the typescript instead of tsc), but when tsc is used to output the types, the output is nested (dist-types/src/*) because of the json import being outside the src dir. There is a way around this but it's complicated. The easier way is to just cheat and do the importing in this js file. Typescript consumes it none the wiser and babel transpiles it to cjs/es accordingly.
5
- *
6
- * The values could be inlined, but there are no battle tested actively maintained babel plugins for this, and problems would be hard to debug (given when developing, the package.json never contains the real version because the library is semantically released.)
7
- */
8
-
9
-
10
- export const version = pkg.version
11
- export const repository = pkg.repository