json-fs-query 1.0.4 → 1.0.6

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 (139) hide show
  1. package/browser/jsonFsQuery.js +10158 -27750
  2. package/browser/jsonFsQuery.min.js +1 -1
  3. package/dist/data/introspect/create.d.ts +10 -10
  4. package/dist/data/introspect/create.js +47 -47
  5. package/dist/data/introspect/definitions/field.d.ts +27 -27
  6. package/dist/data/introspect/definitions/field.js +36 -36
  7. package/dist/data/introspect/definitions/key.d.ts +25 -25
  8. package/dist/data/introspect/definitions/key.js +53 -53
  9. package/dist/data/introspect/definitions/keyReference.d.ts +17 -17
  10. package/dist/data/introspect/definitions/keyReference.js +20 -20
  11. package/dist/data/introspect/definitions/record.d.ts +25 -25
  12. package/dist/data/introspect/definitions/record.js +32 -32
  13. package/dist/data/introspect/describe.js +15 -15
  14. package/dist/data/introspect/drop.js +37 -37
  15. package/dist/data/introspect/index.d.ts +7 -7
  16. package/dist/data/introspect/index.js +30 -30
  17. package/dist/data/introspect/policies/foreignKeyExists.d.ts +8 -8
  18. package/dist/data/introspect/policies/foreignKeyExists.js +25 -25
  19. package/dist/data/introspect/policies/hasDependents.d.ts +12 -12
  20. package/dist/data/introspect/policies/hasDependents.js +52 -52
  21. package/dist/data/introspect/policies/keyGenerate.d.ts +1 -1
  22. package/dist/data/introspect/policies/keyGenerate.js +17 -17
  23. package/dist/data/introspect/policies/keyUnique.d.ts +9 -9
  24. package/dist/data/introspect/policies/keyUnique.js +31 -31
  25. package/dist/data/introspect/policies/typeCheck.d.ts +11 -11
  26. package/dist/data/introspect/policies/typeCheck.js +57 -57
  27. package/dist/data/introspect/policies/typeEnum.d.ts +1 -1
  28. package/dist/data/introspect/policies/typeEnum.js +9 -9
  29. package/dist/data/introspect/policies/useDefault.d.ts +1 -1
  30. package/dist/data/introspect/policies/useDefault.js +8 -8
  31. package/dist/data/queries/deleteEntity.d.ts +1 -1
  32. package/dist/data/queries/deleteEntity.js +100 -100
  33. package/dist/data/queries/groupBy.d.ts +1 -1
  34. package/dist/data/queries/groupBy.js +22 -22
  35. package/dist/data/queries/insertEntity.d.ts +1 -1
  36. package/dist/data/queries/insertEntity.js +117 -117
  37. package/dist/data/queries/joinEntity.d.ts +1 -1
  38. package/dist/data/queries/joinEntity.js +32 -32
  39. package/dist/data/queries/joinEntity.min.js +1 -1
  40. package/dist/data/queries/limitOffset.d.ts +7 -7
  41. package/dist/data/queries/limitOffset.js +21 -21
  42. package/dist/data/queries/mergeJoins.d.ts +1 -1
  43. package/dist/data/queries/mergeJoins.js +19 -19
  44. package/dist/data/queries/mergeJoins.min.js +1 -1
  45. package/dist/data/queries/parser.d.ts +1 -1
  46. package/dist/data/queries/parser.js +19 -19
  47. package/dist/data/queries/parser.min.js +1 -1
  48. package/dist/data/queries/query.d.ts +1 -1
  49. package/dist/data/queries/query.js +18 -18
  50. package/dist/data/queries/query.min.js +1 -1
  51. package/dist/data/queries/readEntity.js +14 -14
  52. package/dist/data/queries/selectFields.d.ts +9 -9
  53. package/dist/data/queries/selectFields.js +36 -36
  54. package/dist/data/queries/sortBy.d.ts +1 -1
  55. package/dist/data/queries/sortBy.js +15 -15
  56. package/dist/data/queries/sortBy.min.js +1 -1
  57. package/dist/data/queries/updateEntity.d.ts +1 -1
  58. package/dist/data/queries/updateEntity.js +190 -190
  59. package/dist/data/queries/where.d.ts +6 -6
  60. package/dist/data/queries/where.js +56 -56
  61. package/dist/data/utilities/computeKeyValue.d.ts +12 -12
  62. package/dist/data/utilities/computeKeyValue.js +26 -26
  63. package/dist/data/utilities/config.d.ts +27 -27
  64. package/dist/data/utilities/config.js +45 -45
  65. package/dist/data/utilities/deregisterRecord.d.ts +7 -7
  66. package/dist/data/utilities/deregisterRecord.js +34 -34
  67. package/dist/data/utilities/getConditionIndexedList.js +47 -47
  68. package/dist/data/utilities/getConditionIndexedList.min.js +1 -1
  69. package/dist/data/utilities/getJoinIndexedList.js +76 -76
  70. package/dist/data/utilities/getJoinIndexedList.min.js +1 -1
  71. package/dist/data/utilities/getMatchingIndexes.js +28 -28
  72. package/dist/data/utilities/getMatchingIndexes.min.js +1 -1
  73. package/dist/data/utilities/matchIndexedJoins.js +61 -61
  74. package/dist/data/utilities/matchIndexedJoins.min.js +1 -1
  75. package/dist/data/utilities/mergeJoin.js +44 -44
  76. package/dist/data/utilities/mergeJoin.min.js +1 -1
  77. package/dist/data/utilities/parsers/findCommand.d.ts +9 -9
  78. package/dist/data/utilities/parsers/findCommand.js +25 -25
  79. package/dist/data/utilities/parsers/findConditions.d.ts +14 -14
  80. package/dist/data/utilities/parsers/findConditions.js +28 -28
  81. package/dist/data/utilities/parsers/findConditions.min.js +1 -1
  82. package/dist/data/utilities/parsers/findGroupBy.d.ts +8 -8
  83. package/dist/data/utilities/parsers/findGroupBy.js +22 -22
  84. package/dist/data/utilities/parsers/findInsertValues.d.ts +12 -12
  85. package/dist/data/utilities/parsers/findInsertValues.js +34 -34
  86. package/dist/data/utilities/parsers/findJoinClauses.d.ts +12 -12
  87. package/dist/data/utilities/parsers/findJoinClauses.js +27 -27
  88. package/dist/data/utilities/parsers/findJoinClauses.min.js +1 -1
  89. package/dist/data/utilities/parsers/findJoinEntity.d.ts +8 -8
  90. package/dist/data/utilities/parsers/findJoinEntity.js +22 -22
  91. package/dist/data/utilities/parsers/findLimit.d.ts +8 -8
  92. package/dist/data/utilities/parsers/findLimit.js +22 -22
  93. package/dist/data/utilities/parsers/findMergeJoins.d.ts +11 -11
  94. package/dist/data/utilities/parsers/findMergeJoins.js +26 -26
  95. package/dist/data/utilities/parsers/findMergeJoins.min.js +1 -1
  96. package/dist/data/utilities/parsers/findOffset.d.ts +8 -8
  97. package/dist/data/utilities/parsers/findOffset.js +22 -22
  98. package/dist/data/utilities/parsers/findSelects.d.ts +11 -11
  99. package/dist/data/utilities/parsers/findSelects.js +30 -30
  100. package/dist/data/utilities/parsers/findSelects.min.js +1 -1
  101. package/dist/data/utilities/parsers/findSortClauses.d.ts +11 -11
  102. package/dist/data/utilities/parsers/findSortClauses.js +30 -30
  103. package/dist/data/utilities/parsers/findSortClauses.min.js +1 -1
  104. package/dist/data/utilities/parsers/findUpdateValues.d.ts +14 -14
  105. package/dist/data/utilities/parsers/findUpdateValues.js +34 -34
  106. package/dist/data/utilities/parsers/isEntityProperty.js +6 -6
  107. package/dist/data/utilities/parsers/parseLiteralValue.d.ts +11 -11
  108. package/dist/data/utilities/parsers/parseLiteralValue.js +34 -34
  109. package/dist/data/utilities/parsers/parseQuery.d.ts +32 -32
  110. package/dist/data/utilities/parsers/parseQuery.js +51 -51
  111. package/dist/data/utilities/parsers/parseQuery.min.js +1 -1
  112. package/dist/data/utilities/parsers/removeFromQuery.js +6 -6
  113. package/dist/data/utilities/parsers/splitEntityProperty.js +13 -13
  114. package/dist/data/utilities/parsers/splitQueries.js +9 -9
  115. package/dist/data/utilities/reconcileJoin.js +26 -26
  116. package/dist/data/utilities/reconcileJoins.js +26 -26
  117. package/dist/data/utilities/reconcileJoins.min.js +1 -1
  118. package/dist/data/utilities/registerRecord.d.ts +7 -7
  119. package/dist/data/utilities/registerRecord.js +67 -67
  120. package/dist/data/utilities/removeFromIndex.d.ts +13 -13
  121. package/dist/data/utilities/removeFromIndex.js +44 -46
  122. package/dist/data/utilities/restrictIndexedList.js +45 -45
  123. package/dist/data/utilities/retrieveFile.js +28 -28
  124. package/dist/data/utilities/retrieveFile.min.js +1 -1
  125. package/dist/data/utilities/retrieveIndexes.d.ts +1 -1
  126. package/dist/data/utilities/retrieveIndexes.js +24 -24
  127. package/dist/data/utilities/retrieveIndexes.min.js +1 -1
  128. package/dist/data/utilities/retrieveRecord.js +9 -9
  129. package/dist/data/utilities/retrieveRecords.js +21 -21
  130. package/dist/data/utilities/retrieveRecords.min.js +1 -1
  131. package/dist/data/utilities/runQuery.d.ts +1 -1
  132. package/dist/data/utilities/runQuery.js +87 -87
  133. package/dist/data/utilities/updateIndex.d.ts +16 -16
  134. package/dist/data/utilities/updateIndex.js +56 -56
  135. package/dist/data/utilities/useJoinClause.d.ts +5 -5
  136. package/dist/data/utilities/useJoinClause.js +51 -51
  137. package/dist/index.d.ts +4 -4
  138. package/dist/index.js +42 -42
  139. package/package.json +9 -5
@@ -1,190 +1,190 @@
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
- const _retrieveRecord = require('../utilities/retrieveRecord')
13
- const _retrieveFile = require('../utilities/retrieveFile')
14
- const _updateIndex = require('../utilities/updateIndex')
15
- const _removeFromIndex = require('../utilities/removeFromIndex')
16
- const _computeKeyValue = require('../utilities/computeKeyValue')
17
- const _keyUnique = require('../introspect/policies/keyUnique')
18
- const _typeCheck = require('../introspect/policies/typeCheck')
19
- const _foreignKeyExists = require('../introspect/policies/foreignKeyExists')
20
- const _hasDependents = require('../introspect/policies/hasDependents')
21
- const _splitEntityProperty = require('../utilities/parsers/splitEntityProperty')
22
- const _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
- }
190
- exports.updateEntity = updateEntity
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
+ };
190
+ exports.updateEntity = updateEntity;
@@ -1,7 +1,7 @@
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 }?: {
2
+ property?: string;
3
+ joinEntity?: any;
4
+ comparator?: string;
5
+ value?: any;
6
+ }) => any[];
7
7
  //# sourceMappingURL=where.d.ts.map
@@ -1,56 +1,56 @@
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
- }
56
- exports.where = where
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
+ };
56
+ exports.where = where;
@@ -1,13 +1,13 @@
1
- import { fieldName } from '../introspect/definitions/field';
2
- /**
3
- * The value a key (of any field count) indexes and checks uniqueness by. A single-field key uses
4
- * the field's own raw value directly - this is the pre-existing, already-shipped index file
5
- * format (e.g. a plain "2024-06-02" or 1), and stays exactly as it was for backward compatibility
6
- * with real fixture data. A multi-field key has no single value to use, so its fields' values are
7
- * combined into one deterministic composite (a JSON array), in the same field order as key.fields
8
- * - two records only share a composite value if every field matches.
9
- * @param fields
10
- * @param values
11
- */
12
- export declare const computeKeyValue: (fields: fieldName[], values: Object) => any;
1
+ import { fieldName } from '../introspect/definitions/field';
2
+ /**
3
+ * The value a key (of any field count) indexes and checks uniqueness by. A single-field key uses
4
+ * the field's own raw value directly - this is the pre-existing, already-shipped index file
5
+ * format (e.g. a plain "2024-06-02" or 1), and stays exactly as it was for backward compatibility
6
+ * with real fixture data. A multi-field key has no single value to use, so its fields' values are
7
+ * combined into one deterministic composite (a JSON array), in the same field order as key.fields
8
+ * - two records only share a composite value if every field matches.
9
+ * @param fields
10
+ * @param values
11
+ */
12
+ export declare const computeKeyValue: (fields: fieldName[], values: Object) => any;
13
13
  //# sourceMappingURL=computeKeyValue.d.ts.map
@@ -1,26 +1,26 @@
1
- 'use strict'
2
-
3
- Object.defineProperty(exports, '__esModule', {
4
- value: true
5
- })
6
- exports.computeKeyValue = 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.map.js')
10
- /**
11
- * The value a key (of any field count) indexes and checks uniqueness by. A single-field key uses
12
- * the field's own raw value directly - this is the pre-existing, already-shipped index file
13
- * format (e.g. a plain "2024-06-02" or 1), and stays exactly as it was for backward compatibility
14
- * with real fixture data. A multi-field key has no single value to use, so its fields' values are
15
- * combined into one deterministic composite (a JSON array), in the same field order as key.fields
16
- * - two records only share a composite value if every field matches.
17
- * @param fields
18
- * @param values
19
- */
20
- const computeKeyValue = (fields, values) => {
21
- if (fields.length === 1) {
22
- return values[fields[0]]
23
- }
24
- return JSON.stringify(fields.map(field => values[field]))
25
- }
26
- exports.computeKeyValue = computeKeyValue
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.computeKeyValue = 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.map.js");
10
+ /**
11
+ * The value a key (of any field count) indexes and checks uniqueness by. A single-field key uses
12
+ * the field's own raw value directly - this is the pre-existing, already-shipped index file
13
+ * format (e.g. a plain "2024-06-02" or 1), and stays exactly as it was for backward compatibility
14
+ * with real fixture data. A multi-field key has no single value to use, so its fields' values are
15
+ * combined into one deterministic composite (a JSON array), in the same field order as key.fields
16
+ * - two records only share a composite value if every field matches.
17
+ * @param fields
18
+ * @param values
19
+ */
20
+ const computeKeyValue = (fields, values) => {
21
+ if (fields.length === 1) {
22
+ return values[fields[0]];
23
+ }
24
+ return JSON.stringify(fields.map(field => values[field]));
25
+ };
26
+ exports.computeKeyValue = computeKeyValue;
@@ -1,28 +1,28 @@
1
- export type RuntimeSettings = {
2
- databasePath: string;
3
- relativePath: string;
4
- enforceForeignKeys: boolean;
5
- };
6
- /**
7
- * Set where this package's data lives: databasePath (a filesystem path, used in Node) and/or
8
- * relativePath (a URL prefix prepended to databasePath, used when reading over fetch() in a
9
- * browser). Call this once before using create()/drop()/query() - a fresh consumer has no other
10
- * way to point this package at their own data directory.
11
- *
12
- * enforceForeignKeys (default true) controls whether deleteEntity/updateEntity block an operation
13
- * that would leave another record's foreign key pointing at a value that no longer exists -
14
- * mirroring MySQL's own foreign_key_checks setting, which is also toggleable rather than always
15
- * on, since strict enforcement isn't always what every consumer wants.
16
- * @param newSettings
17
- */
18
- export declare const configure: (newSettings: Partial<RuntimeSettings>) => void;
19
- /**
20
- * Read one runtime setting, falling back to the given default if it was never configured. This is
21
- * the package's own lightweight replacement for reading settings via js-build-tools/gulp.config -
22
- * that module is a dev-time build tool, not something a published package should require at
23
- * runtime, and it isn't available at all once a consumer installs this package on its own.
24
- * @param key
25
- * @param defaultValue
26
- */
27
- export declare const getSetting: <K extends keyof RuntimeSettings>(key: K, defaultValue: RuntimeSettings[K]) => RuntimeSettings[K];
1
+ export type RuntimeSettings = {
2
+ databasePath: string;
3
+ relativePath: string;
4
+ enforceForeignKeys: boolean;
5
+ };
6
+ /**
7
+ * Set where this package's data lives: databasePath (a filesystem path, used in Node) and/or
8
+ * relativePath (a URL prefix prepended to databasePath, used when reading over fetch() in a
9
+ * browser). Call this once before using create()/drop()/query() - a fresh consumer has no other
10
+ * way to point this package at their own data directory.
11
+ *
12
+ * enforceForeignKeys (default true) controls whether deleteEntity/updateEntity block an operation
13
+ * that would leave another record's foreign key pointing at a value that no longer exists -
14
+ * mirroring MySQL's own foreign_key_checks setting, which is also toggleable rather than always
15
+ * on, since strict enforcement isn't always what every consumer wants.
16
+ * @param newSettings
17
+ */
18
+ export declare const configure: (newSettings: Partial<RuntimeSettings>) => void;
19
+ /**
20
+ * Read one runtime setting, falling back to the given default if it was never configured. This is
21
+ * the package's own lightweight replacement for reading settings via js-build-tools/gulp.config -
22
+ * that module is a dev-time build tool, not something a published package should require at
23
+ * runtime, and it isn't available at all once a consumer installs this package on its own.
24
+ * @param key
25
+ * @param defaultValue
26
+ */
27
+ export declare const getSetting: <K extends keyof RuntimeSettings>(key: K, defaultValue: RuntimeSettings[K]) => RuntimeSettings[K];
28
28
  //# sourceMappingURL=config.d.ts.map