@asimojs/asimo
Version:
Asynchronous dependency manager for Typescript projects
176 lines (169 loc) • 7.81 kB
text/typescript
/**
* Asimo context object used to asynchronously get or create dependencies
* The root context is exposed through the asm global object. Sub-contexts can be created through asm.createChildContext()
*/
export interface AsmContext {
/**
* The context name - helps differentiating multiple contexts
*/
readonly name: string;
/**
* The parent context where dependencies will be retrieved if not found in the current context
*/
readonly parent: AsmContext | null;
/**
* A unique path identifier listing all context names from the top parent context
* e.g. "/asm/subContext1/subContext2"
*/
readonly path: string;
/**
* The interface definitions that are currently loaded in the context
*/
readonly definitions: AsmInterfaceDefinition[];
/**
* Register an object instance.
* Object instances can be retrieved synchronously through the get() method
* @see get
*/
registerObject<T extends object>(iid: InterfaceId<T>, o: T): void;
/**
* Register a service factory. Services are singleton objects that will be created only once
* and stored in the AsmContext. Services will be created on-demand, when retrieved for the first time
* Services can be retrieved asynchronoysly through the fetch() method
* @param iid the interface id
* @param factory a factory that will be called to instanciate the service
* @see fetch
*/
registerService<T>(iid: InterfaceId<T>, factory: (c: AsmContext) => T | Promise<T>): void;
/**
* Register an object factory. The factory will be called any time the get method is called
* for this interface id. On the contrary to services, there is no restriction on the number
* of objects that can be created. Besides asimo doesn't keep any reference to the object
* created.
* Objects produced by factories can be retrieved asynchronoysly through the fetch() method
* @param iid the interface id
* @param factory a factory that will be called to create an object instance
* @see fetch
*/
registerFactory<T>(iid: InterfaceId<T>, factory: (c: AsmContext) => T | Promise<T>): void;
/**
* Register a group loader that will be used to asynchronously load multiple
* service and object factories on-demand (i.e. the group code will only be loaded when
* an explicit get is done on one of its service or object interfaces)
* Services or objects produced by service or object factories can be retrieved through the fetch method.
* @param iids the list of interface ids that are packaged in this group
* @param loader an async factory that should dynamically import() the required modules
* @see fetch
*/
registerGroup(iids: InterfaceId<any>[], loader: () => Promise<unknown>): void;
/**
* Synchrounsly retrieve an object instance registered with registerObject
* For each interface id, asimo will first look in the current context objects - if not found
* it will then perform the same lookup in its parent context (recursively, up to the root context).
* This method allows to get up to 5 dependencies in one call with type support (more can be retrieved without type inference).
* Note: When retrieving 1 resource, the parameters can be either InterfaceId objects or interface namespaces (strings).
* When using InterfaceId typescript will automatically infer the right type - otherwise an explicit
* type cast will be necessary
* Note: this method will throw an error if the targeted object is not found - unless a default value is passed as 2nd argument
* (this only works when fetching a single resource - e.g. asm.get(AppModuleIID, null) )
* @see fetch
*/
get<T>(iid: IidNs<T>): T;
get<T, D extends T | null>(iid: IidNs<T>, defaultValue?: D): T | D;
get<T1, T2>(iid1: InterfaceId<T1>, InterfaceId: IidNs<T2>): [T1, T2];
get<T1, T2, T3>(
iid1: InterfaceId<T1>,
iid2: InterfaceId<T2>,
iid3: InterfaceId<T3>,
): [T1, T2, T3];
get<T1, T2, T3, T4>(
iid1: InterfaceId<T1>,
iid2: InterfaceId<T2>,
iid3: InterfaceId<T3>,
iid4: InterfaceId<T4>,
): [T1, T2, T3, T4];
get<T1, T2, T3, T4, T5>(
iid1: InterfaceId<T1>,
iid2: InterfaceId<T2>,
iid3: InterfaceId<T3>,
iid4: InterfaceId<T4>,
iid5: InterfaceId<T5>,
): [T1, T2, T3, T4, T5];
get(...iids: (InterfaceId<any> | string)[]): Promise<any[]>;
/**
* Asynchronously fetch a service or an object produced by a registered factory. For each interface id, asimo will first look in the current
* context for services or object factories or groups registered for the interface (in this order) - if not found
* it will then perform the same lookup in its parent context (recursively, up to the root context).
* This method allows to get up to 5 dependencies in one call with type support (more can be fetched
* without type inference).
* Note: When fetcing 1 resource, the parameters can be either InterfaceId objects or interface namespaces (strings).
* When using InterfaceId typescript will automatically infer the right type - otherwise an explicit
* type cast will be necessary
* Note: this method will throw an error if the targeted service or object cannot be found/loaded - unless a default value is passed
* (this only works when fetching a single resource - e.g. asm.fetch(CalculatorIID, null) )
*/
fetch<T>(iid: IidNs<T>): Promise<T>;
fetch<T, D extends T | null>(iid: IidNs<T>, defaultValue?: D): Promise<T | D>;
fetch<T1, T2>(iid1: InterfaceId<T1>, InterfaceId: IidNs<T2>): Promise<[T1, T2]>;
fetch<T1, T2, T3>(
iid1: InterfaceId<T1>,
iid2: InterfaceId<T2>,
iid3: InterfaceId<T3>,
): Promise<[T1, T2, T3]>;
fetch<T1, T2, T3, T4>(
iid1: InterfaceId<T1>,
iid2: InterfaceId<T2>,
iid3: InterfaceId<T3>,
iid4: InterfaceId<T4>,
): Promise<[T1, T2, T3, T4]>;
fetch<T1, T2, T3, T4, T5>(
iid1: InterfaceId<T1>,
iid2: InterfaceId<T2>,
iid3: InterfaceId<T3>,
iid4: InterfaceId<T4>,
iid5: InterfaceId<T5>,
): Promise<[T1, T2, T3, T4, T5]>;
fetch(...iids: (InterfaceId<any> | string)[]): Promise<any[]>;
/**
* Create a child context that can override some of the dependencies defined in its parent (cf. get behaviour)
* @param name a name to identifiy and differentiate this context from other contexts
*/
createChildContext(name?: string): AsmContext;
/**
* Define where logs should go - default = console
*/
logger: Logger | null;
/**
* Log the asimo state into an array or in the console if no output argument is provided
* @param output
*/
logState(output?: string[]): void;
}
export interface AsmInterfaceDefinition {
/** The interface id */
iid: string;
/** The type of of object the interface is going to produce */
type: "service" | "object" | "group";
/** Tell if the service associated to the interface has been loaded (not used for object or groups) */
loaded?: boolean;
}
/**
* Object that will receive the logs
*/
export interface Logger {
log(...data: any[]): void;
}
/**
* String representing an interface namespace - e.g. "myapplication.services.Settings"
*/
export type InterfaceNamespace = string;
/**
* Object biding an interface type T with an interface namespace (allows to get typescript validation)
*/
export interface InterfaceId<T> {
ns: InterfaceNamespace;
}
/**
* IID or Namespace: parameter type used to retrieve an object from asimo
*/
export type IidNs<T> = InterfaceId<T> | string;