import type { Factorable, FactorableWithArgs } from '../interfaces/index.mjs' import type { ClassTypeWithInstance, InjectionTokenSchemaType, } from '../token/injection-token.mjs' import type { Registry } from '../token/registry.mjs' import { InjectableScope, InjectableType } from '../enums/index.mjs' import { InjectableTokenMeta } from '../symbols/index.mjs' import { InjectionToken } from '../token/injection-token.mjs' import { globalRegistry } from '../token/registry.mjs' export interface FactoryOptions { scope?: InjectableScope token?: InjectionToken registry?: Registry priority?: number } // #1 Factory without arguments export function Factory(options?: { scope?: InjectableScope registry?: Registry priority?: number }): >>( target: T, context?: ClassDecoratorContext, ) => T // #2 Factory with typed token export function Factory(options: { scope?: InjectableScope token: InjectionToken registry?: Registry priority?: number }): R extends undefined ? never : S extends InjectionTokenSchemaType ? >>( target: T, context?: ClassDecoratorContext, ) => T : S extends undefined ? >>( target: T, context?: ClassDecoratorContext, ) => T : never export function Factory({ scope = InjectableScope.Singleton, token, registry = globalRegistry, priority = 0, }: FactoryOptions = {}) { return < T extends ClassTypeWithInstance< Factorable | FactorableWithArgs >, >( target: T, context?: ClassDecoratorContext, ): T => { if ( (context && context.kind !== 'class') || (target instanceof Function && !context) ) { throw new Error( '[DI] @Factory decorator can only be used on classes.', ) } let injectableToken: InjectionToken = token ?? InjectionToken.create(target) registry.set(injectableToken, scope, target, InjectableType.Factory, priority) // @ts-expect-error target[InjectableTokenMeta] = injectableToken return target } }