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