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 +32 -1
- package/browser/jsonFsQuery.js +110 -24
- package/browser/jsonFsQuery.min.js +1 -1
- package/dist/data/introspect/policies/typeCheck.js +4 -0
- package/dist/data/introspect/policies/typeCheck.min.js +1 -1
- package/dist/data/queries/sortBy.js +14 -1
- package/dist/data/queries/sortBy.min.js +1 -1
- package/dist/data/queries/where.js +20 -2
- package/dist/data/queries/where.min.js +1 -1
- package/dist/data/utilities/parsers/findConditions.d.ts +4 -3
- package/dist/data/utilities/parsers/findConditions.js +26 -9
- package/dist/data/utilities/parsers/findConditions.min.js +1 -1
- package/dist/data/utilities/parsers/findInsertValues.js +2 -2
- package/dist/data/utilities/parsers/findInsertValues.min.js +1 -1
- package/dist/data/utilities/parsers/findJoinEntity.js +3 -1
- package/dist/data/utilities/parsers/findJoinEntity.min.js +1 -1
- package/dist/data/utilities/parsers/findUpdateValues.js +2 -2
- package/dist/data/utilities/parsers/findUpdateValues.min.js +1 -1
- package/dist/data/utilities/parsers/maskStrings.js +5 -1
- package/dist/data/utilities/parsers/maskStrings.min.js +1 -1
- package/dist/data/utilities/parsers/parseCreate.js +4 -2
- package/dist/data/utilities/parsers/parseCreate.min.js +1 -1
- package/dist/data/utilities/parsers/parseLiteralValue.d.ts +16 -0
- package/dist/data/utilities/parsers/parseLiteralValue.js +28 -2
- package/dist/data/utilities/parsers/parseLiteralValue.min.js +1 -1
- package/package.json +1 -1
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
|
|
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
|
|
package/browser/jsonFsQuery.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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}|(
|
|
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>`,
|
|
2141
|
-
* `
|
|
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
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
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('(', '(?:')}
|
|
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}|(
|
|
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
|
-
|
|
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('(', '(?:')}
|
|
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}|(
|
|
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
|
|
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 (
|
|
2712
|
-
return
|
|
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
|
|
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
|