@travetto/model 3.0.0-rc.0 → 3.0.0-rc.10

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 (37) hide show
  1. package/README.md +19 -15
  2. package/{index.ts → __index__.ts} +0 -0
  3. package/package.json +16 -11
  4. package/src/internal/service/crud.ts +5 -5
  5. package/src/internal/service/expiry.ts +6 -7
  6. package/src/internal/service/indexed.ts +1 -1
  7. package/src/internal/service/storage.ts +2 -3
  8. package/src/internal/util.ts +7 -0
  9. package/src/provider/file.ts +25 -26
  10. package/src/provider/memory.ts +11 -11
  11. package/src/registry/decorator.ts +2 -2
  12. package/src/registry/model.ts +5 -4
  13. package/src/registry/types.ts +1 -2
  14. package/src/service/bulk.ts +1 -6
  15. package/{bin/lib → support/bin}/candidate.ts +8 -13
  16. package/{bin/lib → support/bin}/export.ts +0 -0
  17. package/{bin/lib → support/bin}/install.ts +3 -3
  18. package/support/cli.base-command.ts +54 -0
  19. package/{bin/cli-model_export.ts → support/cli.model_export.ts} +12 -3
  20. package/support/cli.model_install.ts +31 -0
  21. package/support/doc.support.ts +46 -0
  22. package/{test-support/resources → support/fixtures}/asset.yml +0 -0
  23. package/{test-support/resources → support/fixtures}/google +0 -0
  24. package/{test-support/resources → support/fixtures}/google.png +0 -0
  25. package/support/main.candidate.ts +16 -0
  26. package/{test-support → support/test}/base.ts +2 -2
  27. package/{test-support → support/test}/basic.ts +2 -2
  28. package/{test-support → support/test}/bulk.ts +3 -3
  29. package/{test-support → support/test}/crud.ts +2 -2
  30. package/{test-support → support/test}/expiry.ts +8 -8
  31. package/{test-support → support/test}/indexed.ts +10 -9
  32. package/{test-support → support/test}/polymorphism.ts +10 -6
  33. package/{test-support → support/test}/stream.ts +15 -23
  34. package/{test-support → support/test}/suite.ts +8 -9
  35. package/bin/candidate.ts +0 -23
  36. package/bin/cli-model_install.ts +0 -23
  37. package/bin/lib/base-command.ts +0 -67
package/README.md CHANGED
@@ -1,5 +1,5 @@
1
1
  <!-- This file was generated by @travetto/doc and should not be modified directly -->
2
- <!-- Please modify https://github.com/travetto/travetto/tree/main/module/model/doc.ts and execute "npx trv doc" to rebuild -->
2
+ <!-- Please modify https://github.com/travetto/travetto/tree/main/module/model/DOC.ts and execute "npx trv doc" to rebuild -->
3
3
  # Data Modeling Support
4
4
  ## Datastore abstraction for core operations.
5
5
 
@@ -8,11 +8,11 @@
8
8
  npm install @travetto/model
9
9
  ```
10
10
 
11
- This module provides a set of contracts/interfaces to data model persistence, modification and retrieval. This module builds heavily upon the [Schema](https://github.com/travetto/travetto/tree/main/module/schema#readme "Data type registry for runtime validation, reflection and binding. "), which is used for data model validation.
11
+ This module provides a set of contracts/interfaces to data model persistence, modification and retrieval. This module builds heavily upon the [Schema](https://github.com/travetto/travetto/tree/main/module/schema#readme "Data type registry for runtime validation, reflection and binding."), which is used for data model validation.
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#L3) 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#L19).
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.
@@ -184,7 +184,7 @@ export interface ModelStreamSupport {
184
184
  }
185
185
  ```
186
186
 
187
- ### [Bulk](https://github.com/travetto/travetto/tree/main/module/model/src/service/bulk.ts#L23)
187
+ ### [Bulk](https://github.com/travetto/travetto/tree/main/module/model/src/service/bulk.ts#L19)
188
188
 
189
189
  Finally, there is support for bulk operations. This is not to simply imply issuing many commands at in parallel, but implementation support for an atomic/bulk operation. This should allow for higher throughput on data ingest, and potentially for atomic support on transactions.
190
190
 
@@ -222,7 +222,7 @@ export interface ModelType {
222
222
  }
223
223
  ```
224
224
 
225
- All fields are optional, but the `id` and `type` are important as those field types are unable to be changed. This may make using existing data models impossible if types other than strings are required. Additionally, the type field, is intended to record the base model type and cannot be remapped. This is important to support polymorphism, not only in [Data Modeling Support](https://github.com/travetto/travetto/tree/main/module/model#readme "Datastore abstraction for core operations."), but also in [Schema](https://github.com/travetto/travetto/tree/main/module/schema#readme "Data type registry for runtime validation, reflection and binding. ").
225
+ All fields are optional, but the `id` and `type` are important as those field types are unable to be changed. This may make using existing data models impossible if types other than strings are required. Additionally, the type field, is intended to record the base model type and cannot be remapped. This is important to support polymorphism, not only in [Data Modeling Support](https://github.com/travetto/travetto/tree/main/module/model#readme "Datastore abstraction for core operations."), but also in [Schema](https://github.com/travetto/travetto/tree/main/module/schema#readme "Data type registry for runtime validation, reflection and binding.").
226
226
 
227
227
  ## Implementations
228
228
 
@@ -236,7 +236,7 @@ All fields are optional, but the `id` and `type` are important as those field ty
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
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|
239
+ |[FileModelService](https://github.com/travetto/travetto/tree/main/module/model/src/provider/file.ts#L51)|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
@@ -245,8 +245,7 @@ repetitive functionality, that is unable to be shared due to not relying upon in
245
245
  **Code: Memory Service**
246
246
  ```typescript
247
247
  import { Readable } from 'stream';
248
- import { StreamUtil } from '@travetto/boot';
249
- import { Util, Class, TimeSpan } from '@travetto/base';
248
+ import { StreamUtil, Class, TimeSpan } from '@travetto/base';
250
249
  import { DeepPartial } from '@travetto/schema';
251
250
  import { Injectable } from '@travetto/di';
252
251
  import { Config } from '@travetto/config';
@@ -265,6 +264,7 @@ import { ModelIndexedUtil } from '../internal/service/indexed';
265
264
  import { ModelStorageUtil } from '../internal/service/storage';
266
265
  import { StreamModel, STREAMS } from '../internal/service/stream';
267
266
  import { IndexConfig } from '../registry/types';
267
+ import { ModelUtil } from '../internal/util';
268
268
  const STREAM_META = `${STREAMS}_meta`;
269
269
  type StoreType = Map<string, Buffer>;
270
270
  @Config('model.memory')
@@ -274,7 +274,7 @@ export class MemoryModelConfig {
274
274
  cullRate?: number | TimeSpan;
275
275
  }
276
276
  function indexName<T extends ModelType>(cls: Class<T>, idx: IndexConfig<T> | string, suffix?: string): string {
277
- return [cls.ᚕid, typeof idx === 'string' ? idx : idx.name, suffix].filter(x => !!x).join(':');
277
+ return [cls.Ⲑid, typeof idx === 'string' ? idx : idx.name, suffix].filter(x => !!x).join(':');
278
278
  }
279
279
  function getFirstId(data: Map<string, unknown> | Set<string>, value?: string | number): string | undefined {
280
280
  let id: string | undefined;
@@ -331,12 +331,12 @@ To enforce that these contracts are honored, the module provides shared test sui
331
331
  import { Suite } from '@travetto/test';
332
332
 
333
333
  import { MemoryModelConfig, MemoryModelService } from '../src/provider/memory';
334
- import { ModelCrudSuite } from '../test-support/crud';
335
- import { ModelExpirySuite } from '../test-support/expiry';
336
- import { ModelStreamSuite } from '../test-support/stream';
337
- import { ModelIndexedSuite } from '../test-support/indexed';
338
- import { ModelBasicSuite } from '../test-support/basic';
339
- import { ModelPolymorphismSuite } from '../test-support/polymorphism';
334
+ import { ModelCrudSuite } from '../support/test/crud';
335
+ import { ModelExpirySuite } from '../support/test/expiry';
336
+ import { ModelStreamSuite } from '../support/test/stream';
337
+ import { ModelIndexedSuite } from '../support/test/indexed';
338
+ import { ModelBasicSuite } from '../support/test/basic';
339
+ import { ModelPolymorphismSuite } from '../support/test/polymorphism';
340
340
 
341
341
  @Suite()
342
342
  export class MemoryBasicSuite extends ModelBasicSuite {
@@ -388,6 +388,8 @@ Usage: model:export [options] [provider] [models...]
388
388
  Options:
389
389
  -e, --env <env> Application environment
390
390
  -h, --help display help for command
391
+
392
+ 
391
393
  ```
392
394
 
393
395
  ## CLI - model:install
@@ -403,4 +405,6 @@ Usage: model:install [options] [provider] [models...]
403
405
  Options:
404
406
  -e, --env <env> Application environment
405
407
  -h, --help display help for command
408
+
409
+ 
406
410
  ```
File without changes
package/package.json CHANGED
@@ -1,7 +1,6 @@
1
1
  {
2
2
  "name": "@travetto/model",
3
- "displayName": "Data Modeling Support",
4
- "version": "3.0.0-rc.0",
3
+ "version": "3.0.0-rc.10",
5
4
  "description": "Datastore abstraction for core operations.",
6
5
  "keywords": [
7
6
  "datastore",
@@ -17,30 +16,36 @@
17
16
  "name": "Travetto Framework"
18
17
  },
19
18
  "files": [
20
- "index.ts",
21
- "bin",
19
+ "__index__.ts",
22
20
  "src",
23
- "test-support"
21
+ "support"
24
22
  ],
25
- "main": "index.ts",
23
+ "main": "__index__.ts",
26
24
  "repository": {
27
25
  "url": "https://github.com/travetto/travetto.git",
28
26
  "directory": "module/model"
29
27
  },
30
28
  "dependencies": {
31
- "@travetto/di": "^3.0.0-rc.0",
32
- "@travetto/config": "^3.0.0-rc.0",
33
- "@travetto/registry": "^3.0.0-rc.0",
34
- "@travetto/schema": "^3.0.0-rc.0"
29
+ "@travetto/config": "^3.0.0-rc.10",
30
+ "@travetto/di": "^3.0.0-rc.10",
31
+ "@travetto/registry": "^3.0.0-rc.10",
32
+ "@travetto/schema": "^3.0.0-rc.10"
35
33
  },
36
34
  "peerDependencies": {
37
- "@travetto/cli": "^3.0.0-rc.0"
35
+ "@travetto/cli": "^3.0.0-rc.8",
36
+ "@travetto/test": "^3.0.0-rc.11"
38
37
  },
39
38
  "peerDependenciesMeta": {
40
39
  "@travetto/cli": {
41
40
  "optional": true
41
+ },
42
+ "@travetto/test": {
43
+ "optional": true
42
44
  }
43
45
  },
46
+ "travetto": {
47
+ "displayName": "Data Modeling Support"
48
+ },
44
49
  "publishConfig": {
45
50
  "access": "public"
46
51
  }
@@ -1,6 +1,6 @@
1
- import * as crypto from 'crypto';
1
+ import crypto from 'crypto';
2
2
 
3
- import { Class, Util } from '@travetto/base';
3
+ import { Class, ObjectUtil } from '@travetto/base';
4
4
  import { SchemaRegistry, SchemaValidator } from '@travetto/schema';
5
5
 
6
6
  import { ModelRegistry } from '../../registry/model';
@@ -41,7 +41,7 @@ export class ModelCrudUtil {
41
41
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
42
42
  const result = ModelRegistry.getBaseModel(cls).from(input as object) as T;
43
43
 
44
- if (!(result instanceof cls || result.constructor.ᚕid === cls.ᚕid)) {
44
+ if (!(result instanceof cls || result.constructor.Ⲑid === cls.Ⲑid)) {
45
45
  if (onTypeMismatch === 'notfound') {
46
46
  throw new NotFoundError(cls, result.id);
47
47
  } else {
@@ -66,7 +66,7 @@ export class ModelCrudUtil {
66
66
  item.id = idSource.uuid();
67
67
  }
68
68
 
69
- if (Util.isPlainObject(item)) {
69
+ if (ObjectUtil.isPlainObject(item)) {
70
70
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
71
71
  item = cls.from(item as object);
72
72
  }
@@ -95,7 +95,7 @@ export class ModelCrudUtil {
95
95
  * @param getExisting How to fetch an existing item
96
96
  */
97
97
  static async naivePartialUpdate<T extends ModelType>(cls: Class<T>, item: Partial<T>, view: undefined | string, getExisting: () => Promise<T>): Promise<T> {
98
- if (Util.isPlainObject(item)) {
98
+ if (ObjectUtil.isPlainObject(item)) {
99
99
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
100
100
  item = cls.from(item as object);
101
101
  }
@@ -1,4 +1,4 @@
1
- import { Class, ShutdownManager, Util, TimeSpan } from '@travetto/base';
1
+ import { ShutdownManager, Class, TimeSpan, TimeUtil } from '@travetto/base';
2
2
 
3
3
  import { ModelRegistry } from '../../registry/model';
4
4
  import { ModelExpirySupport } from '../../service/expiry';
@@ -33,19 +33,18 @@ export class ModelExpiryUtil {
33
33
  const cullable = ModelRegistry.getClasses().filter(cls => !!ModelRegistry.get(cls).expiresAt);
34
34
  if (svc.deleteExpired && cullable.length) {
35
35
  let running = true;
36
- const cullInterval = Util.timeToMs(svc.config?.cullRate ?? '10m');
36
+ const cullInterval = TimeUtil.timeToMs(svc.config?.cullRate ?? '10m');
37
37
 
38
- ShutdownManager.onShutdown({
38
+ ShutdownManager.onShutdown(this, {
39
39
  close(cb) {
40
40
  running = false;
41
41
  cb?.();
42
- },
43
- name: 'expiry-culling'
42
+ }
44
43
  });
45
44
  (async (): Promise<void> => {
46
- await Util.wait('1s'); // Wait a second to start culling
45
+ await TimeUtil.wait('1s'); // Wait a second to start culling
47
46
  while (running) {
48
- await Util.wait(cullInterval);
47
+ await TimeUtil.wait(cullInterval);
49
48
  await Promise.all(cullable.map(cls => svc.deleteExpired(cls)));
50
49
  }
51
50
  })();
@@ -118,7 +118,7 @@ export class ModelIndexedUtil {
118
118
  opts?: ComputeConfig & { sep?: string }
119
119
  ): { type: string, key: string, sort?: number | Date } {
120
120
  const { fields, sorted } = this.computeIndexParts(cls, idx, item, { ...(opts ?? {}), includeSortInFields: false });
121
- const key = fields.map(({ value }) => value).map(x => `${x}`).join(opts?.sep ?? '');
121
+ const key = fields.map(({ value }) => value).map(x => `${x}`).join(opts?.sep ?? '');
122
122
  const cfg = typeof idx === 'string' ? ModelRegistry.getIndex(cls, idx) : idx;
123
123
  return !sorted ? { type: cfg.type, key } : { type: cfg.type, key, sort: sorted.value };
124
124
  }
@@ -1,5 +1,4 @@
1
- import { EnvUtil } from '@travetto/boot';
2
- import { AppManifest, Class } from '@travetto/base';
1
+ import { Class, GlobalEnv } from '@travetto/base';
3
2
  import { SchemaChangeListener } from '@travetto/schema';
4
3
 
5
4
  import { ModelRegistry } from '../../registry/model';
@@ -14,7 +13,7 @@ export class ModelStorageUtil {
14
13
  * Register change listener on startup
15
14
  */
16
15
  static async registerModelChangeListener(storage: ModelStorageSupport, target?: Class): Promise<void> {
17
- if (!EnvUtil.isDynamic() || !(storage?.config?.autoCreate ?? !AppManifest.prod)) {
16
+ if (!GlobalEnv.dynamic || !(storage?.config?.autoCreate ?? !GlobalEnv.prod)) {
18
17
  return;
19
18
  }
20
19
 
@@ -0,0 +1,7 @@
1
+ import { Util } from '@travetto/base';
2
+
3
+ export class ModelUtil {
4
+ static uuid(len: number = 32): string {
5
+ return Util.uuid(len);
6
+ }
7
+ }
@@ -1,11 +1,12 @@
1
- import * as fs from 'fs/promises';
1
+ import fs from 'fs/promises';
2
2
  import { createReadStream } from 'fs';
3
- import * as os from 'os';
4
- import * as path from 'path';
3
+
4
+ import os from 'os';
5
+
5
6
  import { Readable } from 'stream';
6
7
 
7
- import { FsUtil, PathUtil, StreamUtil } from '@travetto/boot';
8
- import { Class, Util, TimeSpan } from '@travetto/base';
8
+ import { path, RootIndex } from '@travetto/manifest';
9
+ import { StreamUtil, Class, TimeSpan } from '@travetto/base';
9
10
  import { Injectable } from '@travetto/di';
10
11
  import { Config } from '@travetto/config';
11
12
  import { Required } from '@travetto/schema';
@@ -21,6 +22,7 @@ import { ModelExpiryUtil } from '../internal/service/expiry';
21
22
  import { NotFoundError } from '../error/not-found';
22
23
  import { ExistsError } from '../error/exists';
23
24
  import { StreamModel, STREAMS } from '../internal/service/stream';
25
+ import { ModelUtil } from '../internal/util';
24
26
 
25
27
  type Suffix = '.bin' | '.meta' | '.json' | '.expires';
26
28
 
@@ -36,12 +38,12 @@ export class FileModelConfig {
36
38
  cullRate?: number | TimeSpan;
37
39
 
38
40
  async postConstruct(): Promise<void> {
39
- if (!this.folder) {
40
- this.folder = PathUtil.resolveUnix(os.tmpdir(), Util.uuid().substring(0, 10));
41
- }
41
+ this.folder ??= path.resolve(os.tmpdir(), `trv_file_${RootIndex.mainModule.name.replace(/[^a-z]/ig, '_')}`);
42
42
  }
43
43
  }
44
44
 
45
+ const exists = (f: string): Promise<boolean> => fs.stat(f).then(() => true, () => false);
46
+
45
47
  /**
46
48
  * Standard file support
47
49
  */
@@ -50,9 +52,9 @@ export class FileModelService implements ModelCrudSupport, ModelStreamSupport, M
50
52
 
51
53
  private static async * scanFolder(folder: string, suffix: string): AsyncGenerator<[id: string, field: string]> {
52
54
  for (const sub of await fs.readdir(folder)) {
53
- for (const file of await fs.readdir(PathUtil.resolveUnix(folder, sub))) {
55
+ for (const file of await fs.readdir(path.resolve(folder, sub))) {
54
56
  if (file.endsWith(suffix)) {
55
- yield [file.replace(suffix, ''), PathUtil.resolveUnix(folder, sub, file)];
57
+ yield [file.replace(suffix, ''), path.resolve(folder, sub, file)];
56
58
  }
57
59
  }
58
60
  }
@@ -64,31 +66,28 @@ export class FileModelService implements ModelCrudSupport, ModelStreamSupport, M
64
66
 
65
67
  /**
66
68
  * The root location for all activity
67
- *
68
- * @param folder
69
69
  */
70
70
  constructor(public readonly config: FileModelConfig) { }
71
71
 
72
72
  async #resolveName<T extends ModelType>(cls: Class<T> | string, suffix?: Suffix, id?: string): Promise<string> {
73
73
  const name = typeof cls === 'string' ? cls : ModelRegistry.getStore(cls);
74
- let resolved = PathUtil.resolveUnix(this.config.folder, this.config.namespace, name);
74
+ let resolved = path.resolve(this.config.folder, this.config.namespace, name);
75
75
  if (id) {
76
- resolved = PathUtil.resolveUnix(resolved, id.replace(/^[/]/, '').substring(0, 3));
76
+ resolved = path.resolve(resolved, id.replace(/^[/]/, '').substring(0, 3));
77
77
  }
78
78
  let dir = resolved;
79
79
  if (id) {
80
- resolved = PathUtil.resolveUnix(resolved, `${id}${suffix}`);
80
+ resolved = path.resolve(resolved, `${id}${suffix}`);
81
81
  dir = path.dirname(resolved);
82
82
  }
83
- if (!await FsUtil.exists(dir)) {
84
- await fs.mkdir(dir, { recursive: true });
85
- }
83
+
84
+ await fs.mkdir(dir, { recursive: true });
86
85
  return resolved;
87
86
  }
88
87
 
89
88
  async #find<T extends ModelType>(cls: Class<T> | string, suffix: Suffix, id?: string): Promise<string> {
90
89
  const file = await this.#resolveName(cls, suffix, id);
91
- if (id && !(await FsUtil.exists(file))) {
90
+ if (id && !(await exists(file))) {
92
91
  throw new NotFoundError(cls, id);
93
92
  }
94
93
  return file;
@@ -107,7 +106,7 @@ export class FileModelService implements ModelCrudSupport, ModelStreamSupport, M
107
106
  }
108
107
 
109
108
  uuid(): string {
110
- return Util.uuid(32);
109
+ return ModelUtil.uuid(32);
111
110
  }
112
111
 
113
112
  async get<T extends ModelType>(cls: Class<T>, id: string): Promise<T> {
@@ -115,7 +114,7 @@ export class FileModelService implements ModelCrudSupport, ModelStreamSupport, M
115
114
 
116
115
  const file = await this.#resolveName(cls, '.json', id);
117
116
 
118
- if (await FsUtil.exists(file)) {
117
+ if (await exists(file)) {
119
118
  const content = await StreamUtil.streamToBuffer(createReadStream(file));
120
119
  return this.checkExpiry(cls, await ModelCrudUtil.load(cls, content));
121
120
  }
@@ -130,7 +129,7 @@ export class FileModelService implements ModelCrudSupport, ModelStreamSupport, M
130
129
 
131
130
  const file = await this.#resolveName(cls, '.json', item.id);
132
131
 
133
- if (await FsUtil.exists(file)) {
132
+ if (await exists(file)) {
134
133
  throw new ExistsError(cls, item.id!);
135
134
  }
136
135
 
@@ -203,7 +202,7 @@ export class FileModelService implements ModelCrudSupport, ModelStreamSupport, M
203
202
 
204
203
  async deleteStream(location: string): Promise<void> {
205
204
  const file = await this.#resolveName(STREAMS, BIN, location);
206
- if (await FsUtil.exists(file)) {
205
+ if (await exists(file)) {
207
206
  await Promise.all([
208
207
  fs.unlink(file),
209
208
  fs.unlink(file.replace('.bin', META))
@@ -226,15 +225,15 @@ export class FileModelService implements ModelCrudSupport, ModelStreamSupport, M
226
225
 
227
226
  // Storage management
228
227
  async createStorage(): Promise<void> {
229
- const dir = PathUtil.resolveUnix(this.config.folder, this.config.namespace);
228
+ const dir = path.resolve(this.config.folder, this.config.namespace);
230
229
  await fs.mkdir(dir, { recursive: true });
231
230
  }
232
231
 
233
232
  async deleteStorage(): Promise<void> {
234
- await FsUtil.unlinkRecursive(PathUtil.resolveUnix(this.config.folder, this.config.namespace));
233
+ await fs.rm(path.resolve(this.config.folder, this.config.namespace), { recursive: true, force: true });
235
234
  }
236
235
 
237
236
  async truncateModel(cls: Class<ModelType>): Promise<void> {
238
- await FsUtil.unlinkRecursive(await this.#resolveName(cls === StreamModel ? STREAMS : cls));
237
+ await fs.rm(await this.#resolveName(cls === StreamModel ? STREAMS : cls), { recursive: true, force: true });
239
238
  }
240
239
  }
@@ -1,7 +1,6 @@
1
1
  import { Readable } from 'stream';
2
2
 
3
- import { StreamUtil } from '@travetto/boot';
4
- import { Util, Class, TimeSpan } from '@travetto/base';
3
+ import { StreamUtil, Class, TimeSpan } from '@travetto/base';
5
4
  import { DeepPartial } from '@travetto/schema';
6
5
  import { Injectable } from '@travetto/di';
7
6
  import { Config } from '@travetto/config';
@@ -21,6 +20,7 @@ import { ModelIndexedUtil } from '../internal/service/indexed';
21
20
  import { ModelStorageUtil } from '../internal/service/storage';
22
21
  import { StreamModel, STREAMS } from '../internal/service/stream';
23
22
  import { IndexConfig } from '../registry/types';
23
+ import { ModelUtil } from '../internal/util';
24
24
 
25
25
  const STREAM_META = `${STREAMS}_meta`;
26
26
 
@@ -34,7 +34,7 @@ export class MemoryModelConfig {
34
34
  }
35
35
 
36
36
  function indexName<T extends ModelType>(cls: Class<T>, idx: IndexConfig<T> | string, suffix?: string): string {
37
- return [cls.ᚕid, typeof idx === 'string' ? idx : idx.name, suffix].filter(x => !!x).join(':');
37
+ return [cls.Ⲑid, typeof idx === 'string' ? idx : idx.name, suffix].filter(x => !!x).join(':');
38
38
  }
39
39
 
40
40
  function getFirstId(data: Map<string, unknown> | Set<string>, value?: string | number): string | undefined {
@@ -119,9 +119,9 @@ export class MemoryModelService implements ModelCrudSupport, ModelStreamSupport,
119
119
  }
120
120
  }
121
121
 
122
- async #write<T extends ModelType>(cls: Class<T>, item: T, action: 'remove'): Promise<void>;
123
- async #write<T extends ModelType>(cls: Class<T>, item: T, action: 'write'): Promise<T>;
124
- async #write<T extends ModelType>(cls: Class<T>, item: T, action: 'write' | 'remove'): Promise<T | void> {
122
+ async #persist<T extends ModelType>(cls: Class<T>, item: T, action: 'remove'): Promise<void>;
123
+ async #persist<T extends ModelType>(cls: Class<T>, item: T, action: 'write'): Promise<T>;
124
+ async #persist<T extends ModelType>(cls: Class<T>, item: T, action: 'write' | 'remove'): Promise<T | void> {
125
125
  const store = this.#getStore(cls);
126
126
  await this.#removeIndices(cls, item.id);
127
127
  if (action === 'write') {
@@ -159,7 +159,7 @@ export class MemoryModelService implements ModelCrudSupport, ModelStreamSupport,
159
159
  for (const idx of ModelRegistry.get(el).indices ?? []) {
160
160
  switch (idx.type) {
161
161
  case 'unique': {
162
- console.error('Unique indices are not supported for', { cls: el.ᚕid, idx: idx.name });
162
+ console.error('Unique indices are not supported for', { cls: el.Ⲑid, idx: idx.name });
163
163
  break;
164
164
  }
165
165
  }
@@ -169,7 +169,7 @@ export class MemoryModelService implements ModelCrudSupport, ModelStreamSupport,
169
169
 
170
170
  // CRUD Support
171
171
  uuid(): string {
172
- return Util.uuid(32);
172
+ return ModelUtil.uuid(32);
173
173
  }
174
174
 
175
175
  async get<T extends ModelType>(cls: Class<T>, id: string): Promise<T> {
@@ -208,13 +208,13 @@ export class MemoryModelService implements ModelCrudSupport, ModelStreamSupport,
208
208
  await ModelCrudUtil.load(cls, store.get(item.id)!, 'exists');
209
209
  }
210
210
  const prepped = await ModelCrudUtil.preStore(cls, item, this);
211
- return await this.#write(cls, prepped, 'write');
211
+ return await this.#persist(cls, prepped, 'write');
212
212
  }
213
213
 
214
214
  async updatePartial<T extends ModelType>(cls: Class<T>, item: Partial<T> & { id: string }, view?: string): Promise<T> {
215
215
  const id = item.id;
216
216
  const clean = await ModelCrudUtil.naivePartialUpdate(cls, item, view, () => this.get(cls, id));
217
- return await this.#write(cls, clean, 'write');
217
+ return await this.#persist(cls, clean, 'write');
218
218
  }
219
219
 
220
220
  async delete<T extends ModelType>(cls: Class<T>, id: string): Promise<void> {
@@ -224,7 +224,7 @@ export class MemoryModelService implements ModelCrudSupport, ModelStreamSupport,
224
224
  }
225
225
  await ModelCrudUtil.load(cls, store.get(id)!);
226
226
  const where: ModelType = { id };
227
- await this.#write(cls, where, 'remove');
227
+ await this.#persist(cls, where, 'remove');
228
228
  }
229
229
 
230
230
  async * list<T extends ModelType>(cls: Class<T>): AsyncIterable<T> {
@@ -7,7 +7,7 @@ import { IndexConfig, ModelOptions } from './types';
7
7
  /**
8
8
  * Model decorator, extends `@Schema`
9
9
  *
10
- * @augments `@trv:schema/Schema`
10
+ * @augments `@travetto/schema:Schema`
11
11
  */
12
12
  export function Model(conf: Partial<ModelOptions<ModelType>> | string = {}) {
13
13
  return function <T extends ModelType, U extends Class<T>>(target: U): U {
@@ -31,7 +31,7 @@ export function Index<T>(...indices: IndexConfig<any>[]) {
31
31
 
32
32
  /**
33
33
  * Model field decorator for denoting expiry date/time
34
- * @augments `@trv:schema/Field`
34
+ * @augments `@travetto/schema:Field`
35
35
  */
36
36
  export function ExpiresAt() {
37
37
  return <K extends string, T extends Partial<Record<K, Date>>>(tgt: T, prop: K): void => {
@@ -1,3 +1,4 @@
1
+ import { RootIndex } from '@travetto/manifest';
1
2
  import { SchemaRegistry } from '@travetto/schema';
2
3
  import { MetadataRegistry } from '@travetto/registry';
3
4
  import { DependencyRegistry } from '@travetto/di';
@@ -51,12 +52,12 @@ class $ModelRegistry extends MetadataRegistry<ModelOptions<ModelType>> {
51
52
  }
52
53
 
53
54
  createPending(cls: Class): Partial<ModelOptions<ModelType>> {
54
- return { class: cls, indices: [], autoCreate: true, baseType: cls.ᚕabstract };
55
+ return { class: cls, indices: [], autoCreate: true, baseType: RootIndex.getFunctionMetadata(cls)?.abstract };
55
56
  }
56
57
 
57
58
  onInstallFinalize(cls: Class): ModelOptions<ModelType> {
58
59
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
59
- const config = this.pending.get(cls.ᚕid)! as ModelOptions<ModelType>;
60
+ const config = this.pending.get(cls.Ⲑid)! as ModelOptions<ModelType>;
60
61
 
61
62
  const schema = SchemaRegistry.get(cls);
62
63
  const view = schema.views[AllViewⲐ].schema;
@@ -145,11 +146,11 @@ class $ModelRegistry extends MetadataRegistry<ModelOptions<ModelType>> {
145
146
  if (candidates.length > 1) {
146
147
  if (config.store) {
147
148
  throw new AppError('Duplicate models with same store name', 'general', {
148
- classes: candidates.map(x => x.ᚕid)
149
+ classes: candidates.map(x => x.Ⲑid)
149
150
  });
150
151
  } else {
151
152
  throw new AppError('Duplicate models with same class name, but no store name provided', 'general', {
152
- classes: candidates.map(x => x.ᚕid)
153
+ classes: candidates.map(x => x.Ⲑid)
153
154
  });
154
155
  }
155
156
  }
@@ -1,5 +1,4 @@
1
- import { Class } from '@travetto/base';
2
- import { Primitive } from '@travetto/base/src/internal/global-types';
1
+ import { Primitive, Class } from '@travetto/base';
3
2
 
4
3
  import { ModelType } from '../types/model';
5
4
 
@@ -4,10 +4,6 @@ import { ValidationResultError } from '@travetto/schema';
4
4
  import { ModelCrudSupport } from './crud';
5
5
  import { ModelType, OptionalId } from '../types/model';
6
6
 
7
- declare global {
8
- interface Error { toJSON(sub?: unknown): unknown }
9
- }
10
-
11
7
  /**
12
8
  * Bulk operation. Each operation has a single action and payload
13
9
  */
@@ -52,9 +48,8 @@ export class BulkProcessError extends AppError {
52
48
  /**
53
49
  * Provide full results back, with validation errors
54
50
  */
55
- override toJSON(extra: Record<string, unknown> = {}): unknown {
51
+ override toJSON(): unknown {
56
52
  return {
57
- ...extra,
58
53
  at: new Date(),
59
54
  message: this.message,
60
55
  category: this.category,
@@ -1,8 +1,9 @@
1
- import { CliUtil } from '@travetto/cli/src/util';
2
- import { ExecUtil } from '@travetto/boot';
3
-
4
- import type { Class } from '@travetto/base';
5
- import type { InjectableConfig } from '@travetto/di';
1
+ import { RootIndex } from '@travetto/manifest';
2
+ import { Class, ExecUtil } from '@travetto/base';
3
+ import { GlobalTerminal } from '@travetto/terminal';
4
+ import { ModelRegistry } from '@travetto/model';
5
+ import { InjectableConfig, DependencyRegistry } from '@travetto/di';
6
+ import { ModelStorageSupportTarget } from '@travetto/model/src/internal/service/common';
6
7
 
7
8
  import type { ModelStorageSupport } from '../../src/service/storage';
8
9
  import type { ModelType } from '../../src/types/model';
@@ -20,7 +21,6 @@ export class ModelCandidateUtil {
20
21
  static async #getModels(models?: string[]): Promise<Class<ModelType>[]> {
21
22
  const names = new Set(models ?? []);
22
23
  const all = names.has('*');
23
- const { ModelRegistry } = await import('@travetto/model');
24
24
  return ModelRegistry.getClasses()
25
25
  .map(x => ModelRegistry.getBaseModel(x))
26
26
  .filter(x => !models || all || names.has(ModelRegistry.getStore(x)));
@@ -30,7 +30,6 @@ export class ModelCandidateUtil {
30
30
  * Get model names
31
31
  */
32
32
  static async getModelNames(): Promise<string[]> {
33
- const { ModelRegistry } = await import('@travetto/model');
34
33
  return (await this.#getModels()).map(x => ModelRegistry.getStore(x)).sort();
35
34
  }
36
35
 
@@ -38,8 +37,6 @@ export class ModelCandidateUtil {
38
37
  * Get all providers that are viable candidates
39
38
  */
40
39
  static async getProviders(op?: keyof ModelStorageSupport): Promise<InjectableConfig[]> {
41
- const { DependencyRegistry } = await import('@travetto/di');
42
- const { ModelStorageSupportTarget } = await import('@travetto/model/src/internal/service/common');
43
40
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
44
41
  const types = DependencyRegistry.getCandidateTypes<ModelStorageSupport>(ModelStorageSupportTarget as unknown as Class<ModelStorageSupport>);
45
42
  return types.filter(x => !op || x.class.prototype[op]);
@@ -58,7 +55,6 @@ export class ModelCandidateUtil {
58
55
  * Get a single provider
59
56
  */
60
57
  static async getProvider(provider: string): Promise<ModelStorageSupport> {
61
- const { DependencyRegistry } = await import('@travetto/di');
62
58
  const config = (await this.getProviders()).find(x => x.class.name === `${provider}ModelService`)!;
63
59
  return DependencyRegistry.getInstance<ModelStorageSupport>(config.class, config.qualifier);
64
60
  }
@@ -68,9 +64,8 @@ export class ModelCandidateUtil {
68
64
  * @returns
69
65
  */
70
66
  static async getCandidates(op: keyof ModelStorageSupport): Promise<CandidateNames> {
71
- return CliUtil.waiting('Resolving', () =>
72
- ExecUtil.workerMain<CandidateNames>(require.resolve('../candidate'), [op]).message
73
- );
67
+ const main = RootIndex.resolveFileImport('@travetto/model/support/main.candidate.ts');
68
+ return GlobalTerminal.withWaiting('Resolving', ExecUtil.worker<CandidateNames>(main, [op]).message);
74
69
  }
75
70
 
76
71
  /**
File without changes
@@ -1,4 +1,4 @@
1
- import type { Class } from '@travetto/base';
1
+ import { ShutdownManager, type Class } from '@travetto/base';
2
2
  import type { ModelStorageSupport } from '@travetto/model/src/service/storage';
3
3
  import type { ModelType } from '@travetto/model/src/types/model';
4
4
 
@@ -8,9 +8,9 @@ export class ModelInstallUtil {
8
8
  throw new Error(`${provider} does not support model installation`);
9
9
  }
10
10
  for (const m of models) {
11
- console.log('Installing', { name: m.ᚕid });
11
+ console.log('Installing', { name: m.Ⲑid });
12
12
  await provider.createModel(m);
13
13
  }
14
- (await import('@travetto/base')).ShutdownManager.execute(-1); // Release database
14
+ ShutdownManager.execute(-1); // Release database
15
15
  }
16
16
  }
@@ -0,0 +1,54 @@
1
+ import { CliCommand, cliTpl, OptionConfig } from '@travetto/cli';
2
+ import type { ModelStorageSupport } from '@travetto/model/src/service/storage';
3
+
4
+ import { ModelCandidateUtil } from './bin/candidate';
5
+
6
+ type Options = {
7
+ env: OptionConfig<string>;
8
+ };
9
+
10
+ /**
11
+ * CLI Entry point for exporting model schemas
12
+ */
13
+ export abstract class BaseModelCommand extends CliCommand<Options> {
14
+
15
+ restoreEnv?: (err: Error) => unknown;
16
+
17
+ op: keyof ModelStorageSupport;
18
+
19
+ resolve = ModelCandidateUtil.resolve.bind(ModelCandidateUtil);
20
+
21
+ getArgs(): string {
22
+ return '[provider] [models...]';
23
+ }
24
+
25
+ getOptions(): Options {
26
+ return { env: this.option({ desc: 'Application environment' }) };
27
+ }
28
+
29
+ usage({ providers, models }: { providers: string[], models: string[] }, err = ''): Promise<void> {
30
+ return this.showHelp(err, cliTpl`
31
+ ${{ title: 'Providers' }}:
32
+ ${providers.map(p => cliTpl` * ${{ type: p }}`).join('\n')}
33
+
34
+ ${{ title: 'Models' }}:
35
+ ${models.map(p => cliTpl` * ${{ param: p }}`).join('\n')}
36
+ `, false);
37
+ }
38
+
39
+ async validate(provider: string, models: string[]): Promise<true | void> {
40
+ const candidates = await ModelCandidateUtil.getCandidates(this.op);
41
+ if (!provider) {
42
+ return this.usage(candidates);
43
+ } else {
44
+ if (!candidates.providers.includes(provider)) {
45
+ return this.usage(candidates, cliTpl`${{ param: provider }} is not a valid provider`);
46
+ }
47
+ const badModel = models.find(x => x !== '*' && !candidates.models.includes(x));
48
+ if (badModel) {
49
+ return this.usage(candidates, cliTpl`${{ param: badModel }} is not a valid model`);
50
+ }
51
+ }
52
+ return true;
53
+ }
54
+ }
@@ -1,5 +1,8 @@
1
- import { BaseModelCommand } from './lib/base-command';
2
- import { ModelExportUtil } from './lib/export';
1
+ import { ConsoleManager } from '@travetto/base';
2
+ import { RootRegistry } from '@travetto/registry';
3
+
4
+ import { BaseModelCommand } from './cli.base-command';
5
+ import { ModelExportUtil } from './bin/export';
3
6
 
4
7
  /**
5
8
  * CLI Entry point for exporting model schemas
@@ -10,7 +13,13 @@ export class ModelExportCommand extends BaseModelCommand {
10
13
 
11
14
  async action(provider: string, models: string[]): Promise<void> {
12
15
  try {
13
- await this.validate(provider, models);
16
+ ConsoleManager.setDebug(false);
17
+ await RootRegistry.init();
18
+
19
+ if (!await this.validate(provider, models)) {
20
+ return this.exit(1);
21
+ }
22
+
14
23
  const resolved = await this.resolve(provider, models);
15
24
  await ModelExportUtil.run(resolved.provider, resolved.models);
16
25
  } catch (err) {
@@ -0,0 +1,31 @@
1
+ import { cliTpl } from '@travetto/cli';
2
+ import { ConsoleManager } from '@travetto/base';
3
+ import { RootRegistry } from '@travetto/registry';
4
+
5
+ import { BaseModelCommand } from './cli.base-command';
6
+ import { ModelInstallUtil } from './bin/install';
7
+
8
+ /**
9
+ * CLI Entry point for installing models
10
+ */
11
+ export class ModelInstallCommand extends BaseModelCommand {
12
+ name = 'model:install';
13
+ op = 'createModel' as const;
14
+
15
+ async action(provider: string, models: string[]): Promise<void> {
16
+ try {
17
+ ConsoleManager.setDebug(false);
18
+ await RootRegistry.init();
19
+
20
+ if (!await this.validate(provider, models)) {
21
+ return this.exit(1);
22
+ }
23
+
24
+ const resolved = await this.resolve(provider, models);
25
+ await ModelInstallUtil.run(resolved.provider, resolved.models);
26
+ console.log(cliTpl`${{ success: 'Successfully' }} installed ${{ param: models.length.toString() }} model(s)`);
27
+ } catch (err) {
28
+ console.error((err && err instanceof Error) ? err.message : err);
29
+ }
30
+ }
31
+ }
@@ -0,0 +1,46 @@
1
+ import { readFileSync } from 'fs';
2
+
3
+ import { RootIndex } from '@travetto/manifest';
4
+ import { d, mod } from '@travetto/doc';
5
+ import { Config } from '@travetto/config';
6
+ import { AllType, AllTypeMap } from '@travetto/doc/src/nodes';
7
+
8
+ export const Links = {
9
+ Basic: d.SnippetLink('Basic', '@travetto/model/src/service/basic.ts', /export interface/),
10
+ Crud: d.SnippetLink('CRUD', '@travetto/model/src/service/crud.ts', /export interface/),
11
+ Expiry: d.SnippetLink('Expiry', '@travetto/model/src/service/expiry.ts', /export interface/),
12
+ Indexed: d.SnippetLink('Indexed', '@travetto/model/src/service/indexed.ts', /export interface/),
13
+ Bulk: d.SnippetLink('Bulk', '@travetto/model/src/service/bulk.ts', /export interface/),
14
+ Stream: d.SnippetLink('Streaming', '@travetto/model/src/service/stream.ts', /export interface/),
15
+ };
16
+
17
+ export const ModelTypes = (file: string | Function): AllTypeMap['SnippetLink'][] => {
18
+ if (typeof file !== 'string') {
19
+ file = RootIndex.getFunctionMetadata(file)!.source;
20
+ }
21
+ const contents = readFileSync(file, 'utf8');
22
+ const found: AllTypeMap['SnippetLink'][] = [];
23
+ const seen = new Set();
24
+ for (const [, key] of contents.matchAll(/Model(Crud|Expiry|Indexed|Bulk|Stream)Support/g)) {
25
+ if (!seen.has(key)) {
26
+ seen.add(key);
27
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
28
+ found.push(Links[key as keyof typeof Links]);
29
+ }
30
+ }
31
+ return found;
32
+ };
33
+
34
+ export const ModelCustomConfig = (cfg: Function): AllType => d`
35
+ 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
36
+ or config, you can override and register it with the ${mod.Di} module.
37
+
38
+ ${d.Code('Wiring up a custom Model Source', 'doc/custom-service.ts')}
39
+
40
+ where the ${cfg} is defined by:
41
+
42
+ ${d.Code(`Structure of ${cfg.name}`, cfg)}
43
+
44
+ Additionally, you can see that the class is registered with the ${Config} annotation, and so these values can be overridden using the
45
+ standard ${mod.Config}resolution paths.
46
+ `;
@@ -0,0 +1,16 @@
1
+ import { RootRegistry } from '@travetto/registry';
2
+
3
+ import type { ModelStorageSupport } from '../src/service/storage';
4
+ import { ModelCandidateUtil } from './bin/candidate';
5
+
6
+ /**
7
+ * Handles direct invocation
8
+ */
9
+ export async function main(op: keyof ModelStorageSupport): Promise<{ models: string[], providers: string[] }> {
10
+ await RootRegistry.init();
11
+
12
+ return {
13
+ models: await ModelCandidateUtil.getModelNames(),
14
+ providers: await ModelCandidateUtil.getProviderNames(op)
15
+ };
16
+ }
@@ -1,8 +1,8 @@
1
1
  import { DependencyRegistry } from '@travetto/di';
2
2
  import { AppError, Class } from '@travetto/base';
3
3
 
4
- import { isBulkSupported, isCrudSupported } from '../src/internal/service/common';
5
- import { ModelType } from '../src/types/model';
4
+ import { isBulkSupported, isCrudSupported } from '../../src/internal/service/common';
5
+ import { ModelType } from '../../src/types/model';
6
6
  import { ModelSuite } from './suite';
7
7
 
8
8
  type ServiceClass = { serviceClass: { new(): unknown } };
@@ -1,9 +1,9 @@
1
- import * as assert from 'assert';
1
+ import assert from 'assert';
2
2
 
3
3
  import { Suite, Test } from '@travetto/test';
4
+ import { ModelCrudSupport, Model, NotFoundError } from '@travetto/model';
4
5
 
5
6
  import { BaseModelSuite } from './base';
6
- import { ModelCrudSupport, Model, NotFoundError } from '..';
7
7
 
8
8
  @Model('basic_person')
9
9
  class Person {
@@ -1,9 +1,9 @@
1
- import * as assert from 'assert';
1
+ import assert from 'assert';
2
2
 
3
3
  import { Suite, Test } from '@travetto/test';
4
4
 
5
- import { Model } from '../src/registry/decorator';
6
- import { ModelBulkSupport } from '../src/service/bulk';
5
+ import { Model } from '../../src/registry/decorator';
6
+ import { ModelBulkSupport } from '../../src/service/bulk';
7
7
  import { BaseModelSuite } from './base';
8
8
 
9
9
  @Model('bulk-user')
@@ -1,10 +1,10 @@
1
- import * as assert from 'assert';
1
+ import assert from 'assert';
2
2
 
3
3
  import { Suite, Test } from '@travetto/test';
4
4
  import { Schema, Text, Precision, } from '@travetto/schema';
5
+ import { ModelCrudSupport, Model, NotFoundError } from '@travetto/model';
5
6
 
6
7
  import { BaseModelSuite } from './base';
7
- import { ModelCrudSupport, Model, NotFoundError } from '..';
8
8
 
9
9
  @Schema()
10
10
  class Address {
@@ -1,12 +1,12 @@
1
- import * as assert from 'assert';
1
+ import assert from 'assert';
2
2
 
3
3
  import { Suite, Test } from '@travetto/test';
4
- import { Util, TimeSpan, TimeUnit } from '@travetto/base';
4
+ import { TimeSpan, TimeUnit, TimeUtil } from '@travetto/base';
5
5
 
6
- import { ExpiresAt, Model } from '../src/registry/decorator';
7
- import { ModelExpirySupport } from '../src/service/expiry';
8
- import { ModelExpiryUtil } from '../src/internal/service/expiry';
9
- import { NotFoundError } from '../src/error/not-found';
6
+ import { ExpiresAt, Model } from '../../src/registry/decorator';
7
+ import { ModelExpirySupport } from '../../src/service/expiry';
8
+ import { ModelExpiryUtil } from '../../src/internal/service/expiry';
9
+ import { NotFoundError } from '../../src/error/not-found';
10
10
  import { BaseModelSuite } from './base';
11
11
 
12
12
  @Model('expiry-user')
@@ -22,11 +22,11 @@ export abstract class ModelExpirySuite extends BaseModelSuite<ModelExpirySupport
22
22
  delayFactor: number = 1;
23
23
 
24
24
  async wait(n: number | TimeSpan) {
25
- await Util.wait(Util.timeToMs(n) * this.delayFactor);
25
+ await TimeUtil.wait(TimeUtil.timeToMs(n) * this.delayFactor);
26
26
  }
27
27
 
28
28
  timeFromNow(v: number | TimeSpan, unit?: TimeUnit) {
29
- return new Date(Date.now() + Util.timeToMs(v, unit) * this.delayFactor);
29
+ return new Date(Date.now() + TimeUtil.timeToMs(v, unit) * this.delayFactor);
30
30
  }
31
31
 
32
32
  @Test()
@@ -1,14 +1,15 @@
1
- import * as assert from 'assert';
1
+ import assert from 'assert';
2
2
 
3
3
  import { Suite, Test } from '@travetto/test';
4
4
  import { Schema } from '@travetto/schema';
5
- import { Util } from '@travetto/base';
5
+ import { TimeUtil } from '@travetto/base';
6
+
7
+ import { Index, Model } from '../../src/registry/decorator';
8
+ import { ModelIndexedSupport } from '../../src/service/indexed';
9
+ import { NotFoundError } from '../../src/error/not-found';
10
+ import { IndexNotSupported } from '../../src/error/invalid-index';
6
11
 
7
- import { Index, Model } from '../src/registry/decorator';
8
- import { ModelIndexedSupport } from '../src/service/indexed';
9
12
  import { BaseModelSuite } from './base';
10
- import { NotFoundError } from '../src/error/not-found';
11
- import { IndexNotSupported } from '../src/error/invalid-index';
12
13
 
13
14
  @Model('index_user')
14
15
  @Index({
@@ -152,9 +153,9 @@ export abstract class ModelIndexedSuite extends BaseModelSuite<ModelIndexedSuppo
152
153
  async queryComplexDateList() {
153
154
  const service = await this.service;
154
155
 
155
- await service.create(User4, User4.from({ child: { name: 'bob', age: 40 }, createdDate: Util.timeFromNow('3d'), color: 'blue' }));
156
- await service.create(User4, User4.from({ child: { name: 'bob', age: 30 }, createdDate: Util.timeFromNow('2d'), color: 'red' }));
157
- await service.create(User4, User4.from({ child: { name: 'bob', age: 50 }, createdDate: Util.timeFromNow('-1d'), color: 'green' }));
156
+ await service.create(User4, User4.from({ child: { name: 'bob', age: 40 }, createdDate: TimeUtil.timeFromNow('3d'), color: 'blue' }));
157
+ await service.create(User4, User4.from({ child: { name: 'bob', age: 30 }, createdDate: TimeUtil.timeFromNow('2d'), color: 'red' }));
158
+ await service.create(User4, User4.from({ child: { name: 'bob', age: 50 }, createdDate: TimeUtil.timeFromNow('-1d'), color: 'green' }));
158
159
 
159
160
  const arr = await this.toArray(service.listByIndex(User4, 'nameCreated', User4.from({ child: { name: 'bob' } })));
160
161
 
@@ -1,15 +1,17 @@
1
- import * as assert from 'assert';
1
+ import assert from 'assert';
2
+ import timers from 'timers/promises';
2
3
 
3
4
  import { Suite, Test } from '@travetto/test';
4
5
  import { Text, TypeMismatchError } from '@travetto/schema';
5
-
6
- import { BaseModelSuite } from './base';
7
6
  import {
8
7
  ModelIndexedSupport, Index, ModelCrudSupport, Model,
9
8
  NotFoundError, SubTypeNotSupportedError
10
- } from '..';
11
- import { isIndexedSupported } from '../src/internal/service/common';
12
- import { ExistsError } from '../src/error/exists';
9
+ } from '@travetto/model';
10
+
11
+ import { isIndexedSupported } from '../../src/internal/service/common';
12
+ import { ExistsError } from '../../src/error/exists';
13
+
14
+ import { BaseModelSuite } from './base';
13
15
 
14
16
  @Model({ baseType: true })
15
17
  export class Worker {
@@ -162,6 +164,8 @@ export abstract class ModelPolymorphismSuite extends BaseModelSuite<ModelCrudSup
162
164
  () => service.update(Engineer, Doctor.from({ ...doc }) as unknown as Engineer),
163
165
  (e: Error) => (e instanceof NotFoundError || e instanceof SubTypeNotSupportedError || e instanceof TypeMismatchError) ? undefined : e);
164
166
 
167
+ await timers.setTimeout(15);
168
+
165
169
  try {
166
170
  const res = await service.upsert(Doctor, Doctor.from({
167
171
  id: doc.id, name: 'gob', specialty: 'eyes'
@@ -1,20 +1,18 @@
1
- import * as assert from 'assert';
2
- import * as fs from 'fs/promises';
3
- import { createReadStream } from 'fs';
4
- import * as crypto from 'crypto';
1
+ import assert from 'assert';
2
+ import crypto from 'crypto';
5
3
  import { Readable } from 'stream';
6
4
 
7
- import { PathUtil } from '@travetto/boot';
8
- import { BeforeAll, Suite, Test } from '@travetto/test';
9
- import { ResourceManager } from '@travetto/base';
5
+ import { Suite, Test, TestFixtures } from '@travetto/test';
10
6
 
11
7
  import { BaseModelSuite } from './base';
12
- import { ModelStreamSupport } from '../src/service/stream';
8
+ import { ModelStreamSupport } from '../../src/service/stream';
13
9
 
14
10
  @Suite()
15
11
  export abstract class ModelStreamSuite extends BaseModelSuite<ModelStreamSupport> {
16
12
 
17
- async getHash(stream: Readable) {
13
+ fixture = new TestFixtures(['@travetto/model#support/fixtures']);
14
+
15
+ async getHash(stream: Readable): Promise<string> {
18
16
  const hash = crypto.createHash('sha1');
19
17
  hash.setEncoding('hex');
20
18
  await new Promise((res, rej) => {
@@ -25,24 +23,18 @@ export abstract class ModelStreamSuite extends BaseModelSuite<ModelStreamSupport
25
23
  return hash.read() as string;
26
24
  }
27
25
 
28
- async getStream(resource: string) {
29
- const file = await ResourceManager.findAbsolute(resource);
30
- const stat = await fs.stat(file);
31
- const hash = await this.getHash(createReadStream(file));
26
+ async getStream(resource: string): Promise<readonly [{ size: number, contentType: string, hash: string, filename: string }, Readable]> {
27
+ const { size } = await this.fixture.describe(resource);
28
+ const hash = await this.getHash(await this.fixture.readStream(resource));
32
29
 
33
30
  return [
34
- { size: stat.size, contentType: '', hash, filename: resource },
35
- createReadStream(file)
31
+ { size, contentType: '', hash, filename: resource },
32
+ await this.fixture.readStream(resource)
36
33
  ] as const;
37
34
  }
38
35
 
39
- @BeforeAll()
40
- async beforeAll() {
41
- ResourceManager.addPath(PathUtil.resolveUnix(__dirname, '..', 'resources'));
42
- }
43
-
44
36
  @Test()
45
- async writeBasic() {
37
+ async writeBasic(): Promise<void> {
46
38
  const service = await this.service;
47
39
  const [meta, stream] = await this.getStream('/asset.yml');
48
40
 
@@ -53,7 +45,7 @@ export abstract class ModelStreamSuite extends BaseModelSuite<ModelStreamSupport
53
45
  }
54
46
 
55
47
  @Test()
56
- async writeStream() {
48
+ async writeStream(): Promise<void> {
57
49
  const service = await this.service;
58
50
  const [meta, stream] = await this.getStream('/asset.yml');
59
51
 
@@ -64,7 +56,7 @@ export abstract class ModelStreamSuite extends BaseModelSuite<ModelStreamSupport
64
56
  }
65
57
 
66
58
  @Test()
67
- async writeAndDelete() {
59
+ async writeAndDelete(): Promise<void> {
68
60
  const service = await this.service;
69
61
  const [meta, stream] = await this.getStream('/asset.yml');
70
62
 
@@ -1,23 +1,22 @@
1
- import { Class, ResourceManager } from '@travetto/base';
2
- import { PathUtil } from '@travetto/boot';
1
+ import { Class } from '@travetto/base';
3
2
  import { DependencyRegistry } from '@travetto/di';
4
3
  import { RootRegistry } from '@travetto/registry';
5
- import { SuiteRegistry } from '@travetto/test';
4
+ import { SuiteRegistry, TestFixtures } from '@travetto/test';
6
5
 
7
- import { isStorageSupported, isStreamSupported } from '../src/internal/service/common';
8
- import { StreamModel } from '../src/internal/service/stream';
9
- import { ModelRegistry } from '../src/registry/model';
6
+ import { isStorageSupported, isStreamSupported } from '../../src/internal/service/common';
7
+ import { StreamModel } from '../../src/internal/service/stream';
8
+ import { ModelRegistry } from '../../src/registry/model';
10
9
 
11
10
  const Loaded = Symbol();
12
11
 
13
12
  export function ModelSuite<T extends { configClass: Class<{ autoCreate?: boolean, namespace?: string }>, serviceClass: Class }>(qualifier?: symbol) {
13
+ const fixtures = new TestFixtures(['@travetto/model']);
14
14
  return (target: Class<T>): void => {
15
+ target.prototype.fixtures = fixtures;
16
+
15
17
  SuiteRegistry.registerPendingListener(
16
18
  target,
17
19
  async function (this: T & { [Loaded]?: boolean }) {
18
- // Track self
19
- ResourceManager.addPath(PathUtil.resolveUnix(__dirname, 'resources'));
20
-
21
20
  await RootRegistry.init();
22
21
 
23
22
  if (!this[Loaded]) {
package/bin/candidate.ts DELETED
@@ -1,23 +0,0 @@
1
- import { EnvInit } from '@travetto/base/bin/init';
2
- import { ExecUtil } from '@travetto/boot';
3
-
4
- import type { ModelStorageSupport } from '../src/service/storage';
5
- import { ModelCandidateUtil } from './lib/candidate';
6
-
7
- /**
8
- * Handles direct invocation
9
- */
10
- export async function main(op: keyof ModelStorageSupport): Promise<void> {
11
- try {
12
- EnvInit.init();
13
- const { PhaseManager } = await import('@travetto/base');
14
- await PhaseManager.run('init');
15
-
16
- ExecUtil.mainResponse({
17
- models: await ModelCandidateUtil.getModelNames(),
18
- providers: await ModelCandidateUtil.getProviderNames(op)
19
- });
20
- } catch (err) {
21
- ExecUtil.mainResponse(err);
22
- }
23
- }
@@ -1,23 +0,0 @@
1
- import { color } from '@travetto/cli/src/color';
2
-
3
- import { BaseModelCommand } from './lib/base-command';
4
- import { ModelInstallUtil } from './lib/install';
5
-
6
- /**
7
- * CLI Entry point for installing models
8
- */
9
- export class ModelInstallCommand extends BaseModelCommand {
10
- name = 'model:install';
11
- op = 'createModel' as const;
12
-
13
- async action(provider: string, models: string[]): Promise<void> {
14
- try {
15
- await this.validate(provider, models);
16
- const resolved = await this.resolve(provider, models);
17
- await ModelInstallUtil.run(resolved.provider, resolved.models);
18
- console.log(color`${{ success: 'Successfully' }} installed ${{ param: models.length.toString() }} model(s)`);
19
- } catch (err) {
20
- console.error((err && err instanceof Error) ? err.message : err);
21
- }
22
- }
23
- }
@@ -1,67 +0,0 @@
1
- import { CliCommand, OptionConfig } from '@travetto/cli/src/command';
2
- import { color } from '@travetto/cli/src/color';
3
- import { EnvInit } from '@travetto/base/bin/init';
4
- import type { ModelStorageSupport } from '@travetto/model/src/service/storage';
5
-
6
- import { ModelCandidateUtil } from './candidate';
7
-
8
- type Options = {
9
- env: OptionConfig<string>;
10
- };
11
-
12
- /**
13
- * CLI Entry point for exporting model schemas
14
- */
15
- export abstract class BaseModelCommand extends CliCommand<Options> {
16
-
17
- restoreEnv?: (err: Error) => unknown;
18
-
19
- op: keyof ModelStorageSupport;
20
-
21
- resolve = ModelCandidateUtil.resolve.bind(ModelCandidateUtil);
22
-
23
- envInit(): void {
24
- EnvInit.init();
25
- }
26
-
27
- override async build(): Promise<void> {
28
- await super.build();
29
- const { ConsoleManager, PhaseManager } = await import('@travetto/base');
30
- ConsoleManager.exclude('debug');
31
- // Init
32
- await PhaseManager.run('init');
33
- }
34
-
35
- getArgs(): string {
36
- return '[provider] [models...]';
37
- }
38
-
39
- getOptions(): Options {
40
- return { env: this.option({ desc: 'Application environment' }) };
41
- }
42
-
43
- async usage({ providers, models }: { providers: string[], models: string[] }, err = ''): Promise<void> {
44
- await this.showHelp(err, color`
45
- ${{ title: 'Providers' }}:
46
- ${providers.map(p => color` * ${{ type: p }}`).join('\n')}
47
-
48
- ${{ title: 'Models' }}:
49
- ${models.map(p => color` * ${{ param: p }}`).join('\n')}
50
- `);
51
- }
52
-
53
- async validate(provider: string, models: string[]): Promise<void> {
54
- const candidates = await ModelCandidateUtil.getCandidates(this.op);
55
- if (!provider) {
56
- return await this.usage(candidates);
57
- } else {
58
- if (!candidates.providers.includes(provider)) {
59
- await this.usage(candidates, color`${{ param: provider }} is not a valid provider`);
60
- }
61
- const badModel = models.find(x => x !== '*' && !candidates.models.includes(x));
62
- if (badModel) {
63
- await this.usage(candidates, color`${{ param: badModel }} is not a valid model`);
64
- }
65
- }
66
- }
67
- }