@loopback/context 4.0.0-alpha.8 → 4.1.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/LICENSE +25 -0
- package/README.md +116 -0
- package/dist/binding-config.d.ts +40 -0
- package/dist/binding-config.js +33 -0
- package/dist/binding-config.js.map +1 -0
- package/dist/binding-decorator.d.ts +45 -0
- package/dist/binding-decorator.js +118 -0
- package/dist/binding-decorator.js.map +1 -0
- package/dist/binding-filter.d.ts +108 -0
- package/dist/binding-filter.js +162 -0
- package/dist/binding-filter.js.map +1 -0
- package/dist/binding-inspector.d.ts +150 -0
- package/dist/binding-inspector.js +249 -0
- package/dist/binding-inspector.js.map +1 -0
- package/dist/binding-key.d.ts +66 -0
- package/dist/binding-key.js +121 -0
- package/dist/binding-key.js.map +1 -0
- package/dist/binding-sorter.d.ts +71 -0
- package/dist/binding-sorter.js +89 -0
- package/dist/binding-sorter.js.map +1 -0
- package/dist/binding.d.ts +577 -0
- package/dist/binding.js +788 -0
- package/dist/binding.js.map +1 -0
- package/dist/context-event.d.ts +23 -0
- package/dist/context-event.js +7 -0
- package/dist/context-event.js.map +1 -0
- package/dist/context-observer.d.ts +36 -0
- package/dist/context-observer.js +7 -0
- package/dist/context-observer.js.map +1 -0
- package/dist/context-subscription.d.ts +147 -0
- package/dist/context-subscription.js +317 -0
- package/dist/context-subscription.js.map +1 -0
- package/dist/context-tag-indexer.d.ts +42 -0
- package/dist/context-tag-indexer.js +135 -0
- package/dist/context-tag-indexer.js.map +1 -0
- package/dist/context-view.d.ts +209 -0
- package/dist/context-view.js +240 -0
- package/dist/context-view.js.map +1 -0
- package/dist/context.d.ts +513 -0
- package/dist/context.js +717 -0
- package/dist/context.js.map +1 -0
- package/dist/index.d.ts +52 -0
- package/dist/index.js +60 -0
- package/dist/index.js.map +1 -0
- package/dist/inject-config.d.ts +67 -0
- package/dist/inject-config.js +181 -0
- package/dist/inject-config.js.map +1 -0
- package/dist/inject.d.ts +250 -0
- package/dist/inject.js +535 -0
- package/dist/inject.js.map +1 -0
- package/dist/interception-proxy.d.ts +76 -0
- package/dist/interception-proxy.js +67 -0
- package/dist/interception-proxy.js.map +1 -0
- package/dist/interceptor-chain.d.ts +121 -0
- package/dist/interceptor-chain.js +148 -0
- package/dist/interceptor-chain.js.map +1 -0
- package/dist/interceptor.d.ts +138 -0
- package/dist/interceptor.js +299 -0
- package/dist/interceptor.js.map +1 -0
- package/dist/invocation.d.ts +101 -0
- package/dist/invocation.js +163 -0
- package/dist/invocation.js.map +1 -0
- package/dist/json-types.d.ts +28 -0
- package/{lib/src/provider.js → dist/json-types.js} +3 -3
- package/dist/json-types.js.map +1 -0
- package/dist/keys.d.ts +65 -0
- package/dist/keys.js +74 -0
- package/dist/keys.js.map +1 -0
- package/dist/provider.d.ts +31 -0
- package/{lib6/src → dist}/provider.js +2 -2
- package/dist/provider.js.map +1 -0
- package/dist/resolution-session.d.ts +180 -0
- package/dist/resolution-session.js +274 -0
- package/dist/resolution-session.js.map +1 -0
- package/dist/resolver.d.ts +46 -0
- package/dist/resolver.js +203 -0
- package/dist/resolver.js.map +1 -0
- package/dist/unique-id.d.ts +14 -0
- package/dist/unique-id.js +26 -0
- package/dist/unique-id.js.map +1 -0
- package/dist/value-promise.d.ts +134 -0
- package/dist/value-promise.js +277 -0
- package/dist/value-promise.js.map +1 -0
- package/package.json +49 -34
- package/src/binding-config.ts +73 -0
- package/src/binding-decorator.ts +136 -0
- package/src/binding-filter.ts +250 -0
- package/src/binding-inspector.ts +371 -0
- package/src/binding-key.ts +136 -0
- package/src/binding-sorter.ts +124 -0
- package/src/binding.ts +1107 -0
- package/src/context-event.ts +30 -0
- package/src/context-observer.ts +50 -0
- package/src/context-subscription.ts +402 -0
- package/src/context-tag-indexer.ts +147 -0
- package/src/context-view.ts +440 -0
- package/src/context.ts +1079 -0
- package/src/index.ts +58 -0
- package/src/inject-config.ts +239 -0
- package/src/inject.ts +796 -0
- package/src/interception-proxy.ts +127 -0
- package/src/interceptor-chain.ts +268 -0
- package/src/interceptor.ts +430 -0
- package/src/invocation.ts +269 -0
- package/src/json-types.ts +35 -0
- package/src/keys.ts +85 -0
- package/src/provider.ts +37 -0
- package/src/resolution-session.ts +414 -0
- package/src/resolver.ts +282 -0
- package/src/unique-id.ts +24 -0
- package/src/value-promise.ts +318 -0
- package/index.d.ts +0 -6
- package/index.js +0 -9
- package/lib/binding.d.ts +0 -75
- package/lib/binding.js +0 -103
- package/lib/binding.js.map +0 -1
- package/lib/context.d.ts +0 -14
- package/lib/context.js +0 -97
- package/lib/context.js.map +0 -1
- package/lib/index.d.ts +0 -1
- package/lib/index.js +0 -12
- package/lib/index.js.map +0 -1
- package/lib/inject.d.ts +0 -46
- package/lib/inject.js +0 -74
- package/lib/inject.js.map +0 -1
- package/lib/isPromise.d.ts +0 -1
- package/lib/isPromise.js +0 -15
- package/lib/isPromise.js.map +0 -1
- package/lib/reflect.d.ts +0 -39
- package/lib/reflect.js +0 -20
- package/lib/reflect.js.map +0 -1
- package/lib/resolver.d.ts +0 -30
- package/lib/resolver.js +0 -129
- package/lib/resolver.js.map +0 -1
- package/lib/src/binding.d.ts +0 -85
- package/lib/src/binding.js +0 -123
- package/lib/src/binding.js.map +0 -1
- package/lib/src/context.d.ts +0 -14
- package/lib/src/context.js +0 -97
- package/lib/src/context.js.map +0 -1
- package/lib/src/index.d.ts +0 -10
- package/lib/src/index.js +0 -27
- package/lib/src/index.js.map +0 -1
- package/lib/src/inject.d.ts +0 -46
- package/lib/src/inject.js +0 -74
- package/lib/src/inject.js.map +0 -1
- package/lib/src/isPromise.d.ts +0 -1
- package/lib/src/isPromise.js +0 -15
- package/lib/src/isPromise.js.map +0 -1
- package/lib/src/provider.d.ts +0 -29
- package/lib/src/provider.js.map +0 -1
- package/lib/src/reflect.d.ts +0 -38
- package/lib/src/reflect.js +0 -143
- package/lib/src/reflect.js.map +0 -1
- package/lib/src/resolver.d.ts +0 -34
- package/lib/src/resolver.js +0 -144
- package/lib/src/resolver.js.map +0 -1
- package/lib6/binding.d.ts +0 -75
- package/lib6/binding.js +0 -103
- package/lib6/binding.js.map +0 -1
- package/lib6/context.d.ts +0 -14
- package/lib6/context.js +0 -97
- package/lib6/context.js.map +0 -1
- package/lib6/index.d.ts +0 -1
- package/lib6/index.js +0 -12
- package/lib6/index.js.map +0 -1
- package/lib6/inject.d.ts +0 -46
- package/lib6/inject.js +0 -74
- package/lib6/inject.js.map +0 -1
- package/lib6/isPromise.d.ts +0 -1
- package/lib6/isPromise.js +0 -15
- package/lib6/isPromise.js.map +0 -1
- package/lib6/reflect.d.ts +0 -39
- package/lib6/reflect.js +0 -20
- package/lib6/reflect.js.map +0 -1
- package/lib6/resolver.d.ts +0 -30
- package/lib6/resolver.js +0 -129
- package/lib6/resolver.js.map +0 -1
- package/lib6/src/binding.d.ts +0 -85
- package/lib6/src/binding.js +0 -133
- package/lib6/src/binding.js.map +0 -1
- package/lib6/src/context.d.ts +0 -14
- package/lib6/src/context.js +0 -97
- package/lib6/src/context.js.map +0 -1
- package/lib6/src/index.d.ts +0 -10
- package/lib6/src/index.js +0 -27
- package/lib6/src/index.js.map +0 -1
- package/lib6/src/inject.d.ts +0 -46
- package/lib6/src/inject.js +0 -74
- package/lib6/src/inject.js.map +0 -1
- package/lib6/src/isPromise.d.ts +0 -1
- package/lib6/src/isPromise.js +0 -15
- package/lib6/src/isPromise.js.map +0 -1
- package/lib6/src/provider.d.ts +0 -29
- package/lib6/src/provider.js.map +0 -1
- package/lib6/src/reflect.d.ts +0 -38
- package/lib6/src/reflect.js +0 -143
- package/lib6/src/reflect.js.map +0 -1
- package/lib6/src/resolver.d.ts +0 -34
- package/lib6/src/resolver.js +0 -154
- package/lib6/src/resolver.js.map +0 -1
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// Copyright IBM Corp. 2020. All Rights Reserved.
|
|
2
|
+
// Node module: @loopback/context
|
|
3
|
+
// This file is licensed under the MIT License.
|
|
4
|
+
// License text available at https://opensource.org/licenses/MIT
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Type definition for JSON types
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* JSON primitive types:
|
|
12
|
+
* - string
|
|
13
|
+
* - number
|
|
14
|
+
* - boolean
|
|
15
|
+
* - null
|
|
16
|
+
*/
|
|
17
|
+
export type JSONPrimitive = string | number | boolean | null;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* JSON values
|
|
21
|
+
* - primitive
|
|
22
|
+
* - object
|
|
23
|
+
* - array
|
|
24
|
+
*/
|
|
25
|
+
export type JSONValue = JSONPrimitive | JSONObject | JSONArray;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* JSON object
|
|
29
|
+
*/
|
|
30
|
+
export interface JSONObject extends Record<string, JSONValue> {}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* JSON array
|
|
34
|
+
*/
|
|
35
|
+
export interface JSONArray extends Array<JSONValue> {}
|
package/src/keys.ts
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
// Copyright IBM Corp. 2018,2020. All Rights Reserved.
|
|
2
|
+
// Node module: @loopback/context
|
|
3
|
+
// This file is licensed under the MIT License.
|
|
4
|
+
// License text available at https://opensource.org/licenses/MIT
|
|
5
|
+
|
|
6
|
+
import {ConfigurationResolver} from './binding-config';
|
|
7
|
+
import {BindingKey} from './binding-key';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Namespace for context tags
|
|
11
|
+
*/
|
|
12
|
+
export namespace ContextTags {
|
|
13
|
+
export const CLASS = 'class';
|
|
14
|
+
export const PROVIDER = 'provider';
|
|
15
|
+
export const DYNAMIC_VALUE_PROVIDER = 'dynamicValueProvider';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Type of the artifact
|
|
19
|
+
*/
|
|
20
|
+
export const TYPE = 'type';
|
|
21
|
+
/**
|
|
22
|
+
* Namespace of the artifact
|
|
23
|
+
*/
|
|
24
|
+
export const NAMESPACE = 'namespace';
|
|
25
|
+
/**
|
|
26
|
+
* Name of the artifact
|
|
27
|
+
*/
|
|
28
|
+
export const NAME = 'name';
|
|
29
|
+
/**
|
|
30
|
+
* Binding key for the artifact
|
|
31
|
+
*/
|
|
32
|
+
export const KEY = 'key';
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Binding tag to associate a configuration binding with the target binding key
|
|
36
|
+
*/
|
|
37
|
+
export const CONFIGURATION_FOR = 'configurationFor';
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Binding tag for global interceptors
|
|
41
|
+
*/
|
|
42
|
+
export const GLOBAL_INTERCEPTOR = 'globalInterceptor';
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Binding tag for global interceptors to specify sources of invocations that
|
|
46
|
+
* the interceptor should apply. The tag value can be a string or string[], such
|
|
47
|
+
* as `'route'` or `['route', 'proxy']`.
|
|
48
|
+
*/
|
|
49
|
+
export const GLOBAL_INTERCEPTOR_SOURCE = 'globalInterceptorSource';
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Binding tag for group name of global interceptors
|
|
53
|
+
*/
|
|
54
|
+
export const GLOBAL_INTERCEPTOR_GROUP = 'globalInterceptorGroup';
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Default namespace for global interceptors
|
|
59
|
+
*/
|
|
60
|
+
export const GLOBAL_INTERCEPTOR_NAMESPACE = 'globalInterceptors';
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Default namespace for local interceptors
|
|
64
|
+
*/
|
|
65
|
+
export const LOCAL_INTERCEPTOR_NAMESPACE = 'interceptors';
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Namespace for context bindings
|
|
69
|
+
*/
|
|
70
|
+
export namespace ContextBindings {
|
|
71
|
+
/**
|
|
72
|
+
* Binding key for ConfigurationResolver
|
|
73
|
+
*/
|
|
74
|
+
export const CONFIGURATION_RESOLVER =
|
|
75
|
+
BindingKey.create<ConfigurationResolver>(
|
|
76
|
+
`${BindingKey.CONFIG_NAMESPACE}.resolver`,
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Binding key for ordered groups of global interceptors
|
|
81
|
+
*/
|
|
82
|
+
export const GLOBAL_INTERCEPTOR_ORDERED_GROUPS = BindingKey.create<string[]>(
|
|
83
|
+
'globalInterceptor.orderedGroups',
|
|
84
|
+
);
|
|
85
|
+
}
|
package/src/provider.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// Copyright IBM Corp. 2017,2018. All Rights Reserved.
|
|
2
|
+
// Node module: @loopback/context
|
|
3
|
+
// This file is licensed under the MIT License.
|
|
4
|
+
// License text available at https://opensource.org/licenses/MIT
|
|
5
|
+
|
|
6
|
+
import {ValueOrPromise} from './value-promise';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Providers allow developers to compute injected values dynamically,
|
|
10
|
+
* with any dependencies required by the value getter injected automatically
|
|
11
|
+
* from the Context.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
*
|
|
15
|
+
* ```ts
|
|
16
|
+
* export class DateProvider implements Provider<Date> {
|
|
17
|
+
* constructor(@inject('stringDate') private param: String){}
|
|
18
|
+
* value(): Date {
|
|
19
|
+
* return new Date(param);
|
|
20
|
+
* }
|
|
21
|
+
* }
|
|
22
|
+
*
|
|
23
|
+
* ctx.bind('stringDate').to('2017-01-01')
|
|
24
|
+
* ctx.bind('provider_key').toProvider(DateProvider);
|
|
25
|
+
*
|
|
26
|
+
* const value = ctx.getAsync('provider_key');
|
|
27
|
+
* // value is a Date instance
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
export interface Provider<T> {
|
|
31
|
+
/**
|
|
32
|
+
* @returns The value to inject to dependents.
|
|
33
|
+
* This method can return a promise too, in which case the IoC framework
|
|
34
|
+
* will resolve this promise to obtain the value to inject.
|
|
35
|
+
*/
|
|
36
|
+
value(): ValueOrPromise<T>;
|
|
37
|
+
}
|
|
@@ -0,0 +1,414 @@
|
|
|
1
|
+
// Copyright IBM Corp. 2018,2020. All Rights Reserved.
|
|
2
|
+
// Node module: @loopback/context
|
|
3
|
+
// This file is licensed under the MIT License.
|
|
4
|
+
// License text available at https://opensource.org/licenses/MIT
|
|
5
|
+
|
|
6
|
+
import {DecoratorFactory} from '@loopback/metadata';
|
|
7
|
+
import debugModule from 'debug';
|
|
8
|
+
import {Binding} from './binding';
|
|
9
|
+
import {BindingSelector} from './binding-filter';
|
|
10
|
+
import {Context} from './context';
|
|
11
|
+
import {Injection, InjectionMetadata} from './inject';
|
|
12
|
+
import {BoundValue, tryWithFinally, ValueOrPromise} from './value-promise';
|
|
13
|
+
|
|
14
|
+
const debugSession = debugModule('loopback:context:resolver:session');
|
|
15
|
+
const getTargetName = DecoratorFactory.getTargetName;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* A function to be executed with the resolution session
|
|
19
|
+
*/
|
|
20
|
+
export type ResolutionAction = (
|
|
21
|
+
session: ResolutionSession,
|
|
22
|
+
) => ValueOrPromise<BoundValue>;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Wrapper for bindings tracked by resolution sessions
|
|
26
|
+
*/
|
|
27
|
+
export interface BindingElement {
|
|
28
|
+
type: 'binding';
|
|
29
|
+
value: Readonly<Binding>;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Wrapper for injections tracked by resolution sessions
|
|
34
|
+
*/
|
|
35
|
+
export interface InjectionElement {
|
|
36
|
+
type: 'injection';
|
|
37
|
+
value: Readonly<Injection>;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface InjectionDescriptor {
|
|
41
|
+
targetName: string;
|
|
42
|
+
bindingSelector: BindingSelector;
|
|
43
|
+
metadata: InjectionMetadata;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Binding or injection elements tracked by resolution sessions
|
|
48
|
+
*/
|
|
49
|
+
export type ResolutionElement = BindingElement | InjectionElement;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Type guard for binding elements
|
|
53
|
+
* @param element - A resolution element
|
|
54
|
+
*/
|
|
55
|
+
function isBinding(
|
|
56
|
+
element: ResolutionElement | undefined,
|
|
57
|
+
): element is BindingElement {
|
|
58
|
+
return element != null && element.type === 'binding';
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Type guard for injection elements
|
|
63
|
+
* @param element - A resolution element
|
|
64
|
+
*/
|
|
65
|
+
function isInjection(
|
|
66
|
+
element: ResolutionElement | undefined,
|
|
67
|
+
): element is InjectionElement {
|
|
68
|
+
return element != null && element.type === 'injection';
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Object to keep states for a session to resolve bindings and their
|
|
73
|
+
* dependencies within a context
|
|
74
|
+
*/
|
|
75
|
+
export class ResolutionSession {
|
|
76
|
+
/**
|
|
77
|
+
* A stack of bindings for the current resolution session. It's used to track
|
|
78
|
+
* the path of dependency resolution and detect circular dependencies.
|
|
79
|
+
*/
|
|
80
|
+
readonly stack: ResolutionElement[] = [];
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Fork the current session so that a new one with the same stack can be used
|
|
84
|
+
* in parallel or future resolutions, such as multiple method arguments,
|
|
85
|
+
* multiple properties, or a getter function
|
|
86
|
+
* @param session - The current session
|
|
87
|
+
*/
|
|
88
|
+
static fork(session?: ResolutionSession): ResolutionSession | undefined {
|
|
89
|
+
if (session === undefined) return undefined;
|
|
90
|
+
const copy = new ResolutionSession();
|
|
91
|
+
copy.stack.push(...session.stack);
|
|
92
|
+
return copy;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Run the given action with the given binding and session
|
|
97
|
+
* @param action - A function to do some work with the resolution session
|
|
98
|
+
* @param binding - The current binding
|
|
99
|
+
* @param session - The current resolution session
|
|
100
|
+
*/
|
|
101
|
+
static runWithBinding(
|
|
102
|
+
action: ResolutionAction,
|
|
103
|
+
binding: Readonly<Binding>,
|
|
104
|
+
session = new ResolutionSession(),
|
|
105
|
+
) {
|
|
106
|
+
// Start to resolve a binding within the session
|
|
107
|
+
session.pushBinding(binding);
|
|
108
|
+
return tryWithFinally(
|
|
109
|
+
() => action(session),
|
|
110
|
+
() => session.popBinding(),
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Run the given action with the given injection and session
|
|
116
|
+
* @param action - A function to do some work with the resolution session
|
|
117
|
+
* @param binding - The current injection
|
|
118
|
+
* @param session - The current resolution session
|
|
119
|
+
*/
|
|
120
|
+
static runWithInjection(
|
|
121
|
+
action: ResolutionAction,
|
|
122
|
+
injection: Readonly<Injection>,
|
|
123
|
+
session = new ResolutionSession(),
|
|
124
|
+
) {
|
|
125
|
+
session.pushInjection(injection);
|
|
126
|
+
return tryWithFinally(
|
|
127
|
+
() => action(session),
|
|
128
|
+
() => session.popInjection(),
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Describe the injection for debugging purpose
|
|
134
|
+
* @param injection - Injection object
|
|
135
|
+
*/
|
|
136
|
+
static describeInjection(
|
|
137
|
+
injection: Readonly<Injection>,
|
|
138
|
+
): InjectionDescriptor {
|
|
139
|
+
const name = getTargetName(
|
|
140
|
+
injection.target,
|
|
141
|
+
injection.member,
|
|
142
|
+
injection.methodDescriptorOrParameterIndex,
|
|
143
|
+
);
|
|
144
|
+
return {
|
|
145
|
+
targetName: name,
|
|
146
|
+
bindingSelector: injection.bindingSelector,
|
|
147
|
+
metadata: injection.metadata,
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Push the injection onto the session
|
|
153
|
+
* @param injection - Injection The current injection
|
|
154
|
+
*/
|
|
155
|
+
pushInjection(injection: Readonly<Injection>) {
|
|
156
|
+
/* istanbul ignore if */
|
|
157
|
+
if (debugSession.enabled) {
|
|
158
|
+
debugSession(
|
|
159
|
+
'Enter injection:',
|
|
160
|
+
ResolutionSession.describeInjection(injection),
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
this.stack.push({type: 'injection', value: injection});
|
|
164
|
+
/* istanbul ignore if */
|
|
165
|
+
if (debugSession.enabled) {
|
|
166
|
+
debugSession('Resolution path:', this.getResolutionPath());
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Pop the last injection
|
|
172
|
+
*/
|
|
173
|
+
popInjection() {
|
|
174
|
+
const top = this.stack.pop();
|
|
175
|
+
if (!isInjection(top)) {
|
|
176
|
+
throw new Error('The top element must be an injection');
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const injection = top.value;
|
|
180
|
+
/* istanbul ignore if */
|
|
181
|
+
if (debugSession.enabled) {
|
|
182
|
+
debugSession(
|
|
183
|
+
'Exit injection:',
|
|
184
|
+
ResolutionSession.describeInjection(injection),
|
|
185
|
+
);
|
|
186
|
+
debugSession('Resolution path:', this.getResolutionPath() || '<empty>');
|
|
187
|
+
}
|
|
188
|
+
return injection;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Getter for the current injection
|
|
193
|
+
*/
|
|
194
|
+
get currentInjection(): Readonly<Injection> | undefined {
|
|
195
|
+
for (let i = this.stack.length - 1; i >= 0; i--) {
|
|
196
|
+
const element = this.stack[i];
|
|
197
|
+
if (isInjection(element)) return element.value;
|
|
198
|
+
}
|
|
199
|
+
return undefined;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Getter for the current binding
|
|
204
|
+
*/
|
|
205
|
+
get currentBinding(): Readonly<Binding> | undefined {
|
|
206
|
+
for (let i = this.stack.length - 1; i >= 0; i--) {
|
|
207
|
+
const element = this.stack[i];
|
|
208
|
+
if (isBinding(element)) return element.value;
|
|
209
|
+
}
|
|
210
|
+
return undefined;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Enter the resolution of the given binding. If
|
|
215
|
+
* @param binding - Binding
|
|
216
|
+
*/
|
|
217
|
+
pushBinding(binding: Readonly<Binding>) {
|
|
218
|
+
/* istanbul ignore if */
|
|
219
|
+
if (debugSession.enabled) {
|
|
220
|
+
debugSession('Enter binding:', binding.toJSON());
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
if (this.stack.find(i => isBinding(i) && i.value === binding)) {
|
|
224
|
+
const msg =
|
|
225
|
+
`Circular dependency detected: ` +
|
|
226
|
+
`${this.getResolutionPath()} --> ${binding.key}`;
|
|
227
|
+
debugSession(msg);
|
|
228
|
+
throw new Error(msg);
|
|
229
|
+
}
|
|
230
|
+
this.stack.push({type: 'binding', value: binding});
|
|
231
|
+
/* istanbul ignore if */
|
|
232
|
+
if (debugSession.enabled) {
|
|
233
|
+
debugSession('Resolution path:', this.getResolutionPath());
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Exit the resolution of a binding
|
|
239
|
+
*/
|
|
240
|
+
popBinding(): Readonly<Binding> {
|
|
241
|
+
const top = this.stack.pop();
|
|
242
|
+
if (!isBinding(top)) {
|
|
243
|
+
throw new Error('The top element must be a binding');
|
|
244
|
+
}
|
|
245
|
+
const binding = top.value;
|
|
246
|
+
/* istanbul ignore if */
|
|
247
|
+
if (debugSession.enabled) {
|
|
248
|
+
debugSession('Exit binding:', binding?.toJSON());
|
|
249
|
+
debugSession('Resolution path:', this.getResolutionPath() || '<empty>');
|
|
250
|
+
}
|
|
251
|
+
return binding;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Getter for bindings on the stack
|
|
256
|
+
*/
|
|
257
|
+
get bindingStack(): Readonly<Binding>[] {
|
|
258
|
+
return this.stack.filter(isBinding).map(e => e.value);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Getter for injections on the stack
|
|
263
|
+
*/
|
|
264
|
+
get injectionStack(): Readonly<Injection>[] {
|
|
265
|
+
return this.stack.filter(isInjection).map(e => e.value);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Get the binding path as `bindingA --> bindingB --> bindingC`.
|
|
270
|
+
*/
|
|
271
|
+
getBindingPath() {
|
|
272
|
+
return this.stack.filter(isBinding).map(describe).join(' --> ');
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Get the injection path as `injectionA --> injectionB --> injectionC`.
|
|
277
|
+
*/
|
|
278
|
+
getInjectionPath() {
|
|
279
|
+
return this.injectionStack
|
|
280
|
+
.map(i => ResolutionSession.describeInjection(i).targetName)
|
|
281
|
+
.join(' --> ');
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Get the resolution path including bindings and injections, for example:
|
|
286
|
+
* `bindingA --> @ClassA[0] --> bindingB --> @ClassB.prototype.prop1
|
|
287
|
+
* --> bindingC`.
|
|
288
|
+
*/
|
|
289
|
+
getResolutionPath() {
|
|
290
|
+
return this.stack.map(describe).join(' --> ');
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
toString() {
|
|
294
|
+
return this.getResolutionPath();
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
function describe(e: ResolutionElement) {
|
|
299
|
+
switch (e.type) {
|
|
300
|
+
case 'injection':
|
|
301
|
+
return '@' + ResolutionSession.describeInjection(e.value).targetName;
|
|
302
|
+
case 'binding':
|
|
303
|
+
return e.value.key;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Options for binding/dependency resolution
|
|
309
|
+
*/
|
|
310
|
+
export interface ResolutionOptions {
|
|
311
|
+
/**
|
|
312
|
+
* A session to track bindings and injections
|
|
313
|
+
*/
|
|
314
|
+
session?: ResolutionSession;
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* A boolean flag to indicate if the dependency is optional. If it's set to
|
|
318
|
+
* `true` and the binding is not bound in a context, the resolution
|
|
319
|
+
* will return `undefined` instead of throwing an error.
|
|
320
|
+
*/
|
|
321
|
+
optional?: boolean;
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* A boolean flag to control if a proxy should be created to apply
|
|
325
|
+
* interceptors for the resolved value. It's only honored for bindings backed
|
|
326
|
+
* by a class.
|
|
327
|
+
*/
|
|
328
|
+
asProxyWithInterceptors?: boolean;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* Resolution options or session
|
|
333
|
+
*/
|
|
334
|
+
export type ResolutionOptionsOrSession = ResolutionOptions | ResolutionSession;
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* Normalize ResolutionOptionsOrSession to ResolutionOptions
|
|
338
|
+
* @param optionsOrSession - resolution options or session
|
|
339
|
+
*/
|
|
340
|
+
export function asResolutionOptions(
|
|
341
|
+
optionsOrSession?: ResolutionOptionsOrSession,
|
|
342
|
+
): ResolutionOptions {
|
|
343
|
+
// backwards compatibility
|
|
344
|
+
if (optionsOrSession instanceof ResolutionSession) {
|
|
345
|
+
return {session: optionsOrSession};
|
|
346
|
+
}
|
|
347
|
+
return optionsOrSession ?? {};
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* Contextual metadata for resolution
|
|
352
|
+
*/
|
|
353
|
+
export interface ResolutionContext<T = unknown> {
|
|
354
|
+
/**
|
|
355
|
+
* The context for resolution
|
|
356
|
+
*/
|
|
357
|
+
readonly context: Context;
|
|
358
|
+
/**
|
|
359
|
+
* The binding to be resolved
|
|
360
|
+
*/
|
|
361
|
+
readonly binding: Readonly<Binding<T>>;
|
|
362
|
+
/**
|
|
363
|
+
* The options used for resolution
|
|
364
|
+
*/
|
|
365
|
+
readonly options: ResolutionOptions;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* Error for context binding resolutions and dependency injections
|
|
370
|
+
*/
|
|
371
|
+
export class ResolutionError extends Error {
|
|
372
|
+
constructor(
|
|
373
|
+
message: string,
|
|
374
|
+
readonly resolutionCtx: Partial<ResolutionContext>,
|
|
375
|
+
) {
|
|
376
|
+
super(ResolutionError.buildMessage(message, resolutionCtx));
|
|
377
|
+
this.name = ResolutionError.name;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
private static buildDetails(resolutionCtx: Partial<ResolutionContext>) {
|
|
381
|
+
return {
|
|
382
|
+
context: resolutionCtx.context?.name ?? '',
|
|
383
|
+
binding: resolutionCtx.binding?.key ?? '',
|
|
384
|
+
resolutionPath: resolutionCtx.options?.session?.getResolutionPath() ?? '',
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* Build the error message for the resolution to include more contextual data
|
|
390
|
+
* @param reason - Cause of the error
|
|
391
|
+
* @param resolutionCtx - Resolution context
|
|
392
|
+
*/
|
|
393
|
+
private static buildMessage(
|
|
394
|
+
reason: string,
|
|
395
|
+
resolutionCtx: Partial<ResolutionContext>,
|
|
396
|
+
) {
|
|
397
|
+
const info = this.describeResolutionContext(resolutionCtx);
|
|
398
|
+
const message = `${reason} (${info})`;
|
|
399
|
+
return message;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
private static describeResolutionContext(
|
|
403
|
+
resolutionCtx: Partial<ResolutionContext>,
|
|
404
|
+
) {
|
|
405
|
+
const details = ResolutionError.buildDetails(resolutionCtx);
|
|
406
|
+
const items: string[] = [];
|
|
407
|
+
for (const [name, val] of Object.entries(details)) {
|
|
408
|
+
if (val !== '') {
|
|
409
|
+
items.push(`${name}: ${val}`);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
return items.join(', ');
|
|
413
|
+
}
|
|
414
|
+
}
|