@travetto/di 3.0.0-rc.2 → 3.0.0-rc.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -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/di/doc.ts and execute "npx trv doc" to rebuild -->
2
+ <!-- Please modify https://github.com/travetto/travetto/tree/main/module/di/DOC.ts and execute "npx trv doc" to rebuild -->
3
3
  # Dependency Injection
4
4
  ## Dependency registration/management and injection support.
5
5
 
@@ -11,7 +11,7 @@ npm install @travetto/di
11
11
  [Dependency injection](https://en.wikipedia.org/wiki/Dependency_injection) is a framework primitive. When used in conjunction with automatic file scanning, it provides for handling of application dependency wiring. Due to the nature of [Typescript](https://typescriptlang.org) and type erasure of interfaces, dependency injection only supports `class`es as a type signifier. The primary goal of dependency injection is to allow for separation of concerns of object creation and it's usage.
12
12
 
13
13
  ## Declaration
14
- The [@Injectable](https://github.com/travetto/travetto/tree/main/module/di/src/decorator.ts#L32) and [@InjectableFactory](https://github.com/travetto/travetto/tree/main/module/di/src/decorator.ts#L75) decorators provide the registration of dependencies. Dependency declaration revolves around exposing `class`es and subtypes thereof to provide necessary functionality. Additionally, the framework will utilize dependencies to satisfy contracts with various implementations (e.g. [MongoModelService](https://github.com/travetto/travetto/tree/main/module/model-mongo/src/service.ts#L49) provides itself as an injectable candidate for [ModelCrudSupport](https://github.com/travetto/travetto/tree/main/module/model/src/service/crud.ts).
14
+ The [@Injectable](https://github.com/travetto/travetto/tree/main/module/di/src/decorator.ts#L31) and [@InjectableFactory](https://github.com/travetto/travetto/tree/main/module/di/src/decorator.ts#L74) decorators provide the registration of dependencies. Dependency declaration revolves around exposing `class`es and subtypes thereof to provide necessary functionality. Additionally, the framework will utilize dependencies to satisfy contracts with various implementation.
15
15
 
16
16
  **Code: Example Injectable**
17
17
  ```typescript
@@ -72,7 +72,7 @@ class SpecificService extends BaseService {
72
72
  }
73
73
  ```
74
74
 
75
- In this scenario, `SpecificService` is a valid candidate for `BaseService` due to the abstract inheritance. Sometimes, you may want to provide a slight variation to a dependency without extending a class. To this end, the [@InjectableFactory](https://github.com/travetto/travetto/tree/main/module/di/src/decorator.ts#L75) decorator denotes a `static` class method that produces an [@Injectable](https://github.com/travetto/travetto/tree/main/module/di/src/decorator.ts#L32).
75
+ In this scenario, `SpecificService` is a valid candidate for `BaseService` due to the abstract inheritance. Sometimes, you may want to provide a slight variation to a dependency without extending a class. To this end, the [@InjectableFactory](https://github.com/travetto/travetto/tree/main/module/di/src/decorator.ts#L74) decorator denotes a `static` class method that produces an [@Injectable](https://github.com/travetto/travetto/tree/main/module/di/src/decorator.ts#L31).
76
76
 
77
77
  **Code: Example InjectableFactory**
78
78
  ```typescript
@@ -93,15 +93,15 @@ class Config {
93
93
 
94
94
  Given the `static` method `initService`, the function will be provided as a valid candidate for `CoolService`. Instead of calling the constructor of the type directly, this function will work as a factory for producing the injectable.
95
95
 
96
- **Note**: Other modules are able to provide aliases to [@Injectable](https://github.com/travetto/travetto/tree/main/module/di/src/decorator.ts#L32) that also provide additional functionality. For example, the [@Config](https://github.com/travetto/travetto/tree/main/module/config/src/decorator.ts#L9) or the [@Controller](https://github.com/travetto/travetto/tree/main/module/rest/src/decorator/controller.ts#L9) decorator registers the associated class as an injectable element.
96
+ **Note**: Other modules are able to provide aliases to [@Injectable](https://github.com/travetto/travetto/tree/main/module/di/src/decorator.ts#L31) that also provide additional functionality. For example, the [Configuration](https://github.com/travetto/travetto/tree/main/module/config#readme "Configuration support") module @Config or the [RESTful API](https://github.com/travetto/travetto/tree/main/module/rest#readme "Declarative api for RESTful APIs with support for the dependency injection module.") module @Controller decorator registers the associated class as an injectable element.
97
97
 
98
98
  ## Injection
99
99
 
100
- Once all of your necessary dependencies are defined, now is the time to provide those [@Injectable](https://github.com/travetto/travetto/tree/main/module/di/src/decorator.ts#L32) instances to your code. There are three primary methods for injection:
100
+ Once all of your necessary dependencies are defined, now is the time to provide those [@Injectable](https://github.com/travetto/travetto/tree/main/module/di/src/decorator.ts#L31) instances to your code. There are three primary methods for injection:
101
101
 
102
- The [@Inject](https://github.com/travetto/travetto/tree/main/module/di/src/decorator.ts#L32) decorator, which denotes a desire to inject a value directly. These will be set post construction.
102
+ The [@Inject](https://github.com/travetto/travetto/tree/main/module/di/src/decorator.ts#L31) decorator, which denotes a desire to inject a value directly. These will be set post construction.
103
103
 
104
- **Code: Example Injectable with dependencies as [@Inject](https://github.com/travetto/travetto/tree/main/module/di/src/decorator.ts#L32) fields**
104
+ **Code: Example Injectable with dependencies as [@Inject](https://github.com/travetto/travetto/tree/main/module/di/src/decorator.ts#L31) fields**
105
105
  ```typescript
106
106
  import { Injectable, Inject } from '@travetto/di';
107
107
  import { DependentService } from './dep';
@@ -117,7 +117,7 @@ class CustomService {
117
117
  }
118
118
  ```
119
119
 
120
- The [@Injectable](https://github.com/travetto/travetto/tree/main/module/di/src/decorator.ts#L32) constructor params, which will be provided as the instance is being constructed.
120
+ The [@Injectable](https://github.com/travetto/travetto/tree/main/module/di/src/decorator.ts#L31) constructor params, which will be provided as the instance is being constructed.
121
121
 
122
122
  **Code: Example Injectable with dependencies in constructor**
123
123
  ```typescript
@@ -134,7 +134,7 @@ class CustomService {
134
134
  }
135
135
  ```
136
136
 
137
- Via [@InjectableFactory](https://github.com/travetto/travetto/tree/main/module/di/src/decorator.ts#L75) params, which are comparable to constructor params
137
+ Via [@InjectableFactory](https://github.com/travetto/travetto/tree/main/module/di/src/decorator.ts#L74) params, which are comparable to constructor params
138
138
 
139
139
  **Code: Example InjectableFactory with parameters as dependencies**
140
140
  ```typescript
@@ -210,3 +210,39 @@ class ManualLookup {
210
210
  }
211
211
  }
212
212
  ```
213
+
214
+ Additionally, support for interfaces (over class inheritance) is provided, but requires binding the interface to a concrete class as the interface does not exist at runtime.
215
+
216
+ **Code: Example Interface Injection**
217
+ ```typescript
218
+ import { DependencyRegistry, Inject, Injectable, InjectableFactory } from '@travetto/di';
219
+
220
+ class TargetConcrete { }
221
+
222
+ /**
223
+ * @concrete .:TargetConcrete
224
+ */
225
+ export interface ServiceContract {
226
+ deleteUser(userId: string): Promise<void>;
227
+ }
228
+
229
+ class MyCustomService implements ServiceContract {
230
+ async deleteUser(userId: string): Promise<void> {
231
+ // Do something
232
+ }
233
+ }
234
+
235
+ @Injectable()
236
+ class SpecificService {
237
+
238
+ @Inject()
239
+ service: ServiceContract;
240
+ }
241
+
242
+ class ManualInvocationOfInterface {
243
+ @InjectableFactory()
244
+ static getCustomService(): Promise<ServiceContract> {
245
+ return DependencyRegistry.getInstance<ServiceContract>(TargetConcrete);
246
+ }
247
+ }
248
+ ```
package/package.json CHANGED
@@ -1,7 +1,6 @@
1
1
  {
2
2
  "name": "@travetto/di",
3
- "displayName": "Dependency Injection",
4
- "version": "3.0.0-rc.2",
3
+ "version": "3.0.0-rc.21",
5
4
  "description": "Dependency registration/management and injection support.",
6
5
  "keywords": [
7
6
  "ast-transformations",
@@ -18,27 +17,28 @@
18
17
  "name": "Travetto Framework"
19
18
  },
20
19
  "files": [
21
- "index.ts",
20
+ "__index__.ts",
22
21
  "src",
23
- "support",
24
- "test-support"
22
+ "support"
25
23
  ],
26
- "main": "index.ts",
24
+ "main": "__index__.ts",
27
25
  "repository": {
28
26
  "url": "https://github.com/travetto/travetto.git",
29
27
  "directory": "module/di"
30
28
  },
31
29
  "dependencies": {
32
- "@travetto/transformer": "^3.0.0-rc.2",
33
- "@travetto/registry": "^3.0.0-rc.2"
30
+ "@travetto/registry": "^3.0.0-rc.21"
34
31
  },
35
- "devDependencies": {
36
- "@travetto/config": "^3.0.0-rc.2",
37
- "@travetto/schema": "^3.0.0-rc.2"
32
+ "peerDependencies": {
33
+ "@travetto/transformer": "^3.0.0-rc.20"
38
34
  },
39
- "docDependencies": {
40
- "@travetto/model-mongo": true,
41
- "@travetto/rest": true
35
+ "peerDependenciesMeta": {
36
+ "@travetto/transformer": {
37
+ "optional": true
38
+ }
39
+ },
40
+ "travetto": {
41
+ "displayName": "Dependency Injection"
42
42
  },
43
43
  "publishConfig": {
44
44
  "access": "public"
package/src/decorator.ts CHANGED
@@ -1,5 +1,4 @@
1
- import { Class, ClassInstance } from '@travetto/base';
2
- import { MethodDescriptor } from '@travetto/base/src/internal/types';
1
+ import type { MethodDescriptor, Class, ClassInstance } from '@travetto/base';
3
2
 
4
3
  import { InjectableFactoryConfig, InjectableConfig, Dependency } from './types';
5
4
  import { DependencyRegistry, ResolutionType } from './registry';
@@ -27,7 +26,7 @@ function collapseConfig<T extends { qualifier?: symbol }>(...args: (symbol | Par
27
26
  /**
28
27
  * Indicate that a class is able to be injected
29
28
  *
30
- * @augments `@trv:di/Injectable`
29
+ * @augments `@travetto/di:Injectable`
31
30
  */
32
31
  export function Injectable(first?: Partial<InjectableConfig> | symbol, ...args: (Partial<InjectableConfig> | undefined)[]) {
33
32
  return <T extends Class>(target: T): T => {
@@ -52,7 +51,7 @@ export function InjectArgs(configs?: InjectConfig[][]) {
52
51
  /**
53
52
  * Indicate that a field is able to be injected
54
53
  *
55
- * @augments `@trv:di/Inject`
54
+ * @augments `@travetto/di:Inject`
56
55
  */
57
56
  export function Inject(first?: InjectConfig | symbol, ...args: (InjectConfig | undefined)[]) {
58
57
  return (target: unknown, propertyKey: string | symbol, idx?: number | PropertyDescriptor): void => {
@@ -70,7 +69,7 @@ export function Inject(first?: InjectConfig | symbol, ...args: (InjectConfig | u
70
69
  /**
71
70
  * Identifies a static method that is able to produce a dependency
72
71
  *
73
- * @augments `@trv:di/InjectableFactory`
72
+ * @augments `@travetto/di:InjectableFactory`
74
73
  */
75
74
  export function InjectableFactory(first?: Partial<InjectableFactoryConfig> | symbol, ...args: (Partial<InjectableFactoryConfig> | undefined)[]) {
76
75
  return <T extends Class>(target: T, property: string | symbol, descriptor: MethodDescriptor): void => {
@@ -79,7 +78,7 @@ export function InjectableFactory(first?: Partial<InjectableFactoryConfig> | sym
79
78
  ...config,
80
79
  dependencies: config.dependencies?.map(x => Array.isArray(x) ? collapseConfig(...x) : collapseConfig(x)),
81
80
  fn: descriptor.value!,
82
- id: `${target.ᚕid}#${property.toString()}`
81
+ id: `${target.Ⲑid}#${property.toString()}`
83
82
  });
84
83
  };
85
84
  }
package/src/error.ts CHANGED
@@ -7,6 +7,6 @@ function getName(symbol: symbol): string {
7
7
 
8
8
  export class InjectionError extends AppError {
9
9
  constructor(message: string, target: ClassTarget, qualifiers?: symbol[]) {
10
- super(`${message}: [${target.ᚕid}]${qualifiers ? `[${qualifiers.map(getName)}]` : ''}`, 'notfound');
10
+ super(`${message}: [${target.Ⲑid}]${qualifiers ? `[${qualifiers.map(getName)}]` : ''}`, 'notfound');
11
11
  }
12
12
  }
@@ -0,0 +1 @@
1
+ export class AutoCreateTarget { }
package/src/registry.ts CHANGED
@@ -1,17 +1,18 @@
1
- import { Class, ClassInstance, ConcreteClass } from '@travetto/base';
1
+ import { Class, ClassInstance, ConcreteClass, GlobalEnv } from '@travetto/base';
2
2
  import { MetadataRegistry, RootRegistry, ChangeEvent } from '@travetto/registry';
3
- import { Dynamic } from '@travetto/base/src/internal/dynamic';
3
+ import { RootIndex } from '@travetto/manifest';
4
4
 
5
5
  import { Dependency, InjectableConfig, ClassTarget, InjectableFactoryConfig } from './types';
6
6
  import { InjectionError } from './error';
7
+ import { AutoCreateTarget } from './internal/types';
7
8
 
8
9
  type TargetId = string;
9
10
  type ClassId = string;
10
- type Resolved<T> = { config: InjectableConfig<T>, qualifier: symbol, id: string };
11
+ export type Resolved<T> = { config: InjectableConfig<T>, qualifier: symbol, id: string };
11
12
 
12
13
  export type ResolutionType = 'strict' | 'loose' | 'any';
13
14
 
14
- const PrimaryCandidateⲐ = Symbol.for('@trv:di/primary');
15
+ const PrimaryCandidateⲐ = Symbol.for('@travetto/di:primary');
15
16
 
16
17
  function hasPostConstruct(o: unknown): o is { postConstruct: () => Promise<unknown> } {
17
18
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
@@ -26,7 +27,6 @@ function hasPreDestroy(o: unknown): o is { preDestroy: () => unknown } {
26
27
  /**
27
28
  * Dependency registry
28
29
  */
29
- @Dynamic('@travetto/di/support/dynamic.injection')
30
30
  class $DependencyRegistry extends MetadataRegistry<InjectableConfig> {
31
31
  protected pendingFinalize: Class[] = [];
32
32
 
@@ -50,7 +50,7 @@ class $DependencyRegistry extends MetadataRegistry<InjectableConfig> {
50
50
  * @param qualifier
51
51
  */
52
52
  protected resolveTarget<T>(target: ClassTarget<T>, qualifier?: symbol, resolution?: ResolutionType): Resolved<T> {
53
- const qualifiers = this.targetToClass.get(target.ᚕid) ?? new Map<symbol, string>();
53
+ const qualifiers = this.targetToClass.get(target.Ⲑid) ?? new Map<symbol, string>();
54
54
 
55
55
  let cls: string | undefined;
56
56
 
@@ -90,7 +90,7 @@ class $DependencyRegistry extends MetadataRegistry<InjectableConfig> {
90
90
  throw new InjectionError('Dependency not found', target);
91
91
  } else if (!qualifiers.has(qualifier)) {
92
92
  if (!this.defaultSymbols.has(qualifier) && resolution === 'loose') {
93
- console.debug('Unable to find specific dependency, falling back to general instance', { qualifier, target: target.ᚕid });
93
+ console.debug('Unable to find specific dependency, falling back to general instance', { qualifier, target: target.Ⲑid });
94
94
  return this.resolveTarget(target);
95
95
  }
96
96
  throw new InjectionError('Dependency not found', target, [qualifier]);
@@ -104,7 +104,7 @@ class $DependencyRegistry extends MetadataRegistry<InjectableConfig> {
104
104
  return {
105
105
  qualifier,
106
106
  config,
107
- id: (config.factory ? config.target : config.class).ᚕid
107
+ id: (config.factory ? config.target : config.class).Ⲑid
108
108
  };
109
109
  }
110
110
 
@@ -124,7 +124,7 @@ class $DependencyRegistry extends MetadataRegistry<InjectableConfig> {
124
124
  return undefined;
125
125
  } else {
126
126
  if (err && err instanceof Error) {
127
- err.message = `${err.message} via=${managed.class.ᚕid}`;
127
+ err.message = `${err.message} via=${managed.class.Ⲑid}`;
128
128
  }
129
129
  throw err;
130
130
  }
@@ -221,7 +221,7 @@ class $DependencyRegistry extends MetadataRegistry<InjectableConfig> {
221
221
  * Destroy an instance
222
222
  */
223
223
  protected destroyInstance(cls: Class, qualifier: symbol): void {
224
- const classId = cls.ᚕid;
224
+ const classId = cls.Ⲑid;
225
225
 
226
226
  const activeInstance = this.instances.get(classId)!.get(qualifier);
227
227
  if (hasPreDestroy(activeInstance)) {
@@ -231,8 +231,20 @@ class $DependencyRegistry extends MetadataRegistry<InjectableConfig> {
231
231
  this.defaultSymbols.delete(qualifier);
232
232
  this.instances.get(classId)!.delete(qualifier);
233
233
  this.instancePromises.get(classId)!.delete(qualifier);
234
- this.classToTarget.get(cls.ᚕid)!.delete(qualifier);
235
- console.debug('On uninstall', { id: cls.ᚕid, qualifier: qualifier.toString(), classId });
234
+ this.classToTarget.get(cls.Ⲑid)!.delete(qualifier);
235
+ console.debug('On uninstall', { id: cls.Ⲑid, qualifier: qualifier.toString(), classId });
236
+ }
237
+
238
+ override async init(): Promise<void> {
239
+ await super.init();
240
+ if (GlobalEnv.dynamic) {
241
+ const { DependencyRegistration } = await import('../support/dynamic.injection.js');
242
+ DependencyRegistration.init(this);
243
+ }
244
+ // Allow for auto-creation
245
+ for (const cfg of await this.getCandidateTypes(AutoCreateTarget)) {
246
+ await this.getInstance(cfg.class, cfg.qualifier);
247
+ }
236
248
  }
237
249
 
238
250
  /**
@@ -286,7 +298,7 @@ class $DependencyRegistry extends MetadataRegistry<InjectableConfig> {
286
298
  * Get all available candidate types for the target
287
299
  */
288
300
  getCandidateTypes<T>(target: Class<T>): InjectableConfig[] {
289
- const targetId = target.ᚕid;
301
+ const targetId = target.Ⲑid;
290
302
  const qualifiers = this.targetToClass.get(targetId)!;
291
303
  const uniqueQualifiers = qualifiers ? Array.from(new Set(qualifiers.values())) : [];
292
304
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
@@ -316,7 +328,7 @@ class $DependencyRegistry extends MetadataRegistry<InjectableConfig> {
316
328
  const config = this.getOrCreatePending(pConfig.class ?? cls);
317
329
 
318
330
  config.class = cls;
319
- config.qualifier = pConfig.qualifier ?? config.qualifier ?? Symbol.for(cls.ᚕid);
331
+ config.qualifier = pConfig.qualifier ?? config.qualifier ?? Symbol.for(cls.Ⲑid);
320
332
  if (pConfig.interfaces) {
321
333
  config.interfaces?.push(...pConfig.interfaces);
322
334
  }
@@ -366,18 +378,18 @@ class $DependencyRegistry extends MetadataRegistry<InjectableConfig> {
366
378
 
367
379
  // Create mock cls for DI purposes
368
380
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
369
- const cls = { id: config.id } as Class;
381
+ const cls = { id: config.id } as Class;
370
382
 
371
383
  finalConfig.class = cls;
372
384
 
373
385
  this.registerClass(cls, finalConfig);
374
386
 
375
- if (!this.factories.has(config.src.ᚕid)) {
376
- this.factories.set(config.src.ᚕid, new Map());
387
+ if (!this.factories.has(config.src.Ⲑid)) {
388
+ this.factories.set(config.src.Ⲑid, new Map());
377
389
  }
378
390
 
379
391
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
380
- this.factories.get(config.src.ᚕid)!.set(cls, finalConfig as InjectableConfig);
392
+ this.factories.get(config.src.Ⲑid)!.set(cls, finalConfig as InjectableConfig);
381
393
  }
382
394
 
383
395
  /**
@@ -387,8 +399,8 @@ class $DependencyRegistry extends MetadataRegistry<InjectableConfig> {
387
399
  super.onInstall(cls, e);
388
400
 
389
401
  // Install factories separate from classes
390
- if (this.factories.has(cls.ᚕid)) {
391
- for (const fact of this.factories.get(cls.ᚕid)!.keys()) {
402
+ if (this.factories.has(cls.Ⲑid)) {
403
+ for (const fact of this.factories.get(cls.Ⲑid)!.keys()) {
392
404
  this.onInstall(fact, e);
393
405
  }
394
406
  }
@@ -398,7 +410,7 @@ class $DependencyRegistry extends MetadataRegistry<InjectableConfig> {
398
410
  * Handle installing a class
399
411
  */
400
412
  onInstallFinalize<T>(cls: Class<T>): InjectableConfig<T> {
401
- const classId = cls.ᚕid;
413
+ const classId = cls.Ⲑid;
402
414
 
403
415
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
404
416
  const config = this.getOrCreatePending(cls) as InjectableConfig<T>;
@@ -407,12 +419,17 @@ class $DependencyRegistry extends MetadataRegistry<InjectableConfig> {
407
419
  let parentClass = config.factory ? config.target : Object.getPrototypeOf(cls);
408
420
 
409
421
  if (config.factory) {
410
- while (Object.getPrototypeOf(parentClass).ᚕabstract) {
422
+ while (RootIndex.getFunctionMetadata(Object.getPrototypeOf(parentClass))?.abstract) {
411
423
  parentClass = Object.getPrototypeOf(parentClass);
412
424
  }
425
+ if (!this.targetToClass.has(classId)) {
426
+ this.targetToClass.set(classId, new Map());
427
+ }
428
+ // Make explicitly discoverable as self
429
+ this.targetToClass.get(classId)?.set(config.qualifier, classId);
413
430
  }
414
431
 
415
- const parentConfig = this.get(parentClass.ᚕid);
432
+ const parentConfig = this.get(parentClass.Ⲑid);
416
433
 
417
434
  if (parentConfig) {
418
435
  config.dependencies.fields = {
@@ -432,7 +449,7 @@ class $DependencyRegistry extends MetadataRegistry<InjectableConfig> {
432
449
  }
433
450
  }
434
451
 
435
- if (cls.ᚕabstract) { // Skip out early, only needed to inherit
452
+ if (RootIndex.getFunctionMetadata(cls)?.abstract) { // Skip out early, only needed to inherit
436
453
  return config;
437
454
  }
438
455
 
@@ -440,13 +457,13 @@ class $DependencyRegistry extends MetadataRegistry<InjectableConfig> {
440
457
  this.classToTarget.set(classId, new Map());
441
458
  }
442
459
 
443
- const targetId = config.target.ᚕid;
460
+ const targetId = config.target.Ⲑid;
444
461
 
445
462
  if (!this.targetToClass.has(targetId)) {
446
463
  this.targetToClass.set(targetId, new Map());
447
464
  }
448
465
 
449
- if (config.qualifier === Symbol.for(cls.ᚕid)) {
466
+ if (config.qualifier === Symbol.for(cls.Ⲑid)) {
450
467
  this.defaultSymbols.add(config.qualifier);
451
468
  }
452
469
 
@@ -455,20 +472,20 @@ class $DependencyRegistry extends MetadataRegistry<InjectableConfig> {
455
472
 
456
473
  // If aliased
457
474
  for (const el of config.interfaces) {
458
- if (!this.targetToClass.has(el.ᚕid)) {
459
- this.targetToClass.set(el.ᚕid, new Map());
475
+ if (!this.targetToClass.has(el.Ⲑid)) {
476
+ this.targetToClass.set(el.Ⲑid, new Map());
460
477
  }
461
- this.targetToClass.get(el.ᚕid)!.set(config.qualifier, classId);
462
- this.classToTarget.get(classId)!.set(Symbol.for(el.ᚕid), el.ᚕid);
478
+ this.targetToClass.get(el.Ⲑid)!.set(config.qualifier, classId);
479
+ this.classToTarget.get(classId)!.set(Symbol.for(el.Ⲑid), el.Ⲑid);
463
480
 
464
481
  if (config.primary && (classId === targetId || config.factory)) {
465
- this.targetToClass.get(el.ᚕid)!.set(PrimaryCandidateⲐ, classId);
482
+ this.targetToClass.get(el.Ⲑid)!.set(PrimaryCandidateⲐ, classId);
466
483
  }
467
484
  }
468
485
 
469
486
  // If targeting self (default @Injectable behavior)
470
- if ((classId === targetId || config.factory) && (parentConfig || parentClass.ᚕabstract)) {
471
- const parentId = parentClass.ᚕid;
487
+ if ((classId === targetId || config.factory) && (parentConfig || RootIndex.getFunctionMetadata(parentClass)?.abstract)) {
488
+ const parentId = parentClass.Ⲑid;
472
489
 
473
490
  if (!this.targetToClass.has(parentId)) {
474
491
  this.targetToClass.set(parentId, new Map());
@@ -495,10 +512,10 @@ class $DependencyRegistry extends MetadataRegistry<InjectableConfig> {
495
512
  // Register primary if only one interface provided and no parent config
496
513
  if (config.interfaces.length === 1 && !parentConfig) {
497
514
  const [primaryInterface] = config.interfaces;
498
- if (!this.targetToClass.has(primaryInterface.ᚕid)) {
499
- this.targetToClass.set(primaryInterface.ᚕid, new Map());
515
+ if (!this.targetToClass.has(primaryInterface.Ⲑid)) {
516
+ this.targetToClass.set(primaryInterface.Ⲑid, new Map());
500
517
  }
501
- this.targetToClass.get(primaryInterface.ᚕid)!.set(PrimaryCandidateⲐ, classId);
518
+ this.targetToClass.get(primaryInterface.Ⲑid)!.set(PrimaryCandidateⲐ, classId);
502
519
  }
503
520
  }
504
521
 
@@ -509,9 +526,9 @@ class $DependencyRegistry extends MetadataRegistry<InjectableConfig> {
509
526
  * Handle uninstalling a class
510
527
  */
511
528
  override onUninstallFinalize(cls: Class): void {
512
- const classId = cls.ᚕid;
529
+ const classId = cls.Ⲑid;
513
530
 
514
- if (!this.classToTarget.has(cls.ᚕid)) {
531
+ if (!this.classToTarget.has(cls.Ⲑid)) {
515
532
  return;
516
533
  }
517
534
 
package/src/types.ts CHANGED
@@ -77,4 +77,9 @@ export interface InjectableFactoryConfig<T = unknown> extends Core<T> {
77
77
  * List of all dependencies as function arguments
78
78
  */
79
79
  dependencies?: Dependency[];
80
- }
80
+ }
81
+
82
+ /**
83
+ * @concrete ./internal/types:AutoCreateTarget
84
+ */
85
+ export interface AutoCreate { }
@@ -1,95 +1,114 @@
1
- import { RetargettingProxy } from '@travetto/base/src/internal/proxy';
2
- import { Class, ClassInstance } from '@travetto/base';
1
+ import { RetargettingProxy, Class, ClassInstance } from '@travetto/base';
2
+ import { RootIndex } from '@travetto/manifest';
3
3
 
4
- import type { DependencyRegistry } from '../src/registry';
4
+ import type { DependencyRegistry, ResolutionType, Resolved } from '../src/registry';
5
5
  import type { ClassTarget, InjectableConfig } from '../src/types';
6
6
 
7
7
  /**
8
8
  * Wraps the Dependency Registry to support proxying instances
9
9
  */
10
- export function init($DependencyRegistry: Class<typeof DependencyRegistry>): typeof $DependencyRegistry {
10
+ class $DynamicDependencyRegistry {
11
+ #proxies = new Map<string, Map<symbol | undefined, RetargettingProxy<unknown>>>();
12
+ #registry: typeof DependencyRegistry;
13
+ #registryCreateInstance: <T>(target: ClassTarget<T>, qualifier: symbol) => Promise<T>;
14
+ #registryResolveTarget: <T>(target: ClassTarget<T>, qualifier?: symbol, resolution?: ResolutionType) => Resolved<T>;
15
+ #registryOnInstallFinalize: <T>(target: Class<T>) => InjectableConfig<T>;
16
+ #registryDestroyInstance: <T>(target: Class<T>, qualifier: symbol) => void;
17
+ #registryOnReset: () => void;
11
18
 
12
19
  /**
13
- * Extending the $DependencyRegistry class to add some functionality for watching
20
+ * Proxy the created instance
14
21
  */
15
- const Cls = class extends $DependencyRegistry {
16
- #proxies = new Map<string, Map<symbol | undefined, RetargettingProxy<unknown>>>();
22
+ proxyInstance<T>(target: ClassTarget<T>, qual: symbol | undefined, instance: T): T {
23
+ const { qualifier, id: classId } = this.#registryResolveTarget(target, qual);
24
+ let proxy: RetargettingProxy<T>;
17
25
 
18
- /**
19
- * Proxy the created instance
20
- */
21
- protected proxyInstance<T>(target: ClassTarget<T>, qual: symbol | undefined, instance: T): T {
22
- const { qualifier, id: classId } = this.resolveTarget(target, qual);
23
- let proxy: RetargettingProxy<T>;
24
-
25
- if (!this.#proxies.has(classId)) {
26
- this.#proxies.set(classId, new Map());
27
- }
26
+ if (!this.#proxies.has(classId)) {
27
+ this.#proxies.set(classId, new Map());
28
+ }
28
29
 
29
- if (!this.#proxies.get(classId)!.has(qualifier)) {
30
- proxy = new RetargettingProxy<T>(instance);
31
- this.#proxies.get(classId)!.set(qualifier, proxy);
32
- console.debug('Registering proxy', { id: target.ᚕid, qualifier: qualifier.toString() });
33
- } else {
30
+ if (!this.#proxies.get(classId)!.has(qualifier)) {
31
+ proxy = new RetargettingProxy<T>(instance);
32
+ this.#proxies.get(classId)!.set(qualifier, proxy);
33
+ console.debug('Registering proxy', { id: target.Ⲑid, qualifier: qualifier.toString() });
34
+ } else {
35
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
36
+ proxy = this.#proxies.get(classId)!.get(qualifier) as RetargettingProxy<T>;
37
+ proxy.setTarget(instance);
38
+ console.debug('Updating target', {
34
39
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
35
- proxy = this.#proxies.get(classId)!.get(qualifier) as RetargettingProxy<T>;
36
- proxy.setTarget(instance);
37
- console.debug('Updating target', {
38
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
39
- id: target.ᚕid, qualifier: qualifier.toString(), instanceType: (instance as unknown as ClassInstance<T>).constructor.name as string
40
- });
41
- }
42
-
43
- return proxy.get();
40
+ id: target.Ⲑid, qualifier: qualifier.toString(), instanceType: (instance as unknown as ClassInstance<T>).constructor.name as string
41
+ });
44
42
  }
45
43
 
46
- /**
47
- * Create instance and wrap in a proxy
48
- */
49
- protected async createInstance<T>(target: ClassTarget<T>, qualifier: symbol): Promise<T> {
50
- const instance = await super.createInstance(target, qualifier);
51
- const classId = this.resolveTarget(target, qualifier).id;
52
- // Reset as proxy instance
53
- const proxied = this.proxyInstance<T>(target, qualifier, instance);
54
- this.instances.get(classId)!.set(qualifier, proxied);
55
- return proxied;
56
- }
44
+ return proxy.get();
45
+ }
57
46
 
58
- /**
59
- * Reload proxy if in watch mode
60
- */
61
- onInstallFinalize<T>(cls: Class<T>): InjectableConfig<T> {
62
- const config = super.onInstallFinalize(cls);
63
- // If already loaded, reload
64
- const classId = cls.ᚕid;
47
+ /**
48
+ * Create instance and wrap in a proxy
49
+ */
50
+ async createInstance<T>(target: ClassTarget<T>, qualifier: symbol): Promise<T> {
51
+ const instance = await this.#registryCreateInstance(target, qualifier);
52
+ const classId = this.#registryResolveTarget(target, qualifier).id;
53
+ // Reset as proxy instance
54
+ const proxied = this.proxyInstance<T>(target, qualifier, instance);
55
+ this.#registry['instances'].get(classId)!.set(qualifier, proxied);
56
+ return proxied;
57
+ }
65
58
 
66
- if (
67
- !cls.ᚕabstract &&
68
- this.#proxies.has(classId) &&
69
- this.#proxies.get(classId)!.has(config.qualifier)
70
- ) {
71
- console.debug('Reloading on next tick');
72
- // Timing matters due to create instance being asynchronous
73
- process.nextTick(() => this.createInstance(config.target, config.qualifier));
74
- }
59
+ /**
60
+ * Reload proxy if in watch mode
61
+ */
62
+ onInstallFinalize<T>(cls: Class<T>): InjectableConfig<T> {
63
+ const config = this.#registryOnInstallFinalize(cls);
64
+ // If already loaded, reload
65
+ const classId = cls.Ⲑid;
75
66
 
76
- return config;
67
+ if (
68
+ !RootIndex.getFunctionMetadata(cls)?.abstract &&
69
+ this.#proxies.has(classId) &&
70
+ this.#proxies.get(classId)!.has(config.qualifier)
71
+ ) {
72
+ console.debug('Reloading on next tick');
73
+ // Timing matters due to create instance being asynchronous
74
+ process.nextTick(() => this.createInstance(config.target, config.qualifier));
77
75
  }
78
76
 
79
- destroyInstance(cls: Class, qualifier: symbol): void {
80
- const classId = cls.ᚕid;
81
- const proxy = this.#proxies.get(classId)!.get(qualifier);
82
- super.destroyInstance(cls, qualifier);
83
- if (proxy) {
84
- proxy.setTarget(null);
85
- }
86
- }
77
+ return config;
78
+ }
87
79
 
88
- onReset(): void {
89
- super.onReset();
90
- this.#proxies.clear();
80
+ destroyInstance(cls: Class, qualifier: symbol): void {
81
+ const classId = cls.Ⲑid;
82
+ const proxy = this.#proxies.get(classId)!.get(qualifier);
83
+ this.#registryDestroyInstance(cls, qualifier);
84
+ if (proxy) {
85
+ proxy.setTarget(null);
91
86
  }
92
- };
87
+ }
88
+
89
+ onReset(): void {
90
+ this.#registryOnReset();
91
+ this.#proxies.clear();
92
+ }
93
+
94
+ register(registry: typeof DependencyRegistry): void {
95
+ this.#registry = registry;
96
+ this.#registryCreateInstance = registry['createInstance'].bind(registry);
97
+ this.#registryResolveTarget = registry['resolveTarget'].bind(registry);
98
+ this.#registryOnInstallFinalize = registry['onInstallFinalize'].bind(registry);
99
+ this.#registryDestroyInstance = registry['destroyInstance'].bind(registry);
100
+ this.#registryOnReset = registry['onReset'].bind(registry);
101
+
102
+ this.#registry['createInstance'] = this.createInstance.bind(this);
103
+ this.#registry['destroyInstance'] = this.destroyInstance.bind(this);
104
+ this.#registry['onReset'] = this.onReset.bind(this);
105
+ this.#registry['onInstallFinalize'] = this.onInstallFinalize.bind(this);
106
+ }
107
+ }
93
108
 
94
- return Cls;
95
- }
109
+ export const DependencyRegistration = {
110
+ init(registry: typeof DependencyRegistry): void {
111
+ const dynamic = new $DynamicDependencyRegistry();
112
+ dynamic.register(registry);
113
+ }
114
+ };
@@ -2,7 +2,7 @@ import { Class, ClassInstance } from '@travetto/base';
2
2
  import { RootRegistry } from '@travetto/registry';
3
3
  import { SuiteRegistry } from '@travetto/test';
4
4
 
5
- import { DependencyRegistry } from '../src/registry';
5
+ import { DependencyRegistry } from '../../src/registry';
6
6
 
7
7
  /**
8
8
  * Registers a suite as injectable
@@ -1,7 +1,7 @@
1
- import * as ts from 'typescript';
1
+ import ts from 'typescript';
2
2
 
3
3
  import {
4
- TransformerState, DecoratorMeta, OnClass, OnProperty, OnStaticMethod, DecoratorUtil, LiteralUtil, TransformerId, OnSetter
4
+ TransformerState, DecoratorMeta, OnClass, OnProperty, OnStaticMethod, DecoratorUtil, LiteralUtil, OnSetter
5
5
  } from '@travetto/transformer';
6
6
 
7
7
  const INJECTABLE_MOD = '@travetto/di/src/decorator';
@@ -11,8 +11,6 @@ const INJECTABLE_MOD = '@travetto/di/src/decorator';
11
11
  */
12
12
  export class InjectableTransformer {
13
13
 
14
- static [TransformerId] = '@trv:di';
15
-
16
14
  /**
17
15
  * Handle a specific declaration param/property
18
16
  */
@@ -27,15 +25,14 @@ export class InjectableTransformer {
27
25
  const callExpr = existing?.expression as ts.CallExpression;
28
26
  const args: ts.Expression[] = [...(callExpr?.arguments ?? [])];
29
27
 
30
- let optional = undefined;
28
+ let optional: ts.Expression | undefined = undefined;
31
29
  if (optional === undefined && !!param.questionToken) {
32
30
  optional = state.fromLiteral(true);
33
31
  }
34
32
 
35
- args.unshift(state.fromLiteral({
36
- target: state.getOrImport(state.resolveExternalType(ts.isSetAccessorDeclaration(param) ? param.parameters[0] : param)),
37
- optional
38
- }));
33
+ const keyParam = ts.isSetAccessorDeclaration(param) ? param.parameters[0] : param;
34
+ const target = state.getOrImport(state.resolveExternalType(keyParam));
35
+ args.unshift(state.fromLiteral({ target, optional }));
39
36
 
40
37
  return args;
41
38
  }
@@ -108,12 +105,14 @@ export class InjectableTransformer {
108
105
  static registerInjectSetter(state: TransformerState, node: ts.SetAccessorDeclaration, dm?: DecoratorMeta): typeof node {
109
106
  const decl = state.findDecorator(this, node, 'Inject', INJECTABLE_MOD);
110
107
 
108
+ const modifiers = DecoratorUtil.spliceDecorators(node, decl, [
109
+ state.createDecorator(INJECTABLE_MOD, 'Inject', ...this.processDeclaration(state, node)),
110
+ ], 0);
111
+
111
112
  // Doing decls
112
113
  return state.factory.updateSetAccessorDeclaration(
113
114
  node,
114
- DecoratorUtil.spliceDecorators(node, decl, [
115
- state.createDecorator(INJECTABLE_MOD, 'Inject', ...this.processDeclaration(state, node)),
116
- ], 0),
115
+ modifiers,
117
116
  node.name,
118
117
  node.parameters,
119
118
  node.body
package/support/invoke.ts DELETED
@@ -1,16 +0,0 @@
1
- import { EnvInit } from '@travetto/base/bin/init';
2
-
3
- export async function invoke(...[mod, cls, method, qualifier]: (string | undefined)[]): Promise<unknown> {
4
- EnvInit.init();
5
- await (await import('@travetto/base')).PhaseManager.run('init');
6
- const inst = await (await import('../src/registry')).DependencyRegistry
7
- .getInstance(
8
- (await import(mod!))[cls!],
9
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
10
- qualifier ? Symbol.for(qualifier) : qualifier as undefined
11
- );
12
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
13
- return await (inst as Record<string, () => Promise<unknown>>)[method!]();
14
- }
15
-
16
- invoke(...process.argv.slice(2));
File without changes