@lppedd/di-wise-neo 0.8.1 → 0.9.1
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/dist/cjs/index.js +77 -75
- package/dist/cjs/index.js.map +1 -1
- package/dist/es/index.mjs +77 -75
- package/dist/es/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/es/index.mjs
CHANGED
@@ -26,7 +26,7 @@ function isConstructor(token) {
|
|
26
26
|
}
|
27
27
|
|
28
28
|
// @internal
|
29
|
-
function
|
29
|
+
function check(condition, message) {
|
30
30
|
if (!condition) {
|
31
31
|
throw new Error(tag(typeof message === "string" ? message : message()));
|
32
32
|
}
|
@@ -36,9 +36,10 @@ function expectNever(value) {
|
|
36
36
|
throw new TypeError(tag(`unexpected value ${String(value)}`));
|
37
37
|
}
|
38
38
|
// @internal
|
39
|
-
function throwUnregisteredError(token) {
|
39
|
+
function throwUnregisteredError(token, name) {
|
40
40
|
const type = isConstructor(token) ? "class" : "token";
|
41
|
-
|
41
|
+
const spec = name !== undefined ? `[name=${name}]` : "";
|
42
|
+
throw new Error(tag(`unregistered ${type} ${token.name}${spec}`));
|
42
43
|
}
|
43
44
|
// @internal
|
44
45
|
function throwExistingUnregisteredError(sourceToken, targetTokenOrError) {
|
@@ -57,13 +58,6 @@ function untag(message) {
|
|
57
58
|
return message.startsWith("[di-wise-neo]") ? message.substring(13).trimStart() : message;
|
58
59
|
}
|
59
60
|
|
60
|
-
// @internal
|
61
|
-
function invariant(condition) {
|
62
|
-
if (!condition) {
|
63
|
-
throw new Error("invariant violation");
|
64
|
-
}
|
65
|
-
}
|
66
|
-
|
67
61
|
// @internal
|
68
62
|
class KeyedStack {
|
69
63
|
has(key) {
|
@@ -74,7 +68,7 @@ class KeyedStack {
|
|
74
68
|
return entry?.value;
|
75
69
|
}
|
76
70
|
push(key, value) {
|
77
|
-
|
71
|
+
check(!this.has(key), "invariant violation");
|
78
72
|
this.myKeys.add(key);
|
79
73
|
this.myEntries.push({
|
80
74
|
key,
|
@@ -102,9 +96,10 @@ class WeakRefMap {
|
|
102
96
|
}
|
103
97
|
this.myMap.delete(key);
|
104
98
|
}
|
99
|
+
return undefined;
|
105
100
|
}
|
106
101
|
set(key, value) {
|
107
|
-
|
102
|
+
check(!this.get(key), "invariant violation");
|
108
103
|
this.myMap.set(key, new WeakRef(value));
|
109
104
|
return ()=>{
|
110
105
|
this.myMap.delete(key);
|
@@ -128,7 +123,7 @@ const [provideInjectionContext, useInjectionContext] = createInjectionContext();
|
|
128
123
|
// @internal
|
129
124
|
function ensureInjectionContext(name) {
|
130
125
|
const context = useInjectionContext();
|
131
|
-
|
126
|
+
check(context, `${name} can only be invoked within an injection context`);
|
132
127
|
return context;
|
133
128
|
}
|
134
129
|
function createInjectionContext() {
|
@@ -358,13 +353,13 @@ class TokenRegistry {
|
|
358
353
|
] || this.getAllFromParent(token, name);
|
359
354
|
}
|
360
355
|
set(token, registration) {
|
361
|
-
|
356
|
+
check(!internals.has(token), `cannot register reserved token ${token.name}`);
|
362
357
|
let registrations = this.myMap.get(token);
|
363
358
|
if (!registrations) {
|
364
359
|
this.myMap.set(token, registrations = []);
|
365
360
|
} else if (registration.name !== undefined) {
|
366
361
|
const existing = registrations.filter((r)=>r.name === registration.name);
|
367
|
-
|
362
|
+
check(existing.length === 0, `a ${token.name} token named '${registration.name}' is already registered`);
|
368
363
|
}
|
369
364
|
registrations.push(registration);
|
370
365
|
}
|
@@ -418,7 +413,7 @@ class TokenRegistry {
|
|
418
413
|
let registrations = thisRegistrations || this.myParent?.getAllFromParent(token, name);
|
419
414
|
if (registrations && name !== undefined) {
|
420
415
|
registrations = registrations.filter((r)=>r.name === name);
|
421
|
-
|
416
|
+
check(registrations.length < 2, `internal error: more than one registration named '${name}'`);
|
422
417
|
}
|
423
418
|
return registrations ?? [];
|
424
419
|
}
|
@@ -559,7 +554,7 @@ function isDisposable(value) {
|
|
559
554
|
const [token, provider, options] = args;
|
560
555
|
const existingProvider = isExistingProvider(provider);
|
561
556
|
const name = existingProvider ? undefined : provider.name;
|
562
|
-
|
557
|
+
check(name === undefined || name.trim(), "the provider name qualifier cannot be empty or blank");
|
563
558
|
if (isClassProvider(provider)) {
|
564
559
|
const metadata = getMetadata(provider.useClass);
|
565
560
|
const registration = {
|
@@ -580,7 +575,7 @@ function isDisposable(value) {
|
|
580
575
|
}
|
581
576
|
} else {
|
582
577
|
if (existingProvider) {
|
583
|
-
|
578
|
+
check(token !== provider.useExisting, `the useExisting token ${token.name} cannot be the same as the token being registered`);
|
584
579
|
}
|
585
580
|
this.myTokenRegistry.set(token, {
|
586
581
|
name: name,
|
@@ -613,29 +608,30 @@ function isDisposable(value) {
|
|
613
608
|
localOptional = optionalOrName;
|
614
609
|
localName = name;
|
615
610
|
}
|
616
|
-
|
611
|
+
let registration = this.myTokenRegistry.get(token, localName);
|
612
|
+
if (!registration && isConstructor(token)) {
|
613
|
+
registration = this.autoRegisterClass(token, localName);
|
614
|
+
}
|
617
615
|
if (registration) {
|
618
616
|
return this.resolveRegistration(token, registration, localName);
|
619
617
|
}
|
620
|
-
|
621
|
-
return this.instantiateClass(token, localOptional);
|
622
|
-
}
|
623
|
-
return localOptional ? undefined : throwUnregisteredError(token);
|
618
|
+
return localOptional ? undefined : throwUnregisteredError(token, localName);
|
624
619
|
}
|
625
620
|
resolveAll(token, optional) {
|
626
621
|
this.checkDisposed();
|
627
|
-
|
622
|
+
let registrations = this.myTokenRegistry.getAll(token);
|
623
|
+
if (registrations.length === 0 && isConstructor(token)) {
|
624
|
+
const registration = this.autoRegisterClass(token);
|
625
|
+
if (registration) {
|
626
|
+
registrations = [
|
627
|
+
registration
|
628
|
+
];
|
629
|
+
}
|
630
|
+
}
|
628
631
|
if (registrations.length > 0) {
|
629
632
|
return registrations //
|
630
633
|
.map((registration)=>this.resolveRegistration(token, registration)).filter((value)=>value != null);
|
631
634
|
}
|
632
|
-
if (isConstructor(token)) {
|
633
|
-
const instance = this.instantiateClass(token, optional);
|
634
|
-
return instance === undefined // = could not resolve, but since it is optional
|
635
|
-
? [] : [
|
636
|
-
instance
|
637
|
-
];
|
638
|
-
}
|
639
635
|
return optional ? [] : throwUnregisteredError(token);
|
640
636
|
}
|
641
637
|
dispose() {
|
@@ -685,40 +681,25 @@ function isDisposable(value) {
|
|
685
681
|
throw e;
|
686
682
|
}
|
687
683
|
}
|
688
|
-
|
684
|
+
autoRegisterClass(Class, name) {
|
689
685
|
const metadata = getMetadata(Class);
|
690
|
-
|
691
|
-
|
692
|
-
//
|
686
|
+
const autoRegister = metadata.autoRegister ?? this.myOptions.autoRegister;
|
687
|
+
if (autoRegister && (name === undefined || metadata.name === name)) {
|
688
|
+
// Temporarily set eagerInstantiate to false to avoid potentially resolving
|
689
|
+
// the class inside register()
|
693
690
|
const eagerInstantiate = metadata.eagerInstantiate;
|
694
691
|
metadata.eagerInstantiate = false;
|
695
692
|
try {
|
696
693
|
this.register(Class);
|
697
|
-
return this.
|
694
|
+
return this.myTokenRegistry.get(Class, name ?? metadata.name);
|
698
695
|
} finally{
|
699
696
|
metadata.eagerInstantiate = eagerInstantiate;
|
700
697
|
}
|
701
698
|
}
|
702
|
-
|
703
|
-
if (optional && scope === Scope.Container) {
|
704
|
-
// It would not be possible to resolve the class in container scope,
|
705
|
-
// as that would require prior registration.
|
706
|
-
// However, since resolution is marked optional, we simply return undefined.
|
707
|
-
return undefined;
|
708
|
-
}
|
709
|
-
assert(scope !== Scope.Container, `unregistered class ${Class.name} cannot be resolved in container scope`);
|
710
|
-
const registration = {
|
711
|
-
provider: metadata.provider,
|
712
|
-
options: {
|
713
|
-
scope: scope
|
714
|
-
},
|
715
|
-
dependencies: metadata.dependencies
|
716
|
-
};
|
717
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
718
|
-
return this.resolveScopedValue(registration, (args)=>new Class(...args));
|
699
|
+
return undefined;
|
719
700
|
}
|
720
701
|
resolveProviderValue(registration, provider) {
|
721
|
-
|
702
|
+
check(registration.provider === provider, "internal error: mismatching provider");
|
722
703
|
if (isClassProvider(provider)) {
|
723
704
|
const Class = provider.useClass;
|
724
705
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
@@ -731,9 +712,7 @@ function isDisposable(value) {
|
|
731
712
|
if (isValueProvider(provider)) {
|
732
713
|
return provider.useValue;
|
733
714
|
}
|
734
|
-
|
735
|
-
assert(false, "internal error: unexpected ExistingProvider");
|
736
|
-
}
|
715
|
+
check(!isExistingProvider(provider), "internal error: unexpected ExistingProvider");
|
737
716
|
expectNever(provider);
|
738
717
|
}
|
739
718
|
resolveScopedValue(registration, factory) {
|
@@ -749,7 +728,7 @@ function isDisposable(value) {
|
|
749
728
|
const options = registration.options;
|
750
729
|
if (resolution.stack.has(provider)) {
|
751
730
|
const dependentRef = resolution.dependents.get(provider);
|
752
|
-
|
731
|
+
check(dependentRef, "circular dependency detected");
|
753
732
|
return dependentRef.current;
|
754
733
|
}
|
755
734
|
const scope = this.resolveScope(options?.scope, context);
|
@@ -808,13 +787,13 @@ function isDisposable(value) {
|
|
808
787
|
resolveConstructorDependencies(registration) {
|
809
788
|
const dependencies = registration.dependencies;
|
810
789
|
if (dependencies) {
|
811
|
-
|
790
|
+
check(isClassProvider(registration.provider), `internal error: not a ClassProvider`);
|
812
791
|
const ctorDeps = dependencies.constructor.filter((d)=>d.appliedBy);
|
813
792
|
if (ctorDeps.length > 0) {
|
814
793
|
// Let's check if all necessary constructor parameters are decorated.
|
815
794
|
// If not, we cannot safely create an instance.
|
816
795
|
const ctor = registration.provider.useClass;
|
817
|
-
|
796
|
+
check(ctor.length === ctorDeps.length, ()=>{
|
818
797
|
const msg = `expected ${ctor.length} decorated constructor parameters in ${ctor.name}`;
|
819
798
|
return msg + `, but found ${ctorDeps.length}`;
|
820
799
|
});
|
@@ -838,7 +817,7 @@ function isDisposable(value) {
|
|
838
817
|
injectDependencies(registration, instance) {
|
839
818
|
const dependencies = registration.dependencies;
|
840
819
|
if (dependencies) {
|
841
|
-
|
820
|
+
check(isClassProvider(registration.provider), `internal error: not a ClassProvider`);
|
842
821
|
const ctor = registration.provider.useClass;
|
843
822
|
// Perform method injection
|
844
823
|
for (const entry of dependencies.methods){
|
@@ -848,7 +827,7 @@ function isDisposable(value) {
|
|
848
827
|
// If not, we cannot safely invoke the method.
|
849
828
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
850
829
|
const method = instance[key];
|
851
|
-
|
830
|
+
check(methodDeps.length === method.length, ()=>{
|
852
831
|
const msg = `expected ${method.length} decorated method parameters`;
|
853
832
|
return msg + ` in ${ctor.name}.${String(key)}, but found ${methodDeps.length}`;
|
854
833
|
});
|
@@ -872,7 +851,7 @@ function isDisposable(value) {
|
|
872
851
|
return instance;
|
873
852
|
}
|
874
853
|
checkDisposed() {
|
875
|
-
|
854
|
+
check(!this.myDisposed, "the container is disposed");
|
876
855
|
}
|
877
856
|
}
|
878
857
|
|
@@ -928,7 +907,7 @@ function isDisposable(value) {
|
|
928
907
|
return function(Class) {
|
929
908
|
const metadata = getMetadata(Class);
|
930
909
|
const currentScope = metadata.scope;
|
931
|
-
|
910
|
+
check(!currentScope || currentScope.value === Scope.Container, ()=>{
|
932
911
|
const { value, appliedBy } = currentScope;
|
933
912
|
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.`;
|
934
913
|
});
|
@@ -953,7 +932,7 @@ function forwardRef(token) {
|
|
953
932
|
},
|
954
933
|
getRefToken: ()=>{
|
955
934
|
const tokenOrTokens = token();
|
956
|
-
|
935
|
+
check(!Array.isArray(tokenOrTokens), "internal error: ref tokens should be a single token");
|
957
936
|
return tokenOrTokens;
|
958
937
|
}
|
959
938
|
};
|
@@ -973,7 +952,7 @@ function isTokenRef(value) {
|
|
973
952
|
function updateParameterMetadata(decorator, target, propertyKey, parameterIndex, updateFn) {
|
974
953
|
// Error out immediately if the decorator has been applied to a static method
|
975
954
|
if (propertyKey !== undefined && typeof target === "function") {
|
976
|
-
|
955
|
+
check(false, `@${decorator} cannot be used on static method ${target.name}.${String(propertyKey)}`);
|
977
956
|
}
|
978
957
|
if (propertyKey === undefined) {
|
979
958
|
// Constructor
|
@@ -993,11 +972,30 @@ function updateParameterMetadata(decorator, target, propertyKey, parameterIndex,
|
|
993
972
|
//
|
994
973
|
// @internal
|
995
974
|
function checkSingleDecorator(dependency, target, propertyKey, parameterIndex) {
|
996
|
-
|
997
|
-
const
|
998
|
-
return
|
975
|
+
check(dependency.appliedBy === undefined, ()=>{
|
976
|
+
const location = getLocation(target, propertyKey, parameterIndex);
|
977
|
+
return `multiple injection decorators on ${location}, but only one is allowed`;
|
978
|
+
});
|
979
|
+
}
|
980
|
+
// Checks that the `@Named` decorator is not used in combination with
|
981
|
+
// `@InjectAll` or `@OptionalAll`, which ignore the name qualification.
|
982
|
+
//
|
983
|
+
// @internal
|
984
|
+
function checkNamedDecorator(dependency, target, propertyKey, parameterIndex) {
|
985
|
+
const { appliedBy, name } = dependency;
|
986
|
+
check(name === undefined || appliedBy !== "InjectAll" && appliedBy !== "OptionalAll", ()=>{
|
987
|
+
const location = getLocation(target, propertyKey, parameterIndex);
|
988
|
+
return `@Named has no effect on ${location} when used with @${appliedBy}`;
|
999
989
|
});
|
1000
990
|
}
|
991
|
+
// Returns a human-readable description of the parameter location.
|
992
|
+
// For example: "Wizard constructor parameter 2" or "Wizard.set parameter 0"
|
993
|
+
//
|
994
|
+
// @internal
|
995
|
+
function getLocation(target, propertyKey, parameterIndex) {
|
996
|
+
const location = propertyKey === undefined ? `${target.name} constructor` : `${target.constructor.name}.${String(propertyKey)}`;
|
997
|
+
return `${location} parameter ${parameterIndex}`;
|
998
|
+
}
|
1001
999
|
|
1002
1000
|
function Inject(token) {
|
1003
1001
|
return function(target, propertyKey, parameterIndex) {
|
@@ -1035,6 +1033,7 @@ function InjectAll(token) {
|
|
1035
1033
|
checkSingleDecorator(dependency, target, propertyKey, parameterIndex);
|
1036
1034
|
dependency.appliedBy = "InjectAll";
|
1037
1035
|
dependency.tokenRef = isTokenRef(token) ? token : forwardRef(()=>token);
|
1036
|
+
checkNamedDecorator(dependency, target, propertyKey, parameterIndex);
|
1038
1037
|
});
|
1039
1038
|
};
|
1040
1039
|
}
|
@@ -1057,21 +1056,23 @@ function InjectAll(token) {
|
|
1057
1056
|
*
|
1058
1057
|
* @__NO_SIDE_EFFECTS__
|
1059
1058
|
*/ function Named(name) {
|
1060
|
-
|
1061
|
-
assert(false, "the @Named qualifier cannot be empty or blank");
|
1062
|
-
}
|
1059
|
+
check(name.trim(), "the @Named qualifier cannot be empty or blank");
|
1063
1060
|
return function(target, propertyKey, parameterIndex) {
|
1064
1061
|
if (parameterIndex === undefined) {
|
1065
1062
|
// The decorator has been applied to the class
|
1066
1063
|
const ctor = target;
|
1067
1064
|
const metadata = getMetadata(ctor);
|
1068
|
-
|
1065
|
+
check(metadata.name === undefined, `multiple @Named decorators on class ${ctor.name}, but only one is allowed`);
|
1069
1066
|
metadata.name = name;
|
1070
1067
|
} else {
|
1071
1068
|
// The decorator has been applied to a method parameter
|
1072
1069
|
updateParameterMetadata("Named", target, propertyKey, parameterIndex, (dependency)=>{
|
1073
|
-
|
1070
|
+
check(dependency.name === undefined, ()=>{
|
1071
|
+
const location = getLocation(target, propertyKey, parameterIndex);
|
1072
|
+
return `multiple @Named decorators on ${location}, but only one is allowed`;
|
1073
|
+
});
|
1074
1074
|
dependency.name = name;
|
1075
|
+
checkNamedDecorator(dependency, target, propertyKey, parameterIndex);
|
1075
1076
|
});
|
1076
1077
|
}
|
1077
1078
|
};
|
@@ -1093,6 +1094,7 @@ function OptionalAll(token) {
|
|
1093
1094
|
checkSingleDecorator(dependency, target, propertyKey, parameterIndex);
|
1094
1095
|
dependency.appliedBy = "OptionalAll";
|
1095
1096
|
dependency.tokenRef = isTokenRef(token) ? token : forwardRef(()=>token);
|
1097
|
+
checkNamedDecorator(dependency, target, propertyKey, parameterIndex);
|
1096
1098
|
});
|
1097
1099
|
};
|
1098
1100
|
}
|
@@ -1122,7 +1124,7 @@ function OptionalAll(token) {
|
|
1122
1124
|
return function(Class) {
|
1123
1125
|
const metadata = getMetadata(Class);
|
1124
1126
|
const currentScope = metadata.scope;
|
1125
|
-
|
1127
|
+
check(!currentScope || currentScope.value === scope, ()=>{
|
1126
1128
|
const { value, appliedBy } = currentScope;
|
1127
1129
|
const by = appliedBy === "Scoped" ? `another @${appliedBy} decorator` : `@${appliedBy}`;
|
1128
1130
|
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.`;
|