wizard-ql 2.0.1 → 2.1.1

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,13 @@
1
1
  // src/spec.ts
2
+ var BRACKETS = [
3
+ ["[", "]"],
4
+ ["{", "}"]
5
+ ];
6
+ var PARENS = [
7
+ ["(", ")"]
8
+ ];
9
+ var NEGATORS = ["!"];
10
+ var ARRAY_DELIMITERS = [","];
2
11
  var OPERATION_ALIAS_DICTIONARY = {
3
12
  AND: "AND",
4
13
  "&&": "AND",
@@ -72,8 +81,6 @@ var COMPARISON_TYPE_DICTIONARY = {
72
81
  var TYPE_PRIORITY = ["boolean", "date", "number", "string"];
73
82
  // src/regex.ts
74
83
  var ESCAPE_REGEX = "(?<=(?<!\\\\)(?:\\\\\\\\)*)";
75
- var QUOTES = ["'", '"', "`"];
76
- var QUOTE_REGEX_STR = `${ESCAPE_REGEX}(?<quote>${QUOTES.map((q) => RegExp.escape(q)).join("|")})(?<quotecontent>.*?)${ESCAPE_REGEX}\\k<quote>`;
77
84
 
78
85
  // src/errors.ts
79
86
  function constructHeader(startToken, startIndex, endToken, endIndex) {
@@ -106,9 +113,11 @@ class ParseError extends Error {
106
113
  endToken;
107
114
  startIndex;
108
115
  endIndex;
109
- constructor(message, startToken, startIndex, endToken = startToken, endIndex = startIndex) {
116
+ tokens;
117
+ constructor(message, tokens, startToken, startIndex, endToken = startToken, endIndex = startIndex) {
110
118
  const header = constructHeader(startToken, startIndex, endToken, endIndex);
111
119
  super(header + message);
120
+ this.tokens = tokens;
112
121
  this.rawMessage = message;
113
122
  this.startToken = startToken;
114
123
  this.endToken = endToken;
@@ -124,9 +133,11 @@ class ConstraintError extends Error {
124
133
  endToken;
125
134
  startIndex;
126
135
  endIndex;
127
- constructor(message, startToken, startIndex, endToken = startToken, endIndex = startIndex) {
136
+ tokens;
137
+ constructor(message, tokens, startToken, startIndex, endToken = startToken, endIndex = startIndex) {
128
138
  const header = constructHeader(startToken, startIndex, endToken, endIndex);
129
139
  super(header + message);
140
+ this.tokens = tokens;
130
141
  this.rawMessage = message;
131
142
  this.startToken = startToken;
132
143
  this.endToken = endToken;
@@ -136,7 +147,7 @@ class ConstraintError extends Error {
136
147
  }
137
148
 
138
149
  // 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");
150
+ 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
151
  var QUOTE_REGEX = new RegExp("(?<=(?<!\\\\)(?:\\\\\\\\)*)(?<quote>\\x27|\\x22|\\x60)(?<quotecontent>.*?)(?<=(?<!\\\\)(?:\\\\\\\\)*)\\k<quote>");
141
152
  var QUOTE_EDGE_REGEX = new RegExp(`^${"(?<=(?<!\\\\)(?:\\\\\\\\)*)(?<quote>\\x27|\\x22|\\x60)(?<quotecontent>.*?)(?<=(?<!\\\\)(?:\\\\\\\\)*)\\k<quote>"}$`);
142
153
  function pushSanitized(array, item, index) {
@@ -156,20 +167,27 @@ function _tokenize(expression, pattern) {
156
167
  let lastMatchEnd = null;
157
168
  for (const match of matches) {
158
169
  pushSanitized(tokens, expression.slice(lastMatchEnd ?? 0, match.index), lastMatchEnd === null ? 0 : lastMatchEnd);
159
- if (["[", "{"].includes(match[0])) {
170
+ if (BRACKETS.some(([o]) => match[0] === o)) {
160
171
  const startToken = {
161
172
  content: match[0],
162
173
  index: match.index
163
174
  };
164
175
  tokens.push(startToken);
176
+ let subopenings = 0;
165
177
  let endToken;
166
178
  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;
179
+ if (match[0] === submatch[0] || match[0] === submatch[0])
180
+ ++subopenings;
181
+ if (BRACKETS.some(([o, c]) => match[0] === o && submatch[0] === c)) {
182
+ if (subopenings)
183
+ --subopenings;
184
+ else {
185
+ endToken = {
186
+ content: submatch[0],
187
+ index: submatch.index
188
+ };
189
+ break;
190
+ }
173
191
  }
174
192
  }
175
193
  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 +218,7 @@ function processToken(token) {
200
218
  const escaped = unquoted ?? token;
201
219
  const unescaped = escaped.replaceAll(new RegExp(`(?<!${ESCAPE_REGEX}\\\\)\\\\`, "g"), "");
202
220
  return {
221
+ raw: token,
203
222
  unquoted,
204
223
  escaped,
205
224
  unescaped
@@ -270,8 +289,8 @@ function complementExpression(expression) {
270
289
  break;
271
290
  }
272
291
  }
273
- function coerceType(value, fieldTypes, operatorTypes, dateInterpreter) {
274
- const { unescaped } = processToken(value);
292
+ function coerceType(processedToken, fieldTypes, operatorTypes, dateInterpreter) {
293
+ const { raw, unescaped } = processedToken;
275
294
  let operatorHits = 0;
276
295
  let fieldHits = 0;
277
296
  for (const type of TYPE_PRIORITY) {
@@ -285,13 +304,13 @@ function coerceType(value, fieldTypes, operatorTypes, dateInterpreter) {
285
304
  continue;
286
305
  switch (type) {
287
306
  case "boolean":
288
- if (value === "true")
307
+ if (raw === "true")
289
308
  return true;
290
- else if (value === "false")
309
+ else if (raw === "false")
291
310
  return false;
292
311
  break;
293
312
  case "number": {
294
- const num = Number(value);
313
+ const num = Number(raw);
295
314
  if (isNaN(num))
296
315
  break;
297
316
  return num;
@@ -299,7 +318,7 @@ function coerceType(value, fieldTypes, operatorTypes, dateInterpreter) {
299
318
  case "string":
300
319
  return unescaped;
301
320
  case "date": {
302
- const num = Number(value);
321
+ const num = Number(raw);
303
322
  const date = dateInterpreter(isNaN(num) ? unescaped : num);
304
323
  if (isNaN(+date))
305
324
  break;
@@ -319,46 +338,47 @@ function coerceType(value, fieldTypes, operatorTypes, dateInterpreter) {
319
338
  }
320
339
  function validateCondition(condition, constraints, valueIsImplicit, ctx) {
321
340
  let validated = false;
341
+ condition.field = processToken(condition.field).unescaped;
322
342
  const field = constraints?.caseInsensitive ? [...Object.keys(constraints.types ?? {}), ...Object.keys(constraints.restricted ?? {})].find((k) => k.toLowerCase() === condition.field.toLowerCase()) ?? condition.field : condition.field;
323
343
  const restriction = constraints?.restricted?.[field];
324
344
  const type = constraints?.types?.[field];
325
345
  if (constraints?.disallowUnvalidated && restriction === undefined && type === undefined)
326
- throw new ConstraintError(`Unknown field "${condition.field}"`, ctx?.startToken, ctx?.startIndex, ctx?.endToken, ctx?.endIndex);
346
+ throw new ConstraintError(`Unknown field "${condition.field}"`, ctx?.tokens, ctx?.startToken, ctx?.startIndex, ctx?.endToken, ctx?.endIndex);
327
347
  const operationType = COMPARISON_TYPE_DICTIONARY[condition.operation];
328
348
  const types = type && (Array.isArray(type) ? type : [type]);
329
- const values = Array.isArray(condition.value) ? condition.value : [condition.value];
349
+ const processedValues = (Array.isArray(condition.value) ? condition.value : [condition.value]).map((v) => processToken(v));
330
350
  if (restriction === true)
331
- throw new ConstraintError(`Field "${condition.field}" is restricted`, ctx?.startToken, ctx?.startIndex, ctx?.endToken, ctx?.endIndex);
351
+ throw new ConstraintError(`Field "${condition.field}" is restricted`, ctx?.tokens, ctx?.startToken, ctx?.startIndex, ctx?.endToken, ctx?.endIndex);
332
352
  else if (Array.isArray(restriction)) {
333
353
  const [philosophy, checks] = restriction;
334
354
  if (philosophy === "deny") {
335
355
  for (const check of checks) {
336
356
  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);
357
+ if (processedValues.some(({ unescaped }) => check.test(unescaped))) {
358
+ 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
359
  }
340
360
  } 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);
361
+ if (processedValues.some(({ unescaped }) => unescaped === check)) {
362
+ 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
363
  }
344
364
  }
345
365
  }
346
366
  } 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);
367
+ for (const { unescaped } of processedValues) {
368
+ if (!checks.some((c) => c instanceof RegExp ? c.test(unescaped) : c === unescaped)) {
369
+ 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
370
  }
351
371
  }
352
372
  }
353
373
  validated = true;
354
374
  }
355
375
  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);
376
+ 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
377
  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);
378
+ 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
379
  const dateInterpreter = constraints?.dateInterpreter ?? ((v) => new Date(v));
360
- for (let i = 0;i < values.length; ++i) {
361
- const v = values[i];
380
+ for (let i = 0;i < processedValues.length; ++i) {
381
+ const v = processedValues[i];
362
382
  let opTypes;
363
383
  if (valueIsImplicit)
364
384
  opTypes = ["boolean"];
@@ -382,15 +402,16 @@ function validateCondition(condition, constraints, valueIsImplicit, ctx) {
382
402
  }
383
403
  try {
384
404
  const value = coerceType(v, types ?? opTypes, opTypes, dateInterpreter);
385
- values[i] = value;
386
- if (!Array.isArray(condition.value))
405
+ if (Array.isArray(condition.value))
406
+ condition.value[i] = value;
407
+ else
387
408
  condition.value = value;
388
409
  } catch (err) {
389
410
  switch (err.message) {
390
411
  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);
412
+ 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
413
  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);
414
+ 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
415
  }
395
416
  }
396
417
  }
@@ -413,15 +434,16 @@ function _parse(tokens, _offset, constraints) {
413
434
  if (inConjunction) {
414
435
  const prior = expressions.at(-1);
415
436
  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);
437
+ 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
438
  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);
439
+ 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
440
  return prior.constituents;
420
441
  }
421
442
  return expressions;
422
443
  }
423
444
  function resolveCondition(ctx) {
424
445
  const baseCtx = {
446
+ tokens,
425
447
  startToken: field?.token ?? comparisonOperation?.token ?? value?.token,
426
448
  startIndex: field?.index ?? comparisonOperation?.index ?? value?.index,
427
449
  endToken: value?.token ?? comparisonOperation?.token ?? field?.token,
@@ -432,7 +454,7 @@ function _parse(tokens, _offset, constraints) {
432
454
  const group = getExpressionGroup(ctx);
433
455
  if (field && comparisonOperation && value) {
434
456
  if (!expectingExpression)
435
- throw new ParseError("Unexpected expression resolution before junctive operator", ctx.startToken, ctx.startIndex, ctx.endToken, ctx.endIndex);
457
+ throw new ParseError("Unexpected expression resolution before junctive operator", ctx.tokens, ctx.startToken, ctx.startIndex, ctx.endToken, ctx.endIndex);
436
458
  group.push(validateCondition({
437
459
  type: "condition",
438
460
  field: field.content,
@@ -443,7 +465,7 @@ function _parse(tokens, _offset, constraints) {
443
465
  expectingExpression = false;
444
466
  } else if (field && !comparisonOperation && !value) {
445
467
  if (!expectingExpression)
446
- throw new ParseError("Unexpected expression resolution before junctive operator", ctx.startToken, ctx.startIndex, ctx.endToken, ctx.endIndex);
468
+ throw new ParseError("Unexpected expression resolution before junctive operator", ctx.tokens, ctx.startToken, ctx.startIndex, ctx.endToken, ctx.endIndex);
447
469
  group.push(validateCondition({
448
470
  type: "condition",
449
471
  field: field.content,
@@ -453,30 +475,31 @@ function _parse(tokens, _offset, constraints) {
453
475
  inConjunction = false;
454
476
  expectingExpression = false;
455
477
  } 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);
478
+ throw new ParseError("Failed to resolve condition; missing operand or operator", ctx.tokens, ctx.startToken, ctx.startIndex, ctx.endToken, ctx.endIndex);
457
479
  field = undefined;
458
480
  comparisonOperation = undefined;
459
481
  value = undefined;
460
482
  }
461
483
  for (let t = 0;t < tokens.length; ++t) {
462
484
  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 === "(") {
485
+ if (PARENS.some(([, c]) => token.content === c))
486
+ throw new ParseError("Unexpected closing parenthesis", tokens, token, _offset + t);
487
+ if (BRACKETS.some(([, c]) => token.content === c))
488
+ throw new ParseError("Unexpected closing bracket/brace", tokens, token, _offset + t);
489
+ const paren = PARENS.find(([o]) => token.content === o);
490
+ if (paren) {
468
491
  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, "(", ")");
492
+ throw new ParseError("Tried to open a group during an operation", tokens, token, _offset + t);
493
+ const closingIndex = getClosingIndex(tokens, t, paren[0], paren[1]);
471
494
  if (closingIndex === -1)
472
- throw new ParseError("Missing closing parenthesis for group", token, _offset + t);
495
+ throw new ParseError("Missing closing parenthesis for group", tokens, token, _offset + t);
473
496
  ++t;
474
497
  const subExpression = _parse(tokens.slice(t, closingIndex), _offset + t, constraints);
475
498
  if (subExpression) {
476
499
  if (subExpression.type === "group" && subExpression.operation === groupOperation)
477
500
  expressions.push(...subExpression.constituents);
478
501
  else {
479
- const group = getExpressionGroup({ startToken: token, startIndex: _offset + t });
502
+ const group = getExpressionGroup({ tokens, startToken: token, startIndex: _offset + t });
480
503
  group.push(subExpression);
481
504
  inConjunction = false;
482
505
  }
@@ -487,6 +510,7 @@ function _parse(tokens, _offset, constraints) {
487
510
  const op = OPERATION_ALIAS_DICTIONARY[token.content];
488
511
  if (op && OPERATION_PURPOSE_DICTIONARY[op] === "junction") {
489
512
  resolveCondition({
513
+ tokens,
490
514
  startToken: field?.token ?? token,
491
515
  startIndex: field?.index ?? _offset + t,
492
516
  endToken: token,
@@ -494,16 +518,16 @@ function _parse(tokens, _offset, constraints) {
494
518
  });
495
519
  const prior = expressions.at(-1);
496
520
  if (!prior)
497
- throw new ParseError("Unexpected junction operator with no preceding expression", token, _offset + t);
521
+ throw new ParseError("Unexpected junction operator with no preceding expression", tokens, token, _offset + t);
498
522
  expectingExpression = true;
499
523
  if (groupOperation && groupOperation !== op) {
500
524
  if (expressions.length < 2)
501
- throw new ParseError("Unexpected junction operator with no preceding expression", token, _offset + t);
525
+ throw new ParseError("Unexpected junction operator with no preceding expression", tokens, token, _offset + t);
502
526
  switch (groupOperation) {
503
527
  case "AND": {
504
528
  const futureSubgroup = _parse(tokens.slice(t + 1), _offset + t, constraints);
505
529
  if (futureSubgroup === null)
506
- throw new ParseError("Dangling junction operator", token, _offset + t);
530
+ throw new ParseError("Dangling junction operator", tokens, token, _offset + t);
507
531
  return {
508
532
  type: "group",
509
533
  operation: "OR",
@@ -540,19 +564,21 @@ function _parse(tokens, _offset, constraints) {
540
564
  }
541
565
  continue;
542
566
  }
543
- if (token.content === "!") {
567
+ if (NEGATORS.includes(token.content)) {
544
568
  const nextToken = tokens[t + 1];
545
- if (nextToken?.content === "(") {
569
+ const nextParen = PARENS.find(([o]) => nextToken?.content === o);
570
+ if (nextParen) {
546
571
  resolveCondition({
572
+ tokens,
547
573
  startToken: field?.token ?? token,
548
574
  startIndex: field?.index ?? _offset + t,
549
575
  endToken: value?.token ?? comparisonOperation?.token ?? field?.token,
550
576
  endIndex: value?.index ?? comparisonOperation?.index ?? field?.index
551
577
  });
552
578
  ++t;
553
- const closingIndex = getClosingIndex(tokens, t, "(", ")");
579
+ const closingIndex = getClosingIndex(tokens, t, nextParen[0], nextParen[1]);
554
580
  if (closingIndex === -1)
555
- throw new ParseError("Missing closing parenthesis for group", token, _offset + t);
581
+ throw new ParseError("Missing closing parenthesis for group", tokens, token, _offset + t);
556
582
  ++t;
557
583
  const futureSubExpression = _parse(tokens.slice(t, closingIndex), _offset + t, constraints);
558
584
  if (futureSubExpression) {
@@ -561,6 +587,7 @@ function _parse(tokens, _offset, constraints) {
561
587
  expressions.push(...futureSubExpression.constituents);
562
588
  else {
563
589
  const group = getExpressionGroup({
590
+ tokens,
564
591
  startToken: token,
565
592
  startIndex: _offset + t,
566
593
  endToken: tokens[closingIndex],
@@ -577,7 +604,7 @@ function _parse(tokens, _offset, constraints) {
577
604
  if (!comparisonOperation || op && OPERATION_PURPOSE_DICTIONARY[op] === "comparison") {
578
605
  if (op && OPERATION_PURPOSE_DICTIONARY[op] === "comparison") {
579
606
  if (comparisonOperation || !field)
580
- throw new ParseError("Unexpected comparison operator", field?.token ?? token, field?.index ?? _offset + t, token, _offset + t);
607
+ throw new ParseError("Unexpected comparison operator", tokens, field?.token ?? token, field?.index ?? _offset + t, token, _offset + t);
581
608
  comparisonOperation = {
582
609
  content: op,
583
610
  token,
@@ -585,14 +612,15 @@ function _parse(tokens, _offset, constraints) {
585
612
  };
586
613
  continue;
587
614
  } else if (field)
588
- throw new ParseError("Expected a comparison operator", field.token, field.index, token, _offset + t);
615
+ throw new ParseError("Expected a comparison operator", tokens, field.token, field.index, token, _offset + t);
589
616
  }
590
617
  if (!field) {
591
- if (token.content === "!") {
618
+ if (NEGATORS.includes(token.content)) {
592
619
  const nextToken = tokens[t + 1];
593
620
  if (!nextToken)
594
- throw new ParseError('Unexpected "!"', token, _offset + t);
621
+ throw new ParseError('Unexpected "!"', tokens, token, _offset + t);
595
622
  resolveCondition({
623
+ tokens,
596
624
  startToken: token,
597
625
  startIndex: _offset + t,
598
626
  endToken: nextToken,
@@ -600,7 +628,7 @@ function _parse(tokens, _offset, constraints) {
600
628
  });
601
629
  ++t;
602
630
  field = {
603
- content: processToken(nextToken.content).unescaped,
631
+ content: nextToken.content,
604
632
  token,
605
633
  index: _offset + t
606
634
  };
@@ -612,6 +640,7 @@ function _parse(tokens, _offset, constraints) {
612
640
  implicit: true
613
641
  };
614
642
  resolveCondition({
643
+ tokens,
615
644
  startToken: field.token,
616
645
  startIndex: field.index,
617
646
  endToken: nextToken,
@@ -619,7 +648,7 @@ function _parse(tokens, _offset, constraints) {
619
648
  });
620
649
  } else {
621
650
  field = {
622
- content: processToken(token.content).unescaped,
651
+ content: token.content,
623
652
  token,
624
653
  index: _offset + t
625
654
  };
@@ -627,17 +656,13 @@ function _parse(tokens, _offset, constraints) {
627
656
  continue;
628
657
  }
629
658
  if (!value) {
630
- if (["[", "{"].includes(token.content)) {
659
+ const bracket = BRACKETS.find(([o]) => token.content === o);
660
+ if (bracket) {
631
661
  let resolveEntry = function(subtoken, subindex) {
632
662
  if (workingEntry) {
633
- const {
634
- unquoted: unquotedWorkingEntry
635
- } = processToken(workingEntry);
636
663
  const subquotes = workingEntry.match(QUOTE_REGEX);
637
664
  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);
665
+ throw new ParseError("Quotes must surround entire values in arrays", tokens, firstEntryToken ?? subtoken, firstEntryTokenIndex ?? subindex, lastEntryToken ?? subtoken, lastEntryTokenIndex ?? subindex);
641
666
  arr.push(workingEntry);
642
667
  workingEntry = "";
643
668
  firstEntryToken = undefined;
@@ -646,9 +671,9 @@ function _parse(tokens, _offset, constraints) {
646
671
  lastEntryTokenIndex = undefined;
647
672
  }
648
673
  };
649
- const closingIndex = getClosingIndex(tokens, t, token.content, token.content === "[" ? "]" : "}");
674
+ const closingIndex = getClosingIndex(tokens, t, bracket[0], bracket[1]);
650
675
  if (closingIndex === -1)
651
- throw new ParseError("Missing closing bracket/brace for array value", token, _offset + t);
676
+ throw new ParseError("Missing closing bracket/brace for array value", tokens, token, _offset + t);
652
677
  ++t;
653
678
  const arr = [];
654
679
  value = {
@@ -664,9 +689,9 @@ function _parse(tokens, _offset, constraints) {
664
689
  let lastEntryTokenIndex;
665
690
  for (let ct = 0;ct < arrayContents.length; ++ct) {
666
691
  const contentToken = arrayContents[ct];
667
- if (contentToken.content === ",") {
692
+ if (ARRAY_DELIMITERS.includes(contentToken.content)) {
668
693
  if (!workingEntry)
669
- throw new ParseError("Unexpected blank entry in array", contentToken, _offset + t + ct);
694
+ throw new ParseError("Unexpected blank entry in array", tokens, contentToken, _offset + t + ct);
670
695
  resolveEntry(contentToken, _offset + t + ct);
671
696
  } else {
672
697
  lastEntryToken = contentToken;
@@ -680,7 +705,7 @@ function _parse(tokens, _offset, constraints) {
680
705
  }
681
706
  resolveEntry(arrayContents.at(-1), _offset + t + arrayContents.length - 1);
682
707
  if (!arr.length) {
683
- throw new ParseError("Empty array provided as value", token, _offset + t - 1, tokens[closingIndex], _offset + closingIndex);
708
+ throw new ParseError("Empty array provided as value", tokens, token, _offset + t - 1, tokens[closingIndex], _offset + closingIndex);
684
709
  }
685
710
  t = closingIndex;
686
711
  } else {
@@ -691,6 +716,7 @@ function _parse(tokens, _offset, constraints) {
691
716
  };
692
717
  }
693
718
  resolveCondition({
719
+ tokens,
694
720
  startToken: field.token,
695
721
  startIndex: field.index,
696
722
  endToken: value.token ?? comparisonOperation?.token ?? field.token,
@@ -699,23 +725,24 @@ function _parse(tokens, _offset, constraints) {
699
725
  }
700
726
  }
701
727
  resolveCondition({
728
+ tokens,
702
729
  startToken: field?.token,
703
730
  startIndex: field?.index,
704
731
  endToken: value?.token ?? comparisonOperation?.token ?? field?.token,
705
732
  endIndex: value?.index ?? comparisonOperation?.index ?? field?.index
706
733
  });
707
734
  if (inConjunction)
708
- throw new ParseError("Dangling junction operator", tokens.at(-1), _offset + tokens.length - 1);
735
+ throw new ParseError("Dangling junction operator", tokens, tokens.at(-1), _offset + tokens.length - 1);
709
736
  if (groupOperation) {
710
737
  if (expressions.length === 1)
711
- throw new ParseError("Dangling junction operator", tokens.at(-1), _offset + tokens.length - 1);
738
+ throw new ParseError("Dangling junction operator", tokens, tokens.at(-1), _offset + tokens.length - 1);
712
739
  return {
713
740
  type: "group",
714
741
  operation: groupOperation,
715
742
  constituents: expressions
716
743
  };
717
744
  } else if (expressions.length > 1)
718
- throw new ParseError("Group possesses multiple conditions without disjunctive operators", tokens[0], _offset);
745
+ throw new ParseError("Group possesses multiple conditions without disjunctive operators", tokens, tokens[0], _offset);
719
746
  else
720
747
  return expressions[0] ?? null;
721
748
  }
@@ -815,6 +842,10 @@ function summarize(expressions) {
815
842
  return summary;
816
843
  }
817
844
  // src/stringify.ts
845
+ var DEFAULT_BRACKET = BRACKETS[0];
846
+ var DEFAULT_PAREN = PARENS[0];
847
+ var DEFAULT_NEGATOR = NEGATORS[0];
848
+ var DEFAULT_DELIMITER = ARRAY_DELIMITERS[0];
818
849
  var formats = {
819
850
  programmatic: {
820
851
  AND: "&",
@@ -883,16 +914,16 @@ function stringify(expression, opts = {}) {
883
914
  string += " ";
884
915
  }
885
916
  if (alwaysParenthesize && constituent.type === "group" || expression.operation === "AND" && constituent.operation === "OR")
886
- string += "(";
917
+ string += DEFAULT_PAREN[0];
887
918
  string += stringify(constituent, opts);
888
919
  if (alwaysParenthesize && constituent.type === "group" || expression.operation === "AND" && constituent.operation === "OR")
889
- string += ")";
920
+ string += DEFAULT_PAREN[1];
890
921
  }
891
922
  break;
892
923
  case "condition":
893
924
  if (condenseBooleans && ["EQUAL", "NOTEQUAL"].includes(expression.operation) && typeof expression.value === "boolean") {
894
925
  const negative = expression.operation === "EQUAL" && !expression.value || expression.operation === "NOTEQUAL" && expression.value;
895
- string += `${negative ? "!" : ""}${expression.field}`;
926
+ string += `${negative ? DEFAULT_NEGATOR : ""}${expression.field}`;
896
927
  } else {
897
928
  string += expression.field;
898
929
  if (!compact || comparisonNotation === "linguistic")
@@ -901,8 +932,8 @@ function stringify(expression, opts = {}) {
901
932
  if (!compact || comparisonNotation === "linguistic")
902
933
  string += " ";
903
934
  if (Array.isArray(expression.value)) {
904
- const join = expression.value.map(addQuotesIfNecessary).join(compact ? "," : ", ");
905
- string += `[${join}]`;
935
+ const join = expression.value.map(addQuotesIfNecessary).join(compact ? DEFAULT_DELIMITER : DEFAULT_DELIMITER + " ");
936
+ string += `${DEFAULT_BRACKET[0]}${join}${DEFAULT_BRACKET[1]}`;
906
937
  } else
907
938
  string += addQuotesIfNecessary(expression.value);
908
939
  }
@@ -982,17 +1013,17 @@ function createDOMInput({ input, constraints, onUpdate, parseOnInitialize }) {
982
1013
  element.toggleAttribute("data-quoted", true);
983
1014
  if (!isNaN(Number(token.content)))
984
1015
  element.toggleAttribute("data-number", true);
985
- if (["(", ")", "[", "]", "{", "}"].includes(token.content)) {
986
- if (inArray === "[" && token.content === "]" || inArray === "{" && token.content === "}")
1016
+ if (PARENS.concat(BRACKETS).some((entry) => entry.includes(token.content))) {
1017
+ if (inArray && BRACKETS.some(([o, c]) => inArray === o && token.content === c))
987
1018
  inArray = undefined;
988
1019
  if (!inArray)
989
1020
  element.setAttribute("data-bracket", token.content);
990
- if (!inArray && ["[", "{"].includes(token.content))
1021
+ if (!inArray && BRACKETS.some(([o]) => o === token.content))
991
1022
  inArray = token.content;
992
1023
  }
993
- if (inArray && token.content === ",")
1024
+ if (inArray && ARRAY_DELIMITERS.includes(token.content))
994
1025
  element.toggleAttribute("data-delimiter", true);
995
- if (!inArray && token.content === "!")
1026
+ if (!inArray && NEGATORS.includes(token.content))
996
1027
  element.toggleAttribute("data-negation", true);
997
1028
  if (!inArray && token.content in OPERATION_ALIAS_DICTIONARY) {
998
1029
  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.1",
10
10
  "repository": {
11
11
  "type": "git",
12
12
  "url": "git+https://github.com/guidance-analytics/wizard-ql.git"