@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.
Files changed (201) hide show
  1. package/LICENSE +25 -0
  2. package/README.md +116 -0
  3. package/dist/binding-config.d.ts +40 -0
  4. package/dist/binding-config.js +33 -0
  5. package/dist/binding-config.js.map +1 -0
  6. package/dist/binding-decorator.d.ts +45 -0
  7. package/dist/binding-decorator.js +118 -0
  8. package/dist/binding-decorator.js.map +1 -0
  9. package/dist/binding-filter.d.ts +108 -0
  10. package/dist/binding-filter.js +162 -0
  11. package/dist/binding-filter.js.map +1 -0
  12. package/dist/binding-inspector.d.ts +150 -0
  13. package/dist/binding-inspector.js +249 -0
  14. package/dist/binding-inspector.js.map +1 -0
  15. package/dist/binding-key.d.ts +66 -0
  16. package/dist/binding-key.js +121 -0
  17. package/dist/binding-key.js.map +1 -0
  18. package/dist/binding-sorter.d.ts +71 -0
  19. package/dist/binding-sorter.js +89 -0
  20. package/dist/binding-sorter.js.map +1 -0
  21. package/dist/binding.d.ts +577 -0
  22. package/dist/binding.js +788 -0
  23. package/dist/binding.js.map +1 -0
  24. package/dist/context-event.d.ts +23 -0
  25. package/dist/context-event.js +7 -0
  26. package/dist/context-event.js.map +1 -0
  27. package/dist/context-observer.d.ts +36 -0
  28. package/dist/context-observer.js +7 -0
  29. package/dist/context-observer.js.map +1 -0
  30. package/dist/context-subscription.d.ts +147 -0
  31. package/dist/context-subscription.js +317 -0
  32. package/dist/context-subscription.js.map +1 -0
  33. package/dist/context-tag-indexer.d.ts +42 -0
  34. package/dist/context-tag-indexer.js +135 -0
  35. package/dist/context-tag-indexer.js.map +1 -0
  36. package/dist/context-view.d.ts +209 -0
  37. package/dist/context-view.js +240 -0
  38. package/dist/context-view.js.map +1 -0
  39. package/dist/context.d.ts +513 -0
  40. package/dist/context.js +717 -0
  41. package/dist/context.js.map +1 -0
  42. package/dist/index.d.ts +52 -0
  43. package/dist/index.js +60 -0
  44. package/dist/index.js.map +1 -0
  45. package/dist/inject-config.d.ts +67 -0
  46. package/dist/inject-config.js +181 -0
  47. package/dist/inject-config.js.map +1 -0
  48. package/dist/inject.d.ts +250 -0
  49. package/dist/inject.js +535 -0
  50. package/dist/inject.js.map +1 -0
  51. package/dist/interception-proxy.d.ts +76 -0
  52. package/dist/interception-proxy.js +67 -0
  53. package/dist/interception-proxy.js.map +1 -0
  54. package/dist/interceptor-chain.d.ts +121 -0
  55. package/dist/interceptor-chain.js +148 -0
  56. package/dist/interceptor-chain.js.map +1 -0
  57. package/dist/interceptor.d.ts +138 -0
  58. package/dist/interceptor.js +299 -0
  59. package/dist/interceptor.js.map +1 -0
  60. package/dist/invocation.d.ts +101 -0
  61. package/dist/invocation.js +163 -0
  62. package/dist/invocation.js.map +1 -0
  63. package/dist/json-types.d.ts +28 -0
  64. package/{lib/src/provider.js → dist/json-types.js} +3 -3
  65. package/dist/json-types.js.map +1 -0
  66. package/dist/keys.d.ts +65 -0
  67. package/dist/keys.js +74 -0
  68. package/dist/keys.js.map +1 -0
  69. package/dist/provider.d.ts +31 -0
  70. package/{lib6/src → dist}/provider.js +2 -2
  71. package/dist/provider.js.map +1 -0
  72. package/dist/resolution-session.d.ts +180 -0
  73. package/dist/resolution-session.js +274 -0
  74. package/dist/resolution-session.js.map +1 -0
  75. package/dist/resolver.d.ts +46 -0
  76. package/dist/resolver.js +203 -0
  77. package/dist/resolver.js.map +1 -0
  78. package/dist/unique-id.d.ts +14 -0
  79. package/dist/unique-id.js +26 -0
  80. package/dist/unique-id.js.map +1 -0
  81. package/dist/value-promise.d.ts +134 -0
  82. package/dist/value-promise.js +277 -0
  83. package/dist/value-promise.js.map +1 -0
  84. package/package.json +49 -34
  85. package/src/binding-config.ts +73 -0
  86. package/src/binding-decorator.ts +136 -0
  87. package/src/binding-filter.ts +250 -0
  88. package/src/binding-inspector.ts +371 -0
  89. package/src/binding-key.ts +136 -0
  90. package/src/binding-sorter.ts +124 -0
  91. package/src/binding.ts +1107 -0
  92. package/src/context-event.ts +30 -0
  93. package/src/context-observer.ts +50 -0
  94. package/src/context-subscription.ts +402 -0
  95. package/src/context-tag-indexer.ts +147 -0
  96. package/src/context-view.ts +440 -0
  97. package/src/context.ts +1079 -0
  98. package/src/index.ts +58 -0
  99. package/src/inject-config.ts +239 -0
  100. package/src/inject.ts +796 -0
  101. package/src/interception-proxy.ts +127 -0
  102. package/src/interceptor-chain.ts +268 -0
  103. package/src/interceptor.ts +430 -0
  104. package/src/invocation.ts +269 -0
  105. package/src/json-types.ts +35 -0
  106. package/src/keys.ts +85 -0
  107. package/src/provider.ts +37 -0
  108. package/src/resolution-session.ts +414 -0
  109. package/src/resolver.ts +282 -0
  110. package/src/unique-id.ts +24 -0
  111. package/src/value-promise.ts +318 -0
  112. package/index.d.ts +0 -6
  113. package/index.js +0 -9
  114. package/lib/binding.d.ts +0 -75
  115. package/lib/binding.js +0 -103
  116. package/lib/binding.js.map +0 -1
  117. package/lib/context.d.ts +0 -14
  118. package/lib/context.js +0 -97
  119. package/lib/context.js.map +0 -1
  120. package/lib/index.d.ts +0 -1
  121. package/lib/index.js +0 -12
  122. package/lib/index.js.map +0 -1
  123. package/lib/inject.d.ts +0 -46
  124. package/lib/inject.js +0 -74
  125. package/lib/inject.js.map +0 -1
  126. package/lib/isPromise.d.ts +0 -1
  127. package/lib/isPromise.js +0 -15
  128. package/lib/isPromise.js.map +0 -1
  129. package/lib/reflect.d.ts +0 -39
  130. package/lib/reflect.js +0 -20
  131. package/lib/reflect.js.map +0 -1
  132. package/lib/resolver.d.ts +0 -30
  133. package/lib/resolver.js +0 -129
  134. package/lib/resolver.js.map +0 -1
  135. package/lib/src/binding.d.ts +0 -85
  136. package/lib/src/binding.js +0 -123
  137. package/lib/src/binding.js.map +0 -1
  138. package/lib/src/context.d.ts +0 -14
  139. package/lib/src/context.js +0 -97
  140. package/lib/src/context.js.map +0 -1
  141. package/lib/src/index.d.ts +0 -10
  142. package/lib/src/index.js +0 -27
  143. package/lib/src/index.js.map +0 -1
  144. package/lib/src/inject.d.ts +0 -46
  145. package/lib/src/inject.js +0 -74
  146. package/lib/src/inject.js.map +0 -1
  147. package/lib/src/isPromise.d.ts +0 -1
  148. package/lib/src/isPromise.js +0 -15
  149. package/lib/src/isPromise.js.map +0 -1
  150. package/lib/src/provider.d.ts +0 -29
  151. package/lib/src/provider.js.map +0 -1
  152. package/lib/src/reflect.d.ts +0 -38
  153. package/lib/src/reflect.js +0 -143
  154. package/lib/src/reflect.js.map +0 -1
  155. package/lib/src/resolver.d.ts +0 -34
  156. package/lib/src/resolver.js +0 -144
  157. package/lib/src/resolver.js.map +0 -1
  158. package/lib6/binding.d.ts +0 -75
  159. package/lib6/binding.js +0 -103
  160. package/lib6/binding.js.map +0 -1
  161. package/lib6/context.d.ts +0 -14
  162. package/lib6/context.js +0 -97
  163. package/lib6/context.js.map +0 -1
  164. package/lib6/index.d.ts +0 -1
  165. package/lib6/index.js +0 -12
  166. package/lib6/index.js.map +0 -1
  167. package/lib6/inject.d.ts +0 -46
  168. package/lib6/inject.js +0 -74
  169. package/lib6/inject.js.map +0 -1
  170. package/lib6/isPromise.d.ts +0 -1
  171. package/lib6/isPromise.js +0 -15
  172. package/lib6/isPromise.js.map +0 -1
  173. package/lib6/reflect.d.ts +0 -39
  174. package/lib6/reflect.js +0 -20
  175. package/lib6/reflect.js.map +0 -1
  176. package/lib6/resolver.d.ts +0 -30
  177. package/lib6/resolver.js +0 -129
  178. package/lib6/resolver.js.map +0 -1
  179. package/lib6/src/binding.d.ts +0 -85
  180. package/lib6/src/binding.js +0 -133
  181. package/lib6/src/binding.js.map +0 -1
  182. package/lib6/src/context.d.ts +0 -14
  183. package/lib6/src/context.js +0 -97
  184. package/lib6/src/context.js.map +0 -1
  185. package/lib6/src/index.d.ts +0 -10
  186. package/lib6/src/index.js +0 -27
  187. package/lib6/src/index.js.map +0 -1
  188. package/lib6/src/inject.d.ts +0 -46
  189. package/lib6/src/inject.js +0 -74
  190. package/lib6/src/inject.js.map +0 -1
  191. package/lib6/src/isPromise.d.ts +0 -1
  192. package/lib6/src/isPromise.js +0 -15
  193. package/lib6/src/isPromise.js.map +0 -1
  194. package/lib6/src/provider.d.ts +0 -29
  195. package/lib6/src/provider.js.map +0 -1
  196. package/lib6/src/reflect.d.ts +0 -38
  197. package/lib6/src/reflect.js +0 -143
  198. package/lib6/src/reflect.js.map +0 -1
  199. package/lib6/src/resolver.d.ts +0 -34
  200. package/lib6/src/resolver.js +0 -154
  201. 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
+ }
@@ -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
+ }