@travetto/model 8.0.0-alpha.9 → 8.0.1
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 +91 -102
- package/__index__.ts +7 -11
- package/package.json +18 -18
- 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/error/unique.ts +13 -0
- package/src/registry/decorator.ts +38 -32
- package/src/registry/registry-adapter.ts +11 -8
- package/src/registry/registry-index.ts +12 -33
- package/src/registry/types.ts +24 -39
- package/src/types/basic.ts +3 -3
- package/src/types/blob.ts +6 -6
- package/src/types/bulk.ts +5 -9
- package/src/types/crud.ts +9 -6
- package/src/types/expiry.ts +2 -2
- package/src/types/model.ts +11 -2
- package/src/types/storage.ts +2 -3
- package/src/util/blob.ts +2 -2
- package/src/util/bulk.ts +6 -3
- package/src/util/crud.ts +105 -22
- 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 +9 -5
- package/support/cli.model_install.ts +9 -5
- package/support/doc.support.tsx +16 -20
- package/support/test/base.ts +14 -12
- package/support/test/basic.ts +44 -3
- package/support/test/blob.ts +8 -9
- package/support/test/bulk.ts +16 -5
- package/support/test/crud.ts +134 -82
- package/support/test/expiry.ts +39 -21
- package/support/test/polymorphism.ts +43 -143
- package/support/test/suite.ts +15 -12
- package/src/types/indexed.ts +0 -43
- package/src/util/indexed.ts +0 -144
- package/support/test/indexed.ts +0 -190
package/README.md
CHANGED
|
@@ -13,10 +13,10 @@ 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
|
-
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#
|
|
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).
|
|
20
20
|
|
|
21
21
|
**Code: Basic Structure**
|
|
22
22
|
```typescript
|
|
@@ -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.
|
|
@@ -110,54 +108,21 @@ export interface ModelCrudSupport extends ModelBasicSupport {
|
|
|
110
108
|
updatePartial<T extends ModelType>(cls: Class<T>, item: Partial<T> & { id: string }, view?: string): Promise<T>;
|
|
111
109
|
|
|
112
110
|
/**
|
|
113
|
-
* List all items
|
|
111
|
+
* List all items of a collection, results returned in batches of items.
|
|
112
|
+
*
|
|
113
|
+
* Note: Batch size hint can be used to optimize batch size, but is not guaranteed.
|
|
114
|
+
*
|
|
115
|
+
* @param cls The class to list
|
|
116
|
+
* @param options Options for listing
|
|
114
117
|
*/
|
|
115
|
-
list<T extends ModelType>(cls: Class<T
|
|
118
|
+
list<T extends ModelType>(cls: Class<T>, options?: ModelListOptions): AsyncIterable<T[]>;
|
|
116
119
|
}
|
|
117
120
|
```
|
|
118
121
|
|
|
119
|
-
|
|
120
|
-
Additionally, an implementation may support the ability for basic [Indexed](https://github.com/travetto/travetto/tree/main/module/model/src/types/indexed.ts#L11) queries. This is not the full featured query support of [Data Model Querying](https://github.com/travetto/travetto/tree/main/module/model-query#readme "Datastore abstraction for advanced query support."), but allowing for indexed lookups. This does not support listing by index, but may be added at a later date.
|
|
121
|
-
|
|
122
|
-
**Code: Indexed Contract**
|
|
123
|
-
```typescript
|
|
124
|
-
export interface ModelIndexedSupport extends ModelBasicSupport {
|
|
125
|
-
/**
|
|
126
|
-
* Get entity by index as defined by fields of idx and the body fields
|
|
127
|
-
* @param cls The type to search by
|
|
128
|
-
* @param idx The index name to search against
|
|
129
|
-
* @param body The payload of fields needed to search
|
|
130
|
-
*/
|
|
131
|
-
getByIndex<T extends ModelType>(cls: Class<T>, idx: string, body: DeepPartial<T>): Promise<T>;
|
|
132
|
-
|
|
133
|
-
/**
|
|
134
|
-
* Delete entity by index as defined by fields of idx and the body fields
|
|
135
|
-
* @param cls The type to search by
|
|
136
|
-
* @param idx The index name to search against
|
|
137
|
-
* @param body The payload of fields needed to search
|
|
138
|
-
*/
|
|
139
|
-
deleteByIndex<T extends ModelType>(cls: Class<T>, idx: string, body: DeepPartial<T>): Promise<void>;
|
|
140
|
-
|
|
141
|
-
/**
|
|
142
|
-
* List entity by ranged index as defined by fields of idx and the body fields
|
|
143
|
-
* @param cls The type to search by
|
|
144
|
-
* @param idx The index name to search against
|
|
145
|
-
* @param body The payload of fields needed to search
|
|
146
|
-
*/
|
|
147
|
-
listByIndex<T extends ModelType>(cls: Class<T>, idx: string, body?: DeepPartial<T>): AsyncIterable<T>;
|
|
148
|
-
|
|
149
|
-
/**
|
|
150
|
-
* Upsert by index, allowing the index to act as a primary key
|
|
151
|
-
* @param cls The type to create for
|
|
152
|
-
* @param idx The index name to use
|
|
153
|
-
* @param body The document to potentially store
|
|
154
|
-
*/
|
|
155
|
-
upsertByIndex<T extends ModelType>(cls: Class<T>, idx: string, body: OptionalId<T>): Promise<T>;
|
|
156
|
-
}
|
|
157
|
-
```
|
|
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.
|
|
158
123
|
|
|
159
124
|
### Expiry
|
|
160
|
-
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.
|
|
161
126
|
|
|
162
127
|
**Code: Expiry Contract**
|
|
163
128
|
```typescript
|
|
@@ -172,12 +137,11 @@ export interface ModelExpirySupport extends ModelCrudSupport {
|
|
|
172
137
|
```
|
|
173
138
|
|
|
174
139
|
### Blob
|
|
175
|
-
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.
|
|
176
141
|
|
|
177
142
|
**Code: Blob Contract**
|
|
178
143
|
```typescript
|
|
179
144
|
export interface ModelBlobSupport {
|
|
180
|
-
|
|
181
145
|
/**
|
|
182
146
|
* Upsert blob to storage
|
|
183
147
|
* @param location The location of the blob
|
|
@@ -213,12 +177,13 @@ export interface ModelBlobSupport {
|
|
|
213
177
|
updateBlobMetadata(location: string, metadata: BinaryMetadata): Promise<void>;
|
|
214
178
|
|
|
215
179
|
/**
|
|
216
|
-
* Produces an externally usable URL for sharing limited read access to a specific resource
|
|
180
|
+
* Produces an externally usable URL for sharing limited read access to a specific resource.
|
|
181
|
+
* If expiresIn is explicitly set to false, returns a direct/public URL.
|
|
217
182
|
*
|
|
218
183
|
* @param location The asset location to read from
|
|
219
|
-
* @param expiresIn Expiry
|
|
184
|
+
* @param expiresIn Expiry or false for public/direct URL
|
|
220
185
|
*/
|
|
221
|
-
getBlobReadUrl?(location: string, expiresIn?: TimeSpan): Promise<string>;
|
|
186
|
+
getBlobReadUrl?(location: string, expiresIn?: TimeSpan | false): Promise<string>;
|
|
222
187
|
|
|
223
188
|
/**
|
|
224
189
|
* Produces an externally usable URL for sharing allowing direct write access
|
|
@@ -232,7 +197,7 @@ export interface ModelBlobSupport {
|
|
|
232
197
|
```
|
|
233
198
|
|
|
234
199
|
### Bulk
|
|
235
|
-
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.
|
|
236
201
|
|
|
237
202
|
**Code: Bulk Contract**
|
|
238
203
|
```typescript
|
|
@@ -242,7 +207,7 @@ export interface ModelBulkSupport extends ModelCrudSupport {
|
|
|
242
207
|
```
|
|
243
208
|
|
|
244
209
|
## Declaration
|
|
245
|
-
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)
|
|
246
211
|
|
|
247
212
|
**Code: ModelType**
|
|
248
213
|
```typescript
|
|
@@ -256,7 +221,7 @@ export interface ModelType {
|
|
|
256
221
|
}
|
|
257
222
|
```
|
|
258
223
|
|
|
259
|
-
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.").
|
|
260
225
|
|
|
261
226
|
## Implementations
|
|
262
227
|
|Service|Basic|CRUD|Indexed|Expiry|Blob|Bulk|
|
|
@@ -272,25 +237,24 @@ The `id` is the only required field for a model, as this is a hard requirement o
|
|
|
272
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|
|
|
273
238
|
|
|
274
239
|
## Custom Model Service
|
|
275
|
-
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.
|
|
276
241
|
|
|
277
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.
|
|
278
243
|
|
|
279
244
|
**Code: Memory Service Test Configuration**
|
|
280
245
|
```typescript
|
|
281
246
|
import { DependencyRegistryIndex } from '@travetto/di';
|
|
282
|
-
import {
|
|
247
|
+
import { type Class, castTo, classConstruct, RuntimeError } from '@travetto/runtime';
|
|
283
248
|
|
|
249
|
+
import type { ModelType } from '../../src/types/model.ts';
|
|
284
250
|
import { ModelBulkUtil } from '../../src/util/bulk.ts';
|
|
285
251
|
import { ModelCrudUtil } from '../../src/util/crud.ts';
|
|
286
|
-
import type { ModelType } from '../../src/types/model.ts';
|
|
287
252
|
import { ModelSuite } from './suite.ts';
|
|
288
253
|
|
|
289
|
-
type ServiceClass = { serviceClass: { new(): unknown } };
|
|
254
|
+
type ServiceClass = { serviceClass: { new (): unknown } };
|
|
290
255
|
|
|
291
256
|
@ModelSuite()
|
|
292
257
|
export abstract class BaseModelSuite<T> {
|
|
293
|
-
|
|
294
258
|
static ifNot(pred: (svc: unknown) => boolean): (x: unknown) => Promise<boolean> {
|
|
295
259
|
return async (x: unknown) => !pred(classConstruct(castTo<ServiceClass>(x).serviceClass));
|
|
296
260
|
}
|
|
@@ -299,11 +263,11 @@ export abstract class BaseModelSuite<T> {
|
|
|
299
263
|
configClass: Class;
|
|
300
264
|
|
|
301
265
|
async getSize<U extends ModelType>(cls: Class<U>): Promise<number> {
|
|
302
|
-
const svc =
|
|
266
|
+
const svc = await this.service;
|
|
303
267
|
if (ModelCrudUtil.isSupported(svc)) {
|
|
304
268
|
let i = 0;
|
|
305
|
-
for await (const
|
|
306
|
-
i +=
|
|
269
|
+
for await (const batch of svc.list(cls)) {
|
|
270
|
+
i += batch.length;
|
|
307
271
|
}
|
|
308
272
|
return i;
|
|
309
273
|
} else {
|
|
@@ -314,7 +278,10 @@ export abstract class BaseModelSuite<T> {
|
|
|
314
278
|
async saveAll<M extends ModelType>(cls: Class<M>, items: M[]): Promise<number> {
|
|
315
279
|
const svc = await this.service;
|
|
316
280
|
if (ModelBulkUtil.isSupported(svc)) {
|
|
317
|
-
const result = await svc.processBulk(
|
|
281
|
+
const result = await svc.processBulk(
|
|
282
|
+
cls,
|
|
283
|
+
items.map(x => ({ insert: x }))
|
|
284
|
+
);
|
|
318
285
|
return result.counts.insert;
|
|
319
286
|
} else if (ModelCrudUtil.isSupported(svc)) {
|
|
320
287
|
const out: Promise<M>[] = [];
|
|
@@ -332,58 +299,80 @@ export abstract class BaseModelSuite<T> {
|
|
|
332
299
|
return DependencyRegistryIndex.getInstance(this.serviceClass);
|
|
333
300
|
}
|
|
334
301
|
|
|
335
|
-
async toArray<U>(src: AsyncIterable<U> | AsyncGenerator<U>): Promise<U[]> {
|
|
336
|
-
const out: U[] = [];
|
|
302
|
+
async toArray<U>(src: AsyncIterable<U | U[]> | AsyncGenerator<U | U[]>): Promise<U[]> {
|
|
303
|
+
const out: (U | U[])[] = [];
|
|
337
304
|
for await (const el of src) {
|
|
338
305
|
out.push(el);
|
|
339
306
|
}
|
|
340
|
-
return out;
|
|
307
|
+
return castTo(out.flat());
|
|
341
308
|
}
|
|
342
309
|
}
|
|
343
310
|
```
|
|
344
311
|
|
|
345
312
|
## CLI - model:export
|
|
346
|
-
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.
|
|
347
314
|
|
|
348
|
-
**Terminal:
|
|
315
|
+
**Terminal: Help for model:export**
|
|
349
316
|
```bash
|
|
350
317
|
$ trv model:export --help
|
|
351
318
|
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
319
|
+
TypeError: Cannot redefine property: toJSON
|
|
320
|
+
at Object.defineProperty (<anonymous>)
|
|
321
|
+
at file://<workspace-root>/module/runtime/src/json.ts:17:8
|
|
322
|
+
at ModuleJob.run (node:internal/modules/esm/module_job:569:25)
|
|
323
|
+
at async node:internal/modules/esm/loader:650:26
|
|
324
|
+
at async $Runtime.importFrom (<workspace-root>/module/runtime/src/context.ts:136:22)
|
|
325
|
+
at async $Registry.#init (<workspace-root>/module/registry/src/registry.ts:88:9)
|
|
326
|
+
at async ModelExportCommand.help (./support/base-command.ts:38:5)
|
|
327
|
+
at async HelpUtil.getExtendedHelpMessage (<workspace-root>/module/cli/src/help.ts:121:30)
|
|
328
|
+
at async HelpUtil.renderCommandHelp (<workspace-root>/module/cli/src/help.ts:170:11)
|
|
329
|
+
at async ExecutionManager.getExecutionCommand (<workspace-root>/module/cli/src/execute.ts:49:22)
|
|
330
|
+
at async ExecutionManager.run (<workspace-root>/module/cli/src/execute.ts:67:23)
|
|
331
|
+
|
|
332
|
+
TypeError: Cannot redefine property: toJSON
|
|
333
|
+
at Object.defineProperty (<anonymous>)
|
|
334
|
+
at file://<workspace-root>/module/runtime/src/json.ts:17:8
|
|
335
|
+
at ModuleJob.run (node:internal/modules/esm/module_job:569:25)
|
|
336
|
+
at async node:internal/modules/esm/loader:650:26
|
|
337
|
+
at async $Runtime.importFrom (<workspace-root>/module/runtime/src/context.ts:136:22)
|
|
338
|
+
at async $Registry.#init (<workspace-root>/module/registry/src/registry.ts:88:9)
|
|
339
|
+
at async ModelExportCommand.help (./support/base-command.ts:38:5)
|
|
340
|
+
at async HelpUtil.getExtendedHelpMessage (<workspace-root>/module/cli/src/help.ts:121:30)
|
|
341
|
+
at async HelpUtil.renderCommandHelp (<workspace-root>/module/cli/src/help.ts:170:11)
|
|
342
|
+
at async ExecutionManager.getExecutionCommand (<workspace-root>/module/cli/src/execute.ts:49:22)
|
|
343
|
+
at async ExecutionManager.run (<workspace-root>/module/cli/src/execute.ts:67:23)
|
|
366
344
|
```
|
|
367
345
|
|
|
368
346
|
## CLI - model:install
|
|
369
|
-
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.
|
|
347
|
+
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.
|
|
370
348
|
|
|
371
|
-
**Terminal:
|
|
349
|
+
**Terminal: Help for model:install**
|
|
372
350
|
```bash
|
|
373
351
|
$ trv model:install --help
|
|
374
352
|
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
353
|
+
TypeError: Cannot redefine property: toJSON
|
|
354
|
+
at Object.defineProperty (<anonymous>)
|
|
355
|
+
at file://<workspace-root>/module/runtime/src/json.ts:17:8
|
|
356
|
+
at ModuleJob.run (node:internal/modules/esm/module_job:569:25)
|
|
357
|
+
at async node:internal/modules/esm/loader:650:26
|
|
358
|
+
at async $Runtime.importFrom (<workspace-root>/module/runtime/src/context.ts:136:22)
|
|
359
|
+
at async $Registry.#init (<workspace-root>/module/registry/src/registry.ts:88:9)
|
|
360
|
+
at async ModelInstallCommand.help (./support/base-command.ts:38:5)
|
|
361
|
+
at async HelpUtil.getExtendedHelpMessage (<workspace-root>/module/cli/src/help.ts:121:30)
|
|
362
|
+
at async HelpUtil.renderCommandHelp (<workspace-root>/module/cli/src/help.ts:170:11)
|
|
363
|
+
at async ExecutionManager.getExecutionCommand (<workspace-root>/module/cli/src/execute.ts:49:22)
|
|
364
|
+
at async ExecutionManager.run (<workspace-root>/module/cli/src/execute.ts:67:23)
|
|
365
|
+
|
|
366
|
+
TypeError: Cannot redefine property: toJSON
|
|
367
|
+
at Object.defineProperty (<anonymous>)
|
|
368
|
+
at file://<workspace-root>/module/runtime/src/json.ts:17:8
|
|
369
|
+
at ModuleJob.run (node:internal/modules/esm/module_job:569:25)
|
|
370
|
+
at async node:internal/modules/esm/loader:650:26
|
|
371
|
+
at async $Runtime.importFrom (<workspace-root>/module/runtime/src/context.ts:136:22)
|
|
372
|
+
at async $Registry.#init (<workspace-root>/module/registry/src/registry.ts:88:9)
|
|
373
|
+
at async ModelInstallCommand.help (./support/base-command.ts:38:5)
|
|
374
|
+
at async HelpUtil.getExtendedHelpMessage (<workspace-root>/module/cli/src/help.ts:121:30)
|
|
375
|
+
at async HelpUtil.renderCommandHelp (<workspace-root>/module/cli/src/help.ts:170:11)
|
|
376
|
+
at async ExecutionManager.getExecutionCommand (<workspace-root>/module/cli/src/execute.ts:49:22)
|
|
377
|
+
at async ExecutionManager.run (<workspace-root>/module/cli/src/execute.ts:67:23)
|
|
389
378
|
```
|
package/__index__.ts
CHANGED
|
@@ -1,25 +1,21 @@
|
|
|
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';
|
|
5
|
+
export * from './src/error/unique.ts';
|
|
1
6
|
export * from './src/registry/decorator.ts';
|
|
2
|
-
export * from './src/registry/registry-index.ts';
|
|
3
7
|
export * from './src/registry/registry-adapter.ts';
|
|
8
|
+
export * from './src/registry/registry-index.ts';
|
|
4
9
|
export * from './src/registry/types.ts';
|
|
5
|
-
export * from './src/types/model.ts';
|
|
6
|
-
|
|
7
10
|
export * from './src/types/basic.ts';
|
|
8
11
|
export * from './src/types/blob.ts';
|
|
9
12
|
export * from './src/types/bulk.ts';
|
|
10
13
|
export * from './src/types/crud.ts';
|
|
11
|
-
export * from './src/types/indexed.ts';
|
|
12
14
|
export * from './src/types/expiry.ts';
|
|
15
|
+
export * from './src/types/model.ts';
|
|
13
16
|
export * from './src/types/storage.ts';
|
|
14
|
-
|
|
15
17
|
export * from './src/util/blob.ts';
|
|
16
18
|
export * from './src/util/bulk.ts';
|
|
17
19
|
export * from './src/util/crud.ts';
|
|
18
20
|
export * from './src/util/expiry.ts';
|
|
19
|
-
export * from './src/util/indexed.ts';
|
|
20
21
|
export * from './src/util/storage.ts';
|
|
21
|
-
|
|
22
|
-
export * from './src/error/exists.ts';
|
|
23
|
-
export * from './src/error/not-found.ts';
|
|
24
|
-
export * from './src/error/invalid-index.ts';
|
|
25
|
-
export * from './src/error/invalid-sub-type.ts';
|
package/package.json
CHANGED
|
@@ -1,40 +1,43 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@travetto/model",
|
|
3
|
-
"version": "8.0.
|
|
4
|
-
"type": "module",
|
|
3
|
+
"version": "8.0.1",
|
|
5
4
|
"description": "Datastore abstraction for core operations.",
|
|
6
5
|
"keywords": [
|
|
7
6
|
"datastore",
|
|
8
7
|
"decorators",
|
|
9
8
|
"schema",
|
|
10
|
-
"
|
|
11
|
-
"
|
|
9
|
+
"travetto",
|
|
10
|
+
"typescript"
|
|
12
11
|
],
|
|
13
12
|
"homepage": "https://travetto.io",
|
|
14
13
|
"license": "MIT",
|
|
15
14
|
"author": {
|
|
16
|
-
"
|
|
17
|
-
"
|
|
15
|
+
"name": "Travetto Framework",
|
|
16
|
+
"email": "travetto.framework@gmail.com"
|
|
17
|
+
},
|
|
18
|
+
"repository": {
|
|
19
|
+
"url": "git+https://github.com/travetto/travetto.git",
|
|
20
|
+
"directory": "module/model"
|
|
18
21
|
},
|
|
19
22
|
"files": [
|
|
20
23
|
"__index__.ts",
|
|
21
24
|
"src",
|
|
22
25
|
"support"
|
|
23
26
|
],
|
|
27
|
+
"type": "module",
|
|
24
28
|
"main": "__index__.ts",
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
"directory": "module/model"
|
|
29
|
+
"publishConfig": {
|
|
30
|
+
"access": "public"
|
|
28
31
|
},
|
|
29
32
|
"dependencies": {
|
|
30
|
-
"@travetto/config": "^8.0.
|
|
31
|
-
"@travetto/di": "^8.0.
|
|
32
|
-
"@travetto/registry": "^8.0.
|
|
33
|
-
"@travetto/schema": "^8.0.
|
|
33
|
+
"@travetto/config": "^8.0.1",
|
|
34
|
+
"@travetto/di": "^8.0.1",
|
|
35
|
+
"@travetto/registry": "^8.0.1",
|
|
36
|
+
"@travetto/schema": "^8.0.1"
|
|
34
37
|
},
|
|
35
38
|
"peerDependencies": {
|
|
36
|
-
"@travetto/cli": "^8.0.
|
|
37
|
-
"@travetto/test": "^8.0.
|
|
39
|
+
"@travetto/cli": "^8.0.1",
|
|
40
|
+
"@travetto/test": "^8.0.1"
|
|
38
41
|
},
|
|
39
42
|
"peerDependenciesMeta": {
|
|
40
43
|
"@travetto/cli": {
|
|
@@ -46,8 +49,5 @@
|
|
|
46
49
|
},
|
|
47
50
|
"travetto": {
|
|
48
51
|
"displayName": "Data Modeling Support"
|
|
49
|
-
},
|
|
50
|
-
"publishConfig": {
|
|
51
|
-
"access": "public"
|
|
52
52
|
}
|
|
53
53
|
}
|
package/src/error/exists.ts
CHANGED
|
@@ -7,7 +7,9 @@ import type { ModelType } from '../types/model.ts';
|
|
|
7
7
|
* Represents when an index is invalid
|
|
8
8
|
*/
|
|
9
9
|
export class IndexNotSupported<T extends ModelType> extends RuntimeError {
|
|
10
|
-
constructor(cls: Class<T>, idx: IndexConfig
|
|
11
|
-
super(`${typeof cls === 'string' ? cls : cls.name} and index ${idx.name} of type ${idx.type} is not supported. ${message}`.trim(), {
|
|
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(), {
|
|
12
|
+
category: 'data'
|
|
13
|
+
});
|
|
12
14
|
}
|
|
13
|
-
}
|
|
15
|
+
}
|
package/src/error/not-found.ts
CHANGED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type Class, RuntimeError } from '@travetto/runtime';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Represents when a data item violates a unique constraint or index
|
|
5
|
+
*/
|
|
6
|
+
export class UniqueError extends RuntimeError {
|
|
7
|
+
constructor(cls: Class | string, constraint: string, details: Record<string, unknown> = {}) {
|
|
8
|
+
super(`${typeof cls === 'string' ? cls : cls.name} violates unique constraint on ${constraint}`, {
|
|
9
|
+
category: 'data',
|
|
10
|
+
details: { constraint, type: typeof cls === 'string' ? cls : cls.name, ...details }
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -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, IndexConfig, 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`
|
|
@@ -24,19 +24,6 @@ export function Model(config: Partial<ModelConfig<ModelType>> | string = {}) {
|
|
|
24
24
|
};
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
/**
|
|
28
|
-
* Defines an index on a model
|
|
29
|
-
* @kind decorator
|
|
30
|
-
*/
|
|
31
|
-
export function Index<T extends ModelType>(...indices: IndexConfig<T>[]) {
|
|
32
|
-
if (indices.some(config => config.fields.some(field => field === 'id'))) {
|
|
33
|
-
throw new RuntimeError('Cannot create an index with the id field');
|
|
34
|
-
}
|
|
35
|
-
return function (cls: Class<T>): void {
|
|
36
|
-
ModelRegistryIndex.getForRegister(cls).register({ indices });
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
|
|
40
27
|
/**
|
|
41
28
|
* Model field decorator for denoting expiry date/time
|
|
42
29
|
* @augments `@travetto/schema:Field`
|
|
@@ -56,10 +43,12 @@ export function ExpiresAt() {
|
|
|
56
43
|
export function PrePersist<T>(handler: DataHandler<T>, scope: PrePersistScope = 'all') {
|
|
57
44
|
return function (cls: Class<T>): void {
|
|
58
45
|
ModelRegistryIndex.getForRegister(cls).register({
|
|
59
|
-
prePersist: [
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
46
|
+
prePersist: [
|
|
47
|
+
{
|
|
48
|
+
scope,
|
|
49
|
+
handler: castTo(handler)
|
|
50
|
+
}
|
|
51
|
+
]
|
|
63
52
|
});
|
|
64
53
|
};
|
|
65
54
|
}
|
|
@@ -72,13 +61,15 @@ export function PrePersist<T>(handler: DataHandler<T>, scope: PrePersistScope =
|
|
|
72
61
|
export function PersistValue<T>(handler: (current: T | undefined) => T, scope: PrePersistScope = 'all') {
|
|
73
62
|
return function <K extends string, C extends Partial<Record<K, T>>>(instance: C, property: K): void {
|
|
74
63
|
ModelRegistryIndex.getForRegister(getClass(instance)).register({
|
|
75
|
-
prePersist: [
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
64
|
+
prePersist: [
|
|
65
|
+
{
|
|
66
|
+
scope,
|
|
67
|
+
handler: (inst): void => {
|
|
68
|
+
const cInst: Record<K, T> = castTo(inst);
|
|
69
|
+
cInst[property] = handler(cInst[property]);
|
|
70
|
+
}
|
|
80
71
|
}
|
|
81
|
-
|
|
72
|
+
]
|
|
82
73
|
});
|
|
83
74
|
};
|
|
84
75
|
}
|
|
@@ -91,13 +82,28 @@ export function PersistValue<T>(handler: (current: T | undefined) => T, scope: P
|
|
|
91
82
|
export function Transient<T>() {
|
|
92
83
|
return function <K extends string, C extends Partial<Record<K, T>>>(instance: C, property: K): void {
|
|
93
84
|
ModelRegistryIndex.getForRegister(getClass(instance)).register({
|
|
94
|
-
prePersist: [
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
85
|
+
prePersist: [
|
|
86
|
+
{
|
|
87
|
+
scope: 'all',
|
|
88
|
+
handler: (inst): void => {
|
|
89
|
+
const cInst: Record<K, T> = castTo(inst);
|
|
90
|
+
delete cInst[property];
|
|
91
|
+
}
|
|
99
92
|
}
|
|
100
|
-
|
|
93
|
+
]
|
|
94
|
+
});
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Prevent a field from being persisted
|
|
100
|
+
* @augments `@travetto/schema:Field`
|
|
101
|
+
* @kind decorator
|
|
102
|
+
*/
|
|
103
|
+
export function TransientField() {
|
|
104
|
+
return function <K extends string, C extends Partial<Record<K, unknown>>>(instance: C, property: K): void {
|
|
105
|
+
ModelRegistryIndex.getForRegister(getClass(instance)).register({
|
|
106
|
+
transientFields: [property]
|
|
101
107
|
});
|
|
102
108
|
};
|
|
103
109
|
}
|
|
@@ -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
|
+
}
|