@@ -29,7 +29,10 @@ export function getProviderScope(provider: AnyProvider) {
2929 return scope
3030}
3131
32- export type Dependencies = Record < string , AnyProvider >
32+ export type Dependencies = Record <
33+ string ,
34+ AnyProvider | { isOptional : true ; provider : AnyProvider }
35+ >
3336
3437export type ResolveProviderType < T extends Provider > = Awaited < T [ 'value' ] >
3538
@@ -38,31 +41,38 @@ export interface Depender<Deps extends Dependencies = {}> {
3841}
3942
4043export type DependencyContext < Deps extends Dependencies > = {
41- [ K in keyof Deps ] : ResolveProviderType < Deps [ K ] >
44+ [ K in keyof Deps as Deps [ K ] extends AnyProvider
45+ ? K
46+ : never ] : Deps [ K ] extends AnyProvider ? ResolveProviderType < Deps [ K ] > : never
47+ } & {
48+ [ K in keyof Deps as Deps [ K ] extends {
49+ isOptional : true
50+ provider : AnyProvider
51+ }
52+ ? K
53+ : never ] ?: Deps [ K ] extends {
54+ isOptional : true
55+ provider : AnyProvider
56+ }
57+ ? ResolveProviderType < Deps [ K ] [ 'provider' ] >
58+ : never
4259}
4360
4461export type ProviderFactoryType <
4562 ProviderType ,
46- ProviderOptions ,
4763 ProviderDeps extends Dependencies ,
48- > = (
49- injections : DependencyContext < ProviderDeps > ,
50- options : ProviderOptions ,
51- ) => ProviderType
64+ > = ( injections : DependencyContext < ProviderDeps > ) => ProviderType
5265
5366export type ProviderDisposeType <
5467 ProviderType ,
55- ProviderOptions ,
5668 ProviderDeps extends Dependencies ,
5769> = (
5870 instance : Awaited < ProviderType > ,
5971 ctx : DependencyContext < ProviderDeps > ,
60- options : ProviderOptions ,
6172) => any
6273
6374export class Provider <
6475 ProviderValue = any ,
65- ProviderOptions = unknown ,
6676 ProviderDeps extends Dependencies = { } ,
6777> implements Depender < ProviderDeps >
6878{
@@ -79,56 +89,34 @@ export class Provider<
7989 readonly value ! : ProviderValue
8090 readonly dependencies : ProviderDeps = { } as ProviderDeps
8191 readonly scope : Scope = Scope . Global
82- readonly factory ! : ProviderFactoryType <
83- ProviderValue ,
84- ProviderOptions ,
85- ProviderDeps
86- >
87- readonly dispose ?: ProviderDisposeType <
88- ProviderValue ,
89- ProviderOptions ,
90- ProviderDeps
91- >
92- readonly options ! : ProviderOptions
92+ readonly factory ! : ProviderFactoryType < ProviderValue , ProviderDeps >
93+ readonly dispose ?: ProviderDisposeType < ProviderValue , ProviderDeps >
9394 readonly description ! : string
9495
9596 withDependencies < Deps extends Dependencies > ( dependencies : Deps ) {
96- const provider = new Provider <
97- ProviderValue ,
98- ProviderOptions ,
99- Merge < ProviderDeps , Deps >
100- > ( )
97+ const provider = new Provider < ProviderValue , Merge < ProviderDeps , Deps > > ( )
10198 return Provider . override ( provider , this , {
10299 dependencies : merge ( this . dependencies , dependencies ) ,
103100 } )
104101 }
105102
106103 withScope < S extends Scope > ( scope : S ) {
107- const provider = new Provider <
108- ProviderValue ,
109- ProviderOptions ,
110- ProviderDeps
111- > ( )
104+ const provider = new Provider < ProviderValue , ProviderDeps > ( )
112105 return Provider . override ( provider , this , { scope } )
113106 }
114107
115- withOptionsType < Options > ( ) {
116- const provider = new Provider < ProviderValue , Options , ProviderDeps > ( )
117- return Provider . override ( provider , this )
118- }
119-
120108 withFactory <
121- F extends ProviderFactoryType < ProviderValue , ProviderOptions , ProviderDeps > ,
109+ F extends ProviderFactoryType < ProviderValue , ProviderDeps > ,
122110 T extends Awaited < ReturnType < F > > ,
123111 > ( factory : F ) {
124- const provider = new Provider < T , ProviderOptions , ProviderDeps > ( )
112+ const provider = new Provider < T , ProviderDeps > ( )
125113 return Provider . override ( provider , this , { factory, value : undefined } )
126114 }
127115
128116 withValue < T extends ProviderValue extends never ? any : ProviderValue > (
129117 value : T ,
130118 ) {
131- const provider = new Provider < T , ProviderOptions , ProviderDeps > ( )
119+ const provider = new Provider < T , ProviderDeps > ( )
132120 return Provider . override ( provider , this , {
133121 value,
134122 factory : undefined ,
@@ -137,31 +125,31 @@ export class Provider<
137125 }
138126
139127 withDisposal ( dispose : this[ 'dispose' ] ) {
140- const provider = new Provider <
141- ProviderValue ,
142- ProviderOptions ,
143- ProviderDeps
144- > ( )
128+ const provider = new Provider < ProviderValue , ProviderDeps > ( )
145129 return Provider . override ( provider , this , { dispose } )
146130 }
147131
148- withOptions ( options : ProviderOptions ) {
149- const provider = new Provider <
150- ProviderValue ,
151- ProviderOptions ,
152- ProviderDeps
153- > ( )
154- return Provider . override ( provider , this , { options } )
155- }
156-
157132 withDescription ( description : string ) {
158- const provider = new Provider <
159- ProviderValue ,
160- ProviderOptions ,
161- ProviderDeps
162- > ( )
133+ const provider = new Provider < ProviderValue , ProviderDeps > ( )
163134 return Provider . override ( provider , this , { description } )
164135 }
136+
137+ optional ( ) {
138+ return {
139+ isOptional : true as const ,
140+ provider : this as Provider < ProviderValue , ProviderDeps > ,
141+ }
142+ }
143+
144+ async resolve (
145+ ...args : keyof ProviderDeps extends never
146+ ? [ ]
147+ : [ DependencyContext < ProviderDeps > ]
148+ ) {
149+ if ( this . value ) return this . value
150+ const [ ctx = { } ] = args
151+ return await this . factory ( ctx as any )
152+ }
165153}
166154
167155export class Container {
@@ -182,8 +170,10 @@ export class Container {
182170 const traverse = ( dependencies : Dependencies ) => {
183171 for ( const key in dependencies ) {
184172 const depender = dependencies [ key ]
185- this . providers . add ( depender )
186- traverse ( depender . dependencies )
173+ const provider =
174+ depender instanceof Provider ? depender : depender . provider
175+ this . providers . add ( provider )
176+ traverse ( provider . dependencies )
187177 }
188178 }
189179
@@ -204,11 +194,11 @@ export class Container {
204194 // to prevent first disposal of a provider
205195 // that other disposing provider depends on
206196 this . application . logger . trace ( 'Disposing [%s] scope context...' , this . scope )
207- for ( const [ { dispose, options , dependencies } , value ] of this . instances ) {
197+ for ( const [ { dispose, dependencies } , value ] of this . instances ) {
208198 if ( dispose ) {
209199 try {
210200 const ctx = await this . createContext ( dependencies )
211- await dispose ( value , ctx , options )
201+ await dispose ( value , ctx )
212202 } catch ( cause ) {
213203 this . application . logger . error (
214204 new Error ( 'Context disposal error. Potential memory leak' , {
@@ -237,13 +227,12 @@ export class Container {
237227 } else if ( this . resolvers . has ( provider ) ) {
238228 return this . resolvers . get ( provider ) !
239229 } else {
240- const { value, factory, scope, dependencies, options } = provider
230+ const { value, factory, scope, dependencies } = provider
241231 if ( typeof value !== 'undefined' ) return Promise . resolve ( value )
242232 if ( this . parent ?. isResolved ( provider ) )
243233 return this . parent . resolve ( provider )
244- // if (typeof factory !== 'function') console.log(provider)
245234 const resolution = this . createContext ( dependencies )
246- . then ( ( ctx ) => factory ( ctx , options ) )
235+ . then ( ( ctx ) => factory ( ctx ) )
247236 . then ( ( instance ) => {
248237 if ( ScopeStrictness [ this . scope ] >= ScopeStrictness [ scope ] )
249238 this . instances . set ( provider , instance )
@@ -271,7 +260,9 @@ export class Container {
271260 const injections : any = { }
272261 const resolvers : Promise < any > [ ] = [ ]
273262 for ( const [ key , dependency ] of Object . entries ( dependencies ) ) {
274- const resolver = this . resolve ( dependency )
263+ const provider =
264+ dependency instanceof Provider ? dependency : dependency . provider
265+ const resolver = this . resolve ( provider )
275266 resolvers . push ( resolver . then ( ( value ) => ( injections [ key ] = value ) ) )
276267 }
277268 await Promise . all ( resolvers )
0 commit comments