@acodeninja/persist 3.0.0-next.23 → 3.0.0-next.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@acodeninja/persist",
3
- "version": "3.0.0-next.23",
3
+ "version": "3.0.0-next.25",
4
4
  "description": "A JSON based data modelling and persistence module with alternate storage mechanisms.",
5
5
  "type": "module",
6
6
  "scripts": {
package/src/Connection.js CHANGED
@@ -17,12 +17,6 @@ export default class Connection {
17
17
  */
18
18
  #storage;
19
19
 
20
- /**
21
- * @private
22
- * @property {CacheEngine|undefined}
23
- */
24
- #cache;
25
-
26
20
  /**
27
21
  * @private
28
22
  * @property {Record<String, Model.constructor>}
@@ -32,12 +26,10 @@ export default class Connection {
32
26
  /**
33
27
  * Create a new connection
34
28
  * @param {StorageEngine} storage
35
- * @param {CacheEngine|undefined} cache
36
29
  * @param {Array<Model.constructor>} models
37
30
  */
38
- constructor(storage, cache, models) {
31
+ constructor(storage, models) {
39
32
  this.#storage = storage;
40
- this.#cache = cache;
41
33
  this.#models = Object.fromEntries((models ?? []).map(model => [model.name, model]));
42
34
 
43
35
  if (!this.#storage) throw new MissingArgumentsConnectionError('No storage engine provided');
@@ -50,11 +42,11 @@ export default class Connection {
50
42
  * @return {Promise<Model>}
51
43
  */
52
44
  async get(modelId) {
53
- const constructor = this.#getModelConstructorFromId(modelId);
45
+ const modelConstructor = this.#getModelConstructorFromId(modelId);
54
46
 
55
47
  const data = await this.#storage.getModel(modelId);
56
48
 
57
- return constructor.fromData(data);
49
+ return modelConstructor.fromData(data);
58
50
  }
59
51
 
60
52
  /**
@@ -165,33 +157,37 @@ export default class Connection {
165
157
  throw new ModelNotRegisteredConnectionError(modelToProcess, this.#storage);
166
158
 
167
159
  modelToProcess.validate();
168
- const currentModel = await this.get(modelToProcess.id).catch(() => null);
160
+
161
+ const modelToProcessConstructor = this.#getModelConstructorFromId(modelToProcess.id);
162
+ const currentModel = await this.hydrate(modelToProcess).catch(() => null);
169
163
 
170
164
  const modelToProcessHasChanged = !_.isEqual(currentModel?.toData() || {}, modelToProcess.toData());
171
165
 
172
166
  if (modelToProcessHasChanged) modelsToPut.push(modelToProcess);
173
167
 
174
- const modelToProcessConstructor = this.#getModelConstructorFromId(modelToProcess.id).name;
168
+ const modelToProcessConstructorName = modelToProcessConstructor.name;
175
169
 
176
170
  if (
177
171
  Boolean(modelToProcess.constructor.indexedProperties().length) &&
178
172
  (!currentModel || !_.isEqual(currentModel.toIndexData(), modelToProcess.toIndexData()))
179
173
  ) {
180
- modelsToReindex[modelToProcessConstructor] = modelsToReindex[modelToProcessConstructor] || [];
181
- modelsToReindex[modelToProcessConstructor].push(modelToProcess);
174
+ modelsToReindex[modelToProcessConstructorName] = modelsToReindex[modelToProcessConstructorName] || [];
175
+ modelsToReindex[modelToProcessConstructorName].push(modelToProcess);
182
176
  }
183
177
 
184
178
  if (
185
179
  Boolean(modelToProcess.constructor.searchProperties().length) &&
186
180
  (!currentModel || !_.isEqual(currentModel.toSearchData(), modelToProcess.toSearchData()))
187
181
  ) {
188
- modelsToReindexSearch[modelToProcessConstructor] = modelsToReindexSearch[modelToProcessConstructor] || [];
189
- modelsToReindexSearch[modelToProcessConstructor].push(modelToProcess);
182
+ modelsToReindexSearch[modelToProcessConstructorName] = modelsToReindexSearch[modelToProcessConstructorName] || [];
183
+ modelsToReindexSearch[modelToProcessConstructorName].push(modelToProcess);
190
184
  }
191
185
 
192
- for (const [field, value] of Object.entries(modelToProcess)) {
193
- if (Model.isModel(value)) {
194
- await processModel(modelToProcess[field]);
186
+ for (const [_name, property] of Object.entries(modelToProcess)) {
187
+ if (Model.isModel(property)) {
188
+ await processModel(property);
189
+ } else if (Array.isArray(property) && Model.isModel(property[0])) {
190
+ await Promise.all(property.map(processModel));
195
191
  }
196
192
  }
197
193
  };
@@ -259,25 +255,23 @@ export default class Connection {
259
255
 
260
256
  if (!modelsToDelete.includes(currentModel.id)) modelsToDelete.push(currentModel.id);
261
257
 
262
- const modelToProcessConstructor = this.#getModelConstructorFromId(modelToProcess.id).name;
258
+ const modelToProcessConstructor = this.#getModelConstructorFromId(modelToProcess.id);
263
259
 
264
- indexActions[modelToProcessConstructor] = indexActions[modelToProcessConstructor] ?? [];
265
- searchIndexActions[modelToProcessConstructor] = searchIndexActions[modelToProcessConstructor] ?? [];
260
+ indexActions[modelToProcessConstructor.name] = indexActions[modelToProcessConstructor.name] ?? [];
261
+ searchIndexActions[modelToProcessConstructor.name] = searchIndexActions[modelToProcessConstructor.name] ?? [];
266
262
 
267
- if (currentModel.constructor.indexedPropertiesResolved().length) {
268
- indexActions[modelToProcessConstructor].push(['delete', modelToProcess]);
269
- }
263
+ indexActions[modelToProcessConstructor.name].push(['delete', modelToProcess]);
270
264
 
271
265
  if (currentModel.constructor.searchProperties().length) {
272
- searchIndexActions[modelToProcessConstructor].push(['delete', modelToProcess]);
266
+ searchIndexActions[modelToProcessConstructor.name].push(['delete', modelToProcess]);
273
267
  }
274
268
 
275
269
  const linkedModels = await this.#getInstancesLinkedTo(modelToProcess, indexCache);
276
270
  const links = this.#getLinksFor(modelToProcess.constructor);
277
271
  Object.values(Object.fromEntries(await Promise.all(
278
272
  Object.entries(linkedModels)
279
- .map(async ([constructor, updatableModels]) => [
280
- constructor,
273
+ .map(async ([modelConstructor, updatableModels]) => [
274
+ modelConstructor,
281
275
  await Promise.all(updatableModels.map(async m => {
282
276
  const upToDateModel = modelCache[m.id] ?? await this.get(m.id);
283
277
  modelCache[upToDateModel.id] = upToDateModel;
@@ -297,13 +291,16 @@ export default class Connection {
297
291
  if (!modelsToDelete.includes(m.id)) modelsToDelete.push(m.id);
298
292
  modelsToProcess.push(m);
299
293
  } else {
294
+ const modelConstructor = this.#getModelConstructorFromId(m.id);
300
295
  m[linkName] = undefined;
301
296
  modelsToPut.push(m);
302
297
 
303
- indexActions[this.#getModelConstructorFromId(m.id)].push(['reindex', m]);
298
+ indexActions[modelConstructor.name] = indexActions[modelConstructor.name] ?? [];
299
+ indexActions[modelConstructor.name].push(['reindex', m]);
304
300
 
305
301
  if (m.constructor.searchProperties().length) {
306
- searchIndexActions[this.#getModelConstructorFromId(m.id)].push(['reindex', m]);
302
+ searchIndexActions[modelConstructor.name] = searchIndexActions[modelConstructor.name] ?? [];
303
+ searchIndexActions[modelConstructor.name].push(['reindex', m]);
307
304
  }
308
305
  }
309
306
  }),
@@ -317,6 +314,7 @@ export default class Connection {
317
314
  await processModel(model);
318
315
 
319
316
  const unrequestedDeletions = modelsToDelete.filter(m => !propagateTo.includes(m));
317
+
320
318
  if (unrequestedDeletions.length) {
321
319
  throw new DeleteHasUnintendedConsequencesStorageEngineError(model.id, {
322
320
  willDelete: unrequestedDeletions,
@@ -359,8 +357,8 @@ export default class Connection {
359
357
  ]);
360
358
 
361
359
  await Promise.all([
362
- Promise.all(modelsToDelete.map(m => this.#storage.deleteModel(m))),
363
360
  Promise.all(modelsToPut.map(m => this.#storage.putModel(m.toData()))),
361
+ Promise.all(modelsToDelete.map(m => this.#storage.deleteModel(m))),
364
362
  Promise.all(
365
363
  Object.entries(indexCache)
366
364
  .map(([constructorName, index]) => this.#storage.putIndex(this.#models[constructorName], index)),
@@ -385,13 +383,13 @@ export default class Connection {
385
383
  * Must include: '+foo bar' must include 'foo' and may include 'bar'
386
384
  * Must not include: '-foo bar' must not include 'foo' and may include 'bar'
387
385
  * Mixed include: '+foo -bar' must include 'foo' must not include 'bar'
388
- * @param {Model.constructor} constructor
386
+ * @param {Model.constructor} modelConstructor
389
387
  * @param {string} query
390
388
  * @return {Promise<Array<SearchResult>>}
391
389
  */
392
- async search(constructor, query) {
393
- const searchIndex = await this.#storage.getSearchIndex(constructor)
394
- .then(index => new SearchIndex(constructor, index));
390
+ async search(modelConstructor, query) {
391
+ const searchIndex = await this.#storage.getSearchIndex(modelConstructor)
392
+ .then(index => new SearchIndex(modelConstructor, index));
395
393
 
396
394
  return searchIndex.search(query);
397
395
  }
@@ -399,13 +397,13 @@ export default class Connection {
399
397
  /**
400
398
  * Find using a structured query and indexed fields.
401
399
  *
402
- * @param {Model.constructor} constructor
400
+ * @param {Model.constructor} modelConstructor
403
401
  * @param {Object} query
404
402
  * @return {Promise<Array<SearchResult>>}
405
403
  */
406
- async find(constructor, query) {
407
- const findIndex = await this.#storage.getIndex(constructor)
408
- .then(index => new FindIndex(constructor, index));
404
+ async find(modelConstructor, query) {
405
+ const findIndex = await this.#storage.getIndex(modelConstructor)
406
+ .then(index => new FindIndex(modelConstructor, index));
409
407
 
410
408
  return findIndex.query(query);
411
409
  }
@@ -420,7 +418,7 @@ export default class Connection {
420
418
 
421
419
  const engine = CreateTransactionalStorageEngine(operations, this.#storage);
422
420
 
423
- const transaction = new this.constructor(engine, this.#cache, Object.values(this.#models));
421
+ const transaction = new this.constructor(engine, Object.values(this.#models));
424
422
 
425
423
  transaction.commit = async () => {
426
424
  try {
@@ -475,11 +473,11 @@ export default class Connection {
475
473
  */
476
474
  #getModelConstructorFromId(modelId) {
477
475
  const modelName = modelId.split('/')[0];
478
- const constructor = this.#models[modelName];
476
+ const modelConstructor = this.#models[modelName];
479
477
 
480
- if (!constructor) throw new ModelNotRegisteredConnectionError(modelName, this.#storage);
478
+ if (!modelConstructor) throw new ModelNotRegisteredConnectionError(modelName, this.#storage);
481
479
 
482
- return constructor;
480
+ return modelConstructor;
483
481
  }
484
482
 
485
483
  /**
@@ -585,11 +583,11 @@ export class MissingArgumentsConnectionError extends ConnectionError {
585
583
  */
586
584
  export class ModelNotRegisteredConnectionError extends ConnectionError {
587
585
  /**
588
- * @param {Model|String} constructor
586
+ * @param {Model|String} modelConstructor
589
587
  * @param {Connection} connection
590
588
  */
591
- constructor(constructor, connection) {
592
- const modelName = typeof constructor === 'string' ? constructor : constructor.constructor.name;
589
+ constructor(modelConstructor, connection) {
590
+ const modelName = typeof modelConstructor === 'string' ? modelConstructor : modelConstructor.constructor.name;
593
591
  super(`The model ${modelName} is not registered in the storage engine ${connection.constructor.name}`);
594
592
  }
595
593
  }
package/src/data/Model.js CHANGED
@@ -199,6 +199,7 @@ class Model {
199
199
  .filter(([_name, type]) => !type._type && !this.isModel(type) && !type._items?._type && (this.isModel(type._items) || this.isModel(type()._items)))
200
200
  .map(([name, _type]) => `${name}.[*].id`),
201
201
  ...this.indexedProperties(),
202
+ 'id',
202
203
  ];
203
204
  }
204
205