/******************************************************************************
 * Copyright 2021 TypeFox GmbH
 * This program and the accompanying materials are made available under the
 * terms of the MIT License, which is available in the project root.
 ******************************************************************************/ /**
 * A `Module<I>` is a description of possibly grouped service factories.
 *
 * Given a type I = { group: { service: A } },
 * Module<I> := { group: { service: (injector: I) => A } }
 *
 * Making `I` available during the creation of `I` allows us to create cyclic
 * dependencies.
 */ export type Module < I , T = I > = { [ K in keyof T ] : Module < I , T [ K ] > | ( ( injector : I ) => T [ K ] ) ; } ; param module1 param module2 param module3 param module4 returns export function inject < I1 , I2 , I3 , I4 , I extends I1 & I2 & I3 & I4 > ( module1 , module2 ? , module3 ? , module4 ? ) { const module = [ module1 , module2 , module3 , module4 ] . reduce ( _merge , { } ) as Module < I > ; return _inject ( module ) ; } /**
 * Helper function that returns an injector by creating a proxy.
 * Invariant: injector is of type I. If injector is undefined, then T = I.
 */ function _inject < I , T > ( module , injector ? ) { const proxy = new Proxy ( { } as any , { deleteProperty : ( ) => false , get : ( obj , prop ) => _resolve ( obj , prop , module , injector || proxy ) , getOwnPropertyDescriptor : ( obj , prop ) => ( _resolve ( obj , prop , module , injector || proxy ) , Object . getOwnPropertyDescriptor ( obj , prop ) ) , has : ( _ , prop ) => prop in module , ownKeys : ( ) => Reflect . ownKeys ( module ) } ) ; return proxy ; } /**
 * Internally used to tag a requested dependency, directly before calling the factory.
 * This allows us to find cycles during instance creation.
 */ const __requested__ = Symbol ( ) ; param obj param prop param module param injector returns throws function _resolve < I , T > ( obj , prop , module , injector ) { if ( prop in obj ) { if ( obj [ prop ] === __requested__ ) { throw new Error ( 'Cycle detected. Please make "' + String ( prop ) + '" lazy. See https://langium.org/docs/di/cyclic-dependencies' ) ; } return obj [ prop ] ; } else if ( prop in module ) { const value = module [ prop as keyof T ] ; obj [ prop ] = __requested__ ; obj [ prop ] = ( typeof value === 'function' ) ? value ( injector ) : _inject ( value , injector ) ; return obj [ prop ] ; } else { return undefined ; } } param target param source returns function _merge ( target , source ? ) { if ( source ) { for ( const [ key , value2 ] of Object . entries ( source ) ) { if ( value2 !== undefined ) { const value1 = target [ key ] ; if ( value1 !== null && value2 !== null && typeof value1 === 'object' && typeof value2 === 'object' ) { target [ key ] = _merge ( value1 , value2 ) ; } else { target [ key ] = value2 ; } } } } return target ; }