@travetto/model 5.0.0-rc.1 → 5.0.0-rc.11

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 (51) hide show
  1. package/README.md +85 -147
  2. package/__index__.ts +2 -4
  3. package/package.json +7 -7
  4. package/src/error/exists.ts +1 -1
  5. package/src/error/invalid-index.ts +1 -1
  6. package/src/error/invalid-sub-type.ts +1 -1
  7. package/src/error/not-found.ts +1 -1
  8. package/src/internal/service/blob.ts +5 -0
  9. package/src/internal/service/bulk.ts +1 -1
  10. package/src/internal/service/common.ts +20 -26
  11. package/src/internal/service/crud.ts +15 -21
  12. package/src/internal/service/expiry.ts +2 -5
  13. package/src/internal/service/indexed.ts +13 -26
  14. package/src/internal/service/storage.ts +3 -7
  15. package/src/registry/decorator.ts +7 -13
  16. package/src/registry/model.ts +13 -8
  17. package/src/registry/types.ts +1 -2
  18. package/src/service/basic.ts +1 -1
  19. package/src/service/blob.ts +41 -0
  20. package/src/service/bulk.ts +1 -1
  21. package/src/service/crud.ts +1 -1
  22. package/src/service/expiry.ts +1 -1
  23. package/src/service/indexed.ts +2 -2
  24. package/src/service/storage.ts +1 -1
  25. package/src/util/blob.ts +60 -0
  26. package/support/base-command.ts +1 -1
  27. package/support/bin/candidate.ts +2 -3
  28. package/support/bin/export.ts +1 -1
  29. package/support/bin/install.ts +1 -1
  30. package/support/cli.model_export.ts +1 -1
  31. package/support/cli.model_install.ts +1 -1
  32. package/support/doc.support.tsx +5 -11
  33. package/support/fixtures/alpha.txt +1 -0
  34. package/support/fixtures/empty +0 -0
  35. package/support/fixtures/empty.m4a +0 -0
  36. package/support/fixtures/logo.gif +0 -0
  37. package/support/fixtures/logo.png +0 -0
  38. package/support/fixtures/small-audio +0 -0
  39. package/support/fixtures/small-audio.mp3 +0 -0
  40. package/support/test/base.ts +3 -3
  41. package/support/test/blob.ts +117 -0
  42. package/support/test/crud.ts +5 -4
  43. package/support/test/expiry.ts +1 -1
  44. package/support/test/indexed.ts +1 -1
  45. package/support/test/polymorphism.ts +7 -6
  46. package/support/test/suite.ts +6 -6
  47. package/src/internal/service/stream.ts +0 -22
  48. package/src/provider/file.ts +0 -233
  49. package/src/provider/memory.ts +0 -341
  50. package/src/service/stream.ts +0 -72
  51. package/support/test/stream.ts +0 -110
package/README.md CHANGED
@@ -16,7 +16,7 @@ yarn add @travetto/model
16
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
  ## Contracts
19
- 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/service/basic.ts#L9), [CRUD](https://github.com/travetto/travetto/tree/main/module/model/src/service/crud.ts#L11), [Indexed](https://github.com/travetto/travetto/tree/main/module/model/src/service/indexed.ts#L12), [Expiry](https://github.com/travetto/travetto/tree/main/module/model/src/service/expiry.ts#L11), [Streaming](https://github.com/travetto/travetto/tree/main/module/model/src/service/stream.ts#L3) and [Bulk](https://github.com/travetto/travetto/tree/main/module/model/src/service/bulk.ts#L19).
19
+ 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/service/basic.ts#L9), [CRUD](https://github.com/travetto/travetto/tree/main/module/model/src/service/crud.ts#L11), [Indexed](https://github.com/travetto/travetto/tree/main/module/model/src/service/indexed.ts#L12), [Expiry](https://github.com/travetto/travetto/tree/main/module/model/src/service/expiry.ts#L11), [Blob](https://github.com/travetto/travetto/tree/main/module/model/src/service/blob.ts#L8) and [Bulk](https://github.com/travetto/travetto/tree/main/module/model/src/service/bulk.ts#L19).
20
20
 
21
21
  ### Basic
22
22
  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/service/basic.ts#L9) contract to be able to participate in the model ecosystem. This contract represents the bare minimum for a model service.
@@ -153,38 +153,44 @@ export interface ModelExpirySupport extends ModelCrudSupport {
153
153
  }
154
154
  ```
155
155
 
156
- ### Stream
157
- Some implementations also allow for the ability to read/write binary data as a [Streaming](https://github.com/travetto/travetto/tree/main/module/model/src/service/stream.ts#L3). 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. This pattern is currently used by [Asset](https://github.com/travetto/travetto/tree/main/module/asset#readme "Modular library for storing and retrieving binary assets") for reading and writing asset data.
156
+ ### Blob
157
+ Some implementations also allow for the ability to read/write binary data as [Blob](https://github.com/travetto/travetto/tree/main/module/model/src/service/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.
158
158
 
159
- **Code: Stream Contract**
159
+ **Code: Blob Contract**
160
160
  ```typescript
161
- export interface ModelStreamSupport {
161
+ export interface ModelBlobSupport {
162
162
 
163
163
  /**
164
- * Upsert stream to storage
165
- * @param location The location of the stream
166
- * @param input The actual stream to write
167
- * @param meta The stream metadata
164
+ * Insert blob to storage
165
+ * @param location The location of the blob
166
+ * @param input The actual blob to write
168
167
  */
169
- upsertStream(location: string, input: Readable, meta: StreamMeta): Promise<void>;
168
+ insertBlob(location: string, input: BinaryInput, meta?: BlobMeta, errorIfExisting?: boolean): Promise<void>;
170
169
 
171
170
  /**
172
- * Get stream from asset store
173
- * @param location The location of the stream
171
+ * Upsert blob to storage
172
+ * @param location The location of the blob
173
+ * @param input The actual blob to write
174
174
  */
175
- getStream(location: string, range?: StreamRange): Promise<Readable>;
175
+ upsertBlob(location: string, input: BinaryInput, meta?: BlobMeta): Promise<void>;
176
176
 
177
177
  /**
178
- * Get metadata for stream
179
- * @param location The location of the stream
178
+ * Get blob from storage
179
+ * @param location The location of the blob
180
180
  */
181
- describeStream(location: string): Promise<StreamMeta>;
181
+ getBlob(location: string, range?: ByteRange): Promise<Blob>;
182
182
 
183
183
  /**
184
- * Delete stream by location
185
- * @param location The location of the stream
184
+ * Get metadata for blob
185
+ * @param location The location of the blob
186
186
  */
187
- deleteStream(location: string): Promise<void>;
187
+ describeBlob(location: string): Promise<BlobMeta>;
188
+
189
+ /**
190
+ * Delete blob by location
191
+ * @param location The location of the blob
192
+ */
193
+ deleteBlob(location: string): Promise<void>;
188
194
  }
189
195
  ```
190
196
 
@@ -216,8 +222,8 @@ export interface ModelType {
216
222
  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.").
217
223
 
218
224
  ## Implementations
219
- |Service|Basic|CRUD|Indexed|Expiry|Stream|Bulk|
220
- |-------|-----|----|-------|------|------|----|
225
+ |Service|Basic|CRUD|Indexed|Expiry|Blob|Bulk|
226
+ |-------|-----|----|-------|------|----|----|
221
227
  |[DynamoDB Model Support](https://github.com/travetto/travetto/tree/main/module/model-dynamodb#readme "DynamoDB backing for the travetto model module.")|X|X|X|X| | |
222
228
  |[Elasticsearch Model Source](https://github.com/travetto/travetto/tree/main/module/model-elasticsearch#readme "Elasticsearch backing for the travetto model module, with real-time modeling support for Elasticsearch mappings.")|X|X|X|X| |X|
223
229
  |[Firestore Model Support](https://github.com/travetto/travetto/tree/main/module/model-firestore#readme "Firestore backing for the travetto model module.")|X|X|X| | | |
@@ -225,143 +231,76 @@ The `id` is the only required field for a model, as this is a hard requirement o
225
231
  |[Redis Model Support](https://github.com/travetto/travetto/tree/main/module/model-redis#readme "Redis backing for the travetto model module.")|X|X|X|X| ||
226
232
  |[S3 Model Support](https://github.com/travetto/travetto/tree/main/module/model-s3#readme "S3 backing for the travetto model module.")|X|X| |X|X| |
227
233
  |[SQL Model Service](https://github.com/travetto/travetto/tree/main/module/model-sql#readme "SQL backing for the travetto model module, with real-time modeling support for SQL schemas.")|X|X|X|X| |X|
228
- |[MemoryModelService](https://github.com/travetto/travetto/tree/main/module/model/src/provider/memory.ts#L54)|X|X|X|X|X|X|
229
- |[FileModelService](https://github.com/travetto/travetto/tree/main/module/model/src/provider/file.ts#L49)|X|X| |X|X|X|
234
+ |[Memory Model Support](https://github.com/travetto/travetto/tree/main/module/model-memory#readme "Memory backing for the travetto model module.")|X|X|X|X|X|X|
235
+ |[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|
230
236
 
231
237
  ## Custom Model Service
232
- 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.
233
-
234
- **Code: Memory Service**
235
- ```typescript
236
- import { Readable } from 'node:stream';
237
- import { buffer as toBuffer } from 'node:stream/consumers';
238
- import { Class, TimeSpan } from '@travetto/base';
239
- import { DeepPartial } from '@travetto/schema';
240
- import { Injectable } from '@travetto/di';
241
- import { Config } from '@travetto/config';
242
- import { ModelCrudSupport } from '../service/crud';
243
- import { ModelStreamSupport, StreamMeta, StreamRange } from '../service/stream';
244
- import { ModelType, OptionalId } from '../types/model';
245
- import { ModelExpirySupport } from '../service/expiry';
246
- import { ModelRegistry } from '../registry/model';
247
- import { ModelStorageSupport } from '../service/storage';
248
- import { ModelCrudUtil } from '../internal/service/crud';
249
- import { ModelExpiryUtil } from '../internal/service/expiry';
250
- import { NotFoundError } from '../error/not-found';
251
- import { ExistsError } from '../error/exists';
252
- import { ModelIndexedSupport } from '../service/indexed';
253
- import { ModelIndexedUtil } from '../internal/service/indexed';
254
- import { ModelStorageUtil } from '../internal/service/storage';
255
- import { enforceRange, StreamModel, STREAMS } from '../internal/service/stream';
256
- import { IndexConfig } from '../registry/types';
257
- const STREAM_META = `${STREAMS}_meta`;
258
- type StoreType = Map<string, Buffer>;
259
- @Config('model.memory')
260
- export class MemoryModelConfig {
261
- autoCreate?: boolean = true;
262
- namespace?: string;
263
- cullRate?: number | TimeSpan;
264
- }
265
- function indexName<T extends ModelType>(cls: Class<T>, idx: IndexConfig<T> | string, suffix?: string): string {
266
- return [cls.Ⲑid, typeof idx === 'string' ? idx : idx.name, suffix].filter(x => !!x).join(':');
267
- }
268
- function getFirstId(data: Map<string, unknown> | Set<string>, value?: string | number): string | undefined {
269
- let id: string | undefined;
270
- if (data instanceof Set) {
271
- id = data.values().next().value;
272
- } else {
273
- id = [...data.entries()].find(([k, v]) => value === undefined || v === value)?.[0];
274
- }
275
- return id;
276
- }
277
- /**
278
- * Standard in-memory support
279
- */
280
- @Injectable()
281
- export class MemoryModelService implements ModelCrudSupport, ModelStreamSupport, ModelExpirySupport, ModelStorageSupport, ModelIndexedSupport {
282
- sorted: new Map<string, Map<string, Map<string, number>>>(),
283
- unsorted: new Map<string, Map<string, Set<string>>>()
284
- };
285
- idSource = ModelCrudUtil.uuidSource();
286
- get client(): Map<string, StoreType>;
287
- constructor(public readonly config: MemoryModelConfig) { }
288
- async postConstruct(): Promise<void>;
289
- // CRUD Support
290
- async get<T extends ModelType>(cls: Class<T>, id: string): Promise<T>;
291
- async create<T extends ModelType>(cls: Class<T>, item: OptionalId<T>): Promise<T>;
292
- async update<T extends ModelType>(cls: Class<T>, item: T): Promise<T>;
293
- async upsert<T extends ModelType>(cls: Class<T>, item: OptionalId<T>): Promise<T>;
294
- async updatePartial<T extends ModelType>(cls: Class<T>, item: Partial<T> & { id: string }, view?: string): Promise<T>;
295
- async delete<T extends ModelType>(cls: Class<T>, id: string): Promise<void>;
296
- async * list<T extends ModelType>(cls: Class<T>): AsyncIterable<T>;
297
- // Stream Support
298
- async upsertStream(location: string, input: Readable, meta: StreamMeta): Promise<void>;
299
- async getStream(location: string, range?: StreamRange): Promise<Readable>;
300
- async describeStream(location: string): Promise<StreamMeta>;
301
- async deleteStream(location: string): Promise<void>;
302
- // Expiry
303
- async deleteExpired<T extends ModelType>(cls: Class<T>): Promise<number>;
304
- // Storage Support
305
- async createStorage(): Promise<void>;
306
- async deleteStorage(): Promise<void>;
307
- async createModel<T extends ModelType>(cls: Class<T>): Promise<void>;
308
- async truncateModel<T extends ModelType>(cls: Class<T>): Promise<void>;
309
- // Indexed
310
- async getByIndex<T extends ModelType>(cls: Class<T>, idx: string, body: DeepPartial<T>): Promise<T>;
311
- async deleteByIndex<T extends ModelType>(cls: Class<T>, idx: string, body: DeepPartial<T>): Promise<void>;
312
- upsertByIndex<T extends ModelType>(cls: Class<T>, idx: string, body: OptionalId<T>): Promise<T>;
313
- async * listByIndex<T extends ModelType>(cls: Class<T>, idx: string, body?: DeepPartial<T>): AsyncIterable<T>;
314
- }
315
- ```
238
+ 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.
316
239
 
317
240
  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.
318
241
 
319
242
  **Code: Memory Service Test Configuration**
320
243
  ```typescript
321
- import { Suite } from '@travetto/test';
322
-
323
- import { MemoryModelConfig, MemoryModelService } from '../src/provider/memory';
324
- import { ModelCrudSuite } from '../support/test/crud';
325
- import { ModelExpirySuite } from '../support/test/expiry';
326
- import { ModelStreamSuite } from '../support/test/stream';
327
- import { ModelIndexedSuite } from '../support/test/indexed';
328
- import { ModelBasicSuite } from '../support/test/basic';
329
- import { ModelPolymorphismSuite } from '../support/test/polymorphism';
330
-
331
- @Suite()
332
- export class MemoryBasicSuite extends ModelBasicSuite {
333
- serviceClass = MemoryModelService;
334
- configClass = MemoryModelConfig;
335
- }
244
+ import { DependencyRegistry } from '@travetto/di';
245
+ import { AppError, castTo, Class, classConstruct } from '@travetto/runtime';
336
246
 
337
- @Suite()
338
- export class MemoryCrudSuite extends ModelCrudSuite {
339
- serviceClass = MemoryModelService;
340
- configClass = MemoryModelConfig;
341
- }
247
+ import { isBulkSupported, isCrudSupported } from '../../src/internal/service/common';
248
+ import { ModelType } from '../../src/types/model';
249
+ import { ModelSuite } from './suite';
342
250
 
343
- @Suite()
344
- export class MemoryStreamSuite extends ModelStreamSuite {
345
- serviceClass = MemoryModelService;
346
- configClass = MemoryModelConfig;
347
- }
251
+ type ServiceClass = { serviceClass: { new(): unknown } };
348
252
 
349
- @Suite()
350
- export class MemoryExpirySuite extends ModelExpirySuite {
351
- serviceClass = MemoryModelService;
352
- configClass = MemoryModelConfig;
353
- }
253
+ @ModelSuite()
254
+ export abstract class BaseModelSuite<T> {
354
255
 
355
- @Suite()
356
- export class MemoryIndexedSuite extends ModelIndexedSuite {
357
- serviceClass = MemoryModelService;
358
- configClass = MemoryModelConfig;
359
- }
256
+ static ifNot(pred: (svc: unknown) => boolean): (x: unknown) => Promise<boolean> {
257
+ return async (x: unknown) => !pred(classConstruct(castTo<ServiceClass>(x).serviceClass));
258
+ }
259
+
260
+ serviceClass: Class<T>;
261
+ configClass: Class;
262
+
263
+ async getSize<U extends ModelType>(cls: Class<U>): Promise<number> {
264
+ const svc = (await this.service);
265
+ if (isCrudSupported(svc)) {
266
+ let i = 0;
267
+ for await (const __el of svc.list(cls)) {
268
+ i += 1;
269
+ }
270
+ return i;
271
+ } else {
272
+ throw new AppError(`Size is not supported for this service: ${this.serviceClass.name}`);
273
+ }
274
+ }
275
+
276
+ async saveAll<M extends ModelType>(cls: Class<M>, items: M[]): Promise<number> {
277
+ const svc = await this.service;
278
+ if (isBulkSupported(svc)) {
279
+ const res = await svc.processBulk(cls, items.map(x => ({ insert: x })));
280
+ return res.counts.insert;
281
+ } else if (isCrudSupported(svc)) {
282
+ const out: Promise<M>[] = [];
283
+ for (const el of items) {
284
+ out.push(svc.create(cls, el));
285
+ }
286
+ await Promise.all(out);
287
+ return out.length;
288
+ } else {
289
+ throw new Error('Service does not support crud operations');
290
+ }
291
+ }
360
292
 
361
- @Suite()
362
- export class MemoryPolymorphicSuite extends ModelPolymorphismSuite {
363
- serviceClass = MemoryModelService;
364
- configClass = MemoryModelConfig;
293
+ get service(): Promise<T> {
294
+ return DependencyRegistry.getInstance(this.serviceClass);
295
+ }
296
+
297
+ async toArray<U>(src: AsyncIterable<U> | AsyncGenerator<U>): Promise<U[]> {
298
+ const out: U[] = [];
299
+ for await (const el of src) {
300
+ out.push(el);
301
+ }
302
+ return out;
303
+ }
365
304
  }
366
305
  ```
367
306
 
@@ -404,7 +343,6 @@ Options:
404
343
 
405
344
  Providers
406
345
  --------------------
407
- * Memory
408
346
  * SQL
409
347
 
410
348
  Models
package/__index__.ts CHANGED
@@ -3,15 +3,13 @@ export * from './src/registry/model';
3
3
  export * from './src/registry/types';
4
4
  export * from './src/types/model';
5
5
  export * from './src/service/basic';
6
+ export * from './src/service/blob';
6
7
  export * from './src/service/bulk';
7
8
  export * from './src/service/crud';
8
9
  export * from './src/service/indexed';
9
10
  export * from './src/service/expiry';
10
11
  export * from './src/service/storage';
11
- export * from './src/service/stream';
12
-
13
- export * from './src/provider/file';
14
- export * from './src/provider/memory';
12
+ export * from './src/util/blob';
15
13
 
16
14
  export * from './src/error/exists';
17
15
  export * from './src/error/not-found';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@travetto/model",
3
- "version": "5.0.0-rc.1",
3
+ "version": "5.0.0-rc.11",
4
4
  "description": "Datastore abstraction for core operations.",
5
5
  "keywords": [
6
6
  "datastore",
@@ -26,14 +26,14 @@
26
26
  "directory": "module/model"
27
27
  },
28
28
  "dependencies": {
29
- "@travetto/config": "^5.0.0-rc.1",
30
- "@travetto/di": "^5.0.0-rc.0",
31
- "@travetto/registry": "^5.0.0-rc.1",
32
- "@travetto/schema": "^5.0.0-rc.0"
29
+ "@travetto/config": "^5.0.0-rc.11",
30
+ "@travetto/di": "^5.0.0-rc.10",
31
+ "@travetto/registry": "^5.0.0-rc.10",
32
+ "@travetto/schema": "^5.0.0-rc.11"
33
33
  },
34
34
  "peerDependencies": {
35
- "@travetto/cli": "^5.0.0-rc.1",
36
- "@travetto/test": "^5.0.0-rc.1"
35
+ "@travetto/cli": "^5.0.0-rc.11",
36
+ "@travetto/test": "^5.0.0-rc.10"
37
37
  },
38
38
  "peerDependenciesMeta": {
39
39
  "@travetto/cli": {
@@ -1,4 +1,4 @@
1
- import { Class, AppError } from '@travetto/base';
1
+ import { Class, AppError } from '@travetto/runtime';
2
2
 
3
3
  /**
4
4
  * Represents when a data item already exists
@@ -1,4 +1,4 @@
1
- import { Class, AppError } from '@travetto/base';
1
+ import { Class, AppError } from '@travetto/runtime';
2
2
 
3
3
  import { IndexConfig } from '../registry/types';
4
4
  import { ModelType } from '../types/model';
@@ -1,4 +1,4 @@
1
- import { Class, AppError } from '@travetto/base';
1
+ import { Class, AppError } from '@travetto/runtime';
2
2
 
3
3
  /**
4
4
  * Represents when a model subtype class is unable to be used directly
@@ -1,4 +1,4 @@
1
- import { Class, AppError } from '@travetto/base';
1
+ import { Class, AppError } from '@travetto/runtime';
2
2
 
3
3
  /**
4
4
  * Represents when a model of cls and id cannot be found
@@ -0,0 +1,5 @@
1
+ import { Class } from '@travetto/runtime';
2
+ import { ModelType } from '../../types/model';
3
+
4
+ export const ModelBlobNamespace = '__blobs';
5
+ export const MODEL_BLOB: Class<ModelType> = class { id: string; };
@@ -1,4 +1,4 @@
1
- import { Class } from '@travetto/base';
1
+ import { Class } from '@travetto/runtime';
2
2
 
3
3
  import { BulkOp } from '../../service/bulk';
4
4
  import { ModelType } from '../../types/model';
@@ -1,78 +1,72 @@
1
+ import { ClassInstance } from '@travetto/runtime';
1
2
  import type { ModelBulkSupport } from '../../service/bulk';
2
3
  import { ModelCrudSupport } from '../../service/crud';
3
4
  import type { ModelExpirySupport } from '../../service/expiry';
4
5
  import { ModelIndexedSupport } from '../../service/indexed';
5
6
  import type { ModelStorageSupport } from '../../service/storage';
6
- import type { ModelStreamSupport } from '../../service/stream';
7
+ import { ModelBlobSupport } from '../../service/blob';
7
8
 
8
9
  export class ModelBasicSupportTarget { }
9
10
  export class ModelCrudSupportTarget { }
10
11
  export class ModelBulkSupportTarget { }
11
12
  export class ModelStorageSupportTarget { }
13
+ export class ModelBlobSupportTarget { }
12
14
  export class ModelExpirySupportTarget { }
13
- export class ModelStreamSupportTarget { }
14
15
  export class ModelIndexedSupportTarget { }
15
16
 
16
17
  /**
17
18
  * Type guard for determining if service supports basic operations
18
19
  * @param o
19
20
  */
20
- export function isBasicSupported(o: unknown): o is ModelBulkSupport {
21
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
22
- return !!o && !!(o as Record<string, unknown>)['create'];
21
+ export function isBasicSupported(o: ClassInstance): o is ModelBulkSupport {
22
+ return !!o && 'create' in o;
23
23
  }
24
24
 
25
25
  /**
26
26
  * Type guard for determining if service supports crud operations
27
27
  * @param o
28
28
  */
29
- export function isCrudSupported(o: unknown): o is ModelCrudSupport {
30
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
31
- return !!o && !!(o as Record<string, unknown>)['upsert'];
29
+ export function isCrudSupported(o: ClassInstance): o is ModelCrudSupport {
30
+ return !!o && 'upsert' in o;
32
31
  }
33
32
 
34
33
  /**
35
- * Type guard for determining if model is expirable
34
+ * Type guard for determining if model supports expiry
36
35
  * @param o
37
36
  */
38
- export function isExpirySupported(o: unknown): o is ModelExpirySupport {
39
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
40
- return !!o && !!(o as Record<string, unknown>)['deleteExpired'];
37
+ export function isExpirySupported(o: ClassInstance): o is ModelExpirySupport {
38
+ return !!o && 'deleteExpired' in o;
41
39
  }
42
40
 
43
41
  /**
44
- * Type guard for determining if service supports storage operation
42
+ * Type guard for determining if service supports streaming operation
45
43
  * @param o
46
44
  */
47
- export function isStorageSupported(o: unknown): o is ModelStorageSupport {
48
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
49
- return !!o && !!(o as Record<string, unknown>)['createStorage'];
45
+ export function isBlobSupported(o: ClassInstance): o is ModelBlobSupport {
46
+ return !!o && 'getBlob' in o;
50
47
  }
51
48
 
52
49
  /**
53
- * Type guard for determining if service supports streaming operation
50
+ * Type guard for determining if service supports storage operation
54
51
  * @param o
55
52
  */
56
- export function isStreamSupported(o: unknown): o is ModelStreamSupport {
57
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
58
- return !!o && !!(o as Record<string, unknown>)['getStream'];
53
+ export function isStorageSupported(o: ClassInstance): o is ModelStorageSupport {
54
+ return !!o && 'createStorage' in o;
59
55
  }
60
56
 
61
57
  /**
62
58
  * Type guard for determining if service supports streaming operation
63
59
  * @param o
64
60
  */
65
- export function isBulkSupported(o: unknown): o is ModelBulkSupport {
66
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
67
- return !!o && !!(o as Record<string, unknown>)['processBulk'];
61
+ export function isBulkSupported(o: ClassInstance): o is ModelBulkSupport {
62
+ return !!o && 'processBulk' in o;
68
63
  }
69
64
 
70
65
  /**
71
66
  * Type guard for determining if service supports indexed operation
72
67
  * @param o
73
68
  */
74
- export function isIndexedSupported(o: unknown): o is ModelIndexedSupport {
75
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
76
- return !!o && !!(o as Record<string, unknown>)['getByIndex'];
69
+ export function isIndexedSupported(o: ClassInstance): o is ModelIndexedSupport {
70
+ return !!o && 'getByIndex' in o;
77
71
  }
78
72
 
@@ -1,6 +1,6 @@
1
1
  import crypto from 'node:crypto';
2
2
 
3
- import { Class, Util } from '@travetto/base';
3
+ import { castTo, Class, asFull, Util, asConstructable } from '@travetto/runtime';
4
4
  import { DataUtil, SchemaRegistry, SchemaValidator, ValidationError, ValidationResultError } from '@travetto/schema';
5
5
 
6
6
  import { ModelRegistry } from '../../registry/model';
@@ -46,14 +46,16 @@ export class ModelCrudUtil {
46
46
  * @param input Input as string or plain object
47
47
  */
48
48
  static async load<T extends ModelType>(cls: Class<T>, input: Buffer | string | object, onTypeMismatch: 'notfound' | 'exists' = 'notfound'): Promise<T> {
49
+ let resolvedInput: object;
49
50
  if (typeof input === 'string') {
50
- input = JSON.parse(input);
51
+ resolvedInput = JSON.parse(input);
51
52
  } else if (input instanceof Buffer) {
52
- input = JSON.parse(input.toString('utf8'));
53
+ resolvedInput = JSON.parse(input.toString('utf8'));
54
+ } else {
55
+ resolvedInput = input;
53
56
  }
54
57
 
55
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
56
- const result = ModelRegistry.getBaseModel(cls).from(input as object) as T;
58
+ const result = ModelRegistry.getBaseModel(cls).from(resolvedInput);
57
59
 
58
60
  if (!(result instanceof cls || result.constructor.Ⲑid === cls.Ⲑid)) {
59
61
  if (onTypeMismatch === 'notfound') {
@@ -78,12 +80,10 @@ export class ModelCrudUtil {
78
80
  }
79
81
 
80
82
  if (DataUtil.isPlainObject(item)) {
81
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
82
- item = cls.from(item as object);
83
+ item = cls.from(castTo(item));
83
84
  }
84
85
 
85
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
86
- const config = ModelRegistry.get(item.constructor as Class<T>);
86
+ const config = ModelRegistry.get(asConstructable(item).constructor);
87
87
  if (config.subType) { // Sub-typing, assign type
88
88
  SchemaRegistry.ensureInstanceTypeField(cls, item);
89
89
  }
@@ -106,8 +106,7 @@ export class ModelCrudUtil {
106
106
  if (errors.length) {
107
107
  throw new ValidationResultError(errors);
108
108
  }
109
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
110
- return item as T;
109
+ return castTo(item);
111
110
  }
112
111
 
113
112
  /**
@@ -119,12 +118,10 @@ export class ModelCrudUtil {
119
118
  */
120
119
  static async naivePartialUpdate<T extends ModelType>(cls: Class<T>, item: Partial<T>, view: undefined | string, getExisting: () => Promise<T>): Promise<T> {
121
120
  if (DataUtil.isPlainObject(item)) {
122
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
123
- item = cls.from(item as object);
121
+ item = cls.from(castTo(item));
124
122
  }
125
123
 
126
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
127
- const config = ModelRegistry.get(item.constructor as Class<T>);
124
+ const config = ModelRegistry.get(asConstructable(item).constructor);
128
125
  if (config.subType) { // Sub-typing, assign type
129
126
  SchemaRegistry.ensureInstanceTypeField(cls, item);
130
127
  }
@@ -139,8 +136,7 @@ export class ModelCrudUtil {
139
136
 
140
137
  item = await this.prePersist(cls, item, 'partial');
141
138
 
142
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
143
- return item as T;
139
+ return asFull(item);
144
140
  }
145
141
 
146
142
  /**
@@ -159,8 +155,7 @@ export class ModelCrudUtil {
159
155
  const config = ModelRegistry.get(cls);
160
156
  for (const state of (config.prePersist ?? [])) {
161
157
  if (state.scope === scope || scope === 'all' || state.scope === 'all') {
162
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
163
- const handler = state.handler as unknown as DataHandler<T>;
158
+ const handler: DataHandler<T> = castTo(state.handler);
164
159
  item = await handler(item) ?? item;
165
160
  }
166
161
  }
@@ -175,8 +170,7 @@ export class ModelCrudUtil {
175
170
  */
176
171
  static async postLoad<T>(cls: Class<T>, item: T): Promise<T> {
177
172
  const config = ModelRegistry.get(cls);
178
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
179
- for (const handler of (config.postLoad ?? []) as unknown as DataHandler<T>[]) {
173
+ for (const handler of castTo<DataHandler<T>[]>(config.postLoad ?? [])) {
180
174
  item = await handler(item) ?? item;
181
175
  }
182
176
  if (typeof item === 'object' && item && 'postLoad' in item && typeof item['postLoad'] === 'function') {
@@ -1,4 +1,4 @@
1
- import { ShutdownManager, Class, TimeSpan, TimeUtil, Util } from '@travetto/base';
1
+ import { ShutdownManager, Class, TimeSpan, TimeUtil, Util, castTo } from '@travetto/runtime';
2
2
 
3
3
  import { ModelRegistry } from '../../registry/model';
4
4
  import { ModelExpirySupport } from '../../service/expiry';
@@ -15,10 +15,7 @@ export class ModelExpiryUtil {
15
15
  */
16
16
  static getExpiryState<T extends ModelType>(cls: Class<T>, item: T): { expiresAt?: Date, expired?: boolean } {
17
17
  const expKey = ModelRegistry.getExpiry(cls);
18
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
19
- const keyAsT = expKey as keyof T;
20
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
21
- const expiresAt = item[keyAsT] ? item[keyAsT] as unknown as Date : undefined;
18
+ const expiresAt: Date = castTo(item[expKey]);
22
19
 
23
20
  return {
24
21
  expiresAt,