json-fs-query 1.0.6 → 1.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.
Files changed (42) hide show
  1. package/browser/jsonFsQuery.js +518 -361
  2. package/browser/jsonFsQuery.min.js +1 -1
  3. package/dist/data/introspect/policies/foreignKeyExists.js +28 -24
  4. package/dist/data/introspect/policies/foreignKeyExists.min.js +1 -1
  5. package/dist/data/introspect/policies/hasDependents.js +53 -51
  6. package/dist/data/introspect/policies/hasDependents.min.js +1 -1
  7. package/dist/data/introspect/policies/keyUnique.js +34 -30
  8. package/dist/data/introspect/policies/keyUnique.min.js +1 -1
  9. package/dist/data/queries/deleteEntity.js +100 -99
  10. package/dist/data/queries/deleteEntity.min.js +1 -1
  11. package/dist/data/queries/insertEntity.js +117 -116
  12. package/dist/data/queries/insertEntity.min.js +1 -1
  13. package/dist/data/queries/sortBy.js +27 -24
  14. package/dist/data/queries/updateEntity.js +193 -189
  15. package/dist/data/queries/updateEntity.min.js +1 -1
  16. package/dist/data/queries/where.d.ts +7 -6
  17. package/dist/data/queries/where.js +67 -55
  18. package/dist/data/queries/where.min.js +1 -1
  19. package/dist/data/utilities/compareAs.d.ts +20 -0
  20. package/dist/data/utilities/compareAs.js +58 -0
  21. package/dist/data/utilities/compareAs.min.js +1 -0
  22. package/dist/data/utilities/fieldType.d.ts +16 -0
  23. package/dist/data/utilities/fieldType.js +23 -0
  24. package/dist/data/utilities/fieldType.min.js +1 -0
  25. package/dist/data/utilities/getConditionIndexedList.js +47 -46
  26. package/dist/data/utilities/getConditionIndexedList.min.js +1 -1
  27. package/dist/data/utilities/matchIndexedJoins.js +62 -60
  28. package/dist/data/utilities/matchIndexedJoins.min.js +1 -1
  29. package/dist/data/utilities/mergeJoin.js +47 -43
  30. package/dist/data/utilities/mergeJoin.min.js +1 -1
  31. package/dist/data/utilities/parsers/findConditions.d.ts +15 -14
  32. package/dist/data/utilities/removeFromIndex.d.ts +14 -13
  33. package/dist/data/utilities/removeFromIndex.js +45 -43
  34. package/dist/data/utilities/removeFromIndex.min.js +1 -1
  35. package/dist/data/utilities/runQuery.js +112 -86
  36. package/dist/data/utilities/runQuery.min.js +1 -1
  37. package/dist/data/utilities/updateIndex.d.ts +17 -16
  38. package/dist/data/utilities/updateIndex.js +57 -55
  39. package/dist/data/utilities/updateIndex.min.js +1 -1
  40. package/dist/data/utilities/useJoinClause.js +56 -50
  41. package/dist/data/utilities/useJoinClause.min.js +1 -1
  42. package/package.json +6 -1
@@ -1,190 +1,194 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.updateEntity = void 0;
7
- require("core-js/modules/es.json.stringify.js");
8
- require("core-js/modules/esnext.iterator.constructor.js");
9
- require("core-js/modules/esnext.iterator.find.js");
10
- require("core-js/modules/esnext.iterator.map.js");
11
- require("core-js/modules/esnext.iterator.some.js");
12
- var _retrieveRecord = require("../utilities/retrieveRecord");
13
- var _retrieveFile = require("../utilities/retrieveFile");
14
- var _updateIndex = require("../utilities/updateIndex");
15
- var _removeFromIndex = require("../utilities/removeFromIndex");
16
- var _computeKeyValue = require("../utilities/computeKeyValue");
17
- var _keyUnique = require("../introspect/policies/keyUnique");
18
- var _typeCheck = require("../introspect/policies/typeCheck");
19
- var _foreignKeyExists = require("../introspect/policies/foreignKeyExists");
20
- var _hasDependents = require("../introspect/policies/hasDependents");
21
- var _splitEntityProperty = require("../utilities/parsers/splitEntityProperty");
22
- var _config = require("../utilities/config");
23
- const {
24
- writeFile
25
- } = require('fs/promises');
26
- /**
27
- * Same reasoning as deleteEntity: a record object carries no memory of which entry file it came
28
- * from, so re-scan entries[] and match by the record's own primary key value(s) - a single field,
29
- * or a composite of several for a multi-field primary key.
30
- * @param record
31
- * @param primaryKey
32
- * @param primaryValue
33
- */
34
- const findEntryFile = async (record, primaryKey, primaryValue) => {
35
- for (const fileName of record.entries) {
36
- const data = await (0, _retrieveFile.retrieveFile)(`${record.path}/${fileName}`);
37
- if ((0, _computeKeyValue.computeKeyValue)(primaryKey.fields, data) == primaryValue) {
38
- return fileName;
39
- }
40
- }
41
- return null;
42
- };
43
- /**
44
- * True if the update actually changes at least one of this key's fields - a key none of whose
45
- * fields appear in newValues, or whose given values are all unchanged, doesn't need re-checking.
46
- * @param key
47
- * @param oldData
48
- * @param newValues
49
- */
50
- const keyFieldsChanged = (key, oldData, newValues) => key.fields.some(field => Object.prototype.hasOwnProperty.call(newValues, field) && newValues[field] != oldData[field]);
51
- /**
52
- * Only check uniqueness for a primary/unique key (single-field, or composite) whose value is
53
- * actually changing - checking an unchanged value would find the record's own existing entry and
54
- * wrongly report it as a duplicate of itself.
55
- * @param entity
56
- * @param record
57
- * @param oldData
58
- * @param newData
59
- * @param newValues
60
- */
61
- const assertUniqueForUpdate = async (entity, record, oldData, newData, newValues) => {
62
- for (const key of record.keys) {
63
- if (key.type !== 'primary' && key.type !== 'unique' || !keyFieldsChanged(key, oldData, newValues)) {
64
- continue;
65
- }
66
- const checkKeys = key.fields.map(field => newData[field]);
67
- const isUnique = await (0, _keyUnique.keyUnique)(entity, key.fields, checkKeys);
68
- if (!isUnique) {
69
- throw new Error(`Duplicate value for ${key.type} key "${key.fields.join(', ')}" on ${entity}`);
70
- }
71
- }
72
- };
73
- /**
74
- * Same "only if it actually changed" scoping as assertUniqueForUpdate: re-checking an unchanged
75
- * 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.
77
- * @param entity
78
- * @param record
79
- * @param oldData
80
- * @param newValues
81
- */
82
- const assertForeignKeysForUpdate = async (entity, record, oldData, newValues) => {
83
- for (const key of record.keys) {
84
- if (key.type !== 'foreign' || key.fields.length !== 1 || !key.references || key.references.length !== 1) {
85
- continue;
86
- }
87
- const field = key.fields[0];
88
- if (!Object.prototype.hasOwnProperty.call(newValues, field) || newValues[field] == oldData[field]) {
89
- continue;
90
- }
91
- const {
92
- entity: refEntity,
93
- property: refProperty
94
- } = (0, _splitEntityProperty.splitEntityProperty)(key.references[0].fields[0]);
95
- const exists = await (0, _foreignKeyExists.foreignKeyExists)(refEntity, refProperty, newValues[field]);
96
- if (!exists) {
97
- throw new Error(`Foreign key "${field}" on ${entity} references a nonexistent ${refEntity}.${refProperty} = ${newValues[field]}`);
98
- }
99
- }
100
- };
101
- /**
102
- * RESTRICT: reject an update that would change a single-field primary key's value while another
103
- * entity's foreign key still points at the old one - that reference would otherwise be left
104
- * dangling, since the row itself isn't gone, just re-keyed. Only meaningful for a single-field
105
- * primary key, matching foreign key support's own single-field-only scope.
106
- * @param entity
107
- * @param primaryKey
108
- * @param oldData
109
- * @param newValues
110
- */
111
- const assertNoDependentsForUpdate = async (entity, primaryKey, oldData, newValues) => {
112
- if (!(0, _config.getSetting)('enforceForeignKeys', true) || primaryKey.fields.length !== 1 || !keyFieldsChanged(primaryKey, oldData, newValues)) {
113
- return;
114
- }
115
- const property = primaryKey.fields[0];
116
- if (await (0, _hasDependents.hasDependents)(entity, property, oldData[property])) {
117
- throw new Error(`Cannot update "${entity}" where "${property}" = ${oldData[property]} - other records still reference it`);
118
- }
119
- };
120
- /**
121
- * Move a key's index entry (single-field, or a composite for a multi-field key) from the old
122
- * value to the new one wherever the update actually changed one of its fields.
123
- * @param record
124
- * @param oldData
125
- * @param newData
126
- * @param newValues
127
- * @param fileName
128
- */
129
- const reconcileIndexes = async (record, oldData, newData, newValues, fileName) => {
130
- for (const key of record.keys) {
131
- if (!keyFieldsChanged(key, oldData, newValues)) {
132
- continue;
133
- }
134
- await (0, _removeFromIndex.removeFromIndex)(record.path, key, (0, _computeKeyValue.computeKeyValue)(key.fields, oldData), fileName);
135
- await (0, _updateIndex.updateIndex)(record.path, key, (0, _computeKeyValue.computeKeyValue)(key.fields, newData), fileName);
136
- }
137
- };
138
- const assertRequiredFields = (entity, record, newData) => {
139
- for (const field of record.definition) {
140
- if (!field.optional && typeof newData[field.name] === 'undefined') {
141
- throw new Error(`Missing required field "${field.name}" for ${entity}`);
142
- }
143
- }
144
- };
145
- /**
146
- * Type-check (and coerce) only the fields actually present in an update's values, never the rest
147
- * of the merged record - a field whose already-stored value doesn't match its declared type
148
- * (nothing enforced this before typeCheck() existed) must not block an update that never touches
149
- * that field.
150
- * @param record
151
- * @param values
152
- */
153
- const applyFieldTypes = (record, values) => {
154
- const typedValues = Object.assign({}, values);
155
- for (const field of record.definition) {
156
- if (Object.prototype.hasOwnProperty.call(typedValues, field.name)) {
157
- typedValues[field.name] = (0, _typeCheck.typeCheck)(field.type, typedValues[field.name]);
158
- }
159
- }
160
- return typedValues;
161
- };
162
- const updateEntity = async (entity = '', values = {}, dataSet = {}) => {
163
- const databasePath = (0, _config.getSetting)('databasePath', 'database/');
164
- const record = await (0, _retrieveRecord.retrieveRecord)(entity);
165
- const primaryKey = record.keys.find(key => key.type === 'primary');
166
- if (!primaryKey) {
167
- throw new Error(`Cannot update "${entity}" without a primary key`);
168
- }
169
- const typedValues = applyFieldTypes(record, values);
170
- const toUpdate = dataSet[entity] || [];
171
- const updatedRows = [];
172
- for (const row of toUpdate) {
173
- const fileName = await findEntryFile(record, primaryKey, (0, _computeKeyValue.computeKeyValue)(primaryKey.fields, row));
174
- if (!fileName) {
175
- throw new Error(`Could not find entry for ${entity} where "${primaryKey.fields.join(', ')}" = ${(0, _computeKeyValue.computeKeyValue)(primaryKey.fields, row)}`);
176
- }
177
- const oldData = await (0, _retrieveFile.retrieveFile)(`${record.path}/${fileName}`);
178
- const newData = Object.assign({}, oldData, typedValues);
179
- await assertUniqueForUpdate(entity, record, oldData, newData, typedValues);
180
- await assertForeignKeysForUpdate(entity, record, oldData, typedValues);
181
- await assertNoDependentsForUpdate(entity, primaryKey, oldData, typedValues);
182
- assertRequiredFields(entity, record, newData);
183
- await reconcileIndexes(record, oldData, newData, typedValues, fileName);
184
- await writeFile(`${databasePath}${record.path}/${fileName}`, JSON.stringify(newData, null, 2));
185
- updatedRows.push(newData);
186
- }
187
- dataSet[entity] = updatedRows;
188
- return dataSet;
189
- };
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.updateEntity = void 0;
7
+ require("core-js/modules/es.json.stringify.js");
8
+ require("core-js/modules/esnext.iterator.constructor.js");
9
+ require("core-js/modules/esnext.iterator.find.js");
10
+ require("core-js/modules/esnext.iterator.map.js");
11
+ require("core-js/modules/esnext.iterator.some.js");
12
+ var _retrieveRecord = require("../utilities/retrieveRecord");
13
+ var _retrieveFile = require("../utilities/retrieveFile");
14
+ var _updateIndex = require("../utilities/updateIndex");
15
+ var _removeFromIndex = require("../utilities/removeFromIndex");
16
+ var _computeKeyValue = require("../utilities/computeKeyValue");
17
+ var _fieldType = require("../utilities/fieldType");
18
+ var _compareAs = require("../utilities/compareAs");
19
+ var _keyUnique = require("../introspect/policies/keyUnique");
20
+ var _typeCheck = require("../introspect/policies/typeCheck");
21
+ var _foreignKeyExists = require("../introspect/policies/foreignKeyExists");
22
+ var _hasDependents = require("../introspect/policies/hasDependents");
23
+ var _splitEntityProperty = require("../utilities/parsers/splitEntityProperty");
24
+ var _config = require("../utilities/config");
25
+ const {
26
+ writeFile
27
+ } = require('fs/promises');
28
+ /**
29
+ * Same reasoning as deleteEntity: a record object carries no memory of which entry file it came
30
+ * from, so re-scan entries[] and match by the record's own primary key value(s) - a single field,
31
+ * or a composite of several for a multi-field primary key.
32
+ * @param record
33
+ * @param primaryKey
34
+ * @param primaryValue
35
+ */
36
+ const findEntryFile = async (record, primaryKey, primaryValue) => {
37
+ for (const fileName of record.entries) {
38
+ const data = await (0, _retrieveFile.retrieveFile)(`${record.path}/${fileName}`);
39
+ if ((0, _compareAs.sameValue)((0, _fieldType.keyValueType)(record, primaryKey), (0, _computeKeyValue.computeKeyValue)(primaryKey.fields, data), primaryValue)) {
40
+ return fileName;
41
+ }
42
+ }
43
+ return null;
44
+ };
45
+ /**
46
+ * True if the update actually changes at least one of this key's fields - a key none of whose
47
+ * fields appear in newValues, or whose given values are all unchanged, doesn't need re-checking.
48
+ * @param record
49
+ * @param key
50
+ * @param oldData
51
+ * @param newValues
52
+ */
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]));
54
+ /**
55
+ * Only check uniqueness for a primary/unique key (single-field, or composite) whose value is
56
+ * actually changing - checking an unchanged value would find the record's own existing entry and
57
+ * wrongly report it as a duplicate of itself.
58
+ * @param entity
59
+ * @param record
60
+ * @param oldData
61
+ * @param newData
62
+ * @param newValues
63
+ */
64
+ const assertUniqueForUpdate = async (entity, record, oldData, newData, newValues) => {
65
+ for (const key of record.keys) {
66
+ if (key.type !== 'primary' && key.type !== 'unique' || !keyFieldsChanged(record, key, oldData, newValues)) {
67
+ continue;
68
+ }
69
+ const checkKeys = key.fields.map(field => newData[field]);
70
+ const isUnique = await (0, _keyUnique.keyUnique)(entity, key.fields, checkKeys);
71
+ if (!isUnique) {
72
+ throw new Error(`Duplicate value for ${key.type} key "${key.fields.join(', ')}" on ${entity}`);
73
+ }
74
+ }
75
+ };
76
+ /**
77
+ * Same "only if it actually changed" scoping as assertUniqueForUpdate: re-checking an unchanged
78
+ * foreign key value would re-validate a reference this update never touches, which could fail an
79
+ * unrelated field's update if that parent record happened to be deleted since.
80
+ * @param entity
81
+ * @param record
82
+ * @param oldData
83
+ * @param newValues
84
+ */
85
+ const assertForeignKeysForUpdate = async (entity, record, oldData, newValues) => {
86
+ for (const key of record.keys) {
87
+ if (key.type !== 'foreign' || key.fields.length !== 1 || !key.references || key.references.length !== 1) {
88
+ continue;
89
+ }
90
+ const field = key.fields[0];
91
+ if (!Object.prototype.hasOwnProperty.call(newValues, field) || (0, _compareAs.sameValue)((0, _fieldType.fieldType)(record, field), newValues[field], oldData[field])) {
92
+ continue;
93
+ }
94
+ const {
95
+ entity: refEntity,
96
+ property: refProperty
97
+ } = (0, _splitEntityProperty.splitEntityProperty)(key.references[0].fields[0]);
98
+ const exists = await (0, _foreignKeyExists.foreignKeyExists)(refEntity, refProperty, newValues[field]);
99
+ if (!exists) {
100
+ throw new Error(`Foreign key "${field}" on ${entity} references a nonexistent ${refEntity}.${refProperty} = ${newValues[field]}`);
101
+ }
102
+ }
103
+ };
104
+ /**
105
+ * RESTRICT: reject an update that would change a single-field primary key's value while another
106
+ * entity's foreign key still points at the old one - that reference would otherwise be left
107
+ * dangling, since the row itself isn't gone, just re-keyed. Only meaningful for a single-field
108
+ * primary key, matching foreign key support's own single-field-only scope.
109
+ * @param entity
110
+ * @param record
111
+ * @param primaryKey
112
+ * @param oldData
113
+ * @param newValues
114
+ */
115
+ const assertNoDependentsForUpdate = async (entity, record, primaryKey, oldData, newValues) => {
116
+ if (!(0, _config.getSetting)('enforceForeignKeys', true) || primaryKey.fields.length !== 1 || !keyFieldsChanged(record, primaryKey, oldData, newValues)) {
117
+ return;
118
+ }
119
+ const property = primaryKey.fields[0];
120
+ if (await (0, _hasDependents.hasDependents)(entity, property, oldData[property])) {
121
+ throw new Error(`Cannot update "${entity}" where "${property}" = ${oldData[property]} - other records still reference it`);
122
+ }
123
+ };
124
+ /**
125
+ * Move a key's index entry (single-field, or a composite for a multi-field key) from the old
126
+ * value to the new one wherever the update actually changed one of its fields.
127
+ * @param record
128
+ * @param oldData
129
+ * @param newData
130
+ * @param newValues
131
+ * @param fileName
132
+ */
133
+ const reconcileIndexes = async (record, oldData, newData, newValues, fileName) => {
134
+ for (const key of record.keys) {
135
+ if (!keyFieldsChanged(record, key, oldData, newValues)) {
136
+ continue;
137
+ }
138
+ await (0, _removeFromIndex.removeFromIndex)(record.path, key, (0, _computeKeyValue.computeKeyValue)(key.fields, oldData), fileName, (0, _fieldType.keyValueType)(record, key));
139
+ await (0, _updateIndex.updateIndex)(record.path, key, (0, _computeKeyValue.computeKeyValue)(key.fields, newData), fileName, (0, _fieldType.keyValueType)(record, key));
140
+ }
141
+ };
142
+ const assertRequiredFields = (entity, record, newData) => {
143
+ for (const field of record.definition) {
144
+ if (!field.optional && typeof newData[field.name] === 'undefined') {
145
+ throw new Error(`Missing required field "${field.name}" for ${entity}`);
146
+ }
147
+ }
148
+ };
149
+ /**
150
+ * Type-check (and coerce) only the fields actually present in an update's values, never the rest
151
+ * of the merged record - a field whose already-stored value doesn't match its declared type
152
+ * (nothing enforced this before typeCheck() existed) must not block an update that never touches
153
+ * that field.
154
+ * @param record
155
+ * @param values
156
+ */
157
+ const applyFieldTypes = (record, values) => {
158
+ const typedValues = Object.assign({}, values);
159
+ for (const field of record.definition) {
160
+ if (Object.prototype.hasOwnProperty.call(typedValues, field.name)) {
161
+ typedValues[field.name] = (0, _typeCheck.typeCheck)(field.type, typedValues[field.name]);
162
+ }
163
+ }
164
+ return typedValues;
165
+ };
166
+ const updateEntity = async (entity = '', values = {}, dataSet = {}) => {
167
+ const databasePath = (0, _config.getSetting)('databasePath', 'database/');
168
+ const record = await (0, _retrieveRecord.retrieveRecord)(entity);
169
+ const primaryKey = record.keys.find(key => key.type === 'primary');
170
+ if (!primaryKey) {
171
+ throw new Error(`Cannot update "${entity}" without a primary key`);
172
+ }
173
+ const typedValues = applyFieldTypes(record, values);
174
+ const toUpdate = dataSet[entity] || [];
175
+ const updatedRows = [];
176
+ for (const row of toUpdate) {
177
+ const fileName = await findEntryFile(record, primaryKey, (0, _computeKeyValue.computeKeyValue)(primaryKey.fields, row));
178
+ if (!fileName) {
179
+ throw new Error(`Could not find entry for ${entity} where "${primaryKey.fields.join(', ')}" = ${(0, _computeKeyValue.computeKeyValue)(primaryKey.fields, row)}`);
180
+ }
181
+ const oldData = await (0, _retrieveFile.retrieveFile)(`${record.path}/${fileName}`);
182
+ const newData = Object.assign({}, oldData, typedValues);
183
+ await assertUniqueForUpdate(entity, record, oldData, newData, typedValues);
184
+ await assertForeignKeysForUpdate(entity, record, oldData, typedValues);
185
+ await assertNoDependentsForUpdate(entity, record, primaryKey, oldData, typedValues);
186
+ assertRequiredFields(entity, record, newData);
187
+ await reconcileIndexes(record, oldData, newData, typedValues, fileName);
188
+ await writeFile(`${databasePath}${record.path}/${fileName}`, JSON.stringify(newData, null, 2));
189
+ updatedRows.push(newData);
190
+ }
191
+ dataSet[entity] = updatedRows;
192
+ return dataSet;
193
+ };
190
194
  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,r)=>{for(const i of e.entries){const s=await(0,_retrieveFile.retrieveFile)(`${e.path}/${i}`);if((0,_computeKeyValue.computeKeyValue)(t.fields,s)==r)return i}return null},keyFieldsChanged=(e,t,r)=>e.fields.some((e=>Object.prototype.hasOwnProperty.call(r,e)&&r[e]!=t[e])),assertUniqueForUpdate=async(e,t,r,i,s)=>{for(const o of t.keys){if("primary"!==o.type&&"unique"!==o.type||!keyFieldsChanged(o,r,s))continue;const t=o.fields.map((e=>i[e]));if(!await(0,_keyUnique.keyUnique)(e,o.fields,t))throw new Error(`Duplicate value for ${o.type} key "${o.fields.join(", ")}" on ${e}`)}},assertForeignKeysForUpdate=async(e,t,r,i)=>{for(const s of t.keys){if("foreign"!==s.type||1!==s.fields.length||!s.references||1!==s.references.length)continue;const t=s.fields[0];if(!Object.prototype.hasOwnProperty.call(i,t)||i[t]==r[t])continue;const{entity:o,property:n}=(0,_splitEntityProperty.splitEntityProperty)(s.references[0].fields[0]);if(!await(0,_foreignKeyExists.foreignKeyExists)(o,n,i[t]))throw new Error(`Foreign key "${t}" on ${e} references a nonexistent ${o}.${n} = ${i[t]}`)}},assertNoDependentsForUpdate=async(e,t,r,i)=>{if(!(0,_config.getSetting)("enforceForeignKeys",!0)||1!==t.fields.length||!keyFieldsChanged(t,r,i))return;const s=t.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,r,i,s)=>{for(const o of e.keys)keyFieldsChanged(o,t,i)&&(await(0,_removeFromIndex.removeFromIndex)(e.path,o,(0,_computeKeyValue.computeKeyValue)(o.fields,t),s),await(0,_updateIndex.updateIndex)(e.path,o,(0,_computeKeyValue.computeKeyValue)(o.fields,r),s))},assertRequiredFields=(e,t,r)=>{for(const i of t.definition)if(!i.optional&&void 0===r[i.name])throw new Error(`Missing required field "${i.name}" for ${e}`)},applyFieldTypes=(e,t)=>{const r=Object.assign({},t);for(const t of e.definition)Object.prototype.hasOwnProperty.call(r,t.name)&&(r[t.name]=(0,_typeCheck.typeCheck)(t.type,r[t.name]));return r},updateEntity=async(e="",t={},r={})=>{const i=(0,_config.getSetting)("databasePath","database/"),s=await(0,_retrieveRecord.retrieveRecord)(e),o=s.keys.find((e=>"primary"===e.type));if(!o)throw new Error(`Cannot update "${e}" without a primary key`);const n=applyFieldTypes(s,t),a=r[e]||[],p=[];for(const t of a){const r=await findEntryFile(s,o,(0,_computeKeyValue.computeKeyValue)(o.fields,t));if(!r)throw new Error(`Could not find entry for ${e} where "${o.fields.join(", ")}" = ${(0,_computeKeyValue.computeKeyValue)(o.fields,t)}`);const a=await(0,_retrieveFile.retrieveFile)(`${s.path}/${r}`),u=Object.assign({},a,n);await assertUniqueForUpdate(e,s,a,u,n),await assertForeignKeysForUpdate(e,s,a,n),await assertNoDependentsForUpdate(e,o,a,n),assertRequiredFields(e,s,u),await reconcileIndexes(s,a,u,n,r),await writeFile(`${i}${s.path}/${r}`,JSON.stringify(u,null,2)),p.push(u)}return r[e]=p,r};exports.updateEntity=updateEntity;
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 s=await(0,_retrieveFile.retrieveFile)(`${e.path}/${r}`);if((0,_compareAs.sameValue)((0,_fieldType.keyValueType)(e,t),(0,_computeKeyValue.computeKeyValue)(t.fields,s),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,s)=>{for(const o of t.keys){if("primary"!==o.type&&"unique"!==o.type||!keyFieldsChanged(t,o,i,s))continue;const n=o.fields.map((e=>r[e]));if(!await(0,_keyUnique.keyUnique)(e,o.fields,n))throw new Error(`Duplicate value for ${o.type} key "${o.fields.join(", ")}" on ${e}`)}},assertForeignKeysForUpdate=async(e,t,i,r)=>{for(const s of t.keys){if("foreign"!==s.type||1!==s.fields.length||!s.references||1!==s.references.length)continue;const o=s.fields[0];if(!Object.prototype.hasOwnProperty.call(r,o)||(0,_compareAs.sameValue)((0,_fieldType.fieldType)(t,o),r[o],i[o]))continue;const{entity:n,property:a}=(0,_splitEntityProperty.splitEntityProperty)(s.references[0].fields[0]);if(!await(0,_foreignKeyExists.foreignKeyExists)(n,a,r[o]))throw new Error(`Foreign key "${o}" on ${e} references a nonexistent ${n}.${a} = ${r[o]}`)}},assertNoDependentsForUpdate=async(e,t,i,r,s)=>{if(!(0,_config.getSetting)("enforceForeignKeys",!0)||1!==i.fields.length||!keyFieldsChanged(t,i,r,s))return;const o=i.fields[0];if(await(0,_hasDependents.hasDependents)(e,o,r[o]))throw new Error(`Cannot update "${e}" where "${o}" = ${r[o]} - other records still reference it`)},reconcileIndexes=async(e,t,i,r,s)=>{for(const o of e.keys)keyFieldsChanged(e,o,t,r)&&(await(0,_removeFromIndex.removeFromIndex)(e.path,o,(0,_computeKeyValue.computeKeyValue)(o.fields,t),s,(0,_fieldType.keyValueType)(e,o)),await(0,_updateIndex.updateIndex)(e.path,o,(0,_computeKeyValue.computeKeyValue)(o.fields,i),s,(0,_fieldType.keyValueType)(e,o)))},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]));return i},updateEntity=async(e="",t={},i={})=>{const r=(0,_config.getSetting)("databasePath","database/"),s=await(0,_retrieveRecord.retrieveRecord)(e),o=s.keys.find((e=>"primary"===e.type));if(!o)throw new Error(`Cannot update "${e}" without a primary key`);const n=applyFieldTypes(s,t),a=i[e]||[],p=[];for(const t of a){const i=await findEntryFile(s,o,(0,_computeKeyValue.computeKeyValue)(o.fields,t));if(!i)throw new Error(`Could not find entry for ${e} where "${o.fields.join(", ")}" = ${(0,_computeKeyValue.computeKeyValue)(o.fields,t)}`);const a=await(0,_retrieveFile.retrieveFile)(`${s.path}/${i}`),l=Object.assign({},a,n);await assertUniqueForUpdate(e,s,a,l,n),await assertForeignKeysForUpdate(e,s,a,n),await assertNoDependentsForUpdate(e,s,o,a,n),assertRequiredFields(e,s,l),await reconcileIndexes(s,a,l,n,i),await writeFile(`${r}${s.path}/${i}`,JSON.stringify(l,null,2)),p.push(l)}return i[e]=p,i};exports.updateEntity=updateEntity;
@@ -1,7 +1,8 @@
1
- export declare const where: (dataSet?: any[], { property, joinEntity, comparator, value }?: {
2
- property?: string;
3
- joinEntity?: any;
4
- comparator?: string;
5
- value?: any;
6
- }) => any[];
1
+ export declare const where: (dataSet?: any[], { property, joinEntity, comparator, value, type }?: {
2
+ property?: string;
3
+ joinEntity?: any;
4
+ comparator?: string;
5
+ value?: any;
6
+ type?: any;
7
+ }) => any[];
7
8
  //# sourceMappingURL=where.d.ts.map
@@ -1,56 +1,68 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.where = void 0;
7
- require("core-js/modules/es.array.includes.js");
8
- require("core-js/modules/esnext.iterator.constructor.js");
9
- require("core-js/modules/esnext.iterator.filter.js");
10
- const likeMatch = (dataValue, pattern) => {
11
- if (typeof dataValue !== 'string' || typeof pattern !== 'string') {
12
- return false;
13
- }
14
- const regexPattern = pattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&').replace(/%/g, '.*').replace(/_/g, '.');
15
- return new RegExp(`^${regexPattern}$`, 'i').test(dataValue);
16
- };
17
- const makeCondition = (data, property, comparator, value) => {
18
- if (!Object.prototype.hasOwnProperty.call(data, property)) {
19
- return false;
20
- }
21
- const dataValue = data[property];
22
- switch (comparator) {
23
- case '=':
24
- return dataValue == value;
25
- case '!=':
26
- case '<>':
27
- return dataValue != value;
28
- case '>':
29
- return dataValue > value;
30
- case '>=':
31
- return dataValue >= value;
32
- case '<':
33
- return dataValue < value;
34
- case '<=':
35
- return dataValue <= value;
36
- case 'in':
37
- return Array.isArray(value) && value.includes(dataValue);
38
- case 'between':
39
- return Array.isArray(value) && value.length === 2 && dataValue >= value[0] && dataValue <= value[1];
40
- case 'like':
41
- return likeMatch(dataValue, value);
42
- }
43
- return false;
44
- };
45
- const where = (dataSet = [], {
46
- property = '',
47
- joinEntity = undefined,
48
- comparator = '=',
49
- value = null
50
- } = {}) => {
51
- return dataSet.filter(data => {
52
- const testProperty = joinEntity ? `${joinEntity}.${property}` : property;
53
- return makeCondition(data, testProperty, comparator, value);
54
- });
55
- };
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.where = void 0;
7
+ require("core-js/modules/es.array.includes.js");
8
+ require("core-js/modules/esnext.iterator.constructor.js");
9
+ require("core-js/modules/esnext.iterator.filter.js");
10
+ require("core-js/modules/esnext.iterator.some.js");
11
+ var _compareAs = require("../utilities/compareAs");
12
+ const likeMatch = (dataValue, pattern) => {
13
+ if (typeof dataValue !== 'string' || typeof pattern !== 'string') {
14
+ return false;
15
+ }
16
+ const regexPattern = pattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&').replace(/%/g, '.*').replace(/_/g, '.');
17
+ return new RegExp(`^${regexPattern}$`, 'i').test(dataValue);
18
+ };
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) => {
29
+ if (!Object.prototype.hasOwnProperty.call(data, property)) {
30
+ return false;
31
+ }
32
+ const dataValue = data[property];
33
+ switch (comparator) {
34
+ case '=':
35
+ return (0, _compareAs.sameValue)(type, dataValue, value);
36
+ case '!=':
37
+ case '<>':
38
+ return !(0, _compareAs.sameValue)(type, dataValue, value);
39
+ case '>':
40
+ return dataValue > value;
41
+ case '>=':
42
+ return dataValue >= value;
43
+ case '<':
44
+ return dataValue < value;
45
+ case '<=':
46
+ return dataValue <= value;
47
+ case 'in':
48
+ return Array.isArray(value) && (type ? value.some(item => (0, _compareAs.sameValue)(type, dataValue, item)) : value.includes(dataValue));
49
+ case 'between':
50
+ return Array.isArray(value) && value.length === 2 && dataValue >= value[0] && dataValue <= value[1];
51
+ case 'like':
52
+ return likeMatch(dataValue, value);
53
+ }
54
+ return false;
55
+ };
56
+ const where = (dataSet = [], {
57
+ property = '',
58
+ joinEntity = undefined,
59
+ comparator = '=',
60
+ value = null,
61
+ type = undefined
62
+ } = {}) => {
63
+ return dataSet.filter(data => {
64
+ const testProperty = joinEntity ? `${joinEntity}.${property}` : property;
65
+ return makeCondition(data, testProperty, comparator, value, type);
66
+ });
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 n=e[r];switch(t){case"=":return n==s;case"!=":case"<>":return n!=s;case">":return n>s;case">=":return n>=s;case"<":return n<s;case"<=":return n<=s;case"in":return Array.isArray(s)&&s.includes(n);case"between":return Array.isArray(s)&&2===s.length&&n>=s[0]&&n<=s[1];case"like":return likeMatch(n,s)}return!1},where=(e=[],{property:r="",joinEntity:t,comparator:s="=",value:n=null}={})=>e.filter((e=>makeCondition(e,t?`${t}.${r}`:r,s,n)));exports.where=where;
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;
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Normalize a value to a field's declared type for an equality comparison - never written back,
3
+ * only compared. Writes are coerced to the declared type (typeCheck), but values already stored
4
+ * may not match it (ids declared string, so they can hold an integer or a UUID, are often stored
5
+ * as numbers), and query literals carry their own type. A declared type is what says '3' and 3
6
+ * are the same value. A missing or unrecognized type returns the value unchanged.
7
+ * @param type
8
+ * @param value
9
+ */
10
+ export declare const compareAs: (type: string | undefined, value: any) => any;
11
+ /**
12
+ * Whether two values are equal for a field of the given declared type: both are normalized to
13
+ * that type (see compareAs) and compared strictly. A field with no declared type, or one that
14
+ * can't be normalized (object, array, or an unrecognized type), keeps loose equality.
15
+ * @param type
16
+ * @param a
17
+ * @param b
18
+ */
19
+ export declare const sameValue: (type: string | undefined, a: any, b: any) => boolean;
20
+ //# sourceMappingURL=compareAs.d.ts.map