/// import { EventEmitter } from 'events'; import { BindingAddress } from './binding-key'; import { Context } from './context'; import { JSONObject } from './json-types'; import { Provider } from './provider'; import { ResolutionContext, ResolutionOptions, ResolutionSession } from './resolution-session'; import { BoundValue, Constructor, MapObject, ValueOrPromise } from './value-promise'; /** * Scope for binding values */ export declare enum BindingScope { /** * The binding provides a value that is calculated each time. This will be * the default scope if not set. * * For example, with the following context hierarchy: * * - `app` (with a binding `'b1'` that produces sequential values 0, 1, ...) * - req1 * - req2 * * Now `'b1'` is resolved to a new value each time for `app` and its * descendants `req1` and `req2`: * - app.get('b1') ==> 0 * - req1.get('b1') ==> 1 * - req2.get('b1') ==> 2 * - req2.get('b1') ==> 3 * - app.get('b1') ==> 4 */ TRANSIENT = "Transient", /** * @deprecated Finer-grained scopes such as `APPLICATION`, `SERVER`, or * `REQUEST` should be used instead to ensure the scope of sharing of resolved * binding values. * * The binding provides a value as a singleton within each local context. The * value is calculated only once per context and cached for subsequential * uses. Child contexts have their own value and do not share with their * ancestors. * * For example, with the following context hierarchy: * * - `app` (with a binding `'b1'` that produces sequential values 0, 1, ...) * - req1 * - req2 * * 1. `0` is the resolved value for `'b1'` within the `app` afterward * - app.get('b1') ==> 0 (always) * * 2. `'b1'` is resolved in `app` but not in `req1`, a new value `1` is * calculated and used for `req1` afterward * - req1.get('b1') ==> 1 (always) * * 3. `'b1'` is resolved in `app` but not in `req2`, a new value `2` is * calculated and used for `req2` afterward * - req2.get('b1') ==> 2 (always) * */ CONTEXT = "Context", /** * The binding provides a value as a singleton within the context hierarchy * (the owning context and its descendants). The value is calculated only * once for the owning context and cached for subsequential uses. Child * contexts share the same value as their ancestors. * * For example, with the following context hierarchy: * * - `app` (with a binding `'b1'` that produces sequential values 0, 1, ...) * - req1 * - req2 * * 1. `0` is the singleton for `app` afterward * - app.get('b1') ==> 0 (always) * * 2. `'b1'` is resolved in `app`, reuse it for `req1` * - req1.get('b1') ==> 0 (always) * * 3. `'b1'` is resolved in `app`, reuse it for `req2` * - req2.get('b1') ==> 0 (always) */ SINGLETON = "Singleton", /** * Application scope * * @remarks * The binding provides an application-scoped value within the context * hierarchy. Resolved value for this binding will be cached and shared for * the same application context (denoted by its scope property set to * `BindingScope.APPLICATION`). * */ APPLICATION = "Application", /** * Server scope * * @remarks * The binding provides an server-scoped value within the context hierarchy. * Resolved value for this binding will be cached and shared for the same * server context (denoted by its scope property set to * `BindingScope.SERVER`). * * It's possible that an application has more than one servers configured, * such as a `RestServer` and a `GrpcServer`. Both server contexts are created * with `scope` set to `BindingScope.SERVER`. Depending on where a binding * is resolved: * - If the binding is resolved from the RestServer or below, it will be * cached using the RestServer context as the key. * - If the binding is resolved from the GrpcServer or below, it will be * cached using the GrpcServer context as the key. * * The same binding can resolved/shared/cached for all servers, each of which * has its own value for the binding. */ SERVER = "Server", /** * Request scope * * @remarks * The binding provides an request-scoped value within the context hierarchy. * Resolved value for this binding will be cached and shared for the same * request context (denoted by its scope property set to * `BindingScope.REQUEST`). * * The `REQUEST` scope is very useful for controllers, services and artifacts * that want to have a single instance/value for a given request. */ REQUEST = "Request" } /** * Type of the binding source */ export declare enum BindingType { /** * A fixed value */ CONSTANT = "Constant", /** * A function to get the value */ DYNAMIC_VALUE = "DynamicValue", /** * A class to be instantiated as the value */ CLASS = "Class", /** * A provider class with `value()` function to get the value */ PROVIDER = "Provider", /** * A alias to another binding key with optional path */ ALIAS = "Alias" } /** * Binding source for `to` */ export type ConstantBindingSource = { type: BindingType.CONSTANT; value: T; }; /** * Binding source for `toDynamicValue` */ export type DynamicValueBindingSource = { type: BindingType.DYNAMIC_VALUE; value: ValueFactory | DynamicValueProviderClass; }; /** * Binding source for `toClass` */ export type ClassBindingSource = { type: BindingType.CLASS; value: Constructor; }; /** * Binding source for `toProvider` */ export type ProviderBindingSource = { type: BindingType.PROVIDER; value: Constructor>; }; /** * Binding source for `toAlias` */ export type AliasBindingSource = { type: BindingType.ALIAS; value: BindingAddress; }; /** * Source for the binding, including the type and value */ export type BindingSource = ConstantBindingSource | DynamicValueBindingSource | ClassBindingSource | ProviderBindingSource | AliasBindingSource; export type TagMap = MapObject; /** * Binding tag can be a simple name or name/value pairs */ export type BindingTag = TagMap | string; /** * A function as the template to configure bindings */ export type BindingTemplate = (binding: Binding) => void; /** * Information for a binding event */ export type BindingEvent = { /** * Event type */ type: 'changed' | string; /** * Source binding that emits the event */ binding: Readonly>; /** * Operation that triggers the event */ operation: 'tag' | 'scope' | 'value' | string; }; /** * Event listeners for binding events */ export type BindingEventListener = ( /** * Binding event */ event: BindingEvent) => void; /** * A factory function for `toDynamicValue` */ export type ValueFactory = (resolutionCtx: ResolutionContext) => ValueOrPromise; /** * A class with a static `value` method as the factory function for * `toDynamicValue`. * * @example * ```ts * import {inject} from '@loopback/context'; * * export class DynamicGreetingProvider { * static value(@inject('currentUser') user: string) { * return `Hello, ${user}`; * } * } * ``` */ export interface DynamicValueProviderClass extends Constructor, Function { value: (...args: BoundValue[]) => ValueOrPromise; } /** * Check if the factory is a value factory provider class * @param factory - A factory function or a dynamic value provider class */ export declare function isDynamicValueProviderClass(factory: unknown): factory is DynamicValueProviderClass; /** * Binding represents an entry in the `Context`. Each binding has a key and a * corresponding value getter. */ export declare class Binding extends EventEmitter { isLocked: boolean; /** * Key of the binding */ readonly key: string; /** * Map for tag name/value pairs */ readonly tagMap: TagMap; private _scope?; /** * Scope of the binding to control how the value is cached/shared */ get scope(): BindingScope; /** * Type of the binding value getter */ get type(): BindingType | undefined; private _cache; private _getValue?; /** * The original source value received from `to`, `toClass`, `toDynamicValue`, * `toProvider`, or `toAlias`. */ private _source?; get source(): BindingSource | undefined; /** * For bindings bound via `toClass()`, this property contains the constructor * function of the class */ get valueConstructor(): Constructor | undefined; /** * For bindings bound via `toProvider()`, this property contains the * constructor function of the provider class */ get providerConstructor(): Constructor> | undefined; constructor(key: BindingAddress, isLocked?: boolean); /** * Cache the resolved value by the binding scope * @param resolutionCtx - The resolution context * @param result - The calculated value for the binding */ private _cacheValue; /** * Clear the cache */ private _clearCache; /** * Invalidate the binding cache so that its value will be reloaded next time. * This is useful to force reloading a cached value when its configuration or * dependencies are changed. * **WARNING**: The state held in the cached value will be gone. * * @param ctx - Context object */ refresh(ctx: Context): void; /** * This is an internal function optimized for performance. * Users should use `@inject(key)` or `ctx.get(key)` instead. * * Get the value bound to this key. Depending on `isSync`, this * function returns either: * - the bound value * - a promise of the bound value * * Consumers wishing to consume sync values directly should use `isPromiseLike` * to check the type of the returned value to decide how to handle it. * * @example * ``` * const result = binding.getValue(ctx); * if (isPromiseLike(result)) { * result.then(doSomething) * } else { * doSomething(result); * } * ``` * * @param ctx - Context for the resolution * @param session - Optional session for binding and dependency resolution */ getValue(ctx: Context, session?: ResolutionSession): ValueOrPromise; /** * Returns a value or promise for this binding in the given context. The * resolved value can be `undefined` if `optional` is set to `true` in * `options`. * @param ctx - Context for the resolution * @param options - Optional options for binding and dependency resolution */ getValue(ctx: Context, options?: ResolutionOptions): ValueOrPromise; private getValueOrProxy; /** * Locate and validate the resolution context * @param ctx - Current context * @param options - Resolution options */ private getResolutionContext; /** * Lock the binding so that it cannot be rebound */ lock(): this; /** * Emit a `changed` event * @param operation - Operation that makes changes */ private emitChangedEvent; /** * Tag the binding with names or name/value objects. A tag has a name and * an optional value. If not supplied, the tag name is used as the value. * * @param tags - A list of names or name/value objects. Each * parameter can be in one of the following forms: * - string: A tag name without value * - string[]: An array of tag names * - TagMap: A map of tag name/value pairs * * @example * ```ts * // Add a named tag `controller` * binding.tag('controller'); * * // Add two named tags: `controller` and `rest` * binding.tag('controller', 'rest'); * * // Add two tags * // - `controller` (name = 'controller') * // `{name: 'my-controller'}` (name = 'name', value = 'my-controller') * binding.tag('controller', {name: 'my-controller'}); * * ``` */ tag(...tags: BindingTag[]): this; /** * Get an array of tag names */ get tagNames(): string[]; /** * Set the binding scope * @param scope - Binding scope */ inScope(scope: BindingScope): this; /** * Apply default scope to the binding. It only changes the scope if it's not * set yet * @param scope - Default binding scope */ applyDefaultScope(scope: BindingScope): this; /** * Set the `_getValue` function * @param getValue - getValue function */ private _setValueGetter; /** * Bind the key to a constant value. The value must be already available * at binding time, it is not allowed to pass a Promise instance. * * @param value - The bound value. * * @example * * ```ts * ctx.bind('appName').to('CodeHub'); * ``` */ to(value: T): this; /** * Bind the key to a computed (dynamic) value. * * @param factoryFn - The factory function creating the value. * Both sync and async functions are supported. * * @example * * ```ts * // synchronous * ctx.bind('now').toDynamicValue(() => Date.now()); * * // asynchronous * ctx.bind('something').toDynamicValue( * async () => Promise.delay(10).then(doSomething) * ); * ``` */ toDynamicValue(factory: ValueFactory | DynamicValueProviderClass): this; private static valueOrProxy; /** * Bind the key to a value computed by a Provider. * * * @example * * ```ts * export class DateProvider implements Provider { * constructor(@inject('stringDate') private param: String){} * value(): Date { * return new Date(param); * } * } * ``` * * @param provider - The value provider to use. */ toProvider(providerClass: Constructor>): this; /** * Bind the key to an instance of the given class. * * @param ctor - The class constructor to call. Any constructor * arguments must be annotated with `@inject` so that * we can resolve them from the context. */ toClass(ctor: Constructor): this; /** * Bind to a class optionally decorated with `@injectable`. Based on the * introspection of the class, it calls `toClass/toProvider/toDynamicValue` * internally. The current binding key will be preserved (not being overridden * by the key inferred from the class or options). * * This is similar to {@link createBindingFromClass} but applies to an * existing binding. * * @example * * ```ts * @injectable({scope: BindingScope.SINGLETON, tags: {service: 'MyService}}) * class MyService { * // ... * } * * const ctx = new Context(); * ctx.bind('services.MyService').toInjectable(MyService); * ``` * * @param ctor - A class decorated with `@injectable`. */ toInjectable(ctor: DynamicValueProviderClass | Constructor>): this; /** * Bind the key to an alias of another binding * @param keyWithPath - Target binding key with optional path, * such as `servers.RestServer.options#apiExplorer` */ toAlias(keyWithPath: BindingAddress): this; /** * Unlock the binding */ unlock(): this; /** * Apply one or more template functions to set up the binding with scope, * tags, and other attributes as a group. * * @example * ```ts * const serverTemplate = (binding: Binding) => * binding.inScope(BindingScope.SINGLETON).tag('server'); * * const serverBinding = new Binding('servers.RestServer1'); * serverBinding.apply(serverTemplate); * ``` * @param templateFns - One or more functions to configure the binding */ apply(...templateFns: BindingTemplate[]): this; /** * Convert to a plain JSON object */ toJSON(): JSONObject; /** * Inspect the binding to return a json representation of the binding information * @param options - Options to control what information should be included */ inspect(options?: BindingInspectOptions): JSONObject; /** * A static method to create a binding so that we can do * `Binding.bind('foo').to('bar');` as `new Binding('foo').to('bar')` is not * easy to read. * @param key - Binding key */ static bind(key: BindingAddress): Binding; /** * Create a configuration binding for the given key * * @example * ```ts * const configBinding = Binding.configure('servers.RestServer.server1') * .to({port: 3000}); * ``` * * @typeParam V Generic type for the configuration value (not the binding to * be configured) * * @param key - Key for the binding to be configured */ static configure(key: BindingAddress): Binding; /** * The "changed" event is emitted by methods such as `tag`, `inScope`, `to`, * and `toClass`. * * @param eventName The name of the event - always `changed`. * @param listener The listener function to call when the event is emitted. */ on(eventName: 'changed', listener: BindingEventListener): this; on(event: string | symbol, listener: (...args: any[]) => void): this; /** * The "changed" event is emitted by methods such as `tag`, `inScope`, `to`, * and `toClass`. * * @param eventName The name of the event - always `changed`. * @param listener The listener function to call when the event is emitted. */ once(eventName: 'changed', listener: BindingEventListener): this; once(event: string | symbol, listener: (...args: any[]) => void): this; } /** * Options for binding.inspect() */ export interface BindingInspectOptions { /** * The flag to control if injections should be inspected */ includeInjections?: boolean; }