@lppedd/di-wise-neo 0.5.2 → 0.6.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.
@@ -422,7 +422,7 @@ declare function createContainer(options?: Partial<ContainerOptions>): Container
422
422
  *
423
423
  * @example
424
424
  * ```ts
425
- * @AutoRegister
425
+ * @AutoRegister()
426
426
  * class Wizard {}
427
427
  *
428
428
  * const wizard = container.resolve(Wizard);
@@ -431,28 +431,28 @@ declare function createContainer(options?: Partial<ContainerOptions>): Container
431
431
  *
432
432
  * @__NO_SIDE_EFFECTS__
433
433
  */
434
- declare function AutoRegister<Ctor extends Constructor<any>>(Class: Ctor): void;
434
+ declare function AutoRegister(): ClassDecorator;
435
435
 
436
436
  /**
437
- * Class decorator that enables eager instantiation of a class when it is registered
438
- * in the container with `Scope.Container`.
437
+ * Class decorator that sets the class scope to **Container** and enables
438
+ * eager instantiation when the class is registered in the container.
439
439
  *
440
440
  * This causes the container to immediately create and cache the instance of the class,
441
441
  * instead of deferring instantiation until the first resolution.
442
442
  *
443
443
  * @example
444
444
  * ```ts
445
- * @EagerInstantiate
446
- * @Scoped(Scope.Container)
445
+ * @EagerInstantiate()
447
446
  * class Wizard {}
448
447
  *
449
- * // A Wizard instance is immediately created and cached by the container
448
+ * // Wizard is registered with Container scope, and an instance
449
+ * // is immediately created and cached by the container
450
450
  * const wizard = container.register(Wizard);
451
451
  * ```
452
452
  *
453
453
  * @__NO_SIDE_EFFECTS__
454
454
  */
455
- declare function EagerInstantiate<Ctor extends Constructor<any>>(Class: Ctor): void;
455
+ declare function EagerInstantiate(): ClassDecorator;
456
456
 
457
457
  interface TokensRef<Value = any> {
458
458
  readonly getRefTokens: () => Set<Token<Value>>;
package/dist/es/index.mjs CHANGED
@@ -190,6 +190,22 @@ function getMetadata(Class) {
190
190
  }
191
191
  });
192
192
  }
193
+ if (metadata.provider.useClass !== Class) {
194
+ // This is part of the class identity mapping API (see setClassIdentityMapping).
195
+ //
196
+ // Scenario:
197
+ // 1. Metadata is created for class A (the original class) because of a parameter decorator.
198
+ // 2. Later, because of a class decorator that extends the decorated class, a third-party
199
+ // registers a class identity mapping from class B to class A, where B is the class
200
+ // generated from the class decorator by extending A.
201
+ //
202
+ // We must update useClass to be the extender class B so that instances created by the
203
+ // DI container match the consumer's registered class. Without this update, the DI
204
+ // system would instantiate the original class A, causing behavior inconsistencies.
205
+ //
206
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
207
+ metadata.provider.useClass = Class;
208
+ }
193
209
  return metadata;
194
210
  }
195
211
  const classIdentityMap = new WeakMap();
@@ -518,7 +534,7 @@ function isDisposable(value) {
518
534
  // The provider is of type ClassProvider, initialized by getMetadata
519
535
  provider: metadata.provider,
520
536
  options: {
521
- scope: metadata.scope ?? this.myOptions.defaultScope
537
+ scope: metadata.scope?.value ?? this.myOptions.defaultScope
522
538
  },
523
539
  dependencies: metadata.dependencies
524
540
  };
@@ -535,7 +551,7 @@ function isDisposable(value) {
535
551
  }
536
552
  // Eager-instantiate only if the class is container-scoped
537
553
  if (metadata.eagerInstantiate && registration.options?.scope === Scope.Container) {
538
- this.resolve(Class);
554
+ this.resolveProviderValue(registration, registration.provider);
539
555
  }
540
556
  } else {
541
557
  const [token, provider, options] = args;
@@ -546,7 +562,7 @@ function isDisposable(value) {
546
562
  options: {
547
563
  // The explicit registration options override what is specified
548
564
  // via class decorators (e.g., @Scoped)
549
- scope: metadata.scope ?? this.myOptions.defaultScope,
565
+ scope: metadata.scope?.value ?? this.myOptions.defaultScope,
550
566
  ...options
551
567
  },
552
568
  dependencies: metadata.dependencies
@@ -554,7 +570,7 @@ function isDisposable(value) {
554
570
  this.myTokenRegistry.set(token, registration);
555
571
  // Eager-instantiate only if the provided class is container-scoped
556
572
  if (metadata.eagerInstantiate && registration.options?.scope === Scope.Container) {
557
- this.resolve(token);
573
+ this.resolveProviderValue(registration, registration.provider);
558
574
  }
559
575
  } else {
560
576
  if (isExistingProvider(provider)) {
@@ -670,7 +686,7 @@ function isDisposable(value) {
670
686
  metadata.eagerInstantiate = eagerInstantiate;
671
687
  }
672
688
  }
673
- const scope = this.resolveScope(metadata.scope);
689
+ const scope = this.resolveScope(metadata.scope?.value);
674
690
  if (optional && scope === Scope.Container) {
675
691
  // It would not be possible to resolve the class in container scope,
676
692
  // as that would require prior registration.
@@ -860,7 +876,7 @@ function isDisposable(value) {
860
876
  *
861
877
  * @example
862
878
  * ```ts
863
- * @AutoRegister
879
+ * @AutoRegister()
864
880
  * class Wizard {}
865
881
  *
866
882
  * const wizard = container.resolve(Wizard);
@@ -868,32 +884,45 @@ function isDisposable(value) {
868
884
  * ```
869
885
  *
870
886
  * @__NO_SIDE_EFFECTS__
871
- */ function AutoRegister(Class) {
872
- const metadata = getMetadata(Class);
873
- metadata.autoRegister = true;
887
+ */ function AutoRegister() {
888
+ return function(Class) {
889
+ const metadata = getMetadata(Class);
890
+ metadata.autoRegister = true;
891
+ };
874
892
  }
875
893
 
876
894
  /**
877
- * Class decorator that enables eager instantiation of a class when it is registered
878
- * in the container with `Scope.Container`.
895
+ * Class decorator that sets the class scope to **Container** and enables
896
+ * eager instantiation when the class is registered in the container.
879
897
  *
880
898
  * This causes the container to immediately create and cache the instance of the class,
881
899
  * instead of deferring instantiation until the first resolution.
882
900
  *
883
901
  * @example
884
902
  * ```ts
885
- * @EagerInstantiate
886
- * @Scoped(Scope.Container)
903
+ * @EagerInstantiate()
887
904
  * class Wizard {}
888
905
  *
889
- * // A Wizard instance is immediately created and cached by the container
906
+ * // Wizard is registered with Container scope, and an instance
907
+ * // is immediately created and cached by the container
890
908
  * const wizard = container.register(Wizard);
891
909
  * ```
892
910
  *
893
911
  * @__NO_SIDE_EFFECTS__
894
- */ function EagerInstantiate(Class) {
895
- const metadata = getMetadata(Class);
896
- metadata.eagerInstantiate = true;
912
+ */ function EagerInstantiate() {
913
+ return function(Class) {
914
+ const metadata = getMetadata(Class);
915
+ const currentScope = metadata.scope;
916
+ assert(!currentScope || currentScope.value === Scope.Container, ()=>{
917
+ const { value, appliedBy } = currentScope;
918
+ return `class ${Class.name}: Scope.${value} was already set by @${appliedBy},\n ` + `but @EagerInstantiate is trying to set a conflicting Scope.Container.\n ` + `Only one decorator should set the class scope, or all must agree on the same value.`;
919
+ });
920
+ metadata.eagerInstantiate = true;
921
+ metadata.scope = {
922
+ value: Scope.Container,
923
+ appliedBy: "EagerInstantiate"
924
+ };
925
+ };
897
926
  }
898
927
 
899
928
  function forwardRef(token) {
@@ -1025,7 +1054,16 @@ function OptionalAll(token) {
1025
1054
  */ function Scoped(scope) {
1026
1055
  return function(Class) {
1027
1056
  const metadata = getMetadata(Class);
1028
- metadata.scope = scope;
1057
+ const currentScope = metadata.scope;
1058
+ assert(!currentScope || currentScope.value === scope, ()=>{
1059
+ const { value, appliedBy } = currentScope;
1060
+ const by = appliedBy === "Scoped" ? `another @${appliedBy} decorator` : `@${appliedBy}`;
1061
+ return `class ${Class.name}: Scope.${value} was already set by ${by},\n ` + `but @Scoped is trying to set a conflicting Scope.${scope}.\n ` + `Only one decorator should set the class scope, or all must agree on the same value.`;
1062
+ });
1063
+ metadata.scope = {
1064
+ value: scope,
1065
+ appliedBy: "Scoped"
1066
+ };
1029
1067
  };
1030
1068
  }
1031
1069