@lppedd/di-wise-neo 0.9.2 → 0.9.3
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 +134 -117
- package/dist/cjs/index.js.map +1 -1
- package/dist/es/index.mjs +134 -117
- package/dist/es/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/cjs/index.js
CHANGED
@@ -1,32 +1,5 @@
|
|
1
1
|
Object.defineProperty(exports, '__esModule', { value: true });
|
2
2
|
|
3
|
-
/**
|
4
|
-
* Type API.
|
5
|
-
*/ /**
|
6
|
-
* Creates a type token.
|
7
|
-
*
|
8
|
-
* @example
|
9
|
-
* ```ts
|
10
|
-
* const ISpell = createType<Spell>("Spell");
|
11
|
-
* ```
|
12
|
-
*
|
13
|
-
* @__NO_SIDE_EFFECTS__
|
14
|
-
*/ function createType(typeName) {
|
15
|
-
const type = {
|
16
|
-
name: `Type<${typeName}>`,
|
17
|
-
inter: createType,
|
18
|
-
union: createType,
|
19
|
-
toString () {
|
20
|
-
return type.name;
|
21
|
-
}
|
22
|
-
};
|
23
|
-
return type;
|
24
|
-
}
|
25
|
-
// @internal
|
26
|
-
function isConstructor(token) {
|
27
|
-
return typeof token === "function";
|
28
|
-
}
|
29
|
-
|
30
3
|
// @internal
|
31
4
|
function check(condition, message) {
|
32
5
|
if (!condition) {
|
@@ -39,18 +12,35 @@ function expectNever(value) {
|
|
39
12
|
}
|
40
13
|
// @internal
|
41
14
|
function throwUnregisteredError(token, name) {
|
42
|
-
const type = isConstructor(token) ? "class" : "token";
|
43
15
|
const spec = name !== undefined ? `[name=${name}]` : "";
|
44
|
-
throw new Error(tag(`unregistered
|
16
|
+
throw new Error(tag(`unregistered token ${getTokenName(token)}${spec}`));
|
17
|
+
}
|
18
|
+
// @internal
|
19
|
+
function throwExistingUnregisteredError(token, cause) {
|
20
|
+
const message = tag(`failed to resolve token ${getTokenName(token)}`);
|
21
|
+
throw isError(cause) ? new Error(`${message}\n [cause] ${untag(cause.message)}`, {
|
22
|
+
cause
|
23
|
+
}) : new Error(`${message}\n [cause] the aliased token ${getTokenName(cause)} is not registered`);
|
45
24
|
}
|
46
25
|
// @internal
|
47
|
-
function
|
48
|
-
|
49
|
-
|
50
|
-
|
26
|
+
function throwParameterResolutionError(ctor, methodKey, dependency, cause) {
|
27
|
+
const location = getLocation(ctor, methodKey);
|
28
|
+
const token = dependency.tokenRef.getRefToken();
|
29
|
+
const message = tag(`failed to resolve dependency for ${location}(parameter #${dependency.index}: ${token.name})`);
|
30
|
+
throw new Error(`${message}\n [cause] ${untag(cause.message)}`, {
|
31
|
+
cause
|
32
|
+
});
|
33
|
+
}
|
34
|
+
// @internal
|
35
|
+
function getLocation(ctor, methodKey) {
|
36
|
+
const ctorName = ctor.name || "<unnamed>";
|
37
|
+
return methodKey ? `${ctorName}.${String(methodKey)}` : ctorName;
|
38
|
+
}
|
39
|
+
// @internal
|
40
|
+
function getTokenName(token) {
|
41
|
+
return token.name || "<unnamed>";
|
51
42
|
}
|
52
43
|
function isError(value) {
|
53
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
54
44
|
return value && value.stack && value.message && typeof value.message === "string";
|
55
45
|
}
|
56
46
|
function tag(message) {
|
@@ -176,7 +166,7 @@ function injectAll(token) {
|
|
176
166
|
class Metadata {
|
177
167
|
constructor(Class){
|
178
168
|
this.dependencies = {
|
179
|
-
|
169
|
+
ctor: [],
|
180
170
|
methods: new Map()
|
181
171
|
};
|
182
172
|
this.tokensRef = {
|
@@ -192,15 +182,15 @@ class Metadata {
|
|
192
182
|
set name(name) {
|
193
183
|
this.provider.name = name;
|
194
184
|
}
|
195
|
-
|
196
|
-
const i = this.dependencies.
|
185
|
+
getCtorDependency(index) {
|
186
|
+
const i = this.dependencies.ctor.findIndex((d)=>d.index === index);
|
197
187
|
if (i > -1) {
|
198
|
-
return this.dependencies.
|
188
|
+
return this.dependencies.ctor[i];
|
199
189
|
}
|
200
190
|
const dependency = {
|
201
191
|
index: index
|
202
192
|
};
|
203
|
-
this.dependencies.
|
193
|
+
this.dependencies.ctor.push(dependency);
|
204
194
|
return dependency;
|
205
195
|
}
|
206
196
|
getMethodDependency(method, index) {
|
@@ -314,6 +304,33 @@ const Scope = {
|
|
314
304
|
Container: "Container"
|
315
305
|
};
|
316
306
|
|
307
|
+
/**
|
308
|
+
* Type API.
|
309
|
+
*/ /**
|
310
|
+
* Creates a type token.
|
311
|
+
*
|
312
|
+
* @example
|
313
|
+
* ```ts
|
314
|
+
* const ISpell = createType<Spell>("Spell");
|
315
|
+
* ```
|
316
|
+
*
|
317
|
+
* @__NO_SIDE_EFFECTS__
|
318
|
+
*/ function createType(typeName) {
|
319
|
+
const type = {
|
320
|
+
name: `Type<${typeName}>`,
|
321
|
+
inter: createType,
|
322
|
+
union: createType,
|
323
|
+
toString () {
|
324
|
+
return type.name;
|
325
|
+
}
|
326
|
+
};
|
327
|
+
return type;
|
328
|
+
}
|
329
|
+
// @internal
|
330
|
+
function isConstructor(token) {
|
331
|
+
return typeof token === "function";
|
332
|
+
}
|
333
|
+
|
317
334
|
// @internal
|
318
335
|
function getTypeName(value) {
|
319
336
|
switch(typeof value){
|
@@ -328,9 +345,9 @@ function getTypeName(value) {
|
|
328
345
|
}
|
329
346
|
const proto = Object.getPrototypeOf(value);
|
330
347
|
if (proto && proto !== Object.prototype) {
|
331
|
-
const
|
332
|
-
if (typeof
|
333
|
-
return
|
348
|
+
const ctor = proto.constructor;
|
349
|
+
if (typeof ctor === "function" && ctor.name) {
|
350
|
+
return ctor.name;
|
334
351
|
}
|
335
352
|
}
|
336
353
|
}
|
@@ -446,7 +463,6 @@ const builders = new WeakSet();
|
|
446
463
|
// @internal
|
447
464
|
// @internal
|
448
465
|
function isDisposable(value) {
|
449
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
450
466
|
return value && typeof value === "object" && typeof value.dispose === "function";
|
451
467
|
}
|
452
468
|
|
@@ -557,7 +573,7 @@ function isDisposable(value) {
|
|
557
573
|
const [token, provider, options] = args;
|
558
574
|
const existingProvider = isExistingProvider(provider);
|
559
575
|
const name = existingProvider ? undefined : provider.name;
|
560
|
-
check(name === undefined || name.trim(),
|
576
|
+
check(name === undefined || name.trim(), `the name qualifier for token ${getTokenName(token)} must not be empty`);
|
561
577
|
if (isClassProvider(provider)) {
|
562
578
|
const metadata = getMetadata(provider.useClass);
|
563
579
|
const registration = {
|
@@ -703,7 +719,6 @@ function isDisposable(value) {
|
|
703
719
|
const provider = registration.provider;
|
704
720
|
if (isClassProvider(provider)) {
|
705
721
|
const Class = provider.useClass;
|
706
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
707
722
|
return this.resolveScopedValue(token, registration, (args)=>new Class(...args));
|
708
723
|
}
|
709
724
|
if (isFactoryProvider(provider)) {
|
@@ -730,8 +745,8 @@ function isDisposable(value) {
|
|
730
745
|
if (resolution.stack.has(provider)) {
|
731
746
|
const dependentRef = resolution.dependents.get(provider);
|
732
747
|
check(dependentRef, ()=>{
|
733
|
-
const
|
734
|
-
return `circular dependency detected while resolving ${
|
748
|
+
const path = resolution.tokenStack.map((t)=>t.name).join(" → ");
|
749
|
+
return `circular dependency detected while resolving ${path} → ${token.name}`;
|
735
750
|
});
|
736
751
|
return dependentRef.current;
|
737
752
|
}
|
@@ -752,8 +767,8 @@ function isDisposable(value) {
|
|
752
767
|
if (valueRef) {
|
753
768
|
return valueRef.current;
|
754
769
|
}
|
755
|
-
const args = this.
|
756
|
-
const value = this.
|
770
|
+
const args = this.resolveCtorDependencies(registration);
|
771
|
+
const value = this.injectMethodDependencies(registration, factory(args));
|
757
772
|
registration.value = {
|
758
773
|
current: value
|
759
774
|
};
|
@@ -765,8 +780,8 @@ function isDisposable(value) {
|
|
765
780
|
if (valueRef) {
|
766
781
|
return valueRef.current;
|
767
782
|
}
|
768
|
-
const args = this.
|
769
|
-
const value = this.
|
783
|
+
const args = this.resolveCtorDependencies(registration);
|
784
|
+
const value = this.injectMethodDependencies(registration, factory(args));
|
770
785
|
resolution.values.set(provider, {
|
771
786
|
current: value
|
772
787
|
});
|
@@ -774,8 +789,8 @@ function isDisposable(value) {
|
|
774
789
|
}
|
775
790
|
case Scope.Transient:
|
776
791
|
{
|
777
|
-
const args = this.
|
778
|
-
return this.
|
792
|
+
const args = this.resolveCtorDependencies(registration);
|
793
|
+
return this.injectMethodDependencies(registration, factory(args));
|
779
794
|
}
|
780
795
|
}
|
781
796
|
} finally{
|
@@ -789,72 +804,74 @@ function isDisposable(value) {
|
|
789
804
|
}
|
790
805
|
return scope;
|
791
806
|
}
|
792
|
-
|
807
|
+
resolveCtorDependencies(registration) {
|
793
808
|
const dependencies = registration.dependencies;
|
794
809
|
if (dependencies) {
|
795
810
|
check(isClassProvider(registration.provider), `internal error: not a ClassProvider`);
|
796
|
-
const ctorDeps = dependencies.
|
811
|
+
const ctorDeps = dependencies.ctor.filter((d)=>d.appliedBy);
|
797
812
|
if (ctorDeps.length > 0) {
|
798
813
|
// Let's check if all necessary constructor parameters are decorated.
|
799
814
|
// If not, we cannot safely create an instance.
|
800
815
|
const ctor = registration.provider.useClass;
|
801
816
|
check(ctor.length === ctorDeps.length, ()=>{
|
802
|
-
const
|
817
|
+
const location = getLocation(ctor);
|
818
|
+
const msg = `${location} expected ${ctor.length} decorated constructor parameters`;
|
803
819
|
return msg + `, but found ${ctorDeps.length}`;
|
804
820
|
});
|
805
|
-
return
|
806
|
-
const token = dep.tokenRef.getRefToken();
|
807
|
-
switch(dep.appliedBy){
|
808
|
-
case "Inject":
|
809
|
-
return this.resolve(token, dep.name);
|
810
|
-
case "InjectAll":
|
811
|
-
return this.resolveAll(token);
|
812
|
-
case "Optional":
|
813
|
-
return this.resolve(token, true, dep.name);
|
814
|
-
case "OptionalAll":
|
815
|
-
return this.resolveAll(token, true);
|
816
|
-
}
|
817
|
-
});
|
821
|
+
return this.resolveArgs(ctorDeps, ctor);
|
818
822
|
}
|
819
823
|
}
|
820
824
|
return [];
|
821
825
|
}
|
822
|
-
|
826
|
+
injectMethodDependencies(registration, instance) {
|
823
827
|
const dependencies = registration.dependencies;
|
824
828
|
if (dependencies) {
|
825
829
|
check(isClassProvider(registration.provider), `internal error: not a ClassProvider`);
|
826
830
|
const ctor = registration.provider.useClass;
|
827
831
|
// Perform method injection
|
828
832
|
for (const entry of dependencies.methods){
|
829
|
-
const
|
833
|
+
const methodKey = entry[0];
|
830
834
|
const methodDeps = entry[1].filter((d)=>d.appliedBy);
|
831
835
|
// Let's check if all necessary method parameters are decorated.
|
832
836
|
// If not, we cannot safely invoke the method.
|
833
|
-
|
834
|
-
const method = instance[key];
|
837
|
+
const method = instance[methodKey];
|
835
838
|
check(methodDeps.length === method.length, ()=>{
|
836
|
-
const
|
837
|
-
|
838
|
-
|
839
|
-
const args = methodDeps.sort((a, b)=>a.index - b.index).map((dep)=>{
|
840
|
-
const token = dep.tokenRef.getRefToken();
|
841
|
-
switch(dep.appliedBy){
|
842
|
-
case "Inject":
|
843
|
-
return injectBy(instance, token, dep.name);
|
844
|
-
case "InjectAll":
|
845
|
-
return injectAll(token);
|
846
|
-
case "Optional":
|
847
|
-
return optionalBy(instance, token, dep.name);
|
848
|
-
case "OptionalAll":
|
849
|
-
return optionalAll(token);
|
850
|
-
}
|
839
|
+
const location = getLocation(ctor, methodKey);
|
840
|
+
const msg = `${location} expected ${method.length} decorated method parameters`;
|
841
|
+
return msg + `, but found ${methodDeps.length}`;
|
851
842
|
});
|
852
|
-
|
843
|
+
const args = this.resolveArgs(methodDeps, ctor, instance, methodKey);
|
853
844
|
method.bind(instance)(...args);
|
854
845
|
}
|
855
846
|
}
|
856
847
|
return instance;
|
857
848
|
}
|
849
|
+
resolveArgs(deps, ctor, instance, methodKey) {
|
850
|
+
const sortedDeps = deps.sort((a, b)=>a.index - b.index);
|
851
|
+
const args = [];
|
852
|
+
for (const dep of sortedDeps){
|
853
|
+
try {
|
854
|
+
args.push(this.resolveDependency(dep, instance));
|
855
|
+
} catch (e) {
|
856
|
+
throwParameterResolutionError(ctor, methodKey, dep, e);
|
857
|
+
}
|
858
|
+
}
|
859
|
+
return args;
|
860
|
+
}
|
861
|
+
resolveDependency(dependency, instance) {
|
862
|
+
const token = dependency.tokenRef.getRefToken();
|
863
|
+
const name = dependency.name;
|
864
|
+
switch(dependency.appliedBy){
|
865
|
+
case "Inject":
|
866
|
+
return instance ? injectBy(instance, token, name) : this.resolve(token, name);
|
867
|
+
case "InjectAll":
|
868
|
+
return instance ? injectAll(token) : this.resolveAll(token);
|
869
|
+
case "Optional":
|
870
|
+
return instance ? optionalBy(instance, token, name) : this.resolve(token, true, name);
|
871
|
+
case "OptionalAll":
|
872
|
+
return instance ? optionalAll(token) : this.resolveAll(token, true);
|
873
|
+
}
|
874
|
+
}
|
858
875
|
checkDisposed() {
|
859
876
|
check(!this.myDisposed, "the container is disposed");
|
860
877
|
}
|
@@ -910,11 +927,13 @@ function isDisposable(value) {
|
|
910
927
|
* @__NO_SIDE_EFFECTS__
|
911
928
|
*/ function EagerInstantiate() {
|
912
929
|
return function(Class) {
|
913
|
-
const
|
930
|
+
const ctor = Class;
|
931
|
+
const metadata = getMetadata(ctor);
|
914
932
|
const currentScope = metadata.scope;
|
915
933
|
check(!currentScope || currentScope.value === Scope.Container, ()=>{
|
916
934
|
const { value, appliedBy } = currentScope;
|
917
|
-
|
935
|
+
const className = getTokenName(ctor);
|
936
|
+
return `class ${className}: 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.`;
|
918
937
|
});
|
919
938
|
metadata.eagerInstantiate = true;
|
920
939
|
metadata.scope = {
|
@@ -944,30 +963,28 @@ function forwardRef(token) {
|
|
944
963
|
}
|
945
964
|
// @internal
|
946
965
|
function isTokensRef(value) {
|
947
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
948
966
|
return value && typeof value === "object" && typeof value.getRefTokens === "function";
|
949
967
|
}
|
950
968
|
// @internal
|
951
969
|
function isTokenRef(value) {
|
952
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
953
970
|
return value && typeof value === "object" && typeof value.getRefToken === "function";
|
954
971
|
}
|
955
972
|
|
956
973
|
// @internal
|
957
|
-
function updateParameterMetadata(decorator, target,
|
974
|
+
function updateParameterMetadata(decorator, target, methodKey, parameterIndex, updateFn) {
|
958
975
|
// Error out immediately if the decorator has been applied to a static method
|
959
|
-
if (
|
960
|
-
check(false, `@${decorator} cannot be used on static method ${target.name}.${String(
|
976
|
+
if (methodKey !== undefined && typeof target === "function") {
|
977
|
+
check(false, `@${decorator} cannot be used on static method ${target.name}.${String(methodKey)}`);
|
961
978
|
}
|
962
|
-
if (
|
979
|
+
if (methodKey === undefined) {
|
963
980
|
// Constructor
|
964
981
|
const metadata = getMetadata(target);
|
965
|
-
const dependency = metadata.
|
982
|
+
const dependency = metadata.getCtorDependency(parameterIndex);
|
966
983
|
updateFn(dependency);
|
967
984
|
} else {
|
968
985
|
// Instance method
|
969
986
|
const metadata = getMetadata(target.constructor);
|
970
|
-
const dependency = metadata.getMethodDependency(
|
987
|
+
const dependency = metadata.getMethodDependency(methodKey, parameterIndex);
|
971
988
|
updateFn(dependency);
|
972
989
|
}
|
973
990
|
}
|
@@ -976,30 +993,30 @@ function updateParameterMetadata(decorator, target, propertyKey, parameterIndex,
|
|
976
993
|
// understand which one "wins", unless the user is aware of the decorator evaluation order.
|
977
994
|
//
|
978
995
|
// @internal
|
979
|
-
function checkSingleDecorator(dependency, target,
|
996
|
+
function checkSingleDecorator(dependency, target, methodKey, parameterIndex) {
|
980
997
|
check(dependency.appliedBy === undefined, ()=>{
|
981
|
-
const
|
982
|
-
return `multiple injection decorators on ${
|
998
|
+
const param = describeParam(target, methodKey, parameterIndex);
|
999
|
+
return `multiple injection decorators on ${param}, but only one is allowed`;
|
983
1000
|
});
|
984
1001
|
}
|
985
1002
|
// Checks that the `@Named` decorator is not used in combination with
|
986
1003
|
// `@InjectAll` or `@OptionalAll`, which ignore the name qualification.
|
987
1004
|
//
|
988
1005
|
// @internal
|
989
|
-
function checkNamedDecorator(dependency, target,
|
1006
|
+
function checkNamedDecorator(dependency, target, methodKey, parameterIndex) {
|
990
1007
|
const { appliedBy, name } = dependency;
|
991
1008
|
check(name === undefined || appliedBy !== "InjectAll" && appliedBy !== "OptionalAll", ()=>{
|
992
|
-
const
|
993
|
-
return `@Named has no effect on ${
|
1009
|
+
const param = describeParam(target, methodKey, parameterIndex);
|
1010
|
+
return `@Named has no effect on ${param} when used with @${appliedBy}`;
|
994
1011
|
});
|
995
1012
|
}
|
996
1013
|
// Returns a human-readable description of the parameter location.
|
997
1014
|
// For example: "Wizard constructor parameter 2" or "Wizard.set parameter 0"
|
998
1015
|
//
|
999
1016
|
// @internal
|
1000
|
-
function
|
1001
|
-
const location =
|
1002
|
-
return `${location}
|
1017
|
+
function describeParam(target, methodKey, parameterIndex) {
|
1018
|
+
const location = methodKey === undefined ? target.name : `${target.constructor.name}.${String(methodKey)}`;
|
1019
|
+
return `${location}(parameter #${parameterIndex})`;
|
1003
1020
|
}
|
1004
1021
|
|
1005
1022
|
function Inject(token) {
|
@@ -1061,20 +1078,21 @@ function InjectAll(token) {
|
|
1061
1078
|
*
|
1062
1079
|
* @__NO_SIDE_EFFECTS__
|
1063
1080
|
*/ function Named(name) {
|
1064
|
-
check(name.trim(), "the @Named qualifier
|
1081
|
+
check(name.trim(), "the @Named qualifier must not be empty");
|
1065
1082
|
return function(target, propertyKey, parameterIndex) {
|
1066
1083
|
if (parameterIndex === undefined) {
|
1067
1084
|
// The decorator has been applied to the class
|
1068
1085
|
const ctor = target;
|
1069
1086
|
const metadata = getMetadata(ctor);
|
1070
|
-
|
1087
|
+
const className = getTokenName(ctor);
|
1088
|
+
check(metadata.name === undefined, `multiple @Named decorators on class ${className}, but only one is allowed`);
|
1071
1089
|
metadata.name = name;
|
1072
1090
|
} else {
|
1073
1091
|
// The decorator has been applied to a method parameter
|
1074
1092
|
updateParameterMetadata("Named", target, propertyKey, parameterIndex, (dependency)=>{
|
1075
1093
|
check(dependency.name === undefined, ()=>{
|
1076
|
-
const
|
1077
|
-
return `multiple @Named decorators on ${
|
1094
|
+
const param = describeParam(target, propertyKey, parameterIndex);
|
1095
|
+
return `multiple @Named decorators on ${param}, but only one is allowed`;
|
1078
1096
|
});
|
1079
1097
|
dependency.name = name;
|
1080
1098
|
checkNamedDecorator(dependency, target, propertyKey, parameterIndex);
|
@@ -1127,12 +1145,14 @@ function OptionalAll(token) {
|
|
1127
1145
|
* @__NO_SIDE_EFFECTS__
|
1128
1146
|
*/ function Scoped(scope) {
|
1129
1147
|
return function(Class) {
|
1130
|
-
const
|
1148
|
+
const ctor = Class;
|
1149
|
+
const metadata = getMetadata(ctor);
|
1131
1150
|
const currentScope = metadata.scope;
|
1132
1151
|
check(!currentScope || currentScope.value === scope, ()=>{
|
1133
1152
|
const { value, appliedBy } = currentScope;
|
1134
1153
|
const by = appliedBy === "Scoped" ? `another @${appliedBy} decorator` : `@${appliedBy}`;
|
1135
|
-
|
1154
|
+
const className = getTokenName(ctor);
|
1155
|
+
return `class ${className}: 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.`;
|
1136
1156
|
});
|
1137
1157
|
metadata.scope = {
|
1138
1158
|
value: scope,
|
@@ -1214,10 +1234,7 @@ function OptionalAll(token) {
|
|
1214
1234
|
use (key, wrap) {
|
1215
1235
|
// We need to bind the 'this' context of the function to the container
|
1216
1236
|
// before passing it to the middleware wrapper.
|
1217
|
-
//
|
1218
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access,@typescript-eslint/no-unsafe-call
|
1219
1237
|
const fn = container[key].bind(container);
|
1220
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
1221
1238
|
container[key] = wrap(fn);
|
1222
1239
|
return composer;
|
1223
1240
|
}
|