json-fs-query 3.2.0 → 3.3.1

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 (124) hide show
  1. package/README.md +265 -17
  2. package/browser/jsonFsQuery.js +2274 -305
  3. package/browser/jsonFsQuery.min.js +1 -1
  4. package/dist/data/introspect/alter.d.ts +12 -0
  5. package/dist/data/introspect/alter.js +304 -0
  6. package/dist/data/introspect/alter.min.js +1 -0
  7. package/dist/data/introspect/create.d.ts +10 -0
  8. package/dist/data/introspect/create.js +8 -2
  9. package/dist/data/introspect/create.min.js +1 -1
  10. package/dist/data/introspect/drop.js +2 -0
  11. package/dist/data/introspect/drop.min.js +1 -1
  12. package/dist/data/introspect/formatDatabase.d.ts +17 -0
  13. package/dist/data/introspect/formatDatabase.js +56 -0
  14. package/dist/data/introspect/formatDatabase.min.js +1 -0
  15. package/dist/data/introspect/index.d.ts +9 -0
  16. package/dist/data/introspect/index.js +18 -5
  17. package/dist/data/introspect/index.min.js +1 -1
  18. package/dist/data/introspect/packDatabase.d.ts +27 -0
  19. package/dist/data/introspect/packDatabase.js +113 -0
  20. package/dist/data/introspect/packDatabase.min.js +1 -0
  21. package/dist/data/queries/databaseStatements.d.ts +31 -0
  22. package/dist/data/queries/databaseStatements.js +97 -0
  23. package/dist/data/queries/databaseStatements.min.js +1 -0
  24. package/dist/data/queries/deleteEntity.js +6 -1
  25. package/dist/data/queries/deleteEntity.min.js +1 -1
  26. package/dist/data/queries/insertEntity.d.ts +8 -0
  27. package/dist/data/queries/insertEntity.js +342 -33
  28. package/dist/data/queries/insertEntity.min.js +1 -1
  29. package/dist/data/queries/query.d.ts +2 -1
  30. package/dist/data/queries/query.js +5 -2
  31. package/dist/data/queries/query.min.js +1 -1
  32. package/dist/data/queries/selectFields.d.ts +5 -4
  33. package/dist/data/queries/selectFields.js +25 -11
  34. package/dist/data/queries/selectFields.min.js +1 -1
  35. package/dist/data/queries/updateEntity.js +39 -5
  36. package/dist/data/queries/updateEntity.min.js +1 -1
  37. package/dist/data/storage/databases.d.ts +32 -0
  38. package/dist/data/storage/databases.js +93 -0
  39. package/dist/data/storage/databases.min.js +1 -0
  40. package/dist/data/storage/fileExists.d.ts +2 -1
  41. package/dist/data/storage/fileExists.js +9 -2
  42. package/dist/data/storage/fileExists.min.js +1 -1
  43. package/dist/data/storage/index.d.ts +5 -2
  44. package/dist/data/storage/index.js +64 -1
  45. package/dist/data/storage/index.min.js +1 -1
  46. package/dist/data/storage/listFiles.d.ts +8 -0
  47. package/dist/data/storage/listFiles.js +52 -0
  48. package/dist/data/storage/listFiles.min.js +1 -0
  49. package/dist/data/storage/makeDirectory.d.ts +2 -1
  50. package/dist/data/storage/makeDirectory.js +8 -2
  51. package/dist/data/storage/makeDirectory.min.js +1 -1
  52. package/dist/data/storage/readJson.d.ts +9 -1
  53. package/dist/data/storage/readJson.js +20 -7
  54. package/dist/data/storage/readJson.min.js +1 -1
  55. package/dist/data/storage/removeDirectory.d.ts +3 -1
  56. package/dist/data/storage/removeDirectory.js +7 -1
  57. package/dist/data/storage/removeDirectory.min.js +1 -1
  58. package/dist/data/storage/removeFile.d.ts +2 -1
  59. package/dist/data/storage/removeFile.js +10 -1
  60. package/dist/data/storage/removeFile.min.js +1 -1
  61. package/dist/data/storage/storagePath.js +1 -1
  62. package/dist/data/storage/storagePath.min.js +1 -1
  63. package/dist/data/storage/writeJson.d.ts +12 -3
  64. package/dist/data/storage/writeJson.js +21 -7
  65. package/dist/data/storage/writeJson.min.js +1 -1
  66. package/dist/data/storage/writeSet.d.ts +64 -0
  67. package/dist/data/storage/writeSet.js +306 -0
  68. package/dist/data/storage/writeSet.min.js +1 -0
  69. package/dist/data/utilities/config.d.ts +37 -0
  70. package/dist/data/utilities/config.js +77 -5
  71. package/dist/data/utilities/config.min.js +1 -1
  72. package/dist/data/utilities/databaseLock.d.ts +5 -0
  73. package/dist/data/utilities/databaseLock.js +15 -3
  74. package/dist/data/utilities/databaseLock.min.js +1 -1
  75. package/dist/data/utilities/getConditionIndexedList.js +3 -4
  76. package/dist/data/utilities/getConditionIndexedList.min.js +1 -1
  77. package/dist/data/utilities/indexBuffer.d.ts +35 -0
  78. package/dist/data/utilities/indexBuffer.js +139 -0
  79. package/dist/data/utilities/indexBuffer.min.js +1 -0
  80. package/dist/data/utilities/joinBases.js +2 -10
  81. package/dist/data/utilities/joinBases.min.js +1 -1
  82. package/dist/data/utilities/knownFields.d.ts +26 -0
  83. package/dist/data/utilities/knownFields.js +128 -0
  84. package/dist/data/utilities/knownFields.min.js +1 -0
  85. package/dist/data/utilities/packedTable.d.ts +34 -0
  86. package/dist/data/utilities/packedTable.js +60 -0
  87. package/dist/data/utilities/packedTable.min.js +1 -0
  88. package/dist/data/utilities/parsers/findInsertValues.d.ts +3 -3
  89. package/dist/data/utilities/parsers/findInsertValues.js +28 -12
  90. package/dist/data/utilities/parsers/findInsertValues.min.js +1 -1
  91. package/dist/data/utilities/parsers/maskStrings.js +11 -2
  92. package/dist/data/utilities/parsers/maskStrings.min.js +1 -1
  93. package/dist/data/utilities/parsers/parseAlter.d.ts +33 -0
  94. package/dist/data/utilities/parsers/parseAlter.js +84 -0
  95. package/dist/data/utilities/parsers/parseAlter.min.js +1 -0
  96. package/dist/data/utilities/parsers/parseCreate.d.ts +14 -0
  97. package/dist/data/utilities/parsers/parseCreate.js +13 -5
  98. package/dist/data/utilities/parsers/parseCreate.min.js +1 -1
  99. package/dist/data/utilities/parsers/parseDatabaseStatement.d.ts +17 -0
  100. package/dist/data/utilities/parsers/parseDatabaseStatement.js +82 -0
  101. package/dist/data/utilities/parsers/parseDatabaseStatement.min.js +1 -0
  102. package/dist/data/utilities/parsers/parseQuery.d.ts +3 -0
  103. package/dist/data/utilities/parsers/parseQuery.js +24 -0
  104. package/dist/data/utilities/parsers/parseQuery.min.js +1 -1
  105. package/dist/data/utilities/parsers/startsQuery.js +1 -1
  106. package/dist/data/utilities/parsers/startsQuery.min.js +1 -1
  107. package/dist/data/utilities/reconcileJoin.d.ts +12 -0
  108. package/dist/data/utilities/reconcileJoin.js +30 -10
  109. package/dist/data/utilities/reconcileJoin.min.js +1 -1
  110. package/dist/data/utilities/retrieveFiles.d.ts +15 -0
  111. package/dist/data/utilities/retrieveFiles.js +48 -0
  112. package/dist/data/utilities/retrieveFiles.min.js +1 -0
  113. package/dist/data/utilities/retrieveRecord.d.ts +6 -0
  114. package/dist/data/utilities/retrieveRecord.js +24 -1
  115. package/dist/data/utilities/retrieveRecord.min.js +1 -1
  116. package/dist/data/utilities/retrieveRecords.d.ts +22 -0
  117. package/dist/data/utilities/retrieveRecords.js +60 -10
  118. package/dist/data/utilities/retrieveRecords.min.js +1 -1
  119. package/dist/data/utilities/runQuery.js +27 -2
  120. package/dist/data/utilities/runQuery.min.js +1 -1
  121. package/dist/index.d.ts +1 -1
  122. package/dist/index.js +24 -0
  123. package/dist/index.min.js +1 -1
  124. package/package.json +8 -4
@@ -23,6 +23,8 @@ var _splitEntityProperty = require("../utilities/parsers/splitEntityProperty");
23
23
  var _config = require("../utilities/config");
24
24
  var _keyIndexFiles = require("../utilities/keyIndexFiles");
25
25
  var _storage = require("../storage");
26
+ var _packedTable = require("../utilities/packedTable");
27
+ var _knownFields = require("../utilities/knownFields");
26
28
  /**
27
29
  * Same reasoning as deleteEntity: a record object carries no memory of which entry file it came
28
30
  * from, so find the file whose primary key value(s) match - a single field, or a composite of
@@ -74,7 +76,8 @@ const assertUniqueForUpdate = async (entity, record, oldData, newData, newValues
74
76
  continue;
75
77
  }
76
78
  const checkKeys = key.fields.map(field => newData[field]);
77
- if (checkKeys.some(value => value === null)) {
79
+ // A null, or a field the row doesn't have (left out, or removed by this update), is never a duplicate
80
+ if (checkKeys.some(value => value === null || typeof value === 'undefined')) {
78
81
  continue;
79
82
  }
80
83
  const isUnique = await (0, _keyUnique.keyUnique)(entity, key.fields, checkKeys);
@@ -99,7 +102,8 @@ const assertForeignKeysForUpdate = async (entity, record, oldData, newValues) =>
99
102
  continue;
100
103
  }
101
104
  const field = key.fields[0];
102
- if (!Object.prototype.hasOwnProperty.call(newValues, field) || newValues[field] === null || (0, _compareAs.sameValue)((0, _fieldType.fieldType)(record, field), newValues[field], oldData[field])) {
105
+ // A null, or a field the update removes (undefined), references nothing
106
+ if (!Object.prototype.hasOwnProperty.call(newValues, field) || newValues[field] === null || typeof newValues[field] === 'undefined' || (0, _compareAs.sameValue)((0, _fieldType.fieldType)(record, field), newValues[field], oldData[field])) {
103
107
  continue;
104
108
  }
105
109
  const {
@@ -169,7 +173,7 @@ const assertUniqueWithinUpdate = (entity, record, planned, newValues) => {
169
173
  oldData,
170
174
  newData
171
175
  } of planned) {
172
- if (!keyFieldsChanged(record, key, oldData, newValues) || key.fields.some(field => newData[field] === null)) {
176
+ if (!keyFieldsChanged(record, key, oldData, newValues) || key.fields.some(field => newData[field] === null || typeof newData[field] === 'undefined')) {
173
177
  continue;
174
178
  }
175
179
  const value = (0, _computeKeyValue.computeKeyValue)(key.fields, newData);
@@ -192,6 +196,11 @@ const assertRequiredFields = (entity, record, newData) => {
192
196
  * of the merged record - a field whose already-stored value doesn't match its declared type
193
197
  * (nothing enforced this before typeCheck() existed) must not block an update that never touches
194
198
  * that field.
199
+ *
200
+ * A null for an optional field that isn't nullable means the field is removed from the row (the
201
+ * field can't hold null, so this is the only thing it can mean, and the only way to clear one):
202
+ * it's kept as undefined here, so the key checks and indexes see the field change, and removed from
203
+ * the row that's written (see withoutRemoved).
195
204
  * @param record
196
205
  * @param values
197
206
  */
@@ -199,18 +208,40 @@ const applyFieldTypes = (record, values) => {
199
208
  const typedValues = Object.assign({}, values);
200
209
  for (const field of record.definition) {
201
210
  if (Object.prototype.hasOwnProperty.call(typedValues, field.name)) {
202
- typedValues[field.name] = (0, _typeCheck.typeCheck)(field.type, typedValues[field.name], field.nullable);
211
+ typedValues[field.name] = typedValues[field.name] === null && field.optional && !field.nullable ? undefined : (0, _typeCheck.typeCheck)(field.type, typedValues[field.name], field.nullable);
203
212
  }
204
213
  }
205
214
  return typedValues;
206
215
  };
216
+ /**
217
+ * A row without the fields an update removes (those it set to undefined, see applyFieldTypes).
218
+ * @param row
219
+ * @param typedValues
220
+ */
221
+ const withoutRemoved = (row, typedValues) => {
222
+ const kept = Object.assign({}, row);
223
+ for (const field of Object.keys(typedValues)) {
224
+ if (typeof typedValues[field] === 'undefined') {
225
+ delete kept[field];
226
+ }
227
+ }
228
+ return kept;
229
+ };
207
230
  const updateEntity = async (entity = '', values = {}, dataSet = {}) => {
208
231
  const record = await (0, _retrieveRecord.retrieveRecord)(entity);
209
232
  const primaryKey = record.keys.find(key => key.type === 'primary');
210
233
  if (!primaryKey) {
211
234
  throw new Error(`Cannot update "${entity}" without a primary key`);
212
235
  }
236
+ // A field the record doesn't define would otherwise be stored outside its definition
237
+ (0, _knownFields.assertFieldsDefined)(entity, record, Object.keys(values || {}), 'set');
213
238
  const typedValues = applyFieldTypes(record, values);
239
+ // A row is found by its primary key, so none of its fields can be cleared (an optional one could be, by null)
240
+ for (const field of primaryKey.fields) {
241
+ if (Object.prototype.hasOwnProperty.call(typedValues, field) && typeof typedValues[field] === 'undefined') {
242
+ throw new Error(`Cannot clear "${field}" on ${entity}: it's part of the primary key`);
243
+ }
244
+ }
214
245
  // Check every row first and write only once all of them pass, so a row that fails leaves the others unchanged too
215
246
  const planned = [];
216
247
  for (const row of dataSet[entity] || []) {
@@ -223,7 +254,7 @@ const updateEntity = async (entity = '', values = {}, dataSet = {}) => {
223
254
  continue;
224
255
  }
225
256
  const oldData = await (0, _retrieveFile.retrieveFile)(`${record.path}/${fileName}`);
226
- const newData = Object.assign({}, oldData, typedValues);
257
+ const newData = withoutRemoved(Object.assign({}, oldData, typedValues), typedValues);
227
258
  await assertUniqueForUpdate(entity, record, oldData, newData, typedValues);
228
259
  await assertForeignKeysForUpdate(entity, record, oldData, typedValues);
229
260
  await assertNoDependentsForUpdate(entity, record, primaryKey, oldData, typedValues);
@@ -235,6 +266,9 @@ const updateEntity = async (entity = '', values = {}, dataSet = {}) => {
235
266
  });
236
267
  }
237
268
  assertUniqueWithinUpdate(entity, record, planned, typedValues);
269
+ if (planned.length) {
270
+ await (0, _packedTable.discardPacked)(record.path);
271
+ }
238
272
  for (const {
239
273
  fileName,
240
274
  oldData,
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.updateEntity=void 0,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"),_keyIndexFiles=require("../utilities/keyIndexFiles"),_storage=require("../storage");const findEntryFile=async(e,t,i)=>{const r=(0,_fieldType.keyValueType)(e,t),o=async o=>(0,_compareAs.sameValue)(r,(0,_computeKeyValue.computeKeyValue)(t.fields,await(0,_retrieveFile.retrieveFile)(`${e.path}/${o}`)),i);for(const s of await(0,_keyIndexFiles.keyIndexFiles)(e,t,(e=>(0,_compareAs.sameValue)(r,e,i)))??[])if(await o(s))return s;for(const t of e.entries)if(await o(t))return t;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=await(0,_retrieveRecord.retrieveRecord)(e),o=r.keys.find((e=>"primary"===e.type));if(!o)throw new Error(`Cannot update "${e}" without a primary key`);const s=applyFieldTypes(r,t),n=[];for(const t of i[e]||[]){const i=await findEntryFile(r,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)}`);if(n.some((e=>e.fileName===i)))continue;const a=await(0,_retrieveFile.retrieveFile)(`${r.path}/${i}`),l=Object.assign({},a,s);await assertUniqueForUpdate(e,r,a,l,s),await assertForeignKeysForUpdate(e,r,a,s),await assertNoDependentsForUpdate(e,r,o,a,s),assertRequiredFields(e,r,l),n.push({fileName:i,oldData:a,newData:l})}assertUniqueWithinUpdate(e,r,n,s);for(const{fileName:e,oldData:t,newData:i}of n)await reconcileIndexes(r,t,i,s,e),await(0,_storage.writeJson)(`${r.path}/${e}`,i);return i[e]=n.map((({newData:e})=>e)),i};exports.updateEntity=updateEntity;
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.updateEntity=void 0,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"),_keyIndexFiles=require("../utilities/keyIndexFiles"),_storage=require("../storage"),_packedTable=require("../utilities/packedTable"),_knownFields=require("../utilities/knownFields");const findEntryFile=async(e,t,i)=>{const r=(0,_fieldType.keyValueType)(e,t),o=async o=>(0,_compareAs.sameValue)(r,(0,_computeKeyValue.computeKeyValue)(t.fields,await(0,_retrieveFile.retrieveFile)(`${e.path}/${o}`)),i);for(const s of await(0,_keyIndexFiles.keyIndexFiles)(e,t,(e=>(0,_compareAs.sameValue)(r,e,i)))??[])if(await o(s))return s;for(const t of e.entries)if(await o(t))return t;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]||void 0===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]||void 0===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]=null===i[t.name]&&t.optional&&!t.nullable?void 0:(0,_typeCheck.typeCheck)(t.type,i[t.name],t.nullable));return i},withoutRemoved=(e,t)=>{const i=Object.assign({},e);for(const e of Object.keys(t))void 0===t[e]&&delete i[e];return i},updateEntity=async(e="",t={},i={})=>{const r=await(0,_retrieveRecord.retrieveRecord)(e),o=r.keys.find((e=>"primary"===e.type));if(!o)throw new Error(`Cannot update "${e}" without a primary key`);(0,_knownFields.assertFieldsDefined)(e,r,Object.keys(t||{}),"set");const s=applyFieldTypes(r,t);for(const t of o.fields)if(Object.prototype.hasOwnProperty.call(s,t)&&void 0===s[t])throw new Error(`Cannot clear "${t}" on ${e}: it's part of the primary key`);const n=[];for(const t of i[e]||[]){const i=await findEntryFile(r,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)}`);if(n.some((e=>e.fileName===i)))continue;const a=await(0,_retrieveFile.retrieveFile)(`${r.path}/${i}`),l=withoutRemoved(Object.assign({},a,s),s);await assertUniqueForUpdate(e,r,a,l,s),await assertForeignKeysForUpdate(e,r,a,s),await assertNoDependentsForUpdate(e,r,o,a,s),assertRequiredFields(e,r,l),n.push({fileName:i,oldData:a,newData:l})}assertUniqueWithinUpdate(e,r,n,s),n.length&&await(0,_packedTable.discardPacked)(r.path);for(const{fileName:e,oldData:t,newData:i}of n)await reconcileIndexes(r,t,i,s,e),await(0,_storage.writeJson)(`${r.path}/${e}`,i);return i[e]=n.map((({newData:e})=>e)),i};exports.updateEntity=updateEntity;
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Where something in the folder holding the databases is (see databasesFolder), as storagePath gives it for a database:
3
+ * on disk in Node, behind relativePath over fetch() in a browser.
4
+ * @param path - A path inside that folder, such as `__DATABASES.json` or a database's name.
5
+ */
6
+ export declare const databasesLocation: (path?: string) => string;
7
+ /**
8
+ * The databases' names, in the order they were created, from `__DATABASES.json` in the folder holding them (a list, so
9
+ * a browser can read it too: it can't list a folder). No list yet means no databases.
10
+ */
11
+ export declare const readDatabaseList: () => Promise<string[]>;
12
+ /**
13
+ * Replace the list of databases. Node only.
14
+ * @param names
15
+ */
16
+ export declare const writeDatabaseList: (names: string[]) => Promise<void>;
17
+ /**
18
+ * Whether a database's folder exists. Node only.
19
+ * @param name
20
+ */
21
+ export declare const databaseFolderExists: (name: string) => boolean;
22
+ /**
23
+ * Create a database's (empty) folder. Node only.
24
+ * @param name
25
+ */
26
+ export declare const makeDatabaseFolder: (name: string) => Promise<void>;
27
+ /**
28
+ * Delete a database's folder and everything in it, and forget anything read from it. Node only.
29
+ * @param name
30
+ */
31
+ export declare const removeDatabaseFolder: (name: string) => Promise<void>;
32
+ //# sourceMappingURL=databases.d.ts.map
@@ -0,0 +1,93 @@
1
+ "use strict";
2
+
3
+ require("core-js/modules/esnext.weak-map.delete-all.js");
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.writeDatabaseList = exports.removeDatabaseFolder = exports.readDatabaseList = exports.makeDatabaseFolder = exports.databasesLocation = exports.databaseFolderExists = void 0;
8
+ require("core-js/modules/es.json.stringify.js");
9
+ var jsEnv = _interopRequireWildcard(require("browser-or-node"));
10
+ var _config = require("../utilities/config");
11
+ var _nodeFiles = require("./nodeFiles");
12
+ var _readCache = require("./readCache");
13
+ var _writeSet = require("./writeSet");
14
+ var e;
15
+ function _interopRequireWildcard(t, r) { if (!r && t && t.__esModule) return t; var n, a, o = Object.defineProperty, f = { __proto__: null, default: t }; if (Object(t) !== t) return f; if (e || "function" != typeof WeakMap || (e = new WeakMap()), e) { if (e.has(t)) return e.get(t); e.set(t, f); } for (a in t) "default" !== a && {}.hasOwnProperty.call(t, a) && ((n = o && Object.getOwnPropertyDescriptor(t, a)) && (n.get || n.set) ? o(f, a, n) : f[a] = t[a]); return f; }
16
+ /**
17
+ * Where something in the folder holding the databases is (see databasesFolder), as storagePath gives it for a database:
18
+ * on disk in Node, behind relativePath over fetch() in a browser.
19
+ * @param path - A path inside that folder, such as `__DATABASES.json` or a database's name.
20
+ */
21
+ const databasesLocation = (path = '') => {
22
+ const relativePath = jsEnv.isBrowser ? (0, _config.getSetting)('relativePath', '') : '';
23
+ return `${relativePath}${(0, _config.databasesFolder)()}${path}`;
24
+ };
25
+ exports.databasesLocation = databasesLocation;
26
+ const listLocation = () => databasesLocation('__DATABASES.json');
27
+ /**
28
+ * The databases' names, in the order they were created, from `__DATABASES.json` in the folder holding them (a list, so
29
+ * a browser can read it too: it can't list a folder). No list yet means no databases.
30
+ */
31
+ const readDatabaseList = async () => {
32
+ let text;
33
+ try {
34
+ text = jsEnv.isNode ? await (0, _nodeFiles.nodeFiles)().readFile(listLocation(), 'utf8') : await fetch(listLocation()).then(response => response.ok === false ? '[]' : response.text());
35
+ } catch (error) {
36
+ if (jsEnv.isNode && error?.code !== 'ENOENT') {
37
+ throw error;
38
+ }
39
+ return [];
40
+ }
41
+ try {
42
+ const names = JSON.parse(text);
43
+ return Array.isArray(names) ? names : [];
44
+ } catch (error) {
45
+ // A static host answers a missing file with a page that isn't JSON
46
+ if (!jsEnv.isNode) {
47
+ return [];
48
+ }
49
+ throw new Error(`Could not read ${listLocation()}: ${error.message}`);
50
+ }
51
+ };
52
+ /**
53
+ * Replace the list of databases. Node only.
54
+ * @param names
55
+ */
56
+ exports.readDatabaseList = readDatabaseList;
57
+ const writeDatabaseList = async names => {
58
+ await (0, _nodeFiles.nodeFiles)().mkdir(databasesLocation(), {
59
+ recursive: true
60
+ });
61
+ await (0, _writeSet.writeAtomically)(listLocation(), JSON.stringify(names));
62
+ };
63
+ /**
64
+ * Whether a database's folder exists. Node only.
65
+ * @param name
66
+ */
67
+ exports.writeDatabaseList = writeDatabaseList;
68
+ const databaseFolderExists = name => require('fs').existsSync(databasesLocation(name));
69
+ /**
70
+ * Create a database's (empty) folder. Node only.
71
+ * @param name
72
+ */
73
+ exports.databaseFolderExists = databaseFolderExists;
74
+ const makeDatabaseFolder = async name => {
75
+ await (0, _nodeFiles.nodeFiles)().mkdir(databasesLocation(name), {
76
+ recursive: true
77
+ });
78
+ };
79
+ /**
80
+ * Delete a database's folder and everything in it, and forget anything read from it. Node only.
81
+ * @param name
82
+ */
83
+ exports.makeDatabaseFolder = makeDatabaseFolder;
84
+ const removeDatabaseFolder = async name => {
85
+ const location = databasesLocation(name);
86
+ (0, _readCache.cacheRemove)(location, true);
87
+ await (0, _nodeFiles.nodeFiles)().rm(location, {
88
+ recursive: true,
89
+ force: true
90
+ });
91
+ (0, _readCache.cacheRemove)(location, true);
92
+ };
93
+ exports.removeDatabaseFolder = removeDatabaseFolder;
@@ -0,0 +1 @@
1
+ "use strict";require("core-js/modules/esnext.weak-map.delete-all.js"),Object.defineProperty(exports,"__esModule",{value:!0}),exports.writeDatabaseList=exports.removeDatabaseFolder=exports.readDatabaseList=exports.makeDatabaseFolder=exports.databasesLocation=exports.databaseFolderExists=void 0,require("core-js/modules/es.json.stringify.js");var e,jsEnv=_interopRequireWildcard(require("browser-or-node")),_config=require("../utilities/config"),_nodeFiles=require("./nodeFiles"),_readCache=require("./readCache"),_writeSet=require("./writeSet");function _interopRequireWildcard(a,t){if(!t&&a&&a.__esModule)return a;var s,r,o=Object.defineProperty,i={__proto__:null,default:a};if(Object(a)!==a)return i;if(e||"function"!=typeof WeakMap||(e=new WeakMap),e){if(e.has(a))return e.get(a);e.set(a,i)}for(r in a)"default"!==r&&{}.hasOwnProperty.call(a,r)&&((s=o&&Object.getOwnPropertyDescriptor(a,r))&&(s.get||s.set)?o(i,r,s):i[r]=a[r]);return i}const databasesLocation=(e="")=>`${jsEnv.isBrowser?(0,_config.getSetting)("relativePath",""):""}${(0,_config.databasesFolder)()}${e}`;exports.databasesLocation=databasesLocation;const listLocation=()=>databasesLocation("__DATABASES.json"),readDatabaseList=async()=>{let e;try{e=jsEnv.isNode?await(0,_nodeFiles.nodeFiles)().readFile(listLocation(),"utf8"):await fetch(listLocation()).then((e=>!1===e.ok?"[]":e.text()))}catch(e){if(jsEnv.isNode&&"ENOENT"!==e?.code)throw e;return[]}try{const a=JSON.parse(e);return Array.isArray(a)?a:[]}catch(e){if(!jsEnv.isNode)return[];throw new Error(`Could not read ${listLocation()}: ${e.message}`)}};exports.readDatabaseList=readDatabaseList;const writeDatabaseList=async e=>{await(0,_nodeFiles.nodeFiles)().mkdir(databasesLocation(),{recursive:!0}),await(0,_writeSet.writeAtomically)(listLocation(),JSON.stringify(e))};exports.writeDatabaseList=writeDatabaseList;const databaseFolderExists=e=>require("fs").existsSync(databasesLocation(e));exports.databaseFolderExists=databaseFolderExists;const makeDatabaseFolder=async e=>{await(0,_nodeFiles.nodeFiles)().mkdir(databasesLocation(e),{recursive:!0})};exports.makeDatabaseFolder=makeDatabaseFolder;const removeDatabaseFolder=async e=>{const a=databasesLocation(e);(0,_readCache.cacheRemove)(a,!0),await(0,_nodeFiles.nodeFiles)().rm(a,{recursive:!0,force:!0}),(0,_readCache.cacheRemove)(a,!0)};exports.removeDatabaseFolder=removeDatabaseFolder;
@@ -1,5 +1,6 @@
1
1
  /**
2
- * Whether a file (or folder) of the database exists. Node only, like the schema statements that use it.
2
+ * Whether a file (or folder) of the database exists, counting what the running statement has written or deleted so
3
+ * far (see withWriteSet). Node only, like the schema statements that use it.
3
4
  * @param path - A path inside the database, such as `users.json`.
4
5
  */
5
6
  export declare const fileExists: (path: string) => boolean;
@@ -5,14 +5,21 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.fileExists = void 0;
7
7
  var _storagePath = require("./storagePath");
8
+ var _writeSet = require("./writeSet");
8
9
  /**
9
- * Whether a file (or folder) of the database exists. Node only, like the schema statements that use it.
10
+ * Whether a file (or folder) of the database exists, counting what the running statement has written or deleted so
11
+ * far (see withWriteSet). Node only, like the schema statements that use it.
10
12
  * @param path - A path inside the database, such as `users.json`.
11
13
  */
12
14
  const fileExists = path => {
15
+ const location = (0, _storagePath.storagePath)(path);
16
+ const pending = (0, _writeSet.pendingExists)(location);
17
+ if (pending !== undefined) {
18
+ return pending;
19
+ }
13
20
  const {
14
21
  existsSync
15
22
  } = require('fs');
16
- return existsSync((0, _storagePath.storagePath)(path));
23
+ return existsSync(location);
17
24
  };
18
25
  exports.fileExists = fileExists;
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.fileExists=void 0;var _storagePath=require("./storagePath");const fileExists=e=>{const{existsSync:s}=require("fs");return s((0,_storagePath.storagePath)(e))};exports.fileExists=fileExists;
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.fileExists=void 0;var _storagePath=require("./storagePath"),_writeSet=require("./writeSet");const fileExists=e=>{const t=(0,_storagePath.storagePath)(e),s=(0,_writeSet.pendingExists)(t);if(void 0!==s)return s;const{existsSync:r}=require("fs");return r(t)};exports.fileExists=fileExists;
@@ -6,11 +6,14 @@
6
6
  * functions is what lets a read cache, batched writes and other storage backends come later without touching queries.
7
7
  */
8
8
  export { storagePath } from './storagePath';
9
- export { readJson } from './readJson';
10
- export { writeJson } from './writeJson';
9
+ export { readJson, readText } from './readJson';
10
+ export { writeJson, jsonText } from './writeJson';
11
11
  export { makeDirectory } from './makeDirectory';
12
12
  export { removeFile } from './removeFile';
13
13
  export { removeDirectory } from './removeDirectory';
14
14
  export { fileExists } from './fileExists';
15
+ export { listFiles } from './listFiles';
16
+ export { databasesLocation, readDatabaseList, writeDatabaseList, databaseFolderExists, makeDatabaseFolder, removeDatabaseFolder } from './databases';
15
17
  export { withReadCache } from './readCache';
18
+ export { withWriteSet } from './writeSet';
16
19
  //# sourceMappingURL=index.d.ts.map
@@ -3,24 +3,72 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
+ Object.defineProperty(exports, "databaseFolderExists", {
7
+ enumerable: true,
8
+ get: function () {
9
+ return _databases.databaseFolderExists;
10
+ }
11
+ });
12
+ Object.defineProperty(exports, "databasesLocation", {
13
+ enumerable: true,
14
+ get: function () {
15
+ return _databases.databasesLocation;
16
+ }
17
+ });
6
18
  Object.defineProperty(exports, "fileExists", {
7
19
  enumerable: true,
8
20
  get: function () {
9
21
  return _fileExists.fileExists;
10
22
  }
11
23
  });
24
+ Object.defineProperty(exports, "jsonText", {
25
+ enumerable: true,
26
+ get: function () {
27
+ return _writeJson.jsonText;
28
+ }
29
+ });
30
+ Object.defineProperty(exports, "listFiles", {
31
+ enumerable: true,
32
+ get: function () {
33
+ return _listFiles.listFiles;
34
+ }
35
+ });
36
+ Object.defineProperty(exports, "makeDatabaseFolder", {
37
+ enumerable: true,
38
+ get: function () {
39
+ return _databases.makeDatabaseFolder;
40
+ }
41
+ });
12
42
  Object.defineProperty(exports, "makeDirectory", {
13
43
  enumerable: true,
14
44
  get: function () {
15
45
  return _makeDirectory.makeDirectory;
16
46
  }
17
47
  });
48
+ Object.defineProperty(exports, "readDatabaseList", {
49
+ enumerable: true,
50
+ get: function () {
51
+ return _databases.readDatabaseList;
52
+ }
53
+ });
18
54
  Object.defineProperty(exports, "readJson", {
19
55
  enumerable: true,
20
56
  get: function () {
21
57
  return _readJson.readJson;
22
58
  }
23
59
  });
60
+ Object.defineProperty(exports, "readText", {
61
+ enumerable: true,
62
+ get: function () {
63
+ return _readJson.readText;
64
+ }
65
+ });
66
+ Object.defineProperty(exports, "removeDatabaseFolder", {
67
+ enumerable: true,
68
+ get: function () {
69
+ return _databases.removeDatabaseFolder;
70
+ }
71
+ });
24
72
  Object.defineProperty(exports, "removeDirectory", {
25
73
  enumerable: true,
26
74
  get: function () {
@@ -45,6 +93,18 @@ Object.defineProperty(exports, "withReadCache", {
45
93
  return _readCache.withReadCache;
46
94
  }
47
95
  });
96
+ Object.defineProperty(exports, "withWriteSet", {
97
+ enumerable: true,
98
+ get: function () {
99
+ return _writeSet.withWriteSet;
100
+ }
101
+ });
102
+ Object.defineProperty(exports, "writeDatabaseList", {
103
+ enumerable: true,
104
+ get: function () {
105
+ return _databases.writeDatabaseList;
106
+ }
107
+ });
48
108
  Object.defineProperty(exports, "writeJson", {
49
109
  enumerable: true,
50
110
  get: function () {
@@ -58,4 +118,7 @@ var _makeDirectory = require("./makeDirectory");
58
118
  var _removeFile = require("./removeFile");
59
119
  var _removeDirectory = require("./removeDirectory");
60
120
  var _fileExists = require("./fileExists");
61
- var _readCache = require("./readCache");
121
+ var _listFiles = require("./listFiles");
122
+ var _databases = require("./databases");
123
+ var _readCache = require("./readCache");
124
+ var _writeSet = require("./writeSet");
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),Object.defineProperty(exports,"fileExists",{enumerable:!0,get:function(){return _fileExists.fileExists}}),Object.defineProperty(exports,"makeDirectory",{enumerable:!0,get:function(){return _makeDirectory.makeDirectory}}),Object.defineProperty(exports,"readJson",{enumerable:!0,get:function(){return _readJson.readJson}}),Object.defineProperty(exports,"removeDirectory",{enumerable:!0,get:function(){return _removeDirectory.removeDirectory}}),Object.defineProperty(exports,"removeFile",{enumerable:!0,get:function(){return _removeFile.removeFile}}),Object.defineProperty(exports,"storagePath",{enumerable:!0,get:function(){return _storagePath.storagePath}}),Object.defineProperty(exports,"withReadCache",{enumerable:!0,get:function(){return _readCache.withReadCache}}),Object.defineProperty(exports,"writeJson",{enumerable:!0,get:function(){return _writeJson.writeJson}});var _storagePath=require("./storagePath"),_readJson=require("./readJson"),_writeJson=require("./writeJson"),_makeDirectory=require("./makeDirectory"),_removeFile=require("./removeFile"),_removeDirectory=require("./removeDirectory"),_fileExists=require("./fileExists"),_readCache=require("./readCache");
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),Object.defineProperty(exports,"databaseFolderExists",{enumerable:!0,get:function(){return _databases.databaseFolderExists}}),Object.defineProperty(exports,"databasesLocation",{enumerable:!0,get:function(){return _databases.databasesLocation}}),Object.defineProperty(exports,"fileExists",{enumerable:!0,get:function(){return _fileExists.fileExists}}),Object.defineProperty(exports,"jsonText",{enumerable:!0,get:function(){return _writeJson.jsonText}}),Object.defineProperty(exports,"listFiles",{enumerable:!0,get:function(){return _listFiles.listFiles}}),Object.defineProperty(exports,"makeDatabaseFolder",{enumerable:!0,get:function(){return _databases.makeDatabaseFolder}}),Object.defineProperty(exports,"makeDirectory",{enumerable:!0,get:function(){return _makeDirectory.makeDirectory}}),Object.defineProperty(exports,"readDatabaseList",{enumerable:!0,get:function(){return _databases.readDatabaseList}}),Object.defineProperty(exports,"readJson",{enumerable:!0,get:function(){return _readJson.readJson}}),Object.defineProperty(exports,"readText",{enumerable:!0,get:function(){return _readJson.readText}}),Object.defineProperty(exports,"removeDatabaseFolder",{enumerable:!0,get:function(){return _databases.removeDatabaseFolder}}),Object.defineProperty(exports,"removeDirectory",{enumerable:!0,get:function(){return _removeDirectory.removeDirectory}}),Object.defineProperty(exports,"removeFile",{enumerable:!0,get:function(){return _removeFile.removeFile}}),Object.defineProperty(exports,"storagePath",{enumerable:!0,get:function(){return _storagePath.storagePath}}),Object.defineProperty(exports,"withReadCache",{enumerable:!0,get:function(){return _readCache.withReadCache}}),Object.defineProperty(exports,"withWriteSet",{enumerable:!0,get:function(){return _writeSet.withWriteSet}}),Object.defineProperty(exports,"writeDatabaseList",{enumerable:!0,get:function(){return _databases.writeDatabaseList}}),Object.defineProperty(exports,"writeJson",{enumerable:!0,get:function(){return _writeJson.writeJson}});var _storagePath=require("./storagePath"),_readJson=require("./readJson"),_writeJson=require("./writeJson"),_makeDirectory=require("./makeDirectory"),_removeFile=require("./removeFile"),_removeDirectory=require("./removeDirectory"),_fileExists=require("./fileExists"),_listFiles=require("./listFiles"),_databases=require("./databases"),_readCache=require("./readCache"),_writeSet=require("./writeSet");
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Every file in a folder of the database and the folders inside it, as paths inside the database (such as
3
+ * `users/1.json`), sorted. During a statement it counts what the statement has written or deleted (see withWriteSet).
4
+ * A folder that doesn't exist has no files. Node only.
5
+ * @param path - A path inside the database, such as `users`. Empty for the whole database.
6
+ */
7
+ export declare const listFiles: (path?: string) => Promise<string[]>;
8
+ //# sourceMappingURL=listFiles.d.ts.map
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.listFiles = void 0;
7
+ require("core-js/modules/esnext.iterator.constructor.js");
8
+ require("core-js/modules/esnext.iterator.map.js");
9
+ var _storagePath = require("./storagePath");
10
+ var _nodeFiles = require("./nodeFiles");
11
+ var _writeSet = require("./writeSet");
12
+ /**
13
+ * The files in `folder` and the folders inside it, as paths relative to the database (`base` is the folder's own).
14
+ * Walks one folder at a time with a plain readdir, so it needs no `recursive` option (which older Node versions
15
+ * ignore without an error, listing only the top folder) and no `Dirent.parentPath`.
16
+ * @param base - The folder's path inside the database, ending in `/` (empty for the database itself).
17
+ */
18
+ const walk = async base => {
19
+ let entries;
20
+ try {
21
+ entries = await (0, _nodeFiles.nodeFiles)().readdir((0, _storagePath.storagePath)(base), {
22
+ withFileTypes: true
23
+ });
24
+ } catch (error) {
25
+ if (error?.code === 'ENOENT') {
26
+ return [];
27
+ }
28
+ throw error;
29
+ }
30
+ const paths = [];
31
+ for (const entry of entries) {
32
+ if (entry.isDirectory()) {
33
+ paths.push(...(await walk(`${base}${entry.name}/`)));
34
+ } else if (entry.isFile()) {
35
+ paths.push(`${base}${entry.name}`);
36
+ }
37
+ }
38
+ return paths;
39
+ };
40
+ /**
41
+ * Every file in a folder of the database and the folders inside it, as paths inside the database (such as
42
+ * `users/1.json`), sorted. During a statement it counts what the statement has written or deleted (see withWriteSet).
43
+ * A folder that doesn't exist has no files. Node only.
44
+ * @param path - A path inside the database, such as `users`. Empty for the whole database.
45
+ */
46
+ const listFiles = async (path = '') => {
47
+ const base = path ? `${path.replace(/\/+$/, '')}/` : '';
48
+ const onDisk = (await walk(base)).map(file => (0, _storagePath.storagePath)(file));
49
+ const root = (0, _storagePath.storagePath)();
50
+ return (0, _writeSet.pendingListing)((0, _storagePath.storagePath)(path), onDisk).map(location => location.slice(root.length)).sort();
51
+ };
52
+ exports.listFiles = listFiles;
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.listFiles=void 0,require("core-js/modules/esnext.iterator.constructor.js"),require("core-js/modules/esnext.iterator.map.js");var _storagePath=require("./storagePath"),_nodeFiles=require("./nodeFiles"),_writeSet=require("./writeSet");const walk=async e=>{let t;try{t=await(0,_nodeFiles.nodeFiles)().readdir((0,_storagePath.storagePath)(e),{withFileTypes:!0})}catch(e){if("ENOENT"===e?.code)return[];throw e}const r=[];for(const s of t)s.isDirectory()?r.push(...await walk(`${e}${s.name}/`)):s.isFile()&&r.push(`${e}${s.name}`);return r},listFiles=async(e="")=>{const t=e?`${e.replace(/\/+$/,"")}/`:"",r=(await walk(t)).map((e=>(0,_storagePath.storagePath)(e))),s=(0,_storagePath.storagePath)();return(0,_writeSet.pendingListing)((0,_storagePath.storagePath)(e),r).map((e=>e.slice(s.length))).sort()};exports.listFiles=listFiles;
@@ -1,5 +1,6 @@
1
1
  /**
2
- * Create a folder of the database, and any missing folders above it (doing nothing if it already exists). Node only.
2
+ * Create a folder of the database, and any missing folders above it (doing nothing if it already exists). During a
3
+ * statement it's created with the statement's other changes (see withWriteSet). Node only.
3
4
  * @param path - A path inside the database, such as `users`. Empty for the database folder itself.
4
5
  */
5
6
  export declare const makeDirectory: (path?: string) => Promise<void>;
@@ -6,12 +6,18 @@ Object.defineProperty(exports, "__esModule", {
6
6
  exports.makeDirectory = void 0;
7
7
  var _storagePath = require("./storagePath");
8
8
  var _nodeFiles = require("./nodeFiles");
9
+ var _writeSet = require("./writeSet");
9
10
  /**
10
- * Create a folder of the database, and any missing folders above it (doing nothing if it already exists). Node only.
11
+ * Create a folder of the database, and any missing folders above it (doing nothing if it already exists). During a
12
+ * statement it's created with the statement's other changes (see withWriteSet). Node only.
11
13
  * @param path - A path inside the database, such as `users`. Empty for the database folder itself.
12
14
  */
13
15
  const makeDirectory = async (path = '') => {
14
- await (0, _nodeFiles.nodeFiles)().mkdir((0, _storagePath.storagePath)(path), {
16
+ const location = (0, _storagePath.storagePath)(path);
17
+ if ((0, _writeSet.stageMakeDirectory)(location)) {
18
+ return;
19
+ }
20
+ await (0, _nodeFiles.nodeFiles)().mkdir(location, {
15
21
  recursive: true
16
22
  });
17
23
  };
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.makeDirectory=void 0;var _storagePath=require("./storagePath"),_nodeFiles=require("./nodeFiles");const makeDirectory=async(e="")=>{await(0,_nodeFiles.nodeFiles)().mkdir((0,_storagePath.storagePath)(e),{recursive:!0})};exports.makeDirectory=makeDirectory;
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.makeDirectory=void 0;var _storagePath=require("./storagePath"),_nodeFiles=require("./nodeFiles"),_writeSet=require("./writeSet");const makeDirectory=async(e="")=>{const r=(0,_storagePath.storagePath)(e);(0,_writeSet.stageMakeDirectory)(r)||await(0,_nodeFiles.nodeFiles)().mkdir(r,{recursive:!0})};exports.makeDirectory=makeDirectory;
@@ -1,6 +1,14 @@
1
+ /**
2
+ * Read one file of the database as text, from wherever readJson would read it (the statement's write set, the read
3
+ * cache, then storage).
4
+ * @param path - A path inside the database, such as `users.json`.
5
+ * @throws {Error} When the file can't be read.
6
+ */
7
+ export declare const readText: (path: string) => Promise<string>;
1
8
  /**
2
9
  * Read and parse one JSON file of the database: from disk in Node, over `fetch()` in a browser (see storagePath for
3
- * where it's looked for). During a call, a file already read is answered from the read cache (see withReadCache).
10
+ * where it's looked for). During a statement, a file it has written or deleted is answered from its write set (see
11
+ * withWriteSet); otherwise, during a call, a file already read is answered from the read cache (see withReadCache).
4
12
  * @param path - A path inside the database, such as `users.json`.
5
13
  * @throws {Error} When the file can't be read or isn't valid JSON (the error from the file system, fetch or JSON.parse).
6
14
  */
@@ -4,22 +4,35 @@ require("core-js/modules/esnext.weak-map.delete-all.js");
4
4
  Object.defineProperty(exports, "__esModule", {
5
5
  value: true
6
6
  });
7
- exports.readJson = void 0;
7
+ exports.readText = exports.readJson = void 0;
8
8
  var jsEnv = _interopRequireWildcard(require("browser-or-node"));
9
9
  var _storagePath = require("./storagePath");
10
10
  var _nodeFiles = require("./nodeFiles");
11
11
  var _readCache = require("./readCache");
12
+ var _writeSet = require("./writeSet");
12
13
  var e;
13
14
  function _interopRequireWildcard(t, r) { if (!r && t && t.__esModule) return t; var n, a, o = Object.defineProperty, f = { __proto__: null, default: t }; if (Object(t) !== t) return f; if (e || "function" != typeof WeakMap || (e = new WeakMap()), e) { if (e.has(t)) return e.get(t); e.set(t, f); } for (a in t) "default" !== a && {}.hasOwnProperty.call(t, a) && ((n = o && Object.getOwnPropertyDescriptor(t, a)) && (n.get || n.set) ? o(f, a, n) : f[a] = t[a]); return f; }
14
15
  /**
15
- * Read and parse one JSON file of the database: from disk in Node, over `fetch()` in a browser (see storagePath for
16
- * where it's looked for). During a call, a file already read is answered from the read cache (see withReadCache).
16
+ * Read one file of the database as text, from wherever readJson would read it (the statement's write set, the read
17
+ * cache, then storage).
17
18
  * @param path - A path inside the database, such as `users.json`.
18
- * @throws {Error} When the file can't be read or isn't valid JSON (the error from the file system, fetch or JSON.parse).
19
+ * @throws {Error} When the file can't be read.
19
20
  */
20
- const readJson = async path => {
21
+ const readText = async path => {
21
22
  const location = (0, _storagePath.storagePath)(path);
22
- const text = await (0, _readCache.cachedRead)(location, () => jsEnv.isNode ? (0, _nodeFiles.nodeFiles)().readFile(location, 'utf8') : fetch(location).then(response => response.text()));
23
- return JSON.parse(text);
23
+ const pending = (0, _writeSet.pendingFile)(location);
24
+ if (pending === null) {
25
+ throw new Error(`ENOENT: no such file or directory, open '${location}' (deleted by this statement)`);
26
+ }
27
+ return typeof pending === 'string' ? pending : (0, _readCache.cachedRead)(location, () => jsEnv.isNode ? (0, _nodeFiles.nodeFiles)().readFile(location, 'utf8') : fetch(location).then(response => response.text()));
24
28
  };
29
+ /**
30
+ * Read and parse one JSON file of the database: from disk in Node, over `fetch()` in a browser (see storagePath for
31
+ * where it's looked for). During a statement, a file it has written or deleted is answered from its write set (see
32
+ * withWriteSet); otherwise, during a call, a file already read is answered from the read cache (see withReadCache).
33
+ * @param path - A path inside the database, such as `users.json`.
34
+ * @throws {Error} When the file can't be read or isn't valid JSON (the error from the file system, fetch or JSON.parse).
35
+ */
36
+ exports.readText = readText;
37
+ const readJson = async path => JSON.parse(await readText(path));
25
38
  exports.readJson = readJson;
@@ -1 +1 @@
1
- "use strict";require("core-js/modules/esnext.weak-map.delete-all.js"),Object.defineProperty(exports,"__esModule",{value:!0}),exports.readJson=void 0;var e,jsEnv=_interopRequireWildcard(require("browser-or-node")),_storagePath=require("./storagePath"),_nodeFiles=require("./nodeFiles"),_readCache=require("./readCache");function _interopRequireWildcard(r,t){if(!t&&r&&r.__esModule)return r;var a,o,s=Object.defineProperty,n={__proto__:null,default:r};if(Object(r)!==r)return n;if(e||"function"!=typeof WeakMap||(e=new WeakMap),e){if(e.has(r))return e.get(r);e.set(r,n)}for(o in r)"default"!==o&&{}.hasOwnProperty.call(r,o)&&((a=s&&Object.getOwnPropertyDescriptor(r,o))&&(a.get||a.set)?s(n,o,a):n[o]=r[o]);return n}const readJson=async e=>{const r=(0,_storagePath.storagePath)(e),t=await(0,_readCache.cachedRead)(r,(()=>jsEnv.isNode?(0,_nodeFiles.nodeFiles)().readFile(r,"utf8"):fetch(r).then((e=>e.text()))));return JSON.parse(t)};exports.readJson=readJson;
1
+ "use strict";require("core-js/modules/esnext.weak-map.delete-all.js"),Object.defineProperty(exports,"__esModule",{value:!0}),exports.readText=exports.readJson=void 0;var e,jsEnv=_interopRequireWildcard(require("browser-or-node")),_storagePath=require("./storagePath"),_nodeFiles=require("./nodeFiles"),_readCache=require("./readCache"),_writeSet=require("./writeSet");function _interopRequireWildcard(r,t){if(!t&&r&&r.__esModule)return r;var a,o,n=Object.defineProperty,s={__proto__:null,default:r};if(Object(r)!==r)return s;if(e||"function"!=typeof WeakMap||(e=new WeakMap),e){if(e.has(r))return e.get(r);e.set(r,s)}for(o in r)"default"!==o&&{}.hasOwnProperty.call(r,o)&&((a=n&&Object.getOwnPropertyDescriptor(r,o))&&(a.get||a.set)?n(s,o,a):s[o]=r[o]);return s}const readText=async e=>{const r=(0,_storagePath.storagePath)(e),t=(0,_writeSet.pendingFile)(r);if(null===t)throw new Error(`ENOENT: no such file or directory, open '${r}' (deleted by this statement)`);return"string"==typeof t?t:(0,_readCache.cachedRead)(r,(()=>jsEnv.isNode?(0,_nodeFiles.nodeFiles)().readFile(r,"utf8"):fetch(r).then((e=>e.text()))))};exports.readText=readText;const readJson=async e=>JSON.parse(await readText(e));exports.readJson=readJson;
@@ -1,5 +1,7 @@
1
1
  /**
2
- * Delete a folder of the database and everything in it; a folder that doesn't exist is left alone. Node only.
2
+ * Delete a folder of the database and everything in it; a folder that doesn't exist is left alone. During a statement
3
+ * it's removed with the statement's other changes (see withWriteSet), and reads in the statement already find
4
+ * everything in it gone. Node only.
3
5
  * @param path - A path inside the database, such as `__indexes/users`.
4
6
  */
5
7
  export declare const removeDirectory: (path: string) => Promise<void>;