async-injection 1.2.4 → 1.3.0
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/Changelog.md +43 -1
- package/ReadMe.md +6 -2
- package/lib/cjs/async-factory-provider.d.ts +10 -2
- package/lib/cjs/async-factory-provider.js +11 -1
- package/lib/cjs/async-factory-provider.js.map +1 -1
- package/lib/cjs/bindable-provider.d.ts +34 -2
- package/lib/cjs/bindable-provider.js +52 -22
- package/lib/cjs/bindable-provider.js.map +1 -1
- package/lib/cjs/binder.d.ts +85 -1
- package/lib/cjs/binder.js.map +1 -1
- package/lib/cjs/class-provider.d.ts +24 -2
- package/lib/cjs/class-provider.js +59 -13
- package/lib/cjs/class-provider.js.map +1 -1
- package/lib/cjs/constant-provider.d.ts +5 -1
- package/lib/cjs/constant-provider.js +5 -1
- package/lib/cjs/constant-provider.js.map +1 -1
- package/lib/cjs/constants.d.ts +1 -0
- package/lib/cjs/constants.js +2 -1
- package/lib/cjs/constants.js.map +1 -1
- package/lib/cjs/container.d.ts +54 -1
- package/lib/cjs/container.js +104 -22
- package/lib/cjs/container.js.map +1 -1
- package/lib/cjs/decorators.d.ts +53 -6
- package/lib/cjs/decorators.js +110 -14
- package/lib/cjs/decorators.js.map +1 -1
- package/lib/cjs/index.d.ts +2 -2
- package/lib/cjs/index.js +5 -5
- package/lib/cjs/index.js.map +1 -1
- package/lib/cjs/injector.d.ts +29 -3
- package/lib/cjs/injector.js.map +1 -1
- package/lib/cjs/provider.d.ts +30 -0
- package/lib/cjs/provider.js +38 -1
- package/lib/cjs/provider.js.map +1 -1
- package/lib/cjs/state.d.ts +7 -4
- package/lib/cjs/state.js +6 -2
- package/lib/cjs/state.js.map +1 -1
- package/lib/cjs/sync-factory-provider.d.ts +14 -2
- package/lib/cjs/sync-factory-provider.js +16 -1
- package/lib/cjs/sync-factory-provider.js.map +1 -1
- package/lib/cjs/utils.d.ts +14 -0
- package/lib/cjs/utils.js +48 -0
- package/lib/cjs/utils.js.map +1 -0
- package/lib/esm/async-factory-provider.d.ts +10 -2
- package/lib/esm/async-factory-provider.js +11 -1
- package/lib/esm/async-factory-provider.js.map +1 -1
- package/lib/esm/bindable-provider.d.ts +34 -2
- package/lib/esm/bindable-provider.js +50 -20
- package/lib/esm/bindable-provider.js.map +1 -1
- package/lib/esm/binder.d.ts +85 -1
- package/lib/esm/binder.js.map +1 -1
- package/lib/esm/class-provider.d.ts +24 -2
- package/lib/esm/class-provider.js +59 -13
- package/lib/esm/class-provider.js.map +1 -1
- package/lib/esm/constant-provider.d.ts +5 -1
- package/lib/esm/constant-provider.js +5 -1
- package/lib/esm/constant-provider.js.map +1 -1
- package/lib/esm/constants.d.ts +1 -0
- package/lib/esm/constants.js +1 -0
- package/lib/esm/constants.js.map +1 -1
- package/lib/esm/container.d.ts +54 -1
- package/lib/esm/container.js +104 -22
- package/lib/esm/container.js.map +1 -1
- package/lib/esm/decorators.d.ts +53 -6
- package/lib/esm/decorators.js +109 -14
- package/lib/esm/decorators.js.map +1 -1
- package/lib/esm/index.d.ts +2 -2
- package/lib/esm/index.js +1 -1
- package/lib/esm/index.js.map +1 -1
- package/lib/esm/injector.d.ts +29 -3
- package/lib/esm/injector.js.map +1 -1
- package/lib/esm/provider.d.ts +30 -0
- package/lib/esm/provider.js +38 -1
- package/lib/esm/provider.js.map +1 -1
- package/lib/esm/state.d.ts +7 -4
- package/lib/esm/state.js +6 -2
- package/lib/esm/state.js.map +1 -1
- package/lib/esm/sync-factory-provider.d.ts +14 -2
- package/lib/esm/sync-factory-provider.js +16 -1
- package/lib/esm/sync-factory-provider.js.map +1 -1
- package/lib/esm/utils.d.ts +14 -0
- package/lib/esm/utils.js +42 -0
- package/lib/esm/utils.js.map +1 -0
- package/package.json +17 -12
|
@@ -2,12 +2,34 @@ import { BindableProvider } from './bindable-provider';
|
|
|
2
2
|
import { ClassConstructor, InjectableId, Injector } from './injector';
|
|
3
3
|
import { State } from './state';
|
|
4
4
|
export declare type ResolveStateCallback = (id: InjectableId<any>) => State;
|
|
5
|
+
/**
|
|
6
|
+
* @inheritDoc
|
|
7
|
+
* This specialization invokes it's configured class constructor synchronously and then scans for (and invokes) any @PostConstruct (which may be synchronous or asynchronous).
|
|
8
|
+
*/
|
|
5
9
|
export declare class ClassBasedProvider<T> extends BindableProvider<T, ClassConstructor<T>> {
|
|
6
10
|
protected stateResolver: ResolveStateCallback;
|
|
7
11
|
constructor(injector: Injector, id: InjectableId<T>, maker: ClassConstructor<T>, stateResolver: ResolveStateCallback);
|
|
12
|
+
/**
|
|
13
|
+
* @inheritDoc
|
|
14
|
+
* @see the class description for this Provider.
|
|
15
|
+
* This method is just a singleton guard, the real work is done by provideAsStateImpl.
|
|
16
|
+
*/
|
|
8
17
|
provideAsState(): State<T>;
|
|
18
|
+
/**
|
|
19
|
+
* @inheritDoc
|
|
20
|
+
* This specialization returns undefined if 'asyncOnly' is true and there is no asynchronous PostConstruct annotation (since class constructors can never by asynchronous).
|
|
21
|
+
*/
|
|
9
22
|
resolveIfSingleton(asyncOnly: boolean): Promise<T>;
|
|
10
|
-
|
|
11
|
-
|
|
23
|
+
/**
|
|
24
|
+
* Make a resolved or pending State that reflects any @PostConstruct annotations.
|
|
25
|
+
*/
|
|
26
|
+
protected makePostConstructState(obj: T): State<T>;
|
|
27
|
+
/**
|
|
28
|
+
* This method collects the States of all the constructor parameters for our target class.
|
|
29
|
+
*/
|
|
30
|
+
protected getConstructorParameterStates(): State[];
|
|
31
|
+
/**
|
|
32
|
+
* Gather the needed constructor parameters, invoke the constructor, and figure out what post construction needs done.
|
|
33
|
+
*/
|
|
12
34
|
private provideAsStateImpl;
|
|
13
35
|
}
|
|
@@ -2,11 +2,21 @@ import { BindableProvider } from './bindable-provider';
|
|
|
2
2
|
import { POSTCONSTRUCT_ASYNC_METADATA_KEY, POSTCONSTRUCT_SYNC_METADATA_KEY, REFLECT_PARAMS } from './constants';
|
|
3
3
|
import { _getInjectedIdAt, _getOptionalDefaultAt } from './decorators';
|
|
4
4
|
import { State } from './state';
|
|
5
|
+
import { isPromise } from './utils';
|
|
6
|
+
/**
|
|
7
|
+
* @inheritDoc
|
|
8
|
+
* This specialization invokes it's configured class constructor synchronously and then scans for (and invokes) any @PostConstruct (which may be synchronous or asynchronous).
|
|
9
|
+
*/
|
|
5
10
|
export class ClassBasedProvider extends BindableProvider {
|
|
6
11
|
constructor(injector, id, maker, stateResolver) {
|
|
7
12
|
super(injector, id, maker);
|
|
8
13
|
this.stateResolver = stateResolver;
|
|
9
14
|
}
|
|
15
|
+
/**
|
|
16
|
+
* @inheritDoc
|
|
17
|
+
* @see the class description for this Provider.
|
|
18
|
+
* This method is just a singleton guard, the real work is done by provideAsStateImpl.
|
|
19
|
+
*/
|
|
10
20
|
provideAsState() {
|
|
11
21
|
let retVal = this.singleton;
|
|
12
22
|
if (!retVal) {
|
|
@@ -16,12 +26,20 @@ export class ClassBasedProvider extends BindableProvider {
|
|
|
16
26
|
this.singleton = retVal;
|
|
17
27
|
return retVal;
|
|
18
28
|
}
|
|
29
|
+
/**
|
|
30
|
+
* @inheritDoc
|
|
31
|
+
* This specialization returns undefined if 'asyncOnly' is true and there is no asynchronous PostConstruct annotation (since class constructors can never by asynchronous).
|
|
32
|
+
*/
|
|
19
33
|
resolveIfSingleton(asyncOnly) {
|
|
20
34
|
if ((!asyncOnly) || Reflect.getMetadata(POSTCONSTRUCT_ASYNC_METADATA_KEY, this.maker))
|
|
21
35
|
return super.resolveIfSingleton(false);
|
|
22
36
|
return undefined;
|
|
23
37
|
}
|
|
38
|
+
/**
|
|
39
|
+
* Make a resolved or pending State that reflects any @PostConstruct annotations.
|
|
40
|
+
*/
|
|
24
41
|
makePostConstructState(obj) {
|
|
42
|
+
var _a, _b;
|
|
25
43
|
if (typeof obj === 'object' && (!Array.isArray(obj)) && obj.constructor) {
|
|
26
44
|
let maybeAsync = false;
|
|
27
45
|
let pcFn;
|
|
@@ -32,13 +50,16 @@ export class ClassBasedProvider extends BindableProvider {
|
|
|
32
50
|
};
|
|
33
51
|
}
|
|
34
52
|
else {
|
|
53
|
+
/* eslint-disable @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access */
|
|
54
|
+
// Check to see if there is a @PostConstruct annotation on a method of the class.
|
|
35
55
|
let postConstruct = Reflect.getMetadata(POSTCONSTRUCT_SYNC_METADATA_KEY, obj.constructor);
|
|
36
56
|
if (!postConstruct) {
|
|
37
57
|
maybeAsync = true;
|
|
38
58
|
postConstruct = Reflect.getMetadata(POSTCONSTRUCT_ASYNC_METADATA_KEY, obj.constructor);
|
|
39
59
|
}
|
|
40
60
|
if (postConstruct && obj.constructor.prototype[postConstruct] && typeof obj.constructor.prototype[postConstruct] === 'function')
|
|
41
|
-
pcFn = obj[postConstruct].bind(obj);
|
|
61
|
+
pcFn = (_b = (_a = obj[postConstruct]).bind) === null || _b === void 0 ? void 0 : _b.call(_a, obj);
|
|
62
|
+
/* eslint-enable @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access */
|
|
42
63
|
}
|
|
43
64
|
if (pcFn) {
|
|
44
65
|
let result;
|
|
@@ -46,81 +67,106 @@ export class ClassBasedProvider extends BindableProvider {
|
|
|
46
67
|
result = pcFn();
|
|
47
68
|
}
|
|
48
69
|
catch (err) {
|
|
70
|
+
// The post construction method threw while executing, give the errorHandler (if any) a crack at recovery.
|
|
49
71
|
try {
|
|
50
|
-
obj = this.queryErrorHandler(err, obj);
|
|
72
|
+
obj = this.queryErrorHandler(err, obj); // The returned obj is unlikely to be the original obj.
|
|
51
73
|
return State.MakeState(null, undefined, obj);
|
|
52
74
|
}
|
|
53
75
|
catch (e) {
|
|
76
|
+
// could not recover, propagate the error.
|
|
54
77
|
return State.MakeState(null, e, undefined);
|
|
55
78
|
}
|
|
56
79
|
}
|
|
57
|
-
|
|
80
|
+
// The post construction method says it will let us know when it's finished.
|
|
81
|
+
if (result && (result instanceof Promise || (maybeAsync && isPromise(result)))) {
|
|
82
|
+
// Return a State that is pending (the other return statements in this method return a State which is resolved or rejected).
|
|
58
83
|
return State.MakeState(this.makePromiseForObj(result, () => obj));
|
|
59
84
|
}
|
|
60
85
|
}
|
|
61
86
|
}
|
|
87
|
+
// No PostConstruct, just return a resolved State
|
|
62
88
|
return State.MakeState(null, undefined, obj);
|
|
63
89
|
}
|
|
90
|
+
/**
|
|
91
|
+
* This method collects the States of all the constructor parameters for our target class.
|
|
92
|
+
*/
|
|
64
93
|
getConstructorParameterStates() {
|
|
65
94
|
const argTypes = Reflect.getMetadata(REFLECT_PARAMS, this.maker);
|
|
66
|
-
if (argTypes === undefined) {
|
|
95
|
+
if (argTypes === undefined || !Array.isArray(argTypes)) {
|
|
67
96
|
return [];
|
|
68
97
|
}
|
|
69
98
|
return argTypes.map((argType, index) => {
|
|
99
|
+
// The reflect-metadata API fails on circular dependencies, and will return undefined for the argument instead.
|
|
70
100
|
if (argType === undefined) {
|
|
71
101
|
throw new Error(`Injection error. Recursive dependency in constructor for ${this.maker.toString()} at index ${index}`);
|
|
72
102
|
}
|
|
103
|
+
// Check if an Inject annotation precedes the parameter.
|
|
73
104
|
const overrideToken = _getInjectedIdAt(this.maker, index);
|
|
74
105
|
const actualToken = overrideToken === undefined ? argType : overrideToken;
|
|
106
|
+
// Ask our configured container to resolve the parameter.
|
|
75
107
|
let param = this.stateResolver(actualToken);
|
|
108
|
+
// If the parameter could not be resolved, see if there is an @Optional annotation
|
|
76
109
|
if ((!param.pending) && param.rejected) {
|
|
77
|
-
|
|
110
|
+
const md = _getOptionalDefaultAt(this.maker, index);
|
|
78
111
|
if (md)
|
|
79
112
|
param = State.MakeState(null, undefined, md.value);
|
|
80
113
|
}
|
|
81
114
|
return param;
|
|
82
115
|
});
|
|
83
116
|
}
|
|
117
|
+
/**
|
|
118
|
+
* Gather the needed constructor parameters, invoke the constructor, and figure out what post construction needs done.
|
|
119
|
+
*/
|
|
84
120
|
provideAsStateImpl() {
|
|
85
|
-
|
|
86
|
-
|
|
121
|
+
const params = this.getConstructorParameterStates();
|
|
122
|
+
// If any of the params are in a rejected state, we cannot construct.
|
|
123
|
+
const paramRejection = params.find((p) => {
|
|
87
124
|
return (!p.pending) && p.rejected;
|
|
88
125
|
});
|
|
89
126
|
if (paramRejection) {
|
|
90
127
|
return paramRejection;
|
|
91
128
|
}
|
|
129
|
+
// If any of the params are in a pending state, we will have to wait for them to be resolved before we can construct.
|
|
92
130
|
const pendingParams = params.filter((p) => {
|
|
93
131
|
return p.pending;
|
|
94
132
|
}).map((p) => {
|
|
95
133
|
return p.promise;
|
|
96
134
|
});
|
|
97
135
|
if (pendingParams.length > 0) {
|
|
98
|
-
|
|
136
|
+
// Some of the parameters needed for construction are not yet available, wait for them and then attempt construction.
|
|
137
|
+
const objPromise = this.makePromiseForObj(Promise.all(pendingParams), () => {
|
|
138
|
+
// All the parameters are now available, instantiate the class.
|
|
139
|
+
// If this throws, it will be handled by our caller.
|
|
99
140
|
return Reflect.construct(this.maker, params.map((p) => p.fulfilled));
|
|
100
141
|
});
|
|
142
|
+
// Once the obj is resolved, then we need to check for PostConstruct and if it was async, wait for that too.
|
|
101
143
|
return State.MakeState(objPromise.then((obj) => {
|
|
102
|
-
|
|
144
|
+
const state = this.makePostConstructState(obj);
|
|
103
145
|
if (state.pending) {
|
|
104
|
-
return state.promise;
|
|
146
|
+
return state.promise; // chain (aka wait some more).
|
|
105
147
|
}
|
|
106
148
|
else if (state.rejected) {
|
|
107
|
-
return
|
|
149
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
150
|
+
return state.rejected; // error
|
|
108
151
|
}
|
|
109
152
|
else {
|
|
110
|
-
return state.fulfilled;
|
|
153
|
+
return state.fulfilled; // value (aka obj).
|
|
111
154
|
}
|
|
112
155
|
}));
|
|
113
156
|
}
|
|
114
157
|
else {
|
|
158
|
+
// All parameters needed for construction are available, instantiate the object.
|
|
115
159
|
try {
|
|
116
|
-
|
|
160
|
+
const newObj = Reflect.construct(this.maker, params.map((p) => p.fulfilled));
|
|
117
161
|
return this.makePostConstructState(newObj);
|
|
118
162
|
}
|
|
119
163
|
catch (err) {
|
|
164
|
+
// There was an error, give the errorHandler (if any) a crack at recovery.
|
|
120
165
|
try {
|
|
121
166
|
return State.MakeState(null, undefined, this.queryErrorHandler(err));
|
|
122
167
|
}
|
|
123
168
|
catch (e) {
|
|
169
|
+
// could not recover, propagate the error.
|
|
124
170
|
return State.MakeState(null, e, undefined);
|
|
125
171
|
}
|
|
126
172
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"class-provider.js","sourceRoot":"","sources":["../../src/class-provider.ts"],"names":[],"mappings":"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","sourcesContent":["import { BindableProvider } from './bindable-provider';\nimport { POSTCONSTRUCT_ASYNC_METADATA_KEY, POSTCONSTRUCT_SYNC_METADATA_KEY, REFLECT_PARAMS } from './constants';\nimport { _getInjectedIdAt, _getOptionalDefaultAt } from './decorators';\nimport { ClassConstructor, InjectableId, Injector } from './injector';\nimport { State } from './state';\n\nexport type ResolveStateCallback = (id: InjectableId<any>) => State;\n\n/**\n * @inheritDoc\n * This specialization invokes it's configured class constructor synchronously and then scans for (and invokes) any @PostConstruct (which may be synchronous or asynchronous).\n */\nexport class ClassBasedProvider<T> extends BindableProvider<T, ClassConstructor<T>> {\n\tconstructor(injector: Injector, id: InjectableId<T>, maker: ClassConstructor<T>, protected stateResolver: ResolveStateCallback) {\n\t\tsuper(injector, id, maker);\n\t}\n\n\t/**\n\t * @inheritDoc\n\t * @see the class description for this Provider.\n\t * This method is just a singleton guard, the real work is done by provideAsStateImpl.\n\t */\n\tprovideAsState(): State<T> {\n\t\tlet retVal = this.singleton;\n\t\tif (!retVal) {\n\t\t\tretVal = this.provideAsStateImpl();\n\t\t}\n\t\tif (this.singleton === null)\n\t\t\tthis.singleton = retVal;\n\t\treturn retVal;\n\t}\n\n\t/**\n\t * @inheritDoc\n\t * This specialization returns undefined if 'asyncOnly' is true and there is no asynchronous PostConstruct annotation (since class constructors can never by asynchronous).\n\t */\n\tresolveIfSingleton(asyncOnly: boolean): Promise<T> {\n\t\t// @ts-ignore\n\t\tif ((!asyncOnly) || Reflect.getMetadata(POSTCONSTRUCT_ASYNC_METADATA_KEY, this.maker))\n\t\t\treturn super.resolveIfSingleton(false);\n\t\treturn undefined;\n\t}\n\n\t/**\n\t * Make a resolved or pending State that reflects any @PostConstruct annotations.\n\t */\n\tprotected makePostConstructState(obj: any) {\n\t\tif (typeof obj === 'object' && (!Array.isArray(obj)) && obj.constructor) {\n\t\t\tlet maybeAsync = false;\n\t\t\tlet pcFn: Function;\n\t\t\tif (typeof this.successHandler === 'function') {\n\t\t\t\tmaybeAsync = true;\n\t\t\t\tpcFn = () => {\n\t\t\t\t\treturn this.successHandler(obj, this.injector, this.id, this.maker);\n\t\t\t\t};\n\t\t\t} else {\n\t\t\t\t// Check to see if there is a @PostConstruct annotation on a method of the class.\n\t\t\t\t// @ts-ignore\n\t\t\t\tlet postConstruct = Reflect.getMetadata(POSTCONSTRUCT_SYNC_METADATA_KEY, obj.constructor);\n\t\t\t\tif (!postConstruct) {\n\t\t\t\t\tmaybeAsync = true;\n\t\t\t\t\t// @ts-ignore\n\t\t\t\t\tpostConstruct = Reflect.getMetadata(POSTCONSTRUCT_ASYNC_METADATA_KEY, obj.constructor);\n\t\t\t\t}\n\t\t\t\tif (postConstruct && obj.constructor.prototype[postConstruct] && typeof obj.constructor.prototype[postConstruct] === 'function')\n\t\t\t\t\tpcFn = obj[postConstruct].bind(obj);\n\t\t\t}\n\t\t\tif (pcFn) {\n\t\t\t\tlet result;\n\t\t\t\ttry {\n\t\t\t\t\tresult = pcFn();\n\t\t\t\t}\n\t\t\t\tcatch (err) {\n\t\t\t\t\t// The post construction method threw while executing, give the errorHandler (if any) a crack at recovery.\n\t\t\t\t\ttry {\n\t\t\t\t\t\tobj = this.queryErrorHandler(err, obj); // The returned obj is unlikely to be the original obj.\n\t\t\t\t\t\treturn State.MakeState<T>(null, undefined, obj);\n\t\t\t\t\t}\n\t\t\t\t\tcatch (e) {\n\t\t\t\t\t\t// could not recover, propagate the error.\n\t\t\t\t\t\treturn State.MakeState<T>(null, e, undefined);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t// The post construction method says it will let us know when it's finished.\n\t\t\t\tif (result && (result instanceof Promise || (maybeAsync && typeof result.then === 'function'))) {\n\t\t\t\t\t// Return a State that is pending (the other return statements in this method return a State which is resolved or rejected).\n\t\t\t\t\treturn State.MakeState<T>(this.makePromiseForObj<void>(result, () => obj));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t// No PostConstruct, just return a resolved State\n\t\treturn State.MakeState<T>(null, undefined, obj);\n\t}\n\n\t/**\n\t * This method collects the States of all the constructor parameters for our target class.\n\t */\n\tprotected getConstructorParameterStates<T>(): State[] {\n\t\t// @ts-ignore\n\t\tconst argTypes = Reflect.getMetadata(REFLECT_PARAMS, this.maker);\n\t\tif (argTypes === undefined) {\n\t\t\treturn [];\n\t\t}\n\t\treturn argTypes.map((argType, index) => {\n\t\t\t// The reflect-metadata API fails on circular dependencies, and will return undefined for the argument instead.\n\t\t\tif (argType === undefined) {\n\t\t\t\tthrow new Error(`Injection error. Recursive dependency in constructor for ${this.maker.toString()} at index ${index}`);\n\t\t\t}\n\t\t\t// Check if an Inject annotation precedes the parameter.\n\t\t\tconst overrideToken = _getInjectedIdAt(this.maker, index);\n\t\t\tconst actualToken = overrideToken === undefined ? argType : overrideToken;\n\t\t\t// Ask our configured container to resolve the parameter.\n\t\t\tlet param = this.stateResolver(actualToken);\n\t\t\t// If the parameter could not be resolved, see if there is an @Optional annotation\n\t\t\tif ((!param.pending) && param.rejected) {\n\t\t\t\tlet md = _getOptionalDefaultAt(this.maker, index);\n\t\t\t\tif (md)\n\t\t\t\t\tparam = State.MakeState<any>(null, undefined, md.value);\n\t\t\t}\n\t\t\treturn param;\n\t\t});\n\t}\n\n\t/**\n\t * Gather the needed constructor parameters, invoke the constructor, and figure out what post construction needs done.\n\t */\n\tprivate provideAsStateImpl(): State<T> {\n\t\tlet params = this.getConstructorParameterStates();\n\n\t\t// If any of the params are in a rejected state, we cannot construct.\n\t\tlet paramRejection = params.find((p) => {\n\t\t\treturn (!p.pending) && p.rejected;\n\t\t});\n\t\tif (paramRejection) {\n\t\t\treturn paramRejection;\n\t\t}\n\t\t// If any of the params are in a pending state, we will have to wait for them to be resolved before we can construct.\n\t\tconst pendingParams = params.filter((p) => {\n\t\t\treturn p.pending;\n\t\t}).map((p) => {\n\t\t\treturn p.promise;\n\t\t});\n\t\tif (pendingParams.length > 0) {\n\t\t\t// Some of the parameters needed for construction are not yet available, wait for them and then attempt construction.\n\t\t\tlet objPromise = this.makePromiseForObj<any[]>(Promise.all(pendingParams), () => {\n\t\t\t\t// All the parameters are now available, instantiate the class.\n\t\t\t\t// If this throws, it will be handled by our caller.\n\t\t\t\treturn Reflect.construct(this.maker, params.map((p) => p.fulfilled));\n\t\t\t});\n\t\t\t// Once the obj is resolved, then we need to check for PostConstruct and if it was async, wait for that too.\n\t\t\treturn State.MakeState<T>(objPromise.then((obj) => {\n\t\t\t\tlet state = this.makePostConstructState(obj);\n\t\t\t\tif (state.pending) {\n\t\t\t\t\treturn state.promise; // chain (aka wait some more).\n\t\t\t\t}\n\t\t\t\telse if (state.rejected) {\n\t\t\t\t\treturn state.rejected; // error\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\treturn state.fulfilled; // value (aka obj).\n\t\t\t\t}\n\t\t\t}));\n\t\t}\n\t\telse {\n\t\t\t// All parameters needed for construction are available, instantiate the object.\n\t\t\ttry {\n\t\t\t\tlet newObj = Reflect.construct(this.maker, params.map((p) => p.fulfilled));\n\t\t\t\treturn this.makePostConstructState(newObj);\n\t\t\t}\n\t\t\tcatch (err) {\n\t\t\t\t// There was an error, give the errorHandler (if any) a crack at recovery.\n\t\t\t\ttry {\n\t\t\t\t\treturn State.MakeState<T>(null, undefined, this.queryErrorHandler(err));\n\t\t\t\t}\n\t\t\t\tcatch (e) {\n\t\t\t\t\t// could not recover, propagate the error.\n\t\t\t\t\treturn State.MakeState<T>(null, e, undefined);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"class-provider.js","sourceRoot":"","sources":["../../src/class-provider.ts"],"names":[],"mappings":"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","sourcesContent":["import { BindableProvider } from './bindable-provider';\nimport { POSTCONSTRUCT_ASYNC_METADATA_KEY, POSTCONSTRUCT_SYNC_METADATA_KEY, REFLECT_PARAMS } from './constants';\nimport { _getInjectedIdAt, _getOptionalDefaultAt } from './decorators';\nimport { ClassConstructor, InjectableId, Injector } from './injector';\nimport { State } from './state';\nimport { isPromise } from './utils';\n\nexport type ResolveStateCallback = (id: InjectableId<any>) => State;\n\n/**\n * @inheritDoc\n * This specialization invokes it's configured class constructor synchronously and then scans for (and invokes) any @PostConstruct (which may be synchronous or asynchronous).\n */\nexport class ClassBasedProvider<T> extends BindableProvider<T, ClassConstructor<T>> {\n\tconstructor(injector: Injector, id: InjectableId<T>, maker: ClassConstructor<T>, protected stateResolver: ResolveStateCallback) {\n\t\tsuper(injector, id, maker);\n\t}\n\n\t/**\n\t * @inheritDoc\n\t * @see the class description for this Provider.\n\t * This method is just a singleton guard, the real work is done by provideAsStateImpl.\n\t */\n\tprovideAsState(): State<T> {\n\t\tlet retVal = this.singleton;\n\t\tif (!retVal) {\n\t\t\tretVal = this.provideAsStateImpl();\n\t\t}\n\t\tif (this.singleton === null)\n\t\t\tthis.singleton = retVal;\n\t\treturn retVal;\n\t}\n\n\t/**\n\t * @inheritDoc\n\t * This specialization returns undefined if 'asyncOnly' is true and there is no asynchronous PostConstruct annotation (since class constructors can never by asynchronous).\n\t */\n\tresolveIfSingleton(asyncOnly: boolean): Promise<T> {\n\t\tif ((!asyncOnly) || Reflect.getMetadata(POSTCONSTRUCT_ASYNC_METADATA_KEY, this.maker))\n\t\t\treturn super.resolveIfSingleton(false);\n\t\treturn undefined;\n\t}\n\n\t/**\n\t * Make a resolved or pending State that reflects any @PostConstruct annotations.\n\t */\n\tprotected makePostConstructState(obj: T): State<T> {\n\t\tif (typeof obj === 'object' && (!Array.isArray(obj)) && obj.constructor) {\n\t\t\tlet maybeAsync = false;\n\t\t\tlet pcFn: () => void | Error | Promise<void | Error>;\n\t\t\tif (typeof this.successHandler === 'function') {\n\t\t\t\tmaybeAsync = true;\n\t\t\t\tpcFn = () => {\n\t\t\t\t\treturn this.successHandler(obj, this.injector, this.id, this.maker);\n\t\t\t\t};\n\t\t\t} else {\n\t\t\t\t/* eslint-disable @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access */\n\t\t\t\t// Check to see if there is a @PostConstruct annotation on a method of the class.\n\t\t\t\tlet postConstruct: string = Reflect.getMetadata(POSTCONSTRUCT_SYNC_METADATA_KEY, obj.constructor);\n\t\t\t\tif (!postConstruct) {\n\t\t\t\t\tmaybeAsync = true;\n\t\t\t\t\tpostConstruct = Reflect.getMetadata(POSTCONSTRUCT_ASYNC_METADATA_KEY, obj.constructor);\n\t\t\t\t}\n\t\t\t\tif (postConstruct && obj.constructor.prototype[postConstruct] && typeof obj.constructor.prototype[postConstruct] === 'function')\n\t\t\t\t\tpcFn = obj[postConstruct].bind?.(obj);\n\n\t\t\t\t/* eslint-enable @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access */\n\t\t\t}\n\t\t\tif (pcFn) {\n\t\t\t\tlet result: any;\n\t\t\t\ttry {\n\t\t\t\t\tresult = pcFn();\n\t\t\t\t}\n\t\t\t\tcatch (err) {\n\t\t\t\t\t// The post construction method threw while executing, give the errorHandler (if any) a crack at recovery.\n\t\t\t\t\ttry {\n\t\t\t\t\t\tobj = this.queryErrorHandler(err, obj); // The returned obj is unlikely to be the original obj.\n\t\t\t\t\t\treturn State.MakeState<T>(null, undefined, obj);\n\t\t\t\t\t}\n\t\t\t\t\tcatch (e) {\n\t\t\t\t\t\t// could not recover, propagate the error.\n\t\t\t\t\t\treturn State.MakeState<T>(null, e, undefined);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t// The post construction method says it will let us know when it's finished.\n\t\t\t\tif (result && (result instanceof Promise || (maybeAsync && isPromise<void>(result)))) {\n\t\t\t\t\t// Return a State that is pending (the other return statements in this method return a State which is resolved or rejected).\n\t\t\t\t\treturn State.MakeState<T>(this.makePromiseForObj<void>(result, () => obj));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t// No PostConstruct, just return a resolved State\n\t\treturn State.MakeState<T>(null, undefined, obj);\n\t}\n\n\t/**\n\t * This method collects the States of all the constructor parameters for our target class.\n\t */\n\tprotected getConstructorParameterStates(): State[] {\n\t\tconst argTypes = Reflect.getMetadata(REFLECT_PARAMS, this.maker);\n\t\tif (argTypes === undefined || !Array.isArray(argTypes)) {\n\t\t\treturn [];\n\t\t}\n\t\treturn argTypes.map((argType, index) => {\n\t\t\t// The reflect-metadata API fails on circular dependencies, and will return undefined for the argument instead.\n\t\t\tif (argType === undefined) {\n\t\t\t\tthrow new Error(`Injection error. Recursive dependency in constructor for ${this.maker.toString()} at index ${index}`);\n\t\t\t}\n\t\t\t// Check if an Inject annotation precedes the parameter.\n\t\t\tconst overrideToken = _getInjectedIdAt(this.maker, index);\n\t\t\tconst actualToken = overrideToken === undefined ? argType : overrideToken;\n\t\t\t// Ask our configured container to resolve the parameter.\n\t\t\tlet param = this.stateResolver(actualToken);\n\t\t\t// If the parameter could not be resolved, see if there is an @Optional annotation\n\t\t\tif ((!param.pending) && param.rejected) {\n\t\t\t\tconst md = _getOptionalDefaultAt(this.maker, index);\n\t\t\t\tif (md)\n\t\t\t\t\tparam = State.MakeState<any>(null, undefined, md.value);\n\t\t\t}\n\t\t\treturn param;\n\t\t});\n\t}\n\n\t/**\n\t * Gather the needed constructor parameters, invoke the constructor, and figure out what post construction needs done.\n\t */\n\tprivate provideAsStateImpl(): State<T> {\n\t\tconst params = this.getConstructorParameterStates();\n\n\t\t// If any of the params are in a rejected state, we cannot construct.\n\t\tconst paramRejection = params.find((p) => {\n\t\t\treturn (!p.pending) && p.rejected;\n\t\t});\n\t\tif (paramRejection) {\n\t\t\treturn paramRejection as State<T>;\n\t\t}\n\t\t// If any of the params are in a pending state, we will have to wait for them to be resolved before we can construct.\n\t\tconst pendingParams = params.filter((p) => {\n\t\t\treturn p.pending;\n\t\t}).map((p) => {\n\t\t\treturn p.promise;\n\t\t});\n\t\tif (pendingParams.length > 0) {\n\t\t\t// Some of the parameters needed for construction are not yet available, wait for them and then attempt construction.\n\t\t\tconst objPromise = this.makePromiseForObj<any[]>(Promise.all(pendingParams), () => {\n\t\t\t\t// All the parameters are now available, instantiate the class.\n\t\t\t\t// If this throws, it will be handled by our caller.\n\t\t\t\treturn Reflect.construct(this.maker, params.map((p) => p.fulfilled as unknown)) as T;\n\t\t\t});\n\t\t\t// Once the obj is resolved, then we need to check for PostConstruct and if it was async, wait for that too.\n\t\t\treturn State.MakeState<T>(objPromise.then((obj) => {\n\t\t\t\tconst state = this.makePostConstructState(obj);\n\t\t\t\tif (state.pending) {\n\t\t\t\t\treturn state.promise; // chain (aka wait some more).\n\t\t\t\t}\n\t\t\t\telse if (state.rejected) {\n\t\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-return\n\t\t\t\t\treturn state.rejected as any; // error\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\treturn state.fulfilled; // value (aka obj).\n\t\t\t\t}\n\t\t\t}));\n\t\t}\n\t\telse {\n\t\t\t// All parameters needed for construction are available, instantiate the object.\n\t\t\ttry {\n\t\t\t\tconst newObj = Reflect.construct(this.maker, params.map((p) => p.fulfilled as unknown));\n\t\t\t\treturn this.makePostConstructState(newObj);\n\t\t\t}\n\t\t\tcatch (err) {\n\t\t\t\t// There was an error, give the errorHandler (if any) a crack at recovery.\n\t\t\t\ttry {\n\t\t\t\t\treturn State.MakeState<T>(null, undefined, this.queryErrorHandler(err));\n\t\t\t\t}\n\t\t\t\tcatch (e) {\n\t\t\t\t\t// could not recover, propagate the error.\n\t\t\t\t\treturn State.MakeState<T>(null, e, undefined);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n"]}
|
|
@@ -1,5 +1,9 @@
|
|
|
1
|
-
import { State } from './state';
|
|
2
1
|
import { Provider } from './provider';
|
|
2
|
+
import { State } from './state';
|
|
3
|
+
/**
|
|
4
|
+
* @inheritDoc
|
|
5
|
+
* This specialization is always a Singleton.
|
|
6
|
+
*/
|
|
3
7
|
export declare class ConstantProvider<T> extends Provider<T> {
|
|
4
8
|
constructor(constant: T);
|
|
5
9
|
provideAsState(): State<T>;
|
|
@@ -1,5 +1,9 @@
|
|
|
1
|
-
import { State } from './state';
|
|
2
1
|
import { Provider } from './provider';
|
|
2
|
+
import { State } from './state';
|
|
3
|
+
/**
|
|
4
|
+
* @inheritDoc
|
|
5
|
+
* This specialization is always a Singleton.
|
|
6
|
+
*/
|
|
3
7
|
export class ConstantProvider extends Provider {
|
|
4
8
|
constructor(constant) {
|
|
5
9
|
super();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constant-provider.js","sourceRoot":"","sources":["../../src/constant-provider.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"constant-provider.js","sourceRoot":"","sources":["../../src/constant-provider.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAEhC;;;GAGG;AACH,MAAM,OAAO,gBAAoB,SAAQ,QAAW;IACnD,YAAY,QAAW;QACtB,KAAK,EAAE,CAAC;QACR,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC,SAAS,CAAI,IAAI,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC;IAChE,CAAC;IAED,cAAc;QACb,OAAO,IAAI,CAAC,SAAS,CAAC;IACvB,CAAC;CACD","sourcesContent":["import { Provider } from './provider';\nimport { State } from './state';\n\n/**\n * @inheritDoc\n * This specialization is always a Singleton.\n */\nexport class ConstantProvider<T> extends Provider<T> {\n\tconstructor(constant: T) {\n\t\tsuper();\n\t\tthis.singleton = State.MakeState<T>(null, undefined, constant);\n\t}\n\n\tprovideAsState(): State<T> {\n\t\treturn this.singleton;\n\t}\n}\n"]}
|
package/lib/esm/constants.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ export declare const INJECTABLE_METADATA_KEY: unique symbol;
|
|
|
2
2
|
export declare const INJECT_METADATA_KEY: unique symbol;
|
|
3
3
|
export declare const POSTCONSTRUCT_SYNC_METADATA_KEY: unique symbol;
|
|
4
4
|
export declare const POSTCONSTRUCT_ASYNC_METADATA_KEY: unique symbol;
|
|
5
|
+
export declare const RELEASE_METADATA_KEY: unique symbol;
|
|
5
6
|
export declare const OPTIONAL_METADATA_KEY: unique symbol;
|
|
6
7
|
export declare const REFLECT_PARAMS = "design:paramtypes";
|
|
7
8
|
export declare const REFLECT_RETURN = "design:returntype";
|
package/lib/esm/constants.js
CHANGED
|
@@ -2,6 +2,7 @@ export const INJECTABLE_METADATA_KEY = Symbol('INJECTABLE_KEY');
|
|
|
2
2
|
export const INJECT_METADATA_KEY = Symbol('INJECT_KEY');
|
|
3
3
|
export const POSTCONSTRUCT_SYNC_METADATA_KEY = Symbol('POSTCONSTRUCT_SYNC_KEY');
|
|
4
4
|
export const POSTCONSTRUCT_ASYNC_METADATA_KEY = Symbol('POSTCONSTRUCT_ASYNC_KEY');
|
|
5
|
+
export const RELEASE_METADATA_KEY = Symbol('RELEASE_KEY');
|
|
5
6
|
export const OPTIONAL_METADATA_KEY = Symbol('OPTIONAL_KEY');
|
|
6
7
|
export const REFLECT_PARAMS = 'design:paramtypes';
|
|
7
8
|
export const REFLECT_RETURN = 'design:returntype';
|
package/lib/esm/constants.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../../src/constants.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,uBAAuB,GAAG,MAAM,CAAC,gBAAgB,CAAC,CAAC;AAChE,MAAM,CAAC,MAAM,mBAAmB,GAAG,MAAM,CAAC,YAAY,CAAC,CAAC;AACxD,MAAM,CAAC,MAAM,+BAA+B,GAAG,MAAM,CAAC,wBAAwB,CAAC,CAAC;AAChF,MAAM,CAAC,MAAM,gCAAgC,GAAG,MAAM,CAAC,yBAAyB,CAAC,CAAC;AAClF,MAAM,CAAC,MAAM,qBAAqB,GAAG,MAAM,CAAC,cAAc,CAAC,CAAC;AAE5D,MAAM,CAAC,MAAM,cAAc,GAAG,mBAAmB,CAAC;AAClD,MAAM,CAAC,MAAM,cAAc,GAAG,mBAAmB,CAAC","sourcesContent":["export const INJECTABLE_METADATA_KEY = Symbol('INJECTABLE_KEY');\nexport const INJECT_METADATA_KEY = Symbol('INJECT_KEY');\nexport const POSTCONSTRUCT_SYNC_METADATA_KEY = Symbol('POSTCONSTRUCT_SYNC_KEY');\nexport const POSTCONSTRUCT_ASYNC_METADATA_KEY = Symbol('POSTCONSTRUCT_ASYNC_KEY');\nexport const OPTIONAL_METADATA_KEY = Symbol('OPTIONAL_KEY');\n\nexport const REFLECT_PARAMS = 'design:paramtypes';\nexport const REFLECT_RETURN = 'design:returntype';\n"]}
|
|
1
|
+
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../../src/constants.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,uBAAuB,GAAG,MAAM,CAAC,gBAAgB,CAAC,CAAC;AAChE,MAAM,CAAC,MAAM,mBAAmB,GAAG,MAAM,CAAC,YAAY,CAAC,CAAC;AACxD,MAAM,CAAC,MAAM,+BAA+B,GAAG,MAAM,CAAC,wBAAwB,CAAC,CAAC;AAChF,MAAM,CAAC,MAAM,gCAAgC,GAAG,MAAM,CAAC,yBAAyB,CAAC,CAAC;AAClF,MAAM,CAAC,MAAM,oBAAoB,GAAG,MAAM,CAAC,aAAa,CAAC,CAAC;AAC1D,MAAM,CAAC,MAAM,qBAAqB,GAAG,MAAM,CAAC,cAAc,CAAC,CAAC;AAE5D,MAAM,CAAC,MAAM,cAAc,GAAG,mBAAmB,CAAC;AAClD,MAAM,CAAC,MAAM,cAAc,GAAG,mBAAmB,CAAC","sourcesContent":["export const INJECTABLE_METADATA_KEY = Symbol('INJECTABLE_KEY');\nexport const INJECT_METADATA_KEY = Symbol('INJECT_KEY');\nexport const POSTCONSTRUCT_SYNC_METADATA_KEY = Symbol('POSTCONSTRUCT_SYNC_KEY');\nexport const POSTCONSTRUCT_ASYNC_METADATA_KEY = Symbol('POSTCONSTRUCT_ASYNC_KEY');\nexport const RELEASE_METADATA_KEY = Symbol('RELEASE_KEY');\nexport const OPTIONAL_METADATA_KEY = Symbol('OPTIONAL_KEY');\n\nexport const REFLECT_PARAMS = 'design:paramtypes';\nexport const REFLECT_RETURN = 'design:returntype';\n"]}
|
package/lib/esm/container.d.ts
CHANGED
|
@@ -2,19 +2,72 @@ import { AsyncFactory, BindAs, Binder, SyncFactory } from './binder';
|
|
|
2
2
|
import { AbstractConstructor, ClassConstructor, InjectableId, Injector } from './injector';
|
|
3
3
|
import { Provider } from './provider';
|
|
4
4
|
import { State } from './state';
|
|
5
|
+
/**
|
|
6
|
+
* Binder and Injector (aka Container) to handle (a)synchronous dependency management.
|
|
7
|
+
*/
|
|
5
8
|
export declare class Container implements Binder {
|
|
6
9
|
protected parent?: Injector;
|
|
10
|
+
/**
|
|
11
|
+
* Create a new Container, with an optional parent Injector which will be searched if any given InjectableId is not bound within this Container.
|
|
12
|
+
*/
|
|
7
13
|
constructor(parent?: Injector);
|
|
8
14
|
protected providers: Map<InjectableId<any>, Provider<any>>;
|
|
15
|
+
/**
|
|
16
|
+
* @inheritDoc
|
|
17
|
+
*/
|
|
9
18
|
isIdKnown<T>(id: InjectableId<T>, ascending?: boolean): boolean;
|
|
19
|
+
/**
|
|
20
|
+
* @inheritDoc
|
|
21
|
+
*/
|
|
10
22
|
get<T>(id: InjectableId<T>): T;
|
|
23
|
+
/**
|
|
24
|
+
* @inheritDoc
|
|
25
|
+
*/
|
|
11
26
|
resolve<T>(id: InjectableId<T>): Promise<T>;
|
|
12
|
-
|
|
27
|
+
/**
|
|
28
|
+
* This method is not part of the Binding interface, because it is highly unusual.
|
|
29
|
+
* But that doesn't mean we can't imagine scenarios where you might require it.
|
|
30
|
+
*
|
|
31
|
+
* @param id The id to be removed.
|
|
32
|
+
* @param ascending If true, this will remove all bindings of the specified id all the way up the parent container chain (if it exists).
|
|
33
|
+
* @param releaseIfSingleton If true, @Provider.releaseIfSingleton will be invoked before the binding is removed.
|
|
34
|
+
*/
|
|
35
|
+
removeBinding<T>(id: InjectableId<T>, ascending?: boolean, releaseIfSingleton?: boolean): void;
|
|
36
|
+
/**
|
|
37
|
+
* @inheritDoc
|
|
38
|
+
*/
|
|
13
39
|
bindConstant<T>(id: InjectableId<T>, value: T): void;
|
|
40
|
+
/**
|
|
41
|
+
* @inheritDoc
|
|
42
|
+
*/
|
|
14
43
|
bindClass<T>(id: ClassConstructor<T>, constructor?: ClassConstructor<T>): BindAs<T, ClassConstructor<T>>;
|
|
15
44
|
bindClass<T>(id: string | symbol | AbstractConstructor<T>, constructor: ClassConstructor<T>): BindAs<T, ClassConstructor<T>>;
|
|
45
|
+
/**
|
|
46
|
+
* @inheritDoc
|
|
47
|
+
*/
|
|
16
48
|
bindFactory<T>(id: InjectableId<T>, factory: SyncFactory<T>): BindAs<T, SyncFactory<T>>;
|
|
49
|
+
/**
|
|
50
|
+
* @inheritDoc
|
|
51
|
+
*/
|
|
17
52
|
bindAsyncFactory<T>(id: InjectableId<T>, factory: AsyncFactory<T>): BindAs<T, AsyncFactory<T>>;
|
|
53
|
+
/**
|
|
54
|
+
* @inheritDoc
|
|
55
|
+
*/
|
|
18
56
|
resolveSingletons(asyncOnly?: boolean, parentRecursion?: boolean): Promise<void>;
|
|
57
|
+
/**
|
|
58
|
+
* As implied by the name prefix, this is a factored out method invoked only by the 'resolve' method.
|
|
59
|
+
* It makes searching our parent (if it exists) easier (and quicker) IF our parent is a fellow instance of Container.
|
|
60
|
+
*/
|
|
19
61
|
protected resolveState<T>(id: InjectableId<T>): State<T>;
|
|
62
|
+
/**
|
|
63
|
+
* Convenience method to assist in releasing non-garbage-collectable resources that Singletons in this Container may have allocated.
|
|
64
|
+
* It will walk through all registered Providers (of this Container only), and invoke their @see Provider.releaseIfSingleton method.
|
|
65
|
+
* This method is not part of the Binding interface, because you normally only create (and release) Containers.
|
|
66
|
+
* NOTE:
|
|
67
|
+
* This *only* releases active/pending Singleton's that have already been created by this Container.
|
|
68
|
+
* The most likely use of this method would be when you have created a new child Container for a limited duration transaction, and you want to easily cleanup temporary resources.
|
|
69
|
+
* For example, your service object may need to know when it should unsubscribe from an RxJs stream (failure to do so can result in your Singleton not being garbage collected at the end of a transaction).
|
|
70
|
+
* In theory, you could handle all unsubscription and cleanup yourself, but the @Release decorator and this method are meant to simply make that easier.
|
|
71
|
+
*/
|
|
72
|
+
releaseSingletons(): void;
|
|
20
73
|
}
|
package/lib/esm/container.js
CHANGED
|
@@ -4,16 +4,31 @@ import { ConstantProvider } from './constant-provider';
|
|
|
4
4
|
import { INJECTABLE_METADATA_KEY } from './constants';
|
|
5
5
|
import { State } from './state';
|
|
6
6
|
import { FactoryBasedProvider } from './sync-factory-provider';
|
|
7
|
+
import { isPromise } from './utils';
|
|
8
|
+
/**
|
|
9
|
+
* Helper class to ensure we can distinguish between Error instances legitimately returned from Providers, and Errors thrown by Providers.
|
|
10
|
+
*
|
|
11
|
+
* @see resolveSingletons.
|
|
12
|
+
*/
|
|
7
13
|
class ReasonWrapper {
|
|
8
14
|
constructor(reason) {
|
|
9
15
|
this.reason = reason;
|
|
10
16
|
}
|
|
11
17
|
}
|
|
18
|
+
/**
|
|
19
|
+
* Binder and Injector (aka Container) to handle (a)synchronous dependency management.
|
|
20
|
+
*/
|
|
12
21
|
export class Container {
|
|
22
|
+
/**
|
|
23
|
+
* Create a new Container, with an optional parent Injector which will be searched if any given InjectableId is not bound within this Container.
|
|
24
|
+
*/
|
|
13
25
|
constructor(parent) {
|
|
14
26
|
this.parent = parent;
|
|
15
27
|
this.providers = new Map();
|
|
16
28
|
}
|
|
29
|
+
/**
|
|
30
|
+
* @inheritDoc
|
|
31
|
+
*/
|
|
17
32
|
isIdKnown(id, ascending) {
|
|
18
33
|
if (!!this.providers.get(id))
|
|
19
34
|
return true;
|
|
@@ -21,23 +36,29 @@ export class Container {
|
|
|
21
36
|
return this.parent.isIdKnown(id, true);
|
|
22
37
|
return false;
|
|
23
38
|
}
|
|
39
|
+
/**
|
|
40
|
+
* @inheritDoc
|
|
41
|
+
*/
|
|
24
42
|
get(id) {
|
|
25
|
-
|
|
43
|
+
const provider = this.providers.get(id);
|
|
26
44
|
if (!provider) {
|
|
27
45
|
if (this.parent)
|
|
28
46
|
return this.parent.get(id);
|
|
29
47
|
throw new Error('Symbol not bound: ' + id.toString());
|
|
30
48
|
}
|
|
31
|
-
|
|
49
|
+
const state = provider.provideAsState();
|
|
32
50
|
if (state.pending)
|
|
33
51
|
throw new Error('Synchronous request on unresolved asynchronous dependency tree: ' + id.toString());
|
|
34
52
|
if (state.rejected)
|
|
35
53
|
throw state.rejected;
|
|
36
54
|
return state.fulfilled;
|
|
37
55
|
}
|
|
56
|
+
/**
|
|
57
|
+
* @inheritDoc
|
|
58
|
+
*/
|
|
38
59
|
resolve(id) {
|
|
39
|
-
|
|
40
|
-
if (state.promise) {
|
|
60
|
+
const state = this.resolveState(id);
|
|
61
|
+
if (isPromise(state.promise)) {
|
|
41
62
|
return state.promise;
|
|
42
63
|
}
|
|
43
64
|
if (state.rejected) {
|
|
@@ -45,11 +66,31 @@ export class Container {
|
|
|
45
66
|
}
|
|
46
67
|
return Promise.resolve(state.fulfilled);
|
|
47
68
|
}
|
|
48
|
-
|
|
69
|
+
// noinspection JSUnusedGlobalSymbols
|
|
70
|
+
/**
|
|
71
|
+
* This method is not part of the Binding interface, because it is highly unusual.
|
|
72
|
+
* But that doesn't mean we can't imagine scenarios where you might require it.
|
|
73
|
+
*
|
|
74
|
+
* @param id The id to be removed.
|
|
75
|
+
* @param ascending If true, this will remove all bindings of the specified id all the way up the parent container chain (if it exists).
|
|
76
|
+
* @param releaseIfSingleton If true, @Provider.releaseIfSingleton will be invoked before the binding is removed.
|
|
77
|
+
*/
|
|
78
|
+
removeBinding(id, ascending, releaseIfSingleton) {
|
|
79
|
+
var _a;
|
|
80
|
+
if (releaseIfSingleton) {
|
|
81
|
+
const p = this.providers.get(id);
|
|
82
|
+
if (p)
|
|
83
|
+
p.releaseIfSingleton();
|
|
84
|
+
}
|
|
49
85
|
this.providers.delete(id);
|
|
50
|
-
if (ascending && this.parent
|
|
51
|
-
|
|
86
|
+
if (ascending && this.parent) {
|
|
87
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call
|
|
88
|
+
(_a = this.parent) === null || _a === void 0 ? void 0 : _a.removeBinding(id, true, releaseIfSingleton);
|
|
89
|
+
}
|
|
52
90
|
}
|
|
91
|
+
/**
|
|
92
|
+
* @inheritDoc
|
|
93
|
+
*/
|
|
53
94
|
bindConstant(id, value) {
|
|
54
95
|
this.providers.set(id, new ConstantProvider(value));
|
|
55
96
|
}
|
|
@@ -60,62 +101,87 @@ export class Container {
|
|
|
60
101
|
if (!Reflect.getMetadata(INJECTABLE_METADATA_KEY, constructor)) {
|
|
61
102
|
throw new Error('Class not decorated with @Injectable [' + constructor.toString() + ']');
|
|
62
103
|
}
|
|
63
|
-
|
|
104
|
+
const provider = new ClassBasedProvider(this, id, constructor, (i) => {
|
|
64
105
|
return this.resolveState(i);
|
|
65
106
|
});
|
|
66
107
|
this.providers.set(id, provider);
|
|
67
108
|
return provider.makeBindAs();
|
|
68
109
|
}
|
|
110
|
+
/**
|
|
111
|
+
* @inheritDoc
|
|
112
|
+
*/
|
|
69
113
|
bindFactory(id, factory) {
|
|
70
|
-
|
|
114
|
+
const provider = new FactoryBasedProvider(this, id, factory);
|
|
71
115
|
this.providers.set(id, provider);
|
|
72
116
|
return provider.makeBindAs();
|
|
73
117
|
}
|
|
118
|
+
/**
|
|
119
|
+
* @inheritDoc
|
|
120
|
+
*/
|
|
74
121
|
bindAsyncFactory(id, factory) {
|
|
75
|
-
|
|
122
|
+
const provider = new AsyncFactoryBasedProvider(this, id, factory);
|
|
76
123
|
this.providers.set(id, provider);
|
|
77
124
|
return provider.makeBindAs();
|
|
78
125
|
}
|
|
126
|
+
/**
|
|
127
|
+
* @inheritDoc
|
|
128
|
+
*/
|
|
79
129
|
resolveSingletons(asyncOnly, parentRecursion) {
|
|
80
|
-
|
|
130
|
+
var _a;
|
|
131
|
+
const makePromiseToResolve = () => {
|
|
81
132
|
return new Promise((resolve, reject) => {
|
|
82
|
-
|
|
133
|
+
const pending = new Map();
|
|
134
|
+
// Ask each provider to resolve itself *IF* it is a singleton.
|
|
83
135
|
this.providers.forEach((value, key) => {
|
|
84
|
-
|
|
85
|
-
|
|
136
|
+
// If the provider is a singleton *and* if resolution is being handled asynchronously, the provider will return a completion promise.
|
|
137
|
+
const p = value.resolveIfSingleton(asyncOnly);
|
|
138
|
+
if (p !== null && typeof p !== 'undefined')
|
|
86
139
|
pending.set(key, p);
|
|
87
140
|
});
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
141
|
+
// The contract for this method is that it behaves somewhat like Promise.allSettled (e.g. won't complete until all pending Singletons have settled).
|
|
142
|
+
// Further the contract states that if any of the asynchronous Singletons rejected, that we will also return a rejected Promise, and that the rejection reason will be a Map of the InjectableId's that did not resolve, and the Error they emitted.
|
|
143
|
+
const pp = Array.from(pending.values());
|
|
144
|
+
const keys = Array.from(pending.keys());
|
|
145
|
+
// Mapping the catch is an alternate version of Promise.allSettled (e.g. keeps Promise.all from short-circuiting).
|
|
146
|
+
Promise.all(pp
|
|
147
|
+
.map(p => p.catch(e => new ReasonWrapper(e))))
|
|
148
|
+
.then((results) => {
|
|
149
|
+
const rejects = new Map();
|
|
150
|
+
// Check the results. Since we don't export ReasonWrapper, it is safe to assume that an instance of that was produced by our map => catch code above, so it's a rejected Singleton error.
|
|
92
151
|
results.forEach((result, idx) => {
|
|
93
152
|
if (result instanceof ReasonWrapper) {
|
|
94
153
|
rejects.set(keys[idx], result.reason);
|
|
95
154
|
}
|
|
96
155
|
});
|
|
156
|
+
// If we had rejections, notify our caller what they were.
|
|
97
157
|
if (rejects.size > 0)
|
|
98
158
|
reject(rejects);
|
|
99
159
|
else
|
|
100
|
-
resolve();
|
|
160
|
+
resolve(); // All good.
|
|
101
161
|
});
|
|
102
162
|
});
|
|
103
163
|
};
|
|
104
|
-
if (parentRecursion && this.parent
|
|
105
|
-
|
|
164
|
+
if (parentRecursion && typeof ((_a = this.parent) === null || _a === void 0 ? void 0 : _a.resolveSingletons) === "function") {
|
|
165
|
+
const pb = this.parent;
|
|
106
166
|
return pb.resolveSingletons(asyncOnly, parentRecursion).then(() => {
|
|
107
167
|
return makePromiseToResolve();
|
|
108
168
|
});
|
|
109
169
|
}
|
|
110
170
|
return makePromiseToResolve();
|
|
111
171
|
}
|
|
172
|
+
/**
|
|
173
|
+
* As implied by the name prefix, this is a factored out method invoked only by the 'resolve' method.
|
|
174
|
+
* It makes searching our parent (if it exists) easier (and quicker) IF our parent is a fellow instance of Container.
|
|
175
|
+
*/
|
|
112
176
|
resolveState(id) {
|
|
113
|
-
|
|
177
|
+
const provider = this.providers.get(id);
|
|
114
178
|
if (!provider) {
|
|
115
179
|
if (this.parent) {
|
|
116
180
|
if (this.parent instanceof Container) {
|
|
117
181
|
return this.parent.resolveState(id);
|
|
118
182
|
}
|
|
183
|
+
// This code (below) will only ever execute if the creator of this container passes in their own implementation of an Injector.
|
|
184
|
+
/* istanbul ignore next */
|
|
119
185
|
try {
|
|
120
186
|
return State.MakeState(this.parent.resolve(id), undefined, undefined);
|
|
121
187
|
}
|
|
@@ -127,5 +193,21 @@ export class Container {
|
|
|
127
193
|
}
|
|
128
194
|
return provider.provideAsState();
|
|
129
195
|
}
|
|
196
|
+
// noinspection JSUnusedGlobalSymbols
|
|
197
|
+
/**
|
|
198
|
+
* Convenience method to assist in releasing non-garbage-collectable resources that Singletons in this Container may have allocated.
|
|
199
|
+
* It will walk through all registered Providers (of this Container only), and invoke their @see Provider.releaseIfSingleton method.
|
|
200
|
+
* This method is not part of the Binding interface, because you normally only create (and release) Containers.
|
|
201
|
+
* NOTE:
|
|
202
|
+
* This *only* releases active/pending Singleton's that have already been created by this Container.
|
|
203
|
+
* The most likely use of this method would be when you have created a new child Container for a limited duration transaction, and you want to easily cleanup temporary resources.
|
|
204
|
+
* For example, your service object may need to know when it should unsubscribe from an RxJs stream (failure to do so can result in your Singleton not being garbage collected at the end of a transaction).
|
|
205
|
+
* In theory, you could handle all unsubscription and cleanup yourself, but the @Release decorator and this method are meant to simply make that easier.
|
|
206
|
+
*/
|
|
207
|
+
releaseSingletons() {
|
|
208
|
+
this.providers.forEach((value) => {
|
|
209
|
+
value.releaseIfSingleton();
|
|
210
|
+
});
|
|
211
|
+
}
|
|
130
212
|
}
|
|
131
213
|
//# sourceMappingURL=container.js.map
|