@travetto/model 8.0.0-alpha.22 → 8.0.0-alpha.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/README.md +22 -23
- package/__index__.ts +6 -9
- package/package.json +7 -7
- package/src/error/exists.ts +1 -1
- package/src/error/invalid-index.ts +5 -3
- package/src/error/invalid-sub-type.ts +1 -1
- package/src/error/not-found.ts +1 -1
- package/src/registry/decorator.ts +25 -19
- package/src/registry/registry-adapter.ts +7 -7
- package/src/registry/registry-index.ts +8 -7
- package/src/registry/types.ts +3 -3
- package/src/types/basic.ts +2 -2
- package/src/types/blob.ts +2 -3
- package/src/types/bulk.ts +4 -8
- package/src/types/crud.ts +2 -4
- package/src/types/expiry.ts +2 -2
- package/src/types/model.ts +2 -2
- package/src/types/storage.ts +1 -2
- package/src/util/blob.ts +2 -2
- package/src/util/bulk.ts +6 -3
- package/src/util/crud.ts +38 -28
- package/src/util/expiry.ts +4 -5
- package/src/util/storage.ts +3 -4
- package/support/base-command.ts +3 -5
- package/support/bin/candidate.ts +8 -11
- package/support/bin/export.ts +2 -2
- package/support/bin/install.ts +2 -2
- package/support/cli.model_export.ts +5 -4
- package/support/cli.model_install.ts +5 -4
- package/support/doc.support.tsx +16 -18
- package/support/test/base.ts +9 -7
- package/support/test/basic.ts +10 -8
- package/support/test/blob.ts +7 -8
- package/support/test/bulk.ts +16 -5
- package/support/test/crud.ts +108 -90
- package/support/test/expiry.ts +39 -21
- package/support/test/polymorphism.ts +39 -31
- package/support/test/suite.ts +15 -12
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@ npm install @travetto/model
|
|
|
13
13
|
yarn add @travetto/model
|
|
14
14
|
```
|
|
15
15
|
|
|
16
|
-
This module provides a set of contracts/interfaces to data model persistence, modification and retrieval.
|
|
16
|
+
This module provides a set of contracts/interfaces to data model persistence, modification and retrieval. This module builds heavily upon the [Schema](https://github.com/travetto/travetto/tree/main/module/schema#readme "Data type registry for runtime validation, reflection and binding."), which is used for data model validation.
|
|
17
17
|
|
|
18
18
|
## A Simple Model
|
|
19
19
|
A model can be simply defined by usage of the [@Model](https://github.com/travetto/travetto/tree/main/module/model/src/registry/decorator.ts#L14) decorator, which opts it into the [Schema](https://github.com/travetto/travetto/tree/main/module/schema#readme "Data type registry for runtime validation, reflection and binding.") contracts, as well as making it available to the [ModelRegistryIndex](https://github.com/travetto/travetto/tree/main/module/model/src/registry/registry-index.ts#L12).
|
|
@@ -30,18 +30,17 @@ export class SampleModel {
|
|
|
30
30
|
}
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
-
Once the model is defined, it can be leveraged with any of the services that implement the various model storage contracts.
|
|
33
|
+
Once the model is defined, it can be leveraged with any of the services that implement the various model storage contracts. These contracts allow for persisting and fetching of the associated model object.
|
|
34
34
|
|
|
35
35
|
## Contracts
|
|
36
|
-
The module is mainly composed of contracts.
|
|
36
|
+
The module is mainly composed of contracts. The contracts define the expected interface for various model patterns. The primary contracts are [Basic](https://github.com/travetto/travetto/tree/main/module/model/src/types/basic.ts#L9), [CRUD](https://github.com/travetto/travetto/tree/main/module/model/src/types/crud.ts#L10), [Expiry](https://github.com/travetto/travetto/tree/main/module/model/src/types/expiry.ts#L10), [Blob](https://github.com/travetto/travetto/tree/main/module/model/src/types/blob.ts#L8) and [Bulk](https://github.com/travetto/travetto/tree/main/module/model/src/types/bulk.ts#L60).
|
|
37
37
|
|
|
38
38
|
### Basic
|
|
39
|
-
All [Data Modeling Support](https://github.com/travetto/travetto/tree/main/module/model#readme "Datastore abstraction for core operations.") implementations, must honor the [Basic](https://github.com/travetto/travetto/tree/main/module/model/src/types/basic.ts#
|
|
39
|
+
All [Data Modeling Support](https://github.com/travetto/travetto/tree/main/module/model#readme "Datastore abstraction for core operations.") implementations, must honor the [Basic](https://github.com/travetto/travetto/tree/main/module/model/src/types/basic.ts#L9) contract to be able to participate in the model ecosystem. This contract represents the bare minimum for a model service.
|
|
40
40
|
|
|
41
41
|
**Code: Basic Contract**
|
|
42
42
|
```typescript
|
|
43
43
|
export interface ModelBasicSupport<C = unknown> {
|
|
44
|
-
|
|
45
44
|
/**
|
|
46
45
|
* Id Source
|
|
47
46
|
*/
|
|
@@ -76,12 +75,11 @@ export interface ModelBasicSupport<C = unknown> {
|
|
|
76
75
|
```
|
|
77
76
|
|
|
78
77
|
### CRUD
|
|
79
|
-
The [CRUD](https://github.com/travetto/travetto/tree/main/module/model/src/types/crud.ts#
|
|
78
|
+
The [CRUD](https://github.com/travetto/travetto/tree/main/module/model/src/types/crud.ts#L10) contract, builds upon the basic contract, and is built around the idea of simple data retrieval and storage, to create a foundation for other services that need only basic support. The model extension in [Authentication](https://github.com/travetto/travetto/tree/main/module/auth#readme "Authentication support for the Travetto framework"), is an example of a module that only needs create, read and delete, and so any implementation of [Data Modeling Support](https://github.com/travetto/travetto/tree/main/module/model#readme "Datastore abstraction for core operations.") that honors this contract, can be used with the [Authentication](https://github.com/travetto/travetto/tree/main/module/auth#readme "Authentication support for the Travetto framework") model extension.
|
|
80
79
|
|
|
81
80
|
**Code: Crud Contract**
|
|
82
81
|
```typescript
|
|
83
82
|
export interface ModelCrudSupport extends ModelBasicSupport {
|
|
84
|
-
|
|
85
83
|
/**
|
|
86
84
|
* Update an item
|
|
87
85
|
* @param item The document to update.
|
|
@@ -121,10 +119,10 @@ export interface ModelCrudSupport extends ModelBasicSupport {
|
|
|
121
119
|
}
|
|
122
120
|
```
|
|
123
121
|
|
|
124
|
-
The `list` operation returns batches of model records as an async stream.
|
|
122
|
+
The `list` operation returns batches of model records as an async stream. It also accepts listing options such as `limit` to cap how many records are produced, alongside other runtime controls such as abort signals and batch size hints.
|
|
125
123
|
|
|
126
124
|
### Expiry
|
|
127
|
-
Certain implementations will also provide support for automatic [Expiry](https://github.com/travetto/travetto/tree/main/module/model/src/types/expiry.ts#L10) of data at runtime.
|
|
125
|
+
Certain implementations will also provide support for automatic [Expiry](https://github.com/travetto/travetto/tree/main/module/model/src/types/expiry.ts#L10) of data at runtime. This is extremely useful for temporary data as, and is used in the [Caching](https://github.com/travetto/travetto/tree/main/module/cache#readme "Caching functionality with decorators for declarative use.") module for expiring data accordingly.
|
|
128
126
|
|
|
129
127
|
**Code: Expiry Contract**
|
|
130
128
|
```typescript
|
|
@@ -139,12 +137,11 @@ export interface ModelExpirySupport extends ModelCrudSupport {
|
|
|
139
137
|
```
|
|
140
138
|
|
|
141
139
|
### Blob
|
|
142
|
-
Some implementations also allow for the ability to read/write binary data as [Blob](https://github.com/travetto/travetto/tree/main/module/model/src/types/blob.ts#L8).
|
|
140
|
+
Some implementations also allow for the ability to read/write binary data as [Blob](https://github.com/travetto/travetto/tree/main/module/model/src/types/blob.ts#L8). Given that all implementations can store [Base64](https://en.wikipedia.org/wiki/Base64) encoded data, the key differentiator here, is native support for streaming data, as well as being able to store binary data of significant sizes.
|
|
143
141
|
|
|
144
142
|
**Code: Blob Contract**
|
|
145
143
|
```typescript
|
|
146
144
|
export interface ModelBlobSupport {
|
|
147
|
-
|
|
148
145
|
/**
|
|
149
146
|
* Upsert blob to storage
|
|
150
147
|
* @param location The location of the blob
|
|
@@ -200,7 +197,7 @@ export interface ModelBlobSupport {
|
|
|
200
197
|
```
|
|
201
198
|
|
|
202
199
|
### Bulk
|
|
203
|
-
Finally, there is support for [Bulk](https://github.com/travetto/travetto/tree/main/module/model/src/types/bulk.ts#
|
|
200
|
+
Finally, there is support for [Bulk](https://github.com/travetto/travetto/tree/main/module/model/src/types/bulk.ts#L60) operations. This is not to simply imply issuing many commands at in parallel, but implementation support for an atomic/bulk operation. This should allow for higher throughput on data ingest, and potentially for atomic support on transactions.
|
|
204
201
|
|
|
205
202
|
**Code: Bulk Contract**
|
|
206
203
|
```typescript
|
|
@@ -210,7 +207,7 @@ export interface ModelBulkSupport extends ModelCrudSupport {
|
|
|
210
207
|
```
|
|
211
208
|
|
|
212
209
|
## Declaration
|
|
213
|
-
Models are declared via the [@Model](https://github.com/travetto/travetto/tree/main/module/model/src/registry/decorator.ts#L14) decorator, which allows the system to know that this is a class that is compatible with the module.
|
|
210
|
+
Models are declared via the [@Model](https://github.com/travetto/travetto/tree/main/module/model/src/registry/decorator.ts#L14) decorator, which allows the system to know that this is a class that is compatible with the module. The only requirement for a model is the [ModelType](https://github.com/travetto/travetto/tree/main/module/model/src/types/model.ts#L10)
|
|
214
211
|
|
|
215
212
|
**Code: ModelType**
|
|
216
213
|
```typescript
|
|
@@ -224,7 +221,7 @@ export interface ModelType {
|
|
|
224
221
|
}
|
|
225
222
|
```
|
|
226
223
|
|
|
227
|
-
The `id` is the only required field for a model, as this is a hard requirement on naming and type.
|
|
224
|
+
The `id` is the only required field for a model, as this is a hard requirement on naming and type. This may make using existing data models impossible if types other than strings are required. Additionally, the `type` field, is intended to record the base model type, but can be remapped. This is important to support polymorphism, not only in [Data Modeling Support](https://github.com/travetto/travetto/tree/main/module/model#readme "Datastore abstraction for core operations."), but also in [Schema](https://github.com/travetto/travetto/tree/main/module/schema#readme "Data type registry for runtime validation, reflection and binding.").
|
|
228
225
|
|
|
229
226
|
## Implementations
|
|
230
227
|
|Service|Basic|CRUD|Indexed|Expiry|Blob|Bulk|
|
|
@@ -240,25 +237,24 @@ The `id` is the only required field for a model, as this is a hard requirement o
|
|
|
240
237
|
|[File Model Support](https://github.com/travetto/travetto/tree/main/module/model-file#readme "File system backing for the travetto model module.")|X|X| |X|X|X|
|
|
241
238
|
|
|
242
239
|
## Custom Model Service
|
|
243
|
-
In addition to the provided contracts, the module also provides common utilities and shared test suites.
|
|
240
|
+
In addition to the provided contracts, the module also provides common utilities and shared test suites. The common utilities are useful for repetitive functionality, that is unable to be shared due to not relying upon inheritance (this was an intentional design decision). This allows for all the [Data Modeling Support](https://github.com/travetto/travetto/tree/main/module/model#readme "Datastore abstraction for core operations.") implementations to completely own the functionality and also to be able to provide additional/unique functionality that goes beyond the interface. [Memory Model Support](https://github.com/travetto/travetto/tree/main/module/model-memory#readme "Memory backing for the travetto model module.") serves as a great example of what a full featured implementation can look like.
|
|
244
241
|
|
|
245
242
|
To enforce that these contracts are honored, the module provides shared test suites to allow for custom implementations to ensure they are adhering to the contract's expected behavior.
|
|
246
243
|
|
|
247
244
|
**Code: Memory Service Test Configuration**
|
|
248
245
|
```typescript
|
|
249
246
|
import { DependencyRegistryIndex } from '@travetto/di';
|
|
250
|
-
import {
|
|
247
|
+
import { type Class, castTo, classConstruct, RuntimeError } from '@travetto/runtime';
|
|
251
248
|
|
|
249
|
+
import type { ModelType } from '../../src/types/model.ts';
|
|
252
250
|
import { ModelBulkUtil } from '../../src/util/bulk.ts';
|
|
253
251
|
import { ModelCrudUtil } from '../../src/util/crud.ts';
|
|
254
|
-
import type { ModelType } from '../../src/types/model.ts';
|
|
255
252
|
import { ModelSuite } from './suite.ts';
|
|
256
253
|
|
|
257
|
-
type ServiceClass = { serviceClass: { new(): unknown } };
|
|
254
|
+
type ServiceClass = { serviceClass: { new (): unknown } };
|
|
258
255
|
|
|
259
256
|
@ModelSuite()
|
|
260
257
|
export abstract class BaseModelSuite<T> {
|
|
261
|
-
|
|
262
258
|
static ifNot(pred: (svc: unknown) => boolean): (x: unknown) => Promise<boolean> {
|
|
263
259
|
return async (x: unknown) => !pred(classConstruct(castTo<ServiceClass>(x).serviceClass));
|
|
264
260
|
}
|
|
@@ -267,7 +263,7 @@ export abstract class BaseModelSuite<T> {
|
|
|
267
263
|
configClass: Class;
|
|
268
264
|
|
|
269
265
|
async getSize<U extends ModelType>(cls: Class<U>): Promise<number> {
|
|
270
|
-
const svc =
|
|
266
|
+
const svc = await this.service;
|
|
271
267
|
if (ModelCrudUtil.isSupported(svc)) {
|
|
272
268
|
let i = 0;
|
|
273
269
|
for await (const batch of svc.list(cls)) {
|
|
@@ -282,7 +278,10 @@ export abstract class BaseModelSuite<T> {
|
|
|
282
278
|
async saveAll<M extends ModelType>(cls: Class<M>, items: M[]): Promise<number> {
|
|
283
279
|
const svc = await this.service;
|
|
284
280
|
if (ModelBulkUtil.isSupported(svc)) {
|
|
285
|
-
const result = await svc.processBulk(
|
|
281
|
+
const result = await svc.processBulk(
|
|
282
|
+
cls,
|
|
283
|
+
items.map(x => ({ insert: x }))
|
|
284
|
+
);
|
|
286
285
|
return result.counts.insert;
|
|
287
286
|
} else if (ModelCrudUtil.isSupported(svc)) {
|
|
288
287
|
const out: Promise<M>[] = [];
|
|
@@ -311,7 +310,7 @@ export abstract class BaseModelSuite<T> {
|
|
|
311
310
|
```
|
|
312
311
|
|
|
313
312
|
## CLI - model:export
|
|
314
|
-
The module provides the ability to generate an export of the model structure from all the various [@Model](https://github.com/travetto/travetto/tree/main/module/model/src/registry/decorator.ts#L14)s within the application.
|
|
313
|
+
The module provides the ability to generate an export of the model structure from all the various [@Model](https://github.com/travetto/travetto/tree/main/module/model/src/registry/decorator.ts#L14)s within the application. This is useful for being able to generate the appropriate files to manually create the data schemas in production.
|
|
315
314
|
|
|
316
315
|
**Terminal: Help for model:export**
|
|
317
316
|
```bash
|
|
@@ -342,7 +341,7 @@ Examples:
|
|
|
342
341
|
```
|
|
343
342
|
|
|
344
343
|
## CLI - model:install
|
|
345
|
-
The module provides the ability to install all the various [@Model](https://github.com/travetto/travetto/tree/main/module/model/src/registry/decorator.ts#L14)s within the application given the current configuration being targeted.
|
|
344
|
+
The module provides the ability to install all the various [@Model](https://github.com/travetto/travetto/tree/main/module/model/src/registry/decorator.ts#L14)s within the application given the current configuration being targeted. This is useful for being able to prepare the datastore manually.
|
|
346
345
|
|
|
347
346
|
**Terminal: Help for model:install**
|
|
348
347
|
```bash
|
package/__index__.ts
CHANGED
|
@@ -1,23 +1,20 @@
|
|
|
1
|
+
export * from './src/error/exists.ts';
|
|
2
|
+
export * from './src/error/invalid-index.ts';
|
|
3
|
+
export * from './src/error/invalid-sub-type.ts';
|
|
4
|
+
export * from './src/error/not-found.ts';
|
|
1
5
|
export * from './src/registry/decorator.ts';
|
|
2
|
-
export * from './src/registry/registry-index.ts';
|
|
3
6
|
export * from './src/registry/registry-adapter.ts';
|
|
7
|
+
export * from './src/registry/registry-index.ts';
|
|
4
8
|
export * from './src/registry/types.ts';
|
|
5
|
-
export * from './src/types/model.ts';
|
|
6
|
-
|
|
7
9
|
export * from './src/types/basic.ts';
|
|
8
10
|
export * from './src/types/blob.ts';
|
|
9
11
|
export * from './src/types/bulk.ts';
|
|
10
12
|
export * from './src/types/crud.ts';
|
|
11
13
|
export * from './src/types/expiry.ts';
|
|
14
|
+
export * from './src/types/model.ts';
|
|
12
15
|
export * from './src/types/storage.ts';
|
|
13
|
-
|
|
14
16
|
export * from './src/util/blob.ts';
|
|
15
17
|
export * from './src/util/bulk.ts';
|
|
16
18
|
export * from './src/util/crud.ts';
|
|
17
19
|
export * from './src/util/expiry.ts';
|
|
18
20
|
export * from './src/util/storage.ts';
|
|
19
|
-
|
|
20
|
-
export * from './src/error/exists.ts';
|
|
21
|
-
export * from './src/error/not-found.ts';
|
|
22
|
-
export * from './src/error/invalid-index.ts';
|
|
23
|
-
export * from './src/error/invalid-sub-type.ts';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@travetto/model",
|
|
3
|
-
"version": "8.0.0-alpha.
|
|
3
|
+
"version": "8.0.0-alpha.23",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Datastore abstraction for core operations.",
|
|
6
6
|
"keywords": [
|
|
@@ -27,14 +27,14 @@
|
|
|
27
27
|
"directory": "module/model"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@travetto/config": "^8.0.0-alpha.
|
|
31
|
-
"@travetto/di": "^8.0.0-alpha.
|
|
32
|
-
"@travetto/registry": "^8.0.0-alpha.
|
|
33
|
-
"@travetto/schema": "^8.0.0-alpha.
|
|
30
|
+
"@travetto/config": "^8.0.0-alpha.22",
|
|
31
|
+
"@travetto/di": "^8.0.0-alpha.20",
|
|
32
|
+
"@travetto/registry": "^8.0.0-alpha.20",
|
|
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/error/exists.ts
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import { type Class, RuntimeError } from '@travetto/runtime';
|
|
2
2
|
|
|
3
|
-
import type { ModelType } from '../types/model.ts';
|
|
4
3
|
import type { IndexConfig } from '../registry/types.ts';
|
|
4
|
+
import type { ModelType } from '../types/model.ts';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Represents when an index is invalid
|
|
8
8
|
*/
|
|
9
9
|
export class IndexNotSupported<T extends ModelType> extends RuntimeError {
|
|
10
10
|
constructor(cls: Class<T>, idx: IndexConfig, message: string = '') {
|
|
11
|
-
super(`${typeof cls === 'string' ? cls : cls.name} and index ${idx.name} of type ${idx.type} is not supported. ${message}`.trim(), {
|
|
11
|
+
super(`${typeof cls === 'string' ? cls : cls.name} and index ${idx.name} of type ${idx.type} is not supported. ${message}`.trim(), {
|
|
12
|
+
category: 'data'
|
|
13
|
+
});
|
|
12
14
|
}
|
|
13
|
-
}
|
|
15
|
+
}
|
package/src/error/not-found.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { type Class, castTo, getClass } from '@travetto/runtime';
|
|
2
2
|
import { SchemaRegistryIndex } from '@travetto/schema';
|
|
3
3
|
|
|
4
4
|
import type { ModelType } from '../types/model.ts';
|
|
5
|
-
import type { DataHandler, ModelConfig, PrePersistScope } from './types.ts';
|
|
6
5
|
import { ModelRegistryIndex } from './registry-index.ts';
|
|
6
|
+
import type { DataHandler, ModelConfig, PrePersistScope } from './types.ts';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Model decorator, extends `@Schema`
|
|
@@ -43,10 +43,12 @@ export function ExpiresAt() {
|
|
|
43
43
|
export function PrePersist<T>(handler: DataHandler<T>, scope: PrePersistScope = 'all') {
|
|
44
44
|
return function (cls: Class<T>): void {
|
|
45
45
|
ModelRegistryIndex.getForRegister(cls).register({
|
|
46
|
-
prePersist: [
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
46
|
+
prePersist: [
|
|
47
|
+
{
|
|
48
|
+
scope,
|
|
49
|
+
handler: castTo(handler)
|
|
50
|
+
}
|
|
51
|
+
]
|
|
50
52
|
});
|
|
51
53
|
};
|
|
52
54
|
}
|
|
@@ -59,13 +61,15 @@ export function PrePersist<T>(handler: DataHandler<T>, scope: PrePersistScope =
|
|
|
59
61
|
export function PersistValue<T>(handler: (current: T | undefined) => T, scope: PrePersistScope = 'all') {
|
|
60
62
|
return function <K extends string, C extends Partial<Record<K, T>>>(instance: C, property: K): void {
|
|
61
63
|
ModelRegistryIndex.getForRegister(getClass(instance)).register({
|
|
62
|
-
prePersist: [
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
64
|
+
prePersist: [
|
|
65
|
+
{
|
|
66
|
+
scope,
|
|
67
|
+
handler: (inst): void => {
|
|
68
|
+
const cInst: Record<K, T> = castTo(inst);
|
|
69
|
+
cInst[property] = handler(cInst[property]);
|
|
70
|
+
}
|
|
67
71
|
}
|
|
68
|
-
|
|
72
|
+
]
|
|
69
73
|
});
|
|
70
74
|
};
|
|
71
75
|
}
|
|
@@ -78,13 +82,15 @@ export function PersistValue<T>(handler: (current: T | undefined) => T, scope: P
|
|
|
78
82
|
export function Transient<T>() {
|
|
79
83
|
return function <K extends string, C extends Partial<Record<K, T>>>(instance: C, property: K): void {
|
|
80
84
|
ModelRegistryIndex.getForRegister(getClass(instance)).register({
|
|
81
|
-
prePersist: [
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
85
|
+
prePersist: [
|
|
86
|
+
{
|
|
87
|
+
scope: 'all',
|
|
88
|
+
handler: (inst): void => {
|
|
89
|
+
const cInst: Record<K, T> = castTo(inst);
|
|
90
|
+
delete cInst[property];
|
|
91
|
+
}
|
|
86
92
|
}
|
|
87
|
-
|
|
93
|
+
]
|
|
88
94
|
});
|
|
89
95
|
};
|
|
90
96
|
}
|
|
@@ -111,4 +117,4 @@ export function PostLoad<T>(handler: DataHandler<T>) {
|
|
|
111
117
|
return function (cls: Class<T>): void {
|
|
112
118
|
ModelRegistryIndex.getForRegister(cls).register({ postLoad: [castTo(handler)] });
|
|
113
119
|
};
|
|
114
|
-
}
|
|
120
|
+
}
|
|
@@ -10,7 +10,7 @@ function combineClasses(target: ModelConfig, sources: Partial<ModelConfig>[]): M
|
|
|
10
10
|
indices: { ...(target.indices || {}), ...(source.indices || {}) },
|
|
11
11
|
postLoad: [...(target.postLoad || []), ...(source.postLoad || [])],
|
|
12
12
|
prePersist: [...(target.prePersist || []), ...(source.prePersist || [])],
|
|
13
|
-
transientFields: [...(target.transientFields || []), ...(source.transientFields || [])]
|
|
13
|
+
transientFields: [...(target.transientFields || []), ...(source.transientFields || [])]
|
|
14
14
|
});
|
|
15
15
|
}
|
|
16
16
|
if (target.store) {
|
|
@@ -28,7 +28,7 @@ export class ModelRegistryAdapter implements RegistryAdapter<ModelConfig> {
|
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
register(...data: Partial<ModelConfig>[]): ModelConfig {
|
|
31
|
-
const config = this.#config ??= {
|
|
31
|
+
const config = (this.#config ??= {
|
|
32
32
|
class: this.#cls,
|
|
33
33
|
indices: {},
|
|
34
34
|
autoCreate: 'development',
|
|
@@ -36,7 +36,7 @@ export class ModelRegistryAdapter implements RegistryAdapter<ModelConfig> {
|
|
|
36
36
|
postLoad: [],
|
|
37
37
|
prePersist: [],
|
|
38
38
|
transientFields: []
|
|
39
|
-
};
|
|
39
|
+
});
|
|
40
40
|
combineClasses(config, data);
|
|
41
41
|
return config;
|
|
42
42
|
}
|
|
@@ -50,13 +50,13 @@ export class ModelRegistryAdapter implements RegistryAdapter<ModelConfig> {
|
|
|
50
50
|
config.store = parent.store;
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
config.postLoad = [...parent.postLoad ?? [], ...config.postLoad ?? []];
|
|
54
|
-
config.prePersist = [...parent.prePersist ?? [], ...config.prePersist ?? []];
|
|
55
|
-
config.transientFields = [...parent.transientFields ?? [], ...config.transientFields ?? []];
|
|
53
|
+
config.postLoad = [...(parent.postLoad ?? []), ...(config.postLoad ?? [])];
|
|
54
|
+
config.prePersist = [...(parent.prePersist ?? []), ...(config.prePersist ?? [])];
|
|
55
|
+
config.transientFields = [...(parent.transientFields ?? []), ...(config.transientFields ?? [])];
|
|
56
56
|
}
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
get(): ModelConfig {
|
|
60
60
|
return this.#config;
|
|
61
61
|
}
|
|
62
|
-
}
|
|
62
|
+
}
|
|
@@ -1,16 +1,15 @@
|
|
|
1
|
-
import { type RegistryIndex, RegistryIndexStore
|
|
2
|
-
import {
|
|
1
|
+
import { Registry, type RegistryIndex, RegistryIndexStore } from '@travetto/registry';
|
|
2
|
+
import { type Class, castTo, RuntimeError } from '@travetto/runtime';
|
|
3
3
|
import { SchemaRegistryIndex } from '@travetto/schema';
|
|
4
4
|
|
|
5
|
-
import type { IndexConfig, ModelConfig } from './types.ts';
|
|
6
5
|
import type { ModelType } from '../types/model.ts';
|
|
7
6
|
import { ModelRegistryAdapter } from './registry-adapter.ts';
|
|
7
|
+
import type { IndexConfig, ModelConfig } from './types.ts';
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* Model registry index for managing model configurations across classes
|
|
11
11
|
*/
|
|
12
12
|
export class ModelRegistryIndex implements RegistryIndex {
|
|
13
|
-
|
|
14
13
|
static #instance = Registry.registerIndex(this);
|
|
15
14
|
|
|
16
15
|
static getForRegister(cls: Class): ModelRegistryAdapter {
|
|
@@ -50,7 +49,9 @@ export class ModelRegistryIndex implements RegistryIndex {
|
|
|
50
49
|
|
|
51
50
|
store = new RegistryIndexStore(ModelRegistryAdapter);
|
|
52
51
|
|
|
53
|
-
/** @private */ constructor(source: unknown) {
|
|
52
|
+
/** @private */ constructor(source: unknown) {
|
|
53
|
+
Registry.validateConstructor(source);
|
|
54
|
+
}
|
|
54
55
|
|
|
55
56
|
onCreate(cls: Class): void {
|
|
56
57
|
const schema = SchemaRegistryIndex.getConfig(cls);
|
|
@@ -63,7 +64,7 @@ export class ModelRegistryIndex implements RegistryIndex {
|
|
|
63
64
|
const { store } = this.getConfig(cls);
|
|
64
65
|
let classes = this.#modelNameMapping.get(store);
|
|
65
66
|
if (!classes) {
|
|
66
|
-
this.#modelNameMapping.set(store, classes = new Set());
|
|
67
|
+
this.#modelNameMapping.set(store, (classes = new Set()));
|
|
67
68
|
}
|
|
68
69
|
classes.add(cls.Ⲑid);
|
|
69
70
|
|
|
@@ -104,4 +105,4 @@ export class ModelRegistryIndex implements RegistryIndex {
|
|
|
104
105
|
}
|
|
105
106
|
return castTo(expiry);
|
|
106
107
|
}
|
|
107
|
-
}
|
|
108
|
+
}
|
package/src/registry/types.ts
CHANGED
|
@@ -2,7 +2,7 @@ import type { Class } from '@travetto/runtime';
|
|
|
2
2
|
|
|
3
3
|
import type { ModelType } from '../types/model.ts';
|
|
4
4
|
|
|
5
|
-
export type DataHandler<T = unknown> = (inst: T) =>
|
|
5
|
+
export type DataHandler<T = unknown> = (inst: T) => Promise<T | void> | T | void;
|
|
6
6
|
|
|
7
7
|
export type PrePersistScope = 'full' | 'partial' | 'all';
|
|
8
8
|
|
|
@@ -55,7 +55,7 @@ export class ModelConfig<T extends ModelType = ModelType> {
|
|
|
55
55
|
/**
|
|
56
56
|
* Pre-persist handlers
|
|
57
57
|
*/
|
|
58
|
-
prePersist?: { scope: PrePersistScope
|
|
58
|
+
prePersist?: { scope: PrePersistScope; handler: DataHandler<unknown> }[];
|
|
59
59
|
/**
|
|
60
60
|
* Post-load handlers
|
|
61
61
|
*/
|
|
@@ -64,4 +64,4 @@ export class ModelConfig<T extends ModelType = ModelType> {
|
|
|
64
64
|
* Fields to ignore during persistence
|
|
65
65
|
*/
|
|
66
66
|
transientFields?: string[];
|
|
67
|
-
}
|
|
67
|
+
}
|
package/src/types/basic.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Class } from '@travetto/runtime';
|
|
2
|
+
|
|
2
3
|
import type { ModelIdSource, ModelType, OptionalId } from './model.ts';
|
|
3
4
|
|
|
4
5
|
/**
|
|
@@ -6,7 +7,6 @@ import type { ModelIdSource, ModelType, OptionalId } from './model.ts';
|
|
|
6
7
|
* @concrete
|
|
7
8
|
*/
|
|
8
9
|
export interface ModelBasicSupport<C = unknown> {
|
|
9
|
-
|
|
10
10
|
/**
|
|
11
11
|
* Id Source
|
|
12
12
|
*/
|
|
@@ -37,4 +37,4 @@ export interface ModelBasicSupport<C = unknown> {
|
|
|
37
37
|
* @throws {NotFoundError} When an item is not found
|
|
38
38
|
*/
|
|
39
39
|
delete<T extends ModelType>(cls: Class<T>, id: string): Promise<void>;
|
|
40
|
-
}
|
|
40
|
+
}
|
package/src/types/blob.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { BinaryMetadata, BinaryType, ByteRange, TimeSpan } from '@travetto/runtime';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Support for Blobs CRUD.
|
|
@@ -6,7 +6,6 @@ import type { BinaryType, BinaryMetadata, ByteRange, TimeSpan } from '@travetto/
|
|
|
6
6
|
* @concrete
|
|
7
7
|
*/
|
|
8
8
|
export interface ModelBlobSupport {
|
|
9
|
-
|
|
10
9
|
/**
|
|
11
10
|
* Upsert blob to storage
|
|
12
11
|
* @param location The location of the blob
|
|
@@ -58,4 +57,4 @@ export interface ModelBlobSupport {
|
|
|
58
57
|
* @param expiresIn Expiry
|
|
59
58
|
*/
|
|
60
59
|
getBlobWriteUrl?(location: string, metadata: BinaryMetadata, expiresIn?: TimeSpan): Promise<string>;
|
|
61
|
-
}
|
|
60
|
+
}
|
package/src/types/bulk.ts
CHANGED
|
@@ -7,11 +7,7 @@ import type { ModelType, OptionalId } from './model.ts';
|
|
|
7
7
|
/**
|
|
8
8
|
* Bulk operation. Each operation has a single action and payload
|
|
9
9
|
*/
|
|
10
|
-
export type BulkOperation<T extends ModelType> =
|
|
11
|
-
{ delete?: T } &
|
|
12
|
-
{ insert?: OptionalId<T> } &
|
|
13
|
-
{ update?: T } &
|
|
14
|
-
{ upsert?: OptionalId<T> };
|
|
10
|
+
export type BulkOperation<T extends ModelType> = { delete?: T } & { insert?: OptionalId<T> } & { update?: T } & { upsert?: OptionalId<T> };
|
|
15
11
|
|
|
16
12
|
/**
|
|
17
13
|
* Bulk response provides a summary of all the operations
|
|
@@ -37,13 +33,13 @@ export interface BulkResponse<E = unknown> {
|
|
|
37
33
|
};
|
|
38
34
|
}
|
|
39
35
|
|
|
40
|
-
type BulkErrorItem = { message: string
|
|
36
|
+
type BulkErrorItem = { message: string; type: string; errors?: ValidationError[]; idx: number };
|
|
41
37
|
|
|
42
38
|
/**
|
|
43
39
|
* Bulk processing error
|
|
44
40
|
*/
|
|
45
41
|
export class BulkProcessError extends RuntimeError<{ errors: BulkErrorItem[] }> {
|
|
46
|
-
constructor(errors: { idx: number
|
|
42
|
+
constructor(errors: { idx: number; error: ValidationResultError }[]) {
|
|
47
43
|
super('Bulk processing errors have occurred', {
|
|
48
44
|
category: 'data',
|
|
49
45
|
details: {
|
|
@@ -63,4 +59,4 @@ export class BulkProcessError extends RuntimeError<{ errors: BulkErrorItem[] }>
|
|
|
63
59
|
*/
|
|
64
60
|
export interface ModelBulkSupport extends ModelCrudSupport {
|
|
65
61
|
processBulk<T extends ModelType>(cls: Class<T>, operations: BulkOperation<T>[]): Promise<BulkResponse>;
|
|
66
|
-
}
|
|
62
|
+
}
|
package/src/types/crud.ts
CHANGED
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
import type { Class } from '@travetto/runtime';
|
|
2
2
|
|
|
3
|
-
import type { ModelListOptions, ModelType, OptionalId } from './model.ts';
|
|
4
|
-
|
|
5
3
|
import type { ModelBasicSupport } from './basic.ts';
|
|
4
|
+
import type { ModelListOptions, ModelType, OptionalId } from './model.ts';
|
|
6
5
|
|
|
7
6
|
/**
|
|
8
7
|
* Interface for simple CRUD
|
|
9
8
|
* @concrete
|
|
10
9
|
*/
|
|
11
10
|
export interface ModelCrudSupport extends ModelBasicSupport {
|
|
12
|
-
|
|
13
11
|
/**
|
|
14
12
|
* Update an item
|
|
15
13
|
* @param item The document to update.
|
|
@@ -46,4 +44,4 @@ export interface ModelCrudSupport extends ModelBasicSupport {
|
|
|
46
44
|
* @param options Options for listing
|
|
47
45
|
*/
|
|
48
46
|
list<T extends ModelType>(cls: Class<T>, options?: ModelListOptions): AsyncIterable<T[]>;
|
|
49
|
-
}
|
|
47
|
+
}
|
package/src/types/expiry.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Class } from '@travetto/runtime';
|
|
2
2
|
|
|
3
|
-
import type { ModelType } from './model.ts';
|
|
4
3
|
import type { ModelCrudSupport } from './crud.ts';
|
|
4
|
+
import type { ModelType } from './model.ts';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Support for managing expiration of data
|
|
@@ -14,4 +14,4 @@ export interface ModelExpirySupport extends ModelCrudSupport {
|
|
|
14
14
|
* @returns Returns the number of documents expired
|
|
15
15
|
*/
|
|
16
16
|
deleteExpired<T extends ModelType>(cls: Class<T>): Promise<number>;
|
|
17
|
-
}
|
|
17
|
+
}
|
package/src/types/model.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Model Id Source
|
|
3
3
|
*/
|
|
4
|
-
export type ModelIdSource = { create: () => string
|
|
4
|
+
export type ModelIdSource = { create: () => string; valid: (id: string) => boolean };
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Simple model interface
|
|
@@ -25,4 +25,4 @@ export interface ModelListOptions {
|
|
|
25
25
|
abort?: AbortSignal;
|
|
26
26
|
limit?: number;
|
|
27
27
|
batchSizeHint?: number;
|
|
28
|
-
}
|
|
28
|
+
}
|
package/src/types/storage.ts
CHANGED
|
@@ -12,7 +12,6 @@ import type { ModelType } from './model.ts';
|
|
|
12
12
|
* @concrete
|
|
13
13
|
*/
|
|
14
14
|
export interface ModelStorageSupport {
|
|
15
|
-
|
|
16
15
|
/**
|
|
17
16
|
* Should storage modification be allowed
|
|
18
17
|
*/
|
|
@@ -48,4 +47,4 @@ export interface ModelStorageSupport {
|
|
|
48
47
|
* Truncate blob storage data
|
|
49
48
|
*/
|
|
50
49
|
truncateBlob?(): Promise<void>;
|
|
51
|
-
}
|
|
50
|
+
}
|
package/src/util/blob.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { hasFunction } from '@travetto/runtime';
|
|
2
|
+
|
|
2
3
|
import type { ModelBlobSupport } from '../types/blob.ts';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Utilities for processing blobs
|
|
6
7
|
*/
|
|
7
8
|
export class ModelBlobUtil {
|
|
8
|
-
|
|
9
9
|
/**
|
|
10
10
|
* Type guard for determining if service supports blob operations
|
|
11
11
|
*/
|
|
@@ -15,4 +15,4 @@ export class ModelBlobUtil {
|
|
|
15
15
|
* Type guard for determining if service supports blob write urls
|
|
16
16
|
*/
|
|
17
17
|
static isWriteUrlSupported = hasFunction<ModelBlobSupport>('getBlobWriteUrl');
|
|
18
|
-
}
|
|
18
|
+
}
|