UNPKG

@deepkit/app

Version:

Deepkit App, CLI framework and service container

316 lines (315 loc) 10.8 kB
import { InjectorModule, InjectorModuleConfig, NormalizedProvider, ProviderWithScope, Token } from '@deepkit/injector'; import { AbstractClassType, ClassType, CustomError, ExtractClassType } from '@deepkit/core'; import { EventListener, EventToken } from '@deepkit/event'; import { WorkflowDefinition } from '@deepkit/workflow'; import { ReflectionFunction, ReflectionMethod, Type } from '@deepkit/type'; import { ControllerConfig } from './service-container.js'; export interface MiddlewareConfig { getClassTypes(): ClassType[]; } export type MiddlewareFactory = () => MiddlewareConfig; export type ExportType = AbstractClassType | string | AppModule<any> | Type | NormalizedProvider; /** * @reflection never */ export interface AddedListener { eventToken: EventToken; reflection: ReflectionMethod | ReflectionFunction; module?: InjectorModule; classType?: ClassType; methodName?: string; order: number; } export declare function stringifyListener(listener: AddedListener): string; export interface ModuleDefinition { /** * The name of the module. This is used in the configuration system. * It allows you to have multiple instances of the same module with different configurations * loaded from a configuration loader (e.g. env variables). * * The lowercase alphanumeric module name. * Choose a short unique name for best usability. If you don't have any configuration * or if you want that your configuration options are available without prefix, you can keep this undefined. */ name?: string; /** * Providers. */ providers?: (ProviderWithScope | ProviderWithScope[])[]; /** * Export providers (its token `provide` value) or modules you imported first. */ exports?: ExportType[]; /** * Module bootstrap class|function. * This class is instantiated or function executed on bootstrap and can set up various injected services. */ bootstrap?: ClassType | Function; /** * Configuration definition. * * @example * ```typescript * * class MyModuleConfig { * debug: boolean = false; * }); * * class MyModule extends createModuleClass({ * config: MyModuleConfig * }); * ``` */ config?: ClassType; /** * CLI controllers. */ controllers?: ClassType[]; /** * Register created workflows. This allows the Framework Debugger to collect * debug information and display the graph of your workflow. */ workflows?: WorkflowDefinition<any>[]; /** * Event listeners. * * @example with simple functions * ```typescript * { * listeners: [ * onEvent.listen((event: MyEvent) => {console.log('event triggered', event);}), * ] * } * ``` * * @example with services * ```typescript * * class MyListener { * @eventDispatcher.listen(onEvent) * onEvent(event: typeof onEvent['type']) { * console.log('event triggered', event); * } * } * * { * listeners: [ * MyListener, * ] * } * ``` */ listeners?: (EventListener | ClassType)[]; /** * HTTP middlewares. */ middlewares?: MiddlewareFactory[]; } export interface CreateModuleDefinition extends ModuleDefinition { /** * Whether all services should be moved to the root module/application. */ forRoot?: true; /** * Modules can not import other modules in the module definitions. * Use instead: * * ```typescript * class MyModule extends createModuleClass({}) { * imports = [new AnotherModule]; * } * ``` * * or * * ```typescript * class MyModule extends createModuleClass({}) { * process() { * this.addModuleImport(new AnotherModule); * } * } * ``` * * or switch to functional modules * * ```typescript * function myModule(module: AppModule) { * module.addModuleImport(new AnotherModule); * } * ``` */ imports?: undefined; } export type FunctionalModule = (module: AppModule<any>) => void; export type FunctionalModuleFactory = (...args: any[]) => (module: AppModule<any>) => void; export interface RootModuleDefinition extends ModuleDefinition { /** * Import another module. */ imports?: (AppModule<any> | FunctionalModule)[]; } export declare class ConfigurationInvalidError extends CustomError { } /** * @reflection never */ export type DeepPartial<T> = T extends string | number | bigint | boolean | null | undefined | symbol | Date | Set<any> | Map<any, any> | Uint8Array | ArrayBuffer | ArrayBufferView | Error | RegExp | Function | Promise<any> ? T : Partial<{ [P in keyof T]: DeepPartial<T[P]>; }>; export interface AppModuleClass<C extends InjectorModuleConfig> { new (config?: DeepPartial<C>): AppModule<C>; } /** * Creates a new module class type from which you can extend. * * ```typescript * class MyModule extends createModuleClass({}) {} * * //and used like this * new App({ * imports: [new MyModule] * }); * ``` */ export declare function createModuleClass<C extends InjectorModuleConfig>(options: CreateModuleDefinition & { config?: ClassType<C>; }): AppModuleClass<C>; /** * Creates a new module instance. * * This is mainly used for small non-reusable modules. * It's recommended to use `createModuleClass` and extend from it. * * @example * ```typescript * const myModule = createModuleClass({ * config: MyConfig * providers: [MyService] * }); * * const app = new App({ * imports: [myModule] * }); * ``` */ export declare function createModule<T extends CreateModuleDefinition>(options: T): AppModule<ExtractClassType<T['config']>>; export type ListenerType = EventListener | ClassType; /** * The AppModule is the base class for all modules. * * You can use `createModule` to create a new module class or extend from `AppModule` manually. * * @example * ```typescript * * class MyModule extends AppModule { * providers = [MyService]; * exports = [MyService]; * * constructor(config: MyConfig) { * super(); * this.setConfigDefinition(MyConfig); * this.configure(config); * this.name = 'myModule'; * } * } */ export declare class AppModule<C extends InjectorModuleConfig = any> extends InjectorModule<C> { options: RootModuleDefinition; setups: ((module: AppModule<any>, config: any) => void)[]; id: number; setupConfigs: ((module: AppModule<any>, config: any) => void)[]; imports: AppModule<any>[]; controllers: ClassType[]; commands: { name?: string; callback: Function; }[]; workflows: WorkflowDefinition<any>[]; listeners: ListenerType[]; middlewares: MiddlewareFactory[]; uses: ((...args: any[]) => void)[]; name: string; constructor(config?: DeepPartial<C>, options?: RootModuleDefinition, setups?: ((module: AppModule<any>, config: any) => void)[], id?: number); protected addModuleImport(m: AppModule<any> | FunctionalModule): void; /** * When all configuration loaders have been loaded, this method is called. * It allows to further manipulate the module state depending on the final config. * Possible use-cases: * - Add more providers depending on the configuration. * - Change the module imports depending on the configuration. * - Change provider setup via this.configureProvider<Provider>(provider => {}) depending on the configuration. */ process(): void; /** * A hook that allows to react on a registered provider in some module. */ processProvider(module: AppModule<any>, token: Token, provider: ProviderWithScope): void; /** * A hook that allows to react on a registered controller in some module. */ processController(module: AppModule<any>, config: ControllerConfig): void; /** * A hook that allows to react on a registered event listeners in some module. */ processListener(module: AppModule<any>, listener: AddedListener): void; /** * After `process` and when all modules have been processed by the service container. * This is also after `processController` and `processProvider` have been called and the full * final module tree is known. Adding now new providers or modules doesn't have any effect. * * Last chance to set up the injector context, via this.setupProvider(). */ postProcess(): void; /** * Renames this module instance. */ rename(name: string): this; getListeners(): ListenerType[]; getWorkflows(): WorkflowDefinition<any>[]; getMiddlewares(): MiddlewareFactory[]; getControllers(): ClassType[]; getCommands(): { name?: string; callback: Function; }[]; addCommand(name: string | undefined, callback: (...args: []) => any): this; addController(...controller: ClassType[]): this; addListener(...listener: (EventListener | ClassType)[]): this; addMiddleware(...middlewares: MiddlewareFactory[]): this; /** * Allows to change the module config before `setup` and bootstrap is called. * This is the last step right before the config is validated. */ setupConfig(callback: (module: AppModule<C>, config: C) => void): this; /** * Allows to change the module after the configuration has been loaded, right before the service container is built. * * This enables you to change the module or its imports depending on the configuration the last time before their services are built. * * At this point no services can be requested as the service container was not built. */ setup(callback: (module: AppModule<C>, config: C) => void): this; /** * Allows to call services before the application bootstraps. * * This enables you to configure modules and request their services. */ use(callback: (...args: any[]) => void): this; getImports(): AppModule<any>[]; getName(): string; /** * Sets configured values. */ configure(config: Partial<C>): this; } export declare type __ΩMiddlewareConfig = any[]; export declare type __ΩMiddlewareFactory = any[]; export declare type __ΩExportType = any[]; export declare type __ΩModuleDefinition = any[]; export declare type __ΩCreateModuleDefinition = any[]; export declare type __ΩFunctionalModule = any[]; export declare type __ΩFunctionalModuleFactory = any[]; export declare type __ΩRootModuleDefinition = any[]; export declare type __ΩAppModuleClass = any[]; export declare type __ΩListenerType = any[];