json-fs-query 1.0.6 → 2.0.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/LICENSE +674 -674
- package/README.md +295 -98
- package/browser/jsonFsQuery.js +12733 -10777
- package/browser/jsonFsQuery.min.js +1 -1
- package/dist/data/introspect/create.d.ts +1 -1
- package/dist/data/introspect/create.js +35 -4
- package/dist/data/introspect/create.min.js +1 -1
- package/dist/data/introspect/definitions/field.d.ts +6 -2
- package/dist/data/introspect/definitions/field.js +7 -1
- package/dist/data/introspect/definitions/field.min.js +1 -1
- package/dist/data/introspect/index.d.ts +6 -3
- package/dist/data/introspect/index.js +10 -19
- package/dist/data/introspect/index.min.js +1 -1
- package/dist/data/introspect/policies/foreignKeyExists.js +5 -1
- package/dist/data/introspect/policies/foreignKeyExists.min.js +1 -1
- package/dist/data/introspect/policies/hasDependents.js +3 -1
- package/dist/data/introspect/policies/hasDependents.min.js +1 -1
- package/dist/data/introspect/policies/keyUnique.js +5 -1
- package/dist/data/introspect/policies/keyUnique.min.js +1 -1
- package/dist/data/introspect/policies/typeCheck.d.ts +9 -1
- package/dist/data/introspect/policies/typeCheck.js +16 -2
- package/dist/data/introspect/policies/typeCheck.min.js +1 -1
- package/dist/data/queries/createRecord.d.ts +11 -0
- package/dist/data/queries/createRecord.js +58 -0
- package/dist/data/queries/createRecord.min.js +1 -0
- package/dist/data/queries/deleteEntity.js +2 -1
- package/dist/data/queries/deleteEntity.min.js +1 -1
- package/dist/data/queries/describeRecord.d.ts +9 -0
- package/dist/data/queries/describeRecord.js +29 -0
- package/dist/data/queries/describeRecord.min.js +1 -0
- package/dist/data/queries/dropRecord.d.ts +9 -0
- package/dist/data/queries/dropRecord.js +61 -0
- package/dist/data/queries/dropRecord.min.js +1 -0
- package/dist/data/queries/insertEntity.js +11 -5
- package/dist/data/queries/insertEntity.min.js +1 -1
- package/dist/data/queries/parser.d.ts +1 -1
- package/dist/data/queries/parser.js +7 -15
- package/dist/data/queries/parser.min.js +1 -1
- package/dist/data/queries/query.d.ts +6 -0
- package/dist/data/queries/query.js +15 -6
- package/dist/data/queries/query.min.js +1 -1
- package/dist/data/queries/queryStatus.d.ts +15 -0
- package/dist/data/queries/queryStatus.js +52 -0
- package/dist/data/queries/queryStatus.min.js +1 -0
- package/dist/data/queries/quote.d.ts +11 -0
- package/dist/data/queries/quote.js +17 -0
- package/dist/data/queries/quote.min.js +1 -0
- package/dist/data/queries/showRecords.d.ts +6 -0
- package/dist/data/queries/showRecords.js +21 -0
- package/dist/data/queries/showRecords.min.js +1 -0
- package/dist/data/queries/sortBy.js +3 -0
- package/dist/data/queries/updateEntity.js +74 -18
- package/dist/data/queries/updateEntity.min.js +1 -1
- package/dist/data/queries/where.d.ts +2 -1
- package/dist/data/queries/where.js +18 -6
- package/dist/data/queries/where.min.js +1 -1
- package/dist/data/utilities/compareAs.d.ts +20 -0
- package/dist/data/utilities/compareAs.js +58 -0
- package/dist/data/utilities/compareAs.min.js +1 -0
- package/dist/data/utilities/config.d.ts +15 -0
- package/dist/data/utilities/config.js +31 -1
- package/dist/data/utilities/config.min.js +1 -1
- package/dist/data/utilities/databaseLock.d.ts +16 -0
- package/dist/data/utilities/databaseLock.js +63 -0
- package/dist/data/utilities/databaseLock.min.js +1 -0
- package/dist/data/utilities/fieldType.d.ts +16 -0
- package/dist/data/utilities/fieldType.js +23 -0
- package/dist/data/utilities/fieldType.min.js +1 -0
- package/dist/data/utilities/getConditionIndexedList.js +2 -1
- package/dist/data/utilities/getConditionIndexedList.min.js +1 -1
- package/dist/data/utilities/matchIndexedJoins.js +3 -1
- package/dist/data/utilities/matchIndexedJoins.min.js +1 -1
- package/dist/data/utilities/mergeJoin.js +5 -1
- package/dist/data/utilities/mergeJoin.min.js +1 -1
- package/dist/data/utilities/parsers/findConditions.d.ts +5 -1
- package/dist/data/utilities/parsers/findConditions.js +15 -6
- package/dist/data/utilities/parsers/findConditions.min.js +1 -1
- package/dist/data/utilities/parsers/findInsertValues.js +3 -2
- package/dist/data/utilities/parsers/findInsertValues.min.js +1 -1
- package/dist/data/utilities/parsers/findSelects.js +6 -3
- package/dist/data/utilities/parsers/findSelects.min.js +1 -1
- package/dist/data/utilities/parsers/findSortClauses.js +5 -2
- package/dist/data/utilities/parsers/findSortClauses.min.js +1 -1
- package/dist/data/utilities/parsers/findUpdateValues.js +3 -2
- package/dist/data/utilities/parsers/findUpdateValues.min.js +1 -1
- package/dist/data/utilities/parsers/maskStrings.d.ts +39 -0
- package/dist/data/utilities/parsers/maskStrings.js +123 -0
- package/dist/data/utilities/parsers/maskStrings.min.js +1 -0
- package/dist/data/utilities/parsers/parseCreate.d.ts +20 -0
- package/dist/data/utilities/parsers/parseCreate.js +330 -0
- package/dist/data/utilities/parsers/parseCreate.min.js +1 -0
- package/dist/data/utilities/parsers/parseLiteralValue.d.ts +2 -1
- package/dist/data/utilities/parsers/parseLiteralValue.js +6 -3
- package/dist/data/utilities/parsers/parseLiteralValue.min.js +1 -1
- package/dist/data/utilities/parsers/parseQuery.d.ts +3 -0
- package/dist/data/utilities/parsers/parseQuery.js +28 -2
- package/dist/data/utilities/parsers/parseQuery.min.js +1 -1
- package/dist/data/utilities/parsers/parseSchemaStatement.d.ts +13 -0
- package/dist/data/utilities/parsers/parseSchemaStatement.js +82 -0
- package/dist/data/utilities/parsers/parseSchemaStatement.min.js +1 -0
- package/dist/data/utilities/parsers/scanQuery.d.ts +52 -0
- package/dist/data/utilities/parsers/scanQuery.js +158 -0
- package/dist/data/utilities/parsers/scanQuery.min.js +1 -0
- package/dist/data/utilities/parsers/splitQueries.d.ts +6 -0
- package/dist/data/utilities/parsers/splitQueries.js +20 -2
- package/dist/data/utilities/parsers/splitQueries.min.js +1 -1
- package/dist/data/utilities/parsers/startsQuery.d.ts +5 -0
- package/dist/data/utilities/parsers/startsQuery.js +10 -0
- package/dist/data/utilities/parsers/startsQuery.min.js +1 -0
- package/dist/data/utilities/recordExists.d.ts +6 -0
- package/dist/data/utilities/recordExists.js +18 -0
- package/dist/data/utilities/recordExists.min.js +1 -0
- package/dist/data/utilities/removeFromIndex.d.ts +2 -1
- package/dist/data/utilities/removeFromIndex.js +4 -2
- package/dist/data/utilities/removeFromIndex.min.js +1 -1
- package/dist/data/utilities/runQuery.js +49 -0
- package/dist/data/utilities/runQuery.min.js +1 -1
- package/dist/data/utilities/updateIndex.d.ts +2 -1
- package/dist/data/utilities/updateIndex.js +4 -2
- package/dist/data/utilities/updateIndex.min.js +1 -1
- package/dist/data/utilities/useJoinClause.js +8 -2
- package/dist/data/utilities/useJoinClause.min.js +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +14 -0
- package/dist/index.min.js +1 -1
- package/package.json +68 -55
|
@@ -8,6 +8,7 @@ require("core-js/modules/es.json.stringify.js");
|
|
|
8
8
|
require("core-js/modules/esnext.iterator.constructor.js");
|
|
9
9
|
require("core-js/modules/esnext.iterator.find.js");
|
|
10
10
|
require("core-js/modules/esnext.iterator.map.js");
|
|
11
|
+
require("core-js/modules/esnext.iterator.some.js");
|
|
11
12
|
var _retrieveRecord = require("../utilities/retrieveRecord");
|
|
12
13
|
var _updateIndex = require("../utilities/updateIndex");
|
|
13
14
|
var _keyGenerate = require("../introspect/policies/keyGenerate");
|
|
@@ -17,6 +18,7 @@ var _typeCheck = require("../introspect/policies/typeCheck");
|
|
|
17
18
|
var _foreignKeyExists = require("../introspect/policies/foreignKeyExists");
|
|
18
19
|
var _splitEntityProperty = require("../utilities/parsers/splitEntityProperty");
|
|
19
20
|
var _computeKeyValue = require("../utilities/computeKeyValue");
|
|
21
|
+
var _fieldType = require("../utilities/fieldType");
|
|
20
22
|
var _config = require("../utilities/config");
|
|
21
23
|
const {
|
|
22
24
|
writeFile
|
|
@@ -43,13 +45,14 @@ const buildEntityValues = async (entity, record, rowValues = {}) => {
|
|
|
43
45
|
}
|
|
44
46
|
continue;
|
|
45
47
|
}
|
|
46
|
-
entityValues[field.name] = (0, _typeCheck.typeCheck)(field.type, value);
|
|
48
|
+
entityValues[field.name] = (0, _typeCheck.typeCheck)(field.type, value, field.nullable);
|
|
47
49
|
}
|
|
48
50
|
return entityValues;
|
|
49
51
|
};
|
|
50
52
|
/**
|
|
51
53
|
* Reject the insert if it collides with an existing primary or unique key value - a single field,
|
|
52
|
-
* or a composite of several for a multi-field key.
|
|
54
|
+
* or a composite of several for a multi-field key. As in SQL, a value with a null in it is never a
|
|
55
|
+
* duplicate, so a nullable unique field can hold null in any number of rows.
|
|
53
56
|
* @param entity
|
|
54
57
|
* @param record
|
|
55
58
|
* @param entityValues
|
|
@@ -60,6 +63,9 @@ const assertUnique = async (entity, record, entityValues) => {
|
|
|
60
63
|
continue;
|
|
61
64
|
}
|
|
62
65
|
const checkKeys = key.fields.map(field => entityValues[field]);
|
|
66
|
+
if (checkKeys.some(value => value === null)) {
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
63
69
|
const isUnique = await (0, _keyUnique.keyUnique)(entity, key.fields, checkKeys);
|
|
64
70
|
if (!isUnique) {
|
|
65
71
|
throw new Error(`Duplicate value for ${key.type} key "${key.fields.join(', ')}" on ${entity}`);
|
|
@@ -68,7 +74,7 @@ const assertUnique = async (entity, record, entityValues) => {
|
|
|
68
74
|
};
|
|
69
75
|
/**
|
|
70
76
|
* Reject the insert if a single-field foreign key's value doesn't exist in the record it
|
|
71
|
-
* references.
|
|
77
|
+
* references. A null references nothing, so it isn't checked (as in SQL).
|
|
72
78
|
* @param entity
|
|
73
79
|
* @param record
|
|
74
80
|
* @param entityValues
|
|
@@ -79,7 +85,7 @@ const assertForeignKeys = async (entity, record, entityValues) => {
|
|
|
79
85
|
continue;
|
|
80
86
|
}
|
|
81
87
|
const field = key.fields[0];
|
|
82
|
-
if (!Object.prototype.hasOwnProperty.call(entityValues, field)) {
|
|
88
|
+
if (!Object.prototype.hasOwnProperty.call(entityValues, field) || entityValues[field] === null) {
|
|
83
89
|
continue;
|
|
84
90
|
}
|
|
85
91
|
const {
|
|
@@ -108,7 +114,7 @@ const insertEntity = async (entity = '', values = [], dataSet = {}) => {
|
|
|
108
114
|
record.entries.push(fileName);
|
|
109
115
|
await writeFile(`${databasePath}${record.path}.json`, JSON.stringify(record, null, 2));
|
|
110
116
|
for (const key of record.keys) {
|
|
111
|
-
await (0, _updateIndex.updateIndex)(record.path, key, (0, _computeKeyValue.computeKeyValue)(key.fields, entityValues), fileName);
|
|
117
|
+
await (0, _updateIndex.updateIndex)(record.path, key, (0, _computeKeyValue.computeKeyValue)(key.fields, entityValues), fileName, (0, _fieldType.keyValueType)(record, key));
|
|
112
118
|
}
|
|
113
119
|
dataSet[entity].push(entityValues);
|
|
114
120
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.insertEntity=void 0,require("core-js/modules/es.json.stringify.js"),require("core-js/modules/esnext.iterator.constructor.js"),require("core-js/modules/esnext.iterator.find.js"),require("core-js/modules/esnext.iterator.map.js");var _retrieveRecord=require("../utilities/retrieveRecord"),_updateIndex=require("../utilities/updateIndex"),_keyGenerate=require("../introspect/policies/keyGenerate"),_keyUnique=require("../introspect/policies/keyUnique"),_useDefault=require("../introspect/policies/useDefault"),_typeCheck=require("../introspect/policies/typeCheck"),_foreignKeyExists=require("../introspect/policies/foreignKeyExists"),_splitEntityProperty=require("../utilities/parsers/splitEntityProperty"),_computeKeyValue=require("../utilities/computeKeyValue"),_config=require("../utilities/config");const{writeFile:writeFile}=require("fs/promises"),buildEntityValues=async(e,t,i={})=>{const r={};for(const s of t.definition){let t=i[s.name];if(s.autoGenerate?t=await(0,_keyGenerate.keyGenerate)(e,[s.name]):Object.prototype.hasOwnProperty.call(s,"default")&&(t=(0,_useDefault.useDefault)(s.default,t)),void 0!==t)r[s.name]=(0,_typeCheck.typeCheck)(s.type,t);else if(!s.optional)throw new Error(`Missing required field "${s.name}" for ${e}`)}return r},assertUnique=async(e,t,i)=>{for(const r of t.keys){if("primary"!==r.type&&"unique"!==r.type)continue;const t=r.fields.map((e=>i[e]));if(!await(0,_keyUnique.keyUnique)(e,r.fields,t))throw new Error(`Duplicate value for ${r.type} key "${r.fields.join(", ")}" on ${e}`)}},assertForeignKeys=async(e,t,i)=>{for(const r of t.keys){if("foreign"!==r.type||1!==r.fields.length||!r.references||1!==r.references.length)continue;const t=r.fields[0];if(!Object.prototype.hasOwnProperty.call(i,t))continue;const{entity:s,property:n}=(0,_splitEntityProperty.splitEntityProperty)(r.references[0].fields[0]);if(!await(0,_foreignKeyExists.foreignKeyExists)(s,n,i[t]))throw new Error(`Foreign key "${t}" on ${e} references a nonexistent ${s}.${n} = ${i[t]}`)}},insertEntity=async(e="",t=[],i={})=>{const r=(0,_config.getSetting)("databasePath","database/"),s=await(0,_retrieveRecord.retrieveRecord)(e),n=s.keys.find((e=>"primary"===e.type&&1===e.fields.length));Object.prototype.hasOwnProperty.call(i,e)||(i[e]=[]);for(const o of t){const t=await buildEntityValues(e,s,o);await assertUnique(e,s,t),await assertForeignKeys(e,s,t);const a=`${n?t[n.fields[0]]:crypto.randomUUID()}.json`;await writeFile(`${r}${s.path}/${a}`,JSON.stringify(t,null,2)),s.entries.push(a),await writeFile(`${r}${s.path}.json`,JSON.stringify(s,null,2));for(const e of s.keys)await(0,_updateIndex.updateIndex)(s.path,e,(0,_computeKeyValue.computeKeyValue)(e.fields,t),a);i[e].push(t)}return i};exports.insertEntity=insertEntity;
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.insertEntity=void 0,require("core-js/modules/es.json.stringify.js"),require("core-js/modules/esnext.iterator.constructor.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");var _retrieveRecord=require("../utilities/retrieveRecord"),_updateIndex=require("../utilities/updateIndex"),_keyGenerate=require("../introspect/policies/keyGenerate"),_keyUnique=require("../introspect/policies/keyUnique"),_useDefault=require("../introspect/policies/useDefault"),_typeCheck=require("../introspect/policies/typeCheck"),_foreignKeyExists=require("../introspect/policies/foreignKeyExists"),_splitEntityProperty=require("../utilities/parsers/splitEntityProperty"),_computeKeyValue=require("../utilities/computeKeyValue"),_fieldType=require("../utilities/fieldType"),_config=require("../utilities/config");const{writeFile:writeFile}=require("fs/promises"),buildEntityValues=async(e,t,i={})=>{const r={};for(const s of t.definition){let t=i[s.name];if(s.autoGenerate?t=await(0,_keyGenerate.keyGenerate)(e,[s.name]):Object.prototype.hasOwnProperty.call(s,"default")&&(t=(0,_useDefault.useDefault)(s.default,t)),void 0!==t)r[s.name]=(0,_typeCheck.typeCheck)(s.type,t,s.nullable);else if(!s.optional)throw new Error(`Missing required field "${s.name}" for ${e}`)}return r},assertUnique=async(e,t,i)=>{for(const r of t.keys){if("primary"!==r.type&&"unique"!==r.type)continue;const t=r.fields.map((e=>i[e]));if(t.some((e=>null===e)))continue;if(!await(0,_keyUnique.keyUnique)(e,r.fields,t))throw new Error(`Duplicate value for ${r.type} key "${r.fields.join(", ")}" on ${e}`)}},assertForeignKeys=async(e,t,i)=>{for(const r of t.keys){if("foreign"!==r.type||1!==r.fields.length||!r.references||1!==r.references.length)continue;const t=r.fields[0];if(!Object.prototype.hasOwnProperty.call(i,t)||null===i[t])continue;const{entity:s,property:n}=(0,_splitEntityProperty.splitEntityProperty)(r.references[0].fields[0]);if(!await(0,_foreignKeyExists.foreignKeyExists)(s,n,i[t]))throw new Error(`Foreign key "${t}" on ${e} references a nonexistent ${s}.${n} = ${i[t]}`)}},insertEntity=async(e="",t=[],i={})=>{const r=(0,_config.getSetting)("databasePath","database/"),s=await(0,_retrieveRecord.retrieveRecord)(e),n=s.keys.find((e=>"primary"===e.type&&1===e.fields.length));Object.prototype.hasOwnProperty.call(i,e)||(i[e]=[]);for(const o of t){const t=await buildEntityValues(e,s,o);await assertUnique(e,s,t),await assertForeignKeys(e,s,t);const a=`${n?t[n.fields[0]]:crypto.randomUUID()}.json`;await writeFile(`${r}${s.path}/${a}`,JSON.stringify(t,null,2)),s.entries.push(a),await writeFile(`${r}${s.path}.json`,JSON.stringify(s,null,2));for(const e of s.keys)await(0,_updateIndex.updateIndex)(s.path,e,(0,_computeKeyValue.computeKeyValue)(e.fields,t),a,(0,_fieldType.keyValueType)(s,e));i[e].push(t)}return i};exports.insertEntity=insertEntity;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const parser: (queries?: string) =>
|
|
1
|
+
export declare const parser: (queries?: string) => import("../utilities/parsers/parseQuery").ParsedQuery[];
|
|
2
2
|
//# sourceMappingURL=parser.d.ts.map
|
|
@@ -4,27 +4,19 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.parser = void 0;
|
|
7
|
+
require("core-js/modules/esnext.iterator.constructor.js");
|
|
8
|
+
require("core-js/modules/esnext.iterator.map.js");
|
|
7
9
|
var _splitQueries = require("../utilities/parsers/splitQueries");
|
|
8
10
|
var _parseQuery = require("../utilities/parsers/parseQuery");
|
|
9
11
|
const parser = (queries = '') => {
|
|
10
12
|
const queryArray = (0, _splitQueries.splitQueries)(queries);
|
|
11
|
-
|
|
12
|
-
let mergeQuery = '';
|
|
13
|
-
for (const query of queryArray) {
|
|
14
|
-
mergeQuery = mergeQuery ? `${mergeQuery}${query}` : query;
|
|
15
|
-
let parsedQuery = {};
|
|
13
|
+
return queryArray.map((query, index) => {
|
|
16
14
|
try {
|
|
17
|
-
|
|
18
|
-
mergeQuery = '';
|
|
15
|
+
return (0, _parseQuery.parseQuery)(query);
|
|
19
16
|
} catch (e) {
|
|
20
|
-
|
|
21
|
-
|
|
17
|
+
const which = queryArray.length > 1 ? ` query ${index + 1} of ${queryArray.length}` : '';
|
|
18
|
+
throw new Error(`Failed to interpret${which}: ${query} (${e.message})`);
|
|
22
19
|
}
|
|
23
|
-
|
|
24
|
-
}
|
|
25
|
-
if (mergeQuery) {
|
|
26
|
-
throw new Error(`Failed to interpret: ${mergeQuery}`);
|
|
27
|
-
}
|
|
28
|
-
return parsedQueries;
|
|
20
|
+
});
|
|
29
21
|
};
|
|
30
22
|
exports.parser = parser;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.parser=void 0;var _splitQueries=require("../utilities/parsers/splitQueries"),_parseQuery=require("../utilities/parsers/parseQuery");const parser=(e="")=>{const r=(0,_splitQueries.splitQueries)(e)
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.parser=void 0,require("core-js/modules/esnext.iterator.constructor.js"),require("core-js/modules/esnext.iterator.map.js");var _splitQueries=require("../utilities/parsers/splitQueries"),_parseQuery=require("../utilities/parsers/parseQuery");const parser=(e="")=>{const r=(0,_splitQueries.splitQueries)(e);return r.map(((e,s)=>{try{return(0,_parseQuery.parseQuery)(e)}catch(t){const i=r.length>1?` query ${s+1} of ${r.length}`:"";throw new Error(`Failed to interpret${i}: ${e} (${t.message})`)}}))};exports.parser=parser;
|
|
@@ -1,2 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parse a query string (one query, or several separated by `;`) and run each query in turn, resolving to one result
|
|
3
|
+
* per query. The queries of one call run together, under the database lock: no other call's queries run between
|
|
4
|
+
* them (see withDatabaseLock).
|
|
5
|
+
* @param queryString
|
|
6
|
+
*/
|
|
1
7
|
export declare const query: (queryString?: string) => Promise<any[]>;
|
|
2
8
|
//# sourceMappingURL=query.d.ts.map
|
|
@@ -6,13 +6,22 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
6
6
|
exports.query = void 0;
|
|
7
7
|
var _parser = require("./parser");
|
|
8
8
|
var _runQuery = require("../utilities/runQuery");
|
|
9
|
+
var _databaseLock = require("../utilities/databaseLock");
|
|
10
|
+
/**
|
|
11
|
+
* Parse a query string (one query, or several separated by `;`) and run each query in turn, resolving to one result
|
|
12
|
+
* per query. The queries of one call run together, under the database lock: no other call's queries run between
|
|
13
|
+
* them (see withDatabaseLock).
|
|
14
|
+
* @param queryString
|
|
15
|
+
*/
|
|
9
16
|
const query = async (queryString = '') => {
|
|
10
17
|
const parsedQueries = (0, _parser.parser)(queryString);
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
18
|
+
return (0, _databaseLock.withDatabaseLock)(async () => {
|
|
19
|
+
const queryResults = [];
|
|
20
|
+
for (const parsed of parsedQueries) {
|
|
21
|
+
const dataSet = await (0, _runQuery.runQuery)(parsed);
|
|
22
|
+
queryResults.push(dataSet[parsed.entity]);
|
|
23
|
+
}
|
|
24
|
+
return queryResults;
|
|
25
|
+
});
|
|
17
26
|
};
|
|
18
27
|
exports.query = query;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.query=void 0;var _parser=require("./parser"),_runQuery=require("../utilities/runQuery");const query=async(
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.query=void 0;var _parser=require("./parser"),_runQuery=require("../utilities/runQuery"),_databaseLock=require("../utilities/databaseLock");const query=async(e="")=>{const r=(0,_parser.parser)(e);return(0,_databaseLock.withDatabaseLock)((async()=>{const e=[];for(const t of r){const r=await(0,_runQuery.runQuery)(t);e.push(r[t.entity])}return e}))};exports.query=query;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export type QueryStatus = {
|
|
2
|
+
complete: boolean;
|
|
3
|
+
waitingFor: string;
|
|
4
|
+
};
|
|
5
|
+
/**
|
|
6
|
+
* Whether some query text, typed so far, is ready to run, and if not what it is waiting for: for a prompt which
|
|
7
|
+
* reads a query over several lines (such as the CLI). Uses the same rules as the parser, so a `;` inside a string,
|
|
8
|
+
* a comment or brackets doesn't count, and an open quote, comment or bracket keeps the query going.
|
|
9
|
+
* @example
|
|
10
|
+
* queryStatus("read notes where text = 'it") // { complete: false, waitingFor: "'" }
|
|
11
|
+
* queryStatus('read notes;') // { complete: true, waitingFor: '' }
|
|
12
|
+
* @param text - everything typed so far; join the lines with '\n', since a `--` comment ends at the end of a line
|
|
13
|
+
*/
|
|
14
|
+
export declare const queryStatus: (text?: string) => QueryStatus;
|
|
15
|
+
//# sourceMappingURL=queryStatus.d.ts.map
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.queryStatus = void 0;
|
|
7
|
+
var _scanQuery = require("../utilities/parsers/scanQuery");
|
|
8
|
+
/**
|
|
9
|
+
* Whether some query text, typed so far, is ready to run, and if not what it is waiting for: for a prompt which
|
|
10
|
+
* reads a query over several lines (such as the CLI). Uses the same rules as the parser, so a `;` inside a string,
|
|
11
|
+
* a comment or brackets doesn't count, and an open quote, comment or bracket keeps the query going.
|
|
12
|
+
* @example
|
|
13
|
+
* queryStatus("read notes where text = 'it") // { complete: false, waitingFor: "'" }
|
|
14
|
+
* queryStatus('read notes;') // { complete: true, waitingFor: '' }
|
|
15
|
+
* @param text - everything typed so far; join the lines with '\n', since a `--` comment ends at the end of a line
|
|
16
|
+
*/
|
|
17
|
+
const queryStatus = (text = '') => {
|
|
18
|
+
const {
|
|
19
|
+
end,
|
|
20
|
+
closed,
|
|
21
|
+
terminated
|
|
22
|
+
} = (0, _scanQuery.scanQuery)(text);
|
|
23
|
+
if (terminated) {
|
|
24
|
+
return {
|
|
25
|
+
complete: true,
|
|
26
|
+
waitingFor: ''
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
if (end.quote) {
|
|
30
|
+
return {
|
|
31
|
+
complete: false,
|
|
32
|
+
waitingFor: end.quote
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
if (end.blockComment) {
|
|
36
|
+
return {
|
|
37
|
+
complete: false,
|
|
38
|
+
waitingFor: '/*'
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
if (!closed) {
|
|
42
|
+
return {
|
|
43
|
+
complete: false,
|
|
44
|
+
waitingFor: end.brackets[end.brackets.length - 1]
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
return {
|
|
48
|
+
complete: false,
|
|
49
|
+
waitingFor: ';'
|
|
50
|
+
};
|
|
51
|
+
};
|
|
52
|
+
exports.queryStatus = queryStatus;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.queryStatus=void 0;var _scanQuery=require("../utilities/parsers/scanQuery");const queryStatus=(e="")=>{const{end:t,closed:r,terminated:o}=(0,_scanQuery.scanQuery)(e);return o?{complete:!0,waitingFor:""}:t.quote?{complete:!1,waitingFor:t.quote}:t.blockComment?{complete:!1,waitingFor:"/*"}:r?{complete:!1,waitingFor:";"}:{complete:!1,waitingFor:t.brackets[t.brackets.length-1]}};exports.queryStatus=queryStatus;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Write a value as a quoted string for a query, so any text can be used in one: the backslash and the quote are
|
|
3
|
+
* escaped, so the parser reads back exactly the text given.
|
|
4
|
+
* @example
|
|
5
|
+
* query(`insert exercises values name = ${quote("Farmer's walk")}`)
|
|
6
|
+
* // insert exercises values name = 'Farmer\'s walk'
|
|
7
|
+
* @param value - the text to quote (anything else is converted with String first)
|
|
8
|
+
* @param quoteChar - the quote to wrap it in
|
|
9
|
+
*/
|
|
10
|
+
export declare const quote: (value: any, quoteChar?: "'" | "\"") => string;
|
|
11
|
+
//# sourceMappingURL=quote.d.ts.map
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.quote = void 0;
|
|
7
|
+
/**
|
|
8
|
+
* Write a value as a quoted string for a query, so any text can be used in one: the backslash and the quote are
|
|
9
|
+
* escaped, so the parser reads back exactly the text given.
|
|
10
|
+
* @example
|
|
11
|
+
* query(`insert exercises values name = ${quote("Farmer's walk")}`)
|
|
12
|
+
* // insert exercises values name = 'Farmer\'s walk'
|
|
13
|
+
* @param value - the text to quote (anything else is converted with String first)
|
|
14
|
+
* @param quoteChar - the quote to wrap it in
|
|
15
|
+
*/
|
|
16
|
+
const quote = (value, quoteChar = '\'') => `${quoteChar}${String(value).replace(/\\/g, '\\\\').split(quoteChar).join(`\\${quoteChar}`)}${quoteChar}`;
|
|
17
|
+
exports.quote = quote;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.quote=void 0;const quote=(e,t="'")=>`${t}${String(e).replace(/\\/g,"\\\\").split(t).join(`\\${t}`)}${t}`;exports.quote=quote;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Run `show records`: the name of every record in the __RECORDS registry, in the order they were created. A database
|
|
3
|
+
* with no records yet (no registry file) gives an empty list.
|
|
4
|
+
*/
|
|
5
|
+
export declare const showRecords: () => Promise<string[]>;
|
|
6
|
+
//# sourceMappingURL=showRecords.d.ts.map
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.showRecords = void 0;
|
|
7
|
+
require("core-js/modules/esnext.iterator.constructor.js");
|
|
8
|
+
require("core-js/modules/esnext.iterator.map.js");
|
|
9
|
+
var _retrieveRecord = require("../utilities/retrieveRecord");
|
|
10
|
+
var _recordExists = require("../utilities/recordExists");
|
|
11
|
+
/**
|
|
12
|
+
* Run `show records`: the name of every record in the __RECORDS registry, in the order they were created. A database
|
|
13
|
+
* with no records yet (no registry file) gives an empty list.
|
|
14
|
+
*/
|
|
15
|
+
const showRecords = async () => {
|
|
16
|
+
if (!(0, _recordExists.recordExists)('__RECORDS')) {
|
|
17
|
+
return [];
|
|
18
|
+
}
|
|
19
|
+
return (await (0, _retrieveRecord.retrieveRecord)('__RECORDS')).entries.map(fileName => fileName.replace(/\.json$/, ''));
|
|
20
|
+
};
|
|
21
|
+
exports.showRecords = showRecords;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.showRecords=void 0,require("core-js/modules/esnext.iterator.constructor.js"),require("core-js/modules/esnext.iterator.map.js");var _retrieveRecord=require("../utilities/retrieveRecord"),_recordExists=require("../utilities/recordExists");const showRecords=async()=>(0,_recordExists.recordExists)("__RECORDS")?(await(0,_retrieveRecord.retrieveRecord)("__RECORDS")).entries.map((e=>e.replace(/\.json$/,""))):[];exports.showRecords=showRecords;
|
|
@@ -12,6 +12,9 @@ const sortBy = (dataSet = [], sortClauses = []) => {
|
|
|
12
12
|
for (const sortClause of sortClauses) {
|
|
13
13
|
const property = sortClause.property;
|
|
14
14
|
const direction = sortClause.direction;
|
|
15
|
+
// 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.
|
|
17
|
+
// eslint-disable-next-line eqeqeq
|
|
15
18
|
if (a[property] == b[property]) {
|
|
16
19
|
continue;
|
|
17
20
|
}
|
|
@@ -14,6 +14,8 @@ var _retrieveFile = require("../utilities/retrieveFile");
|
|
|
14
14
|
var _updateIndex = require("../utilities/updateIndex");
|
|
15
15
|
var _removeFromIndex = require("../utilities/removeFromIndex");
|
|
16
16
|
var _computeKeyValue = require("../utilities/computeKeyValue");
|
|
17
|
+
var _fieldType = require("../utilities/fieldType");
|
|
18
|
+
var _compareAs = require("../utilities/compareAs");
|
|
17
19
|
var _keyUnique = require("../introspect/policies/keyUnique");
|
|
18
20
|
var _typeCheck = require("../introspect/policies/typeCheck");
|
|
19
21
|
var _foreignKeyExists = require("../introspect/policies/foreignKeyExists");
|
|
@@ -34,7 +36,7 @@ const {
|
|
|
34
36
|
const findEntryFile = async (record, primaryKey, primaryValue) => {
|
|
35
37
|
for (const fileName of record.entries) {
|
|
36
38
|
const data = await (0, _retrieveFile.retrieveFile)(`${record.path}/${fileName}`);
|
|
37
|
-
if ((0, _computeKeyValue.computeKeyValue)(primaryKey.fields, data)
|
|
39
|
+
if ((0, _compareAs.sameValue)((0, _fieldType.keyValueType)(record, primaryKey), (0, _computeKeyValue.computeKeyValue)(primaryKey.fields, data), primaryValue)) {
|
|
38
40
|
return fileName;
|
|
39
41
|
}
|
|
40
42
|
}
|
|
@@ -43,15 +45,17 @@ const findEntryFile = async (record, primaryKey, primaryValue) => {
|
|
|
43
45
|
/**
|
|
44
46
|
* True if the update actually changes at least one of this key's fields - a key none of whose
|
|
45
47
|
* fields appear in newValues, or whose given values are all unchanged, doesn't need re-checking.
|
|
48
|
+
* @param record
|
|
46
49
|
* @param key
|
|
47
50
|
* @param oldData
|
|
48
51
|
* @param newValues
|
|
49
52
|
*/
|
|
50
|
-
const keyFieldsChanged = (key, oldData, newValues) => key.fields.some(field => Object.prototype.hasOwnProperty.call(newValues, field) && newValues[field]
|
|
53
|
+
const keyFieldsChanged = (record, key, oldData, newValues) => key.fields.some(field => Object.prototype.hasOwnProperty.call(newValues, field) && !(0, _compareAs.sameValue)((0, _fieldType.fieldType)(record, field), newValues[field], oldData[field]));
|
|
51
54
|
/**
|
|
52
55
|
* Only check uniqueness for a primary/unique key (single-field, or composite) whose value is
|
|
53
56
|
* actually changing - checking an unchanged value would find the record's own existing entry and
|
|
54
|
-
* wrongly report it as a duplicate of itself.
|
|
57
|
+
* wrongly report it as a duplicate of itself. A value with a null in it is never a duplicate (as in
|
|
58
|
+
* SQL).
|
|
55
59
|
* @param entity
|
|
56
60
|
* @param record
|
|
57
61
|
* @param oldData
|
|
@@ -60,10 +64,13 @@ const keyFieldsChanged = (key, oldData, newValues) => key.fields.some(field => O
|
|
|
60
64
|
*/
|
|
61
65
|
const assertUniqueForUpdate = async (entity, record, oldData, newData, newValues) => {
|
|
62
66
|
for (const key of record.keys) {
|
|
63
|
-
if (key.type !== 'primary' && key.type !== 'unique' || !keyFieldsChanged(key, oldData, newValues)) {
|
|
67
|
+
if (key.type !== 'primary' && key.type !== 'unique' || !keyFieldsChanged(record, key, oldData, newValues)) {
|
|
64
68
|
continue;
|
|
65
69
|
}
|
|
66
70
|
const checkKeys = key.fields.map(field => newData[field]);
|
|
71
|
+
if (checkKeys.some(value => value === null)) {
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
67
74
|
const isUnique = await (0, _keyUnique.keyUnique)(entity, key.fields, checkKeys);
|
|
68
75
|
if (!isUnique) {
|
|
69
76
|
throw new Error(`Duplicate value for ${key.type} key "${key.fields.join(', ')}" on ${entity}`);
|
|
@@ -73,7 +80,8 @@ const assertUniqueForUpdate = async (entity, record, oldData, newData, newValues
|
|
|
73
80
|
/**
|
|
74
81
|
* Same "only if it actually changed" scoping as assertUniqueForUpdate: re-checking an unchanged
|
|
75
82
|
* foreign key value would re-validate a reference this update never touches, which could fail an
|
|
76
|
-
* unrelated field's update if that parent record happened to be deleted since.
|
|
83
|
+
* unrelated field's update if that parent record happened to be deleted since. A null references
|
|
84
|
+
* nothing, so it isn't checked.
|
|
77
85
|
* @param entity
|
|
78
86
|
* @param record
|
|
79
87
|
* @param oldData
|
|
@@ -85,7 +93,7 @@ const assertForeignKeysForUpdate = async (entity, record, oldData, newValues) =>
|
|
|
85
93
|
continue;
|
|
86
94
|
}
|
|
87
95
|
const field = key.fields[0];
|
|
88
|
-
if (!Object.prototype.hasOwnProperty.call(newValues, field) || newValues[field]
|
|
96
|
+
if (!Object.prototype.hasOwnProperty.call(newValues, field) || newValues[field] === null || (0, _compareAs.sameValue)((0, _fieldType.fieldType)(record, field), newValues[field], oldData[field])) {
|
|
89
97
|
continue;
|
|
90
98
|
}
|
|
91
99
|
const {
|
|
@@ -104,12 +112,13 @@ const assertForeignKeysForUpdate = async (entity, record, oldData, newValues) =>
|
|
|
104
112
|
* dangling, since the row itself isn't gone, just re-keyed. Only meaningful for a single-field
|
|
105
113
|
* primary key, matching foreign key support's own single-field-only scope.
|
|
106
114
|
* @param entity
|
|
115
|
+
* @param record
|
|
107
116
|
* @param primaryKey
|
|
108
117
|
* @param oldData
|
|
109
118
|
* @param newValues
|
|
110
119
|
*/
|
|
111
|
-
const assertNoDependentsForUpdate = async (entity, primaryKey, oldData, newValues) => {
|
|
112
|
-
if (!(0, _config.getSetting)('enforceForeignKeys', true) || primaryKey.fields.length !== 1 || !keyFieldsChanged(primaryKey, oldData, newValues)) {
|
|
120
|
+
const assertNoDependentsForUpdate = async (entity, record, primaryKey, oldData, newValues) => {
|
|
121
|
+
if (!(0, _config.getSetting)('enforceForeignKeys', true) || primaryKey.fields.length !== 1 || !keyFieldsChanged(record, primaryKey, oldData, newValues)) {
|
|
113
122
|
return;
|
|
114
123
|
}
|
|
115
124
|
const property = primaryKey.fields[0];
|
|
@@ -128,11 +137,41 @@ const assertNoDependentsForUpdate = async (entity, primaryKey, oldData, newValue
|
|
|
128
137
|
*/
|
|
129
138
|
const reconcileIndexes = async (record, oldData, newData, newValues, fileName) => {
|
|
130
139
|
for (const key of record.keys) {
|
|
131
|
-
if (!keyFieldsChanged(key, oldData, newValues)) {
|
|
140
|
+
if (!keyFieldsChanged(record, key, oldData, newValues)) {
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
await (0, _removeFromIndex.removeFromIndex)(record.path, key, (0, _computeKeyValue.computeKeyValue)(key.fields, oldData), fileName, (0, _fieldType.keyValueType)(record, key));
|
|
144
|
+
await (0, _updateIndex.updateIndex)(record.path, key, (0, _computeKeyValue.computeKeyValue)(key.fields, newData), fileName, (0, _fieldType.keyValueType)(record, key));
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
/**
|
|
148
|
+
* The rows of one update are checked before any is written, so a check can't see the batch's own new values on disk.
|
|
149
|
+
* Reject the update if two of its rows would end with the same value for a primary or unique key that it changes (a
|
|
150
|
+
* value with a null in it is never a duplicate, as in assertUniqueForUpdate).
|
|
151
|
+
* @param entity
|
|
152
|
+
* @param record
|
|
153
|
+
* @param planned
|
|
154
|
+
* @param newValues
|
|
155
|
+
*/
|
|
156
|
+
const assertUniqueWithinUpdate = (entity, record, planned, newValues) => {
|
|
157
|
+
for (const key of record.keys) {
|
|
158
|
+
if (key.type !== 'primary' && key.type !== 'unique') {
|
|
132
159
|
continue;
|
|
133
160
|
}
|
|
134
|
-
|
|
135
|
-
|
|
161
|
+
const claimed = [];
|
|
162
|
+
for (const {
|
|
163
|
+
oldData,
|
|
164
|
+
newData
|
|
165
|
+
} of planned) {
|
|
166
|
+
if (!keyFieldsChanged(record, key, oldData, newValues) || key.fields.some(field => newData[field] === null)) {
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
const value = (0, _computeKeyValue.computeKeyValue)(key.fields, newData);
|
|
170
|
+
if (claimed.some(other => (0, _compareAs.sameValue)((0, _fieldType.keyValueType)(record, key), other, value))) {
|
|
171
|
+
throw new Error(`Duplicate value for ${key.type} key "${key.fields.join(', ')}" on ${entity}`);
|
|
172
|
+
}
|
|
173
|
+
claimed.push(value);
|
|
174
|
+
}
|
|
136
175
|
}
|
|
137
176
|
};
|
|
138
177
|
const assertRequiredFields = (entity, record, newData) => {
|
|
@@ -154,7 +193,7 @@ const applyFieldTypes = (record, values) => {
|
|
|
154
193
|
const typedValues = Object.assign({}, values);
|
|
155
194
|
for (const field of record.definition) {
|
|
156
195
|
if (Object.prototype.hasOwnProperty.call(typedValues, field.name)) {
|
|
157
|
-
typedValues[field.name] = (0, _typeCheck.typeCheck)(field.type, typedValues[field.name]);
|
|
196
|
+
typedValues[field.name] = (0, _typeCheck.typeCheck)(field.type, typedValues[field.name], field.nullable);
|
|
158
197
|
}
|
|
159
198
|
}
|
|
160
199
|
return typedValues;
|
|
@@ -167,24 +206,41 @@ const updateEntity = async (entity = '', values = {}, dataSet = {}) => {
|
|
|
167
206
|
throw new Error(`Cannot update "${entity}" without a primary key`);
|
|
168
207
|
}
|
|
169
208
|
const typedValues = applyFieldTypes(record, values);
|
|
170
|
-
|
|
171
|
-
const
|
|
172
|
-
for (const row of
|
|
209
|
+
// Check every row first and write only once all of them pass, so a row that fails leaves the others unchanged too
|
|
210
|
+
const planned = [];
|
|
211
|
+
for (const row of dataSet[entity] || []) {
|
|
173
212
|
const fileName = await findEntryFile(record, primaryKey, (0, _computeKeyValue.computeKeyValue)(primaryKey.fields, row));
|
|
174
213
|
if (!fileName) {
|
|
175
214
|
throw new Error(`Could not find entry for ${entity} where "${primaryKey.fields.join(', ')}" = ${(0, _computeKeyValue.computeKeyValue)(primaryKey.fields, row)}`);
|
|
176
215
|
}
|
|
216
|
+
if (planned.some(other => other.fileName === fileName)) {
|
|
217
|
+
// The same entry given twice is one row: updating it once is enough, and it can't collide with itself
|
|
218
|
+
continue;
|
|
219
|
+
}
|
|
177
220
|
const oldData = await (0, _retrieveFile.retrieveFile)(`${record.path}/${fileName}`);
|
|
178
221
|
const newData = Object.assign({}, oldData, typedValues);
|
|
179
222
|
await assertUniqueForUpdate(entity, record, oldData, newData, typedValues);
|
|
180
223
|
await assertForeignKeysForUpdate(entity, record, oldData, typedValues);
|
|
181
|
-
await assertNoDependentsForUpdate(entity, primaryKey, oldData, typedValues);
|
|
224
|
+
await assertNoDependentsForUpdate(entity, record, primaryKey, oldData, typedValues);
|
|
182
225
|
assertRequiredFields(entity, record, newData);
|
|
226
|
+
planned.push({
|
|
227
|
+
fileName,
|
|
228
|
+
oldData,
|
|
229
|
+
newData
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
assertUniqueWithinUpdate(entity, record, planned, typedValues);
|
|
233
|
+
for (const {
|
|
234
|
+
fileName,
|
|
235
|
+
oldData,
|
|
236
|
+
newData
|
|
237
|
+
} of planned) {
|
|
183
238
|
await reconcileIndexes(record, oldData, newData, typedValues, fileName);
|
|
184
239
|
await writeFile(`${databasePath}${record.path}/${fileName}`, JSON.stringify(newData, null, 2));
|
|
185
|
-
updatedRows.push(newData);
|
|
186
240
|
}
|
|
187
|
-
dataSet[entity] =
|
|
241
|
+
dataSet[entity] = planned.map(({
|
|
242
|
+
newData
|
|
243
|
+
}) => newData);
|
|
188
244
|
return dataSet;
|
|
189
245
|
};
|
|
190
246
|
exports.updateEntity = updateEntity;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.updateEntity=void 0,require("core-js/modules/es.json.stringify.js"),require("core-js/modules/esnext.iterator.constructor.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");var _retrieveRecord=require("../utilities/retrieveRecord"),_retrieveFile=require("../utilities/retrieveFile"),_updateIndex=require("../utilities/updateIndex"),_removeFromIndex=require("../utilities/removeFromIndex"),_computeKeyValue=require("../utilities/computeKeyValue"),_keyUnique=require("../introspect/policies/keyUnique"),_typeCheck=require("../introspect/policies/typeCheck"),_foreignKeyExists=require("../introspect/policies/foreignKeyExists"),_hasDependents=require("../introspect/policies/hasDependents"),_splitEntityProperty=require("../utilities/parsers/splitEntityProperty"),_config=require("../utilities/config");const{writeFile:writeFile}=require("fs/promises"),findEntryFile=async(e,t,
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.updateEntity=void 0,require("core-js/modules/es.json.stringify.js"),require("core-js/modules/esnext.iterator.constructor.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");var _retrieveRecord=require("../utilities/retrieveRecord"),_retrieveFile=require("../utilities/retrieveFile"),_updateIndex=require("../utilities/updateIndex"),_removeFromIndex=require("../utilities/removeFromIndex"),_computeKeyValue=require("../utilities/computeKeyValue"),_fieldType=require("../utilities/fieldType"),_compareAs=require("../utilities/compareAs"),_keyUnique=require("../introspect/policies/keyUnique"),_typeCheck=require("../introspect/policies/typeCheck"),_foreignKeyExists=require("../introspect/policies/foreignKeyExists"),_hasDependents=require("../introspect/policies/hasDependents"),_splitEntityProperty=require("../utilities/parsers/splitEntityProperty"),_config=require("../utilities/config");const{writeFile:writeFile}=require("fs/promises"),findEntryFile=async(e,t,i)=>{for(const r of e.entries){const o=await(0,_retrieveFile.retrieveFile)(`${e.path}/${r}`);if((0,_compareAs.sameValue)((0,_fieldType.keyValueType)(e,t),(0,_computeKeyValue.computeKeyValue)(t.fields,o),i))return r}return null},keyFieldsChanged=(e,t,i,r)=>t.fields.some((t=>Object.prototype.hasOwnProperty.call(r,t)&&!(0,_compareAs.sameValue)((0,_fieldType.fieldType)(e,t),r[t],i[t]))),assertUniqueForUpdate=async(e,t,i,r,o)=>{for(const s of t.keys){if("primary"!==s.type&&"unique"!==s.type||!keyFieldsChanged(t,s,i,o))continue;const n=s.fields.map((e=>r[e]));if(n.some((e=>null===e)))continue;if(!await(0,_keyUnique.keyUnique)(e,s.fields,n))throw new Error(`Duplicate value for ${s.type} key "${s.fields.join(", ")}" on ${e}`)}},assertForeignKeysForUpdate=async(e,t,i,r)=>{for(const o of t.keys){if("foreign"!==o.type||1!==o.fields.length||!o.references||1!==o.references.length)continue;const s=o.fields[0];if(!Object.prototype.hasOwnProperty.call(r,s)||null===r[s]||(0,_compareAs.sameValue)((0,_fieldType.fieldType)(t,s),r[s],i[s]))continue;const{entity:n,property:a}=(0,_splitEntityProperty.splitEntityProperty)(o.references[0].fields[0]);if(!await(0,_foreignKeyExists.foreignKeyExists)(n,a,r[s]))throw new Error(`Foreign key "${s}" on ${e} references a nonexistent ${n}.${a} = ${r[s]}`)}},assertNoDependentsForUpdate=async(e,t,i,r,o)=>{if(!(0,_config.getSetting)("enforceForeignKeys",!0)||1!==i.fields.length||!keyFieldsChanged(t,i,r,o))return;const s=i.fields[0];if(await(0,_hasDependents.hasDependents)(e,s,r[s]))throw new Error(`Cannot update "${e}" where "${s}" = ${r[s]} - other records still reference it`)},reconcileIndexes=async(e,t,i,r,o)=>{for(const s of e.keys)keyFieldsChanged(e,s,t,r)&&(await(0,_removeFromIndex.removeFromIndex)(e.path,s,(0,_computeKeyValue.computeKeyValue)(s.fields,t),o,(0,_fieldType.keyValueType)(e,s)),await(0,_updateIndex.updateIndex)(e.path,s,(0,_computeKeyValue.computeKeyValue)(s.fields,i),o,(0,_fieldType.keyValueType)(e,s)))},assertUniqueWithinUpdate=(e,t,i,r)=>{for(const o of t.keys){if("primary"!==o.type&&"unique"!==o.type)continue;const s=[];for(const{oldData:n,newData:a}of i){if(!keyFieldsChanged(t,o,n,r)||o.fields.some((e=>null===a[e])))continue;const i=(0,_computeKeyValue.computeKeyValue)(o.fields,a);if(s.some((e=>(0,_compareAs.sameValue)((0,_fieldType.keyValueType)(t,o),e,i))))throw new Error(`Duplicate value for ${o.type} key "${o.fields.join(", ")}" on ${e}`);s.push(i)}}},assertRequiredFields=(e,t,i)=>{for(const r of t.definition)if(!r.optional&&void 0===i[r.name])throw new Error(`Missing required field "${r.name}" for ${e}`)},applyFieldTypes=(e,t)=>{const i=Object.assign({},t);for(const t of e.definition)Object.prototype.hasOwnProperty.call(i,t.name)&&(i[t.name]=(0,_typeCheck.typeCheck)(t.type,i[t.name],t.nullable));return i},updateEntity=async(e="",t={},i={})=>{const r=(0,_config.getSetting)("databasePath","database/"),o=await(0,_retrieveRecord.retrieveRecord)(e),s=o.keys.find((e=>"primary"===e.type));if(!s)throw new Error(`Cannot update "${e}" without a primary key`);const n=applyFieldTypes(o,t),a=[];for(const t of i[e]||[]){const i=await findEntryFile(o,s,(0,_computeKeyValue.computeKeyValue)(s.fields,t));if(!i)throw new Error(`Could not find entry for ${e} where "${s.fields.join(", ")}" = ${(0,_computeKeyValue.computeKeyValue)(s.fields,t)}`);if(a.some((e=>e.fileName===i)))continue;const r=await(0,_retrieveFile.retrieveFile)(`${o.path}/${i}`),l=Object.assign({},r,n);await assertUniqueForUpdate(e,o,r,l,n),await assertForeignKeysForUpdate(e,o,r,n),await assertNoDependentsForUpdate(e,o,s,r,n),assertRequiredFields(e,o,l),a.push({fileName:i,oldData:r,newData:l})}assertUniqueWithinUpdate(e,o,a,n);for(const{fileName:e,oldData:t,newData:i}of a)await reconcileIndexes(o,t,i,n,e),await writeFile(`${r}${o.path}/${e}`,JSON.stringify(i,null,2));return i[e]=a.map((({newData:e})=>e)),i};exports.updateEntity=updateEntity;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
export declare const where: (dataSet?: any[], { property, joinEntity, comparator, value }?: {
|
|
1
|
+
export declare const where: (dataSet?: any[], { property, joinEntity, comparator, value, type }?: {
|
|
2
2
|
property?: string;
|
|
3
3
|
joinEntity?: any;
|
|
4
4
|
comparator?: string;
|
|
5
5
|
value?: any;
|
|
6
|
+
type?: any;
|
|
6
7
|
}) => any[];
|
|
7
8
|
//# sourceMappingURL=where.d.ts.map
|
|
@@ -7,6 +7,8 @@ exports.where = void 0;
|
|
|
7
7
|
require("core-js/modules/es.array.includes.js");
|
|
8
8
|
require("core-js/modules/esnext.iterator.constructor.js");
|
|
9
9
|
require("core-js/modules/esnext.iterator.filter.js");
|
|
10
|
+
require("core-js/modules/esnext.iterator.some.js");
|
|
11
|
+
var _compareAs = require("../utilities/compareAs");
|
|
10
12
|
const likeMatch = (dataValue, pattern) => {
|
|
11
13
|
if (typeof dataValue !== 'string' || typeof pattern !== 'string') {
|
|
12
14
|
return false;
|
|
@@ -14,17 +16,26 @@ const likeMatch = (dataValue, pattern) => {
|
|
|
14
16
|
const regexPattern = pattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&').replace(/%/g, '.*').replace(/_/g, '.');
|
|
15
17
|
return new RegExp(`^${regexPattern}$`, 'i').test(dataValue);
|
|
16
18
|
};
|
|
17
|
-
|
|
19
|
+
/**
|
|
20
|
+
* Equality (=, !=, <>, in) compares by the field's declared type when given one (see sameValue), so
|
|
21
|
+
* '3' and 3 match in a string or int field. Ordering (>, <, between, like) compares the raw values.
|
|
22
|
+
* @param data
|
|
23
|
+
* @param property
|
|
24
|
+
* @param comparator
|
|
25
|
+
* @param value
|
|
26
|
+
* @param type
|
|
27
|
+
*/
|
|
28
|
+
const makeCondition = (data, property, comparator, value, type = undefined) => {
|
|
18
29
|
if (!Object.prototype.hasOwnProperty.call(data, property)) {
|
|
19
30
|
return false;
|
|
20
31
|
}
|
|
21
32
|
const dataValue = data[property];
|
|
22
33
|
switch (comparator) {
|
|
23
34
|
case '=':
|
|
24
|
-
return dataValue
|
|
35
|
+
return (0, _compareAs.sameValue)(type, dataValue, value);
|
|
25
36
|
case '!=':
|
|
26
37
|
case '<>':
|
|
27
|
-
return dataValue
|
|
38
|
+
return !(0, _compareAs.sameValue)(type, dataValue, value);
|
|
28
39
|
case '>':
|
|
29
40
|
return dataValue > value;
|
|
30
41
|
case '>=':
|
|
@@ -34,7 +45,7 @@ const makeCondition = (data, property, comparator, value) => {
|
|
|
34
45
|
case '<=':
|
|
35
46
|
return dataValue <= value;
|
|
36
47
|
case 'in':
|
|
37
|
-
return Array.isArray(value) && value.includes(dataValue);
|
|
48
|
+
return Array.isArray(value) && (type ? value.some(item => (0, _compareAs.sameValue)(type, dataValue, item)) : value.includes(dataValue));
|
|
38
49
|
case 'between':
|
|
39
50
|
return Array.isArray(value) && value.length === 2 && dataValue >= value[0] && dataValue <= value[1];
|
|
40
51
|
case 'like':
|
|
@@ -46,11 +57,12 @@ const where = (dataSet = [], {
|
|
|
46
57
|
property = '',
|
|
47
58
|
joinEntity = undefined,
|
|
48
59
|
comparator = '=',
|
|
49
|
-
value = null
|
|
60
|
+
value = null,
|
|
61
|
+
type = undefined
|
|
50
62
|
} = {}) => {
|
|
51
63
|
return dataSet.filter(data => {
|
|
52
64
|
const testProperty = joinEntity ? `${joinEntity}.${property}` : property;
|
|
53
|
-
return makeCondition(data, testProperty, comparator, value);
|
|
65
|
+
return makeCondition(data, testProperty, comparator, value, type);
|
|
54
66
|
});
|
|
55
67
|
};
|
|
56
68
|
exports.where = where;
|
|
@@ -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");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)=>{if(!Object.prototype.hasOwnProperty.call(e,r))return!1;const
|
|
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,a=void 0)=>{if(!Object.prototype.hasOwnProperty.call(e,r))return!1;const o=e[r];switch(t){case"=":return(0,_compareAs.sameValue)(a,o,s);case"!=":case"<>":return!(0,_compareAs.sameValue)(a,o,s);case">":return o>s;case">=":return o>=s;case"<":return o<s;case"<=":return o<=s;case"in":return Array.isArray(s)&&(a?s.some((e=>(0,_compareAs.sameValue)(a,o,e))):s.includes(o));case"between":return Array.isArray(s)&&2===s.length&&o>=s[0]&&o<=s[1];case"like":return likeMatch(o,s)}return!1},where=(e=[],{property:r="",joinEntity:t,comparator:s="=",value:a=null,type:o}={})=>e.filter((e=>makeCondition(e,t?`${t}.${r}`:r,s,a,o)));exports.where=where;
|