import 'reflect-metadata' ; /**
 * This is container manager inspirated by Aurelia's one. Will be published on npm and GitHub as solo soon.
 * It works like a context, where all instances are registred as singletons. Then it resolves dependecies
 * and passes singletons to constructor.
 *
 * If you want to use it, just use @autoinject decorator on the class and inside constructor specify what
 * dependencies you want to use.
 */ export class Container { static instance : Container = undefined ; private instances : Map < any , any > = new Map < any , any > ( ) ; private parent : Container ; constructor ( ) { this . set ( Container , this ) ; } get ( key ) { const resolver = ( key instanceof Resolver ) ? key : Singleton . of ( key ) ; let instance = resolver . resolve ( this ) ; if ( ! instance ) { instance = this . createInstance ( key ) ; this . instances . set ( key , instance ) ; } return instance ; } set ( key , value ) { this . instances . set ( key , value ) ; return value ; } has ( key ) { return this . instances . has ( key ) ; } drop ( key ) { return this . instances . delete ( key ) ; } getInstance ( key ) { return this . instances . get ( key ) ; } setRoot ( ) { Container . instance = this ; } isRoot ( ) { return this === Container . instance ; } setParent ( parentContainer ) { this . parent = parentContainer ; } getParent ( ) { return this . parent ; } createInstance ( target ) { let instance ; if ( typeof target !== 'function' ) { console . log ( target ) ; throw new Error ( 'Container cannot make instance of non-function!' ) ; } try { let dependencies = [ ] ; if ( target . __inject && Array . isArray ( target . __inject ) ) { dependencies = target . __inject . map ( x => this . get ( x ) ) ; } instance = new target ( ... dependencies ) ; } catch ( e ) { if ( e instanceof TypeError ) { throw e ; } console . log ( e ) ; } return instance ; } destruct ( ) { this . instances . forEach ( instance => instance . teardown ? instance . teardown ( ) : undefined ) ; } } export function inject ( ... dependecies ) { return function ( target ) : void { target . __inject = dependecies ; } ; } export function autoinject ( potentialTarget ? ) { const deco = function ( target ) { const deps = Reflect [ 'getOwnMetadata' ] ( 'design:paramtypes' , target ) || { } ; target . __inject = deps ; } ; return potentialTarget ? deco ( potentialTarget ) : deco ; } class Resolver { constructor ( ) { } resolve ( container ) { } } export class Instance extends Resolver { constructor ( public key ) { super ( ) ; } static of ( key ) { return new Instance ( key ) ; } resolve ( container ) { return container . createInstance ( this . key ) ; } } export class Singleton extends Resolver { constructor ( public key ) { super ( ) ; } static of ( key ) { return new Singleton ( key ) ; } resolve ( container ) { return container . getInstance ( this . key ) ; } } export class Parent extends Resolver { constructor ( public key ) { super ( ) ; } static of ( key ) { return new Parent ( key ) ; } resolve ( container ) { const parent = container . getParent ( ) ; if ( ! parent ) { throw new Error ( `Parent container not specified!` ) ; } return parent . get ( this . key ) ; } }