@travetto/model 8.0.0-alpha.8 → 8.0.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.
Files changed (42) hide show
  1. package/README.md +91 -102
  2. package/__index__.ts +7 -11
  3. package/package.json +18 -18
  4. package/src/error/exists.ts +1 -1
  5. package/src/error/invalid-index.ts +5 -3
  6. package/src/error/invalid-sub-type.ts +1 -1
  7. package/src/error/not-found.ts +1 -1
  8. package/src/error/unique.ts +13 -0
  9. package/src/registry/decorator.ts +38 -32
  10. package/src/registry/registry-adapter.ts +11 -8
  11. package/src/registry/registry-index.ts +12 -33
  12. package/src/registry/types.ts +24 -39
  13. package/src/types/basic.ts +3 -3
  14. package/src/types/blob.ts +6 -6
  15. package/src/types/bulk.ts +5 -9
  16. package/src/types/crud.ts +9 -6
  17. package/src/types/expiry.ts +2 -2
  18. package/src/types/model.ts +11 -2
  19. package/src/types/storage.ts +2 -3
  20. package/src/util/blob.ts +2 -2
  21. package/src/util/bulk.ts +6 -3
  22. package/src/util/crud.ts +105 -22
  23. package/src/util/expiry.ts +4 -5
  24. package/src/util/storage.ts +3 -4
  25. package/support/base-command.ts +3 -5
  26. package/support/bin/candidate.ts +8 -11
  27. package/support/bin/export.ts +2 -2
  28. package/support/bin/install.ts +2 -2
  29. package/support/cli.model_export.ts +9 -5
  30. package/support/cli.model_install.ts +9 -5
  31. package/support/doc.support.tsx +16 -20
  32. package/support/test/base.ts +14 -12
  33. package/support/test/basic.ts +44 -3
  34. package/support/test/blob.ts +8 -9
  35. package/support/test/bulk.ts +16 -5
  36. package/support/test/crud.ts +134 -82
  37. package/support/test/expiry.ts +39 -21
  38. package/support/test/polymorphism.ts +43 -143
  39. package/support/test/suite.ts +15 -12
  40. package/src/types/indexed.ts +0 -43
  41. package/src/util/indexed.ts +0 -144
  42. package/support/test/indexed.ts +0 -190
@@ -1,14 +1,13 @@
1
- import { ShutdownManager, type Class, type TimeSpan, TimeUtil, Util, castTo, hasFunction } from '@travetto/runtime';
1
+ import { type Class, castTo, hasFunction, ShutdownManager, type TimeSpan, TimeUtil, Util } from '@travetto/runtime';
2
2
 
3
+ import { ModelRegistryIndex } from '../registry/registry-index.ts';
3
4
  import type { ModelExpirySupport } from '../types/expiry.ts';
4
5
  import type { ModelType } from '../types/model.ts';
5
- import { ModelRegistryIndex } from '../registry/registry-index.ts';
6
6
 
7
7
  /**
8
8
  * Utils for model expiry
9
9
  */
10
10
  export class ModelExpiryUtil {
11
-
12
11
  /**
13
12
  * Type guard for determining if model supports expiry
14
13
  */
@@ -17,7 +16,7 @@ export class ModelExpiryUtil {
17
16
  /**
18
17
  * Get expiry info for a given item
19
18
  */
20
- static getExpiryState<T extends ModelType>(cls: Class<T>, item: T): { expiresAt?: Date, expired?: boolean } {
19
+ static getExpiryState<T extends ModelType>(cls: Class<T>, item: T): { expiresAt?: Date; expired?: boolean } {
21
20
  const expKey = ModelRegistryIndex.getExpiryFieldName(cls);
22
21
  const expiresAt: Date = castTo(item[expKey]);
23
22
 
@@ -45,4 +44,4 @@ export class ModelExpiryUtil {
45
44
  })();
46
45
  }
47
46
  }
48
- }
47
+ }
@@ -1,14 +1,13 @@
1
1
  import { type Class, hasFunction, Runtime } from '@travetto/runtime';
2
2
  import { SchemaRegistryIndex } from '@travetto/schema';
3
3
 
4
- import type { ModelStorageSupport } from '../types/storage.ts';
5
4
  import { ModelRegistryIndex } from '../registry/registry-index.ts';
5
+ import type { ModelStorageSupport } from '../types/storage.ts';
6
6
 
7
7
  /**
8
8
  * Model storage util
9
9
  */
10
10
  export class ModelStorageUtil {
11
-
12
11
  /**
13
12
  * Type guard for determining if service supports storage operation
14
13
  */
@@ -33,7 +32,7 @@ export class ModelStorageUtil {
33
32
  return false;
34
33
  }
35
34
 
36
- return (autoCreate === 'production' || !Runtime.production);
35
+ return autoCreate === 'production' || !Runtime.production;
37
36
  };
38
37
 
39
38
  // Initialize on startup (test manages)
@@ -47,4 +46,4 @@ export class ModelStorageUtil {
47
46
  }
48
47
  }
49
48
  }
50
- }
49
+ }
@@ -1,10 +1,9 @@
1
- import { Env } from '@travetto/runtime';
2
- import { type CliCommandShape, cliTpl, CliModuleFlag, CliProfilesFlag } from '@travetto/cli';
1
+ import { type CliCommandShape, CliModuleFlag, CliProfilesFlag, cliTpl } from '@travetto/cli';
3
2
  import { Registry } from '@travetto/registry';
3
+ import { Env } from '@travetto/runtime';
4
4
  import { Schema, type ValidationError } from '@travetto/schema';
5
5
 
6
6
  import type { ModelStorageSupport } from '../src/types/storage.ts';
7
-
8
7
  import { ModelCandidateUtil } from './bin/candidate.ts';
9
8
 
10
9
  /**
@@ -12,7 +11,6 @@ import { ModelCandidateUtil } from './bin/candidate.ts';
12
11
  */
13
12
  @Schema()
14
13
  export abstract class BaseModelCommand implements CliCommandShape {
15
-
16
14
  static async validate(operation: keyof ModelStorageSupport, provider: string, models: string[]): Promise<ValidationError | undefined> {
17
15
  const candidates = await ModelCandidateUtil.export(operation);
18
16
  if (provider && !candidates.providers.includes(provider)) {
@@ -52,4 +50,4 @@ export abstract class BaseModelCommand implements CliCommandShape {
52
50
  }
53
51
 
54
52
  abstract main(...args: unknown[]): ReturnType<CliCommandShape['main']>;
55
- }
53
+ }
@@ -1,17 +1,16 @@
1
- import { toConcrete, type Class } from '@travetto/runtime';
2
- import { type InjectableCandidate, DependencyRegistryIndex } from '@travetto/di';
1
+ import { DependencyRegistryIndex, type InjectableCandidate } from '@travetto/di';
2
+ import { type Class, toConcrete } from '@travetto/runtime';
3
3
  import { SchemaRegistryIndex } from '@travetto/schema';
4
4
 
5
- import type { ModelStorageSupport } from '../../src/types/storage.ts';
6
- import type { ModelType } from '../../src/types/model.ts';
7
5
  import { ModelRegistryIndex } from '../../src/registry/registry-index.ts';
6
+ import type { ModelType } from '../../src/types/model.ts';
7
+ import type { ModelStorageSupport } from '../../src/types/storage.ts';
8
8
 
9
9
  /**
10
10
  * Utilities for finding candidates for model operations
11
11
  */
12
12
  export class ModelCandidateUtil {
13
-
14
- static async export(operation: keyof ModelStorageSupport): Promise<{ models: string[], providers: string[] }> {
13
+ static async export(operation: keyof ModelStorageSupport): Promise<{ models: string[]; providers: string[] }> {
15
14
  return {
16
15
  models: await this.getModelNames(),
17
16
  providers: await this.getProviderNames(operation)
@@ -48,9 +47,7 @@ export class ModelCandidateUtil {
48
47
  * Get list of names of all viable providers
49
48
  */
50
49
  static async getProviderNames(operation?: keyof ModelStorageSupport): Promise<string[]> {
51
- return (await this.getProviders(operation))
52
- .map(x => x.class.name.replace(/ModelService/, ''))
53
- .toSorted();
50
+ return (await this.getProviders(operation)).map(x => x.class.name.replace(/ModelService/, '')).toSorted();
54
51
  }
55
52
 
56
53
  /**
@@ -67,10 +64,10 @@ export class ModelCandidateUtil {
67
64
  * @param models
68
65
  * @returns
69
66
  */
70
- static async resolve(provider: string, models: string[]): Promise<{ provider: ModelStorageSupport, models: Class<ModelType>[] }> {
67
+ static async resolve(provider: string, models: string[]): Promise<{ provider: ModelStorageSupport; models: Class<ModelType>[] }> {
71
68
  return {
72
69
  provider: await this.getProvider(provider),
73
70
  models: await this.#getModels(models)
74
71
  };
75
72
  }
76
- }
73
+ }
@@ -1,5 +1,5 @@
1
- import type { Class } from '@travetto/runtime';
2
1
  import type { ModelStorageSupport, ModelType } from '@travetto/model';
2
+ import type { Class } from '@travetto/runtime';
3
3
 
4
4
  export class ModelExportUtil {
5
5
  static async run(provider: ModelStorageSupport, models: Class<ModelType>[]): Promise<void> {
@@ -7,4 +7,4 @@ export class ModelExportUtil {
7
7
  console.log(await provider.exportModel!(model));
8
8
  }
9
9
  }
10
- }
10
+ }
@@ -1,5 +1,5 @@
1
- import type { Class } from '@travetto/runtime';
2
1
  import type { ModelStorageSupport, ModelType } from '@travetto/model';
2
+ import type { Class } from '@travetto/runtime';
3
3
 
4
4
  export class ModelInstallUtil {
5
5
  static async run(provider: ModelStorageSupport, models: Class<ModelType>[]): Promise<void> {
@@ -11,4 +11,4 @@ export class ModelInstallUtil {
11
11
  await provider.upsertModel(cls);
12
12
  }
13
13
  }
14
- }
14
+ }
@@ -2,20 +2,24 @@ import { CliCommand } from '@travetto/cli';
2
2
  import { MethodValidator } from '@travetto/schema';
3
3
 
4
4
  import { BaseModelCommand } from './base-command.ts';
5
- import { ModelExportUtil } from './bin/export.ts';
6
5
  import { ModelCandidateUtil } from './bin/candidate.ts';
6
+ import { ModelExportUtil } from './bin/export.ts';
7
7
 
8
8
  /**
9
- * Exports model schemas
9
+ * Export model definitions for a selected provider and model set.
10
+ *
11
+ * The command resolves candidate models and delegates to provider-specific
12
+ * export logic to produce schema/install artifacts.
10
13
  */
11
14
  @CliCommand()
12
15
  export class ModelExportCommand extends BaseModelCommand {
13
-
14
- getOperation(): 'exportModel' { return 'exportModel'; }
16
+ getOperation(): 'exportModel' {
17
+ return 'exportModel';
18
+ }
15
19
 
16
20
  @MethodValidator(BaseModelCommand.validate.bind(null, 'exportModel'))
17
21
  async main(provider: string, models: string[]): Promise<void> {
18
22
  const resolved = await ModelCandidateUtil.resolve(provider, models);
19
23
  await ModelExportUtil.run(resolved.provider, resolved.models);
20
24
  }
21
- }
25
+ }
@@ -2,16 +2,20 @@ import { CliCommand, cliTpl } from '@travetto/cli';
2
2
  import { MethodValidator } from '@travetto/schema';
3
3
 
4
4
  import { BaseModelCommand } from './base-command.ts';
5
- import { ModelInstallUtil } from './bin/install.ts';
6
5
  import { ModelCandidateUtil } from './bin/candidate.ts';
6
+ import { ModelInstallUtil } from './bin/install.ts';
7
7
 
8
8
  /**
9
- * Installing models
9
+ * Install or update model definitions for a selected provider.
10
+ *
11
+ * The command resolves candidate models and applies provider install/upsert
12
+ * operations so backing stores are prepared for runtime usage.
10
13
  */
11
14
  @CliCommand()
12
15
  export class ModelInstallCommand extends BaseModelCommand {
13
-
14
- getOperation(): 'upsertModel' { return 'upsertModel'; }
16
+ getOperation(): 'upsertModel' {
17
+ return 'upsertModel';
18
+ }
15
19
 
16
20
  @MethodValidator(BaseModelCommand.validate.bind(null, 'upsertModel'))
17
21
  async main(provider: string, models: string[]): Promise<void> {
@@ -19,4 +23,4 @@ export class ModelInstallCommand extends BaseModelCommand {
19
23
  await ModelInstallUtil.run(resolved.provider, resolved.models);
20
24
  console.log(cliTpl`${{ success: 'Successfully' }} installed ${{ param: models.length.toString() }} model(s)`);
21
25
  }
22
- }
26
+ }
@@ -1,14 +1,14 @@
1
1
  /** @jsxImportSource @travetto/doc/support */
2
- import { d, c, type DocJSXElementByFn, type DocJSXElement, DocFileUtil } from '@travetto/doc';
2
+
3
3
  import { Config } from '@travetto/config';
4
- import { Runtime, toConcrete } from '@travetto/runtime';
4
+ import { c, DocFileUtil, type DocJSXElement, type DocJSXElementByFn, d } from '@travetto/doc';
5
+ import { castKey, Runtime, toConcrete } from '@travetto/runtime';
5
6
 
6
7
  import type { ModelBasicSupport } from '../src/types/basic.ts';
7
8
  import type { ModelBlobSupport } from '../src/types/blob.ts';
8
9
  import type { ModelBulkSupport } from '../src/types/bulk.ts';
9
10
  import type { ModelCrudSupport } from '../src/types/crud.ts';
10
11
  import type { ModelExpirySupport } from '../src/types/expiry.ts';
11
- import type { ModelIndexedSupport } from '../src/types/indexed.ts';
12
12
 
13
13
  const toLink = (title: string, target: Function): DocJSXElementByFn<'CodeLink'> =>
14
14
  d.codeLink(title, Runtime.getSourceFile(target), new RegExp(`\\binterface\\s+${target.name}`));
@@ -17,9 +17,8 @@ export const Links = {
17
17
  Basic: toLink('Basic', toConcrete<ModelBasicSupport>()),
18
18
  Crud: toLink('CRUD', toConcrete<ModelCrudSupport>()),
19
19
  Expiry: toLink('Expiry', toConcrete<ModelExpirySupport>()),
20
- Indexed: toLink('Indexed', toConcrete<ModelIndexedSupport>()),
21
20
  Bulk: toLink('Bulk', toConcrete<ModelBulkSupport>()),
22
- Blob: toLink('Blob', toConcrete<ModelBlobSupport>()),
21
+ Blob: toLink('Blob', toConcrete<ModelBlobSupport>())
23
22
  };
24
23
 
25
24
  export const ModelTypes = (fn: Function): DocJSXElement[] => {
@@ -29,24 +28,21 @@ export const ModelTypes = (fn: Function): DocJSXElement[] => {
29
28
  for (const [, key] of content.matchAll(/Model([A-Za-z]+)Support/g)) {
30
29
  if (!seen.has(key) && key in Links) {
31
30
  seen.add(key);
32
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
33
- const link = Links[key as keyof typeof Links];
31
+ const link = Links[castKey(key)];
34
32
  found.push(link);
35
33
  }
36
34
  }
37
35
  return found.map(type => <li>{type}</li>);
38
36
  };
39
37
 
40
- export const ModelCustomConfig = ({ config }: { config: Function }): DocJSXElement => <>
41
- Out of the box, by installing the module, everything should be wired up by default.If you need to customize any aspect of the source
42
- or config, you can override and register it with the {d.module('Di')} module.
43
-
44
- <c.Code title='Wiring up a custom Model Source' src='doc/custom-service.ts' />
45
-
46
- where the {config} is defined by:
47
-
48
- <c.Code title={`Structure of ${config.name}`} src={config} startRe={/@Config/} />
49
-
50
- Additionally, you can see that the class is registered with the {Config} annotation, and so these values can be overridden using the
51
- standard {d.module('Config')}resolution paths.
52
- </>;
38
+ export const ModelCustomConfig = ({ config }: { config: Function }): DocJSXElement => (
39
+ <>
40
+ Out of the box, by installing the module, everything should be wired up by default.If you need to customize any aspect of the source or
41
+ config, you can override and register it with the {d.module('Di')} module.
42
+ <c.Code title="Wiring up a custom Model Source" src="doc/custom-service.ts" />
43
+ where the {config} is defined by:
44
+ <c.Code title={`Structure of ${config.name}`} src={config} startRe={/@Config/} />
45
+ Additionally, you can see that the class is registered with the {Config} annotation, and so these values can be overridden using the
46
+ standard {d.module('Config')}resolution paths.
47
+ </>
48
+ );
@@ -1,16 +1,15 @@
1
1
  import { DependencyRegistryIndex } from '@travetto/di';
2
- import { RuntimeError, castTo, type Class, classConstruct } from '@travetto/runtime';
2
+ import { type Class, castTo, classConstruct, RuntimeError } from '@travetto/runtime';
3
3
 
4
+ import type { ModelType } from '../../src/types/model.ts';
4
5
  import { ModelBulkUtil } from '../../src/util/bulk.ts';
5
6
  import { ModelCrudUtil } from '../../src/util/crud.ts';
6
- import type { ModelType } from '../../src/types/model.ts';
7
7
  import { ModelSuite } from './suite.ts';
8
8
 
9
- type ServiceClass = { serviceClass: { new(): unknown } };
9
+ type ServiceClass = { serviceClass: { new (): unknown } };
10
10
 
11
11
  @ModelSuite()
12
12
  export abstract class BaseModelSuite<T> {
13
-
14
13
  static ifNot(pred: (svc: unknown) => boolean): (x: unknown) => Promise<boolean> {
15
14
  return async (x: unknown) => !pred(classConstruct(castTo<ServiceClass>(x).serviceClass));
16
15
  }
@@ -19,11 +18,11 @@ export abstract class BaseModelSuite<T> {
19
18
  configClass: Class;
20
19
 
21
20
  async getSize<U extends ModelType>(cls: Class<U>): Promise<number> {
22
- const svc = (await this.service);
21
+ const svc = await this.service;
23
22
  if (ModelCrudUtil.isSupported(svc)) {
24
23
  let i = 0;
25
- for await (const __el of svc.list(cls)) {
26
- i += 1;
24
+ for await (const batch of svc.list(cls)) {
25
+ i += batch.length;
27
26
  }
28
27
  return i;
29
28
  } else {
@@ -34,7 +33,10 @@ export abstract class BaseModelSuite<T> {
34
33
  async saveAll<M extends ModelType>(cls: Class<M>, items: M[]): Promise<number> {
35
34
  const svc = await this.service;
36
35
  if (ModelBulkUtil.isSupported(svc)) {
37
- const result = await svc.processBulk(cls, items.map(x => ({ insert: x })));
36
+ const result = await svc.processBulk(
37
+ cls,
38
+ items.map(x => ({ insert: x }))
39
+ );
38
40
  return result.counts.insert;
39
41
  } else if (ModelCrudUtil.isSupported(svc)) {
40
42
  const out: Promise<M>[] = [];
@@ -52,11 +54,11 @@ export abstract class BaseModelSuite<T> {
52
54
  return DependencyRegistryIndex.getInstance(this.serviceClass);
53
55
  }
54
56
 
55
- async toArray<U>(src: AsyncIterable<U> | AsyncGenerator<U>): Promise<U[]> {
56
- const out: U[] = [];
57
+ async toArray<U>(src: AsyncIterable<U | U[]> | AsyncGenerator<U | U[]>): Promise<U[]> {
58
+ const out: (U | U[])[] = [];
57
59
  for await (const el of src) {
58
60
  out.push(el);
59
61
  }
60
- return out;
62
+ return castTo(out.flat());
61
63
  }
62
- }
64
+ }
@@ -1,10 +1,22 @@
1
1
  import assert from 'node:assert';
2
2
 
3
+ import { Model, type ModelCrudSupport, ModelCrudUtil, NotFoundError, TransientField } from '@travetto/model';
3
4
  import { Suite, Test } from '@travetto/test';
4
- import { type ModelCrudSupport, Model, NotFoundError } from '@travetto/model';
5
5
 
6
6
  import { BaseModelSuite } from './base.ts';
7
7
 
8
+ @Model('computed_person')
9
+ class ComputedPerson {
10
+ id: string;
11
+ name: string;
12
+ @TransientField()
13
+ get nameUpper(): string {
14
+ return this.name.toUpperCase();
15
+ }
16
+ @TransientField()
17
+ ignoredField?: string;
18
+ }
19
+
8
20
  @Model('basic_person')
9
21
  class Person {
10
22
  id: string;
@@ -15,7 +27,6 @@ class Person {
15
27
 
16
28
  @Suite()
17
29
  export abstract class ModelBasicSuite extends BaseModelSuite<ModelCrudSupport> {
18
-
19
30
  @Test('create, read, delete')
20
31
  async create() {
21
32
  const service = await this.service;
@@ -59,4 +70,34 @@ export abstract class ModelBasicSuite extends BaseModelSuite<ModelCrudSupport> {
59
70
  assert(single !== undefined);
60
71
  assert(single.age === 25);
61
72
  }
62
- }
73
+
74
+ @Test('Should not persist computed properties')
75
+ async testComputed() {
76
+ const service = await this.service;
77
+ const id = service.idSource.create();
78
+ await service.create(
79
+ ComputedPerson,
80
+ ComputedPerson.from({
81
+ id,
82
+ name: 'Bob',
83
+ ignoredField: 'secret'
84
+ })
85
+ );
86
+
87
+ const retrieved = await service.get(ComputedPerson, id);
88
+ assert(retrieved.nameUpper === 'BOB');
89
+
90
+ // Verify it wasn't saved in the database holistically:
91
+ // When we fetch the document, the database driver retrieves a raw object and maps it.
92
+ // If the database stored 'nameUpper', trying to map it would set it on the retrieved instance.
93
+ // Since nameUpper is a getter-only property on the instance, we can verify that the persistence
94
+ // preparation (prePersist) recursively stripped the getter property from the stored object.
95
+ const instance = ComputedPerson.from({ id, name: 'Bob', ignoredField: 'secret' });
96
+ assert(Object.hasOwn(instance, 'nameUpper'));
97
+ assert(instance.ignoredField === 'secret');
98
+
99
+ const prepared = await ModelCrudUtil.prePersist(ComputedPerson, instance, 'all');
100
+ assert(prepared.nameUpper === undefined);
101
+ assert(prepared.ignoredField === undefined);
102
+ }
103
+ }
@@ -1,7 +1,7 @@
1
1
  import assert from 'node:assert';
2
2
 
3
- import { Suite, Test, TestFixtures } from '@travetto/test';
4
3
  import { BinaryMetadataUtil, BinaryUtil, Util } from '@travetto/runtime';
4
+ import { Suite, Test, TestFixtures } from '@travetto/test';
5
5
 
6
6
  import { BaseModelSuite } from '@travetto/model/support/test/base.ts';
7
7
 
@@ -10,7 +10,6 @@ import { ModelBlobUtil } from '../../src/util/blob.ts';
10
10
 
11
11
  @Suite()
12
12
  export abstract class ModelBlobSuite extends BaseModelSuite<ModelBlobSupport> {
13
-
14
13
  fixture = new TestFixtures(['@travetto/model']);
15
14
 
16
15
  @Test()
@@ -90,7 +89,7 @@ export abstract class ModelBlobSuite extends BaseModelSuite<ModelBlobSupport> {
90
89
  const partialMeta = BinaryMetadataUtil.read(partial)!;
91
90
  const subContent = await partial.text();
92
91
  const range = BinaryMetadataUtil.enforceRange({ start: 10, end: 20 }, partialMeta);
93
- assert(subContent.length === (range.end - range.start) + 1);
92
+ assert(subContent.length === range.end - range.start + 1);
94
93
 
95
94
  const og = await this.fixture.readUTF8('/text.txt');
96
95
 
@@ -100,7 +99,7 @@ export abstract class ModelBlobSuite extends BaseModelSuite<ModelBlobSupport> {
100
99
  const partialUnboundedMeta = BinaryMetadataUtil.read(partialUnbounded)!;
101
100
  const subContent2 = await partialUnbounded.text();
102
101
  const range2 = BinaryMetadataUtil.enforceRange({ start: 10 }, partialUnboundedMeta);
103
- assert(subContent2.length === (range2.end - range2.start) + 1);
102
+ assert(subContent2.length === range2.end - range2.start + 1);
104
103
  assert(subContent2.startsWith('klm'));
105
104
  assert(subContent2.endsWith('xyz'));
106
105
 
@@ -156,19 +155,19 @@ export abstract class ModelBlobSuite extends BaseModelSuite<ModelBlobSupport> {
156
155
  const service = await this.service;
157
156
 
158
157
  const bytes = BinaryUtil.binaryArrayToBuffer(BinaryUtil.makeBinaryArray(1.5 * 10000));
159
- for (let i = 0; i < bytes.byteLength; i++) {
158
+ for (let i = 0; i < bytes.byteLength; i += 1) {
160
159
  bytes.writeUInt8(Math.trunc(Math.random() * 255), i);
161
160
  }
162
161
 
163
162
  const writable = await service.getBlobWriteUrl!('largeFile/one', {
164
- contentType: 'image/jpeg',
163
+ contentType: 'image/jpeg'
165
164
  });
166
165
 
167
166
  assert(writable);
168
167
 
169
168
  const response = await fetch(writable, {
170
169
  method: 'PUT',
171
- body: new File([bytes], 'gary', { type: 'image/jpeg' }),
170
+ body: new File([bytes], 'gary', { type: 'image/jpeg' })
172
171
  });
173
172
 
174
173
  console.error(await response.text());
@@ -179,7 +178,7 @@ export abstract class ModelBlobSuite extends BaseModelSuite<ModelBlobSupport> {
179
178
  contentType: 'image/jpeg',
180
179
  title: 'orange',
181
180
  filename: 'gary',
182
- size: bytes.byteLength,
181
+ size: bytes.byteLength
183
182
  });
184
183
 
185
184
  const found = await service.getBlob('largeFile/one');
@@ -189,4 +188,4 @@ export abstract class ModelBlobSuite extends BaseModelSuite<ModelBlobSupport> {
189
188
  assert(foundMeta.title === 'orange');
190
189
  assert(foundMeta.filename === 'gary');
191
190
  }
192
- }
191
+ }
@@ -14,7 +14,6 @@ class User {
14
14
 
15
15
  @Suite()
16
16
  export abstract class ModelBulkSuite extends BaseModelSuite<ModelBulkSupport> {
17
-
18
17
  @Test()
19
18
  async bulkInsert() {
20
19
  const service = await this.service;
@@ -48,11 +47,17 @@ export abstract class ModelBulkSuite extends BaseModelSuite<ModelBulkSupport> {
48
47
  const service = await this.service;
49
48
  const users = [0, 1, 2, 4].map(x => User.from({ name: `name-${x}`, id: service.idSource.create() }));
50
49
 
51
- const result = await service.processBulk(User, users.map(u => ({ insert: u })));
50
+ const result = await service.processBulk(
51
+ User,
52
+ users.map(u => ({ insert: u }))
53
+ );
52
54
  assert(result.counts.insert === 4);
53
55
  assert(result.insertedIds.size === 4);
54
56
 
55
- const res2 = await service.processBulk(User, users.map(u => ({ update: u })));
57
+ const res2 = await service.processBulk(
58
+ User,
59
+ users.map(u => ({ update: u }))
60
+ );
56
61
  assert(res2.counts.update === 4);
57
62
  assert(res2.insertedIds.size === 0);
58
63
  }
@@ -62,11 +67,17 @@ export abstract class ModelBulkSuite extends BaseModelSuite<ModelBulkSupport> {
62
67
  const service = await this.service;
63
68
  const users = [0, 1, 2, 4].map(x => User.from({ name: `name-${x}`, id: service.idSource.create() }));
64
69
 
65
- const result = await service.processBulk(User, users.map(u => ({ insert: u })));
70
+ const result = await service.processBulk(
71
+ User,
72
+ users.map(u => ({ insert: u }))
73
+ );
66
74
  assert(result.counts.insert === 4);
67
75
  assert(result.insertedIds.size === 4);
68
76
 
69
- const res2 = await service.processBulk(User, users.map(u => ({ delete: u })));
77
+ const res2 = await service.processBulk(
78
+ User,
79
+ users.map(u => ({ delete: u }))
80
+ );
70
81
  assert(res2.counts.delete === 4);
71
82
  }
72
83
  }