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

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.21",
3
+ "version": "3.0.0-next.23",
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
@@ -7,24 +7,6 @@ import Model from './data/Model.js';
7
7
  import SearchIndex from './data/SearchIndex.js';
8
8
  import _ from 'lodash';
9
9
 
10
- /**
11
- * Represents a transactional operation to be executed, typically queued and later committed.
12
- *
13
- * Stores the method to invoke, the arguments to apply, and tracks the result or error state
14
- * of the transaction once it's processed.
15
- *
16
- * @class Transaction
17
- */
18
- export class Transaction {
19
- constructor(method, ...args) {
20
- this.method = method;
21
- this.args = args;
22
- this.original = undefined;
23
- this.error = undefined;
24
- this.committed = false;
25
- }
26
- }
27
-
28
10
  /**
29
11
  * @class Connection
30
12
  */
@@ -189,11 +171,12 @@ export default class Connection {
189
171
 
190
172
  if (modelToProcessHasChanged) modelsToPut.push(modelToProcess);
191
173
 
174
+ const modelToProcessConstructor = this.#getModelConstructorFromId(modelToProcess.id).name;
175
+
192
176
  if (
193
177
  Boolean(modelToProcess.constructor.indexedProperties().length) &&
194
178
  (!currentModel || !_.isEqual(currentModel.toIndexData(), modelToProcess.toIndexData()))
195
179
  ) {
196
- const modelToProcessConstructor = this.#getModelConstructorFromId(modelToProcess.id).name;
197
180
  modelsToReindex[modelToProcessConstructor] = modelsToReindex[modelToProcessConstructor] || [];
198
181
  modelsToReindex[modelToProcessConstructor].push(modelToProcess);
199
182
  }
@@ -202,7 +185,6 @@ export default class Connection {
202
185
  Boolean(modelToProcess.constructor.searchProperties().length) &&
203
186
  (!currentModel || !_.isEqual(currentModel.toSearchData(), modelToProcess.toSearchData()))
204
187
  ) {
205
- const modelToProcessConstructor = this.#getModelConstructorFromId(modelToProcess.id).name;
206
188
  modelsToReindexSearch[modelToProcessConstructor] = modelsToReindexSearch[modelToProcessConstructor] || [];
207
189
  modelsToReindexSearch[modelToProcessConstructor].push(modelToProcess);
208
190
  }
@@ -223,7 +205,7 @@ export default class Connection {
223
205
  const index = await this.#storage.getIndex(modelConstructor);
224
206
 
225
207
  await this.#storage.putIndex(modelConstructor, {
226
- ...index || {},
208
+ ...index,
227
209
  ...Object.fromEntries(models.map(m => [m.id, m.toIndexData()])),
228
210
  });
229
211
  })),
@@ -232,7 +214,7 @@ export default class Connection {
232
214
  const index = await this.#storage.getSearchIndex(modelConstructor);
233
215
 
234
216
  await this.#storage.putSearchIndex(modelConstructor, {
235
- ...index || {},
217
+ ...index,
236
218
  ...Object.fromEntries(models.map(m => [m.id, m.toSearchData()])),
237
219
  });
238
220
  })),
@@ -277,7 +259,8 @@ export default class Connection {
277
259
 
278
260
  if (!modelsToDelete.includes(currentModel.id)) modelsToDelete.push(currentModel.id);
279
261
 
280
- const modelToProcessConstructor = this.#getModelConstructorFromId(modelToProcess.id);
262
+ const modelToProcessConstructor = this.#getModelConstructorFromId(modelToProcess.id).name;
263
+
281
264
  indexActions[modelToProcessConstructor] = indexActions[modelToProcessConstructor] ?? [];
282
265
  searchIndexActions[modelToProcessConstructor] = searchIndexActions[modelToProcessConstructor] ?? [];
283
266
 
@@ -433,42 +416,55 @@ export default class Connection {
433
416
  * @return {Connection}
434
417
  */
435
418
  transaction() {
436
- const transactions = [];
419
+ const operations = [];
437
420
 
438
- const engine = CreateTransactionalStorageEngine(transactions, this.#storage);
421
+ const engine = CreateTransactionalStorageEngine(operations, this.#storage);
439
422
 
440
- const connection = new this.constructor(engine, this.#cache, Object.values(this.#models));
423
+ const transaction = new this.constructor(engine, this.#cache, Object.values(this.#models));
441
424
 
442
- connection.commit = async () => {
425
+ transaction.commit = async () => {
443
426
  try {
444
- for (const [index, transaction] of transactions.entries()) {
427
+ for (const [index, operation] of operations.entries()) {
445
428
  try {
446
- if (transaction.method === 'putModel')
447
- transactions[index].original = await this.#storage.getModel(transaction.args[0].id).catch(() => undefined);
429
+ if (operation.method === 'putModel')
430
+ operations[index].original = await this.#storage.getModel(operation.args[0].id).catch(() => undefined);
448
431
 
449
- if (transaction.method === 'deleteModel')
450
- transactions[index].original = await this.#storage.getModel(transaction.args[0]);
432
+ if (operation.method === 'deleteModel')
433
+ operations[index].original = await this.#storage.getModel(operation.args[0]);
451
434
 
452
- await this.#storage[transaction.method](...transaction.args);
435
+ if (operation.method === 'putIndex')
436
+ operations[index].original = await this.#storage.getIndex(operation.args[0]);
453
437
 
454
- transactions[index].committed = true;
438
+ if (operation.method === 'putSearchIndex')
439
+ operations[index].original = await this.#storage.getSearchIndex(operation.args[0]);
440
+
441
+ await this.#storage[operation.method](...operation.args);
442
+
443
+ operations[index].committed = true;
455
444
  } catch (error) {
456
- transactions[index].error = error;
445
+ operations[index].error = error;
457
446
  throw error;
458
447
  }
459
448
  }
460
449
  } catch (error) {
461
- await Promise.all(
462
- transactions
463
- .filter(t => t.committed && t.original)
464
- .map(t => this.#storage.putModel(t.original)),
465
- );
450
+ for (const operation of operations) {
451
+ if (operation.committed && operation.original) {
452
+ if (['putModel', 'deleteModel'].includes(operation.method))
453
+ await this.#storage.putModel(operation.original);
454
+
455
+ if (operation.method === 'putIndex')
456
+ await this.#storage.putIndex(operation.args[0], operation.original);
457
+
458
+ if (operation.method === 'putSearchIndex')
459
+ await this.#storage.putSearchIndex(operation.args[0], operation.original);
460
+ }
461
+ }
466
462
 
467
- throw new CommitFailedTransactionError(transactions, error);
463
+ throw new CommitFailedTransactionError(operations, error);
468
464
  }
469
465
  };
470
466
 
471
- return connection;
467
+ return transaction;
472
468
  }
473
469
 
474
470
  /**
@@ -525,13 +521,11 @@ export default class Connection {
525
521
  })),
526
522
  )
527
523
  .flat()
528
- .reduce((accumulator, {containingModel, propertyName, propertyProperty}) => ({
529
- ...accumulator,
530
- [containingModel]: {
531
- ...accumulator[containingModel] || {},
532
- [propertyName]: propertyProperty,
533
- },
534
- }), {});
524
+ .reduce((accumulator, {containingModel, propertyName, propertyProperty}) => {
525
+ accumulator[containingModel] = accumulator[containingModel] ?? {};
526
+ accumulator[containingModel][propertyName] = propertyProperty;
527
+ return accumulator;
528
+ }, {});
535
529
  }
536
530
 
537
531
  /**
@@ -620,11 +614,11 @@ class TransactionError extends Error {
620
614
  export class CommitFailedTransactionError extends TransactionError {
621
615
  /**
622
616
  *
623
- * @param {Array<Transaction>} transactions
617
+ * @param {Array<Operation>} transactions
624
618
  * @param {Error} error
625
619
  */
626
620
  constructor(transactions, error) {
627
- super('Transaction failed to commit.');
621
+ super('Operation failed to commit.');
628
622
  this.transactions = transactions;
629
623
  this.error = error;
630
624
  }
@@ -29,7 +29,7 @@ export default class HTTPStorageEngine extends StorageEngine {
29
29
  * Get a model
30
30
  * @param {string} id
31
31
  * @throws ModelNotFoundStorageEngineError
32
- * @return Promise<Model>
32
+ * @return Promise<Object>
33
33
  */
34
34
  getModel(id) {
35
35
  return this.#processFetch(this.#generateURL([id]), this.#getReadOptions())
@@ -43,7 +43,7 @@ export default class HTTPStorageEngine extends StorageEngine {
43
43
 
44
44
  /**
45
45
  * Update a model
46
- * @param {object} model
46
+ * @param {Object} model
47
47
  * @throws HTTPRequestFailedError
48
48
  * @return Promise<void>
49
49
  */
@@ -75,7 +75,7 @@ export default class HTTPStorageEngine extends StorageEngine {
75
75
  /**
76
76
  * Get a model's index data
77
77
  * @param {Model.constructor} modelConstructor
78
- * @return Promise<void>
78
+ * @return Promise<Record<String, Object>>
79
79
  */
80
80
  getIndex(modelConstructor) {
81
81
  return this.#processFetch(
@@ -88,7 +88,7 @@ export default class HTTPStorageEngine extends StorageEngine {
88
88
  /**
89
89
  * Put a model's index data
90
90
  * @param {Model.constructor} modelConstructor
91
- * @param {object} index
91
+ * @param {Record<String, Object>} index
92
92
  * @throws MethodNotImplementedStorageEngineError
93
93
  * @return Promise<void>
94
94
  */
@@ -103,7 +103,7 @@ export default class HTTPStorageEngine extends StorageEngine {
103
103
  * Get a model's raw search index data
104
104
  * @param {Model.constructor} modelConstructor
105
105
  * @throws MethodNotImplementedStorageEngineError
106
- * @return Promise<object>
106
+ * @return Promise<Record<String, Object>>
107
107
  */
108
108
  getSearchIndex(modelConstructor) {
109
109
  return this.#processFetch(
@@ -48,7 +48,7 @@ class S3StorageEngine extends StorageEngine {
48
48
 
49
49
  /**
50
50
  * Upload an object to S3
51
- * @param {object} model - The model object to upload.
51
+ * @param {Object} model - The model object to upload.
52
52
  * @returns {Promise<void>}
53
53
  */
54
54
  async putModel(model) {
@@ -87,7 +87,7 @@ class S3StorageEngine extends StorageEngine {
87
87
  * Get a model's index data
88
88
  * @param {Model.constructor} modelConstructor
89
89
  * @throws MethodNotImplementedStorageEngineError
90
- * @return Promise<object>
90
+ * @return Promise<Record<String, Object>>
91
91
  */
92
92
  async getIndex(modelConstructor) {
93
93
  const Key = this.#generatePath([modelConstructor.name, '_index.json']);
@@ -106,7 +106,7 @@ class S3StorageEngine extends StorageEngine {
106
106
  /**
107
107
  * Put a model's index data
108
108
  * @param {Model.constructor} modelConstructor
109
- * @param {object} index
109
+ * @param {Record<String, Object>} index
110
110
  * @throws MethodNotImplementedStorageEngineError
111
111
  * @return Promise<void>
112
112
  */
@@ -1,5 +1,3 @@
1
- import {Transaction} from '../../Connection.js';
2
-
3
1
  export default class StorageEngine {
4
2
  /**
5
3
  * @param {Object} configuration
@@ -13,7 +11,7 @@ export default class StorageEngine {
13
11
  * @param {string} _id
14
12
  * @throws MethodNotImplementedStorageEngineError
15
13
  * @throws ModelNotFoundStorageEngineError
16
- * @return Promise<Model>
14
+ * @return Promise<Object>
17
15
  */
18
16
  getModel(_id) {
19
17
  return Promise.reject(new MethodNotImplementedStorageEngineError('getModel', this));
@@ -21,7 +19,7 @@ export default class StorageEngine {
21
19
 
22
20
  /**
23
21
  * Update a model
24
- * @param {object} _model
22
+ * @param {Object} _model
25
23
  * @throws MethodNotImplementedStorageEngineError
26
24
  * @return Promise<void>
27
25
  */
@@ -44,7 +42,7 @@ export default class StorageEngine {
44
42
  * Get a model's index data
45
43
  * @param {Model.constructor} _modelConstructor
46
44
  * @throws MethodNotImplementedStorageEngineError
47
- * @return Promise<void>
45
+ * @return Promise<Record<String, Object>>
48
46
  */
49
47
  getIndex(_modelConstructor) {
50
48
  return Promise.reject(new MethodNotImplementedStorageEngineError('getIndex', this));
@@ -53,7 +51,7 @@ export default class StorageEngine {
53
51
  /**
54
52
  * Put a model's index data
55
53
  * @param {Model.constructor} _modelConstructor
56
- * @param {object} _data
54
+ * @param {Record<String, Object>} _data
57
55
  * @throws MethodNotImplementedStorageEngineError
58
56
  * @return Promise<void>
59
57
  */
@@ -65,7 +63,7 @@ export default class StorageEngine {
65
63
  * Get a model's raw search index data
66
64
  * @param {Model.constructor} _modelConstructor
67
65
  * @throws MethodNotImplementedStorageEngineError
68
- * @return Promise<object>
66
+ * @return Promise<Record<String, Object>>
69
67
  */
70
68
  getSearchIndex(_modelConstructor) {
71
69
  return Promise.reject(new MethodNotImplementedStorageEngineError('getSearchIndex', this));
@@ -76,7 +74,7 @@ export default class StorageEngine {
76
74
  * @param {Model.constructor} _constructor
77
75
  * @param {Record<string, object>} _index
78
76
  * @throws MethodNotImplementedStorageEngineError
79
- * @return Promise<void>
77
+ * @return Promise<Record<String, Object>>
80
78
  */
81
79
  putSearchIndex(_constructor, _index) {
82
80
  return Promise.reject(new MethodNotImplementedStorageEngineError('putSearchIndex', this));
@@ -142,32 +140,50 @@ export class DeleteHasUnintendedConsequencesStorageEngineError extends StorageEn
142
140
  }
143
141
  }
144
142
 
143
+ /**
144
+ * Represents a transactional operation to be executed, typically queued and later committed.
145
+ *
146
+ * Stores the method to invoke, the arguments to apply, and tracks the result or error state
147
+ * of the transaction once it's processed.
148
+ *
149
+ * @class Operation
150
+ */
151
+ export class Operation {
152
+ constructor(method, ...args) {
153
+ this.method = method;
154
+ this.args = args;
155
+ this.original = undefined;
156
+ this.error = undefined;
157
+ this.committed = false;
158
+ }
159
+ }
160
+
145
161
  /**
146
162
  *
147
- * @param {Array<Transaction>} transactions
163
+ * @param {Array<Operation>} transactions
148
164
  * @param {StorageEngine} engine
149
165
  * @return {StorageEngine}
150
166
  */
151
- export function CreateTransactionalStorageEngine(transactions, engine) {
167
+ export function CreateTransactionalStorageEngine(operations, engine) {
152
168
  const transactionalEngine = Object.create(engine);
153
169
 
154
170
  transactionalEngine.putModel = (...args) => {
155
- transactions.push(new Transaction('putModel', ...args));
171
+ operations.push(new Operation('putModel', ...args));
156
172
  return Promise.resolve();
157
173
  };
158
174
 
159
175
  transactionalEngine.deleteModel = (...args) => {
160
- transactions.push(new Transaction('deleteModel', ...args));
176
+ operations.push(new Operation('deleteModel', ...args));
161
177
  return Promise.resolve();
162
178
  };
163
179
 
164
180
  transactionalEngine.putIndex = (...args) => {
165
- transactions.push(new Transaction('putIndex', ...args));
181
+ operations.push(new Operation('putIndex', ...args));
166
182
  return Promise.resolve();
167
183
  };
168
184
 
169
185
  transactionalEngine.putSearchIndex = (...args) => {
170
- transactions.push(new Transaction('putSearchIndex', ...args));
186
+ operations.push(new Operation('putSearchIndex', ...args));
171
187
  return Promise.resolve();
172
188
  };
173
189