@travetto/model 5.0.0-rc.7 → 5.0.0-rc.9

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
@@ -235,7 +235,7 @@ In addition to the provided contracts, the module also provides common utilities
235
235
  ```typescript
236
236
  import { Readable } from 'node:stream';
237
237
  import { buffer as toBuffer } from 'node:stream/consumers';
238
- import { Class, TimeSpan, DeepPartial } from '@travetto/runtime';
238
+ import { Class, TimeSpan, DeepPartial, castTo } from '@travetto/runtime';
239
239
  import { Injectable } from '@travetto/di';
240
240
  import { Config } from '@travetto/config';
241
241
  import { ModelCrudSupport } from '../service/crud';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@travetto/model",
3
- "version": "5.0.0-rc.7",
3
+ "version": "5.0.0-rc.9",
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.7",
30
- "@travetto/di": "^5.0.0-rc.7",
31
- "@travetto/registry": "^5.0.0-rc.7",
32
- "@travetto/schema": "^5.0.0-rc.7"
29
+ "@travetto/config": "^5.0.0-rc.9",
30
+ "@travetto/di": "^5.0.0-rc.9",
31
+ "@travetto/registry": "^5.0.0-rc.9",
32
+ "@travetto/schema": "^5.0.0-rc.9"
33
33
  },
34
34
  "peerDependencies": {
35
- "@travetto/cli": "^5.0.0-rc.7",
36
- "@travetto/test": "^5.0.0-rc.7"
35
+ "@travetto/cli": "^5.0.0-rc.9",
36
+ "@travetto/test": "^5.0.0-rc.9"
37
37
  },
38
38
  "peerDependenciesMeta": {
39
39
  "@travetto/cli": {
@@ -1,3 +1,4 @@
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';
@@ -17,62 +18,55 @@ export class ModelIndexedSupportTarget { }
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
42
  * Type guard for determining if service supports storage 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 isStorageSupported(o: ClassInstance): o is ModelStorageSupport {
46
+ return !!o && 'createStorage' in o;
50
47
  }
51
48
 
52
49
  /**
53
50
  * Type guard for determining if service supports streaming 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 isStreamSupported(o: ClassInstance): o is ModelStreamSupport {
54
+ return !!o && 'getStream' 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/runtime';
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/runtime';
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,
@@ -1,4 +1,4 @@
1
- import { Class, DeepPartial } from '@travetto/runtime';
1
+ import { castTo, Class, DeepPartial, TypedObject } from '@travetto/runtime';
2
2
 
3
3
  import { IndexNotSupported } from '../../error/invalid-index';
4
4
  import { NotFoundError } from '../../error/not-found';
@@ -44,38 +44,30 @@ export class ModelIndexedUtil {
44
44
  const parts = [];
45
45
 
46
46
  while (o !== undefined && o !== null) {
47
- const k = Object.keys(f)[0];
48
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
49
- o = (o[k] as Record<string, unknown>);
47
+ const k = TypedObject.keys(f)[0];
48
+ o = castTo(o[k]);
50
49
  parts.push(k);
51
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
52
- const fk = k as (keyof typeof f);
53
- if (typeof f[fk] === 'boolean' || typeof f[fk] === 'number') {
50
+ if (typeof f[k] === 'boolean' || typeof f[k] === 'number') {
54
51
  if (cfg.type === 'sorted') {
55
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
56
- sortDir = f[fk] === true ? 1 : f[fk] as number;
52
+ sortDir = f[k] === true ? 1 : f[k] === false ? 0 : f[k];
57
53
  }
58
54
  break; // At the bottom
59
55
  } else {
60
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
61
- f = f[fk] as Record<string, unknown>;
56
+ f = castTo(f[k]);
62
57
  }
63
58
  }
64
59
  if (field === sortField) {
65
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
66
- sorted = { path: parts, dir: sortDir, value: o as unknown as number | Date };
60
+ sorted = { path: parts, dir: sortDir, value: castTo(o) };
67
61
  }
68
62
  if (o === undefined || o === null) {
69
63
  const empty = field === sortField ? opts.emptySortValue : opts.emptyValue;
70
64
  if (empty === undefined || empty === Error) {
71
65
  throw new IndexNotSupported(cls, cfg, `Missing field value for ${parts.join('.')}`);
72
66
  }
73
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
74
- o = empty as Record<string, unknown>;
67
+ o = castTo(empty!);
75
68
  } else {
76
69
  if (field !== sortField || (opts.includeSortInFields ?? true)) {
77
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
78
- fields.push({ path: parts, value: o as unknown as string | boolean | Date | number });
70
+ fields.push({ path: parts, value: castTo(o) });
79
71
  }
80
72
  }
81
73
  }
@@ -92,12 +84,11 @@ export class ModelIndexedUtil {
92
84
  static projectIndex<T extends ModelType>(cls: Class<T>, idx: IndexConfig<T> | string, item?: DeepPartial<T>, cfg?: ComputeConfig): Record<string, unknown> {
93
85
  const res: Record<string, unknown> = {};
94
86
  for (const { path, value } of this.computeIndexParts(cls, idx, item ?? {}, cfg).fields) {
95
- let sub = res;
87
+ let sub: Record<string, unknown> = res;
96
88
  const all = path.slice(0);
97
89
  const last = all.pop()!;
98
90
  for (const k of all) {
99
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
100
- sub = (sub[k] ??= {}) as typeof res;
91
+ sub = castTo(sub[k] ??= {});
101
92
  }
102
93
  sub[last] = value;
103
94
  }
@@ -134,11 +125,9 @@ export class ModelIndexedUtil {
134
125
  cls: Class<T>, idx: string, body: OptionalId<T>
135
126
  ): Promise<T> {
136
127
  try {
137
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
138
- const { id } = await service.getByIndex(cls, idx, body as DeepPartial<T>);
128
+ const { id } = await service.getByIndex(cls, idx, castTo(body));
139
129
  body.id = id;
140
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
141
- return await service.update(cls, body as T);
130
+ return await service.update(cls, castTo(body));
142
131
  } catch (err) {
143
132
  if (err instanceof NotFoundError) {
144
133
  return await service.create(cls, body);
@@ -12,14 +12,11 @@ export class ModelStorageUtil {
12
12
  /**
13
13
  * Register change listener on startup
14
14
  */
15
- static async registerModelChangeListener(storage: ModelStorageSupport, target?: Class): Promise<void> {
15
+ static async registerModelChangeListener(storage: ModelStorageSupport): Promise<void> {
16
16
  if (!Runtime.dynamic || !(storage?.config?.autoCreate ?? !Runtime.production)) {
17
17
  return;
18
18
  }
19
19
 
20
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
21
- target = target ?? storage.constructor as Class<ModelStorageSupport>;
22
-
23
20
  const checkType = (cls: Class, enforceBase = true): boolean => {
24
21
  if (enforceBase && ModelRegistry.getBaseModel(cls) !== cls) {
25
22
  return false;
@@ -5,7 +5,7 @@ import { Readable } from 'node:stream';
5
5
  import { pipeline } from 'node:stream/promises';
6
6
  import path from 'node:path';
7
7
 
8
- import { Class, TimeSpan, Runtime } from '@travetto/runtime';
8
+ import { Class, TimeSpan, Runtime, asFull } from '@travetto/runtime';
9
9
  import { Injectable } from '@travetto/di';
10
10
  import { Config } from '@travetto/config';
11
11
  import { Required } from '@travetto/schema';
@@ -153,9 +153,7 @@ export class FileModelService implements ModelCrudSupport, ModelStreamSupport, M
153
153
  item = await ModelCrudUtil.naivePartialUpdate(cls, item, view, () => this.get(cls, id));
154
154
  const file = await this.#resolveName(cls, '.json', item.id);
155
155
  await fs.writeFile(file, JSON.stringify(item), { encoding: 'utf8' });
156
-
157
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
158
- return item as T;
156
+ return asFull<T>(item);
159
157
  }
160
158
 
161
159
  async delete<T extends ModelType>(cls: Class<T>, id: string): Promise<void> {
@@ -1,7 +1,7 @@
1
1
  import { Readable } from 'node:stream';
2
2
  import { buffer as toBuffer } from 'node:stream/consumers';
3
3
 
4
- import { Class, TimeSpan, DeepPartial } from '@travetto/runtime';
4
+ import { Class, TimeSpan, DeepPartial, castTo } from '@travetto/runtime';
5
5
  import { Injectable } from '@travetto/di';
6
6
  import { Config } from '@travetto/config';
7
7
 
@@ -87,8 +87,7 @@ export class MemoryModelService implements ModelCrudSupport, ModelStreamSupport,
87
87
  const item = await this.get(cls, id);
88
88
  for (const idx of ModelRegistry.getIndices(cls, ['sorted', 'unsorted'])) {
89
89
  const idxName = indexName(cls, idx);
90
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
91
- const { key } = ModelIndexedUtil.computeIndexKey(cls, idx, item as DeepPartial<T>);
90
+ const { key } = ModelIndexedUtil.computeIndexKey(cls, idx, castTo(item));
92
91
  this.#indices[idx.type].get(idxName)?.get(key)?.delete(id);
93
92
  }
94
93
  } catch (err) {
@@ -101,8 +100,7 @@ export class MemoryModelService implements ModelCrudSupport, ModelStreamSupport,
101
100
  async #writeIndices<T extends ModelType>(cls: Class<T>, item: T): Promise<void> {
102
101
  for (const idx of ModelRegistry.getIndices(cls, ['sorted', 'unsorted'])) {
103
102
  const idxName = indexName(cls, idx);
104
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
105
- const { key, sort } = ModelIndexedUtil.computeIndexKey(cls, idx, item as DeepPartial<T>);
103
+ const { key, sort } = ModelIndexedUtil.computeIndexKey(cls, idx, castTo(item));
106
104
  let index = this.#indices[idx.type].get(idxName)?.get(key);
107
105
 
108
106
  if (!index) {
@@ -1,4 +1,4 @@
1
- import { Class } from '@travetto/runtime';
1
+ import { asConstructable, castTo, Class } from '@travetto/runtime';
2
2
  import { SchemaRegistry } from '@travetto/schema';
3
3
 
4
4
  import { ModelType } from '../types/model';
@@ -25,7 +25,7 @@ export function Model(conf: Partial<ModelOptions<ModelType>> | string = {}) {
25
25
  * Defines an index on a model
26
26
  */
27
27
  export function Index<T extends ModelType>(...indices: IndexConfig<T>[]) {
28
- return function (target: Class<T>) {
28
+ return function (target: Class<T>): void {
29
29
  ModelRegistry.getOrCreatePending(target).indices!.push(...indices);
30
30
  };
31
31
  }
@@ -36,8 +36,7 @@ export function Index<T extends ModelType>(...indices: IndexConfig<T>[]) {
36
36
  */
37
37
  export function ExpiresAt() {
38
38
  return <K extends string, T extends Partial<Record<K, Date>>>(tgt: T, prop: K): void => {
39
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
40
- ModelRegistry.register(tgt.constructor as Class<T>, { expiresAt: prop });
39
+ ModelRegistry.register(asConstructable(tgt).constructor, { expiresAt: prop });
41
40
  };
42
41
  }
43
42
 
@@ -49,8 +48,7 @@ export function PrePersist<T>(handler: DataHandler<T>, scope: PrePersistScope =
49
48
  ModelRegistry.registerDataHandlers(tgt, {
50
49
  prePersist: [{
51
50
  scope,
52
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
53
- handler: handler as DataHandler
51
+ handler: castTo(handler)
54
52
  }]
55
53
  });
56
54
  };
@@ -61,13 +59,11 @@ export function PrePersist<T>(handler: DataHandler<T>, scope: PrePersistScope =
61
59
  */
62
60
  export function PersistValue<T>(handler: (curr: T | undefined) => T, scope: PrePersistScope = 'all') {
63
61
  return function <K extends string, C extends Partial<Record<K, T>>>(tgt: C, prop: K): void {
64
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
65
- ModelRegistry.registerDataHandlers(tgt.constructor as Class<C>, {
62
+ ModelRegistry.registerDataHandlers(asConstructable(tgt).constructor, {
66
63
  prePersist: [{
67
64
  scope,
68
65
  handler: (inst): void => {
69
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
70
- const cInst = (inst as unknown as Record<K, T>);
66
+ const cInst: Record<K, T> = castTo(inst);
71
67
  cInst[prop] = handler(cInst[prop]);
72
68
  }
73
69
  }]
@@ -81,7 +77,6 @@ export function PersistValue<T>(handler: (curr: T | undefined) => T, scope: PreP
81
77
  */
82
78
  export function PostLoad<T>(handler: DataHandler<T>) {
83
79
  return function (tgt: Class<T>): void {
84
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
85
- ModelRegistry.registerDataHandlers(tgt, { postLoad: [handler as DataHandler] });
80
+ ModelRegistry.registerDataHandlers(tgt, { postLoad: [castTo(handler)] });
86
81
  };
87
82
  }
@@ -1,7 +1,7 @@
1
1
  import { SchemaRegistry } from '@travetto/schema';
2
2
  import { MetadataRegistry } from '@travetto/registry';
3
3
  import { DependencyRegistry } from '@travetto/di';
4
- import { AppError, Class, describeFunction } from '@travetto/runtime';
4
+ import { AppError, castTo, Class, describeFunction, asFull } from '@travetto/runtime';
5
5
  import { AllViewⲐ } from '@travetto/schema/src/internal/types';
6
6
 
7
7
  import { IndexConfig, IndexType, ModelOptions } from './types';
@@ -71,8 +71,7 @@ class $ModelRegistry extends MetadataRegistry<ModelOptions<ModelType>> {
71
71
  }
72
72
 
73
73
  onInstallFinalize(cls: Class): ModelOptions<ModelType> {
74
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
75
- const config = this.pending.get(cls.Ⲑid)! as ModelOptions<ModelType>;
74
+ const config = asFull(this.pending.get(cls.Ⲑid)!);
76
75
 
77
76
  const schema = SchemaRegistry.get(cls);
78
77
  const view = schema.views[AllViewⲐ].schema;
@@ -103,7 +102,7 @@ class $ModelRegistry extends MetadataRegistry<ModelOptions<ModelType>> {
103
102
  /**
104
103
  * Find base class for a given model
105
104
  */
106
- getBaseModel(cls: Class): Class<ModelType> {
105
+ getBaseModel<T extends ModelType>(cls: Class<T>): Class<T> {
107
106
  if (!this.baseModels.has(cls)) {
108
107
  let conf = this.get(cls) ?? this.getOrCreatePending(cls);
109
108
  let parent = cls;
@@ -208,12 +207,12 @@ class $ModelRegistry extends MetadataRegistry<ModelOptions<ModelType>> {
208
207
  * Get expiry field
209
208
  * @param cls
210
209
  */
211
- getExpiry(cls: Class): string {
210
+ getExpiry<T extends ModelType>(cls: Class<T>): keyof T {
212
211
  const expiry = this.get(cls).expiresAt;
213
212
  if (!expiry) {
214
213
  throw new AppError(`${cls.name} is not configured with expiry support, please use @ExpiresAt to declare expiration behavior`, 'general');
215
214
  }
216
- return expiry;
215
+ return castTo(expiry);
217
216
  }
218
217
  }
219
218
 
@@ -12,11 +12,11 @@ export interface StreamMeta {
12
12
  /**
13
13
  * Hash of the file contents. Different files with the same name, will have the same hash
14
14
  */
15
- hash: string;
15
+ hash?: string;
16
16
  /**
17
17
  * The original base filename of the file
18
18
  */
19
- filename: string;
19
+ filename?: string;
20
20
  /**
21
21
  * Filenames title, optional for elements like images, audio, videos
22
22
  */
@@ -1,4 +1,4 @@
1
- import { Class } from '@travetto/runtime';
1
+ import { castTo, Class } from '@travetto/runtime';
2
2
  import { ModelRegistry } from '@travetto/model/src/registry/model';
3
3
  import { InjectableConfig, DependencyRegistry } from '@travetto/di';
4
4
  import { ModelStorageSupportTarget } from '@travetto/model/src/internal/service/common';
@@ -40,8 +40,7 @@ export class ModelCandidateUtil {
40
40
  * Get all providers that are viable candidates
41
41
  */
42
42
  static async getProviders(op?: keyof ModelStorageSupport): Promise<InjectableConfig[]> {
43
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
44
- const types = DependencyRegistry.getCandidateTypes<ModelStorageSupport>(ModelStorageSupportTarget as unknown as Class<ModelStorageSupport>);
43
+ const types = DependencyRegistry.getCandidateTypes<ModelStorageSupport>(castTo(ModelStorageSupportTarget));
45
44
  return types.filter(x => !op || x.class.prototype?.[op]);
46
45
  }
47
46
 
@@ -1,5 +1,5 @@
1
1
  import { DependencyRegistry } from '@travetto/di';
2
- import { AppError, Class } from '@travetto/runtime';
2
+ import { AppError, castTo, Class, classConstruct } from '@travetto/runtime';
3
3
 
4
4
  import { isBulkSupported, isCrudSupported } from '../../src/internal/service/common';
5
5
  import { ModelType } from '../../src/types/model';
@@ -11,7 +11,7 @@ type ServiceClass = { serviceClass: { new(): unknown } };
11
11
  export abstract class BaseModelSuite<T> {
12
12
 
13
13
  static ifNot(pred: (svc: unknown) => boolean): (x: unknown) => Promise<boolean> {
14
- return async (x: unknown) => !pred(new (x as ServiceClass).serviceClass());
14
+ return async (x: unknown) => !pred(classConstruct(castTo<ServiceClass>(x).serviceClass));
15
15
  }
16
16
 
17
17
  serviceClass: Class<T>;
@@ -36,7 +36,7 @@ export abstract class BaseModelSuite<T> {
36
36
  const res = await svc.processBulk(cls, items.map(x => ({ insert: x })));
37
37
  return res.counts.insert;
38
38
  } else if (isCrudSupported(svc)) {
39
- const out = [] as Promise<M>[];
39
+ const out: Promise<M>[] = [];
40
40
  for (const el of items) {
41
41
  out.push(svc.create(cls, el));
42
42
  }
@@ -49,7 +49,7 @@ class User2 {
49
49
  name: string;
50
50
 
51
51
  prePersist() {
52
- this.name = `${this.name}-suff`;
52
+ this.name = `${this.name}-suffix`;
53
53
  }
54
54
  }
55
55
 
@@ -193,7 +193,7 @@ export abstract class ModelCrudSuite extends BaseModelSuite<ModelCrudSupport> {
193
193
  }));
194
194
 
195
195
  assert(o.address === undefined);
196
- assert(o.name === 'bob-suff');
196
+ assert(o.name === 'bob-suffix');
197
197
 
198
198
  await service.updatePartial(User2, User2.from({
199
199
  id: o.id,
@@ -217,7 +217,7 @@ export abstract class ModelCrudSuite extends BaseModelSuite<ModelCrudSupport> {
217
217
  assert(res.createdDate instanceof Date);
218
218
  }
219
219
 
220
- @Test('verify prepersist on create/update')
220
+ @Test('verify pre-persist on create/update')
221
221
  async testPrePersist() {
222
222
  const service = await this.service;
223
223
  const res = await service.create(Dated, Dated.from({}));
@@ -12,6 +12,7 @@ import { isIndexedSupported } from '../../src/internal/service/common';
12
12
  import { ExistsError } from '../../src/error/exists';
13
13
 
14
14
  import { BaseModelSuite } from './base';
15
+ import { castTo } from '@travetto/runtime';
15
16
 
16
17
  @Model({ baseType: true })
17
18
  export class Worker {
@@ -70,7 +71,7 @@ export class IndexedEngineer extends IndexedWorker {
70
71
  }
71
72
 
72
73
  async function collect<T>(iterable: AsyncIterable<T>): Promise<T[]> {
73
- const out = [] as T[];
74
+ const out: T[] = [];
74
75
  for await (const el of iterable) {
75
76
  out.push(el);
76
77
  }
@@ -116,12 +117,12 @@ export abstract class ModelPolymorphismSuite extends BaseModelSuite<ModelCrudSup
116
117
 
117
118
  const fire3 = all.find(x => x instanceof Firefighter);
118
119
  assert(fire3 instanceof Firefighter);
119
- assert((fire3 as Firefighter).firehouse === 20);
120
+ assert(fire3.firehouse === 20);
120
121
  assert(fire3.name === 'rob');
121
122
 
122
123
  const eng3 = all.find(x => x instanceof Engineer);
123
124
  assert(eng3 instanceof Engineer);
124
- assert((eng3 as Engineer).major === 'oranges');
125
+ assert(eng3.major === 'oranges');
125
126
  assert(eng3.name === 'cob');
126
127
 
127
128
  const engineers = await collect(service.list(Engineer));
@@ -161,7 +162,7 @@ export abstract class ModelPolymorphismSuite extends BaseModelSuite<ModelCrudSup
161
162
  );
162
163
 
163
164
  await assert.rejects(
164
- () => service.update(Engineer, Doctor.from({ ...doc }) as unknown as Engineer),
165
+ () => service.update(Engineer, castTo(Doctor.from({ ...doc }))),
165
166
  (e: Error) => (e instanceof NotFoundError || e instanceof SubTypeNotSupportedError || e instanceof TypeMismatchError) ? undefined : e);
166
167
 
167
168
  await timers.setTimeout(15);
@@ -205,7 +206,7 @@ export abstract class ModelPolymorphismSuite extends BaseModelSuite<ModelCrudSup
205
206
 
206
207
  @Test('Polymorphic index', { skip: BaseModelSuite.ifNot(isIndexedSupported) })
207
208
  async polymorphicIndexGet() {
208
- const service = (await this.service) as unknown as ModelIndexedSupport;
209
+ const service: ModelIndexedSupport = castTo(await this.service);
209
210
  const now = 30;
210
211
  const [doc, fire, eng] = [
211
212
  IndexedDoctor.from({ name: 'bob', specialty: 'feet', age: now }),
@@ -235,7 +236,7 @@ export abstract class ModelPolymorphismSuite extends BaseModelSuite<ModelCrudSup
235
236
 
236
237
  @Test('Polymorphic index', { skip: BaseModelSuite.ifNot(isIndexedSupported) })
237
238
  async polymorphicIndexDelete() {
238
- const service = (await this.service) as unknown as ModelIndexedSupport;
239
+ const service: ModelIndexedSupport = castTo(await this.service);
239
240
  const now = 30;
240
241
  const [doc, fire, eng] = [
241
242
  IndexedDoctor.from({ name: 'bob', specialty: 'feet', age: now }),
@@ -3,7 +3,7 @@ import assert from 'node:assert';
3
3
  import crypto from 'node:crypto';
4
4
  import { Readable } from 'node:stream';
5
5
  import { pipeline } from 'node:stream/promises';
6
- import { buffer as toBuffer } from 'node:stream/consumers';
6
+ import { text as toText } from 'node:stream/consumers';
7
7
 
8
8
  import { Suite, Test, TestFixtures } from '@travetto/test';
9
9
 
@@ -17,9 +17,9 @@ export abstract class ModelStreamSuite extends BaseModelSuite<ModelStreamSupport
17
17
  fixture = new TestFixtures(['@travetto/model']);
18
18
 
19
19
  async getHash(stream: Readable): Promise<string> {
20
- const hasher = crypto.createHash('sha1').setEncoding('hex');
21
- await pipeline(stream, hasher);
22
- return hasher.read().toString();
20
+ const hash = crypto.createHash('sha1').setEncoding('hex');
21
+ await pipeline(stream, hash);
22
+ return hash.read().toString();
23
23
  }
24
24
 
25
25
  async getStream(resource: string): Promise<readonly [{ size: number, contentType: string, hash: string, filename: string }, Readable]> {
@@ -77,30 +77,33 @@ export abstract class ModelStreamSuite extends BaseModelSuite<ModelStreamSupport
77
77
  await service.upsertStream(meta.hash, stream, meta);
78
78
 
79
79
  const retrieved = await service.getStream(meta.hash);
80
- const content = (await toBuffer(retrieved)).toString('utf8');
80
+ const content = await toText(retrieved);
81
81
  assert(content.startsWith('abc'));
82
82
  assert(content.endsWith('xyz'));
83
83
 
84
84
  const partial = await service.getStream(meta.hash, { start: 10, end: 20 });
85
- const subContent = (await toBuffer(partial)).toString('utf8');
85
+ const subContent = await toText(partial);
86
86
  const range = await enforceRange({ start: 10, end: 20 }, meta.size);
87
87
  assert(subContent.length === (range.end - range.start) + 1);
88
- assert(subContent === 'klmnopqrstu');
88
+
89
+ const og = await this.fixture.read('/text.txt');
90
+
91
+ assert(subContent === og.substring(10, 21));
89
92
 
90
93
  const partialUnbounded = await service.getStream(meta.hash, { start: 10 });
91
- const subContent2 = (await toBuffer(partialUnbounded)).toString('utf8');
94
+ const subContent2 = await toText(partialUnbounded);
92
95
  const range2 = await enforceRange({ start: 10 }, meta.size);
93
96
  assert(subContent2.length === (range2.end - range2.start) + 1);
94
97
  assert(subContent2.startsWith('klm'));
95
98
  assert(subContent2.endsWith('xyz'));
96
99
 
97
100
  const partialSingle = await service.getStream(meta.hash, { start: 10, end: 10 });
98
- const subContent3 = (await toBuffer(partialSingle)).toString('utf8');
101
+ const subContent3 = await toText(partialSingle);
99
102
  assert(subContent3.length === 1);
100
103
  assert(subContent3 === 'k');
101
104
 
102
- const partialOverbounded = await service.getStream(meta.hash, { start: 20, end: 40 });
103
- const subContent4 = (await toBuffer(partialOverbounded)).toString('utf8');
105
+ const partialOverBounded = await service.getStream(meta.hash, { start: 20, end: 40 });
106
+ const subContent4 = await toText(partialOverBounded);
104
107
  assert(subContent4.length === 6);
105
108
  assert(subContent4.endsWith('xyz'));
106
109