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
|
@@ -41,6 +41,10 @@ const typeCheck = (type = 'string', value = undefined, nullable = false) => {
|
|
|
41
41
|
if (Number.isNaN(coerced) || !Number.isInteger(coerced)) {
|
|
42
42
|
throw new Error(`Value "${value}" is not a valid int`);
|
|
43
43
|
}
|
|
44
|
+
// Past Number.MAX_SAFE_INTEGER an int can't be held exactly: '9007199254740993' would be stored as ...992
|
|
45
|
+
if (!Number.isSafeInteger(coerced)) {
|
|
46
|
+
throw new Error(`Value "${value}" is too large for an int (the largest is ${Number.MAX_SAFE_INTEGER})`);
|
|
47
|
+
}
|
|
44
48
|
return coerced;
|
|
45
49
|
}
|
|
46
50
|
case 'boolean':
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.typeCheck=exports.fieldTypes=void 0;const fieldTypes=exports.fieldTypes=["string","int","boolean","object","array"],typeCheck=(e="string",r=void 0,t=!1)=>{if(void 0===r)return r;if(null===r){if(t)return r;throw new Error('Value "null" is not allowed: the field is not nullable')}switch(e){case"string":return"string"==typeof r?r:String(r);case"int":{const e=Number(r);if(Number.isNaN(e)||!Number.isInteger(e))throw new Error(`Value "${r}" is not a valid int`);return e}case"boolean":if("boolean"==typeof r)return r;if("true"===r)return!0;if("false"===r)return!1;throw new Error(`Value "${r}" is not a valid boolean`);case"object":if("object"!=typeof r||null===r||Array.isArray(r))throw new Error(`Value "${r}" is not a valid object`);return r;case"array":if(!Array.isArray(r))throw new Error(`Value "${r}" is not a valid array`);return r;default:return r}};exports.typeCheck=typeCheck;
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.typeCheck=exports.fieldTypes=void 0;const fieldTypes=exports.fieldTypes=["string","int","boolean","object","array"],typeCheck=(e="string",r=void 0,t=!1)=>{if(void 0===r)return r;if(null===r){if(t)return r;throw new Error('Value "null" is not allowed: the field is not nullable')}switch(e){case"string":return"string"==typeof r?r:String(r);case"int":{const e=Number(r);if(Number.isNaN(e)||!Number.isInteger(e))throw new Error(`Value "${r}" is not a valid int`);if(!Number.isSafeInteger(e))throw new Error(`Value "${r}" is too large for an int (the largest is ${Number.MAX_SAFE_INTEGER})`);return e}case"boolean":if("boolean"==typeof r)return r;if("true"===r)return!0;if("false"===r)return!1;throw new Error(`Value "${r}" is not a valid boolean`);case"object":if("object"!=typeof r||null===r||Array.isArray(r))throw new Error(`Value "${r}" is not a valid object`);return r;case"array":if(!Array.isArray(r))throw new Error(`Value "${r}" is not a valid array`);return r;default:return r}};exports.typeCheck=typeCheck;
|
|
@@ -4,6 +4,14 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.sortBy = void 0;
|
|
7
|
+
// Where a value sorts before any other, ascending: missing first, then null, then the values (MySQL's null placement,
|
|
8
|
+
// with missing below null). Descending reverses it.
|
|
9
|
+
const presence = (data, property) => {
|
|
10
|
+
if (!Object.prototype.hasOwnProperty.call(data, property)) {
|
|
11
|
+
return 0;
|
|
12
|
+
}
|
|
13
|
+
return data[property] === null ? 1 : 2;
|
|
14
|
+
};
|
|
7
15
|
const sortBy = (dataSet = [], sortClauses = []) => {
|
|
8
16
|
if (!sortClauses.length) {
|
|
9
17
|
return dataSet;
|
|
@@ -12,8 +20,13 @@ const sortBy = (dataSet = [], sortClauses = []) => {
|
|
|
12
20
|
for (const sortClause of sortClauses) {
|
|
13
21
|
const property = sortClause.property;
|
|
14
22
|
const direction = sortClause.direction;
|
|
23
|
+
const rankA = presence(a, property);
|
|
24
|
+
const rankB = presence(b, property);
|
|
25
|
+
if (rankA !== rankB) {
|
|
26
|
+
return direction === 'asc' ? rankA - rankB : rankB - rankA;
|
|
27
|
+
}
|
|
15
28
|
// Ordering compares the raw stored values, not by declared type (that's only for equality, see where), so
|
|
16
|
-
// numeric ids in a string field still sort numerically. Loose so '3' and 3 still tie.
|
|
29
|
+
// numeric ids in a string field still sort numerically. Loose so '3' and 3 still tie, as do two nulls or two missing.
|
|
17
30
|
// eslint-disable-next-line eqeqeq
|
|
18
31
|
if (a[property] == b[property]) {
|
|
19
32
|
continue;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.sortBy=void 0;const sortBy=(
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.sortBy=void 0;const presence=(e,r)=>Object.prototype.hasOwnProperty.call(e,r)?null===e[r]?1:2:0,sortBy=(e=[],r=[])=>r.length?(e.sort(((e,t)=>{for(const o of r){const r=o.property,s=o.direction,n=presence(e,r),c=presence(t,r);if(n!==c)return"asc"===s?n-c:c-n;if(e[r]==t[r])continue;const p=e[r]<t[r]?-1:1;return"asc"===s?p:-1*p}return 0})),e):e;exports.sortBy=sortBy;
|
|
@@ -19,6 +19,12 @@ const likeMatch = (dataValue, pattern) => {
|
|
|
19
19
|
/**
|
|
20
20
|
* Equality (=, !=, <>, in) compares by the field's declared type when given one (see sameValue), so
|
|
21
21
|
* '3' and 3 match in a string or int field. Ordering (>, <, between, like) compares the raw values.
|
|
22
|
+
*
|
|
23
|
+
* Null and missing are their own cases, as in SQL: a field that is null or missing matches no comparison (not even
|
|
24
|
+
* `!=`), and a null never equals anything, not even another null. Only these test for them:
|
|
25
|
+
* - `is null`: the field is there and holds null
|
|
26
|
+
* - `is not null`: the field is there and holds a value (so a missing field doesn't match either)
|
|
27
|
+
* - `exists` / `does not exist`: the field is there (null counts) / is missing
|
|
22
28
|
* @param data
|
|
23
29
|
* @param property
|
|
24
30
|
* @param comparator
|
|
@@ -26,7 +32,18 @@ const likeMatch = (dataValue, pattern) => {
|
|
|
26
32
|
* @param type
|
|
27
33
|
*/
|
|
28
34
|
const makeCondition = (data, property, comparator, value, type = undefined) => {
|
|
29
|
-
|
|
35
|
+
const exists = Object.prototype.hasOwnProperty.call(data, property);
|
|
36
|
+
switch (comparator) {
|
|
37
|
+
case 'exists':
|
|
38
|
+
return exists;
|
|
39
|
+
case 'does not exist':
|
|
40
|
+
return !exists;
|
|
41
|
+
case 'is null':
|
|
42
|
+
return exists && data[property] === null;
|
|
43
|
+
case 'is not null':
|
|
44
|
+
return exists && data[property] !== null;
|
|
45
|
+
}
|
|
46
|
+
if (!exists || data[property] === null || value === null) {
|
|
30
47
|
return false;
|
|
31
48
|
}
|
|
32
49
|
const dataValue = data[property];
|
|
@@ -47,7 +64,8 @@ const makeCondition = (data, property, comparator, value, type = undefined) => {
|
|
|
47
64
|
case 'in':
|
|
48
65
|
return Array.isArray(value) && (type ? value.some(item => (0, _compareAs.sameValue)(type, dataValue, item)) : value.includes(dataValue));
|
|
49
66
|
case 'between':
|
|
50
|
-
|
|
67
|
+
// A null bound would act as 0 in >= / <=: it bounds nothing, so nothing matches (the parser refuses it)
|
|
68
|
+
return Array.isArray(value) && value.length === 2 && !value.includes(null) && dataValue >= value[0] && dataValue <= value[1];
|
|
51
69
|
case 'like':
|
|
52
70
|
return likeMatch(dataValue, value);
|
|
53
71
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.where=void 0,require("core-js/modules/es.array.includes.js"),require("core-js/modules/esnext.iterator.constructor.js"),require("core-js/modules/esnext.iterator.filter.js"),require("core-js/modules/esnext.iterator.some.js");var _compareAs=require("../utilities/compareAs");const likeMatch=(e,r)=>{if("string"!=typeof e||"string"!=typeof r)return!1;const t=r.replace(/[.*+?^${}()|[\]\\]/g,"\\$&").replace(/%/g,".*").replace(/_/g,".");return new RegExp(`^${t}$`,"i").test(e)},makeCondition=(e,r,t,s,
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.where=void 0,require("core-js/modules/es.array.includes.js"),require("core-js/modules/esnext.iterator.constructor.js"),require("core-js/modules/esnext.iterator.filter.js"),require("core-js/modules/esnext.iterator.some.js");var _compareAs=require("../utilities/compareAs");const likeMatch=(e,r)=>{if("string"!=typeof e||"string"!=typeof r)return!1;const t=r.replace(/[.*+?^${}()|[\]\\]/g,"\\$&").replace(/%/g,".*").replace(/_/g,".");return new RegExp(`^${t}$`,"i").test(e)},makeCondition=(e,r,t,s,n=void 0)=>{const a=Object.prototype.hasOwnProperty.call(e,r);switch(t){case"exists":return a;case"does not exist":return!a;case"is null":return a&&null===e[r];case"is not null":return a&&null!==e[r]}if(!a||null===e[r]||null===s)return!1;const o=e[r];switch(t){case"=":return(0,_compareAs.sameValue)(n,o,s);case"!=":case"<>":return!(0,_compareAs.sameValue)(n,o,s);case">":return o>s;case">=":return o>=s;case"<":return o<s;case"<=":return o<=s;case"in":return Array.isArray(s)&&(n?s.some((e=>(0,_compareAs.sameValue)(n,o,e))):s.includes(o));case"between":return Array.isArray(s)&&2===s.length&&!s.includes(null)&&o>=s[0]&&o<=s[1];case"like":return likeMatch(o,s)}return!1},where=(e=[],{property:r="",joinEntity:t,comparator:s="=",value:n=null,type:a}={})=>e.filter((e=>makeCondition(e,t?`${t}.${r}`:r,s,n,a)));exports.where=where;
|
|
@@ -8,12 +8,13 @@ export type ParsedCondition = {
|
|
|
8
8
|
type?: string;
|
|
9
9
|
};
|
|
10
10
|
/**
|
|
11
|
-
* Extract condition clauses from the query: `where <field> <comparator> <value>`,
|
|
12
|
-
* `
|
|
11
|
+
* Extract condition clauses from the query: `where <field> <comparator> <value>`, or `where <field> is null`,
|
|
12
|
+
* `is not null`, `exists` or `does not exist` (no value), each further one joined with `and where`. A comparison
|
|
13
|
+
* 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
|
|
13
14
|
* `and where`, which silently returned the wrong rows.
|
|
14
15
|
* @param parsed
|
|
15
16
|
* @param query
|
|
16
|
-
* @throws {Error} For `or where
|
|
17
|
+
* @throws {Error} For `or where`, and for a comparison with null.
|
|
17
18
|
*/
|
|
18
19
|
export declare const findConditions: (parsed: ParsedQuery, query: string) => string;
|
|
19
20
|
//# sourceMappingURL=findConditions.d.ts.map
|
|
@@ -4,18 +4,20 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.findConditions = void 0;
|
|
7
|
+
require("core-js/modules/es.array.includes.js");
|
|
7
8
|
var _removeFromQuery = require("./removeFromQuery");
|
|
8
9
|
var _parseLiteralValue = require("./parseLiteralValue");
|
|
9
10
|
var _maskStrings = require("./maskStrings");
|
|
10
11
|
// `and`/`or`/`where` count only as whole words: in `on t.x = u.color where ...` the `or` is part of the name `color`
|
|
11
|
-
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}|(
|
|
12
|
+
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');
|
|
12
13
|
/**
|
|
13
|
-
* Extract condition clauses from the query: `where <field> <comparator> <value>`,
|
|
14
|
-
* `
|
|
14
|
+
* Extract condition clauses from the query: `where <field> <comparator> <value>`, or `where <field> is null`,
|
|
15
|
+
* `is not null`, `exists` or `does not exist` (no value), each further one joined with `and where`. A comparison
|
|
16
|
+
* 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
|
|
15
17
|
* `and where`, which silently returned the wrong rows.
|
|
16
18
|
* @param parsed
|
|
17
19
|
* @param query
|
|
18
|
-
* @throws {Error} For `or where
|
|
20
|
+
* @throws {Error} For `or where`, and for a comparison with null.
|
|
19
21
|
*/
|
|
20
22
|
const findConditions = (parsed, query) => {
|
|
21
23
|
const conditionsFound = query.matchAll(conditionMatch);
|
|
@@ -24,11 +26,26 @@ const findConditions = (parsed, query) => {
|
|
|
24
26
|
if (typeof condition[2] !== 'undefined' && condition[2].toLowerCase() === 'or') {
|
|
25
27
|
throw new Error('`or where` is not supported yet: use `and where`, or run one query per alternative');
|
|
26
28
|
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
if (typeof condition[5] !== 'undefined') {
|
|
30
|
+
parsed['conditions'].push({
|
|
31
|
+
property: condition[4],
|
|
32
|
+
comparator: condition[5].toLowerCase().replace(/\s+/g, ' '),
|
|
33
|
+
value: null
|
|
34
|
+
});
|
|
35
|
+
} else {
|
|
36
|
+
const value = (0, _parseLiteralValue.parseLiteralValue)(condition, 8);
|
|
37
|
+
if (value === null) {
|
|
38
|
+
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\``);
|
|
39
|
+
}
|
|
40
|
+
if (condition[6].toLowerCase() === 'between' && Array.isArray(value) && value.includes(null)) {
|
|
41
|
+
throw new Error(`\`${condition[4]} between\` can't have a null bound: it would never match`);
|
|
42
|
+
}
|
|
43
|
+
parsed['conditions'].push({
|
|
44
|
+
property: condition[4],
|
|
45
|
+
comparator: condition[6],
|
|
46
|
+
value
|
|
47
|
+
});
|
|
48
|
+
}
|
|
32
49
|
// Leave out the character before the condition (group 1): it isn't part of it, and an earlier removal may have trimmed it
|
|
33
50
|
query = (0, _removeFromQuery.removeFromQuery)(condition[0].slice(condition[1].length), query);
|
|
34
51
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.findConditions=void 0;var _removeFromQuery=require("./removeFromQuery"),_parseLiteralValue=require("./parseLiteralValue"),_maskStrings=require("./maskStrings");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}|(
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.findConditions=void 0,require("core-js/modules/es.array.includes.js");var _removeFromQuery=require("./removeFromQuery"),_parseLiteralValue=require("./parseLiteralValue"),_maskStrings=require("./maskStrings");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"),findConditions=(e,r)=>{const o=r.matchAll(conditionMatch);for(const n of o){if(void 0!==n[2]&&"or"===n[2].toLowerCase())throw new Error("`or where` is not supported yet: use `and where`, or run one query per alternative");if(void 0!==n[5])e.conditions.push({property:n[4],comparator:n[5].toLowerCase().replace(/\s+/g," "),value:null});else{const r=(0,_parseLiteralValue.parseLiteralValue)(n,8);if(null===r)throw new Error(`\`${n[4]} ${n[6]} null\` never matches (null equals nothing, as in SQL): use \`${n[4]} is null\` or \`${n[4]} is not null\``);if("between"===n[6].toLowerCase()&&Array.isArray(r)&&r.includes(null))throw new Error(`\`${n[4]} between\` can't have a null bound: it would never match`);e.conditions.push({property:n[4],comparator:n[6],value:r})}r=(0,_removeFromQuery.removeFromQuery)(n[0].slice(n[1].length),r)}return r};exports.findConditions=findConditions;
|
|
@@ -7,9 +7,9 @@ exports.findInsertValues = void 0;
|
|
|
7
7
|
var _removeFromQuery = require("./removeFromQuery");
|
|
8
8
|
var _parseLiteralValue = require("./parseLiteralValue");
|
|
9
9
|
var _maskStrings = require("./maskStrings");
|
|
10
|
-
const literal = `(?:${_maskStrings.placeholderSource.replace('(', '(?:')}
|
|
10
|
+
const literal = `(?:${_maskStrings.placeholderSource.replace('(', '(?:')}|${_parseLiteralValue.numberSource}|${_maskStrings.arraySource.replace('(', '(?:')}|null|true|false)`;
|
|
11
11
|
const valuesClauseMatch = new RegExp(`(values)\\s+([a-z0-9_-]+\\s*=\\s*${literal}(?:\\s*,\\s*[a-z0-9_-]+\\s*=\\s*${literal})*)`, 'i');
|
|
12
|
-
const assignmentMatch = new RegExp(`([a-z0-9_-]+)\\s*=\\s*(${_maskStrings.placeholderSource}|(
|
|
12
|
+
const assignmentMatch = new RegExp(`([a-z0-9_-]+)\\s*=\\s*(${_maskStrings.placeholderSource}|(${_parseLiteralValue.numberSource})|${_maskStrings.arraySource}|(null)|(true)|(false))`, 'ig');
|
|
13
13
|
/**
|
|
14
14
|
* Extract an insert's "values field = value, field2 = value2" clause from the query, the same way
|
|
15
15
|
* findUpdateValues() does for "set". insertEntity() accepts an array of rows for bulk inserts, but
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.findInsertValues=void 0;var _removeFromQuery=require("./removeFromQuery"),_parseLiteralValue=require("./parseLiteralValue"),_maskStrings=require("./maskStrings");const literal=`(?:${_maskStrings.placeholderSource.replace("(","(?:")}
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.findInsertValues=void 0;var _removeFromQuery=require("./removeFromQuery"),_parseLiteralValue=require("./parseLiteralValue"),_maskStrings=require("./maskStrings");const literal=`(?:${_maskStrings.placeholderSource.replace("(","(?:")}|${_parseLiteralValue.numberSource}|${_maskStrings.arraySource.replace("(","(?:")}|null|true|false)`,valuesClauseMatch=new RegExp(`(values)\\s+([a-z0-9_-]+\\s*=\\s*${literal}(?:\\s*,\\s*[a-z0-9_-]+\\s*=\\s*${literal})*)`,"i"),assignmentMatch=new RegExp(`([a-z0-9_-]+)\\s*=\\s*(${_maskStrings.placeholderSource}|(${_parseLiteralValue.numberSource})|${_maskStrings.arraySource}|(null)|(true)|(false))`,"ig"),findInsertValues=(e,r)=>{const a=r.match(valuesClauseMatch);if(e.insertValues=[],!a)return r;const s={},l=a[2].matchAll(assignmentMatch);for(const e of l)s[e[1]]=(0,_parseLiteralValue.parseLiteralValue)(e,3);return e.insertValues=[s],(0,_removeFromQuery.removeFromQuery)(a[0],r)};exports.findInsertValues=findInsertValues;
|
|
@@ -5,7 +5,9 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.findJoinEntity = void 0;
|
|
7
7
|
var _removeFromQuery = require("./removeFromQuery");
|
|
8
|
-
|
|
8
|
+
// Only straight after the entity (findCommand leaves `.joined ...`): a `.` later on is a number's (`1.5`) or a
|
|
9
|
+
// condition's `entity.field`, not the join
|
|
10
|
+
const joinEntityMatch = /^\.([a-z0-9_-]+)/i;
|
|
9
11
|
/**
|
|
10
12
|
* Extract the main join entity from the query.
|
|
11
13
|
* @param parsed
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.findJoinEntity=void 0;var _removeFromQuery=require("./removeFromQuery");const joinEntityMatch
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.findJoinEntity=void 0;var _removeFromQuery=require("./removeFromQuery");const joinEntityMatch=/^\.([a-z0-9_-]+)/i,findJoinEntity=(t,e)=>{const i=e.match(joinEntityMatch);return i?(t.joinEntity=i?i[1]:void 0,(0,_removeFromQuery.removeFromQuery)(i[0],e)):e};exports.findJoinEntity=findJoinEntity;
|
|
@@ -7,9 +7,9 @@ exports.findUpdateValues = void 0;
|
|
|
7
7
|
var _removeFromQuery = require("./removeFromQuery");
|
|
8
8
|
var _parseLiteralValue = require("./parseLiteralValue");
|
|
9
9
|
var _maskStrings = require("./maskStrings");
|
|
10
|
-
const literal = `(?:${_maskStrings.placeholderSource.replace('(', '(?:')}
|
|
10
|
+
const literal = `(?:${_maskStrings.placeholderSource.replace('(', '(?:')}|${_parseLiteralValue.numberSource}|${_maskStrings.arraySource.replace('(', '(?:')}|null|true|false)`;
|
|
11
11
|
const setClauseMatch = new RegExp(`(set)\\s+([a-z0-9_-]+\\s*=\\s*${literal}(?:\\s*,\\s*[a-z0-9_-]+\\s*=\\s*${literal})*)`, 'i');
|
|
12
|
-
const assignmentMatch = new RegExp(`([a-z0-9_-]+)\\s*=\\s*(${_maskStrings.placeholderSource}|(
|
|
12
|
+
const assignmentMatch = new RegExp(`([a-z0-9_-]+)\\s*=\\s*(${_maskStrings.placeholderSource}|(${_parseLiteralValue.numberSource})|${_maskStrings.arraySource}|(null)|(true)|(false))`, 'ig');
|
|
13
13
|
/**
|
|
14
14
|
* Extract an update's "set field = value, field2 = value2" clause from the query. Uses a two-pass
|
|
15
15
|
* match, unlike the other parsers here: the set clause's own boundary is found first (so it can
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.findUpdateValues=void 0;var _removeFromQuery=require("./removeFromQuery"),_parseLiteralValue=require("./parseLiteralValue"),_maskStrings=require("./maskStrings");const literal=`(?:${_maskStrings.placeholderSource.replace("(","(?:")}
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.findUpdateValues=void 0;var _removeFromQuery=require("./removeFromQuery"),_parseLiteralValue=require("./parseLiteralValue"),_maskStrings=require("./maskStrings");const literal=`(?:${_maskStrings.placeholderSource.replace("(","(?:")}|${_parseLiteralValue.numberSource}|${_maskStrings.arraySource.replace("(","(?:")}|null|true|false)`,setClauseMatch=new RegExp(`(set)\\s+([a-z0-9_-]+\\s*=\\s*${literal}(?:\\s*,\\s*[a-z0-9_-]+\\s*=\\s*${literal})*)`,"i"),assignmentMatch=new RegExp(`([a-z0-9_-]+)\\s*=\\s*(${_maskStrings.placeholderSource}|(${_parseLiteralValue.numberSource})|${_maskStrings.arraySource}|(null)|(true)|(false))`,"ig"),findUpdateValues=(e,r)=>{const a=r.match(setClauseMatch);if(e.updateValues={},!a)return r;const s=a[2].matchAll(assignmentMatch);for(const r of s)e.updateValues[r[1]]=(0,_parseLiteralValue.parseLiteralValue)(r,3);return(0,_removeFromQuery.removeFromQuery)(a[0],r)};exports.findUpdateValues=findUpdateValues;
|
|
@@ -78,11 +78,15 @@ const unmaskStrings = (text, quote = 'json') => text.replace(placeholderMatch, (
|
|
|
78
78
|
exports.unmaskStrings = unmaskStrings;
|
|
79
79
|
const arrayJson = text => {
|
|
80
80
|
try {
|
|
81
|
-
// A number too big for a double reads as Infinity, which JSON would write back as null
|
|
81
|
+
// A number too big for a double reads as Infinity, which JSON would write back as null, and a whole number past
|
|
82
|
+
// Number.MAX_SAFE_INTEGER may already be rounded: refuse both, as parseNumber does outside arrays
|
|
82
83
|
return JSON.stringify(JSON.parse(unmaskStrings(text), (key, value) => {
|
|
83
84
|
if (typeof value === 'number' && !Number.isFinite(value)) {
|
|
84
85
|
throw new Error(`${value} is too large`);
|
|
85
86
|
}
|
|
87
|
+
if (Number.isInteger(value) && !Number.isSafeInteger(value)) {
|
|
88
|
+
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`);
|
|
89
|
+
}
|
|
86
90
|
return value;
|
|
87
91
|
}));
|
|
88
92
|
} catch (e) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.unmaskStrings=exports.placeholderSource=exports.maskStrings=exports.maskArrays=exports.fromPlaceholder=exports.arraySource=void 0,require("core-js/modules/es.json.stringify.js"),require("core-js/modules/esnext.iterator.constructor.js"),require("core-js/modules/esnext.iterator.map.js"),require("core-js/modules/esnext.iterator.some.js");var _scanQuery=require("./scanQuery");const placeholderSource=exports.placeholderSource="\\u0000([0-9a-f]*)\\u0000",placeholderMatch=new RegExp(placeholderSource,"g"),arraySource=exports.arraySource="\\u0001([0-9a-f]*)\\u0001",arrayMatch=new RegExp(arraySource,"g"),toHex=r=>r.split("").map((r=>r.charCodeAt(0).toString(16).padStart(4,"0"))).join(""),fromPlaceholder=r=>(r.match(/.{4}/g)||[]).map((r=>String.fromCharCode(parseInt(r,16)))).join("");exports.fromPlaceholder=fromPlaceholder;const maskStrings=r=>{const{strings:e}=(0,_scanQuery.assertClosed)((0,_scanQuery.scanQuery)(r));for(const[s,
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.unmaskStrings=exports.placeholderSource=exports.maskStrings=exports.maskArrays=exports.fromPlaceholder=exports.arraySource=void 0,require("core-js/modules/es.json.stringify.js"),require("core-js/modules/esnext.iterator.constructor.js"),require("core-js/modules/esnext.iterator.map.js"),require("core-js/modules/esnext.iterator.some.js");var _scanQuery=require("./scanQuery");const placeholderSource=exports.placeholderSource="\\u0000([0-9a-f]*)\\u0000",placeholderMatch=new RegExp(placeholderSource,"g"),arraySource=exports.arraySource="\\u0001([0-9a-f]*)\\u0001",arrayMatch=new RegExp(arraySource,"g"),toHex=r=>r.split("").map((r=>r.charCodeAt(0).toString(16).padStart(4,"0"))).join(""),fromPlaceholder=r=>(r.match(/.{4}/g)||[]).map((r=>String.fromCharCode(parseInt(r,16)))).join("");exports.fromPlaceholder=fromPlaceholder;const maskStrings=r=>{const{strings:e}=(0,_scanQuery.assertClosed)((0,_scanQuery.scanQuery)(r));for(const[s,t]of[["\0","NUL"],["","SOH"]])for(let o=r.indexOf(s);-1!==o;o=r.indexOf(s,o+1))if(!e.some((({start:r,end:e})=>r<o&&o<e)))throw new Error(`Unexpected ${t} character at position ${o}`);return e.reduceRight(((r,{start:e,end:s,value:t})=>`${r.slice(0,e)}\0${toHex(t)}\0${r.slice(s)}`),r)};exports.maskStrings=maskStrings;const unmaskStrings=(r,e="json")=>r.replace(placeholderMatch,((r,s)=>{const t=fromPlaceholder(s);return"json"===e?JSON.stringify(t):`'${t.replace(/\\/g,"\\\\").replace(/'/g,"\\'")}'`})).replace(arrayMatch,((r,e)=>fromPlaceholder(e)));exports.unmaskStrings=unmaskStrings;const arrayJson=r=>{try{return JSON.stringify(JSON.parse(unmaskStrings(r),((r,e)=>{if("number"==typeof e&&!Number.isFinite(e))throw new Error(`${e} is too large`);if(Number.isInteger(e)&&!Number.isSafeInteger(e))throw new Error(`${e} is too large to store exactly (the largest whole number is ${Number.MAX_SAFE_INTEGER}): quote it to keep it as text`);return e})))}catch(e){throw new Error(`Invalid array ${unmaskStrings(r,"'")}: ${e.message}`)}},maskArrays=r=>{let e="",s=0,t=0;for(let o=0;o<r.length;o++){const a=r[o];if("["===a)0===s&&(t=o),s++;else if("]"===a&&s>0&&(s--,0===s)){e+=`${toHex(arrayJson(r.slice(t,o+1)))}`;continue}0===s&&(e+=a)}return s>0?e+r.slice(t):e};exports.maskArrays=maskArrays;
|
|
@@ -28,11 +28,13 @@ require("core-js/modules/esnext.set.symmetric-difference.js");
|
|
|
28
28
|
require("core-js/modules/esnext.set.union.js");
|
|
29
29
|
var _typeCheck = require("../../introspect/policies/typeCheck");
|
|
30
30
|
var _maskStrings = require("./maskStrings");
|
|
31
|
+
var _parseLiteralValue = require("./parseLiteralValue");
|
|
31
32
|
var _startsQuery = require("./startsQuery");
|
|
32
33
|
const createMatch = exports.createMatch = /^\s*create(?![a-z0-9_-])/i;
|
|
33
34
|
const headMatch = /^\s*create\s+(if\s+not\s+exists\s+)?([a-z0-9_-]+)\s*\(/i;
|
|
34
35
|
const nameSource = '[a-z0-9_-]+';
|
|
35
36
|
const nameMatch = new RegExp(`^${nameSource}$`, 'i');
|
|
37
|
+
const numberMatch = new RegExp(`^${_parseLiteralValue.numberSource}$`, 'i');
|
|
36
38
|
const fieldMatch = new RegExp(`^(${nameSource})\\s+(${nameSource})((?:\\s[\\s\\S]*)?)$`, 'i');
|
|
37
39
|
const primaryMatch = /^primary\s+key\s*\(([^()]*)\)$/i;
|
|
38
40
|
const uniqueMatch = /^unique\s*\(([^()]*)\)$/i;
|
|
@@ -123,8 +125,8 @@ const parseDefault = token => {
|
|
|
123
125
|
if (array) {
|
|
124
126
|
return JSON.parse((0, _maskStrings.fromPlaceholder)(array[1]));
|
|
125
127
|
}
|
|
126
|
-
if (
|
|
127
|
-
return
|
|
128
|
+
if (numberMatch.test(token)) {
|
|
129
|
+
return (0, _parseLiteralValue.parseNumber)(token);
|
|
128
130
|
}
|
|
129
131
|
switch (token.toLowerCase()) {
|
|
130
132
|
case 'null':
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.parseCreate=exports.createMatch=void 0,require("core-js/modules/es.array.includes.js"),require("core-js/modules/esnext.iterator.constructor.js"),require("core-js/modules/esnext.iterator.filter.js"),require("core-js/modules/esnext.iterator.find.js"),require("core-js/modules/esnext.iterator.map.js"),require("core-js/modules/esnext.iterator.some.js"),require("core-js/modules/esnext.set.add-all.js"),require("core-js/modules/esnext.set.delete-all.js"),require("core-js/modules/esnext.set.difference.js"),require("core-js/modules/esnext.set.every.js"),require("core-js/modules/esnext.set.filter.js"),require("core-js/modules/esnext.set.find.js"),require("core-js/modules/esnext.set.intersection.js"),require("core-js/modules/esnext.set.is-disjoint-from.js"),require("core-js/modules/esnext.set.is-subset-of.js"),require("core-js/modules/esnext.set.is-superset-of.js"),require("core-js/modules/esnext.set.join.js"),require("core-js/modules/esnext.set.map.js"),require("core-js/modules/esnext.set.reduce.js"),require("core-js/modules/esnext.set.some.js"),require("core-js/modules/esnext.set.symmetric-difference.js"),require("core-js/modules/esnext.set.union.js");var _typeCheck=require("../../introspect/policies/typeCheck"),_maskStrings=require("./maskStrings"),_startsQuery=require("./startsQuery");const createMatch=exports.createMatch=/^\s*create(?![a-z0-9_-])/i,headMatch=/^\s*create\s+(if\s+not\s+exists\s+)?([a-z0-9_-]+)\s*\(/i,nameSource="[a-z0-9_-]+",nameMatch=new RegExp(`^${nameSource}$`,"i"),fieldMatch=new RegExp(`^(${nameSource})\\s+(${nameSource})((?:\\s[\\s\\S]*)?)$`,"i"),primaryMatch=/^primary\s+key\s*\(([^()]*)\)$/i,uniqueMatch=/^unique\s*\(([^()]*)\)$/i,indexMatch=/^index\s*\(([^()]*)\)$/i,foreignMatch=new RegExp(`^foreign\\s+key\\s*\\(([^()]*)\\)\\s*references\\s+(${nameSource})\\s*\\(([^()]*)\\)$`,"i"),stringLiteral=new RegExp(`^${_maskStrings.placeholderSource}$`),arrayLiteral=new RegExp(`^${_maskStrings.arraySource}$`),lookupPrefix={primary:"pk",unique:"unique",index:"index",foreign:"fk"},shown=e=>(0,_maskStrings.unmaskStrings)(e.trim(),"'"),findClose=(e,r)=>{let s=0;for(let t=r;t<e.length;t++)if("("===e[t])s++;else if(")"===e[t]&&(s--,0===s))return t;return-1},splitItems=e=>{const r=[];let s=0,t=0;for(let i=0;i<e.length;i++)"("===e[i]?s++:")"===e[i]?s--:","===e[i]&&0===s&&(r.push(e.slice(t,i)),t=i+1);return r.push(e.slice(t)),r.map((e=>e.trim()))},parseNames=(e,r)=>{const s=e.split(",").map((e=>e.trim()));if(s.some((e=>!nameMatch.test(e))))throw new Error(`Expected field names separated by commas in: ${shown(r)}`);if(new Set(s).size!==s.length)throw new Error(`The same field is named twice in: ${shown(r)}`);return s},parseDefault=e=>{if(void 0===e)throw new Error("Missing a value after default");const r=e.match(stringLiteral);if(r)return(0,_maskStrings.fromPlaceholder)(r[1]);const s=e.match(arrayLiteral);if(s)return JSON.parse((0,_maskStrings.fromPlaceholder)(s[1]));if(
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.parseCreate=exports.createMatch=void 0,require("core-js/modules/es.array.includes.js"),require("core-js/modules/esnext.iterator.constructor.js"),require("core-js/modules/esnext.iterator.filter.js"),require("core-js/modules/esnext.iterator.find.js"),require("core-js/modules/esnext.iterator.map.js"),require("core-js/modules/esnext.iterator.some.js"),require("core-js/modules/esnext.set.add-all.js"),require("core-js/modules/esnext.set.delete-all.js"),require("core-js/modules/esnext.set.difference.js"),require("core-js/modules/esnext.set.every.js"),require("core-js/modules/esnext.set.filter.js"),require("core-js/modules/esnext.set.find.js"),require("core-js/modules/esnext.set.intersection.js"),require("core-js/modules/esnext.set.is-disjoint-from.js"),require("core-js/modules/esnext.set.is-subset-of.js"),require("core-js/modules/esnext.set.is-superset-of.js"),require("core-js/modules/esnext.set.join.js"),require("core-js/modules/esnext.set.map.js"),require("core-js/modules/esnext.set.reduce.js"),require("core-js/modules/esnext.set.some.js"),require("core-js/modules/esnext.set.symmetric-difference.js"),require("core-js/modules/esnext.set.union.js");var _typeCheck=require("../../introspect/policies/typeCheck"),_maskStrings=require("./maskStrings"),_parseLiteralValue=require("./parseLiteralValue"),_startsQuery=require("./startsQuery");const createMatch=exports.createMatch=/^\s*create(?![a-z0-9_-])/i,headMatch=/^\s*create\s+(if\s+not\s+exists\s+)?([a-z0-9_-]+)\s*\(/i,nameSource="[a-z0-9_-]+",nameMatch=new RegExp(`^${nameSource}$`,"i"),numberMatch=new RegExp(`^${_parseLiteralValue.numberSource}$`,"i"),fieldMatch=new RegExp(`^(${nameSource})\\s+(${nameSource})((?:\\s[\\s\\S]*)?)$`,"i"),primaryMatch=/^primary\s+key\s*\(([^()]*)\)$/i,uniqueMatch=/^unique\s*\(([^()]*)\)$/i,indexMatch=/^index\s*\(([^()]*)\)$/i,foreignMatch=new RegExp(`^foreign\\s+key\\s*\\(([^()]*)\\)\\s*references\\s+(${nameSource})\\s*\\(([^()]*)\\)$`,"i"),stringLiteral=new RegExp(`^${_maskStrings.placeholderSource}$`),arrayLiteral=new RegExp(`^${_maskStrings.arraySource}$`),lookupPrefix={primary:"pk",unique:"unique",index:"index",foreign:"fk"},shown=e=>(0,_maskStrings.unmaskStrings)(e.trim(),"'"),findClose=(e,r)=>{let s=0;for(let t=r;t<e.length;t++)if("("===e[t])s++;else if(")"===e[t]&&(s--,0===s))return t;return-1},splitItems=e=>{const r=[];let s=0,t=0;for(let i=0;i<e.length;i++)"("===e[i]?s++:")"===e[i]?s--:","===e[i]&&0===s&&(r.push(e.slice(t,i)),t=i+1);return r.push(e.slice(t)),r.map((e=>e.trim()))},parseNames=(e,r)=>{const s=e.split(",").map((e=>e.trim()));if(s.some((e=>!nameMatch.test(e))))throw new Error(`Expected field names separated by commas in: ${shown(r)}`);if(new Set(s).size!==s.length)throw new Error(`The same field is named twice in: ${shown(r)}`);return s},parseDefault=e=>{if(void 0===e)throw new Error("Missing a value after default");const r=e.match(stringLiteral);if(r)return(0,_maskStrings.fromPlaceholder)(r[1]);const s=e.match(arrayLiteral);if(s)return JSON.parse((0,_maskStrings.fromPlaceholder)(s[1]));if(numberMatch.test(e))return(0,_parseLiteralValue.parseNumber)(e);switch(e.toLowerCase()){case"null":return null;case"true":return!0;case"false":return!1}throw new Error(`Unrecognized default value: ${shown(e)}`)},parseField=e=>{const[r,s,t,i]=e,n=t.toLowerCase();if(!_typeCheck.fieldTypes.includes(n))throw new Error(`Unknown type "${t}" for field "${s}" (expected ${_typeCheck.fieldTypes.join(", ")})`);const o={name:s,type:n,optional:!1,autoGenerate:!1},a=new Set,l=e=>{if(a.has(e))throw new Error(`"${e}" is given twice for field "${s}"`);a.add(e)},u=""===i.trim()?[]:i.trim().split(/\s+/);for(let e=0;e<u.length;e++){const t=u[e].toLowerCase();if("optional"===t)l("optional"),o.optional=!0;else if("auto"===t)l("auto"),o.autoGenerate=!0;else if("null"===t)l("null"),o.nullable=!0;else if("not"===t&&"null"===(u[e+1]||"").toLowerCase())l("null"),o.nullable=!1,e++;else{if("default"!==t)throw new Error(`Unrecognized "${shown(u[e])}" for field "${s}" in: ${shown(r)}`);l("default"),o.useDefault=!0,o.defaultValue=parseDefault(u[++e])}}if(o.autoGenerate&&o.useDefault)throw new Error(`Field "${s}" can't be both auto and have a default`);if(o.autoGenerate&&"string"!==n)throw new Error(`Field "${s}" is auto, which generates a UUID, so its type must be string`);return o},parseKey=e=>{let r=e.match(primaryMatch);if(r)return{type:"primary",fields:parseNames(r[1],e)};if(r=e.match(uniqueMatch),r)return{type:"unique",fields:parseNames(r[1],e)};if(r=e.match(indexMatch),r)return{type:"index",fields:parseNames(r[1],e)};if(r=e.match(foreignMatch),r){const s=parseNames(r[1],e),t=parseNames(r[3],e);if(1!==s.length||1!==t.length)throw new Error(`Only single-field foreign keys are supported so far: ${shown(e)}`);return{type:"foreign",fields:s,references:[{fields:[`${r[2]}.${t[0]}`],lookup:`${r[2]}.json`}]}}return null},finishKeys=(e,r)=>{const s=e.map((e=>e.name));if(r.filter((e=>"primary"===e.type)).length>1)throw new Error("Only one primary key is allowed");const t=new Set;return r.map((e=>{const r=e.fields.find((e=>!s.includes(e)));if(r)throw new Error(`The ${e.type} key (${e.fields.join(", ")}) names "${r}", which is not a field`);const i=`${lookupPrefix[e.type]}_${e.fields.join("_")}.json`;if(t.has(i))throw new Error(`The same ${e.type} key on "${e.fields.join(", ")}" is given twice`);return t.add(i),Object.assign({type:e.type,fields:e.fields,lookup:i},e.references?{references:e.references}:{})}))},parseCreate=e=>{const r=e.match(headMatch);if(!r)throw new Error("Expected: create [if not exists] <name> (<fields and keys>)");const s=r[2];if("__RECORDS"===s)throw new Error("__RECORDS is reserved for the list of records");const t=r[0].length-1,i=findClose(e,t);if(-1===i)throw new Error("Missing ) at the end of the field list");const n=e.slice(i+1).trim();if(n){if(_startsQuery.startsQueryMatch.test(n))throw new Error(`Missing ; before: ${shown(n)}`);throw new Error(`Unrecognized query value: ${shown(n)}`)}const o=e.slice(t+1,i),a=[],l=[];if(o.trim())for(const e of splitItems(o)){if(!e)throw new Error("Empty item in the field list (an extra comma?)");const r=parseKey(e);if(r){l.push(r);continue}const s=e.match(fieldMatch);if(!s)throw new Error(`Expected a field (<name> <type> ...) or a key in: ${shown(e)}`);const t=parseField(s);if(a.some((e=>e.name===t.name)))throw new Error(`Field "${t.name}" is given twice`);a.push(t)}return{entity:s,create:{ifNotExists:Boolean(r[1]),definition:a,keys:finishKeys(a,l)}}};exports.parseCreate=parseCreate;
|
|
@@ -1,4 +1,20 @@
|
|
|
1
1
|
export type LiteralValue = string | number | boolean | null | any[];
|
|
2
|
+
/**
|
|
3
|
+
* Regex source matching one number literal: an optional `-`, digits, an optional fraction and an optional exponent
|
|
4
|
+
* (`12`, `-3`, `1.5`, `1e3`, `2.5E-4`), with no group of its own. Anything left after it (`5abc`, `1.5.2`) is an
|
|
5
|
+
* unrecognized query value, as for any other leftover text.
|
|
6
|
+
*/
|
|
7
|
+
export declare const numberSource = "-?[0-9]+(?:\\.[0-9]+)?(?:e[+-]?[0-9]+)?";
|
|
8
|
+
/**
|
|
9
|
+
* Read one number literal's text (see numberSource) as a number, refusing any it can't hold exactly as written: a
|
|
10
|
+
* whole number past Number.MAX_SAFE_INTEGER, however it is written (`9007199254740993`, `9007199254740993.0`, `1e20`),
|
|
11
|
+
* may be stored rounded (9007199254740993 as 9007199254740992), and a number too big for a double reads as Infinity,
|
|
12
|
+
* which JSON writes back as null. Fractions are kept as the nearest double, as JSON does (an exact decimal type is
|
|
13
|
+
* planned).
|
|
14
|
+
* @param text
|
|
15
|
+
* @throws {Error} For an integer past Number.MAX_SAFE_INTEGER, or a number too big for a double.
|
|
16
|
+
*/
|
|
17
|
+
export declare const parseNumber: (text: string) => number;
|
|
2
18
|
/**
|
|
3
19
|
* Several parsers (findConditions, findInsertValues, findUpdateValues) capture a query-string
|
|
4
20
|
* literal into one of six consecutive alternation groups depending on its shape - quoted string
|
|
@@ -3,8 +3,33 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.parseLiteralValue = void 0;
|
|
6
|
+
exports.parseNumber = exports.parseLiteralValue = exports.numberSource = void 0;
|
|
7
7
|
var _maskStrings = require("./maskStrings");
|
|
8
|
+
/**
|
|
9
|
+
* Regex source matching one number literal: an optional `-`, digits, an optional fraction and an optional exponent
|
|
10
|
+
* (`12`, `-3`, `1.5`, `1e3`, `2.5E-4`), with no group of its own. Anything left after it (`5abc`, `1.5.2`) is an
|
|
11
|
+
* unrecognized query value, as for any other leftover text.
|
|
12
|
+
*/
|
|
13
|
+
const numberSource = exports.numberSource = '-?[0-9]+(?:\\.[0-9]+)?(?:e[+-]?[0-9]+)?';
|
|
14
|
+
/**
|
|
15
|
+
* Read one number literal's text (see numberSource) as a number, refusing any it can't hold exactly as written: a
|
|
16
|
+
* whole number past Number.MAX_SAFE_INTEGER, however it is written (`9007199254740993`, `9007199254740993.0`, `1e20`),
|
|
17
|
+
* may be stored rounded (9007199254740993 as 9007199254740992), and a number too big for a double reads as Infinity,
|
|
18
|
+
* which JSON writes back as null. Fractions are kept as the nearest double, as JSON does (an exact decimal type is
|
|
19
|
+
* planned).
|
|
20
|
+
* @param text
|
|
21
|
+
* @throws {Error} For an integer past Number.MAX_SAFE_INTEGER, or a number too big for a double.
|
|
22
|
+
*/
|
|
23
|
+
const parseNumber = text => {
|
|
24
|
+
const value = Number(text);
|
|
25
|
+
if (!Number.isFinite(value)) {
|
|
26
|
+
throw new Error(`${text} is too large`);
|
|
27
|
+
}
|
|
28
|
+
if (Number.isInteger(value) && !Number.isSafeInteger(value)) {
|
|
29
|
+
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`);
|
|
30
|
+
}
|
|
31
|
+
return value;
|
|
32
|
+
};
|
|
8
33
|
/**
|
|
9
34
|
* Several parsers (findConditions, findInsertValues, findUpdateValues) capture a query-string
|
|
10
35
|
* literal into one of six consecutive alternation groups depending on its shape - quoted string
|
|
@@ -15,12 +40,13 @@ var _maskStrings = require("./maskStrings");
|
|
|
15
40
|
* @param match
|
|
16
41
|
* @param baseIndex
|
|
17
42
|
*/
|
|
43
|
+
exports.parseNumber = parseNumber;
|
|
18
44
|
const parseLiteralValue = (match, baseIndex) => {
|
|
19
45
|
if (typeof match[baseIndex] !== 'undefined') {
|
|
20
46
|
return (0, _maskStrings.fromPlaceholder)(match[baseIndex]);
|
|
21
47
|
}
|
|
22
48
|
if (typeof match[baseIndex + 1] !== 'undefined') {
|
|
23
|
-
return
|
|
49
|
+
return parseNumber(match[baseIndex + 1]);
|
|
24
50
|
}
|
|
25
51
|
if (typeof match[baseIndex + 2] !== 'undefined') {
|
|
26
52
|
// An array is a placeholder from maskArrays, holding its JSON
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.parseLiteralValue=void 0;var _maskStrings=require("./maskStrings");const parseLiteralValue=(e,r)=>void 0!==e[r]?(0,_maskStrings.fromPlaceholder)(e[r]):void 0!==e[r+1]?
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.parseNumber=exports.parseLiteralValue=exports.numberSource=void 0;var _maskStrings=require("./maskStrings");const numberSource=exports.numberSource="-?[0-9]+(?:\\.[0-9]+)?(?:e[+-]?[0-9]+)?",parseNumber=e=>{const r=Number(e);if(!Number.isFinite(r))throw new Error(`${e} is too large`);if(Number.isInteger(r)&&!Number.isSafeInteger(r))throw new Error(`${e} is too large to store exactly (the largest whole number is ${Number.MAX_SAFE_INTEGER}): quote it to keep it as text`);return r};exports.parseNumber=parseNumber;const parseLiteralValue=(e,r)=>void 0!==e[r]?(0,_maskStrings.fromPlaceholder)(e[r]):void 0!==e[r+1]?parseNumber(e[r+1]):void 0!==e[r+2]?JSON.parse((0,_maskStrings.fromPlaceholder)(e[r+2])):void 0!==e[r+3]?null:void 0!==e[r+4];exports.parseLiteralValue=parseLiteralValue;
|