1- import { Binding , Container , Module } from " ./types" ;
2- import { createModule } from " ./module" ;
1+ import { Binding , Container , Module } from ' ./types' ;
2+ import { createModule } from ' ./module' ;
33
44export function createContainer ( ) : Container {
55 const modules = new Map < symbol , Module > ( ) ;
66 const singletonInstances = new Map < symbol , unknown > ( ) ;
77 const scopedInstances = new Map < symbol , Map < symbol , unknown > > ( ) ;
8+ const resolutionStack : symbol [ ] = [ ] ;
89 let currentScopeId : symbol | undefined ;
910
10- const DEFAULT_MODULE_KEY = Symbol ( " DEFAULT" ) ;
11+ const DEFAULT_MODULE_KEY = Symbol ( ' DEFAULT' ) ;
1112 const defaultModule = createModule ( ) ;
1213 modules . set ( DEFAULT_MODULE_KEY , defaultModule ) ;
1314
@@ -33,37 +34,49 @@ export function createContainer(): Container {
3334 } ;
3435
3536 const get = < T > ( key : symbol ) : T => {
36- const binding = findLastBinding ( key ) ;
37- if ( ! binding ) throw new Error ( `No binding found for key: ${ key . toString ( ) } ` ) ;
37+ if ( resolutionStack . includes ( key ) ) {
38+ const cycle = [ ...resolutionStack , key ] . map ( ( k ) => k . toString ( ) ) . join ( ' -> ' ) ;
39+ throw new Error ( `Circular dependency detected: ${ cycle } ` ) ;
40+ }
3841
39- const { factory , scope } = binding ;
42+ resolutionStack . push ( key ) ;
4043
41- if ( scope === "singleton" ) {
42- if ( ! singletonInstances . has ( key ) ) {
43- singletonInstances . set ( key , factory ( resolveDependency ) ) ;
44- }
45- return singletonInstances . get ( key ) as T ;
46- }
44+ try {
45+ const binding = findLastBinding ( key ) ;
46+ if ( ! binding ) throw new Error ( `No binding found for key: ${ key . toString ( ) } ` ) ;
4747
48- if ( scope === "transient" ) {
49- return factory ( resolveDependency ) as T ;
50- }
48+ const { factory, scope } = binding ;
5149
52- if ( scope === "scoped" ) {
53- if ( ! currentScopeId ) throw new Error ( `Cannot resolve scoped binding outside of a scope: ${ key . toString ( ) } ` ) ;
50+ if ( scope === 'singleton' ) {
51+ if ( ! singletonInstances . has ( key ) ) {
52+ singletonInstances . set ( key , factory ( resolveDependency ) ) ;
53+ }
54+ return singletonInstances . get ( key ) as T ;
55+ }
5456
55- if ( ! scopedInstances . has ( currentScopeId ) ) {
56- scopedInstances . set ( currentScopeId , new Map < symbol , unknown > ( ) ) ;
57+ if ( scope === 'transient' ) {
58+ return factory ( resolveDependency ) as T ;
5759 }
58- const scopeMap = scopedInstances . get ( currentScopeId ) ! ;
59- if ( ! scopeMap . has ( key ) ) {
60- scopeMap . set ( key , factory ( resolveDependency ) ) ;
60+
61+ if ( scope === 'scoped' ) {
62+ if ( ! currentScopeId )
63+ throw new Error ( `Cannot resolve scoped binding outside of a scope: ${ key . toString ( ) } ` ) ;
64+
65+ if ( ! scopedInstances . has ( currentScopeId ) ) {
66+ scopedInstances . set ( currentScopeId , new Map < symbol , unknown > ( ) ) ;
67+ }
68+ const scopeMap = scopedInstances . get ( currentScopeId ) ! ;
69+ if ( ! scopeMap . has ( key ) ) {
70+ scopeMap . set ( key , factory ( resolveDependency ) ) ;
71+ }
72+
73+ return scopeMap . get ( key ) as T ;
6174 }
6275
63- return scopeMap . get ( key ) as T ;
76+ throw new Error ( `Unknown scope: ${ scope } ` ) ;
77+ } finally {
78+ resolutionStack . pop ( ) ;
6479 }
65-
66- throw new Error ( `Unknown scope: ${ scope } ` ) ;
6780 } ;
6881
6982 const resolveDependency = ( depKey : symbol ) : unknown => {
@@ -72,7 +85,7 @@ export function createContainer(): Container {
7285
7386 const runInScope = < T > ( callback : ( ) => T ) : T => {
7487 const previousScopeId = currentScopeId ;
75- currentScopeId = Symbol ( " scope" ) ;
88+ currentScopeId = Symbol ( ' scope' ) ;
7689 try {
7790 return callback ( ) ;
7891 } finally {
0 commit comments