UNPKG

18.7 kBTypeScriptView Raw
1/// <reference types="node" />
2import { EventEmitter } from 'events';
3import { BindingAddress } from './binding-key';
4import { Context } from './context';
5import { JSONObject } from './json-types';
6import { Provider } from './provider';
7import { ResolutionContext, ResolutionOptions, ResolutionSession } from './resolution-session';
8import { BoundValue, Constructor, MapObject, ValueOrPromise } from './value-promise';
9/**
10 * Scope for binding values
11 */
12export declare enum BindingScope {
13 /**
14 * The binding provides a value that is calculated each time. This will be
15 * the default scope if not set.
16 *
17 * For example, with the following context hierarchy:
18 *
19 * - `app` (with a binding `'b1'` that produces sequential values 0, 1, ...)
20 * - req1
21 * - req2
22 *
23 * Now `'b1'` is resolved to a new value each time for `app` and its
24 * descendants `req1` and `req2`:
25 * - app.get('b1') ==> 0
26 * - req1.get('b1') ==> 1
27 * - req2.get('b1') ==> 2
28 * - req2.get('b1') ==> 3
29 * - app.get('b1') ==> 4
30 */
31 TRANSIENT = "Transient",
32 /**
33 * @deprecated Finer-grained scopes such as `APPLICATION`, `SERVER`, or
34 * `REQUEST` should be used instead to ensure the scope of sharing of resolved
35 * binding values.
36 *
37 * The binding provides a value as a singleton within each local context. The
38 * value is calculated only once per context and cached for subsequential
39 * uses. Child contexts have their own value and do not share with their
40 * ancestors.
41 *
42 * For example, with the following context hierarchy:
43 *
44 * - `app` (with a binding `'b1'` that produces sequential values 0, 1, ...)
45 * - req1
46 * - req2
47 *
48 * 1. `0` is the resolved value for `'b1'` within the `app` afterward
49 * - app.get('b1') ==> 0 (always)
50 *
51 * 2. `'b1'` is resolved in `app` but not in `req1`, a new value `1` is
52 * calculated and used for `req1` afterward
53 * - req1.get('b1') ==> 1 (always)
54 *
55 * 3. `'b1'` is resolved in `app` but not in `req2`, a new value `2` is
56 * calculated and used for `req2` afterward
57 * - req2.get('b1') ==> 2 (always)
58 *
59 */
60 CONTEXT = "Context",
61 /**
62 * The binding provides a value as a singleton within the context hierarchy
63 * (the owning context and its descendants). The value is calculated only
64 * once for the owning context and cached for subsequential uses. Child
65 * contexts share the same value as their ancestors.
66 *
67 * For example, with the following context hierarchy:
68 *
69 * - `app` (with a binding `'b1'` that produces sequential values 0, 1, ...)
70 * - req1
71 * - req2
72 *
73 * 1. `0` is the singleton for `app` afterward
74 * - app.get('b1') ==> 0 (always)
75 *
76 * 2. `'b1'` is resolved in `app`, reuse it for `req1`
77 * - req1.get('b1') ==> 0 (always)
78 *
79 * 3. `'b1'` is resolved in `app`, reuse it for `req2`
80 * - req2.get('b1') ==> 0 (always)
81 */
82 SINGLETON = "Singleton",
83 /**
84 * Application scope
85 *
86 * @remarks
87 * The binding provides an application-scoped value within the context
88 * hierarchy. Resolved value for this binding will be cached and shared for
89 * the same application context (denoted by its scope property set to
90 * `BindingScope.APPLICATION`).
91 *
92 */
93 APPLICATION = "Application",
94 /**
95 * Server scope
96 *
97 * @remarks
98 * The binding provides an server-scoped value within the context hierarchy.
99 * Resolved value for this binding will be cached and shared for the same
100 * server context (denoted by its scope property set to
101 * `BindingScope.SERVER`).
102 *
103 * It's possible that an application has more than one servers configured,
104 * such as a `RestServer` and a `GrpcServer`. Both server contexts are created
105 * with `scope` set to `BindingScope.SERVER`. Depending on where a binding
106 * is resolved:
107 * - If the binding is resolved from the RestServer or below, it will be
108 * cached using the RestServer context as the key.
109 * - If the binding is resolved from the GrpcServer or below, it will be
110 * cached using the GrpcServer context as the key.
111 *
112 * The same binding can resolved/shared/cached for all servers, each of which
113 * has its own value for the binding.
114 */
115 SERVER = "Server",
116 /**
117 * Request scope
118 *
119 * @remarks
120 * The binding provides an request-scoped value within the context hierarchy.
121 * Resolved value for this binding will be cached and shared for the same
122 * request context (denoted by its scope property set to
123 * `BindingScope.REQUEST`).
124 *
125 * The `REQUEST` scope is very useful for controllers, services and artifacts
126 * that want to have a single instance/value for a given request.
127 */
128 REQUEST = "Request"
129}
130/**
131 * Type of the binding source
132 */
133export declare enum BindingType {
134 /**
135 * A fixed value
136 */
137 CONSTANT = "Constant",
138 /**
139 * A function to get the value
140 */
141 DYNAMIC_VALUE = "DynamicValue",
142 /**
143 * A class to be instantiated as the value
144 */
145 CLASS = "Class",
146 /**
147 * A provider class with `value()` function to get the value
148 */
149 PROVIDER = "Provider",
150 /**
151 * A alias to another binding key with optional path
152 */
153 ALIAS = "Alias"
154}
155/**
156 * Binding source for `to`
157 */
158export type ConstantBindingSource<T> = {
159 type: BindingType.CONSTANT;
160 value: T;
161};
162/**
163 * Binding source for `toDynamicValue`
164 */
165export type DynamicValueBindingSource<T> = {
166 type: BindingType.DYNAMIC_VALUE;
167 value: ValueFactory<T> | DynamicValueProviderClass<T>;
168};
169/**
170 * Binding source for `toClass`
171 */
172export type ClassBindingSource<T> = {
173 type: BindingType.CLASS;
174 value: Constructor<T>;
175};
176/**
177 * Binding source for `toProvider`
178 */
179export type ProviderBindingSource<T> = {
180 type: BindingType.PROVIDER;
181 value: Constructor<Provider<T>>;
182};
183/**
184 * Binding source for `toAlias`
185 */
186export type AliasBindingSource<T> = {
187 type: BindingType.ALIAS;
188 value: BindingAddress<T>;
189};
190/**
191 * Source for the binding, including the type and value
192 */
193export type BindingSource<T> = ConstantBindingSource<T> | DynamicValueBindingSource<T> | ClassBindingSource<T> | ProviderBindingSource<T> | AliasBindingSource<T>;
194export type TagMap = MapObject<any>;
195/**
196 * Binding tag can be a simple name or name/value pairs
197 */
198export type BindingTag = TagMap | string;
199/**
200 * A function as the template to configure bindings
201 */
202export type BindingTemplate<T = unknown> = (binding: Binding<T>) => void;
203/**
204 * Information for a binding event
205 */
206export type BindingEvent = {
207 /**
208 * Event type
209 */
210 type: 'changed' | string;
211 /**
212 * Source binding that emits the event
213 */
214 binding: Readonly<Binding<unknown>>;
215 /**
216 * Operation that triggers the event
217 */
218 operation: 'tag' | 'scope' | 'value' | string;
219};
220/**
221 * Event listeners for binding events
222 */
223export type BindingEventListener = (
224/**
225 * Binding event
226 */
227event: BindingEvent) => void;
228/**
229 * A factory function for `toDynamicValue`
230 */
231export type ValueFactory<T = unknown> = (resolutionCtx: ResolutionContext) => ValueOrPromise<T | undefined>;
232/**
233 * A class with a static `value` method as the factory function for
234 * `toDynamicValue`.
235 *
236 * @example
237 * ```ts
238 * import {inject} from '@loopback/context';
239 *
240 * export class DynamicGreetingProvider {
241 * static value(@inject('currentUser') user: string) {
242 * return `Hello, ${user}`;
243 * }
244 * }
245 * ```
246 */
247export interface DynamicValueProviderClass<T = unknown> extends Constructor<unknown>, Function {
248 value: (...args: BoundValue[]) => ValueOrPromise<T>;
249}
250/**
251 * Check if the factory is a value factory provider class
252 * @param factory - A factory function or a dynamic value provider class
253 */
254export declare function isDynamicValueProviderClass<T = unknown>(factory: unknown): factory is DynamicValueProviderClass<T>;
255/**
256 * Binding represents an entry in the `Context`. Each binding has a key and a
257 * corresponding value getter.
258 */
259export declare class Binding<T = BoundValue> extends EventEmitter {
260 isLocked: boolean;
261 /**
262 * Key of the binding
263 */
264 readonly key: string;
265 /**
266 * Map for tag name/value pairs
267 */
268 readonly tagMap: TagMap;
269 private _scope?;
270 /**
271 * Scope of the binding to control how the value is cached/shared
272 */
273 get scope(): BindingScope;
274 /**
275 * Type of the binding value getter
276 */
277 get type(): BindingType | undefined;
278 private _cache;
279 private _getValue?;
280 /**
281 * The original source value received from `to`, `toClass`, `toDynamicValue`,
282 * `toProvider`, or `toAlias`.
283 */
284 private _source?;
285 get source(): BindingSource<T> | undefined;
286 /**
287 * For bindings bound via `toClass()`, this property contains the constructor
288 * function of the class
289 */
290 get valueConstructor(): Constructor<T> | undefined;
291 /**
292 * For bindings bound via `toProvider()`, this property contains the
293 * constructor function of the provider class
294 */
295 get providerConstructor(): Constructor<Provider<T>> | undefined;
296 constructor(key: BindingAddress<T>, isLocked?: boolean);
297 /**
298 * Cache the resolved value by the binding scope
299 * @param resolutionCtx - The resolution context
300 * @param result - The calculated value for the binding
301 */
302 private _cacheValue;
303 /**
304 * Clear the cache
305 */
306 private _clearCache;
307 /**
308 * Invalidate the binding cache so that its value will be reloaded next time.
309 * This is useful to force reloading a cached value when its configuration or
310 * dependencies are changed.
311 * **WARNING**: The state held in the cached value will be gone.
312 *
313 * @param ctx - Context object
314 */
315 refresh(ctx: Context): void;
316 /**
317 * This is an internal function optimized for performance.
318 * Users should use `@inject(key)` or `ctx.get(key)` instead.
319 *
320 * Get the value bound to this key. Depending on `isSync`, this
321 * function returns either:
322 * - the bound value
323 * - a promise of the bound value
324 *
325 * Consumers wishing to consume sync values directly should use `isPromiseLike`
326 * to check the type of the returned value to decide how to handle it.
327 *
328 * @example
329 * ```
330 * const result = binding.getValue(ctx);
331 * if (isPromiseLike(result)) {
332 * result.then(doSomething)
333 * } else {
334 * doSomething(result);
335 * }
336 * ```
337 *
338 * @param ctx - Context for the resolution
339 * @param session - Optional session for binding and dependency resolution
340 */
341 getValue(ctx: Context, session?: ResolutionSession): ValueOrPromise<T>;
342 /**
343 * Returns a value or promise for this binding in the given context. The
344 * resolved value can be `undefined` if `optional` is set to `true` in
345 * `options`.
346 * @param ctx - Context for the resolution
347 * @param options - Optional options for binding and dependency resolution
348 */
349 getValue(ctx: Context, options?: ResolutionOptions): ValueOrPromise<T | undefined>;
350 private getValueOrProxy;
351 /**
352 * Locate and validate the resolution context
353 * @param ctx - Current context
354 * @param options - Resolution options
355 */
356 private getResolutionContext;
357 /**
358 * Lock the binding so that it cannot be rebound
359 */
360 lock(): this;
361 /**
362 * Emit a `changed` event
363 * @param operation - Operation that makes changes
364 */
365 private emitChangedEvent;
366 /**
367 * Tag the binding with names or name/value objects. A tag has a name and
368 * an optional value. If not supplied, the tag name is used as the value.
369 *
370 * @param tags - A list of names or name/value objects. Each
371 * parameter can be in one of the following forms:
372 * - string: A tag name without value
373 * - string[]: An array of tag names
374 * - TagMap: A map of tag name/value pairs
375 *
376 * @example
377 * ```ts
378 * // Add a named tag `controller`
379 * binding.tag('controller');
380 *
381 * // Add two named tags: `controller` and `rest`
382 * binding.tag('controller', 'rest');
383 *
384 * // Add two tags
385 * // - `controller` (name = 'controller')
386 * // `{name: 'my-controller'}` (name = 'name', value = 'my-controller')
387 * binding.tag('controller', {name: 'my-controller'});
388 *
389 * ```
390 */
391 tag(...tags: BindingTag[]): this;
392 /**
393 * Get an array of tag names
394 */
395 get tagNames(): string[];
396 /**
397 * Set the binding scope
398 * @param scope - Binding scope
399 */
400 inScope(scope: BindingScope): this;
401 /**
402 * Apply default scope to the binding. It only changes the scope if it's not
403 * set yet
404 * @param scope - Default binding scope
405 */
406 applyDefaultScope(scope: BindingScope): this;
407 /**
408 * Set the `_getValue` function
409 * @param getValue - getValue function
410 */
411 private _setValueGetter;
412 /**
413 * Bind the key to a constant value. The value must be already available
414 * at binding time, it is not allowed to pass a Promise instance.
415 *
416 * @param value - The bound value.
417 *
418 * @example
419 *
420 * ```ts
421 * ctx.bind('appName').to('CodeHub');
422 * ```
423 */
424 to(value: T): this;
425 /**
426 * Bind the key to a computed (dynamic) value.
427 *
428 * @param factoryFn - The factory function creating the value.
429 * Both sync and async functions are supported.
430 *
431 * @example
432 *
433 * ```ts
434 * // synchronous
435 * ctx.bind('now').toDynamicValue(() => Date.now());
436 *
437 * // asynchronous
438 * ctx.bind('something').toDynamicValue(
439 * async () => Promise.delay(10).then(doSomething)
440 * );
441 * ```
442 */
443 toDynamicValue(factory: ValueFactory<T> | DynamicValueProviderClass<T>): this;
444 private static valueOrProxy;
445 /**
446 * Bind the key to a value computed by a Provider.
447 *
448 * * @example
449 *
450 * ```ts
451 * export class DateProvider implements Provider<Date> {
452 * constructor(@inject('stringDate') private param: String){}
453 * value(): Date {
454 * return new Date(param);
455 * }
456 * }
457 * ```
458 *
459 * @param provider - The value provider to use.
460 */
461 toProvider(providerClass: Constructor<Provider<T>>): this;
462 /**
463 * Bind the key to an instance of the given class.
464 *
465 * @param ctor - The class constructor to call. Any constructor
466 * arguments must be annotated with `@inject` so that
467 * we can resolve them from the context.
468 */
469 toClass<C extends T & object>(ctor: Constructor<C>): this;
470 /**
471 * Bind to a class optionally decorated with `@injectable`. Based on the
472 * introspection of the class, it calls `toClass/toProvider/toDynamicValue`
473 * internally. The current binding key will be preserved (not being overridden
474 * by the key inferred from the class or options).
475 *
476 * This is similar to {@link createBindingFromClass} but applies to an
477 * existing binding.
478 *
479 * @example
480 *
481 * ```ts
482 * @injectable({scope: BindingScope.SINGLETON, tags: {service: 'MyService}})
483 * class MyService {
484 * // ...
485 * }
486 *
487 * const ctx = new Context();
488 * ctx.bind('services.MyService').toInjectable(MyService);
489 * ```
490 *
491 * @param ctor - A class decorated with `@injectable`.
492 */
493 toInjectable(ctor: DynamicValueProviderClass<T> | Constructor<T | Provider<T>>): this;
494 /**
495 * Bind the key to an alias of another binding
496 * @param keyWithPath - Target binding key with optional path,
497 * such as `servers.RestServer.options#apiExplorer`
498 */
499 toAlias(keyWithPath: BindingAddress<T>): this;
500 /**
501 * Unlock the binding
502 */
503 unlock(): this;
504 /**
505 * Apply one or more template functions to set up the binding with scope,
506 * tags, and other attributes as a group.
507 *
508 * @example
509 * ```ts
510 * const serverTemplate = (binding: Binding) =>
511 * binding.inScope(BindingScope.SINGLETON).tag('server');
512 *
513 * const serverBinding = new Binding<RestServer>('servers.RestServer1');
514 * serverBinding.apply(serverTemplate);
515 * ```
516 * @param templateFns - One or more functions to configure the binding
517 */
518 apply(...templateFns: BindingTemplate<T>[]): this;
519 /**
520 * Convert to a plain JSON object
521 */
522 toJSON(): JSONObject;
523 /**
524 * Inspect the binding to return a json representation of the binding information
525 * @param options - Options to control what information should be included
526 */
527 inspect(options?: BindingInspectOptions): JSONObject;
528 /**
529 * A static method to create a binding so that we can do
530 * `Binding.bind('foo').to('bar');` as `new Binding('foo').to('bar')` is not
531 * easy to read.
532 * @param key - Binding key
533 */
534 static bind<V = unknown>(key: BindingAddress<V>): Binding<V>;
535 /**
536 * Create a configuration binding for the given key
537 *
538 * @example
539 * ```ts
540 * const configBinding = Binding.configure('servers.RestServer.server1')
541 * .to({port: 3000});
542 * ```
543 *
544 * @typeParam V Generic type for the configuration value (not the binding to
545 * be configured)
546 *
547 * @param key - Key for the binding to be configured
548 */
549 static configure<V = unknown>(key: BindingAddress): Binding<V>;
550 /**
551 * The "changed" event is emitted by methods such as `tag`, `inScope`, `to`,
552 * and `toClass`.
553 *
554 * @param eventName The name of the event - always `changed`.
555 * @param listener The listener function to call when the event is emitted.
556 */
557 on(eventName: 'changed', listener: BindingEventListener): this;
558 on(event: string | symbol, listener: (...args: any[]) => void): this;
559 /**
560 * The "changed" event is emitted by methods such as `tag`, `inScope`, `to`,
561 * and `toClass`.
562 *
563 * @param eventName The name of the event - always `changed`.
564 * @param listener The listener function to call when the event is emitted.
565 */
566 once(eventName: 'changed', listener: BindingEventListener): this;
567 once(event: string | symbol, listener: (...args: any[]) => void): this;
568}
569/**
570 * Options for binding.inspect()
571 */
572export interface BindingInspectOptions {
573 /**
574 * The flag to control if injections should be inspected
575 */
576 includeInjections?: boolean;
577}
578
\No newline at end of file