UNPKG

opinionated-machine

Version:

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

160 lines (159 loc) 8.14 kB
import type { BuildResolver, BuildResolverOptions, DisposableResolver } from 'awilix'; import type { FunctionReturning } from 'awilix/lib/container'; import type { DependencyInjectionOptions } from './DIContext.js'; /** * Type-level representation of a class value that infers the instance type * from the `prototype` property rather than from the constructor signature. * * This breaks circular type dependencies that occur when a class constructor * references a type derived from the module's own resolver return type * (e.g. `InferModuleDependencies`), because TypeScript can resolve the * instance type (prototype) without evaluating constructor parameter types. * * Constructor parameter types are still fully checked — the cycle is only * broken at the resolver inference level. */ type ClassValue<T> = { prototype: T; }; declare module 'awilix' { interface ResolverOptions<T> { public?: boolean; isSSEController?: boolean; isDualModeController?: boolean; } } export type PublicResolver<T> = BuildResolver<T> & DisposableResolver<T> & { readonly __publicResolver: true; }; export interface EnqueuedJobQueueManager { start(enabled?: string[] | boolean): Promise<void>; } export declare function asSingletonClass<T = object>(Type: ClassValue<T>, opts: BuildResolverOptions<T> & { public: true; }): PublicResolver<T>; export declare function asSingletonClass<T = object>(Type: ClassValue<T>, opts?: BuildResolverOptions<T>): BuildResolver<T> & DisposableResolver<T>; /** * Register a class with an additional config parameter passed to the constructor. * Uses asFunction wrapper internally to pass the config as a second parameter. * Requires PROXY injection mode. * * @example * ```typescript * myService: asClassWithConfig(MyService, { enableFeature: true }), * ``` */ export declare function asClassWithConfig<T = object, Config = unknown>(Type: ClassValue<T>, config: Config, opts?: BuildResolverOptions<T>): BuildResolver<T> & DisposableResolver<T>; export declare function asSingletonFunction<T>(fn: FunctionReturning<T>, opts: BuildResolverOptions<T> & { public: true; }): PublicResolver<T>; export declare function asSingletonFunction<T>(fn: FunctionReturning<T>, opts?: BuildResolverOptions<T>): BuildResolver<T> & DisposableResolver<T>; export declare function asServiceClass<T = object>(Type: ClassValue<T>, opts: BuildResolverOptions<T> & { public: false; }): BuildResolver<T> & DisposableResolver<T>; export declare function asServiceClass<T = object>(Type: ClassValue<T>, opts?: BuildResolverOptions<T>): PublicResolver<T>; export declare function asUseCaseClass<T = object>(Type: ClassValue<T>, opts: BuildResolverOptions<T> & { public: false; }): BuildResolver<T> & DisposableResolver<T>; export declare function asUseCaseClass<T = object>(Type: ClassValue<T>, opts?: BuildResolverOptions<T>): PublicResolver<T>; export declare function asRepositoryClass<T = object>(Type: ClassValue<T>, opts?: BuildResolverOptions<T>): BuildResolver<T> & DisposableResolver<T>; export declare function asControllerClass<T = object>(Type: ClassValue<T>, opts?: BuildResolverOptions<T>): BuildResolver<T> & DisposableResolver<T>; export type SSEControllerModuleOptions = { diOptions: DependencyInjectionOptions; /** Enable rooms. Resolves `sseRoomBroadcaster` from DI cradle. */ rooms?: boolean; }; /** * Register an SSE controller class with the DI container. * * SSE controllers handle Server-Sent Events connections and require * graceful shutdown to close all active connections. * * When `diOptions.isTestMode` is true, connection spying is enabled * allowing tests to await connections via `controller.connectionSpy`. * * @example * ```typescript * // Without test mode * notificationsSSEController: asSSEControllerClass(NotificationsSSEController), * * // With test mode (enables connection spy) * notificationsSSEController: asSSEControllerClass(NotificationsSSEController, { diOptions }), * * // With rooms enabled (resolves sseRoomBroadcaster from DI) * dashboardController: asSSEControllerClass(DashboardSSEController, { diOptions, rooms: true }), * ``` */ export declare function asSSEControllerClass<T = object>(Type: ClassValue<T>, sseOptions?: SSEControllerModuleOptions, opts?: BuildResolverOptions<T>): BuildResolver<T> & DisposableResolver<T>; export type DualModeControllerModuleOptions = { diOptions: DependencyInjectionOptions; /** Enable rooms. Resolves `sseRoomBroadcaster` from DI cradle. */ rooms?: boolean; }; /** * Register a dual-mode controller class with the DI container. * * Dual-mode controllers handle both SSE streaming and JSON responses on the * same route path, automatically branching based on the `Accept` header. * They require graceful shutdown to close all active SSE connections. * * When `diOptions.isTestMode` is true, connection spying is enabled * allowing tests to await connections via `controller.connectionSpy`. * * @example * ```typescript * // Without test mode * chatController: asDualModeControllerClass(ChatController), * * // With test mode (enables connection spy) * chatController: asDualModeControllerClass(ChatController, { diOptions }), * * // With rooms enabled (resolves sseRoomBroadcaster from DI) * dashboardController: asDualModeControllerClass(DashboardController, { diOptions, rooms: true }), * ``` */ export declare function asDualModeControllerClass<T = object>(Type: ClassValue<T>, dualModeOptions?: DualModeControllerModuleOptions, opts?: BuildResolverOptions<T>): BuildResolver<T> & DisposableResolver<T>; export type MessageQueueConsumerModuleOptions = { queueName: string; diOptions: DependencyInjectionOptions; }; export declare function asMessageQueueHandlerClass<T = object>(Type: ClassValue<T>, mqOptions: MessageQueueConsumerModuleOptions, opts?: BuildResolverOptions<T>): BuildResolver<T> & DisposableResolver<T>; export type EnqueuedJobWorkerModuleOptions = { queueName: string; diOptions: DependencyInjectionOptions; }; export declare function asEnqueuedJobWorkerClass<T = object>(Type: ClassValue<T>, workerOptions: EnqueuedJobWorkerModuleOptions, opts?: BuildResolverOptions<T>): BuildResolver<T> & DisposableResolver<T>; /** * Helper function to register a pg-boss job processor class with the DI container. * Handles asyncInit/asyncDispose lifecycle and enabled check based on diOptions. * * @example * ```typescript * enrichUserPresenceJob: asPgBossProcessorClass(EnrichUserPresenceJob, { * diOptions, * queueName: EnrichUserPresenceJob.QUEUE_ID, * }), * ``` */ export declare function asPgBossProcessorClass<T extends { start(): Promise<void>; stop(): Promise<void>; }>(Type: ClassValue<T>, processorOptions: EnqueuedJobWorkerModuleOptions, opts?: BuildResolverOptions<T>): BuildResolver<T> & DisposableResolver<T>; export type PeriodicJobOptions = { jobName: string; diOptions: DependencyInjectionOptions; }; export declare function asPeriodicJobClass<T = object>(Type: ClassValue<T>, workerOptions: PeriodicJobOptions, opts?: BuildResolverOptions<T>): BuildResolver<T> & DisposableResolver<T>; export type JobQueueModuleOptions = { queueName?: string; diOptions: DependencyInjectionOptions; }; export declare function asJobQueueClass<T = object>(Type: ClassValue<T>, queueOptions: JobQueueModuleOptions, opts: BuildResolverOptions<T> & { public: false; }): BuildResolver<T> & DisposableResolver<T>; export declare function asJobQueueClass<T = object>(Type: ClassValue<T>, queueOptions: JobQueueModuleOptions, opts?: BuildResolverOptions<T>): PublicResolver<T>; export declare function asEnqueuedJobQueueManagerFunction<T extends EnqueuedJobQueueManager>(fn: FunctionReturning<T>, diOptions: DependencyInjectionOptions, opts: BuildResolverOptions<T> & { public: false; }): BuildResolver<T> & DisposableResolver<T>; export declare function asEnqueuedJobQueueManagerFunction<T extends EnqueuedJobQueueManager>(fn: FunctionReturning<T>, diOptions: DependencyInjectionOptions, opts?: BuildResolverOptions<T>): PublicResolver<T>; export {};