import { EventEmitter } from 'events';
import { Provider } from './provider/provider.js';
import { MapObject, BoundValue, ValueOrPromise, Constructor } from './utils/value-promise.js';
import { BindingAddress, BindingKey } from './binding/binding-key.js';
import { MetadataMap } from 'metarize';
import { JSONObject } from './utils/json-types.js';
import { Debugger } from './utils/debug.js';

/**
 * A function that filters bindings. It returns `true` to select a given
 * binding.
 *
 * @remarks
 * Originally, we allowed filters to be tied with a single value type.
 * This actually does not make much sense - the filter function is typically
 * invoked on all bindings to find those ones matching the given criteria.
 * Filters must be prepared to handle bindings of any value type. We learned
 * about this problem after enabling TypeScript's `strictFunctionTypes` check.
 * This aspect is resolved by typing the input argument as `Binding<unknown>`.
 *
 * Ideally, `BindingFilter` should be declared as a type guard as follows:
 * ```ts
 * export type BindingFilterGuard<ValueType = unknown> = (
 *   binding: Readonly<Binding<unknown>>,
 * ) => binding is Readonly<Binding<ValueType>>;
 * ```
 *
 * But TypeScript treats the following types as incompatible and does not accept
 * type 1 for type 2.
 *
 * 1. `(binding: Readonly<Binding<unknown>>) => boolean`
 * 2. `(binding: Readonly<Binding<unknown>>) => binding is Readonly<Binding<ValueType>>`
 *
 * If we described BindingFilter as a type-guard, then all filter implementations
 * would have to be explicitly typed as type-guards too, which would make it
 * tedious to write quick filter functions like `b => b.key.startsWith('services')`.
 *
 * To keep things simple and easy to use, we use `boolean` as the return type
 * of a binding filter function.
 */
type BindingFilter = (binding: Readonly<Binding<unknown>>) => boolean;
/**
 * Select binding(s) by key or a filter function
 */
type BindingSelector<ValueType = unknown> = BindingAddress<ValueType> | BindingFilter;
/**
 * Type guard for binding address
 * @param bindingSelector - Binding key or filter function
 */
declare function isBindingAddress(bindingSelector: BindingSelector): bindingSelector is BindingAddress;
/**
 * Binding filter function that holds a binding tag pattern. `Context.find()`
 * uses the `bindingTagPattern` to optimize the matching of bindings by tag to
 * avoid expensive check for all bindings.
 */
interface BindingTagFilter extends BindingFilter {
    /**
     * A special property on the filter function to provide access to the binding
     * tag pattern which can be utilized to optimize the matching of bindings by
     * tag in a context.
     */
    bindingTagPattern: BindingTag | RegExp;
}
/**
 * Type guard for BindingTagFilter
 * @param filter - A BindingFilter function
 */
declare function isBindingTagFilter(filter?: BindingFilter): filter is BindingTagFilter;
/**
 * A function to check if a given tag value is matched for `filterByTag`
 */
type TagValueMatcher = (tagValue: unknown, tagName: string, tagMap: MapObject<unknown>) => boolean;
/**
 * A symbol that can be used to match binding tags by name regardless of the
 * value.
 *
 * @example
 *
 * The following code matches bindings with tag `{controller: 'A'}` or
 * `{controller: 'controller'}`. But if the tag name 'controller' does not
 * exist for a binding, the binding will NOT be included.
 *
 * ```ts
 * ctx.findByTag({controller: ANY_TAG_VALUE})
 * ```
 */
declare const ANY_TAG_VALUE: TagValueMatcher;
/**
 * Create a tag value matcher function that returns `true` if the target tag
 * value equals to the item value or is an array that includes the item value.
 * @param itemValues - A list of tag item value
 */
declare function includesTagValue(...itemValues: unknown[]): TagValueMatcher;
/**
 * Create a binding filter for the tag pattern
 * @param tagPattern - Binding tag name, regexp, or object
 */
declare function filterByTag(tagPattern: BindingTag | RegExp): BindingTagFilter;
/**
 * Create a binding filter from key pattern
 * @param keyPattern - Binding key/wildcard, regexp, or a filter function
 */
declare function filterByKey(keyPattern?: string | RegExp | BindingFilter): BindingFilter;

/**
 * Compare function to sort an array of bindings.
 * It is used by `Array.prototype.sort()`.
 *
 * @example
 * ```ts
 * const compareByKey: BindingComparator = (a, b) => a.key.localeCompare(b.key);
 * ```
 */
interface BindingComparator {
    /**
     * Compare two bindings
     * @param bindingA - First binding
     * @param bindingB - Second binding
     * @returns A number to determine order of bindingA and bindingB
     * - 0 leaves bindingA and bindingB unchanged
     * - <0 bindingA comes before bindingB
     * - >0 bindingA comes after bindingB
     */
    (bindingA: Readonly<Binding<unknown>>, bindingB: Readonly<Binding<unknown>>): number;
}
/**
 * Creates a binding compare function to sort bindings by tagged phase name.
 *
 * @remarks
 * Two bindings are compared as follows:
 *
 * 1. Get values for the given tag as `phase` for bindings, if the tag is not
 * present, default `phase` to `''`.
 * 2. If both bindings have `phase` value in `orderOfPhases`, honor the order
 * specified by `orderOfPhases`.
 * 3. If a binding's `phase` does not exist in `orderOfPhases`, it comes before
 * the one with `phase` exists in `orderOfPhases`.
 * 4. If both bindings have `phase` value outside of `orderOfPhases`, they are
 * ordered by phase names alphabetically and symbol values come before string
 * values.
 *
 * @param phaseTagName - Name of the binding tag for phase
 * @param orderOfPhases - An array of phase names as the predefined order
 */
declare function compareBindingsByTag(phaseTagName?: string, orderOfPhases?: (string | symbol)[]): BindingComparator;
/**
 * Compare two values by the predefined order
 *
 * @remarks
 *
 * The comparison is performed as follows:
 *
 * 1. If both values are included in `order`, they are sorted by their indexes in
 * `order`.
 * 2. The value included in `order` comes after the value not included in `order`.
 * 3. If neither values are included in `order`, they are sorted:
 *   - symbol values come before string values
 *   - alphabetical order for two symbols or two strings
 *
 * @param a - First value
 * @param b - Second value
 * @param order - An array of values as the predefined order
 */
declare function compareByOrder(a: string | symbol | undefined | null, b: string | symbol | undefined | null, order?: (string | symbol)[]): number;
/**
 * Sort bindings by phase names denoted by a tag and the predefined order
 *
 * @param bindings - An array of bindings
 * @param phaseTagName - Tag name for phase, for example, we can use the value
 * `'a'` of tag `order` as the phase name for `binding.tag({order: 'a'})`.
 *
 * @param orderOfPhases - An array of phase names as the predefined order
 */
declare function sortBindingsByPhase<T = unknown>(bindings: Readonly<Binding<T>>[], phaseTagName?: string, orderOfPhases?: (string | symbol)[]): Readonly<Binding<T>>[];

/**
 * A function to provide resolution of injected values.
 *
 * @example
 * ```ts
 * const resolver: ResolverFunction = (ctx, injection, session) {
 *   return session.currentBinding?.key;
 * }
 * ```
 */
type ResolverFunction = (ctx: Context, injection: Readonly<Injection>, session: ResolutionSession) => ValueOrPromise<BoundValue>;
/**
 * An object to provide metadata for `@inject`
 */
interface InjectionMetadata extends Omit<ResolutionOptions, 'session'> {
    /**
     * Name of the decorator function, such as `@inject` or `@inject.setter`.
     * It's usually set by the decorator implementation.
     */
    decorator?: string;
    /**
     * Optional comparator for matched bindings
     */
    bindingComparator?: BindingComparator;
    /**
     * Other attributes
     */
    [attribute: string]: BoundValue;
}
/**
 * Descriptor for an injection point
 */
interface Injection<ValueType = BoundValue> {
    target: object;
    member?: string;
    methodDescriptorOrParameterIndex?: TypedPropertyDescriptor<ValueType> | number;
    bindingSelector: BindingSelector<ValueType>;
    metadata: InjectionMetadata;
    resolve?: ResolverFunction;
}
/**
 * The function injected by `@inject.getter(bindingSelector)`. It can be used
 * to fetch bound value(s) from the underlying binding(s). The return value will
 * be an array if the `bindingSelector` is a `BindingFilter` function.
 */
type Getter<T> = () => Promise<T>;
declare namespace Getter {
    /**
     * Convert a value into a Getter returning that value.
     * @param value
     */
    function fromValue<T>(value: T): Getter<T>;
}
/**
 * The function injected by `@inject.setter(bindingKey)`. It sets the underlying
 * binding to a constant value using `binding.to(value)`.
 *
 * @example
 *
 * ```ts
 * setterFn('my-value');
 * ```
 * @param value - The value for the underlying binding
 */
type Setter<T> = (value: T) => void;
/**
 * Metadata for `@inject.binding`
 */
interface InjectBindingMetadata extends InjectionMetadata {
    /**
     * Controls how the underlying binding is resolved/created
     */
    bindingCreation?: BindingCreationPolicy;
}
/**
 * A decorator to annotate method arguments for automatic injection
 * by the IoC container.
 *
 * @example
 * Usage - Typescript:
 *
 * ```ts
 * class InfoController {
 *   @inject('authentication.user') public userName: string;
 *
 *   constructor(@inject('application.name') public appName: string) {
 *   }
 *   // ...
 * }
 * ```
 *
 * Usage - JavaScript:
 *
 *  - TODO(bajtos)
 *
 * @param bindingSelector - What binding to use in order to resolve the value of the
 * decorated constructor parameter or property.
 * @param metadata - Optional metadata to help the injection
 * @param resolve - Optional function to resolve the injection
 *
 */
declare function inject(bindingSelector: BindingSelector, metadata?: InjectionMetadata, resolve?: ResolverFunction): (target: object, member: string | undefined, methodDescriptorOrParameterIndex?: TypedPropertyDescriptor<BoundValue> | number) => void;
declare namespace inject {
    /**
     * Inject a function for getting the actual bound value.
     *
     * This is useful when implementing Actions, where
     * the action is instantiated for Sequence constructor, but some
     * of action's dependencies become bound only after other actions
     * have been executed by the sequence.
     *
     * See also `Getter<T>`.
     *
     * @param bindingSelector - The binding key or filter we want to eventually get
     * value(s) from.
     * @param metadata - Optional metadata to help the injection
     */
    const getter: (bindingSelector: BindingSelector<unknown>, metadata?: InjectionMetadata) => (target: object, member: string | undefined, methodDescriptorOrParameterIndex?: TypedPropertyDescriptor<BoundValue> | number) => void;
    /**
     * Inject a function for setting (binding) the given key to a given
     * value. (Only static/constant values are supported, it's not possible
     * to bind a key to a class or a provider.)
     *
     * This is useful e.g. when implementing Actions that are contributing
     * new Elements.
     *
     * See also `Setter<T>`.
     *
     * @param bindingKey - The key of the value we want to set.
     * @param metadata - Optional metadata to help the injection
     */
    const setter: (bindingKey: BindingAddress, metadata?: InjectBindingMetadata) => (target: object, member: string | undefined, methodDescriptorOrParameterIndex?: TypedPropertyDescriptor<BoundValue> | number) => void;
    /**
     * Inject the binding object for the given key. This is useful if a binding
     * needs to be set up beyond just a constant value allowed by
     * `@inject.setter`. The injected binding is found or created based on the
     * `metadata.bindingCreation` option. See `BindingCreationPolicy` for more
     * details.
     *
     * @example
     *
     * ```ts
     * class MyAuthAction {
     *   @inject.binding('current-user', {
     *     bindingCreation: BindingCreationPolicy.ALWAYS_CREATE,
     *   })
     *   private userBinding: Binding<UserProfile>;
     *
     *   async authenticate() {
     *     this.userBinding.toDynamicValue(() => {...});
     *   }
     * }
     * ```
     *
     * @param bindingKey - Binding key
     * @param metadata - Metadata for the injection
     */
    const binding: (bindingKey?: string | BindingKey<unknown>, metadata?: InjectBindingMetadata) => (target: object, member: string | undefined, methodDescriptorOrParameterIndex?: TypedPropertyDescriptor<BoundValue> | number) => void;
    /**
     * Inject an array of values by a tag pattern string or regexp
     *
     * @example
     * ```ts
     * class AuthenticationManager {
     *   constructor(
     *     @inject.tag('authentication.strategy') public strategies: Strategy[],
     *   ) {}
     * }
     * ```
     * @param bindingTag - Tag name, regex or object
     * @param metadata - Optional metadata to help the injection
     */
    const tag: (bindingTag: BindingTag | RegExp, metadata?: InjectionMetadata) => (target: object, member: string | undefined, methodDescriptorOrParameterIndex?: TypedPropertyDescriptor<BoundValue> | number) => void;
    /**
     * Inject matching bound values by the filter function
     *
     * @example
     * ```ts
     * class MyControllerWithView {
     *   @inject.view(filterByTag('foo'))
     *   view: ContextView<string[]>;
     * }
     * ```
     * @param bindingFilter - A binding filter function
     * @param metadata
     */
    const view: (bindingFilter: BindingFilter, metadata?: InjectionMetadata) => (target: object, member: string | undefined, methodDescriptorOrParameterIndex?: TypedPropertyDescriptor<BoundValue> | number) => void;
    /**
     * Inject the context object.
     *
     * @example
     * ```ts
     * class MyProvider {
     *  constructor(@inject.context() private ctx: Context) {}
     * }
     * ```
     */
    const context: () => (target: object, member: string | undefined, methodDescriptorOrParameterIndex?: TypedPropertyDescriptor<BoundValue> | number) => void;
}
/**
 * Assert the target type inspected from TypeScript for injection to be the
 * expected type. If the types don't match, an error is thrown.
 * @param injection - Injection information
 * @param expectedType - Expected type
 * @param expectedTypeName - Name of the expected type to be used in the error
 * @returns The name of the target
 */
declare function assertTargetType(injection: Readonly<Injection>, expectedType: Constructor<unknown>, expectedTypeName?: string): string;
/**
 * Return an array of injection objects for parameters
 * @param target - The target class for constructor or static methods,
 * or the prototype for instance methods
 * @param method - Method name, undefined for constructor
 */
declare function describeInjectedArguments(target: object, method?: string): Readonly<Injection>[];
/**
 * Inspect the target type for the injection to find out the corresponding
 * JavaScript type
 * @param injection - Injection information
 */
declare function inspectTargetType(injection: Readonly<Injection>): Function | undefined;
/**
 * Return a map of injection objects for properties
 * @param target - The target class for static properties or
 * prototype for instance properties.
 */
declare function describeInjectedProperties(target: object): MetadataMap<Readonly<Injection>>;
/**
 * Inspect injections for a binding created with `toClass` or `toProvider`
 * @param binding - Binding object
 */
declare function inspectInjections(binding: Readonly<Binding<unknown>>): JSONObject;
/**
 * Check if the given class has `@inject` or other decorations that map to
 * `@inject`.
 *
 * @param cls - Class with possible `@inject` decorations
 */
declare function hasInjections(cls: Constructor<unknown>): boolean;

/**
 * A function to be executed with the resolution session
 */
type ResolutionAction = (session: ResolutionSession) => ValueOrPromise<BoundValue>;
/**
 * Wrapper for bindings tracked by resolution sessions
 */
interface BindingElement {
    type: 'binding';
    value: Readonly<Binding>;
}
/**
 * Wrapper for injections tracked by resolution sessions
 */
interface InjectionElement {
    type: 'injection';
    value: Readonly<Injection>;
}
interface InjectionDescriptor {
    targetName: string;
    bindingSelector: BindingSelector;
    metadata: InjectionMetadata;
}
/**
 * Binding or injection elements tracked by resolution sessions
 */
type ResolutionElement = BindingElement | InjectionElement;
/**
 * Object to keep states for a session to resolve bindings and their
 * dependencies within a context
 */
declare class ResolutionSession {
    /**
     * A stack of bindings for the current resolution session. It's used to track
     * the path of dependency resolution and detect circular dependencies.
     */
    readonly stack: ResolutionElement[];
    /**
     * Fork the current session so that a new one with the same stack can be used
     * in parallel or future resolutions, such as multiple method arguments,
     * multiple properties, or a getter function
     * @param session - The current session
     */
    static fork(session?: ResolutionSession): ResolutionSession | undefined;
    /**
     * Run the given action with the given binding and session
     * @param action - A function to do some work with the resolution session
     * @param binding - The current binding
     * @param session - The current resolution session
     */
    static runWithBinding(action: ResolutionAction, binding: Readonly<Binding>, session?: ResolutionSession): any;
    /**
     * Run the given action with the given injection and session
     * @param action - A function to do some work with the resolution session
     * @param binding - The current injection
     * @param session - The current resolution session
     */
    static runWithInjection(action: ResolutionAction, injection: Readonly<Injection>, session?: ResolutionSession): any;
    /**
     * Describe the injection for debugging purpose
     * @param injection - Injection object
     */
    static describeInjection(injection: Readonly<Injection>): InjectionDescriptor;
    /**
     * Push the injection onto the session
     * @param injection - Injection The current injection
     */
    pushInjection(injection: Readonly<Injection>): void;
    /**
     * Pop the last injection
     */
    popInjection(): Readonly<Injection<any>>;
    /**
     * Getter for the current injection
     */
    get currentInjection(): Readonly<Injection> | undefined;
    /**
     * Getter for the current binding
     */
    get currentBinding(): Readonly<Binding> | undefined;
    /**
     * Enter the resolution of the given binding. If
     * @param binding - Binding
     */
    pushBinding(binding: Readonly<Binding>): void;
    /**
     * Exit the resolution of a binding
     */
    popBinding(): Readonly<Binding>;
    /**
     * Getter for bindings on the stack
     */
    get bindingStack(): Readonly<Binding>[];
    /**
     * Getter for injections on the stack
     */
    get injectionStack(): Readonly<Injection>[];
    /**
     * Get the binding path as `bindingA --> bindingB --> bindingC`.
     */
    getBindingPath(): string;
    /**
     * Get the injection path as `injectionA --> injectionB --> injectionC`.
     */
    getInjectionPath(): string;
    /**
     * Get the resolution path including bindings and injections, for example:
     * `bindingA --> @ClassA[0] --> bindingB --> @ClassB.prototype.prop1
     * --> bindingC`.
     */
    getResolutionPath(): string;
    toString(): string;
}
/**
 * Options for binding/dependency resolution
 */
interface ResolutionOptions {
    /**
     * A session to track bindings and injections
     */
    session?: ResolutionSession;
    /**
     * A boolean flag to indicate if the dependency is optional. If it's set to
     * `true` and the binding is not bound in a context, the resolution
     * will return `undefined` instead of throwing an error.
     */
    optional?: boolean;
    /**
     * A boolean flag to control if a proxy should be created to apply
     * interceptors for the resolved value. It's only honored for bindings backed
     * by a class.
     */
    asProxyWithInterceptors?: boolean;
}
/**
 * Resolution options or session
 */
type ResolutionOptionsOrSession = ResolutionOptions | ResolutionSession;
/**
 * Normalize ResolutionOptionsOrSession to ResolutionOptions
 * @param optionsOrSession - resolution options or session
 */
declare function asResolutionOptions(optionsOrSession?: ResolutionOptionsOrSession): ResolutionOptions;
/**
 * Contextual metadata for resolution
 */
interface ResolutionContext<T = unknown> {
    /**
     * The context for resolution
     */
    readonly context: Context;
    /**
     * The binding to be resolved
     */
    readonly binding: Readonly<Binding<T>>;
    /**
     * The options used for resolution
     */
    readonly options: ResolutionOptions;
}
/**
 * Error for context binding resolutions and dependency injections
 */
declare class ResolutionError extends Error {
    readonly resolutionCtx: Partial<ResolutionContext>;
    constructor(message: string, resolutionCtx: Partial<ResolutionContext>);
    private static buildDetails;
    /**
     * Build the error message for the resolution to include more contextual data
     * @param reason - Cause of the error
     * @param resolutionCtx - Resolution context
     */
    private static buildMessage;
    private static describeResolutionContext;
}

/**
 * Resolver for configuration of bindings. It's responsible for finding
 * corresponding configuration for a given binding key.
 *
 * By default, `undefined` is expected if no configuration is provided. The
 * behavior can be overridden by setting `optional` to `false` in resolution
 * options.
 */
interface ConfigurationResolver {
    /**
     * Resolve config for the binding key
     *
     * @param key - Binding key
     * @param propertyPath - Property path for the option. For example, `x.y`
     * requests for `<config>.x.y`. If not set, the `config` object will be
     * returned.
     * @param resolutionOptions - Options for the resolution.
     * - optional: if not set or set to `true`, `undefined` will be returned if
     * no corresponding value is found. Otherwise, an error will be thrown.
     */
    getConfigAsValueOrPromise<ConfigValueType>(key: BindingAddress<unknown>, propertyPath?: string, resolutionOptions?: ResolutionOptions): ValueOrPromise<ConfigValueType | undefined>;
}
/**
 * Resolver for configurations of bindings
 */
declare class DefaultConfigurationResolver implements ConfigurationResolver {
    readonly context: Context;
    constructor(context: Context);
    getConfigAsValueOrPromise<ConfigValueType>(key: BindingAddress<unknown>, propertyPath?: string, resolutionOptions?: ResolutionOptions): ValueOrPromise<ConfigValueType | undefined>;
}
/**
 * Create binding key for configuration of the binding
 * @param key - Binding key for the target binding
 * @param propertyPath - Property path for the configuration
 */
declare function configBindingKeyFor<ConfigValueType = unknown>(key: BindingAddress, propertyPath?: string): BindingKey<ConfigValueType>;

/**
 * Events emitted by a context
 */
type ContextEvent = {
    /**
     * Source context that emits the event
     */
    context: Context;
    /**
     * Binding that is being added/removed/updated
     */
    binding: Readonly<Binding<unknown>>;
    /**
     * Event type
     */
    type: string;
};
/**
 * Synchronous listener for context events
 */
type ContextEventListener = (event: ContextEvent) => void;

/**
 * Context event types. We support `bind` and `unbind` for now but
 * keep it open for new types
 */
type ContextEventType = 'bind' | 'unbind' | string;
/**
 * Listen on `bind`, `unbind`, or other events
 * @param eventType - Context event type
 * @param binding - The binding as event source
 * @param context - Context object for the binding event
 */
type ContextObserverFn = (eventType: ContextEventType, binding: Readonly<Binding<unknown>>, context: Context) => ValueOrPromise<void>;
/**
 * Observers of context bind/unbind events
 */
interface ContextObserver {
    /**
     * An optional filter function to match bindings. If not present, the listener
     * will be notified of all binding events.
     */
    filter?: BindingFilter;
    /**
     * Listen on `bind`, `unbind`, or other events
     * @param eventType - Context event type
     * @param binding - The binding as event source
     */
    observe: ContextObserverFn;
}
/**
 * Context event observer type - An instance of `ContextObserver` or a function
 */
type ContextEventObserver = ContextObserver | ContextObserverFn;

/**
 * Subscription of context events. It's modeled after
 * https://github.com/tc39/proposal-observable.
 */
interface Subscription {
    /**
     * unsubscribe
     */
    unsubscribe(): void;
    /**
     * Is the subscription closed?
     */
    closed: boolean;
}
/**
 * Event data for observer notifications
 */
interface Notification extends ContextEvent {
    /**
     * A snapshot of observers when the original event is emitted
     */
    observers: Set<ContextEventObserver>;
}
/**
 * Manager for context observer subscriptions
 */
declare class ContextSubscriptionManager extends EventEmitter {
    protected readonly context: Context;
    /**
     * A listener to watch parent context events
     */
    protected _parentContextEventListener?: ContextEventListener;
    /**
     * A list of registered context observers. The Set will be created when the
     * first observer is added.
     */
    protected _observers: Set<ContextEventObserver> | undefined;
    /**
     * Internal counter for pending notification events which are yet to be
     * processed by observers.
     */
    private pendingNotifications;
    /**
     * Queue for background notifications for observers
     */
    private notificationQueue;
    constructor(context: Context);
    /**
     * @internal
     */
    get parentContextEventListener(): ContextEventListener | undefined;
    /**
     * @internal
     */
    get observers(): Set<ContextEventObserver> | undefined;
    /**
     * Wrap the debug statement so that it always print out the context name
     * as the prefix
     * @param args - Arguments for the debug
     */
    private _debug;
    /**
     * Set up an internal listener to notify registered observers asynchronously
     * upon `bind` and `unbind` events. This method will be called lazily when
     * the first observer is added.
     */
    private setupEventHandlersIfNeeded;
    private handleParentEvent;
    /**
     * A strongly-typed method to emit context events
     * @param type Event type
     * @param event Context event
     */
    private emitEvent;
    /**
     * Emit an `error` event
     * @param err Error
     */
    private emitError;
    /**
     * Start a background task to listen on context events and notify observers
     */
    private startNotificationTask;
    /**
     * Publish an event to the registered observers. Please note the
     * notification is queued and performed asynchronously so that we allow fluent
     * APIs such as `ctx.bind('key').to(...).tag(...);` and give observers the
     * fully populated binding.
     *
     * @param event - Context event
     * @param observers - Current set of context observers
     */
    protected notifyObservers(event: ContextEvent, observers?: Set<ContextEventObserver> | undefined): Promise<void>;
    /**
     * Process notification events as they arrive on the queue
     */
    private processNotifications;
    /**
     * Listen on given event types and emit `notification` event. This method
     * merge multiple event types into one for notification.
     * @param eventTypes - Context event types
     */
    private setupNotification;
    /**
     * Wait until observers are notified for all of currently pending notification
     * events.
     *
     * This method is for test only to perform assertions after observers are
     * notified for relevant events.
     */
    waitUntilPendingNotificationsDone(timeout?: number): Promise<void>;
    /**
     * Add a context event observer to the context
     * @param observer - Context observer instance or function
     */
    subscribe(observer: ContextEventObserver): Subscription;
    /**
     * Remove the context event observer from the context
     * @param observer - Context event observer
     */
    unsubscribe(observer: ContextEventObserver): boolean;
    /**
     * Check if an observer is subscribed to this context
     * @param observer - Context observer
     */
    isSubscribed(observer: ContextObserver): boolean;
    /**
     * Handle errors caught during the notification of observers
     * @param err - Error
     */
    private handleNotificationError;
    /**
     * Close the context: clear observers, stop notifications, and remove event
     * listeners from its parent context.
     *
     * @remarks
     * This method MUST be called to avoid memory leaks once a context object is
     * no longer needed and should be recycled. An example is the `RequestContext`,
     * which is created per request.
     */
    close(): void;
}

/**
 * Indexer for context bindings by tag
 */
declare class ContextTagIndexer {
    protected readonly context: Context;
    /**
     * Index for bindings by tag names
     */
    readonly bindingsIndexedByTag: Map<string, Set<Readonly<Binding<unknown>>>>;
    /**
     * A listener for binding events
     */
    private bindingEventListener;
    /**
     * A listener to maintain tag index for bindings
     */
    private tagIndexListener;
    constructor(context: Context);
    /**
     * Set up context/binding listeners and refresh index for bindings by tag
     */
    private setupTagIndexForBindings;
    /**
     * Remove tag index for the given binding
     * @param binding - Binding object
     */
    private removeTagIndexForBinding;
    /**
     * Update tag index for the given binding
     * @param binding - Binding object
     */
    private updateTagIndexForBinding;
    /**
     * Find bindings by tag leveraging indexes
     * @param tag - Tag name pattern or name/value pairs
     */
    findByTagIndex<ValueType = BoundValue>(tag: BindingTag | RegExp): Readonly<Binding<ValueType>>[];
    close(): void;
}

/**
 * An event emitted by a `ContextView`
 */
interface ContextViewEvent<T> extends ContextEvent {
    /**
     * Optional cached value for an `unbind` event
     */
    cachedValue?: T;
}
/**
 * `ContextView` provides a view for a given context chain to maintain a live
 * list of matching bindings and their resolved values within the context
 * hierarchy.
 *
 * This class is the key utility to implement dynamic extensions for extension
 * points. For example, the RestServer can react to `controller` bindings even
 * they are added/removed/updated after the application starts.
 *
 * `ContextView` is an event emitter that emits the following events:
 * - 'bind': when a binding is added to the view
 * - 'unbind': when a binding is removed from the view
 * - 'close': when the view is closed (stopped observing context events)
 * - 'refresh': when the view is refreshed as bindings are added/removed
 * - 'resolve': when the cached values are resolved and updated
 */
declare class ContextView<T = unknown> extends EventEmitter implements ContextObserver {
    readonly context: Context;
    readonly filter: BindingFilter;
    readonly comparator?: BindingComparator | undefined;
    private resolutionOptions?;
    /**
     * An array of cached bindings that matches the binding filter
     */
    protected _cachedBindings: Readonly<Binding<T>>[] | undefined;
    /**
     * A map of cached values by binding
     */
    protected _cachedValues: Map<Readonly<Binding<T>>, T> | undefined;
    private _subscription;
    /**
     * Create a context view
     * @param context - Context object to watch
     * @param filter - Binding filter to match bindings of interest
     * @param comparator - Comparator to sort the matched bindings
     */
    constructor(context: Context, filter: BindingFilter, comparator?: BindingComparator | undefined, resolutionOptions?: Omit<ResolutionOptions, "session"> | undefined);
    /**
     * Update the cached values keyed by binding
     * @param values - An array of resolved values
     */
    private updateCachedValues;
    /**
     * Get an array of cached values
     */
    private getCachedValues;
    /**
     * Start listening events from the context
     */
    open(): Subscription | undefined;
    /**
     * Stop listening events from the context
     */
    close(): void;
    /**
     * Get the list of matched bindings. If they are not cached, it tries to find
     * them from the context.
     */
    get bindings(): Readonly<Binding<T>>[];
    /**
     * Find matching bindings and refresh the cache
     */
    protected findBindings(): Readonly<Binding<T>>[];
    /**
     * Listen on `bind` or `unbind` and invalidate the cache
     */
    observe(event: ContextEventType, binding: Readonly<Binding<unknown>>, context: Context): void;
    /**
     * Refresh the view by invalidating its cache
     */
    refresh(): void;
    /**
     * Resolve values for the matching bindings
     * @param session - Resolution session
     */
    resolve(session?: ResolutionOptionsOrSession): ValueOrPromise<T[]>;
    /**
     * Get the list of resolved values. If they are not cached, it tries to find
     * and resolve them.
     */
    values(session?: ResolutionOptionsOrSession): Promise<T[]>;
    /**
     * As a `Getter` function
     */
    asGetter(session?: ResolutionOptionsOrSession): Getter<T[]>;
    /**
     * Get the single value
     */
    singleValue(session?: ResolutionOptionsOrSession): Promise<T | undefined>;
    /**
     * The "bind" event is emitted when a new binding is added to the view.
     *
     * @param eventName The name of the event - always `bind`.
     * @param listener The listener function to call when the event is emitted.
     */
    on(eventName: 'bind', listener: <V>(event: ContextViewEvent<V>) => void): this;
    /**
     * The "unbind" event is emitted a new binding is removed from the view.
     *
     * @param eventName The name of the event - always `unbind`.
     * @param listener The listener function to call when the event is emitted.
     */
    on(eventName: 'unbind', listener: <V>(event: ContextViewEvent<V> & {
        cachedValue?: V;
    }) => void): this;
    /**
     * The "refresh" event is emitted when the view is refreshed as bindings are
     * added/removed.
     *
     * @param eventName The name of the event - always `refresh`.
     * @param listener The listener function to call when the event is emitted.
     */
    on(eventName: 'refresh', listener: () => void): this;
    /**
     * The "resolve" event is emitted when the cached values are resolved and
     * updated.
     *
     * @param eventName The name of the event - always `refresh`.
     * @param listener The listener function to call when the event is emitted.
     */
    on(eventName: 'refresh', listener: <V>(result: V[]) => void): this;
    /**
     * The "close" event is emitted when the view is closed (stopped observing
     * context events)
     *
     * @param eventName The name of the event - always `close`.
     * @param listener The listener function to call when the event is emitted.
     */
    on(eventName: 'close', listener: () => void): this;
    on(event: string | symbol, listener: (...args: any[]) => void): this;
    /**
     * The "bind" event is emitted when a new binding is added to the view.
     *
     * @param eventName The name of the event - always `bind`.
     * @param listener The listener function to call when the event is emitted.
     */
    once(eventName: 'bind', listener: <V>(event: ContextViewEvent<V>) => void): this;
    /**
     * The "unbind" event is emitted a new binding is removed from the view.
     *
     * @param eventName The name of the event - always `unbind`.
     * @param listener The listener function to call when the event is emitted.
     */
    once(eventName: 'unbind', listener: <V>(event: ContextViewEvent<V> & {
        cachedValue?: V;
    }) => void): this;
    /**
     * The "refresh" event is emitted when the view is refreshed as bindings are
     * added/removed.
     *
     * @param eventName The name of the event - always `refresh`.
     * @param listener The listener function to call when the event is emitted.
     */
    once(eventName: 'refresh', listener: () => void): this;
    /**
     * The "resolve" event is emitted when the cached values are resolved and
     * updated.
     *
     * @param eventName The name of the event - always `refresh`.
     * @param listener The listener function to call when the event is emitted.
     */
    once(eventName: 'refresh', listener: <V>(result: V[]) => void): this;
    /**
     * The "close" event is emitted when the view is closed (stopped observing
     * context events)
     *
     * @param eventName The name of the event - always `close`.
     * @param listener The listener function to call when the event is emitted.
     */
    once(eventName: 'close', listener: () => void): this;
    once(event: string | symbol, listener: (...args: any[]) => void): this;
}
/**
 * Create a context view as a getter with the given filter
 * @param ctx - Context object
 * @param bindingFilter - A function to match bindings
 * @param session - Resolution session
 */
declare function createViewGetter<T = unknown>(ctx: Context, bindingFilter: BindingFilter, session?: ResolutionSession): Getter<T[]>;
/**
 * Create a context view as a getter with the given filter and sort matched
 * bindings by the comparator.
 * @param ctx - Context object
 * @param bindingFilter - A function to match bindings
 * @param bindingComparator - A function to compare two bindings
 * @param session - Resolution session
 */
declare function createViewGetter<T = unknown>(ctx: Context, bindingFilter: BindingFilter, bindingComparator?: BindingComparator, session?: ResolutionOptionsOrSession): Getter<T[]>;

/**
 * Context provides an implementation of Inversion of Control (IoC) container
 */
declare class Context extends EventEmitter {
    /**
     * Name of the context
     */
    readonly name: string;
    /**
     * Key to binding map as the internal registry
     */
    protected readonly registry: Map<string, Binding>;
    /**
     * Indexer for bindings by tag
     */
    protected readonly tagIndexer: ContextTagIndexer;
    /**
     * Manager for observer subscriptions
     */
    readonly subscriptionManager: ContextSubscriptionManager;
    /**
     * Parent context
     */
    protected _parent?: Context;
    /**
     * Configuration resolver
     */
    protected configResolver: ConfigurationResolver;
    /**
     * A logger function which can be overridden by subclasses.
     *
     * @example
     * ```ts
     * import createDebugger from '../utils/debug.js';
     * const debugger = createDebugger('contexify:application');
     * export class Application extends Context {
     *   super('application');
     *   this._debug = debugger;
     * }
     * ```
     */
    protected _debug: Debugger;
    /**
     * Scope for binding resolution
     */
    scope: BindingScope;
    /**
     * Create a new context.
     *
     * @example
     * ```ts
     * // Create a new root context, let the framework to create a unique name
     * const rootCtx = new Context();
     *
     * // Create a new child context inheriting bindings from `rootCtx`
     * const childCtx = new Context(rootCtx);
     *
     * // Create another root context called "application"
     * const appCtx = new Context('application');
     *
     * // Create a new child context called "request" and inheriting bindings
     * // from `appCtx`
     * const reqCtx = new Context(appCtx, 'request');
     * ```
     * @param _parent - The optional parent context
     * @param name - Name of the context. If not provided, a unique identifier
     * will be generated as the name.
     */
    constructor(_parent?: Context | string, name?: string);
    /**
     * Get the debug namespace for the context class. Subclasses can override
     * this method to supply its own namespace.
     *
     * @example
     * ```ts
     * export class Application extends Context {
     *   super('application');
     * }
     *
     * protected getDebugNamespace() {
     *   return 'contexify:application';
     * }
     * ```
     */
    protected getDebugNamespace(): string;
    private generateName;
    /**
     * @internal
     * Getter for ContextSubscriptionManager
     */
    get parent(): Context | undefined;
    /**
     * Wrap the debug statement so that it always print out the context name
     * as the prefix
     * @param args - Arguments for the debug
     */
    protected debug(...args: unknown[]): void;
    /**
     * A strongly-typed method to emit context events
     * @param type Event type
     * @param event Context event
     */
    emitEvent<T extends ContextEvent>(type: string, event: T): void;
    /**
     * Emit an `error` event
     * @param err Error
     */
    emitError(err: unknown): void;
    /**
     * Create a binding with the given key in the context. If a locked binding
     * already exists with the same key, an error will be thrown.
     *
     * @param key - Binding key
     */
    bind<ValueType = BoundValue>(key: BindingAddress<ValueType>): Binding<ValueType>;
    /**
     * Add a binding to the context. If a locked binding already exists with the
     * same key, an error will be thrown.
     * @param binding - The configured binding to be added
     */
    add(binding: Binding<unknown>): this;
    /**
     * Create a corresponding binding for configuration of the target bound by
     * the given key in the context.
     *
     * For example, `ctx.configure('controllers.MyController').to({x: 1})` will
     * create binding `controllers.MyController:$config` with value `{x: 1}`.
     *
     * @param key - The key for the binding to be configured
     */
    configure<ConfigValueType = BoundValue>(key?: BindingAddress): Binding<ConfigValueType>;
    /**
     * Get the value or promise of configuration for a given binding by key
     *
     * @param key - Binding key
     * @param propertyPath - Property path for the option. For example, `x.y`
     * requests for `<config>.x.y`. If not set, the `<config>` object will be
     * returned.
     * @param resolutionOptions - Options for the resolution.
     * - optional: if not set or set to `true`, `undefined` will be returned if
     * no corresponding value is found. Otherwise, an error will be thrown.
     */
    getConfigAsValueOrPromise<ConfigValueType>(key: BindingAddress, propertyPath?: string, resolutionOptions?: ResolutionOptions): ValueOrPromise<ConfigValueType | undefined>;
    /**
     * Set up the configuration resolver if needed
     */
    protected setupConfigurationResolverIfNeeded(): ConfigurationResolver;
    /**
     * Resolve configuration for the binding by key
     *
     * @param key - Binding key
     * @param propertyPath - Property path for the option. For example, `x.y`
     * requests for `<config>.x.y`. If not set, the `<config>` object will be
     * returned.
     * @param resolutionOptions - Options for the resolution.
     */
    getConfig<ConfigValueType>(key: BindingAddress, propertyPath?: string, resolutionOptions?: ResolutionOptions): Promise<ConfigValueType | undefined>;
    /**
     * Resolve configuration synchronously for the binding by key
     *
     * @param key - Binding key
     * @param propertyPath - Property path for the option. For example, `x.y`
     * requests for `config.x.y`. If not set, the `config` object will be
     * returned.
     * @param resolutionOptions - Options for the resolution.
     */
    getConfigSync<ConfigValueType>(key: BindingAddress, propertyPath?: string, resolutionOptions?: ResolutionOptions): ConfigValueType | undefined;
    /**
     * Unbind a binding from the context. No parent contexts will be checked.
     *
     * @remarks
     * If you need to unbind a binding owned by a parent context, use the code
     * below:
     *
     * ```ts
     * const ownerCtx = ctx.getOwnerContext(key);
     * return ownerCtx != null && ownerCtx.unbind(key);
     * ```
     *
     * @param key - Binding key
     * @returns true if the binding key is found and removed from this context
     */
    unbind(key: BindingAddress): boolean;
    /**
     * Add a context event observer to the context
     * @param observer - Context observer instance or function
     */
    subscribe(observer: ContextEventObserver): Subscription;
    /**
     * Remove the context event observer from the context
     * @param observer - Context event observer
     */
    unsubscribe(observer: ContextEventObserver): boolean;
    /**
     * Close the context: clear observers, stop notifications, and remove event
     * listeners from its parent context.
     *
     * @remarks
     * This method MUST be called to avoid memory leaks once a context object is
     * no longer needed and should be recycled. An example is the `RequestContext`,
     * which is created per request.
     */
    close(): void;
    /**
     * Check if an observer is subscribed to this context
     * @param observer - Context observer
     */
    isSubscribed(observer: ContextObserver): boolean;
    /**
     * Create a view of the context chain with the given binding filter
     * @param filter - A function to match bindings
     * @param comparator - A function to sort matched bindings
     * @param options - Resolution options
     */
    createView<T = unknown>(filter: BindingFilter, comparator?: BindingComparator, options?: Omit<ResolutionOptions, 'session'>): ContextView<T>;
    /**
     * Check if a binding exists with the given key in the local context without
     * delegating to the parent context
     * @param key - Binding key
     */
    contains(key: BindingAddress): boolean;
    /**
     * Check if a key is bound in the context or its ancestors
     * @param key - Binding key
     */
    isBound(key: BindingAddress): boolean;
    /**
     * Get the owning context for a binding or its key
     * @param keyOrBinding - Binding object or key
     */
    getOwnerContext(keyOrBinding: BindingAddress | Readonly<Binding<unknown>>): Context | undefined;
    /**
     * Get the context matching the scope
     * @param scope - Binding scope
     */
    getScopedContext(scope: BindingScope.APPLICATION | BindingScope.SERVER | BindingScope.REQUEST): Context | undefined;
    /**
     * Locate the resolution context for the given binding. Only bindings in the
     * resolution context and its ancestors are visible as dependencies to resolve
     * the given binding
     * @param binding - Binding object
     */
    getResolutionContext(binding: Readonly<Binding<unknown>>): Context | undefined;
    /**
     * Check if this context is visible (same or ancestor) to the given one
     * @param ctx - Another context object
     */
    isVisibleTo(ctx: Context): boolean;
    /**
     * Find bindings using a key pattern or filter function
     * @param pattern - A filter function, a regexp or a wildcard pattern with
     * optional `*` and `?`. Find returns such bindings where the key matches
     * the provided pattern.
     *
     * For a wildcard:
     * - `*` matches zero or more characters except `.` and `:`
     * - `?` matches exactly one character except `.` and `:`
     *
     * For a filter function:
     * - return `true` to include the binding in the results
     * - return `false` to exclude it.
     */
    find<ValueType = BoundValue>(pattern?: string | RegExp | BindingFilter): Readonly<Binding<ValueType>>[];
    /**
     * Find bindings using the tag filter. If the filter matches one of the
     * binding tags, the binding is included.
     *
     * @param tagFilter - A filter for tags. It can be in one of the following
     * forms:
     * - A regular expression, such as `/controller/`
     * - A wildcard pattern string with optional `*` and `?`, such as `'con*'`
     *   For a wildcard:
     *   - `*` matches zero or more characters except `.` and `:`
     *   - `?` matches exactly one character except `.` and `:`
     * - An object containing tag name/value pairs, such as
     * `{name: 'my-controller'}`
     */
    findByTag<ValueType = BoundValue>(tagFilter: BindingTag | RegExp): Readonly<Binding<ValueType>>[];
    /**
     * Find bindings by tag leveraging indexes
     * @param tag - Tag name pattern or name/value pairs
     */
    protected _findByTagIndex<ValueType = BoundValue>(tag: BindingTag | RegExp): Readonly<Binding<ValueType>>[];
    protected _mergeWithParent<ValueType>(childList: Readonly<Binding<ValueType>>[], parentList?: Readonly<Binding<ValueType>>[]): Readonly<Binding<ValueType>>[];
    /**
     * Get the value bound to the given key, throw an error when no value is
     * bound for the given key.
     *
     * @example
     *
     * ```ts
     * // get the value bound to "application.instance"
     * const app = await ctx.get<Application>('application.instance');
     *
     * // get "rest" property from the value bound to "config"
     * const config = await ctx.get<RestComponentConfig>('config#rest');
     *
     * // get "a" property of "numbers" property from the value bound to "data"
     * ctx.bind('data').to({numbers: {a: 1, b: 2}, port: 3000});
     * const a = await ctx.get<number>('data#numbers.a');
     * ```
     *
     * @param keyWithPath - The binding key, optionally suffixed with a path to the
     *   (deeply) nested property to retrieve.
     * @param session - Optional session for resolution (accepted for backward
     * compatibility)
     * @returns A promise of the bound value.
     */
    get<ValueType>(keyWithPath: BindingAddress<ValueType>, session?: ResolutionSession): Promise<ValueType>;
    /**
     * Get the value bound to the given key, optionally return a (deep) property
     * of the bound value.
     *
     * @example
     *
     * ```ts
     * // get "rest" property from the value bound to "config"
     * // use `undefined` when no config is provided
     * const config = await ctx.get<RestComponentConfig>('config#rest', {
     *   optional: true
     * });
     * ```
     *
     * @param keyWithPath - The binding key, optionally suffixed with a path to the
     *   (deeply) nested property to retrieve.
     * @param options - Options for resolution.
     * @returns A promise of the bound value, or a promise of undefined when
     * the optional binding is not found.
     */
    get<ValueType>(keyWithPath: BindingAddress<ValueType>, options: ResolutionOptions): Promise<ValueType | undefined>;
    /**
     * Get the synchronous value bound to the given key, optionally
     * return a (deep) property of the bound value.
     *
     * This method throws an error if the bound value requires async computation
     * (returns a promise). You should never rely on sync bindings in production
     * code.
     *
     * @example
     *
     * ```ts
     * // get the value bound to "application.instance"
     * const app = ctx.getSync<Application>('application.instance');
     *
     * // get "rest" property from the value bound to "config"
     * const config = await ctx.getSync<RestComponentConfig>('config#rest');
     * ```
     *
     * @param keyWithPath - The binding key, optionally suffixed with a path to the
     *   (deeply) nested property to retrieve.
     * @param session - Session for resolution (accepted for backward compatibility)
     * @returns A promise of the bound value.
     */
    getSync<ValueType>(keyWithPath: BindingAddress<ValueType>, session?: ResolutionSession): ValueType;
    /**
     * Get the synchronous value bound to the given key, optionally
     * return a (deep) property of the bound value.
     *
     * This method throws an error if the bound value requires async computation
     * (returns a promise). You should never rely on sync bindings in production
     * code.
     *
     * @example
     *
     * ```ts
     * // get "rest" property from the value bound to "config"
     * // use "undefined" when no config is provided
     * const config = await ctx.getSync<RestComponentConfig>('config#rest', {
     *   optional: true
     * });
     * ```
     *
     * @param keyWithPath - The binding key, optionally suffixed with a path to the
     *   (deeply) nested property to retrieve.
     * @param options - Options for resolution.
     * @returns The bound value, or undefined when an optional binding is not found.
     */
    getSync<ValueType>(keyWithPath: BindingAddress<ValueType>, options?: ResolutionOptions): ValueType | undefined;
    /**
     * Look up a binding by key in the context and its ancestors. If no matching
     * binding is found, an error will be thrown.
     *
     * @param key - Binding key
     */
    getBinding<ValueType = BoundValue>(key: BindingAddress<ValueType>): Binding<ValueType>;
    /**
     * Look up a binding by key in the context and its ancestors. If no matching
     * binding is found and `options.optional` is not set to true, an error will
     * be thrown.
     *
     * @param key - Binding key
     * @param options - Options to control if the binding is optional. If
     * `options.optional` is set to true, the method will return `undefined`
     * instead of throwing an error if the binding key is not found.
     */
    getBinding<ValueType>(key: BindingAddress<ValueType>, options?: {
        optional?: boolean;
    }): Binding<ValueType> | undefined;
    /**
     * Find or create a binding for the given key
     * @param key - Binding address
     * @param policy - Binding creation policy
     */
    findOrCreateBinding<T>(key: BindingAddress<T>, policy?: BindingCreationPolicy): Binding<T>;
    /**
     * Get the value bound to the given key.
     *
     * This is an internal version that preserves the dual sync/async result
     * of `Binding#getValue()`. Users should use `get()` or `getSync()` instead.
     *
     * @example
     *
     * ```ts
     * // get the value bound to "application.instance"
     * ctx.getValueOrPromise<Application>('application.instance');
     *
     * // get "rest" property from the value bound to "config"
     * ctx.getValueOrPromise<RestComponentConfig>('config#rest');
     *
     * // get "a" property of "numbers" property from the value bound to "data"
     * ctx.bind('data').to({numbers: {a: 1, b: 2}, port: 3000});
     * ctx.getValueOrPromise<number>('data#numbers.a');
     * ```
     *
     * @param keyWithPath - The binding key, optionally suffixed with a path to the
     *   (deeply) nested property to retrieve.
     * @param optionsOrSession - Options for resolution or a session
     * @returns The bound value or a promise of the bound value, depending
     *   on how the binding is configured.
     * @internal
     */
    getValueOrPromise<ValueType>(keyWithPath: BindingAddress<ValueType>, optionsOrSession?: ResolutionOptionsOrSession): ValueOrPromise<ValueType | undefined>;
    /**
     * Create a plain JSON object for the context
     */
    toJSON(): JSONObject;
    /**
     * Inspect the context and dump out a JSON object representing the context
     * hierarchy
     * @param options - Options for inspect
     */
    inspect(options?: ContextInspectOptions): JSONObject;
    /**
     * Inspect the context hierarchy
     * @param options - Options for inspect
     * @param visitedClasses - A map to keep class to name so that we can have
     * different names for classes with colliding names. The situation can happen
     * when two classes with the same name are bound in different modules.
     */
    private _inspect;
    /**
     * The "bind" event is emitted when a new binding is added to the context.
     * The "unbind" event is emitted when an existing binding is removed.
     *
     * @param eventName The name of the event - always `bind` or `unbind`.
     * @param listener The listener function to call when the event is emitted.
     */
    on(eventName: 'bind' | 'unbind', listener: ContextEventListener): this;
    on(event: string | symbol, listener: (...args: any[]) => void): this;
    /**
     * The "bind" event is emitted when a new binding is added to the context.
     * The "unbind" event is emitted when an existing binding is removed.
     *
     * @param eventName The name of the event - always `bind` or `unbind`.
     * @param listener The listener function to call when the event is emitted.
     */
    once(eventName: 'bind' | 'unbind', listener: ContextEventListener): this;
    once(event: string | symbol, listener: (...args: any[]) => void): this;
}
/**
 * Options for context.inspect()
 */
interface ContextInspectOptions extends BindingInspectOptions {
    /**
     * The flag to control if parent context should be inspected
     */
    includeParent?: boolean;
}
/**
 * Policy to control if a binding should be created for the context
 */
declare enum BindingCreationPolicy {
    /**
     * Always create a binding with the key for the context
     */
    ALWAYS_CREATE = "Always",
    /**
     * Never create a binding for the context. If the key is not bound in the
     * context, throw an error.
     */
    NEVER_CREATE = "Never",
    /**
     * Create a binding if the key is not bound in the context. Otherwise, return
     * the existing binding.
     */
    CREATE_IF_NOT_BOUND = "IfNotBound"
}

/**
 * Scope for binding values
 */
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
 */
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`
 */
type ConstantBindingSource<T> = {
    type: BindingType.CONSTANT;
    value: T;
};
/**
 * Binding source for `toDynamicValue`
 */
type DynamicValueBindingSource<T> = {
    type: BindingType.DYNAMIC_VALUE;
    value: ValueFactory<T> | DynamicValueProviderClass<T>;
};
/**
 * Binding source for `toClass`
 */
type ClassBindingSource<T> = {
    type: BindingType.CLASS;
    value: Constructor<T>;
};
/**
 * Binding source for `toProvider`
 */
type ProviderBindingSource<T> = {
    type: BindingType.PROVIDER;
    value: Constructor<Provider<T>>;
};
/**
 * Binding source for `toAlias`
 */
type AliasBindingSource<T> = {
    type: BindingType.ALIAS;
    value: BindingAddress<T>;
};
/**
 * Source for the binding, including the type and value
 */
type BindingSource<T> = ConstantBindingSource<T> | DynamicValueBindingSource<T> | ClassBindingSource<T> | ProviderBindingSource<T> | AliasBindingSource<T>;
type TagMap = MapObject<any>;
/**
 * Binding tag can be a simple name or name/value pairs
 */
type BindingTag = TagMap | string;
/**
 * A function as the template to configure bindings
 */
type BindingTemplate<T = unknown> = (binding: Binding<T>) => void;
/**
 * Information for a binding event
 */
type BindingEvent = {
    /**
     * Event type
     */
    type: 'changed' | string;
    /**
     * Source binding that emits the event
     */
    binding: Readonly<Binding<unknown>>;
    /**
     * Operation that triggers the event
     */
    operation: 'tag' | 'scope' | 'value' | string;
};
/**
 * Event listeners for binding events
 */
type BindingEventListener = (
/**
 * Binding event
 */
event: BindingEvent) => void;
/**
 * A factory function for `toDynamicValue`
 */
type ValueFactory<T = unknown> = (resolutionCtx: ResolutionContext) => ValueOrPromise<T | undefined>;
/**
 * A class with a static `value` method as the factory function for
 * `toDynamicValue`.
 *
 * @example
 * ```ts
 * import {inject} from 'contexify';
 *
 * export class DynamicGreetingProvider {
 *   static value(@inject('currentUser') user: string) {
 *     return `Hello, ${user}`;
 *   }
 * }
 * ```
 */
interface DynamicValueProviderClass<T = unknown> extends Constructor<unknown> {
    value: (...args: BoundValue[]) => ValueOrPromise<T>;
}
/**
 * Check if the factory is a value factory provider class
 * @param factory - A factory function or a dynamic value provider class
 */
declare function isDynamicValueProviderClass<T = unknown>(factory: unknown): factory is DynamicValueProviderClass<T>;
/**
 * Binding represents an entry in the `Context`. Each binding has a key and a
 * corresponding value getter.
 */
declare class Binding<T = BoundValue> 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<T> | undefined;
    /**
     * For bindings bound via `toClass()`, this property contains the constructor
     * function of the class
     */
    get valueConstructor(): Constructor<T> | undefined;
    /**
     * For bindings bound via `toProvider()`, this property contains the
     * constructor function of the provider class
     */
    get providerConstructor(): Constructor<Provider<T>> | undefined;
    constructor(key: BindingAddress<T>, 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<T>;
    /**
     * 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<T | undefined>;
    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<T> | DynamicValueProviderClass<T>): this;
    private static valueOrProxy;
    /**
     * Bind the key to a value computed by a Provider.
     *
     * * @example
     *
     * ```ts
     * export class DateProvider implements Provider<Date> {
     *   constructor(@inject('stringDate') private param: String){}
     *   value(): Date {
     *     return new Date(param);
     *   }
     * }
     * ```
     *
     * @param provider - The value provider to use.
     */
    toProvider(providerClass: Constructor<Provider<T>>): 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<C extends T & object>(ctor: Constructor<C>): 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<T> | Constructor<T | Provider<T>>): 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<T>): 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<RestServer>('servers.RestServer1');
     * serverBinding.apply(serverTemplate);
     * ```
     * @param templateFns - One or more functions to configure the binding
     */
    apply(...templateFns: BindingTemplate<T>[]): 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<V = unknown>(key: BindingAddress<V>): Binding<V>;
    /**
     * 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<V = unknown>(key: BindingAddress): Binding<V>;
    /**
     * 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()
 */
interface BindingInspectOptions {
    /**
     * The flag to control if injections should be inspected
     */
    includeInjections?: boolean;
}

export { type ResolutionOptions as $, type AliasBindingSource as A, BindingScope as B, type ConstantBindingSource as C, type DynamicValueBindingSource as D, type ContextInspectOptions as E, BindingCreationPolicy as F, type ContextEvent as G, type ContextEventListener as H, type ContextEventType as I, type ContextObserverFn as J, type ContextObserver as K, type ContextEventObserver as L, ContextSubscriptionManager as M, type Notification as N, type ContextViewEvent as O, type ProviderBindingSource as P, ContextView as Q, createViewGetter as R, type Subscription as S, type TagMap as T, type ResolutionAction as U, type ValueFactory as V, type BindingElement as W, type InjectionElement as X, type InjectionDescriptor as Y, type ResolutionElement as Z, ResolutionSession as _, BindingType as a, type ResolutionOptionsOrSession as a0, asResolutionOptions as a1, type ResolutionContext as a2, ResolutionError as a3, type ResolverFunction as a4, type InjectionMetadata as a5, type Injection as a6, inject as a7, Getter as a8, type Setter as a9, type InjectBindingMetadata as aa, assertTargetType as ab, describeInjectedArguments as ac, inspectTargetType as ad, describeInjectedProperties as ae, inspectInjections as af, hasInjections as ag, type ConfigurationResolver as ah, DefaultConfigurationResolver as ai, configBindingKeyFor as aj, ContextTagIndexer as ak, type ClassBindingSource as b, type BindingSource as c, type BindingTag as d, type BindingTemplate as e, type BindingEvent as f, type BindingEventListener as g, type DynamicValueProviderClass as h, isDynamicValueProviderClass as i, Binding as j, type BindingInspectOptions as k, type BindingFilter as l, type BindingSelector as m, isBindingAddress as n, type BindingTagFilter as o, isBindingTagFilter as p, type TagValueMatcher as q, ANY_TAG_VALUE as r, includesTagValue as s, filterByTag as t, filterByKey as u, type BindingComparator as v, compareBindingsByTag as w, compareByOrder as x, sortBindingsByPhase as y, Context as z };
