import Resolver from './resolver'; import { Dict } from '../utils/types'; import Loader from '@denali-js/loader'; export interface ContainerOptions { /** * The container should treat the member as a singleton. If true, the * container will create that singleton on the first lookup. */ singleton?: boolean; /** * What should the container fallback to if it's unable to find a specific * entry under the given type? This is used to allow for the common * "application" fallback entry, i.e. if you don't define a model-specific * ORM adapter, then because the `orm-adapter` type has `fallbacks: [ * 'application' ]`, the container will return the ApplicationORMAdapter * instead. */ fallbacks?: string[]; } /** * The Container is the dependency injection solution for Denali. It is * responsible for managing the lifecycle of objects (i.e. singletons), * as well as orchestrating dependency lookup. This provides two key benefits: * * * Apps can consume classes that originate from anywhere in the addon * dependency tree, without needing to care/specify where. * * We can more easily test parts of the framework by mocking out container * entries instead of dealing with hardcoding dependencies * * @package metal * @since 0.1.0 */ export declare class Container { /** * Manual registrations that should override resolver retrieved values */ protected registry: Dict; /** * An array of resolvers used to retrieve container members. Resolvers are * tried in order, first to find the member wins. Normally, each addon will * supply it's own resolver, allowing for addon order and precedence when * looking up container entries. */ protected resolvers: Resolver[]; /** * Internal cache of lookup values */ protected lookups: Dict; /** * The top level loader for the entire bundle */ loader: Loader; /** * Default options for container entries. Keyed on specifier or type. See * ContainerOptions. */ protected options: Dict; /** * Take a top level bundle loader and load it into this container */ loadBundle(loader: Loader): void; /** * Load a bundle scope into the container. A bundle scope typically * corresponds to an addon. Each bundle scope can provide it's own resolver to * tell the consuming app how to look things up within the bundle scope. If * no resolver is supplied, Denali will use the default Denali resolver. */ loadBundleScope(loader: Loader): void; /** * Add a manual registration that will take precedence over any resolved * lookups. * * @since 0.1.0 */ register(specifier: string, entry: any, options?: ContainerOptions): void; /** * Lookup the given specifier in the container. If options.loose is true, * failed lookups will return undefined rather than throw. * * @since 0.1.0 * @param options.loose if the entry is not found, don't throw, just return `false` * @param options.raw Ignore the cache, and lookup the underlying container * value rather than any singletons; mostly useful for tests, you should rarely use * this option, and at your own risk */ lookup(specifier: string, options?: { raw?: true; }): T; lookup(specifier: string, options?: { raw?: true; loose: true; }): T | false; protected instantiateSingletons(specifier: string, entry: T): T; /** * Recursive lookup method that takes a specifier and fallback specifiers. * Checks manual registrations first, then iterates through each resolver. If * the entry is still not found, it recurses through the fallback options * before ultimatley throwing (or returning false if loose: true) */ protected lookupRaw(specifier: string): T | false; /** * Lookup all the entries for a given type in the container. This will ask * all resolvers to eagerly load all classes for this type. Returns an object * whose keys are container specifiers and values are the looked up values * for those specifiers. * * @since 0.1.0 */ lookupAll(type: string): Dict; /** * Returns an array of entry names for all entries under this type. * * @since 0.1.0 */ availableForType(type: string): string[]; /** * Return the value for the given option on the given specifier. Specifier * may be a full specifier or just a type. * * @since 0.1.0 */ getOption(specifier: string, optionName: U): ContainerOptions[U]; /** * Set the option for the given specifier or type. * * @since 0.1.0 */ setOption(specifier: string, optionName: keyof ContainerOptions, value: any): void; /** * Clear any cached lookups for this specifier. You probably don't want to * use this. The only significant use case is for testing to allow test * containers to override an already looked up value. */ clearCache(specifier: string): void; /** * Empties the entire container, including removing all resolvers and the * loader, as well as emptying all caches. The primary use case is for * unit testing, when you want a clean slate environment to selectively * add things back to. */ clear(): void; /** * Given container-managed singletons a chance to cleanup on application * shutdown * * @since 0.1.0 */ teardown(): void; } declare const container: Container; export default container; export declare const lookup: typeof Container.prototype.lookup;