@lppedd/di-wise-neo 0.9.0 → 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 +52 -38
- package/dist/cjs/index.js.map +1 -1
- package/dist/es/index.mjs +52 -38
- 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
|
}
|
@@ -58,13 +58,6 @@ function untag(message) {
|
|
58
58
|
return message.startsWith("[di-wise-neo]") ? message.substring(13).trimStart() : message;
|
59
59
|
}
|
60
60
|
|
61
|
-
// @internal
|
62
|
-
function invariant(condition) {
|
63
|
-
if (!condition) {
|
64
|
-
throw new Error("invariant violation");
|
65
|
-
}
|
66
|
-
}
|
67
|
-
|
68
61
|
// @internal
|
69
62
|
class KeyedStack {
|
70
63
|
has(key) {
|
@@ -75,7 +68,7 @@ class KeyedStack {
|
|
75
68
|
return entry?.value;
|
76
69
|
}
|
77
70
|
push(key, value) {
|
78
|
-
|
71
|
+
check(!this.has(key), "invariant violation");
|
79
72
|
this.myKeys.add(key);
|
80
73
|
this.myEntries.push({
|
81
74
|
key,
|
@@ -106,7 +99,7 @@ class WeakRefMap {
|
|
106
99
|
return undefined;
|
107
100
|
}
|
108
101
|
set(key, value) {
|
109
|
-
|
102
|
+
check(!this.get(key), "invariant violation");
|
110
103
|
this.myMap.set(key, new WeakRef(value));
|
111
104
|
return ()=>{
|
112
105
|
this.myMap.delete(key);
|
@@ -130,7 +123,7 @@ const [provideInjectionContext, useInjectionContext] = createInjectionContext();
|
|
130
123
|
// @internal
|
131
124
|
function ensureInjectionContext(name) {
|
132
125
|
const context = useInjectionContext();
|
133
|
-
|
126
|
+
check(context, `${name} can only be invoked within an injection context`);
|
134
127
|
return context;
|
135
128
|
}
|
136
129
|
function createInjectionContext() {
|
@@ -360,13 +353,13 @@ class TokenRegistry {
|
|
360
353
|
] || this.getAllFromParent(token, name);
|
361
354
|
}
|
362
355
|
set(token, registration) {
|
363
|
-
|
356
|
+
check(!internals.has(token), `cannot register reserved token ${token.name}`);
|
364
357
|
let registrations = this.myMap.get(token);
|
365
358
|
if (!registrations) {
|
366
359
|
this.myMap.set(token, registrations = []);
|
367
360
|
} else if (registration.name !== undefined) {
|
368
361
|
const existing = registrations.filter((r)=>r.name === registration.name);
|
369
|
-
|
362
|
+
check(existing.length === 0, `a ${token.name} token named '${registration.name}' is already registered`);
|
370
363
|
}
|
371
364
|
registrations.push(registration);
|
372
365
|
}
|
@@ -420,7 +413,7 @@ class TokenRegistry {
|
|
420
413
|
let registrations = thisRegistrations || this.myParent?.getAllFromParent(token, name);
|
421
414
|
if (registrations && name !== undefined) {
|
422
415
|
registrations = registrations.filter((r)=>r.name === name);
|
423
|
-
|
416
|
+
check(registrations.length < 2, `internal error: more than one registration named '${name}'`);
|
424
417
|
}
|
425
418
|
return registrations ?? [];
|
426
419
|
}
|
@@ -561,7 +554,7 @@ function isDisposable(value) {
|
|
561
554
|
const [token, provider, options] = args;
|
562
555
|
const existingProvider = isExistingProvider(provider);
|
563
556
|
const name = existingProvider ? undefined : provider.name;
|
564
|
-
|
557
|
+
check(name === undefined || name.trim(), "the provider name qualifier cannot be empty or blank");
|
565
558
|
if (isClassProvider(provider)) {
|
566
559
|
const metadata = getMetadata(provider.useClass);
|
567
560
|
const registration = {
|
@@ -582,7 +575,7 @@ function isDisposable(value) {
|
|
582
575
|
}
|
583
576
|
} else {
|
584
577
|
if (existingProvider) {
|
585
|
-
|
578
|
+
check(token !== provider.useExisting, `the useExisting token ${token.name} cannot be the same as the token being registered`);
|
586
579
|
}
|
587
580
|
this.myTokenRegistry.set(token, {
|
588
581
|
name: name,
|
@@ -706,7 +699,7 @@ function isDisposable(value) {
|
|
706
699
|
return undefined;
|
707
700
|
}
|
708
701
|
resolveProviderValue(registration, provider) {
|
709
|
-
|
702
|
+
check(registration.provider === provider, "internal error: mismatching provider");
|
710
703
|
if (isClassProvider(provider)) {
|
711
704
|
const Class = provider.useClass;
|
712
705
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
@@ -719,9 +712,7 @@ function isDisposable(value) {
|
|
719
712
|
if (isValueProvider(provider)) {
|
720
713
|
return provider.useValue;
|
721
714
|
}
|
722
|
-
|
723
|
-
assert(false, "internal error: unexpected ExistingProvider");
|
724
|
-
}
|
715
|
+
check(!isExistingProvider(provider), "internal error: unexpected ExistingProvider");
|
725
716
|
expectNever(provider);
|
726
717
|
}
|
727
718
|
resolveScopedValue(registration, factory) {
|
@@ -737,7 +728,7 @@ function isDisposable(value) {
|
|
737
728
|
const options = registration.options;
|
738
729
|
if (resolution.stack.has(provider)) {
|
739
730
|
const dependentRef = resolution.dependents.get(provider);
|
740
|
-
|
731
|
+
check(dependentRef, "circular dependency detected");
|
741
732
|
return dependentRef.current;
|
742
733
|
}
|
743
734
|
const scope = this.resolveScope(options?.scope, context);
|
@@ -796,13 +787,13 @@ function isDisposable(value) {
|
|
796
787
|
resolveConstructorDependencies(registration) {
|
797
788
|
const dependencies = registration.dependencies;
|
798
789
|
if (dependencies) {
|
799
|
-
|
790
|
+
check(isClassProvider(registration.provider), `internal error: not a ClassProvider`);
|
800
791
|
const ctorDeps = dependencies.constructor.filter((d)=>d.appliedBy);
|
801
792
|
if (ctorDeps.length > 0) {
|
802
793
|
// Let's check if all necessary constructor parameters are decorated.
|
803
794
|
// If not, we cannot safely create an instance.
|
804
795
|
const ctor = registration.provider.useClass;
|
805
|
-
|
796
|
+
check(ctor.length === ctorDeps.length, ()=>{
|
806
797
|
const msg = `expected ${ctor.length} decorated constructor parameters in ${ctor.name}`;
|
807
798
|
return msg + `, but found ${ctorDeps.length}`;
|
808
799
|
});
|
@@ -826,7 +817,7 @@ function isDisposable(value) {
|
|
826
817
|
injectDependencies(registration, instance) {
|
827
818
|
const dependencies = registration.dependencies;
|
828
819
|
if (dependencies) {
|
829
|
-
|
820
|
+
check(isClassProvider(registration.provider), `internal error: not a ClassProvider`);
|
830
821
|
const ctor = registration.provider.useClass;
|
831
822
|
// Perform method injection
|
832
823
|
for (const entry of dependencies.methods){
|
@@ -836,7 +827,7 @@ function isDisposable(value) {
|
|
836
827
|
// If not, we cannot safely invoke the method.
|
837
828
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
838
829
|
const method = instance[key];
|
839
|
-
|
830
|
+
check(methodDeps.length === method.length, ()=>{
|
840
831
|
const msg = `expected ${method.length} decorated method parameters`;
|
841
832
|
return msg + ` in ${ctor.name}.${String(key)}, but found ${methodDeps.length}`;
|
842
833
|
});
|
@@ -860,7 +851,7 @@ function isDisposable(value) {
|
|
860
851
|
return instance;
|
861
852
|
}
|
862
853
|
checkDisposed() {
|
863
|
-
|
854
|
+
check(!this.myDisposed, "the container is disposed");
|
864
855
|
}
|
865
856
|
}
|
866
857
|
|
@@ -916,7 +907,7 @@ function isDisposable(value) {
|
|
916
907
|
return function(Class) {
|
917
908
|
const metadata = getMetadata(Class);
|
918
909
|
const currentScope = metadata.scope;
|
919
|
-
|
910
|
+
check(!currentScope || currentScope.value === Scope.Container, ()=>{
|
920
911
|
const { value, appliedBy } = currentScope;
|
921
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.`;
|
922
913
|
});
|
@@ -941,7 +932,7 @@ function forwardRef(token) {
|
|
941
932
|
},
|
942
933
|
getRefToken: ()=>{
|
943
934
|
const tokenOrTokens = token();
|
944
|
-
|
935
|
+
check(!Array.isArray(tokenOrTokens), "internal error: ref tokens should be a single token");
|
945
936
|
return tokenOrTokens;
|
946
937
|
}
|
947
938
|
};
|
@@ -961,7 +952,7 @@ function isTokenRef(value) {
|
|
961
952
|
function updateParameterMetadata(decorator, target, propertyKey, parameterIndex, updateFn) {
|
962
953
|
// Error out immediately if the decorator has been applied to a static method
|
963
954
|
if (propertyKey !== undefined && typeof target === "function") {
|
964
|
-
|
955
|
+
check(false, `@${decorator} cannot be used on static method ${target.name}.${String(propertyKey)}`);
|
965
956
|
}
|
966
957
|
if (propertyKey === undefined) {
|
967
958
|
// Constructor
|
@@ -981,11 +972,30 @@ function updateParameterMetadata(decorator, target, propertyKey, parameterIndex,
|
|
981
972
|
//
|
982
973
|
// @internal
|
983
974
|
function checkSingleDecorator(dependency, target, propertyKey, parameterIndex) {
|
984
|
-
|
985
|
-
const
|
986
|
-
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`;
|
987
978
|
});
|
988
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}`;
|
989
|
+
});
|
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
|
+
}
|
989
999
|
|
990
1000
|
function Inject(token) {
|
991
1001
|
return function(target, propertyKey, parameterIndex) {
|
@@ -1023,6 +1033,7 @@ function InjectAll(token) {
|
|
1023
1033
|
checkSingleDecorator(dependency, target, propertyKey, parameterIndex);
|
1024
1034
|
dependency.appliedBy = "InjectAll";
|
1025
1035
|
dependency.tokenRef = isTokenRef(token) ? token : forwardRef(()=>token);
|
1036
|
+
checkNamedDecorator(dependency, target, propertyKey, parameterIndex);
|
1026
1037
|
});
|
1027
1038
|
};
|
1028
1039
|
}
|
@@ -1045,21 +1056,23 @@ function InjectAll(token) {
|
|
1045
1056
|
*
|
1046
1057
|
* @__NO_SIDE_EFFECTS__
|
1047
1058
|
*/ function Named(name) {
|
1048
|
-
|
1049
|
-
assert(false, "the @Named qualifier cannot be empty or blank");
|
1050
|
-
}
|
1059
|
+
check(name.trim(), "the @Named qualifier cannot be empty or blank");
|
1051
1060
|
return function(target, propertyKey, parameterIndex) {
|
1052
1061
|
if (parameterIndex === undefined) {
|
1053
1062
|
// The decorator has been applied to the class
|
1054
1063
|
const ctor = target;
|
1055
1064
|
const metadata = getMetadata(ctor);
|
1056
|
-
|
1065
|
+
check(metadata.name === undefined, `multiple @Named decorators on class ${ctor.name}, but only one is allowed`);
|
1057
1066
|
metadata.name = name;
|
1058
1067
|
} else {
|
1059
1068
|
// The decorator has been applied to a method parameter
|
1060
1069
|
updateParameterMetadata("Named", target, propertyKey, parameterIndex, (dependency)=>{
|
1061
|
-
|
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
|
+
});
|
1062
1074
|
dependency.name = name;
|
1075
|
+
checkNamedDecorator(dependency, target, propertyKey, parameterIndex);
|
1063
1076
|
});
|
1064
1077
|
}
|
1065
1078
|
};
|
@@ -1081,6 +1094,7 @@ function OptionalAll(token) {
|
|
1081
1094
|
checkSingleDecorator(dependency, target, propertyKey, parameterIndex);
|
1082
1095
|
dependency.appliedBy = "OptionalAll";
|
1083
1096
|
dependency.tokenRef = isTokenRef(token) ? token : forwardRef(()=>token);
|
1097
|
+
checkNamedDecorator(dependency, target, propertyKey, parameterIndex);
|
1084
1098
|
});
|
1085
1099
|
};
|
1086
1100
|
}
|
@@ -1110,7 +1124,7 @@ function OptionalAll(token) {
|
|
1110
1124
|
return function(Class) {
|
1111
1125
|
const metadata = getMetadata(Class);
|
1112
1126
|
const currentScope = metadata.scope;
|
1113
|
-
|
1127
|
+
check(!currentScope || currentScope.value === scope, ()=>{
|
1114
1128
|
const { value, appliedBy } = currentScope;
|
1115
1129
|
const by = appliedBy === "Scoped" ? `another @${appliedBy} decorator` : `@${appliedBy}`;
|
1116
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.`;
|