wizard-ql 2.0.1 → 2.1.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/dist/errors.d.ts CHANGED
@@ -14,15 +14,18 @@ export declare class ParseError extends Error {
14
14
  readonly startIndex: number | undefined;
15
15
  /** The end token index */
16
16
  readonly endIndex: number | undefined;
17
+ /** The tokens being parsed */
18
+ readonly tokens: Token[] | undefined;
17
19
  /**
18
20
  * Construct a Parsing Error
19
21
  * @param message The error message
22
+ * @param tokens The tokens being parsed
20
23
  * @param startToken The first token this error pertains to
21
24
  * @param startIndex The start token index
22
25
  * @param endToken The last token this error pertains to
23
26
  * @param endIndex The end token index
24
27
  */
25
- constructor(message: string, startToken: Token | undefined, startIndex: number | undefined, endToken?: Token | undefined, endIndex?: number | undefined);
28
+ constructor(message: string, tokens: Token[] | undefined, startToken: Token | undefined, startIndex: number | undefined, endToken?: Token | undefined, endIndex?: number | undefined);
26
29
  }
27
30
  /**
28
31
  * A WizardQL string expression parsing constraint error
@@ -39,13 +42,16 @@ export declare class ConstraintError extends Error {
39
42
  readonly startIndex: number | undefined;
40
43
  /** The end token index */
41
44
  readonly endIndex: number | undefined;
45
+ /** The tokens being parsed */
46
+ readonly tokens: Token[] | undefined;
42
47
  /**
43
48
  * Construct a Constraint Error
44
49
  * @param message The error message
50
+ * @param tokens The tokens being parsed
45
51
  * @param startToken The first token this error pertains to
46
52
  * @param startIndex The start token index
47
53
  * @param endToken The last token this error pertains to
48
54
  * @param endIndex The end token index
49
55
  */
50
- constructor(message: string, startToken: Token | undefined, startIndex: number | undefined, endToken?: Token | undefined, endIndex?: number | undefined);
56
+ constructor(message: string, tokens: Token[] | undefined, startToken: Token | undefined, startIndex: number | undefined, endToken?: Token | undefined, endIndex?: number | undefined);
51
57
  }
package/dist/index.js CHANGED
@@ -1,4 +1,14 @@
1
1
  // src/spec.ts
2
+ var QUOTES = ["'", '"', "`"];
3
+ var BRACKETS = [
4
+ ["[", "]"],
5
+ ["{", "}"]
6
+ ];
7
+ var PARENS = [
8
+ ["(", ")"]
9
+ ];
10
+ var NEGATORS = ["!"];
11
+ var ARRAY_DELIMITERS = [","];
2
12
  var OPERATION_ALIAS_DICTIONARY = {
3
13
  AND: "AND",
4
14
  "&&": "AND",
@@ -72,7 +82,6 @@ var COMPARISON_TYPE_DICTIONARY = {
72
82
  var TYPE_PRIORITY = ["boolean", "date", "number", "string"];
73
83
  // src/regex.ts
74
84
  var ESCAPE_REGEX = "(?<=(?<!\\\\)(?:\\\\\\\\)*)";
75
- var QUOTES = ["'", '"', "`"];
76
85
  var QUOTE_REGEX_STR = `${ESCAPE_REGEX}(?<quote>${QUOTES.map((q) => RegExp.escape(q)).join("|")})(?<quotecontent>.*?)${ESCAPE_REGEX}\\k<quote>`;
77
86
 
78
87
  // src/errors.ts
@@ -106,9 +115,11 @@ class ParseError extends Error {
106
115
  endToken;
107
116
  startIndex;
108
117
  endIndex;
109
- constructor(message, startToken, startIndex, endToken = startToken, endIndex = startIndex) {
118
+ tokens;
119
+ constructor(message, tokens, startToken, startIndex, endToken = startToken, endIndex = startIndex) {
110
120
  const header = constructHeader(startToken, startIndex, endToken, endIndex);
111
121
  super(header + message);
122
+ this.tokens = tokens;
112
123
  this.rawMessage = message;
113
124
  this.startToken = startToken;
114
125
  this.endToken = endToken;
@@ -124,9 +135,11 @@ class ConstraintError extends Error {
124
135
  endToken;
125
136
  startIndex;
126
137
  endIndex;
127
- constructor(message, startToken, startIndex, endToken = startToken, endIndex = startIndex) {
138
+ tokens;
139
+ constructor(message, tokens, startToken, startIndex, endToken = startToken, endIndex = startIndex) {
128
140
  const header = constructHeader(startToken, startIndex, endToken, endIndex);
129
141
  super(header + message);
142
+ this.tokens = tokens;
130
143
  this.rawMessage = message;
131
144
  this.startToken = startToken;
132
145
  this.endToken = endToken;
@@ -136,7 +149,7 @@ class ConstraintError extends Error {
136
149
  }
137
150
 
138
151
  // src/parse.ts
139
- var TOKEN_REGEX = new RegExp("(?<=(?<!\\\\)(?:\\\\\\\\)*)(?<quote>\\x27|\\x22|\\x60)(?<quotecontent>.*?)(?<=(?<!\\\\)(?:\\\\\\\\)*)\\k<quote>|(?<=(?<=(?<!\\\\)(?:\\\\\\\\)*)\\s|^)\\x41ND(?=(?<=(?<!\\\\)(?:\\\\\\\\)*)\\s|$)|(?<=(?<!\\\\)(?:\\\\\\\\)*)\\x26\\x26|(?<=(?<!\\\\)(?:\\\\\\\\)*)\\x26|(?<=(?<!\\\\)(?:\\\\\\\\)*)\\^|(?<=(?<=(?<!\\\\)(?:\\\\\\\\)*)\\s|^)\\x4fR(?=(?<=(?<!\\\\)(?:\\\\\\\\)*)\\s|$)|(?<=(?<!\\\\)(?:\\\\\\\\)*)\\|\\||(?<=(?<!\\\\)(?:\\\\\\\\)*)\\||(?<=(?<=(?<!\\\\)(?:\\\\\\\\)*)\\s|^)\\x56(?=(?<=(?<!\\\\)(?:\\\\\\\\)*)\\s|$)|(?<=(?<=(?<!\\\\)(?:\\\\\\\\)*)\\s|^)\\x47EQ(?=(?<=(?<!\\\\)(?:\\\\\\\\)*)\\s|$)|(?<=(?<!\\\\)(?:\\\\\\\\)*)\\x3e\\x3d|(?<=(?<!\\\\)(?:\\\\\\\\)*)\\x3d\\x3e|(?<=(?<=(?<!\\\\)(?:\\\\\\\\)*)\\s|^)\\x4cEQ(?=(?<=(?<!\\\\)(?:\\\\\\\\)*)\\s|$)|(?<=(?<!\\\\)(?:\\\\\\\\)*)\\x3c\\x3d|(?<=(?<!\\\\)(?:\\\\\\\\)*)\\x3d\\x3c|(?<=(?<=(?<!\\\\)(?:\\\\\\\\)*)\\s|^)\\x4eOTEQUALS(?=(?<=(?<!\\\\)(?:\\\\\\\\)*)\\s|$)|(?<=(?<=(?<!\\\\)(?:\\\\\\\\)*)\\s|^)\\x4eOTEQUAL(?=(?<=(?<!\\\\)(?:\\\\\\\\)*)\\s|$)|(?<=(?<=(?<!\\\\)(?:\\\\\\\\)*)\\s|^)\\x4eEQ(?=(?<=(?<!\\\\)(?:\\\\\\\\)*)\\s|$)|(?<=(?<=(?<!\\\\)(?:\\\\\\\\)*)\\s|^)\\x49SNT(?=(?<=(?<!\\\\)(?:\\\\\\\\)*)\\s|$)|(?<=(?<!\\\\)(?:\\\\\\\\)*)\\x21\\x3d\\x3d|(?<=(?<!\\\\)(?:\\\\\\\\)*)\\x21\\x3d|(?<=(?<=(?<!\\\\)(?:\\\\\\\\)*)\\s|^)\\x45QUALS(?=(?<=(?<!\\\\)(?:\\\\\\\\)*)\\s|$)|(?<=(?<=(?<!\\\\)(?:\\\\\\\\)*)\\s|^)\\x45QUAL(?=(?<=(?<!\\\\)(?:\\\\\\\\)*)\\s|$)|(?<=(?<=(?<!\\\\)(?:\\\\\\\\)*)\\s|^)\\x45Q(?=(?<=(?<!\\\\)(?:\\\\\\\\)*)\\s|$)|(?<=(?<=(?<!\\\\)(?:\\\\\\\\)*)\\s|^)\\x49S(?=(?<=(?<!\\\\)(?:\\\\\\\\)*)\\s|$)|(?<=(?<!\\\\)(?:\\\\\\\\)*)\\x3d\\x3d|(?<=(?<!\\\\)(?:\\\\\\\\)*)\\x3d|(?<=(?<=(?<!\\\\)(?:\\\\\\\\)*)\\s|^)\\x4cESS(?=(?<=(?<!\\\\)(?:\\\\\\\\)*)\\s|$)|(?<=(?<!\\\\)(?:\\\\\\\\)*)\\x3c|(?<=(?<=(?<!\\\\)(?:\\\\\\\\)*)\\s|^)\\x47REATER(?=(?<=(?<!\\\\)(?:\\\\\\\\)*)\\s|$)|(?<=(?<!\\\\)(?:\\\\\\\\)*)\\x3e|(?<=(?<=(?<!\\\\)(?:\\\\\\\\)*)\\s|^)\\x4dORE(?=(?<=(?<!\\\\)(?:\\\\\\\\)*)\\s|$)|(?<=(?<=(?<!\\\\)(?:\\\\\\\\)*)\\s|^)\\x4eOTIN(?=(?<=(?<!\\\\)(?:\\\\\\\\)*)\\s|$)|(?<=(?<!\\\\)(?:\\\\\\\\)*)\\x21\\x3a|(?<=(?<=(?<!\\\\)(?:\\\\\\\\)*)\\s|^)\\x49N(?=(?<=(?<!\\\\)(?:\\\\\\\\)*)\\s|$)|(?<=(?<!\\\\)(?:\\\\\\\\)*)\\x3a|(?<=(?<=(?<!\\\\)(?:\\\\\\\\)*)\\s|^)\\x4eOTMATCHES(?=(?<=(?<!\\\\)(?:\\\\\\\\)*)\\s|$)|(?<=(?<=(?<!\\\\)(?:\\\\\\\\)*)\\s|^)\\x4eOTMATCH(?=(?<=(?<!\\\\)(?:\\\\\\\\)*)\\s|$)|(?<=(?<!\\\\)(?:\\\\\\\\)*)\\x21\\x7e|(?<=(?<=(?<!\\\\)(?:\\\\\\\\)*)\\s|^)\\x4dATCHES(?=(?<=(?<!\\\\)(?:\\\\\\\\)*)\\s|$)|(?<=(?<=(?<!\\\\)(?:\\\\\\\\)*)\\s|^)\\x4dATCH(?=(?<=(?<!\\\\)(?:\\\\\\\\)*)\\s|$)|(?<=(?<!\\\\)(?:\\\\\\\\)*)\\x7e|(?<=(?<!\\\\)(?:\\\\\\\\)*)\\(|(?<=(?<!\\\\)(?:\\\\\\\\)*)\\)|(?<=(?<!\\\\)(?:\\\\\\\\)*)\\[|(?<=(?<!\\\\)(?:\\\\\\\\)*)\\]|(?<=(?<!\\\\)(?:\\\\\\\\)*)\\{|(?<=(?<!\\\\)(?:\\\\\\\\)*)\\}|(?<=(?<!\\\\)(?:\\\\\\\\)*)\\x2c|(?<=(?<!\\\\)(?:\\\\\\\\)*)\\x21", "g");
152
+ var TOKEN_REGEX = new RegExp("(?<=(?<!\\\\)(?:\\\\\\\\)*)(?<quote>\\x27|\\x22|\\x60)(?<quotecontent>.*?)(?<=(?<!\\\\)(?:\\\\\\\\)*)\\k<quote>|(?<=(?<=(?<!\\\\)(?:\\\\\\\\)*)\\s|^)\\x41ND(?=(?<=(?<!\\\\)(?:\\\\\\\\)*)\\s|$)|(?<=(?<!\\\\)(?:\\\\\\\\)*)\\x26\\x26|(?<=(?<!\\\\)(?:\\\\\\\\)*)\\x26|(?<=(?<!\\\\)(?:\\\\\\\\)*)\\^|(?<=(?<=(?<!\\\\)(?:\\\\\\\\)*)\\s|^)\\x4fR(?=(?<=(?<!\\\\)(?:\\\\\\\\)*)\\s|$)|(?<=(?<!\\\\)(?:\\\\\\\\)*)\\|\\||(?<=(?<!\\\\)(?:\\\\\\\\)*)\\||(?<=(?<=(?<!\\\\)(?:\\\\\\\\)*)\\s|^)\\x56(?=(?<=(?<!\\\\)(?:\\\\\\\\)*)\\s|$)|(?<=(?<=(?<!\\\\)(?:\\\\\\\\)*)\\s|^)\\x47EQ(?=(?<=(?<!\\\\)(?:\\\\\\\\)*)\\s|$)|(?<=(?<!\\\\)(?:\\\\\\\\)*)\\x3e\\x3d|(?<=(?<!\\\\)(?:\\\\\\\\)*)\\x3d\\x3e|(?<=(?<=(?<!\\\\)(?:\\\\\\\\)*)\\s|^)\\x4cEQ(?=(?<=(?<!\\\\)(?:\\\\\\\\)*)\\s|$)|(?<=(?<!\\\\)(?:\\\\\\\\)*)\\x3c\\x3d|(?<=(?<!\\\\)(?:\\\\\\\\)*)\\x3d\\x3c|(?<=(?<=(?<!\\\\)(?:\\\\\\\\)*)\\s|^)\\x4eOTEQUALS(?=(?<=(?<!\\\\)(?:\\\\\\\\)*)\\s|$)|(?<=(?<=(?<!\\\\)(?:\\\\\\\\)*)\\s|^)\\x4eOTEQUAL(?=(?<=(?<!\\\\)(?:\\\\\\\\)*)\\s|$)|(?<=(?<=(?<!\\\\)(?:\\\\\\\\)*)\\s|^)\\x4eEQ(?=(?<=(?<!\\\\)(?:\\\\\\\\)*)\\s|$)|(?<=(?<=(?<!\\\\)(?:\\\\\\\\)*)\\s|^)\\x49SNT(?=(?<=(?<!\\\\)(?:\\\\\\\\)*)\\s|$)|(?<=(?<!\\\\)(?:\\\\\\\\)*)\\x21\\x3d\\x3d|(?<=(?<!\\\\)(?:\\\\\\\\)*)\\x21\\x3d|(?<=(?<=(?<!\\\\)(?:\\\\\\\\)*)\\s|^)\\x45QUALS(?=(?<=(?<!\\\\)(?:\\\\\\\\)*)\\s|$)|(?<=(?<=(?<!\\\\)(?:\\\\\\\\)*)\\s|^)\\x45QUAL(?=(?<=(?<!\\\\)(?:\\\\\\\\)*)\\s|$)|(?<=(?<=(?<!\\\\)(?:\\\\\\\\)*)\\s|^)\\x45Q(?=(?<=(?<!\\\\)(?:\\\\\\\\)*)\\s|$)|(?<=(?<=(?<!\\\\)(?:\\\\\\\\)*)\\s|^)\\x49S(?=(?<=(?<!\\\\)(?:\\\\\\\\)*)\\s|$)|(?<=(?<!\\\\)(?:\\\\\\\\)*)\\x3d\\x3d|(?<=(?<!\\\\)(?:\\\\\\\\)*)\\x3d|(?<=(?<=(?<!\\\\)(?:\\\\\\\\)*)\\s|^)\\x4cESS(?=(?<=(?<!\\\\)(?:\\\\\\\\)*)\\s|$)|(?<=(?<!\\\\)(?:\\\\\\\\)*)\\x3c|(?<=(?<=(?<!\\\\)(?:\\\\\\\\)*)\\s|^)\\x47REATER(?=(?<=(?<!\\\\)(?:\\\\\\\\)*)\\s|$)|(?<=(?<!\\\\)(?:\\\\\\\\)*)\\x3e|(?<=(?<=(?<!\\\\)(?:\\\\\\\\)*)\\s|^)\\x4dORE(?=(?<=(?<!\\\\)(?:\\\\\\\\)*)\\s|$)|(?<=(?<=(?<!\\\\)(?:\\\\\\\\)*)\\s|^)\\x4eOTIN(?=(?<=(?<!\\\\)(?:\\\\\\\\)*)\\s|$)|(?<=(?<!\\\\)(?:\\\\\\\\)*)\\x21\\x3a|(?<=(?<=(?<!\\\\)(?:\\\\\\\\)*)\\s|^)\\x49N(?=(?<=(?<!\\\\)(?:\\\\\\\\)*)\\s|$)|(?<=(?<!\\\\)(?:\\\\\\\\)*)\\x3a|(?<=(?<=(?<!\\\\)(?:\\\\\\\\)*)\\s|^)\\x4eOTMATCHES(?=(?<=(?<!\\\\)(?:\\\\\\\\)*)\\s|$)|(?<=(?<=(?<!\\\\)(?:\\\\\\\\)*)\\s|^)\\x4eOTMATCH(?=(?<=(?<!\\\\)(?:\\\\\\\\)*)\\s|$)|(?<=(?<!\\\\)(?:\\\\\\\\)*)\\x21\\x7e|(?<=(?<=(?<!\\\\)(?:\\\\\\\\)*)\\s|^)\\x4dATCHES(?=(?<=(?<!\\\\)(?:\\\\\\\\)*)\\s|$)|(?<=(?<=(?<!\\\\)(?:\\\\\\\\)*)\\s|^)\\x4dATCH(?=(?<=(?<!\\\\)(?:\\\\\\\\)*)\\s|$)|(?<=(?<!\\\\)(?:\\\\\\\\)*)\\x7e|(?<=(?<!\\\\)(?:\\\\\\\\)*)\\(|(?<=(?<!\\\\)(?:\\\\\\\\)*)\\)|(?<=(?<!\\\\)(?:\\\\\\\\)*)\\[|(?<=(?<!\\\\)(?:\\\\\\\\)*)\\]|(?<=(?<!\\\\)(?:\\\\\\\\)*)\\{|(?<=(?<!\\\\)(?:\\\\\\\\)*)\\}|(?<=(?<!\\\\)(?:\\\\\\\\)*)\\x21|(?<=(?<!\\\\)(?:\\\\\\\\)*)\\x2c", "g");
140
153
  var QUOTE_REGEX = new RegExp("(?<=(?<!\\\\)(?:\\\\\\\\)*)(?<quote>\\x27|\\x22|\\x60)(?<quotecontent>.*?)(?<=(?<!\\\\)(?:\\\\\\\\)*)\\k<quote>");
141
154
  var QUOTE_EDGE_REGEX = new RegExp(`^${"(?<=(?<!\\\\)(?:\\\\\\\\)*)(?<quote>\\x27|\\x22|\\x60)(?<quotecontent>.*?)(?<=(?<!\\\\)(?:\\\\\\\\)*)\\k<quote>"}$`);
142
155
  function pushSanitized(array, item, index) {
@@ -156,20 +169,27 @@ function _tokenize(expression, pattern) {
156
169
  let lastMatchEnd = null;
157
170
  for (const match of matches) {
158
171
  pushSanitized(tokens, expression.slice(lastMatchEnd ?? 0, match.index), lastMatchEnd === null ? 0 : lastMatchEnd);
159
- if (["[", "{"].includes(match[0])) {
172
+ if (BRACKETS.some(([o]) => match[0] === o)) {
160
173
  const startToken = {
161
174
  content: match[0],
162
175
  index: match.index
163
176
  };
164
177
  tokens.push(startToken);
178
+ let subopenings = 0;
165
179
  let endToken;
166
180
  for (const submatch of matches) {
167
- if (match[0] === "[" && submatch[0] === "]" || match[0] === "{" && submatch[0] === "}") {
168
- endToken = {
169
- content: submatch[0],
170
- index: submatch.index
171
- };
172
- break;
181
+ if (match[0] === submatch[0] || match[0] === submatch[0])
182
+ ++subopenings;
183
+ if (BRACKETS.some(([o, c]) => match[0] === o && submatch[0] === c)) {
184
+ if (subopenings)
185
+ --subopenings;
186
+ else {
187
+ endToken = {
188
+ content: submatch[0],
189
+ index: submatch.index
190
+ };
191
+ break;
192
+ }
173
193
  }
174
194
  }
175
195
  const subtokens = endToken ? _tokenize(expression.slice(startToken.index + startToken.content.length, endToken.index), new RegExp(`${"(?<=(?<!\\\\)(?:\\\\\\\\)*)(?<quote>\\x27|\\x22|\\x60)(?<quotecontent>.*?)(?<=(?<!\\\\)(?:\\\\\\\\)*)\\k<quote>"}|${ESCAPE_REGEX},`, "g")) : _tokenize(expression.slice(startToken.index + startToken.content.length), TOKEN_REGEX);
@@ -200,6 +220,7 @@ function processToken(token) {
200
220
  const escaped = unquoted ?? token;
201
221
  const unescaped = escaped.replaceAll(new RegExp(`(?<!${ESCAPE_REGEX}\\\\)\\\\`, "g"), "");
202
222
  return {
223
+ raw: token,
203
224
  unquoted,
204
225
  escaped,
205
226
  unescaped
@@ -270,8 +291,8 @@ function complementExpression(expression) {
270
291
  break;
271
292
  }
272
293
  }
273
- function coerceType(value, fieldTypes, operatorTypes, dateInterpreter) {
274
- const { unescaped } = processToken(value);
294
+ function coerceType(processedToken, fieldTypes, operatorTypes, dateInterpreter) {
295
+ const { raw, unescaped } = processedToken;
275
296
  let operatorHits = 0;
276
297
  let fieldHits = 0;
277
298
  for (const type of TYPE_PRIORITY) {
@@ -285,13 +306,13 @@ function coerceType(value, fieldTypes, operatorTypes, dateInterpreter) {
285
306
  continue;
286
307
  switch (type) {
287
308
  case "boolean":
288
- if (value === "true")
309
+ if (raw === "true")
289
310
  return true;
290
- else if (value === "false")
311
+ else if (raw === "false")
291
312
  return false;
292
313
  break;
293
314
  case "number": {
294
- const num = Number(value);
315
+ const num = Number(raw);
295
316
  if (isNaN(num))
296
317
  break;
297
318
  return num;
@@ -299,7 +320,7 @@ function coerceType(value, fieldTypes, operatorTypes, dateInterpreter) {
299
320
  case "string":
300
321
  return unescaped;
301
322
  case "date": {
302
- const num = Number(value);
323
+ const num = Number(raw);
303
324
  const date = dateInterpreter(isNaN(num) ? unescaped : num);
304
325
  if (isNaN(+date))
305
326
  break;
@@ -319,46 +340,47 @@ function coerceType(value, fieldTypes, operatorTypes, dateInterpreter) {
319
340
  }
320
341
  function validateCondition(condition, constraints, valueIsImplicit, ctx) {
321
342
  let validated = false;
343
+ condition.field = processToken(condition.field).unescaped;
322
344
  const field = constraints?.caseInsensitive ? [...Object.keys(constraints.types ?? {}), ...Object.keys(constraints.restricted ?? {})].find((k) => k.toLowerCase() === condition.field.toLowerCase()) ?? condition.field : condition.field;
323
345
  const restriction = constraints?.restricted?.[field];
324
346
  const type = constraints?.types?.[field];
325
347
  if (constraints?.disallowUnvalidated && restriction === undefined && type === undefined)
326
- throw new ConstraintError(`Unknown field "${condition.field}"`, ctx?.startToken, ctx?.startIndex, ctx?.endToken, ctx?.endIndex);
348
+ throw new ConstraintError(`Unknown field "${condition.field}"`, ctx?.tokens, ctx?.startToken, ctx?.startIndex, ctx?.endToken, ctx?.endIndex);
327
349
  const operationType = COMPARISON_TYPE_DICTIONARY[condition.operation];
328
350
  const types = type && (Array.isArray(type) ? type : [type]);
329
- const values = Array.isArray(condition.value) ? condition.value : [condition.value];
351
+ const processedValues = (Array.isArray(condition.value) ? condition.value : [condition.value]).map((v) => processToken(v));
330
352
  if (restriction === true)
331
- throw new ConstraintError(`Field "${condition.field}" is restricted`, ctx?.startToken, ctx?.startIndex, ctx?.endToken, ctx?.endIndex);
353
+ throw new ConstraintError(`Field "${condition.field}" is restricted`, ctx?.tokens, ctx?.startToken, ctx?.startIndex, ctx?.endToken, ctx?.endIndex);
332
354
  else if (Array.isArray(restriction)) {
333
355
  const [philosophy, checks] = restriction;
334
356
  if (philosophy === "deny") {
335
357
  for (const check of checks) {
336
358
  if (check instanceof RegExp) {
337
- if (values.some((v) => check.test(v.toString()))) {
338
- throw new ConstraintError(`Value for field "${condition.field}" violates prohibitive pattern constraint "${check.toString()}". Prohibited values/patterns: Allowed values/patterns: ${checks.join(", ")}`, ctx?.startToken, ctx?.startIndex, ctx?.endToken, ctx?.endIndex);
359
+ if (processedValues.some(({ unescaped }) => check.test(unescaped))) {
360
+ throw new ConstraintError(`Value for field "${condition.field}" violates prohibitive pattern constraint "${check.toString()}". Prohibited values/patterns: Allowed values/patterns: ${checks.join(", ")}`, ctx?.tokens, ctx?.startToken, ctx?.startIndex, ctx?.endToken, ctx?.endIndex);
339
361
  }
340
362
  } else {
341
- if (values.includes(check)) {
342
- throw new ConstraintError(`Forbidden value "${check}" for field "${condition.field}". Prohibited values/patterns: ${checks.join(", ")}`, ctx?.startToken, ctx?.startIndex, ctx?.endToken, ctx?.endIndex);
363
+ if (processedValues.some(({ unescaped }) => unescaped === check)) {
364
+ throw new ConstraintError(`Forbidden value "${check}" for field "${condition.field}". Prohibited values/patterns: ${checks.join(", ")}`, ctx?.tokens, ctx?.startToken, ctx?.startIndex, ctx?.endToken, ctx?.endIndex);
343
365
  }
344
366
  }
345
367
  }
346
368
  } else {
347
- for (const value of values) {
348
- if (!checks.some((c) => c instanceof RegExp && c.test(value.toString()) || c === value)) {
349
- throw new ConstraintError(`Value for field "${condition.field}" does not meet any allowed value/pattern. Allowed values/patterns: ${checks.join(", ")}`, ctx?.startToken, ctx?.startIndex, ctx?.endToken, ctx?.endIndex);
369
+ for (const { unescaped } of processedValues) {
370
+ if (!checks.some((c) => c instanceof RegExp ? c.test(unescaped) : c === unescaped)) {
371
+ throw new ConstraintError(`Value for field "${condition.field}" does not meet any allowed value/pattern. Allowed values/patterns: ${checks.join(", ")}`, ctx?.tokens, ctx?.startToken, ctx?.startIndex, ctx?.endToken, ctx?.endIndex);
350
372
  }
351
373
  }
352
374
  }
353
375
  validated = true;
354
376
  }
355
377
  if (operationType === "array" && !Array.isArray(condition.value))
356
- throw new ConstraintError(`Value "${condition.value.toString()}" is not permitted for "${condition.operation}" which expects an array`, ctx?.startToken, ctx?.startIndex, ctx?.endToken, ctx?.endIndex);
378
+ throw new ConstraintError(`Value "${condition.value.toString()}" is not permitted for "${condition.operation}" which expects an array`, ctx?.tokens, ctx?.startToken, ctx?.startIndex, ctx?.endToken, ctx?.endIndex);
357
379
  else if (operationType !== "array" && Array.isArray(condition.value))
358
- throw new ConstraintError(`Value "${condition.value.toString()}" is not permitted for "${condition.operation}" which expects a non-array value`, ctx?.startToken, ctx?.startIndex, ctx?.endToken, ctx?.endIndex);
380
+ throw new ConstraintError(`Value "${condition.value.toString()}" is not permitted for "${condition.operation}" which expects a non-array value`, ctx?.tokens, ctx?.startToken, ctx?.startIndex, ctx?.endToken, ctx?.endIndex);
359
381
  const dateInterpreter = constraints?.dateInterpreter ?? ((v) => new Date(v));
360
- for (let i = 0;i < values.length; ++i) {
361
- const v = values[i];
382
+ for (let i = 0;i < processedValues.length; ++i) {
383
+ const v = processedValues[i];
362
384
  let opTypes;
363
385
  if (valueIsImplicit)
364
386
  opTypes = ["boolean"];
@@ -382,15 +404,16 @@ function validateCondition(condition, constraints, valueIsImplicit, ctx) {
382
404
  }
383
405
  try {
384
406
  const value = coerceType(v, types ?? opTypes, opTypes, dateInterpreter);
385
- values[i] = value;
386
- if (!Array.isArray(condition.value))
407
+ if (Array.isArray(condition.value))
408
+ condition.value[i] = value;
409
+ else
387
410
  condition.value = value;
388
411
  } catch (err) {
389
412
  switch (err.message) {
390
413
  case "operator":
391
- throw new ConstraintError(`Value "${condition.value.toString()}" not allowed for operation "${condition.operation}" which only allows for "${operationType}" type`, ctx?.startToken, ctx?.startIndex, ctx?.endToken, ctx?.endIndex);
414
+ throw new ConstraintError(`Value "${condition.value.toString()}" not allowed for operation "${condition.operation}" which only allows for "${operationType}" type`, ctx?.tokens, ctx?.startToken, ctx?.startIndex, ctx?.endToken, ctx?.endIndex);
392
415
  case "field":
393
- throw new ConstraintError(`Value "${condition.value.toString()}" includes a type not permitted for field "${condition.field}". Allowed types: ${(types ?? opTypes).join(", ")}`, ctx?.startToken, ctx?.startIndex, ctx?.endToken, ctx?.endIndex);
416
+ throw new ConstraintError(`Value "${condition.value.toString()}" includes a type not permitted for field "${condition.field}". Allowed types: ${(types ?? opTypes).join(", ")}`, ctx?.tokens, ctx?.startToken, ctx?.startIndex, ctx?.endToken, ctx?.endIndex);
394
417
  }
395
418
  }
396
419
  }
@@ -413,15 +436,16 @@ function _parse(tokens, _offset, constraints) {
413
436
  if (inConjunction) {
414
437
  const prior = expressions.at(-1);
415
438
  if (!prior)
416
- throw new ParseError("Unexpected: Expression list empty when parser is meant to append to an AND group", ctx?.startToken, ctx?.startIndex, ctx?.endToken, ctx?.endIndex);
439
+ throw new ParseError("Unexpected: Expression list empty when parser is meant to append to an AND group", ctx?.tokens, ctx?.startToken, ctx?.startIndex, ctx?.endToken, ctx?.endIndex);
417
440
  if (prior.type !== "group" || prior.operation !== "AND")
418
- throw new ParseError("Unexpected: Last expression is not an AND group yet parser thinks it's appending to one", ctx?.startToken, ctx?.startIndex, ctx?.endToken, ctx?.endIndex);
441
+ throw new ParseError("Unexpected: Last expression is not an AND group yet parser thinks it's appending to one", ctx?.tokens, ctx?.startToken, ctx?.startIndex, ctx?.endToken, ctx?.endIndex);
419
442
  return prior.constituents;
420
443
  }
421
444
  return expressions;
422
445
  }
423
446
  function resolveCondition(ctx) {
424
447
  const baseCtx = {
448
+ tokens,
425
449
  startToken: field?.token ?? comparisonOperation?.token ?? value?.token,
426
450
  startIndex: field?.index ?? comparisonOperation?.index ?? value?.index,
427
451
  endToken: value?.token ?? comparisonOperation?.token ?? field?.token,
@@ -432,7 +456,7 @@ function _parse(tokens, _offset, constraints) {
432
456
  const group = getExpressionGroup(ctx);
433
457
  if (field && comparisonOperation && value) {
434
458
  if (!expectingExpression)
435
- throw new ParseError("Unexpected expression resolution before junctive operator", ctx.startToken, ctx.startIndex, ctx.endToken, ctx.endIndex);
459
+ throw new ParseError("Unexpected expression resolution before junctive operator", ctx.tokens, ctx.startToken, ctx.startIndex, ctx.endToken, ctx.endIndex);
436
460
  group.push(validateCondition({
437
461
  type: "condition",
438
462
  field: field.content,
@@ -443,7 +467,7 @@ function _parse(tokens, _offset, constraints) {
443
467
  expectingExpression = false;
444
468
  } else if (field && !comparisonOperation && !value) {
445
469
  if (!expectingExpression)
446
- throw new ParseError("Unexpected expression resolution before junctive operator", ctx.startToken, ctx.startIndex, ctx.endToken, ctx.endIndex);
470
+ throw new ParseError("Unexpected expression resolution before junctive operator", ctx.tokens, ctx.startToken, ctx.startIndex, ctx.endToken, ctx.endIndex);
447
471
  group.push(validateCondition({
448
472
  type: "condition",
449
473
  field: field.content,
@@ -453,30 +477,31 @@ function _parse(tokens, _offset, constraints) {
453
477
  inConjunction = false;
454
478
  expectingExpression = false;
455
479
  } else if (field || comparisonOperation || value !== undefined)
456
- throw new ParseError("Failed to resolve condition; missing operand or operator", ctx.startToken, ctx.startIndex, ctx.endToken, ctx.endIndex);
480
+ throw new ParseError("Failed to resolve condition; missing operand or operator", ctx.tokens, ctx.startToken, ctx.startIndex, ctx.endToken, ctx.endIndex);
457
481
  field = undefined;
458
482
  comparisonOperation = undefined;
459
483
  value = undefined;
460
484
  }
461
485
  for (let t = 0;t < tokens.length; ++t) {
462
486
  const token = tokens[t];
463
- if (token.content === ")")
464
- throw new ParseError("Unexpected closing parenthesis", token, _offset + t);
465
- if (["]", "}"].includes(token.content))
466
- throw new ParseError("Unexpected closing bracket/brace", token, _offset + t);
467
- if (token.content === "(") {
487
+ if (PARENS.some(([, c]) => token.content === c))
488
+ throw new ParseError("Unexpected closing parenthesis", tokens, token, _offset + t);
489
+ if (BRACKETS.some(([, c]) => token.content === c))
490
+ throw new ParseError("Unexpected closing bracket/brace", tokens, token, _offset + t);
491
+ const paren = PARENS.find(([o]) => token.content === o);
492
+ if (paren) {
468
493
  if (field || comparisonOperation || value)
469
- throw new ParseError("Tried to open a group during an operation", token, _offset + t);
470
- const closingIndex = getClosingIndex(tokens, t, "(", ")");
494
+ throw new ParseError("Tried to open a group during an operation", tokens, token, _offset + t);
495
+ const closingIndex = getClosingIndex(tokens, t, paren[0], paren[1]);
471
496
  if (closingIndex === -1)
472
- throw new ParseError("Missing closing parenthesis for group", token, _offset + t);
497
+ throw new ParseError("Missing closing parenthesis for group", tokens, token, _offset + t);
473
498
  ++t;
474
499
  const subExpression = _parse(tokens.slice(t, closingIndex), _offset + t, constraints);
475
500
  if (subExpression) {
476
501
  if (subExpression.type === "group" && subExpression.operation === groupOperation)
477
502
  expressions.push(...subExpression.constituents);
478
503
  else {
479
- const group = getExpressionGroup({ startToken: token, startIndex: _offset + t });
504
+ const group = getExpressionGroup({ tokens, startToken: token, startIndex: _offset + t });
480
505
  group.push(subExpression);
481
506
  inConjunction = false;
482
507
  }
@@ -487,6 +512,7 @@ function _parse(tokens, _offset, constraints) {
487
512
  const op = OPERATION_ALIAS_DICTIONARY[token.content];
488
513
  if (op && OPERATION_PURPOSE_DICTIONARY[op] === "junction") {
489
514
  resolveCondition({
515
+ tokens,
490
516
  startToken: field?.token ?? token,
491
517
  startIndex: field?.index ?? _offset + t,
492
518
  endToken: token,
@@ -494,16 +520,16 @@ function _parse(tokens, _offset, constraints) {
494
520
  });
495
521
  const prior = expressions.at(-1);
496
522
  if (!prior)
497
- throw new ParseError("Unexpected junction operator with no preceding expression", token, _offset + t);
523
+ throw new ParseError("Unexpected junction operator with no preceding expression", tokens, token, _offset + t);
498
524
  expectingExpression = true;
499
525
  if (groupOperation && groupOperation !== op) {
500
526
  if (expressions.length < 2)
501
- throw new ParseError("Unexpected junction operator with no preceding expression", token, _offset + t);
527
+ throw new ParseError("Unexpected junction operator with no preceding expression", tokens, token, _offset + t);
502
528
  switch (groupOperation) {
503
529
  case "AND": {
504
530
  const futureSubgroup = _parse(tokens.slice(t + 1), _offset + t, constraints);
505
531
  if (futureSubgroup === null)
506
- throw new ParseError("Dangling junction operator", token, _offset + t);
532
+ throw new ParseError("Dangling junction operator", tokens, token, _offset + t);
507
533
  return {
508
534
  type: "group",
509
535
  operation: "OR",
@@ -540,19 +566,21 @@ function _parse(tokens, _offset, constraints) {
540
566
  }
541
567
  continue;
542
568
  }
543
- if (token.content === "!") {
569
+ if (NEGATORS.includes(token.content)) {
544
570
  const nextToken = tokens[t + 1];
545
- if (nextToken?.content === "(") {
571
+ const nextParen = PARENS.find(([o]) => nextToken?.content === o);
572
+ if (nextParen) {
546
573
  resolveCondition({
574
+ tokens,
547
575
  startToken: field?.token ?? token,
548
576
  startIndex: field?.index ?? _offset + t,
549
577
  endToken: value?.token ?? comparisonOperation?.token ?? field?.token,
550
578
  endIndex: value?.index ?? comparisonOperation?.index ?? field?.index
551
579
  });
552
580
  ++t;
553
- const closingIndex = getClosingIndex(tokens, t, "(", ")");
581
+ const closingIndex = getClosingIndex(tokens, t, nextParen[0], nextParen[1]);
554
582
  if (closingIndex === -1)
555
- throw new ParseError("Missing closing parenthesis for group", token, _offset + t);
583
+ throw new ParseError("Missing closing parenthesis for group", tokens, token, _offset + t);
556
584
  ++t;
557
585
  const futureSubExpression = _parse(tokens.slice(t, closingIndex), _offset + t, constraints);
558
586
  if (futureSubExpression) {
@@ -561,6 +589,7 @@ function _parse(tokens, _offset, constraints) {
561
589
  expressions.push(...futureSubExpression.constituents);
562
590
  else {
563
591
  const group = getExpressionGroup({
592
+ tokens,
564
593
  startToken: token,
565
594
  startIndex: _offset + t,
566
595
  endToken: tokens[closingIndex],
@@ -577,7 +606,7 @@ function _parse(tokens, _offset, constraints) {
577
606
  if (!comparisonOperation || op && OPERATION_PURPOSE_DICTIONARY[op] === "comparison") {
578
607
  if (op && OPERATION_PURPOSE_DICTIONARY[op] === "comparison") {
579
608
  if (comparisonOperation || !field)
580
- throw new ParseError("Unexpected comparison operator", field?.token ?? token, field?.index ?? _offset + t, token, _offset + t);
609
+ throw new ParseError("Unexpected comparison operator", tokens, field?.token ?? token, field?.index ?? _offset + t, token, _offset + t);
581
610
  comparisonOperation = {
582
611
  content: op,
583
612
  token,
@@ -585,14 +614,15 @@ function _parse(tokens, _offset, constraints) {
585
614
  };
586
615
  continue;
587
616
  } else if (field)
588
- throw new ParseError("Expected a comparison operator", field.token, field.index, token, _offset + t);
617
+ throw new ParseError("Expected a comparison operator", tokens, field.token, field.index, token, _offset + t);
589
618
  }
590
619
  if (!field) {
591
- if (token.content === "!") {
620
+ if (NEGATORS.includes(token.content)) {
592
621
  const nextToken = tokens[t + 1];
593
622
  if (!nextToken)
594
- throw new ParseError('Unexpected "!"', token, _offset + t);
623
+ throw new ParseError('Unexpected "!"', tokens, token, _offset + t);
595
624
  resolveCondition({
625
+ tokens,
596
626
  startToken: token,
597
627
  startIndex: _offset + t,
598
628
  endToken: nextToken,
@@ -600,7 +630,7 @@ function _parse(tokens, _offset, constraints) {
600
630
  });
601
631
  ++t;
602
632
  field = {
603
- content: processToken(nextToken.content).unescaped,
633
+ content: nextToken.content,
604
634
  token,
605
635
  index: _offset + t
606
636
  };
@@ -612,6 +642,7 @@ function _parse(tokens, _offset, constraints) {
612
642
  implicit: true
613
643
  };
614
644
  resolveCondition({
645
+ tokens,
615
646
  startToken: field.token,
616
647
  startIndex: field.index,
617
648
  endToken: nextToken,
@@ -619,7 +650,7 @@ function _parse(tokens, _offset, constraints) {
619
650
  });
620
651
  } else {
621
652
  field = {
622
- content: processToken(token.content).unescaped,
653
+ content: token.content,
623
654
  token,
624
655
  index: _offset + t
625
656
  };
@@ -627,17 +658,13 @@ function _parse(tokens, _offset, constraints) {
627
658
  continue;
628
659
  }
629
660
  if (!value) {
630
- if (["[", "{"].includes(token.content)) {
661
+ const bracket = BRACKETS.find(([o]) => token.content === o);
662
+ if (bracket) {
631
663
  let resolveEntry = function(subtoken, subindex) {
632
664
  if (workingEntry) {
633
- const {
634
- unquoted: unquotedWorkingEntry
635
- } = processToken(workingEntry);
636
665
  const subquotes = workingEntry.match(QUOTE_REGEX);
637
666
  if (subquotes && (subquotes.index !== 0 || subquotes[0].length !== workingEntry.length))
638
- throw new ParseError("Quotes must surround entire values in arrays", firstEntryToken ?? subtoken, firstEntryTokenIndex ?? subindex, lastEntryToken ?? subtoken, lastEntryTokenIndex ?? subindex);
639
- if (!unquotedWorkingEntry && (token.content === "[" && new RegExp(`${ESCAPE_REGEX}(?:\\[|\\])`).test(workingEntry) || token.content === "{" && new RegExp(`${ESCAPE_REGEX}(?:\\{|\\})`).test(workingEntry)))
640
- throw new ParseError("Unescaped bracket in an array value", firstEntryToken ?? subtoken, firstEntryTokenIndex ?? subindex, lastEntryToken ?? subtoken, lastEntryTokenIndex ?? subindex);
667
+ throw new ParseError("Quotes must surround entire values in arrays", tokens, firstEntryToken ?? subtoken, firstEntryTokenIndex ?? subindex, lastEntryToken ?? subtoken, lastEntryTokenIndex ?? subindex);
641
668
  arr.push(workingEntry);
642
669
  workingEntry = "";
643
670
  firstEntryToken = undefined;
@@ -646,9 +673,9 @@ function _parse(tokens, _offset, constraints) {
646
673
  lastEntryTokenIndex = undefined;
647
674
  }
648
675
  };
649
- const closingIndex = getClosingIndex(tokens, t, token.content, token.content === "[" ? "]" : "}");
676
+ const closingIndex = getClosingIndex(tokens, t, bracket[0], bracket[1]);
650
677
  if (closingIndex === -1)
651
- throw new ParseError("Missing closing bracket/brace for array value", token, _offset + t);
678
+ throw new ParseError("Missing closing bracket/brace for array value", tokens, token, _offset + t);
652
679
  ++t;
653
680
  const arr = [];
654
681
  value = {
@@ -664,9 +691,9 @@ function _parse(tokens, _offset, constraints) {
664
691
  let lastEntryTokenIndex;
665
692
  for (let ct = 0;ct < arrayContents.length; ++ct) {
666
693
  const contentToken = arrayContents[ct];
667
- if (contentToken.content === ",") {
694
+ if (ARRAY_DELIMITERS.includes(contentToken.content)) {
668
695
  if (!workingEntry)
669
- throw new ParseError("Unexpected blank entry in array", contentToken, _offset + t + ct);
696
+ throw new ParseError("Unexpected blank entry in array", tokens, contentToken, _offset + t + ct);
670
697
  resolveEntry(contentToken, _offset + t + ct);
671
698
  } else {
672
699
  lastEntryToken = contentToken;
@@ -680,7 +707,7 @@ function _parse(tokens, _offset, constraints) {
680
707
  }
681
708
  resolveEntry(arrayContents.at(-1), _offset + t + arrayContents.length - 1);
682
709
  if (!arr.length) {
683
- throw new ParseError("Empty array provided as value", token, _offset + t - 1, tokens[closingIndex], _offset + closingIndex);
710
+ throw new ParseError("Empty array provided as value", tokens, token, _offset + t - 1, tokens[closingIndex], _offset + closingIndex);
684
711
  }
685
712
  t = closingIndex;
686
713
  } else {
@@ -691,6 +718,7 @@ function _parse(tokens, _offset, constraints) {
691
718
  };
692
719
  }
693
720
  resolveCondition({
721
+ tokens,
694
722
  startToken: field.token,
695
723
  startIndex: field.index,
696
724
  endToken: value.token ?? comparisonOperation?.token ?? field.token,
@@ -699,23 +727,24 @@ function _parse(tokens, _offset, constraints) {
699
727
  }
700
728
  }
701
729
  resolveCondition({
730
+ tokens,
702
731
  startToken: field?.token,
703
732
  startIndex: field?.index,
704
733
  endToken: value?.token ?? comparisonOperation?.token ?? field?.token,
705
734
  endIndex: value?.index ?? comparisonOperation?.index ?? field?.index
706
735
  });
707
736
  if (inConjunction)
708
- throw new ParseError("Dangling junction operator", tokens.at(-1), _offset + tokens.length - 1);
737
+ throw new ParseError("Dangling junction operator", tokens, tokens.at(-1), _offset + tokens.length - 1);
709
738
  if (groupOperation) {
710
739
  if (expressions.length === 1)
711
- throw new ParseError("Dangling junction operator", tokens.at(-1), _offset + tokens.length - 1);
740
+ throw new ParseError("Dangling junction operator", tokens, tokens.at(-1), _offset + tokens.length - 1);
712
741
  return {
713
742
  type: "group",
714
743
  operation: groupOperation,
715
744
  constituents: expressions
716
745
  };
717
746
  } else if (expressions.length > 1)
718
- throw new ParseError("Group possesses multiple conditions without disjunctive operators", tokens[0], _offset);
747
+ throw new ParseError("Group possesses multiple conditions without disjunctive operators", tokens, tokens[0], _offset);
719
748
  else
720
749
  return expressions[0] ?? null;
721
750
  }
@@ -815,6 +844,10 @@ function summarize(expressions) {
815
844
  return summary;
816
845
  }
817
846
  // src/stringify.ts
847
+ var DEFAULT_BRACKET = BRACKETS[0];
848
+ var DEFAULT_PAREN = PARENS[0];
849
+ var DEFAULT_NEGATOR = NEGATORS[0];
850
+ var DEFAULT_DELIMITER = ARRAY_DELIMITERS[0];
818
851
  var formats = {
819
852
  programmatic: {
820
853
  AND: "&",
@@ -883,16 +916,16 @@ function stringify(expression, opts = {}) {
883
916
  string += " ";
884
917
  }
885
918
  if (alwaysParenthesize && constituent.type === "group" || expression.operation === "AND" && constituent.operation === "OR")
886
- string += "(";
919
+ string += DEFAULT_PAREN[0];
887
920
  string += stringify(constituent, opts);
888
921
  if (alwaysParenthesize && constituent.type === "group" || expression.operation === "AND" && constituent.operation === "OR")
889
- string += ")";
922
+ string += DEFAULT_PAREN[1];
890
923
  }
891
924
  break;
892
925
  case "condition":
893
926
  if (condenseBooleans && ["EQUAL", "NOTEQUAL"].includes(expression.operation) && typeof expression.value === "boolean") {
894
927
  const negative = expression.operation === "EQUAL" && !expression.value || expression.operation === "NOTEQUAL" && expression.value;
895
- string += `${negative ? "!" : ""}${expression.field}`;
928
+ string += `${negative ? DEFAULT_NEGATOR : ""}${expression.field}`;
896
929
  } else {
897
930
  string += expression.field;
898
931
  if (!compact || comparisonNotation === "linguistic")
@@ -901,8 +934,8 @@ function stringify(expression, opts = {}) {
901
934
  if (!compact || comparisonNotation === "linguistic")
902
935
  string += " ";
903
936
  if (Array.isArray(expression.value)) {
904
- const join = expression.value.map(addQuotesIfNecessary).join(compact ? "," : ", ");
905
- string += `[${join}]`;
937
+ const join = expression.value.map(addQuotesIfNecessary).join(compact ? DEFAULT_DELIMITER : DEFAULT_DELIMITER + " ");
938
+ string += `${DEFAULT_BRACKET[0]}${join}${DEFAULT_BRACKET[1]}`;
906
939
  } else
907
940
  string += addQuotesIfNecessary(expression.value);
908
941
  }
@@ -982,17 +1015,17 @@ function createDOMInput({ input, constraints, onUpdate, parseOnInitialize }) {
982
1015
  element.toggleAttribute("data-quoted", true);
983
1016
  if (!isNaN(Number(token.content)))
984
1017
  element.toggleAttribute("data-number", true);
985
- if (["(", ")", "[", "]", "{", "}"].includes(token.content)) {
986
- if (inArray === "[" && token.content === "]" || inArray === "{" && token.content === "}")
1018
+ if (PARENS.concat(BRACKETS).some((entry) => entry.includes(token.content))) {
1019
+ if (inArray && BRACKETS.some(([o, c]) => inArray === o && token.content === c))
987
1020
  inArray = undefined;
988
1021
  if (!inArray)
989
1022
  element.setAttribute("data-bracket", token.content);
990
- if (!inArray && ["[", "{"].includes(token.content))
1023
+ if (!inArray && BRACKETS.some(([o]) => o === token.content))
991
1024
  inArray = token.content;
992
1025
  }
993
- if (inArray && token.content === ",")
1026
+ if (inArray && ARRAY_DELIMITERS.includes(token.content))
994
1027
  element.toggleAttribute("data-delimiter", true);
995
- if (!inArray && token.content === "!")
1028
+ if (!inArray && NEGATORS.includes(token.content))
996
1029
  element.toggleAttribute("data-negation", true);
997
1030
  if (!inArray && token.content in OPERATION_ALIAS_DICTIONARY) {
998
1031
  element.setAttribute("data-operation", OPERATION_PURPOSE_DICTIONARY[OPERATION_ALIAS_DICTIONARY[token.content]]);
package/dist/parse.d.ts CHANGED
@@ -17,7 +17,7 @@ export interface ExpressionConstraints<T extends TypeRecord, V extends boolean>
17
17
  * By default allow any value and restrict a collection of values by passing ['deny', VALUES[]]\
18
18
  * By default restrict any value and allow a collection of values by passing ['allow', VALUES[]]\
19
19
  * If the query value is of array type, it will check all entries of the array and ensure they're all allowed.\
20
- * This check runs before type coercsion so the value checked will always be a string.
20
+ * This check runs before type coercion so the value checked will always be a string. However, quotes and escapes WILL be removed
21
21
  */
22
22
  restricted?: Partial<Record<keyof T | (string & {}), boolean | ['allow' | 'deny', Array<string | RegExp>]>>;
23
23
  /**
package/dist/spec.d.ts CHANGED
@@ -1,3 +1,8 @@
1
+ export declare const QUOTES: string[];
2
+ export declare const BRACKETS: Array<[open: string, close: string]>;
3
+ export declare const PARENS: Array<[open: string, close: string]>;
4
+ export declare const NEGATORS: string[];
5
+ export declare const ARRAY_DELIMITERS: string[];
1
6
  /** All available operation aliases (alias -> operation) */
2
7
  export declare const OPERATION_ALIAS_DICTIONARY: {
3
8
  readonly AND: "AND";
@@ -1,4 +1,4 @@
1
- import type { Expression } from './spec';
1
+ import { type Expression } from './spec';
2
2
  export interface StringifyOptions {
3
3
  /**
4
4
  * The notation to use for junction operations
package/package.json CHANGED
@@ -6,7 +6,7 @@
6
6
  },
7
7
  "homepage": "https://github.com/guidance-analytics/wizard-ql",
8
8
  "description": "WizardQL is a natural-language-like query language for constructing data queries for resources that meet conditions.",
9
- "version": "2.0.1",
9
+ "version": "2.1.0",
10
10
  "repository": {
11
11
  "type": "git",
12
12
  "url": "git+https://github.com/guidance-analytics/wizard-ql.git"