UNPKG

opinionated-machine

Version:

Very opinionated DI framework for fastify, built on top of awilix

106 lines (105 loc) 4.32 kB
import type { Resolver } from 'awilix'; import type { DependencyInjectionOptions } from './DIContext.js'; export type MandatoryNameAndRegistrationPair<T> = { [U in keyof T]: Resolver<T[U]>; }; /** * Infers the module's dependency types from the return type of `resolveDependencies()`. * * This eliminates the need to manually define a `ModuleDependencies` type that duplicates * information already present in the resolver return value. * * @example * ```typescript * export class MyModule extends AbstractModule { * resolveDependencies(diOptions: DependencyInjectionOptions) { * return { * myService: asServiceClass(MyService), * myRepo: asRepositoryClass(MyRepository), * } * } * } * * // Inferred as { myService: MyService; myRepo: MyRepository } * export type MyModuleDependencies = InferModuleDependencies<MyModule> * ``` */ export type InferModuleDependencies<M extends AbstractModule> = ReturnType<M['resolveDependencies']> extends infer R ? { [K in keyof R]: R[K] extends Resolver<infer T> ? T : never; } : never; /** * Infers only the **public** dependency types from the return type of `resolveDependencies()`, * omitting non-public dependencies entirely. * * When a module is used as a secondary module, only resolvers marked with `public: true` * (i.e. those created via `asServiceClass`, `asUseCaseClass`, `asJobQueueClass`, or * `asEnqueuedJobQueueManagerFunction`) are exposed. Non-public resolvers are filtered out. * * @example * ```typescript * export class MyModule extends AbstractModule { * resolveDependencies(diOptions: DependencyInjectionOptions) { * return { * myService: asServiceClass(MyService), // public → MyService * myRepo: asRepositoryClass(MyRepository), // private → omitted * } * } * } * * // Inferred as { myService: MyService } * export type MyModulePublicDependencies = InferPublicModuleDependencies<MyModule> * ``` */ export type InferPublicModuleDependencies<M extends AbstractModule> = ReturnType<M['resolveDependencies']> extends infer R ? { [K in keyof R as R[K] extends { readonly __publicResolver: true; } ? K : never]: R[K] extends Resolver<infer T> ? T : never; } : never; /** * Augmentation target for accumulating public dependencies across modules. * * Each module augments this interface via `declare module` to register its * public dependencies. The augmentations are project-wide — they apply * everywhere as long as the augmenting file is part of the TypeScript * compilation, with no explicit import chain required. * * @example * ```typescript * // In your module file: * declare module 'opinionated-machine' { * interface PublicDependencies extends InferPublicModuleDependencies<MyModule> {} * } * * // In any consumer file: * import type { PublicDependencies } from 'opinionated-machine' * // PublicDependencies contains all augmented public deps * ``` */ export interface PublicDependencies { } export declare abstract class AbstractModule<ModuleDependencies = unknown, ExternalDependencies = never> { abstract resolveDependencies(diOptions: DependencyInjectionOptions, externalDependencies: ExternalDependencies): MandatoryNameAndRegistrationPair<ModuleDependencies>; /** * Override to register REST and SSE controllers. * Returns empty object by default - no changes needed for modules without controllers. * * Controllers registered here are automatically added to the DI container. * SSE controllers (created with asSSEControllerClass) are automatically detected * and registered for SSE route handling. * * @param diOptions - DI options (use for test mode detection with asSSEControllerClass) * * @example * ```typescript * public resolveControllers(diOptions: DependencyInjectionOptions) { * return { * // REST controller * usersController: asControllerClass(UsersController), * // SSE controller (automatically detected via isSSEController flag) * notificationsSSEController: asSSEControllerClass(NotificationsSSEController, { diOptions }), * } * } * ``` */ resolveControllers(_diOptions: DependencyInjectionOptions): MandatoryNameAndRegistrationPair<unknown>; }