UNPKG

20.4 kBTypeScriptView Raw
1/// <reference types="node" />
2import { Debugger } from 'debug';
3import { EventEmitter } from 'events';
4import { Binding, BindingInspectOptions, BindingScope, BindingTag } from './binding';
5import { ConfigurationResolver } from './binding-config';
6import { BindingFilter } from './binding-filter';
7import { BindingAddress } from './binding-key';
8import { BindingComparator } from './binding-sorter';
9import { ContextEvent, ContextEventListener } from './context-event';
10import { ContextEventObserver, ContextObserver } from './context-observer';
11import { ContextSubscriptionManager, Subscription } from './context-subscription';
12import { ContextTagIndexer } from './context-tag-indexer';
13import { ContextView } from './context-view';
14import { JSONObject } from './json-types';
15import { ResolutionOptions, ResolutionOptionsOrSession, ResolutionSession } from './resolution-session';
16import { BoundValue, ValueOrPromise } from './value-promise';
17/**
18 * Context provides an implementation of Inversion of Control (IoC) container
19 */
20export declare class Context extends EventEmitter {
21 /**
22 * Name of the context
23 */
24 readonly name: string;
25 /**
26 * Key to binding map as the internal registry
27 */
28 protected readonly registry: Map<string, Binding>;
29 /**
30 * Indexer for bindings by tag
31 */
32 protected readonly tagIndexer: ContextTagIndexer;
33 /**
34 * Manager for observer subscriptions
35 */
36 readonly subscriptionManager: ContextSubscriptionManager;
37 /**
38 * Parent context
39 */
40 protected _parent?: Context;
41 /**
42 * Configuration resolver
43 */
44 protected configResolver: ConfigurationResolver;
45 /**
46 * A debug function which can be overridden by subclasses.
47 *
48 * @example
49 * ```ts
50 * import debugFactory from 'debug';
51 * const debug = debugFactory('loopback:context:application');
52 * export class Application extends Context {
53 * super('application');
54 * this._debug = debug;
55 * }
56 * ```
57 */
58 protected _debug: Debugger;
59 /**
60 * Scope for binding resolution
61 */
62 scope: BindingScope;
63 /**
64 * Create a new context.
65 *
66 * @example
67 * ```ts
68 * // Create a new root context, let the framework to create a unique name
69 * const rootCtx = new Context();
70 *
71 * // Create a new child context inheriting bindings from `rootCtx`
72 * const childCtx = new Context(rootCtx);
73 *
74 * // Create another root context called "application"
75 * const appCtx = new Context('application');
76 *
77 * // Create a new child context called "request" and inheriting bindings
78 * // from `appCtx`
79 * const reqCtx = new Context(appCtx, 'request');
80 * ```
81 * @param _parent - The optional parent context
82 * @param name - Name of the context. If not provided, a unique identifier
83 * will be generated as the name.
84 */
85 constructor(_parent?: Context | string, name?: string);
86 /**
87 * Get the debug namespace for the context class. Subclasses can override
88 * this method to supply its own namespace.
89 *
90 * @example
91 * ```ts
92 * export class Application extends Context {
93 * super('application');
94 * }
95 *
96 * protected getDebugNamespace() {
97 * return 'loopback:context:application';
98 * }
99 * ```
100 */
101 protected getDebugNamespace(): string;
102 private generateName;
103 /**
104 * @internal
105 * Getter for ContextSubscriptionManager
106 */
107 get parent(): Context | undefined;
108 /**
109 * Wrap the debug statement so that it always print out the context name
110 * as the prefix
111 * @param args - Arguments for the debug
112 */
113 protected debug(...args: unknown[]): void;
114 /**
115 * A strongly-typed method to emit context events
116 * @param type Event type
117 * @param event Context event
118 */
119 emitEvent<T extends ContextEvent>(type: string, event: T): void;
120 /**
121 * Emit an `error` event
122 * @param err Error
123 */
124 emitError(err: unknown): void;
125 /**
126 * Create a binding with the given key in the context. If a locked binding
127 * already exists with the same key, an error will be thrown.
128 *
129 * @param key - Binding key
130 */
131 bind<ValueType = BoundValue>(key: BindingAddress<ValueType>): Binding<ValueType>;
132 /**
133 * Add a binding to the context. If a locked binding already exists with the
134 * same key, an error will be thrown.
135 * @param binding - The configured binding to be added
136 */
137 add(binding: Binding<unknown>): this;
138 /**
139 * Create a corresponding binding for configuration of the target bound by
140 * the given key in the context.
141 *
142 * For example, `ctx.configure('controllers.MyController').to({x: 1})` will
143 * create binding `controllers.MyController:$config` with value `{x: 1}`.
144 *
145 * @param key - The key for the binding to be configured
146 */
147 configure<ConfigValueType = BoundValue>(key?: BindingAddress): Binding<ConfigValueType>;
148 /**
149 * Get the value or promise of configuration for a given binding by key
150 *
151 * @param key - Binding key
152 * @param propertyPath - Property path for the option. For example, `x.y`
153 * requests for `<config>.x.y`. If not set, the `<config>` object will be
154 * returned.
155 * @param resolutionOptions - Options for the resolution.
156 * - optional: if not set or set to `true`, `undefined` will be returned if
157 * no corresponding value is found. Otherwise, an error will be thrown.
158 */
159 getConfigAsValueOrPromise<ConfigValueType>(key: BindingAddress, propertyPath?: string, resolutionOptions?: ResolutionOptions): ValueOrPromise<ConfigValueType | undefined>;
160 /**
161 * Set up the configuration resolver if needed
162 */
163 protected setupConfigurationResolverIfNeeded(): ConfigurationResolver;
164 /**
165 * Resolve configuration for the binding by key
166 *
167 * @param key - Binding key
168 * @param propertyPath - Property path for the option. For example, `x.y`
169 * requests for `<config>.x.y`. If not set, the `<config>` object will be
170 * returned.
171 * @param resolutionOptions - Options for the resolution.
172 */
173 getConfig<ConfigValueType>(key: BindingAddress, propertyPath?: string, resolutionOptions?: ResolutionOptions): Promise<ConfigValueType | undefined>;
174 /**
175 * Resolve configuration synchronously for the binding by key
176 *
177 * @param key - Binding key
178 * @param propertyPath - Property path for the option. For example, `x.y`
179 * requests for `config.x.y`. If not set, the `config` object will be
180 * returned.
181 * @param resolutionOptions - Options for the resolution.
182 */
183 getConfigSync<ConfigValueType>(key: BindingAddress, propertyPath?: string, resolutionOptions?: ResolutionOptions): ConfigValueType | undefined;
184 /**
185 * Unbind a binding from the context. No parent contexts will be checked.
186 *
187 * @remarks
188 * If you need to unbind a binding owned by a parent context, use the code
189 * below:
190 *
191 * ```ts
192 * const ownerCtx = ctx.getOwnerContext(key);
193 * return ownerCtx != null && ownerCtx.unbind(key);
194 * ```
195 *
196 * @param key - Binding key
197 * @returns true if the binding key is found and removed from this context
198 */
199 unbind(key: BindingAddress): boolean;
200 /**
201 * Add a context event observer to the context
202 * @param observer - Context observer instance or function
203 */
204 subscribe(observer: ContextEventObserver): Subscription;
205 /**
206 * Remove the context event observer from the context
207 * @param observer - Context event observer
208 */
209 unsubscribe(observer: ContextEventObserver): boolean;
210 /**
211 * Close the context: clear observers, stop notifications, and remove event
212 * listeners from its parent context.
213 *
214 * @remarks
215 * This method MUST be called to avoid memory leaks once a context object is
216 * no longer needed and should be recycled. An example is the `RequestContext`,
217 * which is created per request.
218 */
219 close(): void;
220 /**
221 * Check if an observer is subscribed to this context
222 * @param observer - Context observer
223 */
224 isSubscribed(observer: ContextObserver): boolean;
225 /**
226 * Create a view of the context chain with the given binding filter
227 * @param filter - A function to match bindings
228 * @param comparator - A function to sort matched bindings
229 * @param options - Resolution options
230 */
231 createView<T = unknown>(filter: BindingFilter, comparator?: BindingComparator, options?: Omit<ResolutionOptions, 'session'>): ContextView<T>;
232 /**
233 * Check if a binding exists with the given key in the local context without
234 * delegating to the parent context
235 * @param key - Binding key
236 */
237 contains(key: BindingAddress): boolean;
238 /**
239 * Check if a key is bound in the context or its ancestors
240 * @param key - Binding key
241 */
242 isBound(key: BindingAddress): boolean;
243 /**
244 * Get the owning context for a binding or its key
245 * @param keyOrBinding - Binding object or key
246 */
247 getOwnerContext(keyOrBinding: BindingAddress | Readonly<Binding<unknown>>): Context | undefined;
248 /**
249 * Get the context matching the scope
250 * @param scope - Binding scope
251 */
252 getScopedContext(scope: BindingScope.APPLICATION | BindingScope.SERVER | BindingScope.REQUEST): Context | undefined;
253 /**
254 * Locate the resolution context for the given binding. Only bindings in the
255 * resolution context and its ancestors are visible as dependencies to resolve
256 * the given binding
257 * @param binding - Binding object
258 */
259 getResolutionContext(binding: Readonly<Binding<unknown>>): Context | undefined;
260 /**
261 * Check if this context is visible (same or ancestor) to the given one
262 * @param ctx - Another context object
263 */
264 isVisibleTo(ctx: Context): boolean;
265 /**
266 * Find bindings using a key pattern or filter function
267 * @param pattern - A filter function, a regexp or a wildcard pattern with
268 * optional `*` and `?`. Find returns such bindings where the key matches
269 * the provided pattern.
270 *
271 * For a wildcard:
272 * - `*` matches zero or more characters except `.` and `:`
273 * - `?` matches exactly one character except `.` and `:`
274 *
275 * For a filter function:
276 * - return `true` to include the binding in the results
277 * - return `false` to exclude it.
278 */
279 find<ValueType = BoundValue>(pattern?: string | RegExp | BindingFilter): Readonly<Binding<ValueType>>[];
280 /**
281 * Find bindings using the tag filter. If the filter matches one of the
282 * binding tags, the binding is included.
283 *
284 * @param tagFilter - A filter for tags. It can be in one of the following
285 * forms:
286 * - A regular expression, such as `/controller/`
287 * - A wildcard pattern string with optional `*` and `?`, such as `'con*'`
288 * For a wildcard:
289 * - `*` matches zero or more characters except `.` and `:`
290 * - `?` matches exactly one character except `.` and `:`
291 * - An object containing tag name/value pairs, such as
292 * `{name: 'my-controller'}`
293 */
294 findByTag<ValueType = BoundValue>(tagFilter: BindingTag | RegExp): Readonly<Binding<ValueType>>[];
295 /**
296 * Find bindings by tag leveraging indexes
297 * @param tag - Tag name pattern or name/value pairs
298 */
299 protected _findByTagIndex<ValueType = BoundValue>(tag: BindingTag | RegExp): Readonly<Binding<ValueType>>[];
300 protected _mergeWithParent<ValueType>(childList: Readonly<Binding<ValueType>>[], parentList?: Readonly<Binding<ValueType>>[]): Readonly<Binding<ValueType>>[];
301 /**
302 * Get the value bound to the given key, throw an error when no value is
303 * bound for the given key.
304 *
305 * @example
306 *
307 * ```ts
308 * // get the value bound to "application.instance"
309 * const app = await ctx.get<Application>('application.instance');
310 *
311 * // get "rest" property from the value bound to "config"
312 * const config = await ctx.get<RestComponentConfig>('config#rest');
313 *
314 * // get "a" property of "numbers" property from the value bound to "data"
315 * ctx.bind('data').to({numbers: {a: 1, b: 2}, port: 3000});
316 * const a = await ctx.get<number>('data#numbers.a');
317 * ```
318 *
319 * @param keyWithPath - The binding key, optionally suffixed with a path to the
320 * (deeply) nested property to retrieve.
321 * @param session - Optional session for resolution (accepted for backward
322 * compatibility)
323 * @returns A promise of the bound value.
324 */
325 get<ValueType>(keyWithPath: BindingAddress<ValueType>, session?: ResolutionSession): Promise<ValueType>;
326 /**
327 * Get the value bound to the given key, optionally return a (deep) property
328 * of the bound value.
329 *
330 * @example
331 *
332 * ```ts
333 * // get "rest" property from the value bound to "config"
334 * // use `undefined` when no config is provided
335 * const config = await ctx.get<RestComponentConfig>('config#rest', {
336 * optional: true
337 * });
338 * ```
339 *
340 * @param keyWithPath - The binding key, optionally suffixed with a path to the
341 * (deeply) nested property to retrieve.
342 * @param options - Options for resolution.
343 * @returns A promise of the bound value, or a promise of undefined when
344 * the optional binding is not found.
345 */
346 get<ValueType>(keyWithPath: BindingAddress<ValueType>, options: ResolutionOptions): Promise<ValueType | undefined>;
347 /**
348 * Get the synchronous value bound to the given key, optionally
349 * return a (deep) property of the bound value.
350 *
351 * This method throws an error if the bound value requires async computation
352 * (returns a promise). You should never rely on sync bindings in production
353 * code.
354 *
355 * @example
356 *
357 * ```ts
358 * // get the value bound to "application.instance"
359 * const app = ctx.getSync<Application>('application.instance');
360 *
361 * // get "rest" property from the value bound to "config"
362 * const config = await ctx.getSync<RestComponentConfig>('config#rest');
363 * ```
364 *
365 * @param keyWithPath - The binding key, optionally suffixed with a path to the
366 * (deeply) nested property to retrieve.
367 * @param session - Session for resolution (accepted for backward compatibility)
368 * @returns A promise of the bound value.
369 */
370 getSync<ValueType>(keyWithPath: BindingAddress<ValueType>, session?: ResolutionSession): ValueType;
371 /**
372 * Get the synchronous value bound to the given key, optionally
373 * return a (deep) property of the bound value.
374 *
375 * This method throws an error if the bound value requires async computation
376 * (returns a promise). You should never rely on sync bindings in production
377 * code.
378 *
379 * @example
380 *
381 * ```ts
382 * // get "rest" property from the value bound to "config"
383 * // use "undefined" when no config is provided
384 * const config = await ctx.getSync<RestComponentConfig>('config#rest', {
385 * optional: true
386 * });
387 * ```
388 *
389 * @param keyWithPath - The binding key, optionally suffixed with a path to the
390 * (deeply) nested property to retrieve.
391 * @param options - Options for resolution.
392 * @returns The bound value, or undefined when an optional binding is not found.
393 */
394 getSync<ValueType>(keyWithPath: BindingAddress<ValueType>, options?: ResolutionOptions): ValueType | undefined;
395 /**
396 * Look up a binding by key in the context and its ancestors. If no matching
397 * binding is found, an error will be thrown.
398 *
399 * @param key - Binding key
400 */
401 getBinding<ValueType = BoundValue>(key: BindingAddress<ValueType>): Binding<ValueType>;
402 /**
403 * Look up a binding by key in the context and its ancestors. If no matching
404 * binding is found and `options.optional` is not set to true, an error will
405 * be thrown.
406 *
407 * @param key - Binding key
408 * @param options - Options to control if the binding is optional. If
409 * `options.optional` is set to true, the method will return `undefined`
410 * instead of throwing an error if the binding key is not found.
411 */
412 getBinding<ValueType>(key: BindingAddress<ValueType>, options?: {
413 optional?: boolean;
414 }): Binding<ValueType> | undefined;
415 /**
416 * Find or create a binding for the given key
417 * @param key - Binding address
418 * @param policy - Binding creation policy
419 */
420 findOrCreateBinding<T>(key: BindingAddress<T>, policy?: BindingCreationPolicy): Binding<T>;
421 /**
422 * Get the value bound to the given key.
423 *
424 * This is an internal version that preserves the dual sync/async result
425 * of `Binding#getValue()`. Users should use `get()` or `getSync()` instead.
426 *
427 * @example
428 *
429 * ```ts
430 * // get the value bound to "application.instance"
431 * ctx.getValueOrPromise<Application>('application.instance');
432 *
433 * // get "rest" property from the value bound to "config"
434 * ctx.getValueOrPromise<RestComponentConfig>('config#rest');
435 *
436 * // get "a" property of "numbers" property from the value bound to "data"
437 * ctx.bind('data').to({numbers: {a: 1, b: 2}, port: 3000});
438 * ctx.getValueOrPromise<number>('data#numbers.a');
439 * ```
440 *
441 * @param keyWithPath - The binding key, optionally suffixed with a path to the
442 * (deeply) nested property to retrieve.
443 * @param optionsOrSession - Options for resolution or a session
444 * @returns The bound value or a promise of the bound value, depending
445 * on how the binding is configured.
446 * @internal
447 */
448 getValueOrPromise<ValueType>(keyWithPath: BindingAddress<ValueType>, optionsOrSession?: ResolutionOptionsOrSession): ValueOrPromise<ValueType | undefined>;
449 /**
450 * Create a plain JSON object for the context
451 */
452 toJSON(): JSONObject;
453 /**
454 * Inspect the context and dump out a JSON object representing the context
455 * hierarchy
456 * @param options - Options for inspect
457 */
458 inspect(options?: ContextInspectOptions): JSONObject;
459 /**
460 * Inspect the context hierarchy
461 * @param options - Options for inspect
462 * @param visitedClasses - A map to keep class to name so that we can have
463 * different names for classes with colliding names. The situation can happen
464 * when two classes with the same name are bound in different modules.
465 */
466 private _inspect;
467 /**
468 * The "bind" event is emitted when a new binding is added to the context.
469 * The "unbind" event is emitted when an existing binding is removed.
470 *
471 * @param eventName The name of the event - always `bind` or `unbind`.
472 * @param listener The listener function to call when the event is emitted.
473 */
474 on(eventName: 'bind' | 'unbind', listener: ContextEventListener): this;
475 on(event: string | symbol, listener: (...args: any[]) => void): this;
476 /**
477 * The "bind" event is emitted when a new binding is added to the context.
478 * The "unbind" event is emitted when an existing binding is removed.
479 *
480 * @param eventName The name of the event - always `bind` or `unbind`.
481 * @param listener The listener function to call when the event is emitted.
482 */
483 once(eventName: 'bind' | 'unbind', listener: ContextEventListener): this;
484 once(event: string | symbol, listener: (...args: any[]) => void): this;
485}
486/**
487 * Options for context.inspect()
488 */
489export interface ContextInspectOptions extends BindingInspectOptions {
490 /**
491 * The flag to control if parent context should be inspected
492 */
493 includeParent?: boolean;
494}
495/**
496 * Policy to control if a binding should be created for the context
497 */
498export declare enum BindingCreationPolicy {
499 /**
500 * Always create a binding with the key for the context
501 */
502 ALWAYS_CREATE = "Always",
503 /**
504 * Never create a binding for the context. If the key is not bound in the
505 * context, throw an error.
506 */
507 NEVER_CREATE = "Never",
508 /**
509 * Create a binding if the key is not bound in the context. Otherwise, return
510 * the existing binding.
511 */
512 CREATE_IF_NOT_BOUND = "IfNotBound"
513}
514
\No newline at end of file