json-fs-query 2.3.1 → 3.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 +79 -7
- package/browser/jsonFsQuery.js +12650 -13107
- package/browser/jsonFsQuery.min.js +1 -1
- package/dist/data/introspect/create.js +30 -1
- package/dist/data/introspect/create.min.js +1 -1
- package/dist/data/queries/groupBy.d.ts +20 -1
- package/dist/data/queries/groupBy.js +71 -9
- package/dist/data/queries/groupBy.min.js +1 -1
- package/dist/data/queries/limitOffset.d.ts +1 -1
- package/dist/data/queries/limitOffset.js +1 -1
- package/dist/data/queries/selectFields.d.ts +6 -4
- package/dist/data/queries/selectFields.js +15 -10
- package/dist/data/queries/selectFields.min.js +1 -1
- package/dist/data/utilities/databaseLock.js +2 -1
- package/dist/data/utilities/databaseLock.min.js +1 -1
- package/dist/data/utilities/parsers/findCommand.d.ts +2 -1
- package/dist/data/utilities/parsers/findCommand.js +4 -4
- package/dist/data/utilities/parsers/findCommand.min.js +1 -1
- package/dist/data/utilities/parsers/findConditions.d.ts +8 -9
- package/dist/data/utilities/parsers/findConditions.js +29 -41
- package/dist/data/utilities/parsers/findConditions.min.js +1 -1
- package/dist/data/utilities/parsers/findGroupBy.d.ts +6 -4
- package/dist/data/utilities/parsers/findGroupBy.js +17 -9
- package/dist/data/utilities/parsers/findGroupBy.min.js +1 -1
- package/dist/data/utilities/parsers/findInsertValues.d.ts +4 -3
- package/dist/data/utilities/parsers/findInsertValues.js +9 -9
- package/dist/data/utilities/parsers/findInsertValues.min.js +1 -1
- package/dist/data/utilities/parsers/findJoinClauses.d.ts +4 -3
- package/dist/data/utilities/parsers/findJoinClauses.js +20 -13
- package/dist/data/utilities/parsers/findJoinClauses.min.js +1 -1
- package/dist/data/utilities/parsers/findJoinEntity.d.ts +1 -1
- package/dist/data/utilities/parsers/findJoinEntity.js +3 -5
- package/dist/data/utilities/parsers/findJoinEntity.min.js +1 -1
- package/dist/data/utilities/parsers/findLimit.d.ts +4 -3
- package/dist/data/utilities/parsers/findLimit.js +8 -8
- package/dist/data/utilities/parsers/findLimit.min.js +1 -1
- package/dist/data/utilities/parsers/findMergeJoins.d.ts +4 -3
- package/dist/data/utilities/parsers/findMergeJoins.js +19 -12
- package/dist/data/utilities/parsers/findMergeJoins.min.js +1 -1
- package/dist/data/utilities/parsers/findOffset.d.ts +4 -3
- package/dist/data/utilities/parsers/findOffset.js +8 -8
- package/dist/data/utilities/parsers/findOffset.min.js +1 -1
- package/dist/data/utilities/parsers/findSelects.d.ts +4 -3
- package/dist/data/utilities/parsers/findSelects.js +13 -14
- package/dist/data/utilities/parsers/findSelects.min.js +1 -1
- package/dist/data/utilities/parsers/findSortClauses.d.ts +4 -3
- package/dist/data/utilities/parsers/findSortClauses.js +12 -12
- package/dist/data/utilities/parsers/findSortClauses.min.js +1 -1
- package/dist/data/utilities/parsers/findUpdateValues.d.ts +5 -7
- package/dist/data/utilities/parsers/findUpdateValues.js +10 -13
- package/dist/data/utilities/parsers/findUpdateValues.min.js +1 -1
- package/dist/data/utilities/parsers/parseQuery.js +83 -9
- package/dist/data/utilities/parsers/parseQuery.min.js +1 -1
- package/dist/data/utilities/parsers/splitClauses.d.ts +29 -0
- package/dist/data/utilities/parsers/splitClauses.js +139 -0
- package/dist/data/utilities/parsers/splitClauses.min.js +1 -0
- package/dist/data/utilities/retrieveFile.js +2 -1
- package/dist/data/utilities/retrieveFile.min.js +1 -1
- package/dist/data/utilities/runQuery.js +5 -4
- package/dist/data/utilities/runQuery.min.js +1 -1
- package/package.json +5 -3
- package/dist/data/utilities/parsers/removeFromQuery.d.ts +0 -2
- package/dist/data/utilities/parsers/removeFromQuery.js +0 -8
- package/dist/data/utilities/parsers/removeFromQuery.min.js +0 -1
|
@@ -4,13 +4,13 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.parseQuery = void 0;
|
|
7
|
-
var _siFunciona = _interopRequireDefault(require("si-funciona"));
|
|
8
7
|
var _findCommand = require("./findCommand");
|
|
9
8
|
var _findInsertValues = require("./findInsertValues");
|
|
10
9
|
var _findUpdateValues = require("./findUpdateValues");
|
|
11
10
|
var _findJoinEntity = require("./findJoinEntity");
|
|
12
11
|
var _findSelects = require("./findSelects");
|
|
13
12
|
var _findConditions = require("./findConditions");
|
|
13
|
+
var _conditionTree = require("../conditionTree");
|
|
14
14
|
var _findJoinClauses = require("./findJoinClauses");
|
|
15
15
|
var _findMergeJoins = require("./findMergeJoins");
|
|
16
16
|
var _findSortClauses = require("./findSortClauses");
|
|
@@ -20,8 +20,63 @@ var _findOffset = require("./findOffset");
|
|
|
20
20
|
var _maskStrings = require("./maskStrings");
|
|
21
21
|
var _parseCreate = require("./parseCreate");
|
|
22
22
|
var _parseSchemaStatement = require("./parseSchemaStatement");
|
|
23
|
+
var _splitClauses = require("./splitClauses");
|
|
23
24
|
var _startsQuery = require("./startsQuery");
|
|
24
|
-
|
|
25
|
+
// Each clause's own step, and what its body must start with (for the error when it doesn't)
|
|
26
|
+
const clauseSteps = {
|
|
27
|
+
values: {
|
|
28
|
+
step: _findInsertValues.findInsertValues,
|
|
29
|
+
expected: 'an assignment (field = value)'
|
|
30
|
+
},
|
|
31
|
+
set: {
|
|
32
|
+
step: _findUpdateValues.findUpdateValues,
|
|
33
|
+
expected: 'an assignment (field = value)'
|
|
34
|
+
},
|
|
35
|
+
select: {
|
|
36
|
+
step: _findSelects.findSelects,
|
|
37
|
+
expected: 'a field'
|
|
38
|
+
},
|
|
39
|
+
on: {
|
|
40
|
+
step: _findJoinClauses.findJoinClauses,
|
|
41
|
+
expected: 'a join condition (a.x = b.y)'
|
|
42
|
+
},
|
|
43
|
+
merge: {
|
|
44
|
+
step: _findMergeJoins.findMergeJoins,
|
|
45
|
+
expected: 'a merge (a.x with b.y)'
|
|
46
|
+
},
|
|
47
|
+
where: {
|
|
48
|
+
step: _findConditions.findConditions,
|
|
49
|
+
expected: 'a condition'
|
|
50
|
+
},
|
|
51
|
+
'group by': {
|
|
52
|
+
step: _findGroupBy.findGroupBy,
|
|
53
|
+
expected: 'a field'
|
|
54
|
+
},
|
|
55
|
+
sort: {
|
|
56
|
+
step: _findSortClauses.findSortClauses,
|
|
57
|
+
expected: 'a field'
|
|
58
|
+
},
|
|
59
|
+
limit: {
|
|
60
|
+
step: _findLimit.findLimit,
|
|
61
|
+
expected: 'a whole number'
|
|
62
|
+
},
|
|
63
|
+
offset: {
|
|
64
|
+
step: _findOffset.findOffset,
|
|
65
|
+
expected: 'a whole number'
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
/**
|
|
69
|
+
* The error for text a step couldn't read: another query run on without a `;`, or text that doesn't belong.
|
|
70
|
+
* @param rest The text left over, masked
|
|
71
|
+
* @param after What was read before it, masked
|
|
72
|
+
*/
|
|
73
|
+
const leftoverError = (rest, after) => {
|
|
74
|
+
const shown = text => (0, _maskStrings.unmaskStrings)(text, '\'');
|
|
75
|
+
if (_startsQuery.startsQueryMatch.test(rest)) {
|
|
76
|
+
return new Error(`Missing ; before: ${shown(rest)}`);
|
|
77
|
+
}
|
|
78
|
+
return new Error(`Unexpected "${shown(rest)}" after "${shown(after)}"`);
|
|
79
|
+
};
|
|
25
80
|
/**
|
|
26
81
|
* Break a query down into its components.
|
|
27
82
|
* @param query
|
|
@@ -44,10 +99,6 @@ const parseQuery = query => {
|
|
|
44
99
|
create: undefined,
|
|
45
100
|
ifExists: undefined
|
|
46
101
|
};
|
|
47
|
-
const {
|
|
48
|
-
curry,
|
|
49
|
-
pipe
|
|
50
|
-
} = _siFunciona.default;
|
|
51
102
|
// No find* step may see inside a string or an array: each one becomes a placeholder holding its value
|
|
52
103
|
query = (0, _maskStrings.maskArrays)((0, _maskStrings.maskStrings)(query));
|
|
53
104
|
// A create statement has its own shape (a bracketed list of fields and keys), so it gets its own parser
|
|
@@ -65,13 +116,36 @@ const parseQuery = query => {
|
|
|
65
116
|
if (_parseSchemaStatement.schemaStatementMatch.test(query)) {
|
|
66
117
|
return Object.assign(parsedQuery, (0, _parseSchemaStatement.parseSchemaStatement)(query));
|
|
67
118
|
}
|
|
68
|
-
|
|
69
|
-
|
|
119
|
+
// Clauses come at most once each, in a fixed order (see clauseOrder); each step reads only its own clause's body
|
|
120
|
+
const split = (0, _splitClauses.splitClauses)(query);
|
|
121
|
+
if (!split) {
|
|
122
|
+
query = query.trim();
|
|
70
123
|
if (_startsQuery.startsQueryMatch.test(query)) {
|
|
71
|
-
throw new Error(`Missing ; before: ${(0, _maskStrings.unmaskStrings)(query
|
|
124
|
+
throw new Error(`Missing ; before: ${(0, _maskStrings.unmaskStrings)(query, '\'')}`);
|
|
72
125
|
}
|
|
73
126
|
throw new Error(`Unrecognized query value: ${(0, _maskStrings.unmaskStrings)(query, '\'')}`);
|
|
74
127
|
}
|
|
128
|
+
(0, _findJoinEntity.findJoinEntity)(parsedQuery, (0, _findCommand.findCommand)(parsedQuery, split.head));
|
|
129
|
+
parsedQuery.where = (0, _conditionTree.allOf)([]);
|
|
130
|
+
if (split.leading) {
|
|
131
|
+
throw leftoverError(split.leading, split.head);
|
|
132
|
+
}
|
|
133
|
+
for (const {
|
|
134
|
+
keyword,
|
|
135
|
+
body
|
|
136
|
+
} of split.clauses) {
|
|
137
|
+
const {
|
|
138
|
+
step,
|
|
139
|
+
expected
|
|
140
|
+
} = clauseSteps[keyword];
|
|
141
|
+
const rest = step(parsedQuery, body);
|
|
142
|
+
if (rest === body) {
|
|
143
|
+
throw new Error(`Missing ${expected} after ${keyword}${body ? `: ${(0, _maskStrings.unmaskStrings)(body, '\'')}` : ''}`);
|
|
144
|
+
}
|
|
145
|
+
if (rest) {
|
|
146
|
+
throw leftoverError(rest, `${keyword} ${body.slice(0, body.length - rest.length).trim()}`);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
75
149
|
return parsedQuery;
|
|
76
150
|
};
|
|
77
151
|
exports.parseQuery = parseQuery;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.parseQuery=void 0;var
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.parseQuery=void 0;var _findCommand=require("./findCommand"),_findInsertValues=require("./findInsertValues"),_findUpdateValues=require("./findUpdateValues"),_findJoinEntity=require("./findJoinEntity"),_findSelects=require("./findSelects"),_findConditions=require("./findConditions"),_conditionTree=require("../conditionTree"),_findJoinClauses=require("./findJoinClauses"),_findMergeJoins=require("./findMergeJoins"),_findSortClauses=require("./findSortClauses"),_findGroupBy=require("./findGroupBy"),_findLimit=require("./findLimit"),_findOffset=require("./findOffset"),_maskStrings=require("./maskStrings"),_parseCreate=require("./parseCreate"),_parseSchemaStatement=require("./parseSchemaStatement"),_splitClauses=require("./splitClauses"),_startsQuery=require("./startsQuery");const clauseSteps={values:{step:_findInsertValues.findInsertValues,expected:"an assignment (field = value)"},set:{step:_findUpdateValues.findUpdateValues,expected:"an assignment (field = value)"},select:{step:_findSelects.findSelects,expected:"a field"},on:{step:_findJoinClauses.findJoinClauses,expected:"a join condition (a.x = b.y)"},merge:{step:_findMergeJoins.findMergeJoins,expected:"a merge (a.x with b.y)"},where:{step:_findConditions.findConditions,expected:"a condition"},"group by":{step:_findGroupBy.findGroupBy,expected:"a field"},sort:{step:_findSortClauses.findSortClauses,expected:"a field"},limit:{step:_findLimit.findLimit,expected:"a whole number"},offset:{step:_findOffset.findOffset,expected:"a whole number"}},leftoverError=(e,t)=>{const i=e=>(0,_maskStrings.unmaskStrings)(e,"'");return _startsQuery.startsQueryMatch.test(e)?new Error(`Missing ; before: ${i(e)}`):new Error(`Unexpected "${i(e)}" after "${i(t)}"`)},parseQuery=e=>{const t={command:void 0,entity:void 0,joinEntity:void 0,selectClauses:[],insertValues:[],updateValues:{},where:void 0,joinClauses:[],mergeJoins:[],sortClauses:[],groupBy:void 0,limit:void 0,offset:void 0,create:void 0,ifExists:void 0};if(e=(0,_maskStrings.maskArrays)((0,_maskStrings.maskStrings)(e)),_parseCreate.createMatch.test(e)){const{entity:i,create:r}=(0,_parseCreate.parseCreate)(e);return Object.assign(t,{command:"create",entity:i,create:r})}if(_parseSchemaStatement.schemaStatementMatch.test(e))return Object.assign(t,(0,_parseSchemaStatement.parseSchemaStatement)(e));const i=(0,_splitClauses.splitClauses)(e);if(!i){if(e=e.trim(),_startsQuery.startsQueryMatch.test(e))throw new Error(`Missing ; before: ${(0,_maskStrings.unmaskStrings)(e,"'")}`);throw new Error(`Unrecognized query value: ${(0,_maskStrings.unmaskStrings)(e,"'")}`)}if((0,_findJoinEntity.findJoinEntity)(t,(0,_findCommand.findCommand)(t,i.head)),t.where=(0,_conditionTree.allOf)([]),i.leading)throw leftoverError(i.leading,i.head);for(const{keyword:e,body:r}of i.clauses){const{step:i,expected:s}=clauseSteps[e],n=i(t,r);if(n===r)throw new Error(`Missing ${s} after ${e}${r?`: ${(0,_maskStrings.unmaskStrings)(r,"'")}`:""}`);if(n)throw leftoverError(n,`${e} ${r.slice(0,r.length-n.length).trim()}`)}return t};exports.parseQuery=parseQuery;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The clauses each command allows, in the order they must come. create, drop, describe and show have their own
|
|
3
|
+
* parsers and never get here.
|
|
4
|
+
*/
|
|
5
|
+
export declare const clauseOrder: {
|
|
6
|
+
[command: string]: string[];
|
|
7
|
+
};
|
|
8
|
+
export declare const whereHint = "use and / or inside one where clause (where a = 1 and b = 2)";
|
|
9
|
+
export type SplitClause = {
|
|
10
|
+
keyword: string;
|
|
11
|
+
body: string;
|
|
12
|
+
};
|
|
13
|
+
export type SplitQuery = {
|
|
14
|
+
command: string;
|
|
15
|
+
head: string;
|
|
16
|
+
leading: string;
|
|
17
|
+
clauses: SplitClause[];
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Split a masked query (see maskStrings) into its clauses, reading its top-level words once, left to right. A clause
|
|
21
|
+
* word counts only as a whole word outside brackets, and not where it must be a name: as a clause's first word
|
|
22
|
+
* (`sort limit`), after a comma or `as`, or before a comparator (`where limit = 1`).
|
|
23
|
+
* @param query
|
|
24
|
+
* @returns undefined when the query doesn't start with read, insert, update or delete (left to the find* steps).
|
|
25
|
+
* @throws {Error} For a joined record outside read, a clause the command doesn't allow, a repeated clause, a clause out
|
|
26
|
+
* of order, or the 2.x forms of a second where (`and where`, `or where`, or a plain repeat).
|
|
27
|
+
*/
|
|
28
|
+
export declare const splitClauses: (query: string) => SplitQuery | undefined;
|
|
29
|
+
//# sourceMappingURL=splitClauses.d.ts.map
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.whereHint = exports.splitClauses = exports.clauseOrder = void 0;
|
|
7
|
+
require("core-js/modules/es.array.includes.js");
|
|
8
|
+
require("core-js/modules/esnext.set.add-all.js");
|
|
9
|
+
require("core-js/modules/esnext.set.delete-all.js");
|
|
10
|
+
require("core-js/modules/esnext.set.difference.js");
|
|
11
|
+
require("core-js/modules/esnext.set.every.js");
|
|
12
|
+
require("core-js/modules/esnext.set.filter.js");
|
|
13
|
+
require("core-js/modules/esnext.set.find.js");
|
|
14
|
+
require("core-js/modules/esnext.set.intersection.js");
|
|
15
|
+
require("core-js/modules/esnext.set.is-disjoint-from.js");
|
|
16
|
+
require("core-js/modules/esnext.set.is-subset-of.js");
|
|
17
|
+
require("core-js/modules/esnext.set.is-superset-of.js");
|
|
18
|
+
require("core-js/modules/esnext.set.join.js");
|
|
19
|
+
require("core-js/modules/esnext.set.map.js");
|
|
20
|
+
require("core-js/modules/esnext.set.reduce.js");
|
|
21
|
+
require("core-js/modules/esnext.set.some.js");
|
|
22
|
+
require("core-js/modules/esnext.set.symmetric-difference.js");
|
|
23
|
+
require("core-js/modules/esnext.set.union.js");
|
|
24
|
+
var _maskStrings = require("./maskStrings");
|
|
25
|
+
/**
|
|
26
|
+
* The clauses each command allows, in the order they must come. create, drop, describe and show have their own
|
|
27
|
+
* parsers and never get here.
|
|
28
|
+
*/
|
|
29
|
+
const clauseOrder = exports.clauseOrder = {
|
|
30
|
+
read: ['select', 'on', 'merge', 'where', 'group by', 'sort', 'limit', 'offset'],
|
|
31
|
+
insert: ['values'],
|
|
32
|
+
update: ['set', 'where'],
|
|
33
|
+
delete: ['where']
|
|
34
|
+
};
|
|
35
|
+
const clauseWords = [...new Set(Object.values(clauseOrder).flat())];
|
|
36
|
+
// The command, the record name and an optional joined record (`read workouts.exercises`, or `read workouts .exercises`)
|
|
37
|
+
const headMatch = /^\s*(delete|insert|read|update)\s+([a-z0-9_-]+(\s*\.[a-z0-9_-]+)?)/i;
|
|
38
|
+
// Placeholders (see maskStrings) as one token each, so their hex can't read as a word; words; then the two-character
|
|
39
|
+
// comparators before any other single character
|
|
40
|
+
const tokenMatch = new RegExp(`${_maskStrings.placeholderSource}|${_maskStrings.arraySource}|[a-z0-9_.-]+|<>|!=|>=|<=|\\S`, 'gi');
|
|
41
|
+
// What can follow a field in a condition: a clause word followed by one of these is a field (`where limit = 1`)
|
|
42
|
+
const conditionWords = ['=', '!=', '<>', '>', '<', '>=', '<=', 'in', 'between', 'like', 'is', 'exists', 'does'];
|
|
43
|
+
// `and on` carries on the on clause instead of starting another; so does a merge straight after another
|
|
44
|
+
// (`merge a.x with b.y merge c.z with d.w`, with or without `and`)
|
|
45
|
+
const continues = {
|
|
46
|
+
on: previous => previous === 'and',
|
|
47
|
+
merge: () => true
|
|
48
|
+
};
|
|
49
|
+
// What a second where clause should have been: its conditions joined with and / or inside the first
|
|
50
|
+
const whereHint = exports.whereHint = 'use and / or inside one where clause (where a = 1 and b = 2)';
|
|
51
|
+
/**
|
|
52
|
+
* Split a masked query (see maskStrings) into its clauses, reading its top-level words once, left to right. A clause
|
|
53
|
+
* word counts only as a whole word outside brackets, and not where it must be a name: as a clause's first word
|
|
54
|
+
* (`sort limit`), after a comma or `as`, or before a comparator (`where limit = 1`).
|
|
55
|
+
* @param query
|
|
56
|
+
* @returns undefined when the query doesn't start with read, insert, update or delete (left to the find* steps).
|
|
57
|
+
* @throws {Error} For a joined record outside read, a clause the command doesn't allow, a repeated clause, a clause out
|
|
58
|
+
* of order, or the 2.x forms of a second where (`and where`, `or where`, or a plain repeat).
|
|
59
|
+
*/
|
|
60
|
+
const splitClauses = query => {
|
|
61
|
+
const head = query.match(headMatch);
|
|
62
|
+
if (!head) {
|
|
63
|
+
return undefined;
|
|
64
|
+
}
|
|
65
|
+
const command = head[1].toLowerCase();
|
|
66
|
+
const allowed = clauseOrder[command];
|
|
67
|
+
const article = /^[aeiou]/.test(command) ? 'an' : 'a';
|
|
68
|
+
if (head[3] && command !== 'read') {
|
|
69
|
+
throw new Error(`A joined record (${head[2]}) isn't allowed in ${article} ${command} query`);
|
|
70
|
+
}
|
|
71
|
+
const tokens = [...query.slice(head[0].length).matchAll(tokenMatch)].map(({
|
|
72
|
+
0: text,
|
|
73
|
+
index
|
|
74
|
+
}) => ({
|
|
75
|
+
text,
|
|
76
|
+
lower: text.toLowerCase(),
|
|
77
|
+
start: head[0].length + index
|
|
78
|
+
}));
|
|
79
|
+
const found = [];
|
|
80
|
+
let depth = 0;
|
|
81
|
+
let bodyStarts = false;
|
|
82
|
+
for (let index = 0; index < tokens.length; index++) {
|
|
83
|
+
const {
|
|
84
|
+
lower,
|
|
85
|
+
start
|
|
86
|
+
} = tokens[index];
|
|
87
|
+
const previous = tokens[index - 1]?.lower;
|
|
88
|
+
const next = tokens[index + 1]?.lower;
|
|
89
|
+
if (lower === '(' || lower === ')') {
|
|
90
|
+
depth += lower === '(' ? 1 : -1;
|
|
91
|
+
}
|
|
92
|
+
const keyword = lower === 'group' && next === 'by' ? 'group by' : lower;
|
|
93
|
+
const isName = bodyStarts || previous === ',' || previous === 'as' || conditionWords.includes(next);
|
|
94
|
+
bodyStarts = false;
|
|
95
|
+
if (depth > 0 || keyword === 'by' || !clauseWords.includes(keyword) || isName) {
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
const current = found[found.length - 1]?.keyword;
|
|
99
|
+
if (current === keyword && continues[keyword]?.(previous)) {
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
// 2.x read `and where` / `or where` as `and` / `or`, and a second where as `and`
|
|
103
|
+
const hasWhere = found.some(clause => clause.keyword === 'where');
|
|
104
|
+
if (keyword === 'where' && hasWhere && (previous === 'and' || previous === 'or')) {
|
|
105
|
+
throw new Error(`"${previous} where" is no longer supported: ${whereHint}`);
|
|
106
|
+
}
|
|
107
|
+
if (!allowed.includes(keyword)) {
|
|
108
|
+
throw new Error(`"${keyword}" isn't allowed in ${article} ${command} query (allowed: ${allowed.join(', ')})`);
|
|
109
|
+
}
|
|
110
|
+
if (current && allowed.indexOf(keyword) < allowed.indexOf(current)) {
|
|
111
|
+
throw new Error(`"${keyword}" can't come after "${current}" (order: ${allowed.join(', ')})`);
|
|
112
|
+
}
|
|
113
|
+
if (found.some(clause => clause.keyword === keyword)) {
|
|
114
|
+
throw new Error(`Only one "${keyword}" clause is allowed${keyword === 'where' ? `: ${whereHint}` : ''}`);
|
|
115
|
+
}
|
|
116
|
+
if (keyword === 'group by') {
|
|
117
|
+
index++;
|
|
118
|
+
}
|
|
119
|
+
found.push({
|
|
120
|
+
keyword,
|
|
121
|
+
start,
|
|
122
|
+
end: tokens[index].start + tokens[index].text.length
|
|
123
|
+
});
|
|
124
|
+
bodyStarts = true;
|
|
125
|
+
}
|
|
126
|
+
return {
|
|
127
|
+
command,
|
|
128
|
+
head: head[0].trim(),
|
|
129
|
+
leading: query.slice(head[0].length, found[0]?.start ?? query.length).trim(),
|
|
130
|
+
clauses: found.map(({
|
|
131
|
+
keyword,
|
|
132
|
+
end
|
|
133
|
+
}, index) => ({
|
|
134
|
+
keyword,
|
|
135
|
+
body: query.slice(end, found[index + 1]?.start ?? query.length).trim()
|
|
136
|
+
}))
|
|
137
|
+
};
|
|
138
|
+
};
|
|
139
|
+
exports.splitClauses = splitClauses;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.whereHint=exports.splitClauses=exports.clauseOrder=void 0,require("core-js/modules/es.array.includes.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 _maskStrings=require("./maskStrings");const clauseOrder=exports.clauseOrder={read:["select","on","merge","where","group by","sort","limit","offset"],insert:["values"],update:["set","where"],delete:["where"]},clauseWords=[...new Set(Object.values(clauseOrder).flat())],headMatch=/^\s*(delete|insert|read|update)\s+([a-z0-9_-]+(\s*\.[a-z0-9_-]+)?)/i,tokenMatch=new RegExp(`${_maskStrings.placeholderSource}|${_maskStrings.arraySource}|[a-z0-9_.-]+|<>|!=|>=|<=|\\S`,"gi"),conditionWords=["=","!=","<>",">","<",">=","<=","in","between","like","is","exists","does"],continues={on:e=>"and"===e,merge:()=>!0},whereHint=exports.whereHint="use and / or inside one where clause (where a = 1 and b = 2)",splitClauses=e=>{const s=e.match(headMatch);if(!s)return;const r=s[1].toLowerCase(),t=clauseOrder[r],o=/^[aeiou]/.test(r)?"an":"a";if(s[3]&&"read"!==r)throw new Error(`A joined record (${s[2]}) isn't allowed in ${o} ${r} query`);const n=[...e.slice(s[0].length).matchAll(tokenMatch)].map((({0:e,index:r})=>({text:e,lower:e.toLowerCase(),start:s[0].length+r}))),i=[];let l=0,d=!1;for(let e=0;e<n.length;e++){const{lower:s,start:u}=n[e],a=n[e-1]?.lower,c=n[e+1]?.lower;"("!==s&&")"!==s||(l+="("===s?1:-1);const w="group"===s&&"by"===c?"group by":s,j=d||","===a||"as"===a||conditionWords.includes(c);if(d=!1,l>0||"by"===w||!clauseWords.includes(w)||j)continue;const m=i[i.length-1]?.keyword;if(m===w&&continues[w]?.(a))continue;const h=i.some((e=>"where"===e.keyword));if("where"===w&&h&&("and"===a||"or"===a))throw new Error(`"${a} where" is no longer supported: ${whereHint}`);if(!t.includes(w))throw new Error(`"${w}" isn't allowed in ${o} ${r} query (allowed: ${t.join(", ")})`);if(m&&t.indexOf(w)<t.indexOf(m))throw new Error(`"${w}" can't come after "${m}" (order: ${t.join(", ")})`);if(i.some((e=>e.keyword===w)))throw new Error(`Only one "${w}" clause is allowed${"where"===w?`: ${whereHint}`:""}`);"group by"===w&&e++,i.push({keyword:w,start:u,end:n[e].start+n[e].text.length}),d=!0}return{command:r,head:s[0].trim(),leading:e.slice(s[0].length,i[0]?.start??e.length).trim(),clauses:i.map((({keyword:s,end:r},t)=>({keyword:s,body:e.slice(r,i[t+1]?.start??e.length).trim()})))}};exports.splitClauses=splitClauses;
|
|
@@ -7,7 +7,8 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
7
7
|
exports.retrieveFile = void 0;
|
|
8
8
|
var jsEnv = _interopRequireWildcard(require("browser-or-node"));
|
|
9
9
|
var _config = require("./config");
|
|
10
|
-
|
|
10
|
+
var e;
|
|
11
|
+
function _interopRequireWildcard(t, r) { if (!r && t && t.__esModule) return t; var n, a, o = Object.defineProperty, f = { __proto__: null, default: t }; if (Object(t) !== t) return f; if (e || "function" != typeof WeakMap || (e = new WeakMap()), e) { if (e.has(t)) return e.get(t); e.set(t, f); } for (a in t) "default" !== a && {}.hasOwnProperty.call(t, a) && ((n = o && Object.getOwnPropertyDescriptor(t, a)) && (n.get || n.set) ? o(f, a, n) : f[a] = t[a]); return f; }
|
|
11
12
|
const retrieveFile = async path => {
|
|
12
13
|
let retrieveFn = url => fetch(url).then(res => res.json());
|
|
13
14
|
const relativePath = jsEnv.isBrowser ? (0, _config.getSetting)('relativePath', '') : '';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";require("core-js/modules/esnext.weak-map.delete-all.js"),Object.defineProperty(exports,"__esModule",{value:!0}),exports.retrieveFile=void 0;var jsEnv=_interopRequireWildcard(require("browser-or-node")),_config=require("./config");function _interopRequireWildcard(
|
|
1
|
+
"use strict";require("core-js/modules/esnext.weak-map.delete-all.js"),Object.defineProperty(exports,"__esModule",{value:!0}),exports.retrieveFile=void 0;var e,jsEnv=_interopRequireWildcard(require("browser-or-node")),_config=require("./config");function _interopRequireWildcard(r,t){if(!t&&r&&r.__esModule)return r;var i,o,n=Object.defineProperty,s={__proto__:null,default:r};if(Object(r)!==r)return s;if(e||"function"!=typeof WeakMap||(e=new WeakMap),e){if(e.has(r))return e.get(r);e.set(r,s)}for(o in r)"default"!==o&&{}.hasOwnProperty.call(r,o)&&((i=n&&Object.getOwnPropertyDescriptor(r,o))&&(i.get||i.set)?n(s,o,i):s[o]=r[o]);return s}const retrieveFile=async e=>{let r=e=>fetch(e).then((e=>e.json()));const t=jsEnv.isBrowser?(0,_config.getSetting)("relativePath",""):"";if(jsEnv.isNode){const{readFile:e}=require("fs/promises");r=r=>e(r).then((e=>JSON.parse(e)))}const i=`${t}${(0,_config.getSetting)("databasePath","database/")}${e}`,o=await r(i).catch((e=>console.error(e)));if(!o)throw new Error(`Could not read ${e}`);return o};exports.retrieveFile=retrieveFile;
|
|
@@ -101,8 +101,8 @@ const runQuery = async parsed => {
|
|
|
101
101
|
// anything has looked at it at all. readEntity() (below) can't tell those two states apart on its own (it reloads
|
|
102
102
|
// whenever the array is empty), so getConditionIndexedList reports which entities it resolved: once any indexed
|
|
103
103
|
// condition has narrowed entity's rows, they are the candidates (every condition is applied to them below) and
|
|
104
|
-
// must not be reloaded. This used to ask whether EVERY condition was indexed, so `where date = 'none' and
|
|
105
|
-
//
|
|
104
|
+
// must not be reloaded. This used to ask whether EVERY condition was indexed, so `where date = 'none' and reps =
|
|
105
|
+
// 10` reloaded every row and returned, updated or deleted all rows with reps 10.
|
|
106
106
|
const resolved = new Set();
|
|
107
107
|
parsed = await (0, _getConditionIndexedList.getConditionIndexedList)(parsed, recordData, dataSet, resolved);
|
|
108
108
|
const entityResolved = resolved.has(parsed.entity);
|
|
@@ -144,10 +144,11 @@ const runQuery = async parsed => {
|
|
|
144
144
|
if (parsed.command !== 'delete' && parsed.command !== 'update') {
|
|
145
145
|
dataSet[parsed.entity] = (0, _where.whereTree)(dataSet[parsed.entity], parsed.where, perPair ? parsed.joinEntity : undefined);
|
|
146
146
|
}
|
|
147
|
+
// Sorted before grouping, so the groups come in sorted order; limit and offset count groups once there are any
|
|
147
148
|
dataSet[parsed.entity] = (0, _sortBy.sortBy)(dataSet[parsed.entity], parsed.sortClauses);
|
|
148
|
-
dataSet[parsed.entity] = (0, _limitOffset.limitOffset)(dataSet[parsed.entity], parsed.offset, parsed.limit);
|
|
149
149
|
dataSet[parsed.entity] = (0, _groupBy.groupBy)(dataSet[parsed.entity], parsed.groupBy);
|
|
150
|
-
dataSet[parsed.entity] = (0,
|
|
150
|
+
dataSet[parsed.entity] = (0, _limitOffset.limitOffset)(dataSet[parsed.entity], parsed.offset, parsed.limit);
|
|
151
|
+
dataSet[parsed.entity] = (0, _selectFields.selectFields)(dataSet[parsed.entity], parsed.selectClauses, Boolean(parsed.groupBy?.length));
|
|
151
152
|
return dataSet;
|
|
152
153
|
};
|
|
153
154
|
exports.runQuery = runQuery;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.runQuery=void 0,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 _deleteEntity=require("../queries/deleteEntity"),_insertEntity=require("../queries/insertEntity"),_readEntity=require("../queries/readEntity"),_updateEntity=require("../queries/updateEntity"),_createRecord=require("../queries/createRecord"),_dropRecord=require("../queries/dropRecord"),_describeRecord=require("../queries/describeRecord"),_showRecords=require("../queries/showRecords"),_joinEntity=require("../queries/joinEntity"),_mergeJoins=require("../queries/mergeJoins"),_where=require("../queries/where"),_sortBy=require("../queries/sortBy"),_groupBy=require("../queries/groupBy"),_limitOffset=require("../queries/limitOffset"),_selectFields=require("../queries/selectFields"),_retrieveRecord=require("./retrieveRecord"),_getConditionIndexedList=require("./getConditionIndexedList"),_getJoinIndexedList=require("./getJoinIndexedList"),_isEntityProperty=require("./parsers/isEntityProperty"),_splitEntityProperty=require("./parsers/splitEntityProperty"),_fieldType=require("./fieldType"),_conditionTree=require("./conditionTree"),_conditionEntities=require("./conditionEntities");const typeClauses=async(e,t)=>{for(const r of[...(0,_conditionTree.conditionsOf)(e.where),...e.sortClauses]){let i=e.entity,s=r.property;(0,_isEntityProperty.isEntityProperty)(s)&&({entity:i,property:s}=(0,_splitEntityProperty.splitEntityProperty)(s)),Object.prototype.hasOwnProperty.call(t,i)||(t[i]=await(0,_retrieveRecord.retrieveRecord)(i)),r.type=(0,_fieldType.fieldType)(t[i],s)}},runQuery=async e=>{const t=void 0!==e.where&&(0,_conditionEntities.conditionEntities)(e);switch(e.command){case"create":return{[e.entity]:await(0,_createRecord.createRecord)(e.entity,e.create)};case"drop":return{[e.entity]:await(0,_dropRecord.dropRecord)(e.entity,e.ifExists)};case"describe":return{[e.entity]:await(0,_describeRecord.describeRecord)(e.entity)};case"show":return{[e.entity]:await(0,_showRecords.showRecords)()}}const r={[e.entity]:await(0,_retrieveRecord.retrieveRecord)(e.entity)};void 0!==e.joinEntity&&(r[e.joinEntity]=await(0,_retrieveRecord.retrieveRecord)(e.joinEntity)),await typeClauses(e,r);let i={[e.entity]:[]};const s=new Set;e=await(0,_getConditionIndexedList.getConditionIndexedList)(e,r,i,s);const n=s.has(e.entity);switch("delete"!==(e=await(0,_getJoinIndexedList.getJoinIndexedList)(e,r,i)).command&&"update"!==e.command||(n||(i=await(0,_readEntity.readEntity)(e.entity,i)),i[e.entity]=(0,_where.whereTree)(i[e.entity],e.where)),e.command){case"delete":i=await(0,_deleteEntity.deleteEntity)(e.entity,i);break;case"insert":i=await(0,_insertEntity.insertEntity)(e.entity,e.insertValues,i);break;case"read":n||(i=await(0,_readEntity.readEntity)(e.entity,i));break;case"update":i=await(0,_updateEntity.updateEntity)(e.entity,e.updateValues,i);break;default:throw new Error(`Unknown command: ${e.command}`)}return i=await(0,_joinEntity.joinEntity)(e.entity,e.joinEntity,i,e.joinClauses,n,s.has(e.joinEntity)),i=await(0,_mergeJoins.mergeJoins)(i,e.mergeJoins),"delete"!==e.command&&"update"!==e.command&&(i[e.entity]=(0,_where.whereTree)(i[e.entity],e.where,t?e.joinEntity:void 0)),i[e.entity]=(0,_sortBy.sortBy)(i[e.entity],e.sortClauses),i[e.entity]=(0,
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.runQuery=void 0,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 _deleteEntity=require("../queries/deleteEntity"),_insertEntity=require("../queries/insertEntity"),_readEntity=require("../queries/readEntity"),_updateEntity=require("../queries/updateEntity"),_createRecord=require("../queries/createRecord"),_dropRecord=require("../queries/dropRecord"),_describeRecord=require("../queries/describeRecord"),_showRecords=require("../queries/showRecords"),_joinEntity=require("../queries/joinEntity"),_mergeJoins=require("../queries/mergeJoins"),_where=require("../queries/where"),_sortBy=require("../queries/sortBy"),_groupBy=require("../queries/groupBy"),_limitOffset=require("../queries/limitOffset"),_selectFields=require("../queries/selectFields"),_retrieveRecord=require("./retrieveRecord"),_getConditionIndexedList=require("./getConditionIndexedList"),_getJoinIndexedList=require("./getJoinIndexedList"),_isEntityProperty=require("./parsers/isEntityProperty"),_splitEntityProperty=require("./parsers/splitEntityProperty"),_fieldType=require("./fieldType"),_conditionTree=require("./conditionTree"),_conditionEntities=require("./conditionEntities");const typeClauses=async(e,t)=>{for(const r of[...(0,_conditionTree.conditionsOf)(e.where),...e.sortClauses]){let i=e.entity,s=r.property;(0,_isEntityProperty.isEntityProperty)(s)&&({entity:i,property:s}=(0,_splitEntityProperty.splitEntityProperty)(s)),Object.prototype.hasOwnProperty.call(t,i)||(t[i]=await(0,_retrieveRecord.retrieveRecord)(i)),r.type=(0,_fieldType.fieldType)(t[i],s)}},runQuery=async e=>{const t=void 0!==e.where&&(0,_conditionEntities.conditionEntities)(e);switch(e.command){case"create":return{[e.entity]:await(0,_createRecord.createRecord)(e.entity,e.create)};case"drop":return{[e.entity]:await(0,_dropRecord.dropRecord)(e.entity,e.ifExists)};case"describe":return{[e.entity]:await(0,_describeRecord.describeRecord)(e.entity)};case"show":return{[e.entity]:await(0,_showRecords.showRecords)()}}const r={[e.entity]:await(0,_retrieveRecord.retrieveRecord)(e.entity)};void 0!==e.joinEntity&&(r[e.joinEntity]=await(0,_retrieveRecord.retrieveRecord)(e.joinEntity)),await typeClauses(e,r);let i={[e.entity]:[]};const s=new Set;e=await(0,_getConditionIndexedList.getConditionIndexedList)(e,r,i,s);const n=s.has(e.entity);switch("delete"!==(e=await(0,_getJoinIndexedList.getJoinIndexedList)(e,r,i)).command&&"update"!==e.command||(n||(i=await(0,_readEntity.readEntity)(e.entity,i)),i[e.entity]=(0,_where.whereTree)(i[e.entity],e.where)),e.command){case"delete":i=await(0,_deleteEntity.deleteEntity)(e.entity,i);break;case"insert":i=await(0,_insertEntity.insertEntity)(e.entity,e.insertValues,i);break;case"read":n||(i=await(0,_readEntity.readEntity)(e.entity,i));break;case"update":i=await(0,_updateEntity.updateEntity)(e.entity,e.updateValues,i);break;default:throw new Error(`Unknown command: ${e.command}`)}return i=await(0,_joinEntity.joinEntity)(e.entity,e.joinEntity,i,e.joinClauses,n,s.has(e.joinEntity)),i=await(0,_mergeJoins.mergeJoins)(i,e.mergeJoins),"delete"!==e.command&&"update"!==e.command&&(i[e.entity]=(0,_where.whereTree)(i[e.entity],e.where,t?e.joinEntity:void 0)),i[e.entity]=(0,_sortBy.sortBy)(i[e.entity],e.sortClauses),i[e.entity]=(0,_groupBy.groupBy)(i[e.entity],e.groupBy),i[e.entity]=(0,_limitOffset.limitOffset)(i[e.entity],e.offset,e.limit),i[e.entity]=(0,_selectFields.selectFields)(i[e.entity],e.selectClauses,Boolean(e.groupBy?.length)),i};exports.runQuery=runQuery;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "json-fs-query",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.1.0",
|
|
4
4
|
"description": "Use json files to manage and read data.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -43,8 +43,9 @@
|
|
|
43
43
|
},
|
|
44
44
|
"homepage": "https://github.com/jheagle/json-file-db#readme",
|
|
45
45
|
"devDependencies": {
|
|
46
|
+
"@babel/core": "^8.1.0",
|
|
46
47
|
"gulp": "^5.0.1",
|
|
47
|
-
"js-build-tools": "^
|
|
48
|
+
"js-build-tools": "^8.0.0",
|
|
48
49
|
"release-it": "^21.1.1",
|
|
49
50
|
"test-filesystem": "^2.0.2",
|
|
50
51
|
"typedoc": "^0.28.20"
|
|
@@ -63,6 +64,7 @@
|
|
|
63
64
|
"core-js": false,
|
|
64
65
|
"core-js-pure": false,
|
|
65
66
|
"@parcel/watcher": false,
|
|
66
|
-
"unrs-resolver": false
|
|
67
|
+
"unrs-resolver": false,
|
|
68
|
+
"esbuild": false
|
|
67
69
|
}
|
|
68
70
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.removeFromQuery=void 0;const removeFromQuery=(e,r)=>r.replace(e,"").trim();exports.removeFromQuery=removeFromQuery;
|