@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.
Files changed (42) hide show
  1. package/README.md +91 -102
  2. package/__index__.ts +7 -11
  3. package/package.json +18 -18
  4. package/src/error/exists.ts +1 -1
  5. package/src/error/invalid-index.ts +5 -3
  6. package/src/error/invalid-sub-type.ts +1 -1
  7. package/src/error/not-found.ts +1 -1
  8. package/src/error/unique.ts +13 -0
  9. package/src/registry/decorator.ts +38 -32
  10. package/src/registry/registry-adapter.ts +11 -8
  11. package/src/registry/registry-index.ts +12 -33
  12. package/src/registry/types.ts +24 -39
  13. package/src/types/basic.ts +3 -3
  14. package/src/types/blob.ts +6 -6
  15. package/src/types/bulk.ts +5 -9
  16. package/src/types/crud.ts +9 -6
  17. package/src/types/expiry.ts +2 -2
  18. package/src/types/model.ts +11 -2
  19. package/src/types/storage.ts +2 -3
  20. package/src/util/blob.ts +2 -2
  21. package/src/util/bulk.ts +6 -3
  22. package/src/util/crud.ts +105 -22
  23. package/src/util/expiry.ts +4 -5
  24. package/src/util/storage.ts +3 -4
  25. package/support/base-command.ts +3 -5
  26. package/support/bin/candidate.ts +8 -11
  27. package/support/bin/export.ts +2 -2
  28. package/support/bin/install.ts +2 -2
  29. package/support/cli.model_export.ts +9 -5
  30. package/support/cli.model_install.ts +9 -5
  31. package/support/doc.support.tsx +16 -20
  32. package/support/test/base.ts +14 -12
  33. package/support/test/basic.ts +44 -3
  34. package/support/test/blob.ts +8 -9
  35. package/support/test/bulk.ts +16 -5
  36. package/support/test/crud.ts +134 -82
  37. package/support/test/expiry.ts +39 -21
  38. package/support/test/polymorphism.ts +43 -143
  39. package/support/test/suite.ts +15 -12
  40. package/src/types/indexed.ts +0 -43
  41. package/src/util/indexed.ts +0 -144
  42. 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. 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.
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#L16).
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. These contracts allow for persisting and fetching of the associated model object.
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. 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#L8), [CRUD](https://github.com/travetto/travetto/tree/main/module/model/src/types/crud.ts#L11), [Indexed](https://github.com/travetto/travetto/tree/main/module/model/src/types/indexed.ts#L11), [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#L64).
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#L8) contract to be able to participate in the model ecosystem. This contract represents the bare minimum for a model service.
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#L11) 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 scaffolding 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 scaffolding for the Travetto framework") model extension.
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>): AsyncIterable<T>;
118
+ list<T extends ModelType>(cls: Class<T>, options?: ModelListOptions): AsyncIterable<T[]>;
116
119
  }
117
120
  ```
118
121
 
119
- ### Indexed
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. 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.
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). 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.
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#L64) 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.
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. The only requirement for a model is the [ModelType](https://github.com/travetto/travetto/tree/main/module/model/src/types/model.ts#L10)
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. 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.").
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. 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.
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 { RuntimeError, castTo, type Class, classConstruct } from '@travetto/runtime';
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 = (await this.service);
266
+ const svc = await this.service;
303
267
  if (ModelCrudUtil.isSupported(svc)) {
304
268
  let i = 0;
305
- for await (const __el of svc.list(cls)) {
306
- i += 1;
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(cls, items.map(x => ({ insert: x })));
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. This is useful for being able to generate the appropriate files to manually create the data schemas in production.
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: Running model export**
315
+ **Terminal: Help for model:export**
349
316
  ```bash
350
317
  $ trv model:export --help
351
318
 
352
- Usage: model:export [options] <provider:string> <models...:string>
353
-
354
- Options:
355
- -p, --profile <string> Application profiles
356
- -m, --module <module> Module to run for
357
- --help display help for command
358
-
359
- Providers
360
- --------------------
361
- * SQL
362
-
363
- Models
364
- --------------------
365
- * samplemodel
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. This is useful for being able to prepare the datastore manually.
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: Running model install**
349
+ **Terminal: Help for model:install**
372
350
  ```bash
373
351
  $ trv model:install --help
374
352
 
375
- Usage: model:install [options] <provider:string> <models...:string>
376
-
377
- Options:
378
- -p, --profile <string> Application profiles
379
- -m, --module <module> Module to run for
380
- --help display help for command
381
-
382
- Providers
383
- --------------------
384
- * SQL
385
-
386
- Models
387
- --------------------
388
- * samplemodel
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.0-alpha.9",
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
- "typescript",
11
- "travetto"
9
+ "travetto",
10
+ "typescript"
12
11
  ],
13
12
  "homepage": "https://travetto.io",
14
13
  "license": "MIT",
15
14
  "author": {
16
- "email": "travetto.framework@gmail.com",
17
- "name": "Travetto Framework"
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
- "repository": {
26
- "url": "git+https://github.com/travetto/travetto.git",
27
- "directory": "module/model"
29
+ "publishConfig": {
30
+ "access": "public"
28
31
  },
29
32
  "dependencies": {
30
- "@travetto/config": "^8.0.0-alpha.9",
31
- "@travetto/di": "^8.0.0-alpha.9",
32
- "@travetto/registry": "^8.0.0-alpha.9",
33
- "@travetto/schema": "^8.0.0-alpha.9"
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.0-alpha.14",
37
- "@travetto/test": "^8.0.0-alpha.9"
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
  }
@@ -10,4 +10,4 @@ export class ExistsError extends RuntimeError {
10
10
  details: { id, type: typeof cls === 'string' ? cls : cls.name }
11
11
  });
12
12
  }
13
- }
13
+ }
@@ -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<T>, message: string = '') {
11
- super(`${typeof cls === 'string' ? cls : cls.name} and index ${idx.name} of type ${idx.type} is not supported. ${message}`.trim(), { category: 'data' });
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
+ }
@@ -7,4 +7,4 @@ export class SubTypeNotSupportedError extends RuntimeError {
7
7
  constructor(cls: Class | string) {
8
8
  super(`${typeof cls === 'string' ? cls : cls.name} cannot be used for this operation`, { category: 'data' });
9
9
  }
10
- }
10
+ }
@@ -7,4 +7,4 @@ export class NotFoundError extends RuntimeError {
7
7
  constructor(cls: Class | string, id: string, details: Record<string, unknown> = {}) {
8
8
  super(`${typeof cls === 'string' ? cls : cls.name} with id ${id} not found`, { category: 'notfound', details });
9
9
  }
10
- }
10
+ }
@@ -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 { RuntimeError, castTo, type Class, getClass } from '@travetto/runtime';
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
- scope,
61
- handler: castTo(handler)
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
- scope,
77
- handler: (inst): void => {
78
- const cInst: Record<K, T> = castTo(inst);
79
- cInst[property] = handler(cInst[property]);
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
- scope: 'all',
96
- handler: (inst): void => {
97
- const cInst: Record<K, T> = castTo(inst);
98
- delete cInst[property];
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
+ }