opinionated-machine
Version:
Very opinionated DI framework for fastify, built on top of awilix
160 lines (159 loc) • 8.14 kB
TypeScript
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 {};