@@ -44,13 +44,12 @@ export function Inject<T extends ({ (): Class } | Class)[]>(...tokens: T) {
4444export class InjectError extends ScopeError { }
4545export class InjectCircularDependencyError extends InjectError {
4646 constructor (
47- readonly dependency : Class ,
47+ readonly chain : { index ?: number ; target : Class } [ ] ,
4848 readonly target : Class ,
49- readonly index : number ,
5049 options ?: ErrorOptions ,
5150 ) {
5251 super (
53- `Circular dependencies detected while resolving dependency # ${ index } of #${ target } ` ,
52+ `Circular dependencies detected while resolving dependencies of #${ target } ` ,
5453 options ,
5554 ) ;
5655 }
@@ -169,27 +168,86 @@ export class Container {
169168 #get< T > ( key : Class < Token < T > > | Class < T > , invocation : Container ) : T {
170169 if ( this . #values. has ( key ) ) return this . #values. get ( key ) as T ;
171170
172- const scope = this . #scopes. get ( key ) ;
173- let scoped : Container | undefined = invocation ;
174- if ( scope ) {
175- while ( scoped && scope !== scoped . #scope) scoped = scoped . #parent;
176- if ( ! scoped ) throw new ContainerUndefinedScopeError ( scope ) ;
171+ if ( this . #generators. has ( key ) ) {
172+ const scope = this . #scopes. get ( key ) ;
173+ const _scoped = scope ? invocation . #scoped( scope ) : this ;
174+ return this . #generators. get ( key ) ! ( ) as T ;
177175 }
178- if ( scoped && scoped . #values. has ( key ) ) return scoped . #values. get ( key ) as T ;
179-
180- scoped = scope ? ( scoped ?? this ) : this ;
181- if ( this . #generators. has ( key ) ) return this . #generators. get ( key ) ! ( ) as T ;
182176
183177 if ( this . #resolvers. has ( key ) ) {
184- const value = this . #resolvers. get ( key ) ! ( ) ;
178+ const scope = this . #scopes. get ( key ) ;
179+ const scoped = scope ? invocation . #scoped( scope ) : this ;
180+ const value = this . #resolvers. get ( key ) ! ( ) as T ;
181+
185182 scoped . #values. set ( key , value ) ;
186- return scoped . #values . get ( key ) as T ;
183+ return value ;
187184 }
188185
189186 if ( this . #parent) return this . #parent. #get( key , invocation ) ;
187+ if ( injects . has ( key ) ) {
188+ // Detect & handle circular dependencies
189+ if ( invocation . #creating. some ( ( { target } ) => key === target ) ) {
190+ const chain = invocation . #creating. map ( ( _ ) => ( {
191+ ..._ ,
192+ target : _ . target ?? key ,
193+ } ) ) ;
194+ invocation . #creating. splice ( 0 ) ;
195+ throw new InjectCircularDependencyError ( chain , key ) ;
196+ }
197+
198+ let value : T ;
199+ const args : unknown [ ] = [ ] ;
200+ const tokens = injects . get ( key ) ! ;
201+
202+ // Register the target (should be first) in #creating)
203+ if ( ! invocation . #creating. length )
204+ invocation . #creating. push ( { target : key } ) ;
205+ else if ( ! invocation . #creating. at ( - 1 ) ?. target )
206+ invocation . #creating. at ( - 1 ) ! . target = key ;
207+
208+ for ( let i = 0 , l = tokens . length ; i < l ; i ++ ) {
209+ const token =
210+ false ===
211+ Reflect . getOwnPropertyDescriptor ( tokens [ i ] , "prototype" ) ?. writable
212+ ? ( tokens [ i ] as Class )
213+ : ( tokens [ i ] as ( ) => Class ) ( ) ;
214+ // Register the target's dependency
215+ invocation . #creating. push ( { index : i } ) ;
216+
217+ try {
218+ args . push ( invocation . #get( token , invocation ) ) ;
219+ invocation . #creating. pop ( ) ;
220+ } catch ( err ) {
221+ invocation . #creating. splice ( 0 ) ;
222+
223+ if ( err instanceof ContainerUndefinedKeyError )
224+ throw new InjectMissingDependencyError ( token , key , i , {
225+ cause : err ,
226+ } ) ;
227+ throw err ;
228+ }
229+ }
230+
231+ try {
232+ value = Reflect . construct ( key as Class , args ) as T ;
233+ } finally {
234+ invocation . #creating. pop ( ) ;
235+ }
236+ this . #values. set ( key , value ) ;
237+ return value as T ;
238+ }
190239
191240 throw new ContainerUndefinedKeyError ( key ) ;
192241 }
242+ readonly #creating = [ ] as { target ?: Class ; index ?: number } [ ] ;
243+
244+ #scoped( scope : symbol ) : Container {
245+ let container = this as undefined | Container ;
246+ while ( container && scope !== container . #scope)
247+ container = container . #parent;
248+ if ( ! container ) throw new ContainerUndefinedScopeError ( scope ) ;
249+ return container ;
250+ }
193251
194252 /**
195253 * Register a value generator for the given {@linkcode key}.
0 commit comments