@travetto/model 2.1.5 → 2.2.2

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.
@@ -30,31 +30,32 @@ class $ModelRegistry extends MetadataRegistry<ModelOptions<ModelType>> {
30
30
  * by requested store name. This is the state at the
31
31
  * start of the application.
32
32
  */
33
- intialModelNameMapping = new Map<string, Class[]>();
33
+ initialModelNameMapping = new Map<string, Class[]>();
34
34
 
35
35
  constructor() {
36
36
  // Listen to schema and dependency
37
37
  super(SchemaRegistry, DependencyRegistry);
38
38
  }
39
39
 
40
- getInitialNameMapping() {
41
- if (this.intialModelNameMapping.size === 0) {
40
+ getInitialNameMapping(): Map<string, Class[]> {
41
+ if (this.initialModelNameMapping.size === 0) {
42
42
  for (const cls of this.getClasses()) {
43
43
  const store = this.get(cls).store ?? cls.name;
44
- if (!this.intialModelNameMapping.has(store)) {
45
- this.intialModelNameMapping.set(store, []);
44
+ if (!this.initialModelNameMapping.has(store)) {
45
+ this.initialModelNameMapping.set(store, []);
46
46
  }
47
- this.intialModelNameMapping.get(store)!.push(cls);
47
+ this.initialModelNameMapping.get(store)!.push(cls);
48
48
  }
49
49
  }
50
- return this.intialModelNameMapping;
50
+ return this.initialModelNameMapping;
51
51
  }
52
52
 
53
53
  createPending(cls: Class): Partial<ModelOptions<ModelType>> {
54
54
  return { class: cls, indices: [], autoCreate: true, baseType: cls.ᚕabstract };
55
55
  }
56
56
 
57
- onInstallFinalize(cls: Class) {
57
+ onInstallFinalize(cls: Class): ModelOptions<ModelType> {
58
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
58
59
  const config = this.pending.get(cls.ᚕid)! as ModelOptions<ModelType>;
59
60
 
60
61
  const schema = SchemaRegistry.get(cls);
@@ -68,7 +69,7 @@ class $ModelRegistry extends MetadataRegistry<ModelOptions<ModelType>> {
68
69
  return config;
69
70
  }
70
71
 
71
- override onUninstallFinalize(cls: Class) {
72
+ override onUninstallFinalize(cls: Class): void {
72
73
  this.stores.delete(cls);
73
74
 
74
75
  // Force system to recompute on uninstall
@@ -97,7 +98,7 @@ class $ModelRegistry extends MetadataRegistry<ModelOptions<ModelType>> {
97
98
  /**
98
99
  * Find all classes by their base types
99
100
  */
100
- getAllClassesByBaseType() {
101
+ getAllClassesByBaseType(): Map<Class, Class[]> {
101
102
  if (!this.baseModelGrouped.size) {
102
103
  const out = new Map<Class, Class[]>();
103
104
  for (const el of this.entries.keys()) {
@@ -122,7 +123,7 @@ class $ModelRegistry extends MetadataRegistry<ModelOptions<ModelType>> {
122
123
  /**
123
124
  * Get all classes for a given base type
124
125
  */
125
- getClassesByBaseType(base: Class) {
126
+ getClassesByBaseType(base: Class): Class[] {
126
127
  return this.getAllClassesByBaseType().get(base) ?? [];
127
128
  }
128
129
 
@@ -162,7 +163,7 @@ class $ModelRegistry extends MetadataRegistry<ModelOptions<ModelType>> {
162
163
  * Get Index
163
164
  */
164
165
  getIndex<T extends ModelType, K extends IndexType[]>(cls: Class<T>, name: string, supportedTypes?: K): IndexConfig<T> & { type: K[number] } {
165
- const cfg = this.get(cls).indices?.find(x => x.name === name) as IndexConfig<T>;
166
+ const cfg = this.get(cls).indices?.find((x): x is IndexConfig<T> => x.name === name);
166
167
  if (!cfg) {
167
168
  throw new NotFoundError(`${cls.name} Index`, `${name}`);
168
169
  }
@@ -176,14 +177,14 @@ class $ModelRegistry extends MetadataRegistry<ModelOptions<ModelType>> {
176
177
  * Get Indices
177
178
  */
178
179
  getIndices<T extends ModelType, K extends IndexType[]>(cls: Class<T>, supportedTypes?: K): (IndexConfig<T> & { type: K[number] })[] {
179
- return (this.get(cls).indices ?? []).filter(x => !supportedTypes || supportedTypes.includes(x.type)) as IndexConfig<T>[];
180
+ return (this.get(cls).indices ?? []).filter((x): x is IndexConfig<T> => !supportedTypes || supportedTypes.includes(x.type));
180
181
  }
181
182
 
182
183
  /**
183
184
  * Get expiry field
184
185
  * @param cls
185
186
  */
186
- getExpiry(cls: Class) {
187
+ getExpiry(cls: Class): string {
187
188
  const expiry = this.get(cls).expiresAt;
188
189
  if (!expiry) {
189
190
  throw new AppError(`${cls.name} is not configured with expiry support, please use @ExpiresAt to declare expiration behavior`, 'general');
@@ -22,7 +22,7 @@ export interface ModelBasicSupport<C = unknown> {
22
22
  /**
23
23
  * Create new item
24
24
  * @param item The document to create
25
- * @throws {ExistsError} When an item with the provdided id already exists
25
+ * @throws {ExistsError} When an item with the provided id already exists
26
26
  */
27
27
  create<T extends ModelType>(cls: Class<T>, item: OptionalId<T>): Promise<T>;
28
28
 
@@ -20,11 +20,11 @@ export type BulkOp<T extends ModelType> =
20
20
  /**
21
21
  * Bulk response provides a summary of all the operations
22
22
  */
23
- export interface BulkResponse {
23
+ export interface BulkResponse<E = unknown> {
24
24
  /**
25
25
  * Errors returned
26
26
  */
27
- errors: unknown[];
27
+ errors: E[];
28
28
  /**
29
29
  * Ids that were added
30
30
  */
@@ -45,14 +45,14 @@ export interface BulkResponse {
45
45
  * Bulk processing error
46
46
  */
47
47
  export class BulkProcessError extends AppError {
48
- constructor(public errors: { idx: number, error: Error }[]) {
48
+ constructor(public errors: { idx: number, error: ValidationResultError }[]) {
49
49
  super('Bulk processing errors have occurred', 'data', { errors });
50
50
  }
51
51
 
52
52
  /**
53
53
  * Provide full results back, with validation errors
54
54
  */
55
- override toJSON(extra: Record<string, unknown> = {}) {
55
+ override toJSON(extra: Record<string, unknown> = {}): unknown {
56
56
  return {
57
57
  ...extra,
58
58
  at: new Date(),
@@ -60,7 +60,7 @@ export class BulkProcessError extends AppError {
60
60
  category: this.category,
61
61
  type: this.type,
62
62
  errors: this.errors.map(x => {
63
- const { message, type, errors, payload } = x.error as ValidationResultError;
63
+ const { message, type, errors, payload } = x.error;
64
64
  return { message, type, errors: errors ?? payload, idx: x.idx };
65
65
  })
66
66
  };
@@ -27,7 +27,7 @@ export interface ModelIndexedSupport extends ModelBasicSupport {
27
27
  deleteByIndex<T extends ModelType>(cls: Class<T>, idx: string, body: DeepPartial<T>): Promise<void>;
28
28
 
29
29
  /**
30
- * List entity by rangeable index as defined by fields of idx and the body fields
30
+ * List entity by ranged index as defined by fields of idx and the body fields
31
31
  * @param cls The type to search by
32
32
  * @param idx The index name to search against
33
33
  * @param body The payload of fields needed to search
@@ -1,3 +1,5 @@
1
+ import { Readable } from 'stream';
2
+
1
3
  export interface StreamMeta {
2
4
  /**
3
5
  * File size
@@ -30,13 +32,13 @@ export interface ModelStreamSupport {
30
32
  * @param input The actual stream to write
31
33
  * @param meta The stream metadata
32
34
  */
33
- upsertStream(location: string, input: NodeJS.ReadableStream, meta: StreamMeta): Promise<void>;
35
+ upsertStream(location: string, input: Readable, meta: StreamMeta): Promise<void>;
34
36
 
35
37
  /**
36
38
  * Get stream from asset store
37
39
  * @param location The location of the stream
38
40
  */
39
- getStream(location: string): Promise<NodeJS.ReadableStream>;
41
+ getStream(location: string): Promise<Readable>;
40
42
 
41
43
  /**
42
44
  * Get metadata for stream
@@ -10,14 +10,14 @@ type ServiceClass = { serviceClass: { new(): unknown } };
10
10
  @ModelSuite()
11
11
  export abstract class BaseModelSuite<T> {
12
12
 
13
- static ifNot(pred: (svc: unknown) => boolean) {
13
+ static ifNot(pred: (svc: unknown) => boolean): (x: unknown) => Promise<boolean> {
14
14
  return async (x: unknown) => !pred(new (x as ServiceClass).serviceClass());
15
15
  }
16
16
 
17
17
  serviceClass: Class<T>;
18
18
  configClass: Class;
19
19
 
20
- async getSize<U extends ModelType>(cls: Class<U>) {
20
+ async getSize<U extends ModelType>(cls: Class<U>): Promise<number> {
21
21
  const svc = (await this.service);
22
22
  if (isCrudSupported(svc)) {
23
23
  let i = 0;
@@ -30,7 +30,7 @@ export abstract class BaseModelSuite<T> {
30
30
  }
31
31
  }
32
32
 
33
- async saveAll<M extends ModelType>(cls: Class<M>, items: M[]) {
33
+ async saveAll<M extends ModelType>(cls: Class<M>, items: M[]): Promise<number> {
34
34
  const svc = await this.service;
35
35
  if (isBulkSupported(svc)) {
36
36
  const res = await svc.processBulk(cls, items.map(x => ({ insert: x })));
@@ -47,7 +47,15 @@ export abstract class BaseModelSuite<T> {
47
47
  }
48
48
  }
49
49
 
50
- get service() {
51
- return DependencyRegistry.getInstance(this.serviceClass) as Promise<T>;
50
+ get service(): Promise<T> {
51
+ return DependencyRegistry.getInstance(this.serviceClass);
52
+ }
53
+
54
+ async toArray<U>(src: AsyncIterable<U> | AsyncGenerator<U>): Promise<U[]> {
55
+ const out: U[] = [];
56
+ for await (const el of src) {
57
+ out.push(el);
58
+ }
59
+ return out;
52
60
  }
53
61
  }
@@ -217,7 +217,7 @@ export abstract class ModelCrudSuite extends BaseModelSuite<ModelCrudSupport> {
217
217
  people.map(el => service.upsert(Person, el))
218
218
  );
219
219
 
220
- const found = (await service.list(Person).toArray()).sort((a, b) => a.age - b.age);
220
+ const found = (await this.toArray(service.list(Person))).sort((a, b) => a.age - b.age);
221
221
 
222
222
  assert(found[0].age === people[0].age);
223
223
  assert(found[1].age === people[1].age);
@@ -123,13 +123,13 @@ export abstract class ModelIndexedSuite extends BaseModelSuite<ModelIndexedSuppo
123
123
  await service.create(User3, User3.from({ name: 'bob', age: 30, color: 'red' }));
124
124
  await service.create(User3, User3.from({ name: 'bob', age: 50, color: 'green' }));
125
125
 
126
- const arr = await service.listByIndex(User3, 'userAge', { name: 'bob' }).toArray();
126
+ const arr = await this.toArray(service.listByIndex(User3, 'userAge', { name: 'bob' }));
127
127
 
128
128
  assert(arr[0].color === 'red' && arr[0].name === 'bob');
129
129
  assert(arr[1].color === 'blue' && arr[1].name === 'bob');
130
130
  assert(arr[2].color === 'green' && arr[2].name === 'bob');
131
131
 
132
- await assert.rejects(() => service.listByIndex(User3, 'userAge', {}).toArray(), IndexNotSupported);
132
+ await assert.rejects(() => this.toArray(service.listByIndex(User3, 'userAge', {})), IndexNotSupported);
133
133
  }
134
134
 
135
135
  @Test()
@@ -140,12 +140,12 @@ export abstract class ModelIndexedSuite extends BaseModelSuite<ModelIndexedSuppo
140
140
  await service.create(User4, User4.from({ child: { name: 'bob', age: 30 }, color: 'red' }));
141
141
  await service.create(User4, User4.from({ child: { name: 'bob', age: 50 }, color: 'green' }));
142
142
 
143
- const arr = await service.listByIndex(User4, 'childAge', User4.from({ child: { name: 'bob' } })).toArray();
143
+ const arr = await this.toArray(service.listByIndex(User4, 'childAge', User4.from({ child: { name: 'bob' } })));
144
144
  assert(arr[0].color === 'red' && arr[0].child.name === 'bob' && arr[0].child.age === 30);
145
145
  assert(arr[1].color === 'blue' && arr[1].child.name === 'bob' && arr[1].child.age === 40);
146
146
  assert(arr[2].color === 'green' && arr[2].child.name === 'bob' && arr[2].child.age === 50);
147
147
 
148
- await assert.rejects(() => service.listByIndex(User4, 'childAge', {}).toArray(), IndexNotSupported);
148
+ await assert.rejects(() => this.toArray(service.listByIndex(User4, 'childAge', {})), IndexNotSupported);
149
149
  }
150
150
 
151
151
  @Test()
@@ -156,13 +156,13 @@ export abstract class ModelIndexedSuite extends BaseModelSuite<ModelIndexedSuppo
156
156
  await service.create(User4, User4.from({ child: { name: 'bob', age: 30 }, createdDate: Util.timeFromNow('2d'), color: 'red' }));
157
157
  await service.create(User4, User4.from({ child: { name: 'bob', age: 50 }, createdDate: Util.timeFromNow('-1d'), color: 'green' }));
158
158
 
159
- const arr = await service.listByIndex(User4, 'nameCreated', User4.from({ child: { name: 'bob' } })).toArray();
159
+ const arr = await this.toArray(service.listByIndex(User4, 'nameCreated', User4.from({ child: { name: 'bob' } })));
160
160
 
161
161
  assert(arr[0].color === 'green' && arr[0].child.name === 'bob' && arr[0].child.age === 50);
162
162
  assert(arr[1].color === 'red' && arr[1].child.name === 'bob' && arr[1].child.age === 30);
163
163
  assert(arr[2].color === 'blue' && arr[2].child.name === 'bob' && arr[2].child.age === 40);
164
164
 
165
- await assert.rejects(() => service.listByIndex(User4, 'nameCreated', {}).toArray(), IndexNotSupported);
165
+ await assert.rejects(() => this.toArray(service.listByIndex(User4, 'nameCreated', {})), IndexNotSupported);
166
166
  }
167
167
 
168
168
  @Test()
@@ -173,7 +173,7 @@ export abstract class ModelIndexedSuite extends BaseModelSuite<ModelIndexedSuppo
173
173
  const user2 = await service.upsertByIndex(User4, 'childAge', { child: { name: 'bob', age: 40 }, color: 'green' });
174
174
  const user3 = await service.upsertByIndex(User4, 'childAge', { child: { name: 'bob', age: 40 }, color: 'red' });
175
175
 
176
- const arr = await service.listByIndex(User4, 'childAge', { child: { name: 'bob' } }).toArray();
176
+ const arr = await this.toArray(service.listByIndex(User4, 'childAge', { child: { name: 'bob' } }));
177
177
  assert(arr.length === 1);
178
178
 
179
179
  assert(user1.id === user2.id);
@@ -182,12 +182,12 @@ export abstract class ModelIndexedSuite extends BaseModelSuite<ModelIndexedSuppo
182
182
  assert(user3.color === 'red');
183
183
 
184
184
  const user4 = await service.upsertByIndex(User4, 'childAge', { child: { name: 'bob', age: 30 }, color: 'red' });
185
- const arr2 = await service.listByIndex(User4, 'childAge', { child: { name: 'bob' } }).toArray();
185
+ const arr2 = await this.toArray(service.listByIndex(User4, 'childAge', { child: { name: 'bob' } }));
186
186
  assert(arr2.length === 2);
187
187
 
188
188
  await service.deleteByIndex(User4, 'childAge', user1);
189
189
 
190
- const arr3 = await service.listByIndex(User4, 'childAge', { child: { name: 'bob' } }).toArray();
190
+ const arr3 = await this.toArray(service.listByIndex(User4, 'childAge', { child: { name: 'bob' } }));
191
191
  assert(arr3.length === 1);
192
192
  assert(arr3[0].id === user4.id);
193
193
  }
@@ -153,19 +153,18 @@ export abstract class ModelPolymorphismSuite extends BaseModelSuite<ModelCrudSup
153
153
  await assert.rejects(
154
154
  () =>
155
155
  service.upsert(Doctor, Doctor.from({
156
- id: fire.id, name: 'drob', specialty: 'eyes'
156
+ id: fire.id, name: 'gob', specialty: 'eyes'
157
157
  })),
158
158
  e => (e instanceof SubTypeNotSupportedError || e instanceof ExistsError) ? undefined : e
159
159
  );
160
160
 
161
161
  await assert.rejects(
162
- // @ts-expect-error
163
- () => service.update(Engineer, Doctor.from({ ...doc })),
162
+ () => service.update(Engineer, Doctor.from({ ...doc }) as unknown as Engineer),
164
163
  (e: Error) => (e instanceof NotFoundError || e instanceof SubTypeNotSupportedError || e instanceof TypeMismatchError) ? undefined : e);
165
164
 
166
165
  try {
167
166
  const res = await service.upsert(Doctor, Doctor.from({
168
- id: doc.id, name: 'drob', specialty: 'eyes'
167
+ id: doc.id, name: 'gob', specialty: 'eyes'
169
168
  }));
170
169
 
171
170
  assert(res.updatedDate!.getTime() > update.getTime());
@@ -174,7 +173,7 @@ export abstract class ModelPolymorphismSuite extends BaseModelSuite<ModelCrudSup
174
173
  }
175
174
 
176
175
  const resAlt = await service.upsert(Worker, Doctor.from({
177
- id: doc.id, name: 'drob', specialty: 'eyes'
176
+ id: doc.id, name: 'gob', specialty: 'eyes'
178
177
  }));
179
178
 
180
179
  assert(resAlt.updatedDate!.getTime() > update.getTime());
@@ -225,8 +224,8 @@ export abstract class ModelPolymorphismSuite extends BaseModelSuite<ModelCrudSup
225
224
  name: 'rob'
226
225
  });
227
226
  assert(res2 instanceof IndexedFirefighter); // If service allows for get by subtype
228
- } catch (e) {
229
- assert(e instanceof SubTypeNotSupportedError || e instanceof NotFoundError); // If it does not
227
+ } catch (err) {
228
+ assert(err instanceof SubTypeNotSupportedError || err instanceof NotFoundError); // If it does not
230
229
  }
231
230
  }
232
231
 
@@ -257,8 +256,8 @@ export abstract class ModelPolymorphismSuite extends BaseModelSuite<ModelCrudSup
257
256
  age: now,
258
257
  name: 'rob'
259
258
  });
260
- } catch (e) {
261
- assert(e instanceof SubTypeNotSupportedError || e instanceof NotFoundError);
259
+ } catch (err) {
260
+ assert(err instanceof SubTypeNotSupportedError || err instanceof NotFoundError);
262
261
  }
263
262
 
264
263
  try {
@@ -266,8 +265,8 @@ export abstract class ModelPolymorphismSuite extends BaseModelSuite<ModelCrudSup
266
265
  age: now,
267
266
  name: 'bob'
268
267
  });
269
- } catch (e) {
270
- assert(e instanceof SubTypeNotSupportedError || e instanceof NotFoundError);
268
+ } catch (err) {
269
+ assert(err instanceof SubTypeNotSupportedError || err instanceof NotFoundError);
271
270
  }
272
271
  }
273
272
  }
@@ -2,6 +2,7 @@ import * as assert from 'assert';
2
2
  import * as fs from 'fs/promises';
3
3
  import { createReadStream } from 'fs';
4
4
  import * as crypto from 'crypto';
5
+ import { Readable } from 'stream';
5
6
 
6
7
  import { PathUtil } from '@travetto/boot';
7
8
  import { BeforeAll, Suite, Test } from '@travetto/test';
@@ -13,7 +14,7 @@ import { ModelStreamSupport } from '../src/service/stream';
13
14
  @Suite()
14
15
  export abstract class ModelStreamSuite extends BaseModelSuite<ModelStreamSupport> {
15
16
 
16
- async getHash(stream: NodeJS.ReadableStream) {
17
+ async getHash(stream: Readable) {
17
18
  const hash = crypto.createHash('sha1');
18
19
  hash.setEncoding('hex');
19
20
  await new Promise((res, rej) => {
@@ -11,7 +11,7 @@ import { ModelRegistry } from '../src/registry/model';
11
11
  const Loaded = Symbol();
12
12
 
13
13
  export function ModelSuite<T extends { configClass: Class<{ autoCreate?: boolean, namespace?: string }>, serviceClass: Class }>(qualifier?: symbol) {
14
- return (target: Class<T>) => {
14
+ return (target: Class<T>): void => {
15
15
  SuiteRegistry.registerPendingListener(
16
16
  target,
17
17
  async function (this: T & { [Loaded]?: boolean }) {