json-fs-query 2.0.0 → 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/README.md CHANGED
@@ -101,6 +101,19 @@ await query(`read exercises where name = ${quote(name)}`)
101
101
 
102
102
  `quote(value, '"')` wraps in double quotes instead.
103
103
 
104
+ ### Numbers
105
+
106
+ A number may be negative, have a fraction and an exponent: `12`, `-3`, `1.5`, `2.5e-4`. They work the same in
107
+ `values`, `set`, `where`, arrays and defaults:
108
+
109
+ ```sql
110
+ read workouts where reps > -1
111
+ update workouts set weight = 102.5 where _id = 4
112
+ ```
113
+
114
+ A whole number past 9007199254740991 (`Number.MAX_SAFE_INTEGER`), however it is written (`1e20`,
115
+ `9007199254740993.0`), is an error rather than being stored rounded: quote it to keep it as text. A fraction is stored as the nearest double, as in JSON.
116
+
104
117
  ### Arrays
105
118
 
106
119
  Arrays are JSON-style, and their strings can use either quote. They can be nested, and are used by `in` and
@@ -135,6 +148,24 @@ read workouts where reps > 8 and where date = '2024-06-02'
135
148
  `or where` is not supported yet, and is an error (in 1.x it was silently read as `and where`). And/or with grouping
136
149
  and precedence is planned.
137
150
 
151
+ ### Null and missing fields
152
+
153
+ A field can be missing (left out) or hold `null`, and both are their own cases, as in SQL: they match no
154
+ comparison (`=`, `!=`, `<`, `between`, `in`, `like`...), and a `null` never equals anything, not even another
155
+ `null`. These test for them instead:
156
+
157
+ | Condition | Matches when the field |
158
+ |---|---|
159
+ | `where note is null` | is there and holds `null` |
160
+ | `where note is not null` | is there and holds a value |
161
+ | `where note exists` | is there (`null` counts) |
162
+ | `where note does not exist` | is missing |
163
+
164
+ `where note = null` is an error that points to `is null`, since it could never match. Sorting puts missing first,
165
+ then `null`, then the values (`asc`; `desc` is the reverse).
166
+
167
+ In 2.0 and earlier, a `null` acted as `0` in comparisons and sorting (`where n < 5` matched it), and `= null` matched it.
168
+
138
169
  ### Creating a record: `create`
139
170
 
140
171
  The query form of `create()`. Inside the brackets: the fields, then the keys, separated by commas. Everything is
@@ -162,7 +193,7 @@ A field is `<name> <type>`, then any of these, in any order:
162
193
  | `optional` | may be left out (see "Fields" above) |
163
194
  | `null` / `not null` | may hold `null` / may not (the default) |
164
195
  | `auto` | a UUID is generated on insert; the type must be `string` |
165
- | `default <value>` | used when the field is left out: a string, a whole number, an array, `null`, `true` or `false` (so an `object` field can only default to `null` here; `create()` takes any default) |
196
+ | `default <value>` | used when the field is left out: a string, a number, an array, `null`, `true` or `false` (so an `object` field can only default to `null` here; `create()` takes any default) |
166
197
 
167
198
  The types are `string`, `int`, `boolean`, `object` and `array`; any other is an error.
168
199
 
@@ -487,6 +487,10 @@ const typeCheck = (type = 'string', value = undefined, nullable = false) => {
487
487
  if (Number.isNaN(coerced) || !Number.isInteger(coerced)) {
488
488
  throw new Error(`Value "${value}" is not a valid int`);
489
489
  }
490
+ // Past Number.MAX_SAFE_INTEGER an int can't be held exactly: '9007199254740993' would be stored as ...992
491
+ if (!Number.isSafeInteger(coerced)) {
492
+ throw new Error(`Value "${value}" is too large for an int (the largest is ${Number.MAX_SAFE_INTEGER})`);
493
+ }
490
494
  return coerced;
491
495
  }
492
496
  case 'boolean':
@@ -1207,6 +1211,14 @@ Object.defineProperty(exports, "__esModule", {
1207
1211
  value: true
1208
1212
  });
1209
1213
  exports.sortBy = void 0;
1214
+ // Where a value sorts before any other, ascending: missing first, then null, then the values (MySQL's null placement,
1215
+ // with missing below null). Descending reverses it.
1216
+ const presence = (data, property) => {
1217
+ if (!Object.prototype.hasOwnProperty.call(data, property)) {
1218
+ return 0;
1219
+ }
1220
+ return data[property] === null ? 1 : 2;
1221
+ };
1210
1222
  const sortBy = (dataSet = [], sortClauses = []) => {
1211
1223
  if (!sortClauses.length) {
1212
1224
  return dataSet;
@@ -1215,8 +1227,13 @@ const sortBy = (dataSet = [], sortClauses = []) => {
1215
1227
  for (const sortClause of sortClauses) {
1216
1228
  const property = sortClause.property;
1217
1229
  const direction = sortClause.direction;
1230
+ const rankA = presence(a, property);
1231
+ const rankB = presence(b, property);
1232
+ if (rankA !== rankB) {
1233
+ return direction === 'asc' ? rankA - rankB : rankB - rankA;
1234
+ }
1218
1235
  // Ordering compares the raw stored values, not by declared type (that's only for equality, see where), so
1219
- // numeric ids in a string field still sort numerically. Loose so '3' and 3 still tie.
1236
+ // numeric ids in a string field still sort numerically. Loose so '3' and 3 still tie, as do two nulls or two missing.
1220
1237
  // eslint-disable-next-line eqeqeq
1221
1238
  if (a[property] == b[property]) {
1222
1239
  continue;
@@ -1498,6 +1515,12 @@ const likeMatch = (dataValue, pattern) => {
1498
1515
  /**
1499
1516
  * Equality (=, !=, <>, in) compares by the field's declared type when given one (see sameValue), so
1500
1517
  * '3' and 3 match in a string or int field. Ordering (>, <, between, like) compares the raw values.
1518
+ *
1519
+ * Null and missing are their own cases, as in SQL: a field that is null or missing matches no comparison (not even
1520
+ * `!=`), and a null never equals anything, not even another null. Only these test for them:
1521
+ * - `is null`: the field is there and holds null
1522
+ * - `is not null`: the field is there and holds a value (so a missing field doesn't match either)
1523
+ * - `exists` / `does not exist`: the field is there (null counts) / is missing
1501
1524
  * @param data
1502
1525
  * @param property
1503
1526
  * @param comparator
@@ -1505,7 +1528,18 @@ const likeMatch = (dataValue, pattern) => {
1505
1528
  * @param type
1506
1529
  */
1507
1530
  const makeCondition = (data, property, comparator, value, type = undefined) => {
1508
- if (!Object.prototype.hasOwnProperty.call(data, property)) {
1531
+ const exists = Object.prototype.hasOwnProperty.call(data, property);
1532
+ switch (comparator) {
1533
+ case 'exists':
1534
+ return exists;
1535
+ case 'does not exist':
1536
+ return !exists;
1537
+ case 'is null':
1538
+ return exists && data[property] === null;
1539
+ case 'is not null':
1540
+ return exists && data[property] !== null;
1541
+ }
1542
+ if (!exists || data[property] === null || value === null) {
1509
1543
  return false;
1510
1544
  }
1511
1545
  const dataValue = data[property];
@@ -1526,7 +1560,8 @@ const makeCondition = (data, property, comparator, value, type = undefined) => {
1526
1560
  case 'in':
1527
1561
  return Array.isArray(value) && (type ? value.some(item => (0, _compareAs.sameValue)(type, dataValue, item)) : value.includes(dataValue));
1528
1562
  case 'between':
1529
- return Array.isArray(value) && value.length === 2 && dataValue >= value[0] && dataValue <= value[1];
1563
+ // A null bound would act as 0 in >= / <=: it bounds nothing, so nothing matches (the parser refuses it)
1564
+ return Array.isArray(value) && value.length === 2 && !value.includes(null) && dataValue >= value[0] && dataValue <= value[1];
1530
1565
  case 'like':
1531
1566
  return likeMatch(dataValue, value);
1532
1567
  }
@@ -2131,18 +2166,20 @@ Object.defineProperty(exports, "__esModule", {
2131
2166
  value: true
2132
2167
  });
2133
2168
  exports.findConditions = void 0;
2169
+ require("core-js/modules/es.array.includes.js");
2134
2170
  var _removeFromQuery = require("./removeFromQuery");
2135
2171
  var _parseLiteralValue = require("./parseLiteralValue");
2136
2172
  var _maskStrings = require("./maskStrings");
2137
2173
  // `and`/`or`/`where` count only as whole words: in `on t.x = u.color where ...` the `or` is part of the name `color`
2138
- const conditionMatch = new RegExp(`(^|[^a-z0-9._-])(?:(and|or)\\s+)?(where)\\s+([a-z0-9_-]+\\.?[a-z0-9_-]*)\\s+(=|!=|>|<|>=|<=|in|between|like)\\s+(${_maskStrings.placeholderSource}|([0-9]+)|${_maskStrings.arraySource}|(null)|(true)|(false))`, 'ig');
2174
+ const conditionMatch = new RegExp(`(^|[^a-z0-9._-])(?:(and|or)\\s+)?(where)\\s+([a-z0-9_-]+\\.?[a-z0-9_-]*)\\s+(?:(is\\s+not\\s+null|is\\s+null|exists|does\\s+not\\s+exist)(?![a-z0-9_.-])|(=|!=|>|<|>=|<=|in|between|like)\\s+(${_maskStrings.placeholderSource}|(${_parseLiteralValue.numberSource})|${_maskStrings.arraySource}|(null)|(true)|(false)))`, 'ig');
2139
2175
  /**
2140
- * Extract condition clauses from the query: `where <field> <comparator> <value>`, each further one joined with
2141
- * `and where`. `or where` is refused until or is built (TASKS: the where-clauses epic): it used to be read the same as
2176
+ * Extract condition clauses from the query: `where <field> <comparator> <value>`, or `where <field> is null`,
2177
+ * `is not null`, `exists` or `does not exist` (no value), each further one joined with `and where`. A comparison
2178
+ * with the literal null (`= null`) is refused: it would never match, as null equals nothing (see where). `or where` is refused until or is built (TASKS: the where-clauses epic): it used to be read the same as
2142
2179
  * `and where`, which silently returned the wrong rows.
2143
2180
  * @param parsed
2144
2181
  * @param query
2145
- * @throws {Error} For `or where`.
2182
+ * @throws {Error} For `or where`, and for a comparison with null.
2146
2183
  */
2147
2184
  const findConditions = (parsed, query) => {
2148
2185
  const conditionsFound = query.matchAll(conditionMatch);
@@ -2151,18 +2188,33 @@ const findConditions = (parsed, query) => {
2151
2188
  if (typeof condition[2] !== 'undefined' && condition[2].toLowerCase() === 'or') {
2152
2189
  throw new Error('`or where` is not supported yet: use `and where`, or run one query per alternative');
2153
2190
  }
2154
- parsed['conditions'].push({
2155
- property: condition[4],
2156
- comparator: condition[5],
2157
- value: (0, _parseLiteralValue.parseLiteralValue)(condition, 7)
2158
- });
2191
+ if (typeof condition[5] !== 'undefined') {
2192
+ parsed['conditions'].push({
2193
+ property: condition[4],
2194
+ comparator: condition[5].toLowerCase().replace(/\s+/g, ' '),
2195
+ value: null
2196
+ });
2197
+ } else {
2198
+ const value = (0, _parseLiteralValue.parseLiteralValue)(condition, 8);
2199
+ if (value === null) {
2200
+ throw new Error(`\`${condition[4]} ${condition[6]} null\` never matches (null equals nothing, as in SQL): use \`${condition[4]} is null\` or \`${condition[4]} is not null\``);
2201
+ }
2202
+ if (condition[6].toLowerCase() === 'between' && Array.isArray(value) && value.includes(null)) {
2203
+ throw new Error(`\`${condition[4]} between\` can't have a null bound: it would never match`);
2204
+ }
2205
+ parsed['conditions'].push({
2206
+ property: condition[4],
2207
+ comparator: condition[6],
2208
+ value
2209
+ });
2210
+ }
2159
2211
  // Leave out the character before the condition (group 1): it isn't part of it, and an earlier removal may have trimmed it
2160
2212
  query = (0, _removeFromQuery.removeFromQuery)(condition[0].slice(condition[1].length), query);
2161
2213
  }
2162
2214
  return query;
2163
2215
  };
2164
2216
  exports.findConditions = findConditions;
2165
- },{"./maskStrings":58,"./parseLiteralValue":60,"./removeFromQuery":63}],47:[function(require,module,exports){
2217
+ },{"./maskStrings":58,"./parseLiteralValue":60,"./removeFromQuery":63,"core-js/modules/es.array.includes.js":218}],47:[function(require,module,exports){
2166
2218
  "use strict";
2167
2219
 
2168
2220
  Object.defineProperty(exports, "__esModule", {
@@ -2195,9 +2247,9 @@ exports.findInsertValues = void 0;
2195
2247
  var _removeFromQuery = require("./removeFromQuery");
2196
2248
  var _parseLiteralValue = require("./parseLiteralValue");
2197
2249
  var _maskStrings = require("./maskStrings");
2198
- const literal = `(?:${_maskStrings.placeholderSource.replace('(', '(?:')}|[0-9]+|${_maskStrings.arraySource.replace('(', '(?:')}|null|true|false)`;
2250
+ const literal = `(?:${_maskStrings.placeholderSource.replace('(', '(?:')}|${_parseLiteralValue.numberSource}|${_maskStrings.arraySource.replace('(', '(?:')}|null|true|false)`;
2199
2251
  const valuesClauseMatch = new RegExp(`(values)\\s+([a-z0-9_-]+\\s*=\\s*${literal}(?:\\s*,\\s*[a-z0-9_-]+\\s*=\\s*${literal})*)`, 'i');
2200
- const assignmentMatch = new RegExp(`([a-z0-9_-]+)\\s*=\\s*(${_maskStrings.placeholderSource}|([0-9]+)|${_maskStrings.arraySource}|(null)|(true)|(false))`, 'ig');
2252
+ const assignmentMatch = new RegExp(`([a-z0-9_-]+)\\s*=\\s*(${_maskStrings.placeholderSource}|(${_parseLiteralValue.numberSource})|${_maskStrings.arraySource}|(null)|(true)|(false))`, 'ig');
2201
2253
  /**
2202
2254
  * Extract an insert's "values field = value, field2 = value2" clause from the query, the same way
2203
2255
  * findUpdateValues() does for "set". insertEntity() accepts an array of rows for bulk inserts, but
@@ -2257,7 +2309,9 @@ Object.defineProperty(exports, "__esModule", {
2257
2309
  });
2258
2310
  exports.findJoinEntity = void 0;
2259
2311
  var _removeFromQuery = require("./removeFromQuery");
2260
- const joinEntityMatch = /\.([a-z0-9_-]+)/i;
2312
+ // Only straight after the entity (findCommand leaves `.joined ...`): a `.` later on is a number's (`1.5`) or a
2313
+ // condition's `entity.field`, not the join
2314
+ const joinEntityMatch = /^\.([a-z0-9_-]+)/i;
2261
2315
  /**
2262
2316
  * Extract the main join entity from the query.
2263
2317
  * @param parsed
@@ -2423,9 +2477,9 @@ exports.findUpdateValues = void 0;
2423
2477
  var _removeFromQuery = require("./removeFromQuery");
2424
2478
  var _parseLiteralValue = require("./parseLiteralValue");
2425
2479
  var _maskStrings = require("./maskStrings");
2426
- const literal = `(?:${_maskStrings.placeholderSource.replace('(', '(?:')}|[0-9]+|${_maskStrings.arraySource.replace('(', '(?:')}|null|true|false)`;
2480
+ const literal = `(?:${_maskStrings.placeholderSource.replace('(', '(?:')}|${_parseLiteralValue.numberSource}|${_maskStrings.arraySource.replace('(', '(?:')}|null|true|false)`;
2427
2481
  const setClauseMatch = new RegExp(`(set)\\s+([a-z0-9_-]+\\s*=\\s*${literal}(?:\\s*,\\s*[a-z0-9_-]+\\s*=\\s*${literal})*)`, 'i');
2428
- const assignmentMatch = new RegExp(`([a-z0-9_-]+)\\s*=\\s*(${_maskStrings.placeholderSource}|([0-9]+)|${_maskStrings.arraySource}|(null)|(true)|(false))`, 'ig');
2482
+ const assignmentMatch = new RegExp(`([a-z0-9_-]+)\\s*=\\s*(${_maskStrings.placeholderSource}|(${_parseLiteralValue.numberSource})|${_maskStrings.arraySource}|(null)|(true)|(false))`, 'ig');
2429
2483
  /**
2430
2484
  * Extract an update's "set field = value, field2 = value2" clause from the query. Uses a two-pass
2431
2485
  * match, unlike the other parsers here: the set clause's own boundary is found first (so it can
@@ -2539,11 +2593,15 @@ const unmaskStrings = (text, quote = 'json') => text.replace(placeholderMatch, (
2539
2593
  exports.unmaskStrings = unmaskStrings;
2540
2594
  const arrayJson = text => {
2541
2595
  try {
2542
- // A number too big for a double reads as Infinity, which JSON would write back as null: refuse it instead
2596
+ // A number too big for a double reads as Infinity, which JSON would write back as null, and a whole number past
2597
+ // Number.MAX_SAFE_INTEGER may already be rounded: refuse both, as parseNumber does outside arrays
2543
2598
  return JSON.stringify(JSON.parse(unmaskStrings(text), (key, value) => {
2544
2599
  if (typeof value === 'number' && !Number.isFinite(value)) {
2545
2600
  throw new Error(`${value} is too large`);
2546
2601
  }
2602
+ if (Number.isInteger(value) && !Number.isSafeInteger(value)) {
2603
+ throw new Error(`${value} is too large to store exactly (the largest whole number is ${Number.MAX_SAFE_INTEGER}): quote it to keep it as text`);
2604
+ }
2547
2605
  return value;
2548
2606
  }));
2549
2607
  } catch (e) {
@@ -2613,11 +2671,13 @@ require("core-js/modules/esnext.set.symmetric-difference.js");
2613
2671
  require("core-js/modules/esnext.set.union.js");
2614
2672
  var _typeCheck = require("../../introspect/policies/typeCheck");
2615
2673
  var _maskStrings = require("./maskStrings");
2674
+ var _parseLiteralValue = require("./parseLiteralValue");
2616
2675
  var _startsQuery = require("./startsQuery");
2617
2676
  const createMatch = exports.createMatch = /^\s*create(?![a-z0-9_-])/i;
2618
2677
  const headMatch = /^\s*create\s+(if\s+not\s+exists\s+)?([a-z0-9_-]+)\s*\(/i;
2619
2678
  const nameSource = '[a-z0-9_-]+';
2620
2679
  const nameMatch = new RegExp(`^${nameSource}$`, 'i');
2680
+ const numberMatch = new RegExp(`^${_parseLiteralValue.numberSource}$`, 'i');
2621
2681
  const fieldMatch = new RegExp(`^(${nameSource})\\s+(${nameSource})((?:\\s[\\s\\S]*)?)$`, 'i');
2622
2682
  const primaryMatch = /^primary\s+key\s*\(([^()]*)\)$/i;
2623
2683
  const uniqueMatch = /^unique\s*\(([^()]*)\)$/i;
@@ -2708,8 +2768,8 @@ const parseDefault = token => {
2708
2768
  if (array) {
2709
2769
  return JSON.parse((0, _maskStrings.fromPlaceholder)(array[1]));
2710
2770
  }
2711
- if (/^[0-9]+$/.test(token)) {
2712
- return Number(token);
2771
+ if (numberMatch.test(token)) {
2772
+ return (0, _parseLiteralValue.parseNumber)(token);
2713
2773
  }
2714
2774
  switch (token.toLowerCase()) {
2715
2775
  case 'null':
@@ -2913,14 +2973,39 @@ const parseCreate = query => {
2913
2973
  };
2914
2974
  };
2915
2975
  exports.parseCreate = parseCreate;
2916
- },{"../../introspect/policies/typeCheck":13,"./maskStrings":58,"./startsQuery":67,"core-js/modules/es.array.includes.js":218,"core-js/modules/esnext.iterator.constructor.js":232,"core-js/modules/esnext.iterator.filter.js":235,"core-js/modules/esnext.iterator.find.js":236,"core-js/modules/esnext.iterator.map.js":238,"core-js/modules/esnext.iterator.some.js":240,"core-js/modules/esnext.set.add-all.js":254,"core-js/modules/esnext.set.delete-all.js":255,"core-js/modules/esnext.set.difference.js":256,"core-js/modules/esnext.set.every.js":257,"core-js/modules/esnext.set.filter.js":258,"core-js/modules/esnext.set.find.js":259,"core-js/modules/esnext.set.intersection.js":260,"core-js/modules/esnext.set.is-disjoint-from.js":261,"core-js/modules/esnext.set.is-subset-of.js":262,"core-js/modules/esnext.set.is-superset-of.js":263,"core-js/modules/esnext.set.join.js":264,"core-js/modules/esnext.set.map.js":265,"core-js/modules/esnext.set.reduce.js":266,"core-js/modules/esnext.set.some.js":267,"core-js/modules/esnext.set.symmetric-difference.js":268,"core-js/modules/esnext.set.union.js":269}],60:[function(require,module,exports){
2976
+ },{"../../introspect/policies/typeCheck":13,"./maskStrings":58,"./parseLiteralValue":60,"./startsQuery":67,"core-js/modules/es.array.includes.js":218,"core-js/modules/esnext.iterator.constructor.js":232,"core-js/modules/esnext.iterator.filter.js":235,"core-js/modules/esnext.iterator.find.js":236,"core-js/modules/esnext.iterator.map.js":238,"core-js/modules/esnext.iterator.some.js":240,"core-js/modules/esnext.set.add-all.js":254,"core-js/modules/esnext.set.delete-all.js":255,"core-js/modules/esnext.set.difference.js":256,"core-js/modules/esnext.set.every.js":257,"core-js/modules/esnext.set.filter.js":258,"core-js/modules/esnext.set.find.js":259,"core-js/modules/esnext.set.intersection.js":260,"core-js/modules/esnext.set.is-disjoint-from.js":261,"core-js/modules/esnext.set.is-subset-of.js":262,"core-js/modules/esnext.set.is-superset-of.js":263,"core-js/modules/esnext.set.join.js":264,"core-js/modules/esnext.set.map.js":265,"core-js/modules/esnext.set.reduce.js":266,"core-js/modules/esnext.set.some.js":267,"core-js/modules/esnext.set.symmetric-difference.js":268,"core-js/modules/esnext.set.union.js":269}],60:[function(require,module,exports){
2917
2977
  "use strict";
2918
2978
 
2919
2979
  Object.defineProperty(exports, "__esModule", {
2920
2980
  value: true
2921
2981
  });
2922
- exports.parseLiteralValue = void 0;
2982
+ exports.parseNumber = exports.parseLiteralValue = exports.numberSource = void 0;
2923
2983
  var _maskStrings = require("./maskStrings");
2984
+ /**
2985
+ * Regex source matching one number literal: an optional `-`, digits, an optional fraction and an optional exponent
2986
+ * (`12`, `-3`, `1.5`, `1e3`, `2.5E-4`), with no group of its own. Anything left after it (`5abc`, `1.5.2`) is an
2987
+ * unrecognized query value, as for any other leftover text.
2988
+ */
2989
+ const numberSource = exports.numberSource = '-?[0-9]+(?:\\.[0-9]+)?(?:e[+-]?[0-9]+)?';
2990
+ /**
2991
+ * Read one number literal's text (see numberSource) as a number, refusing any it can't hold exactly as written: a
2992
+ * whole number past Number.MAX_SAFE_INTEGER, however it is written (`9007199254740993`, `9007199254740993.0`, `1e20`),
2993
+ * may be stored rounded (9007199254740993 as 9007199254740992), and a number too big for a double reads as Infinity,
2994
+ * which JSON writes back as null. Fractions are kept as the nearest double, as JSON does (an exact decimal type is
2995
+ * planned).
2996
+ * @param text
2997
+ * @throws {Error} For an integer past Number.MAX_SAFE_INTEGER, or a number too big for a double.
2998
+ */
2999
+ const parseNumber = text => {
3000
+ const value = Number(text);
3001
+ if (!Number.isFinite(value)) {
3002
+ throw new Error(`${text} is too large`);
3003
+ }
3004
+ if (Number.isInteger(value) && !Number.isSafeInteger(value)) {
3005
+ throw new Error(`${text} is too large to store exactly (the largest whole number is ${Number.MAX_SAFE_INTEGER}): quote it to keep it as text`);
3006
+ }
3007
+ return value;
3008
+ };
2924
3009
  /**
2925
3010
  * Several parsers (findConditions, findInsertValues, findUpdateValues) capture a query-string
2926
3011
  * literal into one of six consecutive alternation groups depending on its shape - quoted string
@@ -2931,12 +3016,13 @@ var _maskStrings = require("./maskStrings");
2931
3016
  * @param match
2932
3017
  * @param baseIndex
2933
3018
  */
3019
+ exports.parseNumber = parseNumber;
2934
3020
  const parseLiteralValue = (match, baseIndex) => {
2935
3021
  if (typeof match[baseIndex] !== 'undefined') {
2936
3022
  return (0, _maskStrings.fromPlaceholder)(match[baseIndex]);
2937
3023
  }
2938
3024
  if (typeof match[baseIndex + 1] !== 'undefined') {
2939
- return Number(match[baseIndex + 1]);
3025
+ return parseNumber(match[baseIndex + 1]);
2940
3026
  }
2941
3027
  if (typeof match[baseIndex + 2] !== 'undefined') {
2942
3028
  // An array is a placeholder from maskArrays, holding its JSON