@travetto/di 8.0.0-alpha.19 → 8.0.0-alpha.20
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 +28 -31
- package/__index__.ts +1 -1
- package/package.json +3 -3
- package/src/decorator.ts +5 -5
- package/src/error.ts +2 -2
- package/src/registry/registry-adapter.ts +6 -5
- package/src/registry/registry-index.ts +16 -15
- package/src/registry/registry-resolver.ts +9 -12
- package/src/types.ts +2 -3
- package/support/test/suite.ts +3 -3
package/README.md
CHANGED
|
@@ -13,10 +13,10 @@ npm install @travetto/di
|
|
|
13
13
|
yarn add @travetto/di
|
|
14
14
|
```
|
|
15
15
|
|
|
16
|
-
[Dependency injection](https://en.wikipedia.org/wiki/Dependency_injection) is a framework primitive.
|
|
16
|
+
[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.
|
|
17
17
|
|
|
18
18
|
## Declaration
|
|
19
|
-
The [@Injectable](https://github.com/travetto/travetto/tree/main/module/di/src/decorator.ts#L16) and [@InjectableFactory](https://github.com/travetto/travetto/tree/main/module/di/src/decorator.ts#L48) decorators provide the registration of dependencies.
|
|
19
|
+
The [@Injectable](https://github.com/travetto/travetto/tree/main/module/di/src/decorator.ts#L16) and [@InjectableFactory](https://github.com/travetto/travetto/tree/main/module/di/src/decorator.ts#L48) 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.
|
|
20
20
|
|
|
21
21
|
**Code: Example Injectable**
|
|
22
22
|
```typescript
|
|
@@ -30,11 +30,11 @@ class CustomService {
|
|
|
30
30
|
}
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
-
When declaring a dependency, you can also provide a token to allow for multiple instances of the dependency to be defined.
|
|
33
|
+
When declaring a dependency, you can also provide a token to allow for multiple instances of the dependency to be defined. This can be used in many situations:
|
|
34
34
|
|
|
35
35
|
**Code: Example Injectable with multiple targets**
|
|
36
36
|
```typescript
|
|
37
|
-
import {
|
|
37
|
+
import { Inject, Injectable } from '@travetto/di';
|
|
38
38
|
|
|
39
39
|
@Injectable()
|
|
40
40
|
class CustomService {
|
|
@@ -59,7 +59,7 @@ class Consumer {
|
|
|
59
59
|
}
|
|
60
60
|
```
|
|
61
61
|
|
|
62
|
-
As you can see, the `target` field is also set, which indicates to the dependency registration process what `class` the injectable is compatible with.
|
|
62
|
+
As you can see, the `target` field is also set, which indicates to the dependency registration process what `class` the injectable is compatible with. Additionally, when using `abstract` classes, the parent `class` is always considered as a valid candidate type.
|
|
63
63
|
|
|
64
64
|
**Code: Example Injectable with target via abstract class**
|
|
65
65
|
```typescript
|
|
@@ -77,16 +77,14 @@ class SpecificService extends BaseService {
|
|
|
77
77
|
}
|
|
78
78
|
```
|
|
79
79
|
|
|
80
|
-
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
|
|
80
|
+
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#L48) decorator denotes a `static` class method that produces an [@Injectable](https://github.com/travetto/travetto/tree/main/module/di/src/decorator.ts#L16).
|
|
81
81
|
|
|
82
82
|
**Code: Example InjectableFactory**
|
|
83
83
|
```typescript
|
|
84
84
|
import { InjectableFactory } from '@travetto/di';
|
|
85
85
|
|
|
86
86
|
// Not injectable by default
|
|
87
|
-
class CoolService {
|
|
88
|
-
|
|
89
|
-
}
|
|
87
|
+
class CoolService {}
|
|
90
88
|
|
|
91
89
|
class Config {
|
|
92
90
|
@InjectableFactory()
|
|
@@ -96,12 +94,12 @@ class Config {
|
|
|
96
94
|
}
|
|
97
95
|
```
|
|
98
96
|
|
|
99
|
-
Given the `static` method `initService`, the function will be provided as a valid candidate for `CoolService`.
|
|
97
|
+
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.
|
|
100
98
|
|
|
101
99
|
**Code: Example Conditional Dependency**
|
|
102
100
|
```typescript
|
|
103
|
-
import { Runtime } from '@travetto/runtime';
|
|
104
101
|
import { Inject, Injectable } from '@travetto/di';
|
|
102
|
+
import { Runtime } from '@travetto/runtime';
|
|
105
103
|
|
|
106
104
|
@Injectable({ enabled: Runtime.production })
|
|
107
105
|
class ProductionLogger {
|
|
@@ -123,18 +121,19 @@ class RuntimeService {
|
|
|
123
121
|
}
|
|
124
122
|
```
|
|
125
123
|
|
|
126
|
-
In this example, the enabled flag is specified in relationship to the deployment environment.
|
|
124
|
+
In this example, the enabled flag is specified in relationship to the deployment environment. When coupled with optional properties, and optional chaining, allows for seamless inclusion of optional dependencies at runtime.
|
|
127
125
|
|
|
128
|
-
**Note**: Other modules are able to provide aliases to [@Injectable](https://github.com/travetto/travetto/tree/main/module/di/src/decorator.ts#L16) that also provide additional functionality.
|
|
126
|
+
**Note**: Other modules are able to provide aliases to [@Injectable](https://github.com/travetto/travetto/tree/main/module/di/src/decorator.ts#L16) 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 [Web API](https://github.com/travetto/travetto/tree/main/module/web#readme "Declarative support for creating Web Applications") module @Controller decorator registers the associated class as an injectable element.
|
|
129
127
|
|
|
130
128
|
## Injection
|
|
131
|
-
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#L16) instances to your code.
|
|
129
|
+
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#L16) instances to your code. There are three primary methods for injection:
|
|
132
130
|
|
|
133
|
-
The [@Inject](https://github.com/travetto/travetto/tree/main/module/di/src/decorator.ts#L16) decorator, which denotes a desire to inject a value directly.
|
|
131
|
+
The [@Inject](https://github.com/travetto/travetto/tree/main/module/di/src/decorator.ts#L16) decorator, which denotes a desire to inject a value directly. These will be set post construction.
|
|
134
132
|
|
|
135
133
|
**Code: Example Injectable with dependencies as Inject fields**
|
|
136
134
|
```typescript
|
|
137
|
-
import {
|
|
135
|
+
import { Inject, Injectable } from '@travetto/di';
|
|
136
|
+
|
|
138
137
|
import type { DependentService } from './dependency.ts';
|
|
139
138
|
|
|
140
139
|
@Injectable()
|
|
@@ -153,11 +152,11 @@ The [@Injectable](https://github.com/travetto/travetto/tree/main/module/di/src/d
|
|
|
153
152
|
**Code: Example Injectable with dependencies in constructor**
|
|
154
153
|
```typescript
|
|
155
154
|
import { Injectable } from '@travetto/di';
|
|
155
|
+
|
|
156
156
|
import type { DependentService } from './dependency.ts';
|
|
157
157
|
|
|
158
158
|
@Injectable()
|
|
159
159
|
class CustomService {
|
|
160
|
-
|
|
161
160
|
dependentService: DependentService;
|
|
162
161
|
|
|
163
162
|
constructor(service: DependentService) {
|
|
@@ -176,7 +175,7 @@ Via [@InjectableFactory](https://github.com/travetto/travetto/tree/main/module/d
|
|
|
176
175
|
```typescript
|
|
177
176
|
import { InjectableFactory } from '@travetto/di';
|
|
178
177
|
|
|
179
|
-
import { type DependentService
|
|
178
|
+
import { CustomService, type DependentService } from './dependency.ts';
|
|
180
179
|
|
|
181
180
|
class Config {
|
|
182
181
|
@InjectableFactory()
|
|
@@ -191,17 +190,15 @@ If you are building modules for others to consume, often times it is possible to
|
|
|
191
190
|
|
|
192
191
|
**Code: Example Multiple Candidate Types**
|
|
193
192
|
```typescript
|
|
194
|
-
import {
|
|
193
|
+
import { Inject, Injectable } from '@travetto/di';
|
|
195
194
|
|
|
196
|
-
export abstract class Contract {
|
|
197
|
-
|
|
198
|
-
}
|
|
195
|
+
export abstract class Contract {}
|
|
199
196
|
|
|
200
197
|
@Injectable()
|
|
201
|
-
class SimpleContract extends Contract {
|
|
198
|
+
class SimpleContract extends Contract {}
|
|
202
199
|
|
|
203
200
|
@Injectable()
|
|
204
|
-
export class ComplexContract extends Contract {
|
|
201
|
+
export class ComplexContract extends Contract {}
|
|
205
202
|
|
|
206
203
|
@Injectable()
|
|
207
204
|
class ContractConsumer {
|
|
@@ -211,12 +208,13 @@ class ContractConsumer {
|
|
|
211
208
|
}
|
|
212
209
|
```
|
|
213
210
|
|
|
214
|
-
By default, if there is only one candidate without qualification, then that candidate will be used.
|
|
211
|
+
By default, if there is only one candidate without qualification, then that candidate will be used. If multiple candidates are found, then the injection system will bail. To overcome this the end user will need to specify which candidate type should be considered `primary`:
|
|
215
212
|
|
|
216
213
|
**Code: Example Multiple Candidate Types**
|
|
217
214
|
```typescript
|
|
218
215
|
import { InjectableFactory } from '@travetto/di';
|
|
219
|
-
|
|
216
|
+
|
|
217
|
+
import type { ComplexContract, Contract } from './injectable-multiple-default.ts';
|
|
220
218
|
|
|
221
219
|
class Config {
|
|
222
220
|
// Complex will be marked as the available Contract
|
|
@@ -228,9 +226,9 @@ class Config {
|
|
|
228
226
|
```
|
|
229
227
|
|
|
230
228
|
## Non-Framework Dependencies
|
|
231
|
-
The module is built around the framework's management of class registration, and being able to decorate the code with [@Injectable](https://github.com/travetto/travetto/tree/main/module/di/src/decorator.ts#L16) decorators. There may also be a desire to leverage external code and pull it into the dependency injection framework.
|
|
229
|
+
The module is built around the framework's management of class registration, and being able to decorate the code with [@Injectable](https://github.com/travetto/travetto/tree/main/module/di/src/decorator.ts#L16) decorators. There may also be a desire to leverage external code and pull it into the dependency injection framework. This could easily be achieved using a wrapper class that is owned by the framework.
|
|
232
230
|
|
|
233
|
-
It is also possible to directly reference external types, and they will be converted into unique symbols.
|
|
231
|
+
It is also possible to directly reference external types, and they will be converted into unique symbols. These symbols cannot be used manually, but can be leveraged using [@Inject](https://github.com/travetto/travetto/tree/main/module/di/src/decorator.ts#L16) decorators.
|
|
234
232
|
|
|
235
233
|
**Code: Example External Dependencies**
|
|
236
234
|
```typescript
|
|
@@ -272,10 +270,10 @@ Some times you will need to lookup a dependency dynamically, or you want to cont
|
|
|
272
270
|
|
|
273
271
|
**Code: Example of Manual Lookup**
|
|
274
272
|
```typescript
|
|
275
|
-
import {
|
|
273
|
+
import { DependencyRegistryIndex, Injectable } from '@travetto/di';
|
|
276
274
|
|
|
277
275
|
@Injectable()
|
|
278
|
-
class Complex {
|
|
276
|
+
class Complex {}
|
|
279
277
|
|
|
280
278
|
class ManualLookup {
|
|
281
279
|
async invoke() {
|
|
@@ -307,7 +305,6 @@ class MyCustomService implements ServiceContract {
|
|
|
307
305
|
|
|
308
306
|
@Injectable()
|
|
309
307
|
class SpecificService {
|
|
310
|
-
|
|
311
308
|
@Inject()
|
|
312
309
|
service: ServiceContract;
|
|
313
310
|
}
|
package/__index__.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@travetto/di",
|
|
3
|
-
"version": "8.0.0-alpha.
|
|
3
|
+
"version": "8.0.0-alpha.20",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Dependency registration/management and injection support.",
|
|
6
6
|
"keywords": [
|
|
@@ -28,10 +28,10 @@
|
|
|
28
28
|
"directory": "module/di"
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@travetto/registry": "^8.0.0-alpha.
|
|
31
|
+
"@travetto/registry": "^8.0.0-alpha.20"
|
|
32
32
|
},
|
|
33
33
|
"peerDependencies": {
|
|
34
|
-
"@travetto/transformer": "^8.0.0-alpha.
|
|
34
|
+
"@travetto/transformer": "^8.0.0-alpha.14"
|
|
35
35
|
},
|
|
36
36
|
"peerDependenciesMeta": {
|
|
37
37
|
"@travetto/transformer": {
|
package/src/decorator.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { type Any,
|
|
1
|
+
import { type Any, type Class, type ClassInstance, castTo, getClass } from '@travetto/runtime';
|
|
2
2
|
import { CONSTRUCTOR_PROPERTY } from '@travetto/schema';
|
|
3
3
|
|
|
4
|
-
import type { InjectableCandidate, ResolutionType } from './types.ts';
|
|
5
4
|
import { DependencyRegistryIndex } from './registry/registry-index.ts';
|
|
5
|
+
import type { InjectableCandidate, ResolutionType } from './types.ts';
|
|
6
6
|
|
|
7
7
|
const fromInput = <T extends { qualifier?: symbol }>(input?: T | symbol): T =>
|
|
8
8
|
typeof input === 'symbol' ? castTo({ qualifier: input }) : (input ?? castTo<T>({}));
|
|
@@ -19,7 +19,7 @@ export function Injectable(input?: Partial<InjectableCandidate> | symbol) {
|
|
|
19
19
|
};
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
export type InjectConfig = { qualifier?: symbol
|
|
22
|
+
export type InjectConfig = { qualifier?: symbol; resolution?: ResolutionType };
|
|
23
23
|
|
|
24
24
|
/**
|
|
25
25
|
* Indicate that a field or parameter is able to be injected
|
|
@@ -48,7 +48,7 @@ export function Inject(input?: InjectConfig | symbol) {
|
|
|
48
48
|
export function InjectableFactory(input?: Partial<InjectableCandidate> | symbol) {
|
|
49
49
|
return <T extends Class>(cls: T, property: string, descriptor: TypedPropertyDescriptor<(...args: Any[]) => Any>): void => {
|
|
50
50
|
DependencyRegistryIndex.registerFactory(cls, property, fromInput(input), {
|
|
51
|
-
factory: (...params: unknown[]) => descriptor.value!.apply(cls, params)
|
|
51
|
+
factory: (...params: unknown[]) => descriptor.value!.apply(cls, params)
|
|
52
52
|
});
|
|
53
53
|
};
|
|
54
54
|
}
|
|
@@ -61,4 +61,4 @@ export function PostConstruct(priority: number = 10) {
|
|
|
61
61
|
return (instance: ClassInstance, property: string, descriptor: TypedPropertyDescriptor<() => Any>): void => {
|
|
62
62
|
DependencyRegistryIndex.registerPostConstruct(getClass(instance), { operation: descriptor.value!, priority });
|
|
63
63
|
};
|
|
64
|
-
}
|
|
64
|
+
}
|
package/src/error.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { type Class, RuntimeError } from '@travetto/runtime';
|
|
2
2
|
|
|
3
3
|
function getName(symbol: symbol): string {
|
|
4
4
|
return symbol.toString().split(/[()]/g)[1];
|
|
@@ -14,4 +14,4 @@ export class InjectionError extends RuntimeError {
|
|
|
14
14
|
}
|
|
15
15
|
});
|
|
16
16
|
}
|
|
17
|
-
}
|
|
17
|
+
}
|
|
@@ -2,7 +2,7 @@ import type { RegistryAdapter } from '@travetto/registry';
|
|
|
2
2
|
import { type Class, classConstruct, describeFunction, safeAssign } from '@travetto/runtime';
|
|
3
3
|
import { CONSTRUCTOR_PROPERTY, SchemaRegistryIndex } from '@travetto/schema';
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { getDefaultQualifier, type InjectableCandidate, type InjectableConfig } from '../types.ts';
|
|
6
6
|
|
|
7
7
|
function combineInjectableCandidates<T extends InjectableCandidate>(base: T, ...overrides: Partial<T>[]): typeof base {
|
|
8
8
|
for (const override of overrides) {
|
|
@@ -43,7 +43,7 @@ export class DependencyRegistryAdapter implements RegistryAdapter<InjectableConf
|
|
|
43
43
|
method,
|
|
44
44
|
enabled: true,
|
|
45
45
|
factory: undefined!,
|
|
46
|
-
candidateType: undefined
|
|
46
|
+
candidateType: undefined!
|
|
47
47
|
};
|
|
48
48
|
return combineInjectableCandidates(candidates[method], ...data);
|
|
49
49
|
}
|
|
@@ -51,7 +51,7 @@ export class DependencyRegistryAdapter implements RegistryAdapter<InjectableConf
|
|
|
51
51
|
registerClass(...data: Partial<InjectableCandidate<unknown>>[]): InjectableCandidate {
|
|
52
52
|
return this.registerFactory(CONSTRUCTOR_PROPERTY, ...data, {
|
|
53
53
|
factory: (...args: unknown[]) => classConstruct(this.#cls, args),
|
|
54
|
-
candidateType: this.#cls
|
|
54
|
+
candidateType: this.#cls
|
|
55
55
|
});
|
|
56
56
|
}
|
|
57
57
|
|
|
@@ -67,8 +67,9 @@ export class DependencyRegistryAdapter implements RegistryAdapter<InjectableConf
|
|
|
67
67
|
candidate.qualifier ??= getDefaultQualifier(candidateType);
|
|
68
68
|
}
|
|
69
69
|
// Inherit post construct from parent
|
|
70
|
-
this.#config.postConstruct = [...(parent?.postConstruct ?? []), ...this.#config.postConstruct]
|
|
71
|
-
|
|
70
|
+
this.#config.postConstruct = [...(parent?.postConstruct ?? []), ...this.#config.postConstruct].sort(
|
|
71
|
+
(a, b) => (a.priority ?? 1) - (b.priority ?? 1)
|
|
72
|
+
);
|
|
72
73
|
}
|
|
73
74
|
|
|
74
75
|
getCandidateConfigs(): InjectableCandidate[] {
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { type RegistryIndex, RegistryIndexStore
|
|
2
|
-
import { castKey, castTo,
|
|
1
|
+
import { Registry, type RegistryIndex, RegistryIndexStore } from '@travetto/registry';
|
|
2
|
+
import { type Class, castKey, castTo, describeFunction, getParentClass, TypedObject } from '@travetto/runtime';
|
|
3
3
|
import { type SchemaFieldConfig, type SchemaParameterConfig, SchemaRegistryIndex } from '@travetto/schema';
|
|
4
4
|
|
|
5
|
+
import { InjectionError } from '../error.ts';
|
|
5
6
|
import type { Dependency, InjectableCandidate, InjectableConfig, PostConstructor, ResolutionType } from '../types.ts';
|
|
6
7
|
import { DependencyRegistryAdapter } from './registry-adapter.ts';
|
|
7
|
-
import { InjectionError } from '../error.ts';
|
|
8
8
|
import { DependencyRegistryResolver } from './registry-resolver.ts';
|
|
9
9
|
|
|
10
10
|
const MetadataSymbol = Symbol();
|
|
@@ -15,7 +15,6 @@ function readMetadata(item: { metadata?: Record<symbol, unknown> }): Dependency
|
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
export class DependencyRegistryIndex implements RegistryIndex {
|
|
18
|
-
|
|
19
18
|
static #instance = Registry.registerIndex(DependencyRegistryIndex);
|
|
20
19
|
|
|
21
20
|
static getForRegister(cls: Class): DependencyRegistryAdapter {
|
|
@@ -79,7 +78,9 @@ export class DependencyRegistryIndex implements RegistryIndex {
|
|
|
79
78
|
|
|
80
79
|
store = new RegistryIndexStore(DependencyRegistryAdapter);
|
|
81
80
|
|
|
82
|
-
/** @private */ constructor(source: unknown) {
|
|
81
|
+
/** @private */ constructor(source: unknown) {
|
|
82
|
+
Registry.validateConstructor(source);
|
|
83
|
+
}
|
|
83
84
|
|
|
84
85
|
getConfig(cls: Class): InjectableConfig {
|
|
85
86
|
return this.store.get(cls).get();
|
|
@@ -91,7 +92,7 @@ export class DependencyRegistryIndex implements RegistryIndex {
|
|
|
91
92
|
for (const config of adapter.getCandidateConfigs()) {
|
|
92
93
|
const parentClass = getParentClass(config.candidateType);
|
|
93
94
|
const parentConfig = parentClass ? this.store.getOptional(parentClass) : undefined;
|
|
94
|
-
const hasParentBase =
|
|
95
|
+
const hasParentBase = parentConfig || (parentClass && !!describeFunction(parentClass)?.abstract);
|
|
95
96
|
const baseParent = hasParentBase ? parentClass : undefined;
|
|
96
97
|
this.#resolver.registerClass(config, baseParent);
|
|
97
98
|
}
|
|
@@ -128,11 +129,11 @@ export class DependencyRegistryIndex implements RegistryIndex {
|
|
|
128
129
|
* Retrieve list dependencies
|
|
129
130
|
*/
|
|
130
131
|
async fetchDependencyParameters<T>(candidate: InjectableCandidate<T>): Promise<unknown[]> {
|
|
131
|
-
const inputs = SchemaRegistryIndex.has(candidate.class)
|
|
132
|
-
SchemaRegistryIndex.get(candidate.class).getMethod(candidate.method).parameters
|
|
132
|
+
const inputs = SchemaRegistryIndex.has(candidate.class)
|
|
133
|
+
? SchemaRegistryIndex.get(candidate.class).getMethod(candidate.method).parameters
|
|
134
|
+
: [];
|
|
133
135
|
|
|
134
|
-
const promises = inputs
|
|
135
|
-
.map(input => this.#resolveDependencyValue(readMetadata(input) ?? {}, input, candidate.class));
|
|
136
|
+
const promises = inputs.map(input => this.#resolveDependencyValue(readMetadata(input) ?? {}, input, candidate.class));
|
|
136
137
|
|
|
137
138
|
return await Promise.all(promises);
|
|
138
139
|
}
|
|
@@ -144,7 +145,7 @@ export class DependencyRegistryIndex implements RegistryIndex {
|
|
|
144
145
|
const inputs = SchemaRegistryIndex.getOptional(candidateType)?.getFields() ?? {};
|
|
145
146
|
|
|
146
147
|
const promises = TypedObject.entries(inputs)
|
|
147
|
-
.filter(([key, input]) => readMetadata(input) !== undefined &&
|
|
148
|
+
.filter(([key, input]) => readMetadata(input) !== undefined && input.access !== 'readonly' && instance[castKey(key)] === undefined)
|
|
148
149
|
.map(async ([key, input]) => [key, await this.#resolveDependencyValue(readMetadata(input) ?? {}, input, sourceClass)] as const);
|
|
149
150
|
|
|
150
151
|
const pairs = await Promise.all(promises);
|
|
@@ -188,8 +189,8 @@ export class DependencyRegistryIndex implements RegistryIndex {
|
|
|
188
189
|
async getInstance<T>(candidateType: Class<T>, requestedQualifier?: symbol, resolution?: ResolutionType): Promise<T> {
|
|
189
190
|
const { target, qualifier } = this.#resolver.resolveCandidate(candidateType, requestedQualifier, resolution);
|
|
190
191
|
|
|
191
|
-
return castTo(
|
|
192
|
-
.getOrInsert(target, new Map())
|
|
193
|
-
|
|
192
|
+
return castTo(
|
|
193
|
+
this.#instancePromises.getOrInsert(target, new Map()).getOrInsertComputed(qualifier, () => this.construct(target, qualifier))
|
|
194
|
+
);
|
|
194
195
|
}
|
|
195
|
-
}
|
|
196
|
+
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
+
import { type Class, castTo } from '@travetto/runtime';
|
|
1
2
|
import { SchemaRegistryIndex, UnknownType } from '@travetto/schema';
|
|
2
|
-
import { castTo, type Class } from '@travetto/runtime';
|
|
3
3
|
|
|
4
|
-
import { getDefaultQualifier, type InjectableCandidate, PrimaryCandidateSymbol, type ResolutionType } from '../types.ts';
|
|
5
4
|
import { InjectionError } from '../error.ts';
|
|
5
|
+
import { getDefaultQualifier, type InjectableCandidate, PrimaryCandidateSymbol, type ResolutionType } from '../types.ts';
|
|
6
6
|
|
|
7
|
-
type Resolved<T> = { candidate: InjectableCandidate<T
|
|
7
|
+
type Resolved<T> = { candidate: InjectableCandidate<T>; qualifier: symbol; target: Class };
|
|
8
8
|
|
|
9
9
|
function setInMap<T>(map: Map<Class, Map<typeof key, T>>, cls: Class, key: symbol | string, dest: T): void {
|
|
10
10
|
map.getOrInsert(cls, new Map()).set(key, dest);
|
|
@@ -35,9 +35,7 @@ export class DependencyRegistryResolver {
|
|
|
35
35
|
if (qualifiers.has(PrimaryCandidateSymbol)) {
|
|
36
36
|
return PrimaryCandidateSymbol;
|
|
37
37
|
} else {
|
|
38
|
-
const filtered = resolved
|
|
39
|
-
.filter(qualifier => !!qualifier)
|
|
40
|
-
.filter(qualifier => this.#defaultSymbols.has(qualifier));
|
|
38
|
+
const filtered = resolved.filter(qualifier => !!qualifier).filter(qualifier => this.#defaultSymbols.has(qualifier));
|
|
41
39
|
// If there is only one default symbol
|
|
42
40
|
if (filtered.length === 1) {
|
|
43
41
|
return filtered[0];
|
|
@@ -82,8 +80,7 @@ export class DependencyRegistryResolver {
|
|
|
82
80
|
setInMap(this.#byCandidateType, candidateType, qualifier, config);
|
|
83
81
|
|
|
84
82
|
// Track interface aliases as targets
|
|
85
|
-
const interfaces = SchemaRegistryIndex.has(candidateType) ?
|
|
86
|
-
SchemaRegistryIndex.get(candidateType).get().interfaces : [];
|
|
83
|
+
const interfaces = SchemaRegistryIndex.has(candidateType) ? SchemaRegistryIndex.get(candidateType).get().interfaces : [];
|
|
87
84
|
|
|
88
85
|
for (const type of interfaces) {
|
|
89
86
|
setInMap(this.#byCandidateType, type, qualifier, config);
|
|
@@ -151,7 +148,7 @@ export class DependencyRegistryResolver {
|
|
|
151
148
|
return {
|
|
152
149
|
candidate: castTo(config),
|
|
153
150
|
qualifier,
|
|
154
|
-
target:
|
|
151
|
+
target: config.target ?? config.candidateType
|
|
155
152
|
};
|
|
156
153
|
}
|
|
157
154
|
|
|
@@ -162,10 +159,10 @@ export class DependencyRegistryResolver {
|
|
|
162
159
|
}
|
|
163
160
|
|
|
164
161
|
getCandidateEntries(candidateType: Class): [symbol, InjectableCandidate][] {
|
|
165
|
-
return [...this.#byCandidateType.get(candidateType)?.entries() ?? []];
|
|
162
|
+
return [...(this.#byCandidateType.get(candidateType)?.entries() ?? [])];
|
|
166
163
|
}
|
|
167
164
|
|
|
168
165
|
getContainerEntries(containerType: Class): [symbol, InjectableCandidate][] {
|
|
169
|
-
return [...this.#byContainerType.get(containerType)?.entries() ?? []];
|
|
166
|
+
return [...(this.#byContainerType.get(containerType)?.entries() ?? [])];
|
|
170
167
|
}
|
|
171
|
-
}
|
|
168
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -65,11 +65,10 @@ export interface InjectableCandidate<T = unknown> {
|
|
|
65
65
|
|
|
66
66
|
/** Post Construct Handler */
|
|
67
67
|
export type PostConstructor<T> = {
|
|
68
|
-
operation: (
|
|
68
|
+
operation: (this: T) => Promise<unknown> | unknown;
|
|
69
69
|
priority: number;
|
|
70
70
|
};
|
|
71
71
|
|
|
72
|
-
|
|
73
72
|
/**
|
|
74
73
|
* Full injectable configuration for a class
|
|
75
74
|
*/
|
|
@@ -92,4 +91,4 @@ export function getDefaultQualifier(cls: Class): symbol {
|
|
|
92
91
|
return Symbol.for(cls.Ⲑid);
|
|
93
92
|
}
|
|
94
93
|
|
|
95
|
-
export const PrimaryCandidateSymbol = Symbol();
|
|
94
|
+
export const PrimaryCandidateSymbol = Symbol();
|
package/support/test/suite.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { type Class } from '@travetto/runtime';
|
|
2
1
|
import { Registry } from '@travetto/registry';
|
|
3
|
-
import {
|
|
2
|
+
import type { Class } from '@travetto/runtime';
|
|
3
|
+
import { type SuitePhaseHandler, SuiteRegistryIndex } from '@travetto/test';
|
|
4
4
|
|
|
5
5
|
import { DependencyRegistryIndex } from '../../src/registry/registry-index.ts';
|
|
6
6
|
|
|
@@ -26,4 +26,4 @@ export function InjectableSuite() {
|
|
|
26
26
|
phaseHandlers: [new ModelSuiteHandler(cls)]
|
|
27
27
|
});
|
|
28
28
|
};
|
|
29
|
-
}
|
|
29
|
+
}
|