import { ON_DEACTIVATION_ERROR , POST_CONSTRUCT_ERROR , PRE_DESTROY_ERROR } from "../constants/error_msgs" ; import { BindingScopeEnum , TargetTypeEnum } from "../constants/literal_types" ; import * as METADATA_KEY from "../constants/metadata_keys" ; import { interfaces } from "../interfaces/interfaces" ; import { Metadata } from "../planning/metadata" ; import { isPromise , isPromiseOrContainsPromise } from "../utils/async" ; interface InstanceCreationInstruction { constructorInjections : unknown [ ] ; propertyInjections : unknown [ ] ; propertyRequests : interfaces . Request [ ] ; } interface ResolvedRequests extends InstanceCreationInstruction { isAsync : boolean ; } interface CreateInstanceWithInjectionArg < T > extends InstanceCreationInstruction { constr : interfaces . Newable < T > ; } function _resolveRequests ( childRequests , resolveRequest ) { return childRequests . reduce < ResolvedRequests > ( ( resolvedRequests , childRequest ) => { const injection = resolveRequest ( childRequest ) ; const targetType = childRequest . target . type ; if ( targetType === TargetTypeEnum . ConstructorArgument ) { resolvedRequests . constructorInjections . push ( injection ) ; } else { resolvedRequests . propertyRequests . push ( childRequest ) ; resolvedRequests . propertyInjections . push ( injection ) ; } if ( ! resolvedRequests . isAsync ) { resolvedRequests . isAsync = isPromiseOrContainsPromise ( injection ) ; } return resolvedRequests ; } , { constructorInjections : [ ] , propertyInjections : [ ] , propertyRequests : [ ] , isAsync : false } ) ; } function _createInstance < T > ( constr , childRequests , resolveRequest ) { let result ; if ( childRequests . length > 0 ) { const resolved = _resolveRequests ( childRequests , resolveRequest ) ; const createInstanceWithInjectionsArg = { ... resolved , constr } ; if ( resolved . isAsync ) { result = createInstanceWithInjectionsAsync ( createInstanceWithInjectionsArg ) ; } else { result = createInstanceWithInjections ( createInstanceWithInjectionsArg ) ; } } else { result = new constr ( ) ; } return result ; } function createInstanceWithInjections < T > ( args ) { const instance = new args . constr ( ... args . constructorInjections as never [ ] ) ; args . propertyRequests . forEach ( ( r , index ) => { const property = r . target . identifier ; const injection = args . propertyInjections [ index ] ; ( instance as Record < string | symbol , unknown > ) [ property ] = injection ; } ) ; return instance ; } async function createInstanceWithInjectionsAsync < T > ( args ) { const constructorInjections = await possiblyWaitInjections ( args . constructorInjections ) ; const propertyInjections = await possiblyWaitInjections ( args . propertyInjections ) ; return createInstanceWithInjections < T > ( { ... args , constructorInjections , propertyInjections } ) ; } async function possiblyWaitInjections ( possiblePromiseinjections ) { const injections = [ ] ; for ( const injection of possiblePromiseinjections ) { if ( Array . isArray ( injection ) ) { injections . push ( Promise . all ( injection ) ) ; } else { injections . push ( injection ) ; } } return Promise . all ( injections ) ; } function _getInstanceAfterPostConstruct < T > ( constr , result ) { const postConstructResult = _postConstruct ( constr , result ) ; if ( isPromise ( postConstructResult ) ) { return postConstructResult . then ( ( ) => result ) ; } else { return result ; } } function _postConstruct < T > ( constr , instance ) { if ( Reflect . hasMetadata ( METADATA_KEY . POST_CONSTRUCT , constr ) ) { const data = Reflect . getMetadata ( METADATA_KEY . POST_CONSTRUCT , constr ) ; try { return ( instance as T & Record < string , ( ) => void > ) [ ( data . value as string ) ] ?. ( ) ; } catch ( e ) { throw new Error ( POST_CONSTRUCT_ERROR ( constr . name , e . message ) ) ; } } } function _validateInstanceResolution < T = unknown > ( binding , constr ) { if ( binding . scope !== BindingScopeEnum . Singleton ) { _throwIfHandlingDeactivation ( binding , constr ) ; } } function _throwIfHandlingDeactivation < T = unknown > ( binding , constr ) { const scopeErrorMessage = `Class cannot be instantiated in ${ binding . scope === BindingScopeEnum . Request ? "request" : "transient" } scope.` ; if ( typeof binding . onDeactivation === "function" ) { throw new Error ( ON_DEACTIVATION_ERROR ( constr . name , scopeErrorMessage ) ) ; } if ( Reflect . hasMetadata ( METADATA_KEY . PRE_DESTROY , constr ) ) { throw new Error ( PRE_DESTROY_ERROR ( constr . name , scopeErrorMessage ) ) ; } } function resolveInstance < T > ( binding , constr , childRequests , resolveRequest ) { _validateInstanceResolution ( binding , constr ) ; const result = _createInstance ( constr , childRequests , resolveRequest ) ; if ( isPromise ( result ) ) { return result . then ( ( resolvedResult ) => _getInstanceAfterPostConstruct ( constr , resolvedResult ) ) ; } else { return _getInstanceAfterPostConstruct ( constr , result ) ; } } export { resolveInstance } ;