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

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
@@ -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.1",
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.1"
31
31
  },
32
32
  "peerDependencies": {
33
- "@travetto/transformer": "^7.0.0-rc.0"
33
+ "@travetto/transformer": "^7.0.0-rc.1"
34
34
  },
35
35
  "peerDependenciesMeta": {
36
36
  "@travetto/transformer": {
@@ -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
  }
@@ -62,25 +60,25 @@ export class DependencyRegistryIndex implements RegistryIndex {
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,36 +88,30 @@ 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
  }
@@ -149,12 +141,11 @@ export class DependencyRegistryIndex implements RegistryIndex {
149
141
 
150
142
  process(events: ChangeEvent<Class>[]): void {
151
143
  for (const ev of events) {
152
- if (ev.type === 'added') {
153
- this.#addClass(ev.curr);
154
- } else if (ev.type === 'removing') {
144
+ if ('prev' in ev) {
155
145
  this.#removeClass(ev.prev);
156
- } else if (ev.type === 'changed') {
157
- this.#changedClass(ev.curr, ev.prev);
146
+ }
147
+ if ('curr' in ev) {
148
+ this.#addClass(ev.curr, 'prev' in ev);
158
149
  }
159
150
  }
160
151
  }
@@ -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,7 +182,7 @@ 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
188
  .filter(([k, input]) => readMetadata(input) !== undefined && (input.access !== 'readonly' && instance[castKey(k)] === undefined))
@@ -242,26 +233,26 @@ 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
253
  } catch (err) {
263
254
  // Clear it out, don't save failed constructions
264
- this.#instancePromises.get(targetId)!.delete(qualifier);
255
+ this.#instancePromises.get(target)!.delete(qualifier);
265
256
  throw err;
266
257
  }
267
258
  }
@@ -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,10 +4,9 @@ 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 {
9
+ function setInMap<T>(map: Map<Class, Map<typeof key, T>>, src: Class, key: symbol | string, dest: T): void {
11
10
  if (!map.has(src)) {
12
11
  map.set(src, new Map());
13
12
  }
@@ -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
 
@@ -65,13 +64,11 @@ export class DependencyRegistryResolver {
65
64
  /**
66
65
  * Register a class with the dependency resolver
67
66
  */
68
- registerClass(config: InjectableCandidate, baseParentId?: string): void {
67
+ registerClass(config: InjectableCandidate, baseParent?: Class): void {
69
68
  const candidateType = config.candidateType;
70
- const candidateClassId = candidateType.Ⲑid;
71
69
  const target = config.target ?? candidateType;
72
70
 
73
- const targetClassId = target.Ⲑid;
74
- const isSelfTarget = candidateClassId === targetClassId;
71
+ const isSelfTarget = target === candidateType;
75
72
  const qualifier = config.qualifier ?? getDefaultQualifier(candidateType);
76
73
 
77
74
  // Record qualifier if its the default for the class
@@ -80,42 +77,41 @@ export class DependencyRegistryResolver {
80
77
  }
81
78
 
82
79
  // Register inbound config by method and class
83
- setInMap(this.#byContainerType, config.class.Ⲑid, config.method, config);
80
+ setInMap(this.#byContainerType, config.class, config.method, config);
84
81
 
85
- setInMap(this.#byCandidateType, targetClassId, qualifier, config);
86
- setInMap(this.#byCandidateType, candidateClassId, qualifier, config);
82
+ setInMap(this.#byCandidateType, target, qualifier, config);
83
+ setInMap(this.#byCandidateType, candidateType, qualifier, config);
87
84
 
88
85
  // Track interface aliases as targets
89
86
  const interfaces = SchemaRegistryIndex.has(candidateType) ?
90
87
  SchemaRegistryIndex.get(candidateType).get().interfaces : [];
91
88
 
92
- for (const { Ⲑid: interfaceId } of interfaces) {
93
- setInMap(this.#byCandidateType, interfaceId, qualifier, config);
89
+ for (const type of interfaces) {
90
+ setInMap(this.#byCandidateType, type, qualifier, config);
94
91
  }
95
92
 
96
93
  // If targeting self (default @Injectable behavior)
97
- if (isSelfTarget && baseParentId) {
98
- setInMap(this.#byCandidateType, baseParentId, qualifier, config);
94
+ if (isSelfTarget && baseParent) {
95
+ setInMap(this.#byCandidateType, baseParent, qualifier, config);
99
96
  }
100
97
 
101
98
  // Registry primary candidates
102
99
  if (config.primary) {
103
- if (baseParentId) {
104
- setInMap(this.#byCandidateType, baseParentId, PrimaryCandidateSymbol, config);
100
+ if (baseParent) {
101
+ setInMap(this.#byCandidateType, baseParent, PrimaryCandidateSymbol, config);
105
102
  }
106
103
 
107
104
  // Register primary for self
108
- setInMap(this.#byCandidateType, targetClassId, PrimaryCandidateSymbol, config);
105
+ setInMap(this.#byCandidateType, target, PrimaryCandidateSymbol, config);
109
106
 
110
107
  // Register primary if only one interface provided and no parent config
111
- if (interfaces.length === 1 && (!baseParentId || !this.#byContainerType.has(baseParentId))) {
108
+ if (interfaces.length === 1 && (!baseParent || !this.#byContainerType.has(baseParent))) {
112
109
  const [primaryInterface] = interfaces;
113
- const primaryClassId = primaryInterface.Ⲑid;
114
- setInMap(this.#byCandidateType, primaryClassId, PrimaryCandidateSymbol, config);
110
+ setInMap(this.#byCandidateType, primaryInterface, PrimaryCandidateSymbol, config);
115
111
  } else if (isSelfTarget) {
116
112
  // Register primary for all interfaces if self targeting
117
- for (const { Ⲑid: interfaceId } of interfaces) {
118
- setInMap(this.#byCandidateType, interfaceId, PrimaryCandidateSymbol, config);
113
+ for (const type of interfaces) {
114
+ setInMap(this.#byCandidateType, type, PrimaryCandidateSymbol, config);
119
115
  }
120
116
  }
121
117
  }
@@ -127,7 +123,7 @@ export class DependencyRegistryResolver {
127
123
  * @param qualifier
128
124
  */
129
125
  resolveCandidate<T>(candidateType: Class<T>, qualifier?: symbol, resolution?: ResolutionType): Resolved<T> {
130
- const qualifiers = this.#byCandidateType.get(candidateType.Ⲑid) ?? new Map<symbol, InjectableCandidate>();
126
+ const qualifiers = this.#byCandidateType.get(candidateType) ?? new Map<symbol, InjectableCandidate>();
131
127
 
132
128
  let config: InjectableCandidate;
133
129
 
@@ -152,24 +148,21 @@ export class DependencyRegistryResolver {
152
148
  return {
153
149
  candidate: castTo(config),
154
150
  qualifier,
155
- targetId: (config.target ?? config.candidateType).Ⲑid
151
+ target: (config.target ?? config.candidateType)
156
152
  };
157
153
  }
158
154
 
159
155
  removeClass(cls: Class, qualifier: symbol): void {
160
- const classId = cls.Ⲑid;
161
156
  this.#defaultSymbols.delete(qualifier);
162
- this.#byCandidateType.get(classId)!.delete(qualifier);
163
- this.#byContainerType.get(classId)!.delete(qualifier);
157
+ this.#byCandidateType.get(cls)!.delete(qualifier);
158
+ this.#byContainerType.get(cls)!.delete(qualifier);
164
159
  }
165
160
 
166
161
  getCandidateEntries(candidateType: Class): [symbol, InjectableCandidate][] {
167
- const candidateTypeId = candidateType.Ⲑid;
168
- return [...this.#byCandidateType.get(candidateTypeId)?.entries() ?? []];
162
+ return [...this.#byCandidateType.get(candidateType)?.entries() ?? []];
169
163
  }
170
164
 
171
165
  getContainerEntries(containerType: Class): [symbol, InjectableCandidate][] {
172
- const containerTypeId = containerType.Ⲑid;
173
- return [...this.#byContainerType.get(containerTypeId)?.entries() ?? []];
166
+ return [...this.#byContainerType.get(containerType)?.entries() ?? []];
174
167
  }
175
168
  }