@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 +1 -1
- package/src/Connection.js +45 -47
- package/src/data/Model.js +1 -0
package/package.json
CHANGED
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,
|
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
|
45
|
+
const modelConstructor = this.#getModelConstructorFromId(modelId);
|
54
46
|
|
55
47
|
const data = await this.#storage.getModel(modelId);
|
56
48
|
|
57
|
-
return
|
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
|
-
|
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
|
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[
|
181
|
-
modelsToReindex[
|
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[
|
189
|
-
modelsToReindexSearch[
|
182
|
+
modelsToReindexSearch[modelToProcessConstructorName] = modelsToReindexSearch[modelToProcessConstructorName] || [];
|
183
|
+
modelsToReindexSearch[modelToProcessConstructorName].push(modelToProcess);
|
190
184
|
}
|
191
185
|
|
192
|
-
for (const [
|
193
|
-
if (Model.isModel(
|
194
|
-
await processModel(
|
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)
|
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
|
-
|
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 ([
|
280
|
-
|
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[
|
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[
|
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}
|
386
|
+
* @param {Model.constructor} modelConstructor
|
389
387
|
* @param {string} query
|
390
388
|
* @return {Promise<Array<SearchResult>>}
|
391
389
|
*/
|
392
|
-
async search(
|
393
|
-
const searchIndex = await this.#storage.getSearchIndex(
|
394
|
-
.then(index => new SearchIndex(
|
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}
|
400
|
+
* @param {Model.constructor} modelConstructor
|
403
401
|
* @param {Object} query
|
404
402
|
* @return {Promise<Array<SearchResult>>}
|
405
403
|
*/
|
406
|
-
async find(
|
407
|
-
const findIndex = await this.#storage.getIndex(
|
408
|
-
.then(index => new FindIndex(
|
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,
|
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
|
476
|
+
const modelConstructor = this.#models[modelName];
|
479
477
|
|
480
|
-
if (!
|
478
|
+
if (!modelConstructor) throw new ModelNotRegisteredConnectionError(modelName, this.#storage);
|
481
479
|
|
482
|
-
return
|
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}
|
586
|
+
* @param {Model|String} modelConstructor
|
589
587
|
* @param {Connection} connection
|
590
588
|
*/
|
591
|
-
constructor(
|
592
|
-
const modelName = typeof
|
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
|
|