@travetto/model 2.1.5 → 2.2.0

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 CHANGED
@@ -12,7 +12,7 @@ This module provides a set of contracts/interfaces to data model persistence, mo
12
12
 
13
13
  ## Contracts
14
14
 
15
- 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#L1) and [Bulk](https://github.com/travetto/travetto/tree/main/module/model/src/service/bulk.ts#L23).
15
+ 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#L23).
16
16
 
17
17
  ### [Basic](https://github.com/travetto/travetto/tree/main/module/model/src/service/basic.ts#L9)
18
18
  All [Data Modeling Support](https://github.com/travetto/travetto/tree/main/module/model#readme "Datastore abstraction for core operations.") implementations, must honor the BasicCrud contract to be able to participate in the model ecosystem. This contract represents the bare minimum for a model service.
@@ -35,7 +35,7 @@ export interface ModelBasicSupport<C = unknown> {
35
35
  /**
36
36
  * Create new item
37
37
  * @param item The document to create
38
- * @throws {ExistsError} When an item with the provdided id already exists
38
+ * @throws {ExistsError} When an item with the provided id already exists
39
39
  */
40
40
  create<T extends ModelType>(cls: Class<T>, item: OptionalId<T>): Promise<T>;
41
41
 
@@ -115,7 +115,7 @@ export interface ModelIndexedSupport extends ModelBasicSupport {
115
115
  deleteByIndex<T extends ModelType>(cls: Class<T>, idx: string, body: DeepPartial<T>): Promise<void>;
116
116
 
117
117
  /**
118
- * List entity by rangeable index as defined by fields of idx and the body fields
118
+ * List entity by ranged index as defined by fields of idx and the body fields
119
119
  * @param cls The type to search by
120
120
  * @param idx The index name to search against
121
121
  * @param body The payload of fields needed to search
@@ -148,7 +148,7 @@ export interface ModelExpirySupport extends ModelCrudSupport {
148
148
  }
149
149
  ```
150
150
 
151
- ### [Streaming](https://github.com/travetto/travetto/tree/main/module/model/src/service/stream.ts#L1)
151
+ ### [Streaming](https://github.com/travetto/travetto/tree/main/module/model/src/service/stream.ts#L3)
152
152
 
153
153
  Some implementations also allow for the ability to read/write binary data as a stream. 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.
154
154
 
@@ -162,13 +162,13 @@ export interface ModelStreamSupport {
162
162
  * @param input The actual stream to write
163
163
  * @param meta The stream metadata
164
164
  */
165
- upsertStream(location: string, input: NodeJS.ReadableStream, meta: StreamMeta): Promise<void>;
165
+ upsertStream(location: string, input: Readable, meta: StreamMeta): Promise<void>;
166
166
 
167
167
  /**
168
168
  * Get stream from asset store
169
169
  * @param location The location of the stream
170
170
  */
171
- getStream(location: string): Promise<NodeJS.ReadableStream>;
171
+ getStream(location: string): Promise<Readable>;
172
172
 
173
173
  /**
174
174
  * Get metadata for stream
@@ -235,8 +235,8 @@ All fields are optional, but the `id` and `type` are important as those field ty
235
235
  |[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| ||
236
236
  |[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| |
237
237
  |[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|
238
- |[MemoryModelService](https://github.com/travetto/travetto/tree/main/module/model/src/provider/memory.ts#L50)|X|X|X|X|X|X|
239
- |[FileModelService](https://github.com/travetto/travetto/tree/main/module/model/src/provider/file.ts#L47)|X|X| |X|X|X|
238
+ |[MemoryModelService](https://github.com/travetto/travetto/tree/main/module/model/src/provider/memory.ts#L54)|X|X|X|X|X|X|
239
+ |[FileModelService](https://github.com/travetto/travetto/tree/main/module/model/src/provider/file.ts#L49)|X|X| |X|X|X|
240
240
 
241
241
  ## Custom Model Service
242
242
  In addition to the provided contracts, the module also provides common utilities and shared test suites. The common utilities are useful for
@@ -244,6 +244,7 @@ repetitive functionality, that is unable to be shared due to not relying upon in
244
244
 
245
245
  **Code: Memory Service**
246
246
  ```typescript
247
+ import { Readable } from 'stream';
247
248
  import { StreamUtil } from '@travetto/boot';
248
249
  import { Util, Class, TimeSpan } from '@travetto/base';
249
250
  import { DeepPartial } from '@travetto/schema';
@@ -265,19 +266,20 @@ import { ModelStorageUtil } from '../internal/service/storage';
265
266
  import { StreamModel, STREAMS } from '../internal/service/stream';
266
267
  import { IndexConfig } from '../registry/types';
267
268
  const STREAM_META = `${STREAMS}_meta`;
269
+ type StoreType = Map<string, Buffer>;
268
270
  @Config('model.memory')
269
271
  export class MemoryModelConfig {
270
272
  autoCreate?: boolean;
271
273
  namespace?: string;
272
274
  cullRate?: number | TimeSpan;
273
275
  }
274
- function indexName<T extends ModelType>(cls: Class<T>, idx: IndexConfig<T> | string, suffix?: string) {
276
+ function indexName<T extends ModelType>(cls: Class<T>, idx: IndexConfig<T> | string, suffix?: string): string {
275
277
  return [cls.ᚕid, typeof idx === 'string' ? idx : idx.name, suffix].filter(x => !!x).join(':');
276
278
  }
277
- function getFirstId(data: Map<string, unknown> | Set<string>, value?: string | number) {
279
+ function getFirstId(data: Map<string, unknown> | Set<string>, value?: string | number): string | undefined {
278
280
  let id: string | undefined;
279
281
  if (data instanceof Set) {
280
- id = data.values().next().value as string;
282
+ id = data.values().next().value;
281
283
  } else {
282
284
  id = [...data.entries()].find(([k, v]) => value === undefined || v === value)?.[0];
283
285
  }
@@ -291,34 +293,34 @@ export class MemoryModelService implements ModelCrudSupport, ModelStreamSupport,
291
293
  sorted: new Map<string, Map<string, Map<string, number>>>(),
292
294
  unsorted: new Map<string, Map<string, Set<string>>>()
293
295
  };
294
- get client() { return this.#store; }
295
- async postConstruct() ;
296
+ get client(): Map<string, StoreType> { return this.#store; }
297
+ async postConstruct(): Promise<void> ;
296
298
  // CRUD Support
297
- uuid() ;
298
- async get<T extends ModelType>(cls: Class<T>, id: string) ;
299
- async create<T extends ModelType>(cls: Class<T>, item: OptionalId<T>) ;
300
- async update<T extends ModelType>(cls: Class<T>, item: T) ;
301
- async upsert<T extends ModelType>(cls: Class<T>, item: OptionalId<T>) ;
302
- async updatePartial<T extends ModelType>(cls: Class<T>, item: Partial<T> & { id: string }, view?: string) ;
303
- async delete<T extends ModelType>(cls: Class<T>, id: string) ;
304
- async * list<T extends ModelType>(cls: Class<T>) ;
299
+ uuid(): string ;
300
+ async get<T extends ModelType>(cls: Class<T>, id: string): Promise<T> ;
301
+ async create<T extends ModelType>(cls: Class<T>, item: OptionalId<T>): Promise<T> ;
302
+ async update<T extends ModelType>(cls: Class<T>, item: T): Promise<T> ;
303
+ async upsert<T extends ModelType>(cls: Class<T>, item: OptionalId<T>): Promise<T> ;
304
+ async updatePartial<T extends ModelType>(cls: Class<T>, item: Partial<T> & { id: string }, view?: string): Promise<T> ;
305
+ async delete<T extends ModelType>(cls: Class<T>, id: string): Promise<void> ;
306
+ async * list<T extends ModelType>(cls: Class<T>): AsyncIterable<T> ;
305
307
  // Stream Support
306
- async upsertStream(location: string, input: NodeJS.ReadableStream, meta: StreamMeta) ;
307
- async getStream(location: string) ;
308
- async describeStream(location: string) ;
309
- async deleteStream(location: string) ;
308
+ async upsertStream(location: string, input: Readable, meta: StreamMeta): Promise<void> ;
309
+ async getStream(location: string): Promise<Readable> ;
310
+ async describeStream(location: string): Promise<StreamMeta> ;
311
+ async deleteStream(location: string): Promise<void> ;
310
312
  // Expiry Support
311
- async deleteExpired<T extends ModelType>(cls: Class<T>) ;
313
+ async deleteExpired<T extends ModelType>(cls: Class<T>): Promise<number> ;
312
314
  // Storage Support
313
- async createStorage() ;
314
- async deleteStorage() ;
315
- async createModel<T extends ModelType>(cls: Class<T>) ;
316
- async truncateModel<T extends ModelType>(cls: Class<T>) ;
315
+ async createStorage(): Promise<void> ;
316
+ async deleteStorage(): Promise<void> ;
317
+ async createModel<T extends ModelType>(cls: Class<T>): Promise<void> ;
318
+ async truncateModel<T extends ModelType>(cls: Class<T>): Promise<void> ;
317
319
  // Indexed
318
320
  async getByIndex<T extends ModelType>(cls: Class<T>, idx: string, body: DeepPartial<T>): Promise<T> ;
319
- async deleteByIndex<T extends ModelType>(cls: Class<T>, idx: string, body: DeepPartial<T>) ;
321
+ async deleteByIndex<T extends ModelType>(cls: Class<T>, idx: string, body: DeepPartial<T>): Promise<void> ;
320
322
  upsertByIndex<T extends ModelType>(cls: Class<T>, idx: string, body: OptionalId<T>): Promise<T> ;
321
- async * listByIndex<T extends ModelType>(cls: Class<T>, idx: string, body?: DeepPartial<T>): AsyncGenerator<T> ;
323
+ async * listByIndex<T extends ModelType>(cls: Class<T>, idx: string, body?: DeepPartial<T>): AsyncIterable<T> ;
322
324
  }
323
325
  ```
324
326
 
@@ -390,7 +392,7 @@ Options:
390
392
 
391
393
  ## CLI - model:install
392
394
 
393
- The module provides the ability to install all the various [@Model](https://github.com/travetto/travetto/tree/main/module/model/src/registry/decorator.ts#L12)s within the application given the current configuration being targetted. This is useful for being able to prepare the datastore manually.
395
+ The module provides the ability to install all the various [@Model](https://github.com/travetto/travetto/tree/main/module/model/src/registry/decorator.ts#L12)s within the application given the current configuration being targeted. This is useful for being able to prepare the datastore manually.
394
396
 
395
397
  **Terminal: Running model install**
396
398
  ```bash
package/bin/candidate.ts CHANGED
@@ -7,7 +7,7 @@ import { ModelCandidateUtil } from './lib/candidate';
7
7
  /**
8
8
  * Handles direct invocation
9
9
  */
10
- export async function main(op: keyof ModelStorageSupport) {
10
+ export async function main(op: keyof ModelStorageSupport): Promise<void> {
11
11
  try {
12
12
  EnvInit.init();
13
13
  const { PhaseManager } = await import('@travetto/base');
@@ -8,13 +8,17 @@ export class ModelExportPlugin extends BaseModelPlugin {
8
8
  name = 'model:export';
9
9
  op = 'exportModel' as const;
10
10
 
11
- async action(provider: string, models: string[]) {
11
+ async action(provider: string, models: string[]): Promise<void> {
12
12
  try {
13
13
  await this.validate(provider, models);
14
14
  const resolved = await this.resolve(provider, models);
15
15
  await ModelExportUtil.run(resolved.provider, resolved.models);
16
- } catch (e) {
17
- console.error(e.message);
16
+ } catch (err) {
17
+ if (err instanceof Error) {
18
+ console.error(err.message);
19
+ } else {
20
+ console.error((err && err instanceof Error) ? err.message : err);
21
+ }
18
22
  }
19
23
  }
20
24
  }
@@ -10,14 +10,14 @@ export class ModelInstallPlugin extends BaseModelPlugin {
10
10
  name = 'model:install';
11
11
  op = 'createModel' as const;
12
12
 
13
- async action(provider: string, models: string[]) {
13
+ async action(provider: string, models: string[]): Promise<void> {
14
14
  try {
15
15
  await this.validate(provider, models);
16
16
  const resolved = await this.resolve(provider, models);
17
17
  await ModelInstallUtil.run(resolved.provider, resolved.models);
18
18
  console.log(color`${{ success: 'Successfully' }} installed ${{ param: models.length.toString() }} model(s)`);
19
- } catch (e) {
20
- console.error(e.message);
19
+ } catch (err) {
20
+ console.error((err && err instanceof Error) ? err.message : err);
21
21
  }
22
22
  }
23
23
  }
@@ -16,11 +16,11 @@ export abstract class BaseModelPlugin extends BasePlugin {
16
16
 
17
17
  resolve = ModelCandidateUtil.resolve.bind(ModelCandidateUtil);
18
18
 
19
- envInit() {
19
+ envInit(): void {
20
20
  EnvInit.init();
21
21
  }
22
22
 
23
- override async build() {
23
+ override async build(): Promise<void> {
24
24
  await super.build();
25
25
  const { ConsoleManager, PhaseManager } = await import('@travetto/base');
26
26
  ConsoleManager.exclude('debug');
@@ -28,17 +28,18 @@ export abstract class BaseModelPlugin extends BasePlugin {
28
28
  await PhaseManager.run('init');
29
29
  }
30
30
 
31
- getArgs() {
31
+ getArgs(): string {
32
32
  return '[provider] [models...]';
33
33
  }
34
34
 
35
+ // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
35
36
  getOptions() {
36
37
  return { env: this.option({ desc: 'Application environment' }) };
37
38
  }
38
39
 
39
- async usage({ providers, models }: { providers: string[], models: string[] }, err = '') {
40
+ async usage({ providers, models }: { providers: string[], models: string[] }, err = ''): Promise<void> {
40
41
  await this.showHelp(err, color`
41
- ${{ title: 'Proivders' }}:
42
+ ${{ title: 'Providers' }}:
42
43
  ${providers.map(p => color` * ${{ type: p }}`).join('\n')}
43
44
 
44
45
  ${{ title: 'Models' }}:
@@ -46,7 +47,7 @@ ${models.map(p => color` * ${{ param: p }}`).join('\n')}
46
47
  `);
47
48
  }
48
49
 
49
- async validate(provider: string, models: string[]) {
50
+ async validate(provider: string, models: string[]): Promise<void> {
50
51
  const candidates = await ModelCandidateUtil.getCandidates(this.op);
51
52
  if (!provider) {
52
53
  return await this.usage(candidates);
@@ -7,6 +7,8 @@ import type { InjectableConfig } from '@travetto/di';
7
7
  import type { ModelStorageSupport } from '../../src/service/storage';
8
8
  import type { ModelType } from '../../src/types/model';
9
9
 
10
+ type CandidateNames = { providers: string[], models: string[] };
11
+
10
12
  /**
11
13
  * Utilities for finding candidates for model operations
12
14
  */
@@ -15,7 +17,7 @@ export class ModelCandidateUtil {
15
17
  /**
16
18
  * Get all models
17
19
  */
18
- static async #getModels(models?: string[]) {
20
+ static async #getModels(models?: string[]): Promise<Class<ModelType>[]> {
19
21
  const names = new Set(models ?? []);
20
22
  const all = names.has('*');
21
23
  const { ModelRegistry } = await import('@travetto/model');
@@ -27,7 +29,7 @@ export class ModelCandidateUtil {
27
29
  /**
28
30
  * Get model names
29
31
  */
30
- static async getModelNames() {
32
+ static async getModelNames(): Promise<string[]> {
31
33
  const { ModelRegistry } = await import('@travetto/model');
32
34
  return (await this.#getModels()).map(x => ModelRegistry.getStore(x)).sort();
33
35
  }
@@ -38,6 +40,7 @@ export class ModelCandidateUtil {
38
40
  static async getProviders(op?: keyof ModelStorageSupport): Promise<InjectableConfig[]> {
39
41
  const { DependencyRegistry } = await import('@travetto/di');
40
42
  const { ModelStorageSupportTarget } = await import('@travetto/model/src/internal/service/common');
43
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
41
44
  const types = DependencyRegistry.getCandidateTypes<ModelStorageSupport>(ModelStorageSupportTarget as unknown as Class<ModelStorageSupport>);
42
45
  return types.filter(x => !op || x.class.prototype[op]);
43
46
  }
@@ -45,7 +48,7 @@ export class ModelCandidateUtil {
45
48
  /**
46
49
  * Get list of names of all viable providers
47
50
  */
48
- static async getProviderNames(op?: keyof ModelStorageSupport) {
51
+ static async getProviderNames(op?: keyof ModelStorageSupport): Promise<string[]> {
49
52
  return (await this.getProviders(op))
50
53
  .map(x => x.class.name.replace(/ModelService/, ''))
51
54
  .sort();
@@ -54,7 +57,7 @@ export class ModelCandidateUtil {
54
57
  /**
55
58
  * Get a single provider
56
59
  */
57
- static async getProvider(provider: string) {
60
+ static async getProvider(provider: string): Promise<ModelStorageSupport> {
58
61
  const { DependencyRegistry } = await import('@travetto/di');
59
62
  const config = (await this.getProviders()).find(x => x.class.name === `${provider}ModelService`)!;
60
63
  return DependencyRegistry.getInstance<ModelStorageSupport>(config.class, config.qualifier);
@@ -64,9 +67,9 @@ export class ModelCandidateUtil {
64
67
  * Get candidates asynchronously
65
68
  * @returns
66
69
  */
67
- static async getCandidates(op: keyof ModelStorageSupport) {
70
+ static async getCandidates(op: keyof ModelStorageSupport): Promise<CandidateNames> {
68
71
  return CliUtil.waiting('Resolving', () =>
69
- ExecUtil.workerMain<{ providers: string[], models: string[] }>(require.resolve('../candidate'), [op]).message
72
+ ExecUtil.workerMain<CandidateNames>(require.resolve('../candidate'), [op]).message
70
73
  );
71
74
  }
72
75
 
package/bin/lib/export.ts CHANGED
@@ -3,7 +3,7 @@ import type { ModelStorageSupport } from '@travetto/model/src/service/storage';
3
3
  import type { ModelType } from '@travetto/model/src/types/model';
4
4
 
5
5
  export class ModelExportUtil {
6
- static async run(provider: ModelStorageSupport, models: Class<ModelType>[]) {
6
+ static async run(provider: ModelStorageSupport, models: Class<ModelType>[]): Promise<void> {
7
7
  for (const model of models) {
8
8
  console.log(await provider.exportModel!(model));
9
9
  }
@@ -3,7 +3,7 @@ import type { ModelStorageSupport } from '@travetto/model/src/service/storage';
3
3
  import type { ModelType } from '@travetto/model/src/types/model';
4
4
 
5
5
  export class ModelInstallUtil {
6
- static async run(provider: ModelStorageSupport, models: Class<ModelType>[]) {
6
+ static async run(provider: ModelStorageSupport, models: Class<ModelType>[]): Promise<void> {
7
7
  if (!provider.createModel) {
8
8
  throw new Error(`${provider} does not support model installation`);
9
9
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@travetto/model",
3
3
  "displayName": "Data Modeling Support",
4
- "version": "2.1.5",
4
+ "version": "2.2.0",
5
5
  "description": "Datastore abstraction for core operations.",
6
6
  "keywords": [
7
7
  "datastore",
@@ -28,13 +28,13 @@
28
28
  "directory": "module/model"
29
29
  },
30
30
  "dependencies": {
31
- "@travetto/di": "^2.1.5",
32
- "@travetto/config": "^2.1.5",
33
- "@travetto/registry": "^2.1.5",
34
- "@travetto/schema": "^2.1.5"
31
+ "@travetto/di": "^2.2.0",
32
+ "@travetto/config": "^2.2.0",
33
+ "@travetto/registry": "^2.2.0",
34
+ "@travetto/schema": "^2.2.0"
35
35
  },
36
36
  "optionalPeerDependencies": {
37
- "@travetto/cli": "^2.1.3"
37
+ "@travetto/cli": "^2.2.0"
38
38
  },
39
39
  "publishConfig": {
40
40
  "access": "public"
@@ -4,6 +4,14 @@ import { BulkOp } from '../../service/bulk';
4
4
  import { ModelType } from '../../types/model';
5
5
  import { ModelCrudUtil } from './crud';
6
6
 
7
+ export type BulkPreStore<T extends ModelType> = {
8
+ insertedIds: Map<number, string>;
9
+ upsertedIds: Map<number, string>;
10
+ updatedIds: Map<number, string>;
11
+ existingUpsertedIds: Map<number, string>;
12
+ operations: BulkOp<T>[];
13
+ };
14
+
7
15
  export class ModelBulkUtil {
8
16
  /**
9
17
  * Prepares bulk ops for storage
@@ -11,7 +19,7 @@ export class ModelBulkUtil {
11
19
  * @param operations
12
20
  * @param idSource
13
21
  */
14
- static async preStore<T extends ModelType>(cls: Class<T>, operations: BulkOp<T>[], idSource: { uuid(): string }) {
22
+ static async preStore<T extends ModelType>(cls: Class<T>, operations: BulkOp<T>[], idSource: { uuid(): string }): Promise<BulkPreStore<T>> {
15
23
  const insertedIds = new Map<number, string>();
16
24
  const upsertedIds = new Map<number, string>();
17
25
  const updatedIds = new Map<number, string>();
@@ -18,6 +18,7 @@ export class ModelIndexedSupportTarget { }
18
18
  * @param o
19
19
  */
20
20
  export function isBasicSupported(o: unknown): o is ModelBulkSupport {
21
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
21
22
  return !!o && !!(o as Record<string, unknown>)['create'];
22
23
  }
23
24
 
@@ -26,6 +27,7 @@ export function isBasicSupported(o: unknown): o is ModelBulkSupport {
26
27
  * @param o
27
28
  */
28
29
  export function isCrudSupported(o: unknown): o is ModelCrudSupport {
30
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
29
31
  return !!o && !!(o as Record<string, unknown>)['upsert'];
30
32
  }
31
33
 
@@ -34,6 +36,7 @@ export function isCrudSupported(o: unknown): o is ModelCrudSupport {
34
36
  * @param o
35
37
  */
36
38
  export function isExpirySupported(o: unknown): o is ModelExpirySupport {
39
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
37
40
  return !!o && !!(o as Record<string, unknown>)['deleteExpired'];
38
41
  }
39
42
 
@@ -42,6 +45,7 @@ export function isExpirySupported(o: unknown): o is ModelExpirySupport {
42
45
  * @param o
43
46
  */
44
47
  export function isStorageSupported(o: unknown): o is ModelStorageSupport {
48
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
45
49
  return !!o && !!(o as Record<string, unknown>)['createStorage'];
46
50
  }
47
51
 
@@ -50,6 +54,7 @@ export function isStorageSupported(o: unknown): o is ModelStorageSupport {
50
54
  * @param o
51
55
  */
52
56
  export function isStreamSupported(o: unknown): o is ModelStreamSupport {
57
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
53
58
  return !!o && !!(o as Record<string, unknown>)['getStream'];
54
59
  }
55
60
 
@@ -58,6 +63,7 @@ export function isStreamSupported(o: unknown): o is ModelStreamSupport {
58
63
  * @param o
59
64
  */
60
65
  export function isBulkSupported(o: unknown): o is ModelBulkSupport {
66
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
61
67
  return !!o && !!(o as Record<string, unknown>)['processBulk'];
62
68
  }
63
69
 
@@ -66,6 +72,7 @@ export function isBulkSupported(o: unknown): o is ModelBulkSupport {
66
72
  * @param o
67
73
  */
68
74
  export function isIndexedSupported(o: unknown): o is ModelIndexedSupport {
75
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
69
76
  return !!o && !!(o as Record<string, unknown>)['getByIndex'];
70
77
  }
71
78
 
@@ -19,7 +19,7 @@ export class ModelCrudUtil {
19
19
  * @param value Input value
20
20
  * @param length Number of characters to produce
21
21
  */
22
- static hashValue(value: string, length = 32) {
22
+ static hashValue(value: string, length = 32): string {
23
23
  if (value.length < 32) {
24
24
  value = value.padEnd(32, ' ');
25
25
  }
@@ -38,6 +38,7 @@ export class ModelCrudUtil {
38
38
  input = JSON.parse(input.toString('utf8'));
39
39
  }
40
40
 
41
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
41
42
  const result = ModelRegistry.getBaseModel(cls).from(input as object) as T;
42
43
 
43
44
  if (!(result instanceof cls || result.constructor.ᚕid === cls.ᚕid)) {
@@ -55,7 +56,7 @@ export class ModelCrudUtil {
55
56
  }
56
57
 
57
58
  /**
58
- * Prepares item for storge
59
+ * Prepares item for storage
59
60
  *
60
61
  * @param cls Type to store for
61
62
  * @param item Item to store
@@ -66,11 +67,13 @@ export class ModelCrudUtil {
66
67
  }
67
68
 
68
69
  if (Util.isPlainObject(item)) {
69
- item = cls.from(item as {});
70
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
71
+ item = cls.from(item as object);
70
72
  }
71
73
 
74
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
72
75
  const config = ModelRegistry.get(item.constructor as Class<T>);
73
- if (config.subType) { // Subtyping, assign type
76
+ if (config.subType) { // Sub-typing, assign type
74
77
  SchemaRegistry.ensureInstanceTypeField(cls, item);
75
78
  }
76
79
 
@@ -80,6 +83,7 @@ export class ModelCrudUtil {
80
83
  await item.prePersist();
81
84
  }
82
85
 
86
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
83
87
  return item as T;
84
88
  }
85
89
 
@@ -92,11 +96,13 @@ export class ModelCrudUtil {
92
96
  */
93
97
  static async naivePartialUpdate<T extends ModelType>(cls: Class<T>, item: Partial<T>, view: undefined | string, getExisting: () => Promise<T>): Promise<T> {
94
98
  if (Util.isPlainObject(item)) {
95
- item = cls.from(item as {});
99
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
100
+ item = cls.from(item as object);
96
101
  }
97
102
 
103
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
98
104
  const config = ModelRegistry.get(item.constructor as Class<T>);
99
- if (config.subType) { // Subtyping, assign type
105
+ if (config.subType) { // Sub-typing, assign type
100
106
  SchemaRegistry.ensureInstanceTypeField(cls, item);
101
107
  }
102
108
 
@@ -112,13 +118,14 @@ export class ModelCrudUtil {
112
118
  await item.prePersist();
113
119
  }
114
120
 
121
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
115
122
  return item as T;
116
123
  }
117
124
 
118
125
  /**
119
126
  * Ensure subtype is not supported
120
127
  */
121
- static ensureNotSubType(cls: Class) {
128
+ static ensureNotSubType(cls: Class): void {
122
129
  if (ModelRegistry.get(cls).subType) {
123
130
  throw new SubTypeNotSupportedError(cls);
124
131
  }
@@ -12,23 +12,26 @@ export class ModelExpiryUtil {
12
12
  /**
13
13
  * Get expiry info for a given item
14
14
  */
15
- static getExpiryState<T extends ModelType>(cls: Class<T>, item: T) {
15
+ static getExpiryState<T extends ModelType>(cls: Class<T>, item: T): { expiresAt?: Date, expired?: boolean } {
16
16
  const expKey = ModelRegistry.getExpiry(cls);
17
- const expiresAt = item[expKey as keyof T] ? item[expKey as keyof T] as unknown as Date : undefined;
17
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
18
+ const keyAsT = expKey as keyof T;
19
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
20
+ const expiresAt = item[keyAsT] ? item[keyAsT] as unknown as Date : undefined;
18
21
 
19
22
  return {
20
23
  expiresAt,
21
24
  expired: expiresAt ? expiresAt.getTime() < Date.now() : undefined
22
- } as const;
25
+ };
23
26
  }
24
27
 
25
28
  /**
26
29
  * Delete all expired on a fixed interval, if supported and needed
27
30
  * @param svc
28
31
  */
29
- static registerCull(svc: ModelExpirySupport & { readonly config?: { cullRate?: number | TimeSpan } }) {
30
- const expirable = ModelRegistry.getClasses().filter(cls => !!ModelRegistry.get(cls).expiresAt);
31
- if (svc.deleteExpired && expirable.length) {
32
+ static registerCull(svc: ModelExpirySupport & { readonly config?: { cullRate?: number | TimeSpan } }): void {
33
+ const cullable = ModelRegistry.getClasses().filter(cls => !!ModelRegistry.get(cls).expiresAt);
34
+ if (svc.deleteExpired && cullable.length) {
32
35
  let running = true;
33
36
  const cullInterval = Util.timeToMs(svc.config?.cullRate ?? '10m');
34
37
 
@@ -39,11 +42,11 @@ export class ModelExpiryUtil {
39
42
  },
40
43
  name: 'expiry-culling'
41
44
  });
42
- (async () => {
45
+ (async (): Promise<void> => {
43
46
  await Util.wait('1s'); // Wait a second to start culling
44
47
  while (running) {
45
48
  await Util.wait(cullInterval);
46
- await Promise.all(expirable.map(cls => svc.deleteExpired(cls)));
49
+ await Promise.all(cullable.map(cls => svc.deleteExpired(cls)));
47
50
  }
48
51
  })();
49
52
  }