@lppedd/di-wise-neo 0.15.1 → 0.17.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
@@ -8,6 +8,7 @@
8
8
  [![status](https://img.shields.io/badge/status-beta-AC29EC)](https://github.com/lppedd/di-wise-neo/blob/main/CHANGELOG.md#0100)
9
9
  [![build](https://img.shields.io/github/actions/workflow/status/lppedd/di-wise-neo/test.yml.svg?branch=main)](https://github.com/lppedd/di-wise-neo/actions/workflows/test.yml)
10
10
  [![coverage](https://img.shields.io/codecov/c/github/lppedd/di-wise-neo/main?token=R9XZFTQ0BA)](https://app.codecov.io/gh/lppedd/di-wise-neo/tree/main/src)
11
+ [![minified size](https://img.shields.io/bundlejs/size/@lppedd/di-wise-neo)](https://bundlejs.com/?q=@lppedd/di-wise-neo)
11
12
  [![license](https://img.shields.io/badge/license-MIT-F7F7F7)](https://github.com/lppedd/di-wise-neo/blob/main/LICENSE)
12
13
 
13
14
  </div>
@@ -152,9 +153,9 @@ export class ContributionRegistrar {
152
153
 
153
154
  // Create a new DI container
154
155
  const container = createContainer({
155
- // Optionally override the default "transient" registration scope.
156
- // I prefer to use "container" (a.k.a. singleton) scope, but "transient" is the better default.
157
- defaultScope: Scope.Container,
156
+ // Optionally override the default "Transient" registration scope.
157
+ // I prefer to use "Container" (a.k.a. Singleton) scope, but "Transient" is the better default.
158
+ defaultScope: "Container",
158
159
  });
159
160
 
160
161
  // Register our managed dependencies in the container
@@ -211,7 +212,7 @@ An explicit **scope** can be specified using the third argument, when applicable
211
212
  If omitted, the default scope is **Transient**.
212
213
 
213
214
  ```ts
214
- container.register(token, provider, { scope: Scope.Resolution });
215
+ container.register(token, provider, { scope: "Resolution" });
215
216
  ```
216
217
 
217
218
  ### ClassProvider
@@ -455,13 +456,13 @@ These decorators attach metadata to the class type, which is then interpreted by
455
456
  Specifies a default scope for the decorated class:
456
457
 
457
458
  ```ts
458
- @Scoped(Scope.Container)
459
+ @Scoped("Container")
459
460
  export class ExtensionContext {
460
461
  /* ... */
461
462
  }
462
463
  ```
463
464
 
464
- Applying `@Scoped(Scope.Container)` to the `ExtensionContext` class instructs the DI container
465
+ Applying `@Scoped("Container")` to the `ExtensionContext` class instructs the DI container
465
466
  to register it with the **Container** scope by default.
466
467
 
467
468
  This default can be overridden by explicitly providing registration options:
@@ -470,7 +471,7 @@ This default can be overridden by explicitly providing registration options:
470
471
  container.register(
471
472
  ExtensionContext,
472
473
  { useClass: ExtensionContext },
473
- { scope: Scope.Resolution },
474
+ { scope: "Resolution" },
474
475
  );
475
476
  ```
476
477
 
@@ -483,7 +484,7 @@ to distinguish between multiple implementations of the same type.
483
484
 
484
485
  ```ts
485
486
  @Named("persistent")
486
- @Scoped(Scope.Container)
487
+ @Scoped("Container")
487
488
  export class PersistentSecretStorage implements SecretStorage {
488
489
  /* ... */
489
490
  }
@@ -50,7 +50,7 @@ declare function build<Value>(factory: (...args: []) => Value): Type<Value>;
50
50
  /**
51
51
  * An injectable type `T`.
52
52
  */
53
- interface Type<T> {
53
+ interface Type<T> extends ParameterDecorator {
54
54
  /**
55
55
  * The name of the type.
56
56
  */
@@ -63,6 +63,10 @@ interface Type<T> {
63
63
  * @private
64
64
  */
65
65
  readonly __type: T | undefined;
66
+ /**
67
+ * Returns the type's {@link Type.name|name}.
68
+ */
69
+ readonly toString: () => string;
66
70
  }
67
71
  /**
68
72
  * An injectable type `T` with a default {@link Provider} and optional default registration options.
@@ -87,11 +91,11 @@ interface Constructor<Instance extends object> {
87
91
  /**
88
92
  * Token type.
89
93
  */
90
- type Token<Value = any> = [Value] extends [object] ? Type<Value> | Constructor<Value & object> : Type<Value>;
94
+ type Token<Value> = [Value] extends [object] ? Type<Value> | Constructor<Value & object> : Type<Value>;
91
95
  /**
92
96
  * Describes a {@link Token} array with at least one element.
93
97
  */
94
- type Tokens<Value = any> = [Token<Value>, ...Token<Value>[]];
98
+ type Tokens<Value> = [Token<Value>, ...Token<Value>[]];
95
99
  /**
96
100
  * Creates a type token.
97
101
  *
@@ -122,10 +126,10 @@ declare function createType<T>(typeName: string, provider: Provider<T>, options?
122
126
  interface ClassRef<Instance extends object> {
123
127
  readonly getRefClass: () => Constructor<Instance>;
124
128
  }
125
- interface TokensRef<Value = any> {
129
+ interface TokensRef<Value> {
126
130
  readonly getRefTokens: () => Set<Token<Value>>;
127
131
  }
128
- interface TokenRef<Value = any> {
132
+ interface TokenRef<Value> {
129
133
  readonly getRefToken: () => Token<Value>;
130
134
  }
131
135
  /**
@@ -268,27 +272,60 @@ interface ExistingProvider<Value> {
268
272
  /**
269
273
  * A token provider.
270
274
  */
271
- type Provider<Value = any> = ClassProvider<Value & object> | FactoryProvider<Value> | ValueProvider<Value> | ExistingProvider<Value>;
275
+ type Provider<Value> = ClassProvider<Value & object> | FactoryProvider<Value> | ValueProvider<Value> | ExistingProvider<Value>;
272
276
 
273
277
  /**
274
278
  * Container creation options.
275
279
  */
276
280
  interface ContainerOptions {
281
+ /**
282
+ * The default scope for registrations.
283
+ *
284
+ * @defaultValue Transient
285
+ */
286
+ readonly defaultScope: Scope;
277
287
  /**
278
288
  * Whether to automatically register an unregistered class when resolving it as a token.
279
289
  *
280
290
  * @defaultValue false
281
291
  */
282
292
  readonly autoRegister: boolean;
293
+ }
294
+ /**
295
+ * Child container creation options.
296
+ */
297
+ interface ChildContainerOptions extends ContainerOptions {
283
298
  /**
284
- * The default scope for registrations.
299
+ * Whether to copy {@link ContainerHook}(s) from the parent container.
285
300
  *
286
- * @defaultValue Scope.Transient
301
+ * @defaultValue true
287
302
  */
288
- readonly defaultScope: Scope;
303
+ readonly copyHooks: boolean;
304
+ }
305
+ /**
306
+ * A hook into the lifecycle of a {@link Container}.
307
+ */
308
+ interface ContainerHook {
309
+ /**
310
+ * Called when the container provides a value for a {@link Token}.
311
+ * - For **Container** scoped tokens, it is called only once when the token is first resolved and cached.
312
+ * - For **Resolution** scoped tokens, it is called once per token resolution graph.
313
+ * - For **Transient** scoped tokens, it is called each time the token is resolved,
314
+ * which might mean multiple times per resolution graph.
315
+ *
316
+ * @param value The provided value.
317
+ * @param scope The {@link Scope} of the provided value.
318
+ */
319
+ readonly onProvide?: (value: unknown, scope: Scope) => void;
320
+ /**
321
+ * Called after the container has been disposed.
322
+ *
323
+ * @param values The values that were cached by the container.
324
+ */
325
+ readonly onDispose?: (values: unknown[]) => void;
289
326
  }
290
327
  /**
291
- * Container API.
328
+ * A Dependency Injection container.
292
329
  */
293
330
  interface Container {
294
331
  /**
@@ -308,7 +345,7 @@ interface Container {
308
345
  *
309
346
  * You can pass specific options to override the inherited ones.
310
347
  */
311
- createChild(options?: Partial<ContainerOptions>): Container;
348
+ createChild(options?: Partial<ChildContainerOptions>): Container;
312
349
  /**
313
350
  * Clears and returns all distinct cached values from this container's internal registry.
314
351
  * Values from {@link ValueProvider} registrations are not included, as they are never cached.
@@ -324,7 +361,7 @@ interface Container {
324
361
  * the cached value is taken from the most recent of those registrations.
325
362
  * Otherwise, it may be retrieved from parent containers, if any.
326
363
  *
327
- * Values are never cached for tokens with _transient_ or _resolution_ scope,
364
+ * Values are never cached for tokens with **Transient** or **Resolution** scope,
328
365
  * or for {@link ValueProvider} registrations.
329
366
  */
330
367
  getCached<Value>(token: Token<Value>): Value | undefined;
@@ -336,7 +373,7 @@ interface Container {
336
373
  * cached values are taken from those registrations.
337
374
  * Otherwise, cached values may be retrieved from parent containers, if any.
338
375
  *
339
- * Values are never cached for tokens with _transient_ or _resolution_ scope,
376
+ * Values are never cached for tokens with **Transient** or **Resolution** scope,
340
377
  * or for {@link ValueProvider} registrations.
341
378
  */
342
379
  getAllCached<Value>(token: Token<Value>): Value[];
@@ -352,7 +389,7 @@ interface Container {
352
389
  /**
353
390
  * Returns whether the token is registered in this container or in parent containers, if any.
354
391
  */
355
- isRegistered(token: Token, name?: string): boolean;
392
+ isRegistered<Value>(token: Token<Value>, name?: string): boolean;
356
393
  /**
357
394
  * Registers a {@link ClassProvider}, using the class itself as its token.
358
395
  *
@@ -423,7 +460,7 @@ interface Container {
423
460
  * - For {@link ClassProvider}, a new instance of the class is created according to its scope.
424
461
  * - For {@link ExistingProvider}, the instance is resolved by referring to another registered token.
425
462
  *
426
- * If the class is registered with _container_ scope, the resolved instance is cached
463
+ * If the class is registered with **Container** scope, the resolved instance is cached
427
464
  * in the container's internal registry.
428
465
  */
429
466
  resolve<Instance extends object>(Class: Constructor<Instance>, name?: string): Instance;
@@ -441,7 +478,7 @@ interface Container {
441
478
  * - For {@link ClassProvider}, a new instance of the class is created according to its scope.
442
479
  * - For {@link ExistingProvider}, the value is resolved by referring to another registered token.
443
480
  *
444
- * If the token is registered with _container_ scope, the resolved value is cached
481
+ * If the token is registered with **Container** scope, the resolved value is cached
445
482
  * in the container's internal registry.
446
483
  */
447
484
  resolve<Value>(token: Token<Value>, name?: string): Value;
@@ -467,7 +504,7 @@ interface Container {
467
504
  * - For {@link ClassProvider}, a new instance of the class is created according to its scope.
468
505
  * - For {@link ExistingProvider}, the instance is resolved by referring to another registered token.
469
506
  *
470
- * If the class is registered with _container_ scope, the resolved instance is cached
507
+ * If the class is registered with **Container** scope, the resolved instance is cached
471
508
  * in the container's internal registry.
472
509
  */
473
510
  tryResolve<Instance extends object>(Class: Constructor<Instance>, name?: string): Instance | undefined;
@@ -486,7 +523,7 @@ interface Container {
486
523
  * - For {@link ClassProvider}, a new instance of the class is created according to its scope.
487
524
  * - For {@link ExistingProvider}, the value is resolved by referring to another registered token.
488
525
  *
489
- * If the token is registered with _container_ scope, the resolved value is cached
526
+ * If the token is registered with **Container** scope, the resolved value is cached
490
527
  * in the container's internal registry.
491
528
  */
492
529
  tryResolve<Value>(token: Token<Value>, name?: string): Value | undefined;
@@ -509,7 +546,7 @@ interface Container {
509
546
  * - For {@link ClassProvider}, a new instance of the class is created according to its scope.
510
547
  * - For {@link ExistingProvider}, the instance is resolved by referring to another registered token.
511
548
  *
512
- * If the class is registered with _container_ scope, the resolved instances are cached
549
+ * If the class is registered with **Container** scope, the resolved instances are cached
513
550
  * in the container's internal registry.
514
551
  *
515
552
  * A separate instance of the class is created for each provider.
@@ -526,7 +563,7 @@ interface Container {
526
563
  * - For {@link ClassProvider}, a new instance of the class is created according to its scope.
527
564
  * - For {@link ExistingProvider}, the value is resolved by referring to another registered token.
528
565
  *
529
- * If the token is registered with _container_ scope, the resolved values are cached
566
+ * If the token is registered with **Container** scope, the resolved values are cached
530
567
  * in the container's internal registry.
531
568
  */
532
569
  resolveAll<Value>(token: Token<Value>): Value[];
@@ -549,7 +586,7 @@ interface Container {
549
586
  * - For {@link ClassProvider}, a new instance of the class is created according to its scope.
550
587
  * - For {@link ExistingProvider}, the instance is resolved by referring to another registered token.
551
588
  *
552
- * If the class is registered with _container_ scope, the resolved instances are cached
589
+ * If the class is registered with **Container** scope, the resolved instances are cached
553
590
  * in the container's internal registry.
554
591
  *
555
592
  * A separate instance of the class is created for each provider.
@@ -567,10 +604,22 @@ interface Container {
567
604
  * - For {@link ClassProvider}, a new instance of the class is created according to its scope.
568
605
  * - For {@link ExistingProvider}, the value is resolved by referring to another registered token.
569
606
  *
570
- * If the token is registered with _container_ scope, the resolved values are cached
607
+ * If the token is registered with **Container** scope, the resolved values are cached
571
608
  * in the container's internal registry.
572
609
  */
573
610
  tryResolveAll<Value>(token: Token<Value>): Value[];
611
+ /**
612
+ * Adds a hook to observe the lifecycle of container-managed values.
613
+ *
614
+ * Does nothing if the hook has already been added.
615
+ */
616
+ addHook(hook: ContainerHook): void;
617
+ /**
618
+ * Removes a previously added hook.
619
+ *
620
+ * Does nothing if the hook has not been added yet.
621
+ */
622
+ removeHook(hook: ContainerHook): void;
574
623
  /**
575
624
  * Disposes this container and all its cached values.
576
625
  *
@@ -837,7 +886,7 @@ declare function OptionalAll<Value>(tokens: TokenRef<Value>): ParameterDecorator
837
886
  *
838
887
  * @example
839
888
  * ```ts
840
- * @Scoped(Scope.Container)
889
+ * @Scoped("Container")
841
890
  * class Wizard {}
842
891
  *
843
892
  * container.register(Wizard);
@@ -846,7 +895,7 @@ declare function OptionalAll<Value>(tokens: TokenRef<Value>): ParameterDecorator
846
895
  * container.register(
847
896
  * Wizard,
848
897
  * { useClass: Wizard },
849
- * { scope: Scope.Container },
898
+ * { scope: "Container" },
850
899
  * );
851
900
  * ```
852
901
  *
@@ -872,6 +921,24 @@ declare function inject<Instance extends object>(Class: Constructor<Instance>, n
872
921
  * circular dependencies is necessary.
873
922
  */
874
923
  declare function inject<Value>(token: Token<Value>, name?: string): Value;
924
+
925
+ /**
926
+ * Injects all instances provided by the registrations associated with the given class.
927
+ *
928
+ * Throws an error if:
929
+ * - The class is not registered in the container.
930
+ * - A circular dependency is detected.
931
+ */
932
+ declare function injectAll<Instance extends object>(Class: Constructor<Instance>): Instance[];
933
+ /**
934
+ * Injects all values provided by the registrations associated with the given token.
935
+ *
936
+ * Throws an error if:
937
+ * - The token is not registered in the container.
938
+ * - A circular dependency is detected.
939
+ */
940
+ declare function injectAll<Value>(token: Token<Value>): Value[];
941
+
875
942
  /**
876
943
  * Injects the instance associated with the given class.
877
944
  *
@@ -922,24 +989,33 @@ declare function injectBy<Instance extends object>(thisArg: any, Class: Construc
922
989
  declare function injectBy<Value>(thisArg: any, token: Token<Value>, name?: string): Value;
923
990
 
924
991
  /**
925
- * Injects all instances provided by the registrations associated with the given class.
926
- *
927
- * Throws an error if:
928
- * - The class is not registered in the container.
929
- * - A circular dependency is detected.
930
- */
931
- declare function injectAll<Instance extends object>(Class: Constructor<Instance>): Instance[];
932
- /**
933
- * Injects all values provided by the registrations associated with the given token.
992
+ * Asserts that the current stack frame is within an injection context,
993
+ * meaning it has access to injection functions (`inject`, `optional`, etc.).
934
994
  *
935
- * Throws an error if:
936
- * - The token is not registered in the container.
937
- * - A circular dependency is detected.
995
+ * @param fn The function performing the assertion, or a string name used in the error message.
996
+ * @throws {Error} If the current stack frame is not within an injection context.
938
997
  */
939
- declare function injectAll<Value>(token: Token<Value>): Value[];
998
+ declare function assertInjectionContext(fn: Function | string): void;
940
999
 
941
1000
  /**
942
- * Injector API.
1001
+ * Allows performing injections outside the normal injection context window.
1002
+ *
1003
+ * @example
1004
+ * ```ts
1005
+ * class Wizard {
1006
+ * private injector = inject(Injector);
1007
+ *
1008
+ * // Lazily initialize the wand property
1009
+ * private wand?: Wand;
1010
+ *
1011
+ * getWand(): Wand {
1012
+ * // An injection context does not exist here, but the
1013
+ * // Injector instance retains and reuse the context
1014
+ * // that was present at the time of its injection
1015
+ * return (this.wand ??= this.injector.inject(Wand));
1016
+ * }
1017
+ * }
1018
+ * ```
943
1019
  */
944
1020
  interface Injector {
945
1021
  /**
@@ -999,21 +1075,23 @@ interface Injector {
999
1075
  runInContext<ReturnType>(fn: () => ReturnType): ReturnType;
1000
1076
  }
1001
1077
  /**
1002
- * Injector token for dynamic injections.
1078
+ * Allows performing injections outside the normal injection context window.
1003
1079
  *
1004
1080
  * @example
1005
1081
  * ```ts
1006
1082
  * class Wizard {
1007
1083
  * private injector = inject(Injector);
1084
+ *
1085
+ * // Lazily initialize the wand property
1008
1086
  * private wand?: Wand;
1009
1087
  *
1010
1088
  * getWand(): Wand {
1089
+ * // An injection context does not exist here, but the
1090
+ * // Injector instance retains and reuse the context
1091
+ * // that was present at the time of its injection
1011
1092
  * return (this.wand ??= this.injector.inject(Wand));
1012
1093
  * }
1013
1094
  * }
1014
- *
1015
- * const wizard = container.resolve(Wizard);
1016
- * wizard.getWand(); // => Wand
1017
1095
  * ```
1018
1096
  */
1019
1097
  declare const Injector: Type<Injector>;
@@ -1036,61 +1114,6 @@ declare const Injector: Type<Injector>;
1036
1114
  */
1037
1115
  declare function setClassIdentityMapping<T extends object>(transformedClass: Constructor<T>, originalClass: Constructor<T>): void;
1038
1116
 
1039
- /**
1040
- * Composer API for middleware functions.
1041
- */
1042
- interface MiddlewareComposer {
1043
- /**
1044
- * Adds a middleware function to the composer.
1045
- */
1046
- use<MethodKey extends keyof Container>(key: MethodKey, wrap: Container[MethodKey] extends Function ? (next: Container[MethodKey]) => Container[MethodKey] : never): MiddlewareComposer;
1047
- }
1048
- /**
1049
- * Middleware function that can be used to extend the container.
1050
- *
1051
- * @example
1052
- * ```ts
1053
- * const logger: Middleware = (composer, _api) => {
1054
- * composer
1055
- * .use("resolve", (next) => (...args) => {
1056
- * console.log("resolve", args);
1057
- * return next(...args);
1058
- * })
1059
- * .use("resolveAll", (next) => (...args) => {
1060
- * console.log("resolveAll", args);
1061
- * return next(...args);
1062
- * });
1063
- * };
1064
- * ```
1065
- */
1066
- interface Middleware {
1067
- (composer: MiddlewareComposer, api: Readonly<Container>): void;
1068
- }
1069
- /**
1070
- * Applies middleware functions to a container.
1071
- *
1072
- * Middlewares are applied in array order but execute in reverse order.
1073
- *
1074
- * @example
1075
- * ```ts
1076
- * const container = applyMiddleware(
1077
- * createContainer(),
1078
- * [A, B],
1079
- * );
1080
- * ```
1081
- *
1082
- * The execution order will be:
1083
- *
1084
- * 1. B before
1085
- * 2. A before
1086
- * 3. original function
1087
- * 4. A after
1088
- * 5. B after
1089
- *
1090
- * This allows outer middlewares to wrap and control the behavior of inner middlewares.
1091
- */
1092
- declare function applyMiddleware(container: Container, middlewares: Middleware[]): Container;
1093
-
1094
1117
  /**
1095
1118
  * Injects the instance associated with the given class,
1096
1119
  * or `undefined` if the class is not registered in the container.
@@ -1107,6 +1130,22 @@ declare function optional<Instance extends object>(Class: Constructor<Instance>,
1107
1130
  * Use {@link optionalBy} if resolving circular dependencies is necessary.
1108
1131
  */
1109
1132
  declare function optional<Value>(token: Token<Value>, name?: string): Value | undefined;
1133
+
1134
+ /**
1135
+ * Injects all instances provided by the registrations associated with the given class
1136
+ * or an empty array if the class is not registered in the container.
1137
+ *
1138
+ * Throws an error if a circular dependency is detected.
1139
+ */
1140
+ declare function optionalAll<Instance extends object>(Class: Constructor<Instance>): Instance[];
1141
+ /**
1142
+ * Injects all values provided by the registrations associated with the given token
1143
+ * or an empty array if the token is not registered in the container.
1144
+ *
1145
+ * Throws an error if a circular dependency is detected.
1146
+ */
1147
+ declare function optionalAll<Value>(token: Token<Value>): Value[];
1148
+
1110
1149
  /**
1111
1150
  * Injects the instance associated with the given class,
1112
1151
  * or `undefined` if the class is not registered in the container.
@@ -1132,20 +1171,5 @@ declare function optionalBy<Instance extends object>(thisArg: any, Class: Constr
1132
1171
  */
1133
1172
  declare function optionalBy<Value>(thisArg: any, token: Token<Value>, name?: string): Value | undefined;
1134
1173
 
1135
- /**
1136
- * Injects all instances provided by the registrations associated with the given class
1137
- * or an empty array if the class is not registered in the container.
1138
- *
1139
- * Throws an error if a circular dependency is detected.
1140
- */
1141
- declare function optionalAll<Instance extends object>(Class: Constructor<Instance>): Instance[];
1142
- /**
1143
- * Injects all values provided by the registrations associated with the given token
1144
- * or an empty array if the token is not registered in the container.
1145
- *
1146
- * Throws an error if a circular dependency is detected.
1147
- */
1148
- declare function optionalAll<Value>(token: Token<Value>): Value[];
1149
-
1150
- export { AutoRegister, EagerInstantiate, Inject, InjectAll, Injectable, Injector, Named, Optional, OptionalAll, Scope, Scoped, applyMiddleware, build, classRef, createContainer, createType, inject, injectAll, injectBy, optional, optionalAll, optionalBy, setClassIdentityMapping, tokenRef };
1151
- export type { ClassProvider, ClassRef, Constructor, Container, ContainerOptions, ExistingProvider, FactoryProvider, Middleware, MiddlewareComposer, Provider, ProviderType, RegistrationOptions, Token, TokenRef, Tokens, TokensRef, Type, ValueProvider };
1174
+ export { AutoRegister, EagerInstantiate, Inject, InjectAll, Injectable, Injector, Named, Optional, OptionalAll, Scope, Scoped, assertInjectionContext, build, classRef, createContainer, createType, inject, injectAll, injectBy, optional, optionalAll, optionalBy, setClassIdentityMapping, tokenRef };
1175
+ export type { ChildContainerOptions, ClassProvider, ClassRef, Constructor, Container, ContainerHook, ContainerOptions, ExistingProvider, FactoryProvider, Provider, ProviderType, RegistrationOptions, Token, TokenRef, Tokens, TokensRef, Type, ValueProvider };