@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,788 @@
1
+ "use strict";
2
+ // Copyright IBM Corp. 2017,2020. All Rights Reserved.
3
+ // Node module: @loopback/context
4
+ // This file is licensed under the MIT License.
5
+ // License text available at https://opensource.org/licenses/MIT
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.Binding = exports.isDynamicValueProviderClass = exports.BindingType = exports.BindingScope = void 0;
8
+ const tslib_1 = require("tslib");
9
+ const debug_1 = (0, tslib_1.__importDefault)(require("debug"));
10
+ const events_1 = require("events");
11
+ const binding_inspector_1 = require("./binding-inspector");
12
+ const binding_key_1 = require("./binding-key");
13
+ const inject_1 = require("./inject");
14
+ const interception_proxy_1 = require("./interception-proxy");
15
+ const invocation_1 = require("./invocation");
16
+ const keys_1 = require("./keys");
17
+ const resolution_session_1 = require("./resolution-session");
18
+ const resolver_1 = require("./resolver");
19
+ const value_promise_1 = require("./value-promise");
20
+ const debug = (0, debug_1.default)('loopback:context:binding');
21
+ /**
22
+ * Scope for binding values
23
+ */
24
+ var BindingScope;
25
+ (function (BindingScope) {
26
+ /**
27
+ * The binding provides a value that is calculated each time. This will be
28
+ * the default scope if not set.
29
+ *
30
+ * For example, with the following context hierarchy:
31
+ *
32
+ * - `app` (with a binding `'b1'` that produces sequential values 0, 1, ...)
33
+ * - req1
34
+ * - req2
35
+ *
36
+ * Now `'b1'` is resolved to a new value each time for `app` and its
37
+ * descendants `req1` and `req2`:
38
+ * - app.get('b1') ==> 0
39
+ * - req1.get('b1') ==> 1
40
+ * - req2.get('b1') ==> 2
41
+ * - req2.get('b1') ==> 3
42
+ * - app.get('b1') ==> 4
43
+ */
44
+ BindingScope["TRANSIENT"] = "Transient";
45
+ /**
46
+ * @deprecated Finer-grained scopes such as `APPLICATION`, `SERVER`, or
47
+ * `REQUEST` should be used instead to ensure the scope of sharing of resolved
48
+ * binding values.
49
+ *
50
+ * The binding provides a value as a singleton within each local context. The
51
+ * value is calculated only once per context and cached for subsequential
52
+ * uses. Child contexts have their own value and do not share with their
53
+ * ancestors.
54
+ *
55
+ * For example, with the following context hierarchy:
56
+ *
57
+ * - `app` (with a binding `'b1'` that produces sequential values 0, 1, ...)
58
+ * - req1
59
+ * - req2
60
+ *
61
+ * 1. `0` is the resolved value for `'b1'` within the `app` afterward
62
+ * - app.get('b1') ==> 0 (always)
63
+ *
64
+ * 2. `'b1'` is resolved in `app` but not in `req1`, a new value `1` is
65
+ * calculated and used for `req1` afterward
66
+ * - req1.get('b1') ==> 1 (always)
67
+ *
68
+ * 3. `'b1'` is resolved in `app` but not in `req2`, a new value `2` is
69
+ * calculated and used for `req2` afterward
70
+ * - req2.get('b1') ==> 2 (always)
71
+ *
72
+ */
73
+ BindingScope["CONTEXT"] = "Context";
74
+ /**
75
+ * The binding provides a value as a singleton within the context hierarchy
76
+ * (the owning context and its descendants). The value is calculated only
77
+ * once for the owning context and cached for subsequential uses. Child
78
+ * contexts share the same value as their ancestors.
79
+ *
80
+ * For example, with the following context hierarchy:
81
+ *
82
+ * - `app` (with a binding `'b1'` that produces sequential values 0, 1, ...)
83
+ * - req1
84
+ * - req2
85
+ *
86
+ * 1. `0` is the singleton for `app` afterward
87
+ * - app.get('b1') ==> 0 (always)
88
+ *
89
+ * 2. `'b1'` is resolved in `app`, reuse it for `req1`
90
+ * - req1.get('b1') ==> 0 (always)
91
+ *
92
+ * 3. `'b1'` is resolved in `app`, reuse it for `req2`
93
+ * - req2.get('b1') ==> 0 (always)
94
+ */
95
+ BindingScope["SINGLETON"] = "Singleton";
96
+ /*
97
+ * The following scopes are checked against the context hierarchy to find
98
+ * the first matching context for a given scope in the chain. Resolved binding
99
+ * values will be cached and shared on the scoped context. This ensures a
100
+ * binding to have the same value for the scoped context.
101
+ */
102
+ /**
103
+ * Application scope
104
+ *
105
+ * @remarks
106
+ * The binding provides an application-scoped value within the context
107
+ * hierarchy. Resolved value for this binding will be cached and shared for
108
+ * the same application context (denoted by its scope property set to
109
+ * `BindingScope.APPLICATION`).
110
+ *
111
+ */
112
+ BindingScope["APPLICATION"] = "Application";
113
+ /**
114
+ * Server scope
115
+ *
116
+ * @remarks
117
+ * The binding provides an server-scoped value within the context hierarchy.
118
+ * Resolved value for this binding will be cached and shared for the same
119
+ * server context (denoted by its scope property set to
120
+ * `BindingScope.SERVER`).
121
+ *
122
+ * It's possible that an application has more than one servers configured,
123
+ * such as a `RestServer` and a `GrpcServer`. Both server contexts are created
124
+ * with `scope` set to `BindingScope.SERVER`. Depending on where a binding
125
+ * is resolved:
126
+ * - If the binding is resolved from the RestServer or below, it will be
127
+ * cached using the RestServer context as the key.
128
+ * - If the binding is resolved from the GrpcServer or below, it will be
129
+ * cached using the GrpcServer context as the key.
130
+ *
131
+ * The same binding can resolved/shared/cached for all servers, each of which
132
+ * has its own value for the binding.
133
+ */
134
+ BindingScope["SERVER"] = "Server";
135
+ /**
136
+ * Request scope
137
+ *
138
+ * @remarks
139
+ * The binding provides an request-scoped value within the context hierarchy.
140
+ * Resolved value for this binding will be cached and shared for the same
141
+ * request context (denoted by its scope property set to
142
+ * `BindingScope.REQUEST`).
143
+ *
144
+ * The `REQUEST` scope is very useful for controllers, services and artifacts
145
+ * that want to have a single instance/value for a given request.
146
+ */
147
+ BindingScope["REQUEST"] = "Request";
148
+ })(BindingScope = exports.BindingScope || (exports.BindingScope = {}));
149
+ /**
150
+ * Type of the binding source
151
+ */
152
+ var BindingType;
153
+ (function (BindingType) {
154
+ /**
155
+ * A fixed value
156
+ */
157
+ BindingType["CONSTANT"] = "Constant";
158
+ /**
159
+ * A function to get the value
160
+ */
161
+ BindingType["DYNAMIC_VALUE"] = "DynamicValue";
162
+ /**
163
+ * A class to be instantiated as the value
164
+ */
165
+ BindingType["CLASS"] = "Class";
166
+ /**
167
+ * A provider class with `value()` function to get the value
168
+ */
169
+ BindingType["PROVIDER"] = "Provider";
170
+ /**
171
+ * A alias to another binding key with optional path
172
+ */
173
+ BindingType["ALIAS"] = "Alias";
174
+ })(BindingType = exports.BindingType || (exports.BindingType = {}));
175
+ /**
176
+ * Adapt the ValueFactoryProvider class to be a value factory
177
+ * @param provider - ValueFactoryProvider class
178
+ */
179
+ function toValueFactory(provider) {
180
+ return resolutionCtx => (0, invocation_1.invokeMethod)(provider, 'value', resolutionCtx.context, [], {
181
+ skipInterceptors: true,
182
+ session: resolutionCtx.options.session,
183
+ });
184
+ }
185
+ /**
186
+ * Check if the factory is a value factory provider class
187
+ * @param factory - A factory function or a dynamic value provider class
188
+ */
189
+ function isDynamicValueProviderClass(factory) {
190
+ // Not a class
191
+ if (typeof factory !== 'function' || !String(factory).startsWith('class ')) {
192
+ return false;
193
+ }
194
+ const valueMethod = factory.value;
195
+ return typeof valueMethod === 'function';
196
+ }
197
+ exports.isDynamicValueProviderClass = isDynamicValueProviderClass;
198
+ /**
199
+ * Binding represents an entry in the `Context`. Each binding has a key and a
200
+ * corresponding value getter.
201
+ */
202
+ class Binding extends events_1.EventEmitter {
203
+ constructor(key, isLocked = false) {
204
+ super();
205
+ this.isLocked = isLocked;
206
+ /**
207
+ * Map for tag name/value pairs
208
+ */
209
+ this.tagMap = {};
210
+ binding_key_1.BindingKey.validate(key);
211
+ this.key = key.toString();
212
+ }
213
+ /**
214
+ * Scope of the binding to control how the value is cached/shared
215
+ */
216
+ get scope() {
217
+ var _a;
218
+ // Default to TRANSIENT if not set
219
+ return (_a = this._scope) !== null && _a !== void 0 ? _a : BindingScope.TRANSIENT;
220
+ }
221
+ /**
222
+ * Type of the binding value getter
223
+ */
224
+ get type() {
225
+ var _a;
226
+ return (_a = this._source) === null || _a === void 0 ? void 0 : _a.type;
227
+ }
228
+ get source() {
229
+ return this._source;
230
+ }
231
+ /**
232
+ * For bindings bound via `toClass()`, this property contains the constructor
233
+ * function of the class
234
+ */
235
+ get valueConstructor() {
236
+ var _a, _b;
237
+ return ((_a = this._source) === null || _a === void 0 ? void 0 : _a.type) === BindingType.CLASS
238
+ ? (_b = this._source) === null || _b === void 0 ? void 0 : _b.value
239
+ : undefined;
240
+ }
241
+ /**
242
+ * For bindings bound via `toProvider()`, this property contains the
243
+ * constructor function of the provider class
244
+ */
245
+ get providerConstructor() {
246
+ var _a, _b;
247
+ return ((_a = this._source) === null || _a === void 0 ? void 0 : _a.type) === BindingType.PROVIDER
248
+ ? (_b = this._source) === null || _b === void 0 ? void 0 : _b.value
249
+ : undefined;
250
+ }
251
+ /**
252
+ * Cache the resolved value by the binding scope
253
+ * @param resolutionCtx - The resolution context
254
+ * @param result - The calculated value for the binding
255
+ */
256
+ _cacheValue(resolutionCtx, result) {
257
+ // Initialize the cache as a weakmap keyed by context
258
+ if (!this._cache)
259
+ this._cache = new WeakMap();
260
+ if (this.scope !== BindingScope.TRANSIENT) {
261
+ this._cache.set(resolutionCtx, result);
262
+ }
263
+ return result;
264
+ }
265
+ /**
266
+ * Clear the cache
267
+ */
268
+ _clearCache() {
269
+ if (!this._cache)
270
+ return;
271
+ // WeakMap does not have a `clear` method
272
+ this._cache = new WeakMap();
273
+ }
274
+ /**
275
+ * Invalidate the binding cache so that its value will be reloaded next time.
276
+ * This is useful to force reloading a cached value when its configuration or
277
+ * dependencies are changed.
278
+ * **WARNING**: The state held in the cached value will be gone.
279
+ *
280
+ * @param ctx - Context object
281
+ */
282
+ refresh(ctx) {
283
+ if (!this._cache)
284
+ return;
285
+ if (this.scope !== BindingScope.TRANSIENT) {
286
+ const resolutionCtx = ctx.getResolutionContext(this);
287
+ if (resolutionCtx != null) {
288
+ this._cache.delete(resolutionCtx);
289
+ }
290
+ }
291
+ }
292
+ // Implementation
293
+ getValue(ctx, optionsOrSession) {
294
+ var _a;
295
+ /* istanbul ignore if */
296
+ if (debug.enabled) {
297
+ debug('Get value for binding %s', this.key);
298
+ }
299
+ const options = (0, resolution_session_1.asResolutionOptions)(optionsOrSession);
300
+ const resolutionCtx = this.getResolutionContext(ctx, options);
301
+ if (resolutionCtx == null)
302
+ return undefined;
303
+ // Keep a snapshot for proxy
304
+ const savedSession = (_a = resolution_session_1.ResolutionSession.fork(options.session)) !== null && _a !== void 0 ? _a : new resolution_session_1.ResolutionSession();
305
+ // First check cached value for non-transient
306
+ if (this._cache) {
307
+ if (this.scope !== BindingScope.TRANSIENT) {
308
+ if (resolutionCtx && this._cache.has(resolutionCtx)) {
309
+ const value = this._cache.get(resolutionCtx);
310
+ return this.getValueOrProxy(resolutionCtx, { ...options, session: savedSession }, value);
311
+ }
312
+ }
313
+ }
314
+ const resolutionMetadata = {
315
+ context: resolutionCtx,
316
+ binding: this,
317
+ options,
318
+ };
319
+ if (typeof this._getValue === 'function') {
320
+ const result = resolution_session_1.ResolutionSession.runWithBinding(s => {
321
+ const optionsWithSession = {
322
+ ...options,
323
+ session: s,
324
+ // Force to be the non-proxy version
325
+ asProxyWithInterceptors: false,
326
+ };
327
+ // We already test `this._getValue` is a function. It's safe to assert
328
+ // that `this._getValue` is not undefined.
329
+ return this._getValue({
330
+ ...resolutionMetadata,
331
+ options: optionsWithSession,
332
+ });
333
+ }, this, options.session);
334
+ const value = this._cacheValue(resolutionCtx, result);
335
+ return this.getValueOrProxy(resolutionCtx, { ...options, session: savedSession }, value);
336
+ }
337
+ // `@inject.binding` adds a binding without _getValue
338
+ if (options.optional)
339
+ return undefined;
340
+ return Promise.reject(new resolution_session_1.ResolutionError(`No value was configured for binding ${this.key}.`, resolutionMetadata));
341
+ }
342
+ getValueOrProxy(resolutionCtx, options, value) {
343
+ const session = options.session;
344
+ session.pushBinding(this);
345
+ return Binding.valueOrProxy({
346
+ context: resolutionCtx,
347
+ binding: this,
348
+ options,
349
+ }, value);
350
+ }
351
+ /**
352
+ * Locate and validate the resolution context
353
+ * @param ctx - Current context
354
+ * @param options - Resolution options
355
+ */
356
+ getResolutionContext(ctx, options) {
357
+ const resolutionCtx = ctx.getResolutionContext(this);
358
+ switch (this.scope) {
359
+ case BindingScope.APPLICATION:
360
+ case BindingScope.SERVER:
361
+ case BindingScope.REQUEST:
362
+ if (resolutionCtx == null) {
363
+ const msg = `Binding "${this.key}" in context "${ctx.name}" cannot` +
364
+ ` be resolved in scope "${this.scope}"`;
365
+ if (options.optional) {
366
+ debug(msg);
367
+ return undefined;
368
+ }
369
+ throw new Error(msg);
370
+ }
371
+ }
372
+ const ownerCtx = ctx.getOwnerContext(this.key);
373
+ if (ownerCtx != null && !ownerCtx.isVisibleTo(resolutionCtx)) {
374
+ const msg = `Resolution context "${resolutionCtx === null || resolutionCtx === void 0 ? void 0 : resolutionCtx.name}" does not have ` +
375
+ `visibility to binding "${this.key} (scope:${this.scope})" in context "${ownerCtx.name}"`;
376
+ if (options.optional) {
377
+ debug(msg);
378
+ return undefined;
379
+ }
380
+ throw new Error(msg);
381
+ }
382
+ return resolutionCtx;
383
+ }
384
+ /**
385
+ * Lock the binding so that it cannot be rebound
386
+ */
387
+ lock() {
388
+ this.isLocked = true;
389
+ return this;
390
+ }
391
+ /**
392
+ * Emit a `changed` event
393
+ * @param operation - Operation that makes changes
394
+ */
395
+ emitChangedEvent(operation) {
396
+ const event = { binding: this, operation, type: 'changed' };
397
+ this.emit('changed', event);
398
+ }
399
+ /**
400
+ * Tag the binding with names or name/value objects. A tag has a name and
401
+ * an optional value. If not supplied, the tag name is used as the value.
402
+ *
403
+ * @param tags - A list of names or name/value objects. Each
404
+ * parameter can be in one of the following forms:
405
+ * - string: A tag name without value
406
+ * - string[]: An array of tag names
407
+ * - TagMap: A map of tag name/value pairs
408
+ *
409
+ * @example
410
+ * ```ts
411
+ * // Add a named tag `controller`
412
+ * binding.tag('controller');
413
+ *
414
+ * // Add two named tags: `controller` and `rest`
415
+ * binding.tag('controller', 'rest');
416
+ *
417
+ * // Add two tags
418
+ * // - `controller` (name = 'controller')
419
+ * // `{name: 'my-controller'}` (name = 'name', value = 'my-controller')
420
+ * binding.tag('controller', {name: 'my-controller'});
421
+ *
422
+ * ```
423
+ */
424
+ tag(...tags) {
425
+ for (const t of tags) {
426
+ if (typeof t === 'string') {
427
+ this.tagMap[t] = t;
428
+ }
429
+ else if (Array.isArray(t)) {
430
+ // Throw an error as TypeScript cannot exclude array from TagMap
431
+ throw new Error('Tag must be a string or an object (but not array): ' + t);
432
+ }
433
+ else {
434
+ Object.assign(this.tagMap, t);
435
+ }
436
+ }
437
+ this.emitChangedEvent('tag');
438
+ return this;
439
+ }
440
+ /**
441
+ * Get an array of tag names
442
+ */
443
+ get tagNames() {
444
+ return Object.keys(this.tagMap);
445
+ }
446
+ /**
447
+ * Set the binding scope
448
+ * @param scope - Binding scope
449
+ */
450
+ inScope(scope) {
451
+ if (this._scope !== scope)
452
+ this._clearCache();
453
+ this._scope = scope;
454
+ this.emitChangedEvent('scope');
455
+ return this;
456
+ }
457
+ /**
458
+ * Apply default scope to the binding. It only changes the scope if it's not
459
+ * set yet
460
+ * @param scope - Default binding scope
461
+ */
462
+ applyDefaultScope(scope) {
463
+ if (!this._scope) {
464
+ this.inScope(scope);
465
+ }
466
+ return this;
467
+ }
468
+ /**
469
+ * Set the `_getValue` function
470
+ * @param getValue - getValue function
471
+ */
472
+ _setValueGetter(getValue) {
473
+ // Clear the cache
474
+ this._clearCache();
475
+ this._getValue = resolutionCtx => {
476
+ return getValue(resolutionCtx);
477
+ };
478
+ this.emitChangedEvent('value');
479
+ }
480
+ /**
481
+ * Bind the key to a constant value. The value must be already available
482
+ * at binding time, it is not allowed to pass a Promise instance.
483
+ *
484
+ * @param value - The bound value.
485
+ *
486
+ * @example
487
+ *
488
+ * ```ts
489
+ * ctx.bind('appName').to('CodeHub');
490
+ * ```
491
+ */
492
+ to(value) {
493
+ if ((0, value_promise_1.isPromiseLike)(value)) {
494
+ // Promises are a construct primarily intended for flow control:
495
+ // In an algorithm with steps 1 and 2, we want to wait for the outcome
496
+ // of step 1 before starting step 2.
497
+ //
498
+ // Promises are NOT a tool for storing values that may become available
499
+ // in the future, depending on the success or a failure of a background
500
+ // async task.
501
+ //
502
+ // Values stored in bindings are typically accessed only later,
503
+ // in a different turn of the event loop or the Promise micro-queue.
504
+ // As a result, when a promise is stored via `.to()` and is rejected
505
+ // later, then more likely than not, there will be no error (catch)
506
+ // handler registered yet, and Node.js will print
507
+ // "Unhandled Rejection Warning".
508
+ throw new Error('Promise instances are not allowed for constant values ' +
509
+ 'bound via ".to()". Register an async getter function ' +
510
+ 'via ".toDynamicValue()" instead.');
511
+ }
512
+ /* istanbul ignore if */
513
+ if (debug.enabled) {
514
+ debug('Bind %s to constant:', this.key, value);
515
+ }
516
+ this._source = {
517
+ type: BindingType.CONSTANT,
518
+ value,
519
+ };
520
+ this._setValueGetter(resolutionCtx => {
521
+ return Binding.valueOrProxy(resolutionCtx, value);
522
+ });
523
+ return this;
524
+ }
525
+ /**
526
+ * Bind the key to a computed (dynamic) value.
527
+ *
528
+ * @param factoryFn - The factory function creating the value.
529
+ * Both sync and async functions are supported.
530
+ *
531
+ * @example
532
+ *
533
+ * ```ts
534
+ * // synchronous
535
+ * ctx.bind('now').toDynamicValue(() => Date.now());
536
+ *
537
+ * // asynchronous
538
+ * ctx.bind('something').toDynamicValue(
539
+ * async () => Promise.delay(10).then(doSomething)
540
+ * );
541
+ * ```
542
+ */
543
+ toDynamicValue(factory) {
544
+ /* istanbul ignore if */
545
+ if (debug.enabled) {
546
+ debug('Bind %s to dynamic value:', this.key, factory);
547
+ }
548
+ this._source = {
549
+ type: BindingType.DYNAMIC_VALUE,
550
+ value: factory,
551
+ };
552
+ let factoryFn;
553
+ if (isDynamicValueProviderClass(factory)) {
554
+ factoryFn = toValueFactory(factory);
555
+ }
556
+ else {
557
+ factoryFn = factory;
558
+ }
559
+ this._setValueGetter(resolutionCtx => {
560
+ const value = factoryFn(resolutionCtx);
561
+ return Binding.valueOrProxy(resolutionCtx, value);
562
+ });
563
+ return this;
564
+ }
565
+ static valueOrProxy(resolutionCtx, value) {
566
+ if (!resolutionCtx.options.asProxyWithInterceptors)
567
+ return value;
568
+ return createInterceptionProxyFromInstance(value, resolutionCtx.context, resolutionCtx.options.session);
569
+ }
570
+ /**
571
+ * Bind the key to a value computed by a Provider.
572
+ *
573
+ * * @example
574
+ *
575
+ * ```ts
576
+ * export class DateProvider implements Provider<Date> {
577
+ * constructor(@inject('stringDate') private param: String){}
578
+ * value(): Date {
579
+ * return new Date(param);
580
+ * }
581
+ * }
582
+ * ```
583
+ *
584
+ * @param provider - The value provider to use.
585
+ */
586
+ toProvider(providerClass) {
587
+ /* istanbul ignore if */
588
+ if (debug.enabled) {
589
+ debug('Bind %s to provider %s', this.key, providerClass.name);
590
+ }
591
+ this._source = {
592
+ type: BindingType.PROVIDER,
593
+ value: providerClass,
594
+ };
595
+ this._setValueGetter(resolutionCtx => {
596
+ const providerOrPromise = (0, resolver_1.instantiateClass)(providerClass, resolutionCtx.context, resolutionCtx.options.session);
597
+ const value = (0, value_promise_1.transformValueOrPromise)(providerOrPromise, p => p.value());
598
+ return Binding.valueOrProxy(resolutionCtx, value);
599
+ });
600
+ return this;
601
+ }
602
+ /**
603
+ * Bind the key to an instance of the given class.
604
+ *
605
+ * @param ctor - The class constructor to call. Any constructor
606
+ * arguments must be annotated with `@inject` so that
607
+ * we can resolve them from the context.
608
+ */
609
+ toClass(ctor) {
610
+ /* istanbul ignore if */
611
+ if (debug.enabled) {
612
+ debug('Bind %s to class %s', this.key, ctor.name);
613
+ }
614
+ this._source = {
615
+ type: BindingType.CLASS,
616
+ value: ctor,
617
+ };
618
+ this._setValueGetter(resolutionCtx => {
619
+ const value = (0, resolver_1.instantiateClass)(ctor, resolutionCtx.context, resolutionCtx.options.session);
620
+ return Binding.valueOrProxy(resolutionCtx, value);
621
+ });
622
+ return this;
623
+ }
624
+ /**
625
+ * Bind to a class optionally decorated with `@injectable`. Based on the
626
+ * introspection of the class, it calls `toClass/toProvider/toDynamicValue`
627
+ * internally. The current binding key will be preserved (not being overridden
628
+ * by the key inferred from the class or options).
629
+ *
630
+ * This is similar to {@link createBindingFromClass} but applies to an
631
+ * existing binding.
632
+ *
633
+ * @example
634
+ *
635
+ * ```ts
636
+ * @injectable({scope: BindingScope.SINGLETON, tags: {service: 'MyService}})
637
+ * class MyService {
638
+ * // ...
639
+ * }
640
+ *
641
+ * const ctx = new Context();
642
+ * ctx.bind('services.MyService').toInjectable(MyService);
643
+ * ```
644
+ *
645
+ * @param ctor - A class decorated with `@injectable`.
646
+ */
647
+ toInjectable(ctor) {
648
+ this.apply((0, binding_inspector_1.bindingTemplateFor)(ctor));
649
+ return this;
650
+ }
651
+ /**
652
+ * Bind the key to an alias of another binding
653
+ * @param keyWithPath - Target binding key with optional path,
654
+ * such as `servers.RestServer.options#apiExplorer`
655
+ */
656
+ toAlias(keyWithPath) {
657
+ /* istanbul ignore if */
658
+ if (debug.enabled) {
659
+ debug('Bind %s to alias %s', this.key, keyWithPath);
660
+ }
661
+ this._source = {
662
+ type: BindingType.ALIAS,
663
+ value: keyWithPath,
664
+ };
665
+ this._setValueGetter(({ context, options }) => {
666
+ return context.getValueOrPromise(keyWithPath, options);
667
+ });
668
+ return this;
669
+ }
670
+ /**
671
+ * Unlock the binding
672
+ */
673
+ unlock() {
674
+ this.isLocked = false;
675
+ return this;
676
+ }
677
+ /**
678
+ * Apply one or more template functions to set up the binding with scope,
679
+ * tags, and other attributes as a group.
680
+ *
681
+ * @example
682
+ * ```ts
683
+ * const serverTemplate = (binding: Binding) =>
684
+ * binding.inScope(BindingScope.SINGLETON).tag('server');
685
+ *
686
+ * const serverBinding = new Binding<RestServer>('servers.RestServer1');
687
+ * serverBinding.apply(serverTemplate);
688
+ * ```
689
+ * @param templateFns - One or more functions to configure the binding
690
+ */
691
+ apply(...templateFns) {
692
+ for (const fn of templateFns) {
693
+ fn(this);
694
+ }
695
+ return this;
696
+ }
697
+ /**
698
+ * Convert to a plain JSON object
699
+ */
700
+ toJSON() {
701
+ var _a, _b, _c, _d;
702
+ const json = {
703
+ key: this.key,
704
+ scope: this.scope,
705
+ tags: this.tagMap,
706
+ isLocked: this.isLocked,
707
+ };
708
+ if (this.type != null) {
709
+ json.type = this.type;
710
+ }
711
+ switch ((_a = this._source) === null || _a === void 0 ? void 0 : _a.type) {
712
+ case BindingType.CLASS:
713
+ json.valueConstructor = (_b = this._source) === null || _b === void 0 ? void 0 : _b.value.name;
714
+ break;
715
+ case BindingType.PROVIDER:
716
+ json.providerConstructor = (_c = this._source) === null || _c === void 0 ? void 0 : _c.value.name;
717
+ break;
718
+ case BindingType.ALIAS:
719
+ json.alias = (_d = this._source) === null || _d === void 0 ? void 0 : _d.value.toString();
720
+ break;
721
+ }
722
+ return json;
723
+ }
724
+ /**
725
+ * Inspect the binding to return a json representation of the binding information
726
+ * @param options - Options to control what information should be included
727
+ */
728
+ inspect(options = {}) {
729
+ options = {
730
+ includeInjections: false,
731
+ ...options,
732
+ };
733
+ const json = this.toJSON();
734
+ if (options.includeInjections) {
735
+ const injections = (0, inject_1.inspectInjections)(this);
736
+ if (Object.keys(injections).length)
737
+ json.injections = injections;
738
+ }
739
+ return json;
740
+ }
741
+ /**
742
+ * A static method to create a binding so that we can do
743
+ * `Binding.bind('foo').to('bar');` as `new Binding('foo').to('bar')` is not
744
+ * easy to read.
745
+ * @param key - Binding key
746
+ */
747
+ static bind(key) {
748
+ return new Binding(key);
749
+ }
750
+ /**
751
+ * Create a configuration binding for the given key
752
+ *
753
+ * @example
754
+ * ```ts
755
+ * const configBinding = Binding.configure('servers.RestServer.server1')
756
+ * .to({port: 3000});
757
+ * ```
758
+ *
759
+ * @typeParam V Generic type for the configuration value (not the binding to
760
+ * be configured)
761
+ *
762
+ * @param key - Key for the binding to be configured
763
+ */
764
+ static configure(key) {
765
+ return new Binding(binding_key_1.BindingKey.buildKeyForConfig(key)).tag({
766
+ [keys_1.ContextTags.CONFIGURATION_FOR]: key.toString(),
767
+ });
768
+ }
769
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
770
+ on(event, listener) {
771
+ return super.on(event, listener);
772
+ }
773
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
774
+ once(event, listener) {
775
+ return super.once(event, listener);
776
+ }
777
+ }
778
+ exports.Binding = Binding;
779
+ function createInterceptionProxyFromInstance(instOrPromise, context, session) {
780
+ return (0, value_promise_1.transformValueOrPromise)(instOrPromise, inst => {
781
+ if (typeof inst !== 'object' || inst == null)
782
+ return inst;
783
+ return (0, interception_proxy_1.createProxyWithInterceptors)(
784
+ // Cast inst from `T` to `object`
785
+ inst, context, session);
786
+ });
787
+ }
788
+ //# sourceMappingURL=binding.js.map