@deepkit/app
Version:
Deepkit App, CLI framework and service container
316 lines (315 loc) • 10.8 kB
TypeScript
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[];