@lppedd/di-wise-neo 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -18,7 +18,7 @@
18
18
  > in part thanks to TypeScript's experimental decorators. Shout out to [@exuanbo](https://github.com/exuanbo)
19
19
  > for the strong foundations!
20
20
 
21
- ## Table of Contents
21
+ ### Table of Contents
22
22
 
23
23
  - [Installation](#installation)
24
24
  - [API reference](#api-reference)
@@ -31,7 +31,7 @@
31
31
  - [Behavioral decorators](#behavioral-decorators)
32
32
  - [Testing support](#testing-support)
33
33
 
34
- ## Why yet another library
34
+ ### Why yet another library
35
35
 
36
36
  I've been developing VS Code extensions for a while as part of my daily work.
37
37
  It's enjoyable work! However, extensions always reach that tipping point where
@@ -444,7 +444,7 @@ export class ExtensionContext {
444
444
 
445
445
  ## Behavioral decorators
446
446
 
447
- The library includes three behavioral decorators that influence how classes are registered in the container.
447
+ The library includes four behavioral decorators that influence how classes are registered in the container.
448
448
  These decorators attach metadata to the class type, which is then interpreted by the container during registration.
449
449
 
450
450
  ### `@Scoped`
@@ -473,6 +473,32 @@ container.register(
473
473
 
474
474
  In this example, `ExtensionContext` will be registered with **Resolution** scope instead.
475
475
 
476
+ ### `@Named`
477
+
478
+ Marks a class or injected dependency with a unique name (qualifier), allowing the container
479
+ to distinguish between multiple implementations of the same type.
480
+
481
+ ```ts
482
+ @Named("persistent")
483
+ @Scoped(Scope.Container)
484
+ export class PersistentSecretStorage implements SecretStorage {
485
+ /* ... */
486
+ }
487
+
488
+ // Register the class with Type<SecretStorage>.
489
+ // The container will automatically qualify the registration with 'persistent'.
490
+ container.register(ISecretStorage, { useClass: PersistentSecretStorage });
491
+
492
+ // Inject the SecretStorage dependency by name
493
+ export class ExtensionContext {
494
+ constructor(@Inject(ISecretStorage) @Named("persistent") readonly secretStorage: SecretStorage) {}
495
+
496
+ /* ... */
497
+ }
498
+ ```
499
+
500
+ The container will throw an error at registration time if the name is already taken by another registration.
501
+
476
502
  ### `@AutoRegister`
477
503
 
478
504
  Enables automatic registration of the decorated class when it is resolved,
@@ -62,22 +62,81 @@ declare function createType<T>(typeName: string): Type<T>;
62
62
  * Provides a class instance for a token via a class constructor.
63
63
  */
64
64
  interface ClassProvider<Instance extends object> {
65
+ /**
66
+ * The class to instantiate for the token.
67
+ */
65
68
  readonly useClass: Constructor<Instance>;
69
+ /**
70
+ * An optional name to qualify this provider.
71
+ * If specified, the token must be resolved using the same name.
72
+ *
73
+ * Equivalent to decorating the class with `@Named(...)`.
74
+ *
75
+ * @example
76
+ * ```ts
77
+ * export class ExtensionContext {
78
+ * // Decorator-based injection
79
+ * constructor(@Inject(ISecretStorage) @Named("persistent") secretStorage: SecretStorage) {}
80
+ *
81
+ * // Function-based injection
82
+ * constructor(secretStorage = inject(ISecretStorage, "persistent")) {}
83
+ * }
84
+ * ```
85
+ */
86
+ readonly name?: string;
66
87
  }
67
88
  /**
68
89
  * Provides a value for a token via a factory function.
69
- *
70
- * The factory function runs inside the injection context and can
71
- * thus access dependencies via {@link inject}-like functions.
72
90
  */
73
91
  interface FactoryProvider<Value> {
92
+ /**
93
+ * A function that produces the value at resolution time.
94
+ *
95
+ * The function runs inside the injection context and can
96
+ * access dependencies via {@link inject}-like helpers.
97
+ */
74
98
  readonly useFactory: (...args: []) => Value;
99
+ /**
100
+ * An optional name to qualify this provider.
101
+ * If specified, the token must be resolved using the same name.
102
+ *
103
+ * @example
104
+ * ```ts
105
+ * export class ExtensionContext {
106
+ * // Decorator-based injection
107
+ * constructor(@Inject(ISecretStorage) @Named("persistent") secretStorage: SecretStorage) {}
108
+ *
109
+ * // Function-based injection
110
+ * constructor(secretStorage = inject(ISecretStorage, "persistent")) {}
111
+ * }
112
+ * ```
113
+ */
114
+ readonly name?: string;
75
115
  }
76
116
  /**
77
117
  * Provides a static - already constructed - value for a token.
78
118
  */
79
119
  interface ValueProvider<T> {
120
+ /**
121
+ * The static value to associate with the token.
122
+ */
80
123
  readonly useValue: T;
124
+ /**
125
+ * An optional name to qualify this provider.
126
+ * If specified, the token must be resolved using the same name.
127
+ *
128
+ * @example
129
+ * ```ts
130
+ * export class ExtensionContext {
131
+ * // Decorator-based injection
132
+ * constructor(@Inject(ISecretStorage) @Named("persistent") secretStorage: SecretStorage) {}
133
+ *
134
+ * // Function-based injection
135
+ * constructor(secretStorage = inject(ISecretStorage, "persistent")) {}
136
+ * }
137
+ * ```
138
+ */
139
+ readonly name?: string;
81
140
  }
82
141
  /**
83
142
  * Aliases another registered token.
@@ -85,6 +144,9 @@ interface ValueProvider<T> {
85
144
  * Resolving this token will return the value of the aliased one.
86
145
  */
87
146
  interface ExistingProvider<Value> {
147
+ /**
148
+ * The existing token to alias.
149
+ */
88
150
  readonly useExisting: Token<Value>;
89
151
  }
90
152
  /**
@@ -212,77 +274,33 @@ interface Container {
212
274
  /**
213
275
  * Returns whether the token is registered in this container or in parent containers, if any.
214
276
  */
215
- isRegistered(token: Token): boolean;
216
- /**
217
- * Registers a concrete class, where the class acts as its own token.
218
- *
219
- * Tokens provided via the {@link Injectable} decorator applied to the class
220
- * are also registered as aliases.
221
- *
222
- * The default registration scope is determined by the {@link Scoped} decorator,
223
- * if present.
224
- */
225
- registerClass<Instance extends object>(Class: Constructor<Instance>): void;
226
- /**
227
- * Registers a concrete class with a token.
228
- *
229
- * The default registration scope is determined by the {@link Scoped} decorator
230
- * applied to the class, if present, but it can be overridden by passing explicit
231
- * registration options.
232
- */
233
- registerClass<Instance extends object, ProvidedInstance extends Instance>(token: Token<Instance>, Class: Constructor<ProvidedInstance>, options?: RegistrationOptions): void;
234
- /**
235
- * Registers a token whose value is produced by a factory function.
236
- *
237
- * The factory function runs inside the injection context and can
238
- * thus access dependencies via {@link inject}-like functions.
239
- */
240
- registerFactory<Value, ProvidedValue extends Value>(token: Token<Value>, factory: (...args: []) => ProvidedValue, options?: RegistrationOptions): void;
241
- /**
242
- * Registers a token with a fixed value.
243
- *
244
- * The provided value is returned as-is when the token is resolved (scopes do not apply).
245
- */
246
- registerValue<Value, ProvidedValue extends Value>(token: Token<Value>, value: ProvidedValue): void;
247
- /**
248
- * Registers one or more tokens as aliases for a target token.
249
- *
250
- * When an alias is resolved, the target token is resolved instead.
251
- */
252
- registerAlias<Value, ProvidedValue extends Value>(targetToken: Token<ProvidedValue>, aliasTokens: Tokens<Value>): void;
277
+ isRegistered(token: Token, name?: string): boolean;
253
278
  /**
254
279
  * Registers a {@link ClassProvider}, using the class itself as its token.
255
280
  *
256
281
  * Tokens provided via the {@link Injectable} decorator applied to the class
257
282
  * are also registered as aliases.
258
283
  *
259
- * The scope is determined by the {@link Scoped} decorator, if present.
260
- *
261
- * @see registerClass
284
+ * The scope is determined by the {@link Scoped} decorator - if present -
285
+ * or by the {@link ContainerOptions.defaultScope} value.
262
286
  */
263
287
  register<Instance extends object>(Class: Constructor<Instance>): Container;
264
288
  /**
265
289
  * Registers a {@link ClassProvider} with a token.
266
290
  *
267
291
  * The default registration scope is determined by the {@link Scoped} decorator
268
- * applied to the provided class, if present, but it can be overridden by
269
- * passing explicit registration options.
270
- *
271
- * @see registerClass
292
+ * applied to the provided class - if present - or by the {@link ContainerOptions.defaultScope}
293
+ * value, but it can be overridden by passing explicit registration options.
272
294
  */
273
295
  register<Instance extends object, ProviderInstance extends Instance>(token: Token<Instance>, provider: ClassProvider<ProviderInstance>, options?: RegistrationOptions): Container;
274
296
  /**
275
297
  * Registers a {@link FactoryProvider} with a token.
276
- *
277
- * @see registerFactory
278
298
  */
279
299
  register<Value, ProviderValue extends Value>(token: Token<Value>, provider: FactoryProvider<ProviderValue>, options?: RegistrationOptions): Container;
280
300
  /**
281
301
  * Registers an {@link ExistingProvider} with a token.
282
302
  *
283
303
  * The token will alias the one set in `useExisting`.
284
- *
285
- * @see registerAlias
286
304
  */
287
305
  register<Value, ProviderValue extends Value>(token: Token<Value>, provider: ExistingProvider<ProviderValue>): Container;
288
306
  /**
@@ -290,8 +308,6 @@ interface Container {
290
308
  *
291
309
  * Values provided via `useValue` are never cached (scopes do not apply)
292
310
  * and are simply returned as-is.
293
- *
294
- * @see registerValue
295
311
  */
296
312
  register<Value, ProviderValue extends Value>(token: Token<Value>, provider: ValueProvider<ProviderValue>): Container;
297
313
  /**
@@ -302,7 +318,7 @@ interface Container {
302
318
  *
303
319
  * Note that only this container is affected. Parent containers, if any, remain unchanged.
304
320
  */
305
- unregister<Value>(token: Token<Value>): Value[];
321
+ unregister<Value>(token: Token<Value>, name?: string): Value[];
306
322
  /**
307
323
  * Resolves the given class to the instance associated with it.
308
324
  *
@@ -328,9 +344,10 @@ interface Container {
328
344
  * If the class is registered with _container_ scope, the resolved instance is cached
329
345
  * in the container's internal registry.
330
346
  */
331
- resolve<Instance extends object>(Class: Constructor<Instance>, optional?: false): Instance;
332
- resolve<Instance extends object>(Class: Constructor<Instance>, optional: true): Instance | undefined;
333
- resolve<Instance extends object>(Class: Constructor<Instance>, optional?: boolean): Instance | undefined;
347
+ resolve<Instance extends object>(Class: Constructor<Instance>, name?: string): Instance;
348
+ resolve<Instance extends object>(Class: Constructor<Instance>, optional?: false, name?: string): Instance;
349
+ resolve<Instance extends object>(Class: Constructor<Instance>, optional: true, name?: string): Instance | undefined;
350
+ resolve<Instance extends object>(Class: Constructor<Instance>, optional?: boolean, name?: string): Instance | undefined;
334
351
  /**
335
352
  * Resolves the given token to the value associated with it.
336
353
  *
@@ -349,9 +366,10 @@ interface Container {
349
366
  * If the token is registered with _container_ scope, the resolved value is cached
350
367
  * in the container's internal registry.
351
368
  */
352
- resolve<Value>(token: Token<Value>, optional?: false): Value;
353
- resolve<Value>(token: Token<Value>, optional: true): Value | undefined;
354
- resolve<Value>(token: Token<Value>, optional?: boolean): Value | undefined;
369
+ resolve<Value>(token: Token<Value>, name?: string): Value;
370
+ resolve<Value>(token: Token<Value>, optional?: false, name?: string): Value;
371
+ resolve<Value>(token: Token<Value>, optional: true, name?: string): Value | undefined;
372
+ resolve<Value>(token: Token<Value>, optional?: boolean, name?: string): Value | undefined;
355
373
  /**
356
374
  * Resolves the given class to all instances provided by the registrations associated with it.
357
375
  *
@@ -447,7 +465,7 @@ declare function AutoRegister(): ClassDecorator;
447
465
  *
448
466
  * // Wizard is registered with Container scope, and an instance
449
467
  * // is immediately created and cached by the container
450
- * const wizard = container.register(Wizard);
468
+ * container.register(Wizard);
451
469
  * ```
452
470
  *
453
471
  * @__NO_SIDE_EFFECTS__
@@ -568,6 +586,26 @@ declare function InjectAll<Value>(token: Token<Value>): ParameterDecorator;
568
586
  */
569
587
  declare function InjectAll<Value>(tokens: TokenRef<Value>): ParameterDecorator;
570
588
 
589
+ /**
590
+ * Qualifies a class or an injected parameter with a unique name.
591
+ *
592
+ * This allows the container to distinguish between multiple implementations
593
+ * of the same interface or type during registration and injection.
594
+ *
595
+ * @example
596
+ * ```ts
597
+ * @Named("dumbledore")
598
+ * class Dumbledore implements Wizard {}
599
+ *
600
+ * // Register Dumbledore with Type<Wizard>
601
+ * container.register(IWizard, { useClass: Dumbledore });
602
+ * const dumbledore = container.resolve(IWizard, "dumbledore");
603
+ * ```
604
+ *
605
+ * @__NO_SIDE_EFFECTS__
606
+ */
607
+ declare function Named(name: string): ClassDecorator & ParameterDecorator;
608
+
571
609
  /**
572
610
  * Parameter decorator that injects the instance associated with the given class,
573
611
  * or `undefined` if the class is not registered in the container.
@@ -656,13 +694,13 @@ declare function Scoped(scope: Scope): ClassDecorator;
656
694
  *
657
695
  * Throws an error if the class is not registered in the container.
658
696
  */
659
- declare function inject<Instance extends object>(Class: Constructor<Instance>): Instance;
697
+ declare function inject<Instance extends object>(Class: Constructor<Instance>, name?: string): Instance;
660
698
  /**
661
699
  * Injects the value associated with the given token.
662
700
  *
663
701
  * Throws an error if the token is not registered in the container.
664
702
  */
665
- declare function inject<Value>(token: Token<Value>): Value;
703
+ declare function inject<Value>(token: Token<Value>, name?: string): Value;
666
704
  /**
667
705
  * Injects the instance associated with the given class.
668
706
  *
@@ -684,8 +722,9 @@ declare function inject<Value>(token: Token<Value>): Value;
684
722
  *
685
723
  * @param thisArg - The containing instance, used to help resolve circular dependencies.
686
724
  * @param Class - The class to resolve.
725
+ * @param name - The name qualifier of the class to resolve.
687
726
  */
688
- declare function injectBy<Instance extends object>(thisArg: any, Class: Constructor<Instance>): Instance;
727
+ declare function injectBy<Instance extends object>(thisArg: any, Class: Constructor<Instance>, name?: string): Instance;
689
728
  /**
690
729
  * Injects the value associated with the given token.
691
730
  *
@@ -707,8 +746,9 @@ declare function injectBy<Instance extends object>(thisArg: any, Class: Construc
707
746
  *
708
747
  * @param thisArg - The containing instance, used to help resolve circular dependencies.
709
748
  * @param token - The token to resolve.
749
+ * @param name - The name qualifier of the token to resolve.
710
750
  */
711
- declare function injectBy<Value>(thisArg: any, token: Token<Value>): Value;
751
+ declare function injectBy<Value>(thisArg: any, token: Token<Value>, name?: string): Value;
712
752
 
713
753
  /**
714
754
  * Injects all instances provided by the registrations associated with the given class.
@@ -865,5 +905,5 @@ interface Middleware {
865
905
  */
866
906
  declare function applyMiddleware(container: Container, middlewares: Middleware[]): Container;
867
907
 
868
- export { AutoRegister, EagerInstantiate, Inject, InjectAll, Injectable, Injector, Optional, OptionalAll, Scope, Scoped, applyMiddleware, build, createContainer, createType, forwardRef, inject, injectAll, injectBy, setClassIdentityMapping };
908
+ export { AutoRegister, EagerInstantiate, Inject, InjectAll, Injectable, Injector, Named, Optional, OptionalAll, Scope, Scoped, applyMiddleware, build, createContainer, createType, forwardRef, inject, injectAll, injectBy, setClassIdentityMapping };
869
909
  export type { ClassProvider, Constructor, Container, ContainerOptions, ExistingProvider, FactoryProvider, Middleware, MiddlewareComposer, Provider, RegistrationOptions, Token, TokenRef, Tokens, TokensRef, Type, ValueProvider };