@travetto/di 7.0.0-rc.0 → 7.0.0-rc.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -135,7 +135,7 @@ The [@Inject](https://github.com/travetto/travetto/tree/main/module/di/src/decor
135
135
  **Code: Example Injectable with dependencies as Inject fields**
136
136
  ```typescript
137
137
  import { Injectable, Inject } from '@travetto/di';
138
- import { DependentService } from './dep.ts';
138
+ import { DependentService } from './dependency.ts';
139
139
 
140
140
  @Injectable()
141
141
  class CustomService {
@@ -153,15 +153,15 @@ The [@Injectable](https://github.com/travetto/travetto/tree/main/module/di/src/d
153
153
  **Code: Example Injectable with dependencies in constructor**
154
154
  ```typescript
155
155
  import { Injectable } from '@travetto/di';
156
- import { DependentService } from './dep.ts';
156
+ import { DependentService } from './dependency.ts';
157
157
 
158
158
  @Injectable()
159
159
  class CustomService {
160
160
 
161
161
  dependentService: DependentService;
162
162
 
163
- constructor(svc: DependentService) {
164
- this.dependentService = svc;
163
+ constructor(service: DependentService) {
164
+ this.dependentService = service;
165
165
  }
166
166
 
167
167
  async coolOperation() {
@@ -176,7 +176,7 @@ Via [@InjectableFactory](https://github.com/travetto/travetto/tree/main/module/d
176
176
  ```typescript
177
177
  import { InjectableFactory } from '@travetto/di';
178
178
 
179
- import { DependentService, CustomService } from './dep.ts';
179
+ import { DependentService, CustomService } from './dependency.ts';
180
180
 
181
181
  class Config {
182
182
  @InjectableFactory()
@@ -268,7 +268,7 @@ class Service {
268
268
  ```
269
269
 
270
270
  ## Manual Invocation
271
- Some times you will need to lookup a dependency dynamically, or you want to control the injection process at a more granular level. To achieve that you will need to directly access the [DependencyRegistryIndex](https://github.com/travetto/travetto/tree/main/module/di/src/registry/registry-index.ts#L21). The registry allows for requesting a dependency by class reference:
271
+ Some times you will need to lookup a dependency dynamically, or you want to control the injection process at a more granular level. To achieve that you will need to directly access the [DependencyRegistryIndex](https://github.com/travetto/travetto/tree/main/module/di/src/registry/registry-index.ts#L19). The registry allows for requesting a dependency by class reference:
272
272
 
273
273
  **Code: Example of Manual Lookup**
274
274
  ```typescript
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@travetto/di",
3
- "version": "7.0.0-rc.0",
3
+ "version": "7.0.0-rc.2",
4
4
  "description": "Dependency registration/management and injection support.",
5
5
  "keywords": [
6
6
  "ast-transformations",
@@ -27,10 +27,10 @@
27
27
  "directory": "module/di"
28
28
  },
29
29
  "dependencies": {
30
- "@travetto/registry": "^7.0.0-rc.0"
30
+ "@travetto/registry": "^7.0.0-rc.2"
31
31
  },
32
32
  "peerDependencies": {
33
- "@travetto/transformer": "^7.0.0-rc.0"
33
+ "@travetto/transformer": "^7.0.0-rc.2"
34
34
  },
35
35
  "peerDependenciesMeta": {
36
36
  "@travetto/transformer": {
package/src/decorator.ts CHANGED
@@ -4,17 +4,17 @@ import { CONSTRUCTOR_PROPERTY } from '@travetto/schema';
4
4
  import { InjectableCandidate, ResolutionType } from './types.ts';
5
5
  import { DependencyRegistryIndex } from './registry/registry-index.ts';
6
6
 
7
- const fromArg = <T extends { qualifier?: symbol }>(arg?: T | symbol): T =>
8
- typeof arg === 'symbol' ? castTo({ qualifier: arg }) : (arg ?? castTo<T>({}));
7
+ const fromInput = <T extends { qualifier?: symbol }>(input?: T | symbol): T =>
8
+ typeof input === 'symbol' ? castTo({ qualifier: input }) : (input ?? castTo<T>({}));
9
9
 
10
10
  /**
11
11
  * Indicate that a class is able to be injected
12
12
  * @augments `@travetto/schema:Schema`
13
13
  * @kind decorator
14
14
  */
15
- export function Injectable(config?: Partial<InjectableCandidate> | symbol) {
15
+ export function Injectable(input?: Partial<InjectableCandidate> | symbol) {
16
16
  return <T extends Class>(cls: T): void => {
17
- DependencyRegistryIndex.getForRegister(cls).registerClass(fromArg(config));
17
+ DependencyRegistryIndex.getForRegister(cls).registerClass(fromInput(input));
18
18
  };
19
19
  }
20
20
 
@@ -26,15 +26,15 @@ export type InjectConfig = { qualifier?: symbol, resolution?: ResolutionType };
26
26
  * @augments `@travetto/schema:Input`
27
27
  * @kind decorator
28
28
  */
29
- export function Inject(config?: InjectConfig | symbol) {
30
- return (instanceOrCls: Class | ClassInstance, property?: string | symbol, idx?: number | PropertyDescriptor): void => {
31
- const cfg = fromArg(config);
29
+ export function Inject(input?: InjectConfig | symbol) {
30
+ return (instanceOrCls: Class | ClassInstance, property?: string, idx?: number | PropertyDescriptor): void => {
31
+ const config = fromInput(input);
32
32
  const cls = getClass(instanceOrCls);
33
33
  const propertyKey = property ?? CONSTRUCTOR_PROPERTY;
34
34
  if (typeof idx !== 'number') {
35
- DependencyRegistryIndex.registerFieldMetadata(cls, propertyKey, cfg);
35
+ DependencyRegistryIndex.registerFieldMetadata(cls, propertyKey, config);
36
36
  } else {
37
- DependencyRegistryIndex.registerParameterMetadata(cls, propertyKey, idx, cfg);
37
+ DependencyRegistryIndex.registerParameterMetadata(cls, propertyKey, idx, config);
38
38
  }
39
39
  };
40
40
  }
@@ -44,9 +44,9 @@ export function Inject(config?: InjectConfig | symbol) {
44
44
  * @augments `@travetto/schema:Method`
45
45
  * @kind decorator
46
46
  */
47
- export function InjectableFactory(config?: Partial<InjectableCandidate> | symbol) {
48
- return <T extends Class>(cls: T, property: string | symbol, descriptor: TypedPropertyDescriptor<(...args: Any[]) => Any>): void => {
49
- DependencyRegistryIndex.getForRegister(cls).registerFactory(property, fromArg(config), {
47
+ export function InjectableFactory(input?: Partial<InjectableCandidate> | symbol) {
48
+ return <T extends Class>(cls: T, property: string, descriptor: TypedPropertyDescriptor<(...args: Any[]) => Any>): void => {
49
+ DependencyRegistryIndex.getForRegister(cls).registerFactory(property, fromInput(input), {
50
50
  factory: (...params: unknown[]) => descriptor.value!.apply(cls, params),
51
51
  });
52
52
  };
@@ -1,24 +1,24 @@
1
1
  import { RegistryAdapter } from '@travetto/registry';
2
- import { Class, classConstruct, describeFunction, getAllEntries, safeAssign } from '@travetto/runtime';
2
+ import { Class, classConstruct, describeFunction, safeAssign } from '@travetto/runtime';
3
3
  import { CONSTRUCTOR_PROPERTY, SchemaRegistryIndex } from '@travetto/schema';
4
4
 
5
5
  import { InjectableConfig, getDefaultQualifier, InjectableCandidate } from '../types';
6
6
 
7
- function combineInjectableCandidates<T extends InjectableCandidate>(base: T, ...override: Partial<T>[]): typeof base {
8
- for (const o of override) {
9
- safeAssign(base, o);
7
+ function combineInjectableCandidates<T extends InjectableCandidate>(base: T, ...overrides: Partial<T>[]): typeof base {
8
+ for (const override of overrides) {
9
+ safeAssign(base, override);
10
10
  }
11
11
  return base;
12
12
  }
13
13
 
14
- function combineClasses<T extends InjectableConfig>(base: T, ...override: Partial<T>[]): typeof base {
15
- for (const o of override) {
14
+ function combineClasses<T extends InjectableConfig>(base: T, ...overrides: Partial<T>[]): typeof base {
15
+ for (const override of overrides) {
16
16
  Object.assign(base, {
17
17
  ...base,
18
- ...o,
18
+ ...override,
19
19
  candidates: {
20
20
  ...base.candidates,
21
- ...o.candidates,
21
+ ...override.candidates,
22
22
  }
23
23
  });
24
24
  }
@@ -38,7 +38,7 @@ export class DependencyRegistryAdapter implements RegistryAdapter<InjectableConf
38
38
  return combineClasses(this.#config, ...data);
39
39
  }
40
40
 
41
- registerFactory(method: string | symbol, ...data: Partial<InjectableCandidate<unknown>>[]): InjectableCandidate {
41
+ registerFactory(method: string, ...data: Partial<InjectableCandidate<unknown>>[]): InjectableCandidate {
42
42
  const { candidates } = this.register();
43
43
  candidates[method] ??= {
44
44
  class: this.#cls,
@@ -62,17 +62,16 @@ export class DependencyRegistryAdapter implements RegistryAdapter<InjectableConf
62
62
  }
63
63
 
64
64
  finalize(): void {
65
- for (const [k] of getAllEntries(this.#config.candidates)) {
66
- const v = this.#config.candidates[k];
67
- const candidateType = SchemaRegistryIndex.get(v.class).getMethodReturnType(k);
68
- v.candidateType = candidateType;
69
- v.qualifier ??= getDefaultQualifier(candidateType);
65
+ for (const method of Object.keys(this.#config.candidates)) {
66
+ const candidate = this.#config.candidates[method];
67
+ const candidateType = SchemaRegistryIndex.get(candidate.class).getMethodReturnType(method);
68
+ candidate.candidateType = candidateType;
69
+ candidate.qualifier ??= getDefaultQualifier(candidateType);
70
70
  }
71
71
  }
72
72
 
73
73
  getCandidateConfigs(): InjectableCandidate[] {
74
- const entries = getAllEntries(this.#config.candidates).map(([_, item]) => item);
75
- return entries
74
+ return Object.values(this.#config.candidates)
76
75
  .filter(item => (item.enabled ?? true) === true || (typeof item.enabled === 'function' && item.enabled()))
77
76
  .filter(item => item.method !== CONSTRUCTOR_PROPERTY || !describeFunction(item.candidateType)?.abstract);
78
77
  }
@@ -9,11 +9,9 @@ import { DependencyRegistryResolver } from './registry-resolver';
9
9
 
10
10
  const MetadataSymbol = Symbol();
11
11
 
12
- type ClassId = string;
13
12
  const hasPostConstruct = hasFunction<{ postConstruct: () => Promise<unknown> }>('postConstruct');
14
13
  const hasPreDestroy = hasFunction<{ preDestroy: () => Promise<unknown> }>('preDestroy');
15
14
 
16
-
17
15
  function readMetadata(item: { metadata?: Record<symbol, unknown> }): Dependency | undefined {
18
16
  return castTo<Dependency | undefined>(item.metadata?.[MetadataSymbol]);
19
17
  }
@@ -35,15 +33,15 @@ export class DependencyRegistryIndex implements RegistryIndex {
35
33
  }
36
34
 
37
35
  static getCandidateTypes<T>(candidateType: Class<T>): Class<T>[] {
38
- return this.#instance.getCandidates(candidateType).map(c => c.candidateType);
36
+ return this.#instance.getCandidates(candidateType).map(candidate => candidate.candidateType);
39
37
  }
40
38
 
41
- static getInstances<T>(candidateType: Class<T>, predicate?: (cfg: InjectableCandidate<T>) => boolean): Promise<T[]> {
39
+ static getInstances<T>(candidateType: Class<T>, predicate?: (config: InjectableCandidate<T>) => boolean): Promise<T[]> {
42
40
  return this.#instance.getInstances<T>(candidateType, predicate);
43
41
  }
44
42
 
45
- static injectFields<T extends { constructor: Class<T> }>(o: T, cls = o.constructor): Promise<T> {
46
- return this.#instance.injectFields(cls, o, cls);
43
+ static injectFields<T extends { constructor: Class<T> }>(item: T, cls = item.constructor): Promise<T> {
44
+ return this.#instance.injectFields(cls, item, cls);
47
45
  }
48
46
 
49
47
  static getOptional(cls: Class): InjectableConfig | undefined {
@@ -54,33 +52,33 @@ export class DependencyRegistryIndex implements RegistryIndex {
54
52
  SchemaRegistryIndex.getForRegister(cls).registerMetadata<InjectableClassMetadata>(MetadataSymbol, metadata);
55
53
  }
56
54
 
57
- static registerParameterMetadata(cls: Class, method: string | symbol, index: number, metadata: Dependency): void {
55
+ static registerParameterMetadata(cls: Class, method: string, index: number, metadata: Dependency): void {
58
56
  SchemaRegistryIndex.getForRegister(cls).registerParameterMetadata(method, index, MetadataSymbol, metadata);
59
57
  }
60
58
 
61
- static registerFieldMetadata(cls: Class, field: string | symbol, metadata: Dependency): void {
59
+ static registerFieldMetadata(cls: Class, field: string, metadata: Dependency): void {
62
60
  SchemaRegistryIndex.getForRegister(cls).registerFieldMetadata(field, MetadataSymbol, metadata);
63
61
  }
64
62
 
65
- #instances = new Map<ClassId, Map<symbol, unknown>>();
66
- #instancePromises = new Map<ClassId, Map<symbol, Promise<unknown>>>();
67
- #proxies = new Map<ClassId, Map<symbol | undefined, RetargettingProxy<unknown>>>();
63
+ #proxies = new Map<string, Map<symbol | undefined, RetargettingProxy<unknown>>>();
64
+ #instances = new Map<Class, Map<symbol, unknown>>();
65
+ #instancePromises = new Map<Class, Map<symbol, Promise<unknown>>>();
68
66
  #resolver = new DependencyRegistryResolver();
69
67
 
70
68
  #proxyInstance<T>(target: Class<unknown>, qualifier: symbol, instance: T): T {
71
- const classId = target.Ⲑid;
72
69
  let proxy: RetargettingProxy<unknown>;
70
+ const targetId = target.Ⲑid;
73
71
 
74
- if (!this.#proxies.has(classId)) {
75
- this.#proxies.set(classId, new Map());
72
+ if (!this.#proxies.has(targetId)) {
73
+ this.#proxies.set(targetId, new Map());
76
74
  }
77
75
 
78
- if (!this.#proxies.get(classId)!.has(qualifier)) {
76
+ if (!this.#proxies.get(targetId)!.has(qualifier)) {
79
77
  proxy = new RetargettingProxy(instance);
80
- this.#proxies.get(classId)!.set(qualifier, proxy);
78
+ this.#proxies.get(targetId)!.set(qualifier, proxy);
81
79
  console.debug('Registering proxy', { id: target.Ⲑid, qualifier: qualifier.toString() });
82
80
  } else {
83
- proxy = this.#proxies.get(classId)!.get(qualifier)!;
81
+ proxy = this.#proxies.get(targetId)!.get(qualifier)!;
84
82
  proxy.setTarget(instance);
85
83
  console.debug('Updating target', {
86
84
  id: target.Ⲑid, qualifier: qualifier.toString(), instanceType: target.name
@@ -90,53 +88,47 @@ export class DependencyRegistryIndex implements RegistryIndex {
90
88
  return proxy.get();
91
89
  }
92
90
 
93
- #addClass(cls: Class): void {
91
+ #addClass(cls: Class, forceCreate: boolean = false): void {
94
92
  const adapter = this.store.get(cls);
95
93
 
96
94
  for (const config of adapter.getCandidateConfigs()) {
97
95
  const parentClass = getParentClass(config.candidateType);
98
96
  const parentConfig = parentClass ? this.store.getOptional(parentClass) : undefined;
99
97
  const hasParentBase = (parentConfig || (parentClass && !!describeFunction(parentClass)?.abstract));
100
- const baseParentId = hasParentBase ? parentClass?.Ⲑid : undefined;
101
- this.#resolver.registerClass(config, baseParentId);
102
- if (config.autoInject) {
98
+ const baseParent = hasParentBase ? parentClass : undefined;
99
+ this.#resolver.registerClass(config, baseParent);
100
+ if (config.autoInject || forceCreate) {
103
101
  // Don't wait
104
- this.getInstance(config.candidateType, config.qualifier);
102
+ Util.queueMacroTask().then(() => {
103
+ this.getInstance(config.candidateType, config.qualifier);
104
+ });
105
105
  }
106
106
  }
107
107
  }
108
108
 
109
- #changedClass(cls: Class, _prev: Class): void {
110
- // Reload instances
111
- for (const qualifier of this.#proxies.get(cls.Ⲑid)?.keys() ?? []) {
112
- // Timing matters due to create instance being asynchronous
113
- Util.queueMacroTask().then(() => { this.getInstance(cls, qualifier); });
114
- }
115
- }
116
-
117
109
  #removeClass(cls: Class): void {
118
- const classId = cls.Ⲑid;
119
-
120
- if (this.#instances.has(classId)) {
110
+ if (this.#instances.has(cls)) {
121
111
  for (const [qualifier, config] of this.#resolver.getContainerEntries(cls)) {
122
- this.destroyInstance(config.candidateType, qualifier);
112
+ try {
113
+ this.destroyInstance(config.candidateType, qualifier);
114
+ } catch { }
123
115
  }
124
116
  }
125
117
  }
126
118
 
127
119
 
128
- async #resolveDependencyValue(dependency: Dependency, input: SchemaFieldConfig | SchemaParameterConfig, src: Class): Promise<unknown> {
120
+ async #resolveDependencyValue(dependency: Dependency, input: SchemaFieldConfig | SchemaParameterConfig, cls: Class): Promise<unknown> {
129
121
  try {
130
122
  const target = dependency.target ?? input.type;
131
123
  return await this.getInstance(target, dependency.qualifier, dependency.resolution);
132
- } catch (err) {
133
- if (input.required?.active === false && err instanceof InjectionError && err.category === 'notfound') {
124
+ } catch (error) {
125
+ if (input.required?.active === false && error instanceof InjectionError && error.category === 'notfound') {
134
126
  return undefined;
135
127
  } else {
136
- if (err && err instanceof Error) {
137
- err.message = `${err.message} via=${src.Ⲑid}[${input.name?.toString() ?? 'constructor'}]`;
128
+ if (error && error instanceof Error) {
129
+ error.message = `${error.message} via=${cls.Ⲑid}[${input.name?.toString() ?? 'constructor'}]`;
138
130
  }
139
- throw err;
131
+ throw error;
140
132
  }
141
133
  }
142
134
  }
@@ -148,13 +140,12 @@ export class DependencyRegistryIndex implements RegistryIndex {
148
140
  }
149
141
 
150
142
  process(events: ChangeEvent<Class>[]): void {
151
- for (const ev of events) {
152
- if (ev.type === 'added') {
153
- this.#addClass(ev.curr);
154
- } else if (ev.type === 'removing') {
155
- this.#removeClass(ev.prev);
156
- } else if (ev.type === 'changed') {
157
- this.#changedClass(ev.curr, ev.prev);
143
+ for (const event of events) {
144
+ if ('previous' in event) {
145
+ this.#removeClass(event.previous);
146
+ }
147
+ if ('current' in event) {
148
+ this.#addClass(event.current, 'previous' in event);
158
149
  }
159
150
  }
160
151
  }
@@ -163,15 +154,15 @@ export class DependencyRegistryIndex implements RegistryIndex {
163
154
  * Get all available candidates for a given type
164
155
  */
165
156
  getCandidates<T>(candidateType: Class<T>): InjectableCandidate<T>[] {
166
- return this.#resolver.getCandidateEntries(candidateType).map(([_, x]) => castTo<InjectableCandidate<T>>(x));
157
+ return this.#resolver.getCandidateEntries(candidateType).map(([_, candidate]) => castTo<InjectableCandidate<T>>(candidate));
167
158
  }
168
159
 
169
160
  /**
170
161
  * Get candidate instances by target type, with an optional filter
171
162
  */
172
- getInstances<T>(candidateType: Class<T>, predicate?: (cfg: InjectableCandidate<T>) => boolean): Promise<T[]> {
173
- const inputs = this.getCandidates<T>(candidateType).filter(x => !predicate || predicate(x));
174
- return Promise.all(inputs.map(l => this.getInstance<T>(l.class, l.qualifier)));
163
+ getInstances<T>(candidateType: Class<T>, predicate?: (config: InjectableCandidate<T>) => boolean): Promise<T[]> {
164
+ const inputs = this.getCandidates<T>(candidateType).filter(candidate => !predicate || predicate(candidate));
165
+ return Promise.all(inputs.map(candidate => this.getInstance<T>(candidate.class, candidate.qualifier)));
175
166
  }
176
167
 
177
168
  /**
@@ -179,7 +170,7 @@ export class DependencyRegistryIndex implements RegistryIndex {
179
170
  */
180
171
  async fetchDependencyParameters<T>(candidate: InjectableCandidate<T>): Promise<unknown[]> {
181
172
  const inputs = SchemaRegistryIndex.has(candidate.class) ?
182
- SchemaRegistryIndex.getMethodConfig(candidate.class, candidate.method).parameters : [];
173
+ SchemaRegistryIndex.get(candidate.class).getMethod(candidate.method).parameters : [];
183
174
 
184
175
  const promises = inputs
185
176
  .map(input => this.#resolveDependencyValue(readMetadata(input) ?? {}, input, candidate.class));
@@ -191,16 +182,16 @@ export class DependencyRegistryIndex implements RegistryIndex {
191
182
  * Retrieve mapped dependencies
192
183
  */
193
184
  async injectFields<T>(candidateType: Class, instance: T, srcClass: Class): Promise<T> {
194
- const inputs = SchemaRegistryIndex.has(candidateType) ? SchemaRegistryIndex.getFieldMap(candidateType) : {};
185
+ const inputs = SchemaRegistryIndex.getOptional(candidateType)?.getFields() ?? {};
195
186
 
196
187
  const promises = TypedObject.entries(inputs)
197
- .filter(([k, input]) => readMetadata(input) !== undefined && (input.access !== 'readonly' && instance[castKey(k)] === undefined))
198
- .map(async ([k, input]) => [k, await this.#resolveDependencyValue(readMetadata(input) ?? {}, input, srcClass)] as const);
188
+ .filter(([key, input]) => readMetadata(input) !== undefined && (input.access !== 'readonly' && instance[castKey(key)] === undefined))
189
+ .map(async ([key, input]) => [key, await this.#resolveDependencyValue(readMetadata(input) ?? {}, input, srcClass)] as const);
199
190
 
200
191
  const pairs = await Promise.all(promises);
201
192
 
202
- for (const [k, v] of pairs) {
203
- instance[castKey(k)] = castTo(v);
193
+ for (const [key, value] of pairs) {
194
+ instance[castKey(key)] = castTo(value);
204
195
  }
205
196
  return instance;
206
197
  }
@@ -226,8 +217,8 @@ export class DependencyRegistryIndex implements RegistryIndex {
226
217
  SchemaRegistryIndex.get(targetType).getMetadata<InjectableClassMetadata>(MetadataSymbol) : undefined;
227
218
 
228
219
  // Run post constructors
229
- for (const op of Object.values(metadata?.postConstruct ?? {})) {
230
- await op(inst);
220
+ for (const operation of Object.values(metadata?.postConstruct ?? {})) {
221
+ await operation(inst);
231
222
  }
232
223
 
233
224
  // Proxy if necessary
@@ -242,27 +233,27 @@ export class DependencyRegistryIndex implements RegistryIndex {
242
233
  throw new AppError('Unable to get instance when target is undefined');
243
234
  }
244
235
 
245
- const { targetId, qualifier } = this.#resolver.resolveCandidate(candidateType, requestedQualifier, resolution);
236
+ const { target, qualifier } = this.#resolver.resolveCandidate(candidateType, requestedQualifier, resolution);
246
237
 
247
- if (!this.#instances.has(targetId)) {
248
- this.#instances.set(targetId, new Map());
249
- this.#instancePromises.set(targetId, new Map());
238
+ if (!this.#instances.has(target)) {
239
+ this.#instances.set(target, new Map());
240
+ this.#instancePromises.set(target, new Map());
250
241
  }
251
242
 
252
- if (this.#instancePromises.get(targetId)!.has(qualifier)) {
253
- return castTo(this.#instancePromises.get(targetId)!.get(qualifier));
243
+ if (this.#instancePromises.get(target)!.has(qualifier)) {
244
+ return castTo(this.#instancePromises.get(target)!.get(qualifier));
254
245
  }
255
246
 
256
247
  const instancePromise = this.construct(candidateType, qualifier);
257
- this.#instancePromises.get(targetId)!.set(qualifier, instancePromise);
248
+ this.#instancePromises.get(target)!.set(qualifier, instancePromise);
258
249
  try {
259
250
  const instance = await instancePromise;
260
- this.#instances.get(targetId)!.set(qualifier, instance);
251
+ this.#instances.get(target)!.set(qualifier, instance);
261
252
  return instance;
262
- } catch (err) {
253
+ } catch (error) {
263
254
  // Clear it out, don't save failed constructions
264
- this.#instancePromises.get(targetId)!.delete(qualifier);
265
- throw err;
255
+ this.#instancePromises.get(target)!.delete(qualifier);
256
+ throw error;
266
257
  }
267
258
  }
268
259
 
@@ -270,19 +261,19 @@ export class DependencyRegistryIndex implements RegistryIndex {
270
261
  * Destroy an instance
271
262
  */
272
263
  destroyInstance(candidateType: Class, requestedQualifier: symbol): void {
273
- const { targetId, qualifier } = this.#resolver.resolveCandidate(candidateType, requestedQualifier);
264
+ const { target, qualifier } = this.#resolver.resolveCandidate(candidateType, requestedQualifier);
274
265
 
275
- const activeInstance = this.#instances.get(targetId)!.get(qualifier);
266
+ const activeInstance = this.#instances.get(target)?.get(qualifier);
276
267
  if (hasPreDestroy(activeInstance)) {
277
268
  activeInstance.preDestroy();
278
269
  }
279
270
 
280
271
  this.#resolver.removeClass(candidateType, qualifier);
281
- this.#instances.get(targetId)!.delete(qualifier);
282
- this.#instancePromises.get(targetId)!.delete(qualifier);
272
+ this.#instances.get(target)?.delete(qualifier);
273
+ this.#instancePromises.get(target)?.delete(qualifier);
283
274
 
284
275
  // May not exist
285
- this.#proxies.get(targetId)?.get(qualifier)?.setTarget(null);
286
- console.debug('On uninstall', { id: targetId, qualifier: qualifier.toString(), classId: targetId });
276
+ this.#proxies.get(target.Ⲑid)?.get(qualifier)?.setTarget(null);
277
+ console.debug('On uninstall', { id: target, qualifier: qualifier.toString(), classId: target });
287
278
  }
288
279
  }
@@ -4,14 +4,13 @@ import { castTo, Class } from '@travetto/runtime';
4
4
  import { getDefaultQualifier, InjectableCandidate, PrimaryCandidateSymbol, ResolutionType } from '../types';
5
5
  import { InjectionError } from '../error';
6
6
 
7
- type Resolved<T> = { candidate: InjectableCandidate<T>, qualifier: symbol, targetId: string };
8
- type ClassId = string;
7
+ type Resolved<T> = { candidate: InjectableCandidate<T>, qualifier: symbol, target: Class };
9
8
 
10
- function setInMap<T>(map: Map<string, Map<typeof key, T>>, src: string, key: symbol | string, dest: T): void {
11
- if (!map.has(src)) {
12
- map.set(src, new Map());
9
+ function setInMap<T>(map: Map<Class, Map<typeof key, T>>, cls: Class, key: symbol | string, dest: T): void {
10
+ if (!map.has(cls)) {
11
+ map.set(cls, new Map());
13
12
  }
14
- map.get(src)!.set(key, dest);
13
+ map.get(cls)!.set(key, dest);
15
14
  }
16
15
 
17
16
  export class DependencyRegistryResolver {
@@ -21,17 +20,17 @@ export class DependencyRegistryResolver {
21
20
  #defaultSymbols = new Set<symbol>();
22
21
 
23
22
  /**
24
- * Maps from the requested type id to the candidates
23
+ * Maps from the requested type to the candidates
25
24
  */
26
- #byCandidateType = new Map<ClassId, Map<symbol, InjectableCandidate>>();
25
+ #byCandidateType = new Map<Class, Map<symbol, InjectableCandidate>>();
27
26
 
28
27
  /**
29
- * Maps from inbound class id (file) to the candidates
28
+ * Maps from inbound class file) to the candidates
30
29
  */
31
- #byContainerType = new Map<ClassId, Map<symbol, InjectableCandidate>>();
30
+ #byContainerType = new Map<Class, Map<symbol, InjectableCandidate>>();
32
31
 
33
32
  #resolveQualifier<T>(type: Class<T>, resolution?: ResolutionType): symbol | undefined {
34
- const qualifiers = this.#byCandidateType.get(type.Ⲑid) ?? new Map<symbol, InjectableCandidate>();
33
+ const qualifiers = this.#byCandidateType.get(type) ?? new Map<symbol, InjectableCandidate>();
35
34
 
36
35
  const resolved = [...qualifiers.keys()];
37
36
 
@@ -39,15 +38,17 @@ export class DependencyRegistryResolver {
39
38
  if (qualifiers.has(PrimaryCandidateSymbol)) {
40
39
  return PrimaryCandidateSymbol;
41
40
  } else {
42
- const filtered = resolved.filter(x => !!x).filter(x => this.#defaultSymbols.has(x));
41
+ const filtered = resolved
42
+ .filter(qualifier => !!qualifier)
43
+ .filter(qualifier => this.#defaultSymbols.has(qualifier));
43
44
  // If there is only one default symbol
44
45
  if (filtered.length === 1) {
45
46
  return filtered[0];
46
47
  } else if (filtered.length > 1) {
47
48
  // If dealing with sub types, prioritize exact matches
48
49
  const exact = this.getCandidateEntries(type)
49
- .map(([_, x]) => x)
50
- .filter(x => x.candidateType === type);
50
+ .map(([_, candidate]) => candidate)
51
+ .filter(candidate => candidate.candidateType === type);
51
52
 
52
53
  if (exact.length === 1) {
53
54
  return exact[0].qualifier;
@@ -65,13 +66,11 @@ export class DependencyRegistryResolver {
65
66
  /**
66
67
  * Register a class with the dependency resolver
67
68
  */
68
- registerClass(config: InjectableCandidate, baseParentId?: string): void {
69
+ registerClass(config: InjectableCandidate, baseParent?: Class): void {
69
70
  const candidateType = config.candidateType;
70
- const candidateClassId = candidateType.Ⲑid;
71
71
  const target = config.target ?? candidateType;
72
72
 
73
- const targetClassId = target.Ⲑid;
74
- const isSelfTarget = candidateClassId === targetClassId;
73
+ const isSelfTarget = target === candidateType;
75
74
  const qualifier = config.qualifier ?? getDefaultQualifier(candidateType);
76
75
 
77
76
  // Record qualifier if its the default for the class
@@ -80,42 +79,41 @@ export class DependencyRegistryResolver {
80
79
  }
81
80
 
82
81
  // Register inbound config by method and class
83
- setInMap(this.#byContainerType, config.class.Ⲑid, config.method, config);
82
+ setInMap(this.#byContainerType, config.class, config.method, config);
84
83
 
85
- setInMap(this.#byCandidateType, targetClassId, qualifier, config);
86
- setInMap(this.#byCandidateType, candidateClassId, qualifier, config);
84
+ setInMap(this.#byCandidateType, target, qualifier, config);
85
+ setInMap(this.#byCandidateType, candidateType, qualifier, config);
87
86
 
88
87
  // Track interface aliases as targets
89
88
  const interfaces = SchemaRegistryIndex.has(candidateType) ?
90
89
  SchemaRegistryIndex.get(candidateType).get().interfaces : [];
91
90
 
92
- for (const { Ⲑid: interfaceId } of interfaces) {
93
- setInMap(this.#byCandidateType, interfaceId, qualifier, config);
91
+ for (const type of interfaces) {
92
+ setInMap(this.#byCandidateType, type, qualifier, config);
94
93
  }
95
94
 
96
95
  // If targeting self (default @Injectable behavior)
97
- if (isSelfTarget && baseParentId) {
98
- setInMap(this.#byCandidateType, baseParentId, qualifier, config);
96
+ if (isSelfTarget && baseParent) {
97
+ setInMap(this.#byCandidateType, baseParent, qualifier, config);
99
98
  }
100
99
 
101
100
  // Registry primary candidates
102
101
  if (config.primary) {
103
- if (baseParentId) {
104
- setInMap(this.#byCandidateType, baseParentId, PrimaryCandidateSymbol, config);
102
+ if (baseParent) {
103
+ setInMap(this.#byCandidateType, baseParent, PrimaryCandidateSymbol, config);
105
104
  }
106
105
 
107
106
  // Register primary for self
108
- setInMap(this.#byCandidateType, targetClassId, PrimaryCandidateSymbol, config);
107
+ setInMap(this.#byCandidateType, target, PrimaryCandidateSymbol, config);
109
108
 
110
109
  // Register primary if only one interface provided and no parent config
111
- if (interfaces.length === 1 && (!baseParentId || !this.#byContainerType.has(baseParentId))) {
110
+ if (interfaces.length === 1 && (!baseParent || !this.#byContainerType.has(baseParent))) {
112
111
  const [primaryInterface] = interfaces;
113
- const primaryClassId = primaryInterface.Ⲑid;
114
- setInMap(this.#byCandidateType, primaryClassId, PrimaryCandidateSymbol, config);
112
+ setInMap(this.#byCandidateType, primaryInterface, PrimaryCandidateSymbol, config);
115
113
  } else if (isSelfTarget) {
116
114
  // Register primary for all interfaces if self targeting
117
- for (const { Ⲑid: interfaceId } of interfaces) {
118
- setInMap(this.#byCandidateType, interfaceId, PrimaryCandidateSymbol, config);
115
+ for (const type of interfaces) {
116
+ setInMap(this.#byCandidateType, type, PrimaryCandidateSymbol, config);
119
117
  }
120
118
  }
121
119
  }
@@ -127,7 +125,7 @@ export class DependencyRegistryResolver {
127
125
  * @param qualifier
128
126
  */
129
127
  resolveCandidate<T>(candidateType: Class<T>, qualifier?: symbol, resolution?: ResolutionType): Resolved<T> {
130
- const qualifiers = this.#byCandidateType.get(candidateType.Ⲑid) ?? new Map<symbol, InjectableCandidate>();
128
+ const qualifiers = this.#byCandidateType.get(candidateType) ?? new Map<symbol, InjectableCandidate>();
131
129
 
132
130
  let config: InjectableCandidate;
133
131
 
@@ -152,24 +150,21 @@ export class DependencyRegistryResolver {
152
150
  return {
153
151
  candidate: castTo(config),
154
152
  qualifier,
155
- targetId: (config.target ?? config.candidateType).Ⲑid
153
+ target: (config.target ?? config.candidateType)
156
154
  };
157
155
  }
158
156
 
159
157
  removeClass(cls: Class, qualifier: symbol): void {
160
- const classId = cls.Ⲑid;
161
158
  this.#defaultSymbols.delete(qualifier);
162
- this.#byCandidateType.get(classId)!.delete(qualifier);
163
- this.#byContainerType.get(classId)!.delete(qualifier);
159
+ this.#byCandidateType.get(cls)!.delete(qualifier);
160
+ this.#byContainerType.get(cls)!.delete(qualifier);
164
161
  }
165
162
 
166
163
  getCandidateEntries(candidateType: Class): [symbol, InjectableCandidate][] {
167
- const candidateTypeId = candidateType.Ⲑid;
168
- return [...this.#byCandidateType.get(candidateTypeId)?.entries() ?? []];
164
+ return [...this.#byCandidateType.get(candidateType)?.entries() ?? []];
169
165
  }
170
166
 
171
167
  getContainerEntries(containerType: Class): [symbol, InjectableCandidate][] {
172
- const containerTypeId = containerType.Ⲑid;
173
- return [...this.#byContainerType.get(containerTypeId)?.entries() ?? []];
168
+ return [...this.#byContainerType.get(containerType)?.entries() ?? []];
174
169
  }
175
170
  }
package/src/types.ts CHANGED
@@ -32,7 +32,7 @@ export interface InjectableCandidate<T = unknown> {
32
32
  /**
33
33
  * Method that is injectable on class
34
34
  */
35
- method: string | symbol;
35
+ method: string;
36
36
  /**
37
37
  * Method handle
38
38
  */
@@ -74,7 +74,7 @@ export interface InjectableConfig<T = unknown> {
74
74
  /**
75
75
  * Candidates that are injectable
76
76
  */
77
- candidates: Record<string | symbol, InjectableCandidate>;
77
+ candidates: Record<string, InjectableCandidate>;
78
78
  }
79
79
 
80
80
  export function getDefaultQualifier(cls: Class): symbol {
@@ -85,5 +85,5 @@ export function getDefaultQualifier(cls: Class): symbol {
85
85
  export const PrimaryCandidateSymbol = Symbol();
86
86
 
87
87
  export type InjectableClassMetadata = {
88
- postConstruct: Record<string | symbol, (<T>(inst: T) => Promise<void>)>;
88
+ postConstruct: Record<string, (<T>(inst: T) => Promise<void>)>;
89
89
  };