@travetto/model-memory 8.0.0-alpha.24 → 8.0.0-alpha.26
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/README.md +3 -3
- package/__index__.ts +1 -1
- package/package.json +8 -8
- package/src/service.ts +108 -87
package/README.md
CHANGED
|
@@ -14,7 +14,7 @@ yarn add @travetto/model-memory
|
|
|
14
14
|
```
|
|
15
15
|
|
|
16
16
|
This module provides a memory-based implementation for the [Data Modeling Support](https://github.com/travetto/travetto/tree/main/module/model#readme "Datastore abstraction for core operations."). Supported features:
|
|
17
|
-
* [CRUD](https://github.com/travetto/travetto/tree/main/module/model/src/types/crud.ts#L11)
|
|
18
|
-
* [Expiry](https://github.com/travetto/travetto/tree/main/module/model/src/types/expiry.ts#L10)
|
|
19
17
|
* [Blob](https://github.com/travetto/travetto/tree/main/module/model/src/types/blob.ts#L8)
|
|
20
|
-
* [
|
|
18
|
+
* [CRUD](https://github.com/travetto/travetto/tree/main/module/model/src/types/crud.ts#L10)
|
|
19
|
+
* [Expiry](https://github.com/travetto/travetto/tree/main/module/model/src/types/expiry.ts#L10)
|
|
20
|
+
* [Indexed](https://github.com/travetto/travetto/tree/main/module/model-indexed/src/types/service.ts#L21)
|
package/__index__.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export * from './src/service.ts';
|
|
1
|
+
export * from './src/service.ts';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@travetto/model-memory",
|
|
3
|
-
"version": "8.0.0-alpha.
|
|
3
|
+
"version": "8.0.0-alpha.26",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Memory backing for the travetto model module.",
|
|
6
6
|
"keywords": [
|
|
@@ -26,15 +26,15 @@
|
|
|
26
26
|
"directory": "module/model-memory"
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@travetto/config": "^8.0.0-alpha.
|
|
30
|
-
"@travetto/di": "^8.0.0-alpha.
|
|
31
|
-
"@travetto/model": "^8.0.0-alpha.
|
|
32
|
-
"@travetto/model-indexed": "^8.0.0-alpha.
|
|
33
|
-
"@travetto/schema": "^8.0.0-alpha.
|
|
29
|
+
"@travetto/config": "^8.0.0-alpha.23",
|
|
30
|
+
"@travetto/di": "^8.0.0-alpha.21",
|
|
31
|
+
"@travetto/model": "^8.0.0-alpha.24",
|
|
32
|
+
"@travetto/model-indexed": "^8.0.0-alpha.26",
|
|
33
|
+
"@travetto/schema": "^8.0.0-alpha.23"
|
|
34
34
|
},
|
|
35
35
|
"peerDependencies": {
|
|
36
|
-
"@travetto/cli": "^8.0.0-alpha.
|
|
37
|
-
"@travetto/test": "^8.0.0-alpha.
|
|
36
|
+
"@travetto/cli": "^8.0.0-alpha.29",
|
|
37
|
+
"@travetto/test": "^8.0.0-alpha.22"
|
|
38
38
|
},
|
|
39
39
|
"peerDependenciesMeta": {
|
|
40
40
|
"@travetto/cli": {
|
package/src/service.ts
CHANGED
|
@@ -1,21 +1,52 @@
|
|
|
1
|
-
import {
|
|
2
|
-
type Class, type TimeSpan, castTo, type BinaryMetadata,
|
|
3
|
-
type ByteRange, type BinaryType, BinaryUtil, type BinaryArray, JSONUtil, BinaryMetadataUtil,
|
|
4
|
-
} from '@travetto/runtime';
|
|
5
|
-
import { Injectable, PostConstruct } from '@travetto/di';
|
|
6
1
|
import { Config } from '@travetto/config';
|
|
2
|
+
import { Injectable, PostConstruct } from '@travetto/di';
|
|
7
3
|
import {
|
|
8
|
-
|
|
9
|
-
NotFoundError, ExistsError, type OptionalId, type ModelBlobSupport, ModelCrudUtil, ModelExpiryUtil, ModelStorageUtil,
|
|
4
|
+
ExistsError,
|
|
10
5
|
IndexNotSupported,
|
|
6
|
+
type ModelBlobSupport,
|
|
7
|
+
type ModelCrudSupport,
|
|
8
|
+
ModelCrudUtil,
|
|
9
|
+
type ModelExpirySupport,
|
|
10
|
+
ModelExpiryUtil,
|
|
11
11
|
type ModelListOptions,
|
|
12
|
+
ModelRegistryIndex,
|
|
13
|
+
type ModelStorageSupport,
|
|
14
|
+
ModelStorageUtil,
|
|
15
|
+
type ModelType,
|
|
16
|
+
NotFoundError,
|
|
17
|
+
type OptionalId,
|
|
18
|
+
UniqueError
|
|
12
19
|
} from '@travetto/model';
|
|
13
20
|
import {
|
|
14
|
-
type
|
|
15
|
-
type
|
|
16
|
-
type
|
|
17
|
-
|
|
21
|
+
type AllIndexes,
|
|
22
|
+
type FullKeyedIndexBody,
|
|
23
|
+
type FullKeyedIndexWithPartialBody,
|
|
24
|
+
isModelIndexedIndex,
|
|
25
|
+
type KeyedIndexBody,
|
|
26
|
+
type KeyedIndexSelection,
|
|
27
|
+
ModelIndexedComputedIndex,
|
|
28
|
+
type ModelIndexedSearchOptions,
|
|
29
|
+
type ModelIndexedSupport,
|
|
30
|
+
ModelIndexedUtil,
|
|
31
|
+
type ModelPageOptions,
|
|
32
|
+
type ModelPageResult,
|
|
33
|
+
type SingleItemIndex,
|
|
34
|
+
type SortedIndex,
|
|
35
|
+
type SortedIndexSelection,
|
|
36
|
+
type SortedIndexSelectionType
|
|
18
37
|
} from '@travetto/model-indexed';
|
|
38
|
+
import {
|
|
39
|
+
type BinaryArray,
|
|
40
|
+
type BinaryMetadata,
|
|
41
|
+
BinaryMetadataUtil,
|
|
42
|
+
type BinaryType,
|
|
43
|
+
BinaryUtil,
|
|
44
|
+
type ByteRange,
|
|
45
|
+
type Class,
|
|
46
|
+
castTo,
|
|
47
|
+
JSONUtil,
|
|
48
|
+
type TimeSpan
|
|
49
|
+
} from '@travetto/runtime';
|
|
19
50
|
|
|
20
51
|
const ModelBlobNamespace = '__blobs';
|
|
21
52
|
const ModelBlobMetaNamespace = `${ModelBlobNamespace}_meta`;
|
|
@@ -23,8 +54,7 @@ const ModelBlobMetaNamespace = `${ModelBlobNamespace}_meta`;
|
|
|
23
54
|
type StoreType = Map<string, BinaryArray>;
|
|
24
55
|
|
|
25
56
|
const sortValue = (a: string | number, b: string | number): number =>
|
|
26
|
-
|
|
27
|
-
a - b : (typeof a === 'string' && typeof b === 'string') ? a.localeCompare(b) : 0;
|
|
57
|
+
typeof a === 'number' && typeof b === 'number' ? a - b : typeof a === 'string' && typeof b === 'string' ? a.localeCompare(b) : 0;
|
|
28
58
|
|
|
29
59
|
@Config('model.memory')
|
|
30
60
|
export class MemoryModelConfig {
|
|
@@ -51,23 +81,25 @@ function getFirstId(data: Map<string, unknown> | Set<string>, value?: string | n
|
|
|
51
81
|
* Standard in-memory support
|
|
52
82
|
*/
|
|
53
83
|
@Injectable()
|
|
54
|
-
export class MemoryModelService
|
|
55
|
-
ModelCrudSupport, ModelBlobSupport,
|
|
56
|
-
|
|
57
|
-
ModelIndexedSupport {
|
|
58
|
-
|
|
84
|
+
export class MemoryModelService
|
|
85
|
+
implements ModelCrudSupport, ModelBlobSupport, ModelExpirySupport, ModelStorageSupport, ModelIndexedSupport
|
|
86
|
+
{
|
|
59
87
|
#store = new Map<string, StoreType>();
|
|
60
88
|
#indices = {
|
|
61
89
|
'indexed:sorted': new Map<string, Map<string, Map<string, number | string>>>(),
|
|
62
|
-
'indexed:keyed': new Map<string, Map<string, Set<string>>>()
|
|
90
|
+
'indexed:keyed': new Map<string, Map<string, Set<string>>>()
|
|
63
91
|
} as const;
|
|
64
92
|
|
|
65
93
|
idSource = ModelCrudUtil.uuidSource();
|
|
66
94
|
config: MemoryModelConfig;
|
|
67
95
|
|
|
68
|
-
constructor(config: MemoryModelConfig) {
|
|
96
|
+
constructor(config: MemoryModelConfig) {
|
|
97
|
+
this.config = config;
|
|
98
|
+
}
|
|
69
99
|
|
|
70
|
-
get client(): Map<string, StoreType> {
|
|
100
|
+
get client(): Map<string, StoreType> {
|
|
101
|
+
return this.#store;
|
|
102
|
+
}
|
|
71
103
|
|
|
72
104
|
#getStore<T extends ModelType>(cls: Class<T> | string): StoreType {
|
|
73
105
|
const key = typeof cls === 'string' ? cls : ModelRegistryIndex.getStoreName(cls);
|
|
@@ -95,7 +127,9 @@ export class MemoryModelService implements
|
|
|
95
127
|
const computed = ModelIndexedComputedIndex.get(idx, item).validate({ sort: true });
|
|
96
128
|
switch (idx.type) {
|
|
97
129
|
case 'indexed:sorted':
|
|
98
|
-
case 'indexed:keyed':
|
|
130
|
+
case 'indexed:keyed':
|
|
131
|
+
this.#indices[idx.type].get(idxName)?.get(computed.getKey())?.delete(id);
|
|
132
|
+
break;
|
|
99
133
|
}
|
|
100
134
|
}
|
|
101
135
|
} catch (error) {
|
|
@@ -118,7 +152,7 @@ export class MemoryModelService implements
|
|
|
118
152
|
if (idx.unique) {
|
|
119
153
|
const existing = this.#indices[idx.type].get(idxName)?.get(key);
|
|
120
154
|
if (existing && existing.size > 0 && !existing.has(item.id)) {
|
|
121
|
-
throw new
|
|
155
|
+
throw new UniqueError(cls, key);
|
|
122
156
|
}
|
|
123
157
|
}
|
|
124
158
|
this.#indices[idx.type].getOrInsert(idxName, new Map()).getOrInsert(key, new Set()).add(item.id);
|
|
@@ -146,11 +180,11 @@ export class MemoryModelService implements
|
|
|
146
180
|
}
|
|
147
181
|
}
|
|
148
182
|
|
|
149
|
-
async #getIdByIndex<
|
|
150
|
-
T
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
183
|
+
async #getIdByIndex<T extends ModelType, K extends KeyedIndexSelection<T>, S extends SortedIndexSelection<T>>(
|
|
184
|
+
cls: Class<T>,
|
|
185
|
+
idx: SingleItemIndex<T, K, S>,
|
|
186
|
+
body: FullKeyedIndexBody<T, K, S>
|
|
187
|
+
): Promise<string> {
|
|
154
188
|
const computed = ModelIndexedComputedIndex.get(idx, body).validate({ sort: true });
|
|
155
189
|
|
|
156
190
|
const index = this.#indices[idx.type].get(indexName(cls, idx))?.get(computed.getKey());
|
|
@@ -176,11 +210,8 @@ export class MemoryModelService implements
|
|
|
176
210
|
throw new NotFoundError(cls, computed.getKey({ sort: true }));
|
|
177
211
|
}
|
|
178
212
|
|
|
179
|
-
async
|
|
180
|
-
|
|
181
|
-
options?: ModelListOptions & ModelPageOptions<number>,
|
|
182
|
-
): AsyncIterable<string[]> {
|
|
183
|
-
let offset = options && 'offset' in options ? options.offset ?? 0 : 0;
|
|
213
|
+
async *#iterateIds(ids: string[], options?: ModelListOptions & ModelPageOptions<number>): AsyncIterable<string[]> {
|
|
214
|
+
let offset = options && 'offset' in options ? (options.offset ?? 0) : 0;
|
|
184
215
|
const batchSize = options?.batchSizeHint ?? 100;
|
|
185
216
|
let produced = 0;
|
|
186
217
|
const maxCount = options?.limit ?? Number.MAX_SAFE_INTEGER;
|
|
@@ -198,9 +229,7 @@ export class MemoryModelService implements
|
|
|
198
229
|
}
|
|
199
230
|
}
|
|
200
231
|
|
|
201
|
-
async
|
|
202
|
-
T extends ModelType,
|
|
203
|
-
>(
|
|
232
|
+
async *#getIndexIds<T extends ModelType>(
|
|
204
233
|
cls: Class<T>,
|
|
205
234
|
idx: AllIndexes<T>,
|
|
206
235
|
body: KeyedIndexBody<T>,
|
|
@@ -219,9 +248,9 @@ export class MemoryModelService implements
|
|
|
219
248
|
ids = [];
|
|
220
249
|
} else if (index instanceof Map) {
|
|
221
250
|
ids = [...index.entries()]
|
|
222
|
-
.filter(([, sort]) => filterIndex ? filterIndex(sort) : true)
|
|
251
|
+
.filter(([, sort]) => (filterIndex ? filterIndex(sort) : true))
|
|
223
252
|
.sort((a, b) => sortValue(a[1], b[1]))
|
|
224
|
-
.map(([id
|
|
253
|
+
.map(([id]) => id);
|
|
225
254
|
} else {
|
|
226
255
|
ids = [...index];
|
|
227
256
|
}
|
|
@@ -299,7 +328,7 @@ export class MemoryModelService implements
|
|
|
299
328
|
await this.#persist(cls, where, 'remove');
|
|
300
329
|
}
|
|
301
330
|
|
|
302
|
-
async *
|
|
331
|
+
async *list<T extends ModelType>(cls: Class<T>, options?: ModelListOptions): AsyncIterable<T[]> {
|
|
303
332
|
for await (const batch of this.#iterateIds([...this.#getStore(cls).keys()], options)) {
|
|
304
333
|
yield ModelCrudUtil.filterOutNotFound(batch.map(id => this.get(cls, id)));
|
|
305
334
|
}
|
|
@@ -307,7 +336,13 @@ export class MemoryModelService implements
|
|
|
307
336
|
|
|
308
337
|
// Blob Support
|
|
309
338
|
async upsertBlob(location: string, input: BinaryType, metadata?: BinaryMetadata, overwrite = true): Promise<void> {
|
|
310
|
-
if (
|
|
339
|
+
if (
|
|
340
|
+
!overwrite &&
|
|
341
|
+
(await this.getBlobMetadata(location).then(
|
|
342
|
+
() => true,
|
|
343
|
+
() => false
|
|
344
|
+
))
|
|
345
|
+
) {
|
|
311
346
|
return;
|
|
312
347
|
}
|
|
313
348
|
const resolved = await BinaryMetadataUtil.compute(input, metadata);
|
|
@@ -318,7 +353,6 @@ export class MemoryModelService implements
|
|
|
318
353
|
}
|
|
319
354
|
|
|
320
355
|
async getBlob(location: string, range?: ByteRange): Promise<Blob> {
|
|
321
|
-
|
|
322
356
|
const blobs = this.#find(ModelBlobNamespace, location, 'notfound');
|
|
323
357
|
|
|
324
358
|
let data = blobs.get(location)!;
|
|
@@ -364,14 +398,13 @@ export class MemoryModelService implements
|
|
|
364
398
|
store.delete(key);
|
|
365
399
|
deleted += 1;
|
|
366
400
|
}
|
|
367
|
-
} catch {
|
|
401
|
+
} catch {} // Do not let a single error stop the process
|
|
368
402
|
}
|
|
369
403
|
return deleted;
|
|
370
404
|
}
|
|
371
405
|
|
|
372
406
|
// Storage Support
|
|
373
|
-
async createStorage(): Promise<void> {
|
|
374
|
-
}
|
|
407
|
+
async createStorage(): Promise<void> {}
|
|
375
408
|
|
|
376
409
|
async deleteStorage(): Promise<void> {
|
|
377
410
|
this.#store.clear();
|
|
@@ -398,53 +431,48 @@ export class MemoryModelService implements
|
|
|
398
431
|
}
|
|
399
432
|
|
|
400
433
|
// Indexed
|
|
401
|
-
async getByIndex<
|
|
402
|
-
T
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
434
|
+
async getByIndex<T extends ModelType, K extends KeyedIndexSelection<T>, S extends SortedIndexSelection<T>>(
|
|
435
|
+
cls: Class<T>,
|
|
436
|
+
idx: SingleItemIndex<T, K, S>,
|
|
437
|
+
body: FullKeyedIndexBody<T, K, S>
|
|
438
|
+
): Promise<T> {
|
|
406
439
|
return this.get(cls, await this.#getIdByIndex(cls, idx, body));
|
|
407
|
-
|
|
408
440
|
}
|
|
409
441
|
|
|
410
|
-
async deleteByIndex<
|
|
411
|
-
T
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
442
|
+
async deleteByIndex<T extends ModelType, K extends KeyedIndexSelection<T>, S extends SortedIndexSelection<T>>(
|
|
443
|
+
cls: Class<T>,
|
|
444
|
+
idx: SingleItemIndex<T, K, S>,
|
|
445
|
+
body: FullKeyedIndexBody<T, K, S>
|
|
446
|
+
): Promise<void> {
|
|
415
447
|
await this.delete(cls, await this.#getIdByIndex(cls, idx, body));
|
|
416
448
|
}
|
|
417
449
|
|
|
418
|
-
upsertByIndex<
|
|
419
|
-
T
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
450
|
+
upsertByIndex<T extends ModelType, K extends KeyedIndexSelection<T>, S extends SortedIndexSelection<T>>(
|
|
451
|
+
cls: Class<T>,
|
|
452
|
+
idx: SingleItemIndex<T, K, S>,
|
|
453
|
+
body: OptionalId<T>
|
|
454
|
+
): Promise<T> {
|
|
423
455
|
return ModelIndexedUtil.naiveUpsert(this, cls, idx, body);
|
|
424
456
|
}
|
|
425
457
|
|
|
426
|
-
updateByIndex<
|
|
427
|
-
T
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
458
|
+
updateByIndex<T extends ModelType, K extends KeyedIndexSelection<T>, S extends SortedIndexSelection<T>>(
|
|
459
|
+
cls: Class<T>,
|
|
460
|
+
idx: SingleItemIndex<T, K, S>,
|
|
461
|
+
body: T
|
|
462
|
+
): Promise<T> {
|
|
431
463
|
return ModelIndexedUtil.naiveUpdate(this, cls, idx, body);
|
|
432
464
|
}
|
|
433
465
|
|
|
434
|
-
async updatePartialByIndex<
|
|
435
|
-
T
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
466
|
+
async updatePartialByIndex<T extends ModelType, K extends KeyedIndexSelection<T>, S extends SortedIndexSelection<T>>(
|
|
467
|
+
cls: Class<T>,
|
|
468
|
+
idx: SingleItemIndex<T, K, S>,
|
|
469
|
+
body: FullKeyedIndexWithPartialBody<T, K, S>
|
|
470
|
+
): Promise<T> {
|
|
439
471
|
const item = await ModelCrudUtil.naivePartialUpdate(cls, () => this.getByIndex(cls, idx, castTo(body)), castTo(body));
|
|
440
472
|
return this.update(cls, item);
|
|
441
473
|
}
|
|
442
474
|
|
|
443
|
-
async pageByIndex<
|
|
444
|
-
T extends ModelType,
|
|
445
|
-
K extends KeyedIndexSelection<T>,
|
|
446
|
-
S extends SortedIndexSelection<T>
|
|
447
|
-
>(
|
|
475
|
+
async pageByIndex<T extends ModelType, K extends KeyedIndexSelection<T>, S extends SortedIndexSelection<T>>(
|
|
448
476
|
cls: Class<T>,
|
|
449
477
|
idx: SortedIndex<T, K, S>,
|
|
450
478
|
body: KeyedIndexBody<T, K>,
|
|
@@ -456,16 +484,12 @@ export class MemoryModelService implements
|
|
|
456
484
|
const items: T[] = [];
|
|
457
485
|
for await (const batch of this.#getIndexIds(cls, idx, body, { limit: 100, ...options, offset })) {
|
|
458
486
|
produced += batch.length;
|
|
459
|
-
items.push(...await ModelCrudUtil.filterOutNotFound(batch.map(id => this.get(cls, id))));
|
|
487
|
+
items.push(...(await ModelCrudUtil.filterOutNotFound(batch.map(id => this.get(cls, id)))));
|
|
460
488
|
}
|
|
461
489
|
return { items, nextOffset: items.length ? JSONUtil.toBase64(offset + produced) : undefined };
|
|
462
490
|
}
|
|
463
491
|
|
|
464
|
-
async
|
|
465
|
-
T extends ModelType,
|
|
466
|
-
K extends KeyedIndexSelection<T>,
|
|
467
|
-
S extends SortedIndexSelection<T>
|
|
468
|
-
>(
|
|
492
|
+
async *listByIndex<T extends ModelType, K extends KeyedIndexSelection<T>, S extends SortedIndexSelection<T>>(
|
|
469
493
|
cls: Class<T>,
|
|
470
494
|
idx: SortedIndex<T, K, S>,
|
|
471
495
|
body: KeyedIndexBody<T, K>,
|
|
@@ -483,12 +507,9 @@ export class MemoryModelService implements
|
|
|
483
507
|
B extends SortedIndexSelectionType<T, S> & string
|
|
484
508
|
>(cls: Class<T>, idx: SortedIndex<T, K, S>, body: KeyedIndexBody<T, K>, prefix: B, options?: ModelIndexedSearchOptions): Promise<T[]> {
|
|
485
509
|
const items: T[] = [];
|
|
486
|
-
for await (const batch of this.#getIndexIds(
|
|
487
|
-
|
|
488
|
-
id => (typeof id === 'string' && id.startsWith(prefix))
|
|
489
|
-
)) {
|
|
490
|
-
items.push(...await ModelCrudUtil.filterOutNotFound(batch.map(id => this.get(cls, id))));
|
|
510
|
+
for await (const batch of this.#getIndexIds(cls, idx, body, options, id => typeof id === 'string' && id.startsWith(prefix))) {
|
|
511
|
+
items.push(...(await ModelCrudUtil.filterOutNotFound(batch.map(id => this.get(cls, id)))));
|
|
491
512
|
}
|
|
492
513
|
return items;
|
|
493
514
|
}
|
|
494
|
-
}
|
|
515
|
+
}
|