@lppedd/di-wise-neo 0.10.0 → 0.11.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/dist/cjs/index.d.ts +24 -3
- package/dist/cjs/index.js +118 -52
- package/dist/cjs/index.js.map +1 -1
- package/dist/es/index.d.mts +24 -3
- package/dist/es/index.mjs +118 -52
- package/dist/es/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/es/index.d.mts
CHANGED
@@ -147,7 +147,26 @@ interface ExistingProvider<Value> {
|
|
147
147
|
/**
|
148
148
|
* The existing token to alias.
|
149
149
|
*/
|
150
|
-
readonly useExisting: Token<Value
|
150
|
+
readonly useExisting: Token<Value> | {
|
151
|
+
readonly token: Token<Value>;
|
152
|
+
readonly name?: string;
|
153
|
+
};
|
154
|
+
/**
|
155
|
+
* An optional name to qualify this provider.
|
156
|
+
* If specified, the token must be resolved using the same name.
|
157
|
+
*
|
158
|
+
* @example
|
159
|
+
* ```ts
|
160
|
+
* export class ExtensionContext {
|
161
|
+
* // Decorator-based injection
|
162
|
+
* constructor(@Inject(ISecretStorage) @Named("persistent") secretStorage: SecretStorage) {}
|
163
|
+
*
|
164
|
+
* // Function-based injection
|
165
|
+
* constructor(secretStorage = inject(ISecretStorage, "persistent")) {}
|
166
|
+
* }
|
167
|
+
* ```
|
168
|
+
*/
|
169
|
+
readonly name?: string;
|
151
170
|
}
|
152
171
|
/**
|
153
172
|
* A token provider.
|
@@ -324,7 +343,8 @@ interface Container {
|
|
324
343
|
*
|
325
344
|
* If the class is not registered, but it is decorated with {@link AutoRegister},
|
326
345
|
* or {@link ContainerOptions.autoRegister} is true, the class is registered automatically.
|
327
|
-
* Otherwise, resolution fails.
|
346
|
+
* Otherwise, the resolution fails.
|
347
|
+
*
|
328
348
|
* The scope for the automatic registration is determined by either
|
329
349
|
* the {@link Scoped} decorator on the class, or {@link ContainerOptions.defaultScope}.
|
330
350
|
*
|
@@ -372,7 +392,8 @@ interface Container {
|
|
372
392
|
*
|
373
393
|
* If the class is not registered, but it is decorated with {@link AutoRegister},
|
374
394
|
* or {@link ContainerOptions.autoRegister} is true, the class is registered automatically.
|
375
|
-
* Otherwise, resolution fails.
|
395
|
+
* Otherwise, the resolution fails.
|
396
|
+
*
|
376
397
|
* The scope for the automatic registration is determined by either
|
377
398
|
* the {@link Scoped} decorator on the class, or {@link ContainerOptions.defaultScope}.
|
378
399
|
*
|
package/dist/es/index.mjs
CHANGED
@@ -5,27 +5,38 @@ function check(condition, message) {
|
|
5
5
|
}
|
6
6
|
}
|
7
7
|
// @internal
|
8
|
-
function
|
9
|
-
throw new
|
8
|
+
function throwUnregisteredError(tokenInfo) {
|
9
|
+
throw new Error(tag(`unregistered token ${getFullTokenName(tokenInfo)}`));
|
10
10
|
}
|
11
11
|
// @internal
|
12
|
-
function
|
13
|
-
const
|
14
|
-
|
12
|
+
function throwTargetUnregisteredError(tokenInfo, aliases) {
|
13
|
+
const path = aliases.length > 0 ? ` (alias for ${getTokenPath(aliases)})` : "";
|
14
|
+
const desc = getFullTokenName(tokenInfo) + path;
|
15
|
+
const cause = `\n [cause] useExisting points to unregistered token ${getFullTokenName(aliases.at(-1))}`;
|
16
|
+
throw new Error(tag(`failed to resolve token ${desc}`) + cause);
|
15
17
|
}
|
16
18
|
// @internal
|
17
|
-
function
|
18
|
-
const
|
19
|
-
throw
|
19
|
+
function throwCircularAliasError(aliases) {
|
20
|
+
const path = getTokenPath(aliases);
|
21
|
+
throw new Error(tag(`circular alias detected while resolving ${path}`));
|
22
|
+
}
|
23
|
+
// @internal
|
24
|
+
function throwResolutionError(tokenInfo, aliases, cause) {
|
25
|
+
const path = aliases.length > 0 ? ` (alias for ${getTokenPath(aliases)})` : "";
|
26
|
+
const desc = getFullTokenName(tokenInfo) + path;
|
27
|
+
throw new Error(tag(`failed to resolve token ${desc}`) + getCause(cause), {
|
20
28
|
cause
|
21
|
-
})
|
29
|
+
});
|
22
30
|
}
|
23
31
|
// @internal
|
24
32
|
function throwParameterResolutionError(ctor, methodKey, dependency, cause) {
|
25
33
|
const location = getLocation(ctor, methodKey);
|
26
|
-
const tokenName =
|
34
|
+
const tokenName = getFullTokenName([
|
35
|
+
dependency.tokenRef.getRefToken(),
|
36
|
+
dependency.name
|
37
|
+
]);
|
27
38
|
const msg = tag(`failed to resolve dependency for ${location}(parameter #${dependency.index}: ${tokenName})`);
|
28
|
-
throw new Error(
|
39
|
+
throw new Error(msg + getCause(cause), {
|
29
40
|
cause
|
30
41
|
});
|
31
42
|
}
|
@@ -35,11 +46,27 @@ function getLocation(ctor, methodKey) {
|
|
35
46
|
return methodKey ? `${ctorName}.${String(methodKey)}` : ctorName;
|
36
47
|
}
|
37
48
|
// @internal
|
49
|
+
function getTokenPath(tokens) {
|
50
|
+
return tokens.map(getFullTokenName).join(" \u2192 ");
|
51
|
+
}
|
52
|
+
// @internal
|
38
53
|
function getTokenName(token) {
|
39
54
|
return token.name || "<unnamed>";
|
40
55
|
}
|
56
|
+
function getFullTokenName(tokenInfo) {
|
57
|
+
const [token, name] = tokenInfo;
|
58
|
+
const tokenName = token.name || "<unnamed>";
|
59
|
+
return name ? `${tokenName}["${name}"]` : tokenName;
|
60
|
+
}
|
61
|
+
function getCause(error) {
|
62
|
+
if (!error) {
|
63
|
+
return "";
|
64
|
+
}
|
65
|
+
const msg = isError(error) ? error.message : String(error);
|
66
|
+
return `\n [cause] ${untag(msg)}`;
|
67
|
+
}
|
41
68
|
function isError(value) {
|
42
|
-
return value
|
69
|
+
return value?.stack && typeof value?.message === "string";
|
43
70
|
}
|
44
71
|
function tag(message) {
|
45
72
|
return `[di-wise-neo] ${message}`;
|
@@ -301,9 +328,8 @@ const Scope = {
|
|
301
328
|
Container: "Container"
|
302
329
|
};
|
303
330
|
|
331
|
+
const typeSymbol = Symbol("di-wise-neo.typeToken");
|
304
332
|
/**
|
305
|
-
* Type API.
|
306
|
-
*/ /**
|
307
333
|
* Creates a type token.
|
308
334
|
*
|
309
335
|
* @example
|
@@ -317,6 +343,7 @@ const Scope = {
|
|
317
343
|
name: `Type<${typeName}>`,
|
318
344
|
inter: createType,
|
319
345
|
union: createType,
|
346
|
+
__type: typeSymbol,
|
320
347
|
toString () {
|
321
348
|
return type.name;
|
322
349
|
}
|
@@ -324,6 +351,10 @@ const Scope = {
|
|
324
351
|
return type;
|
325
352
|
}
|
326
353
|
// @internal
|
354
|
+
function isType(token) {
|
355
|
+
return token.__type === typeSymbol;
|
356
|
+
}
|
357
|
+
// @internal
|
327
358
|
function isConstructor(token) {
|
328
359
|
return typeof token === "function";
|
329
360
|
}
|
@@ -429,8 +460,7 @@ class TokenRegistry {
|
|
429
460
|
return Array.from(values);
|
430
461
|
}
|
431
462
|
getAllFromParent(token, name) {
|
432
|
-
|
433
|
-
let registrations = thisRegistrations || this.myParent?.getAllFromParent(token, name);
|
463
|
+
let registrations = this.myMap.get(token) || this.myParent?.getAllFromParent(token, name);
|
434
464
|
if (registrations && name !== undefined) {
|
435
465
|
registrations = registrations.filter((r)=>r.name === name);
|
436
466
|
check(registrations.length < 2, `internal error: more than one registration named '${name}'`);
|
@@ -545,8 +575,9 @@ function isDisposable(value) {
|
|
545
575
|
if (args.length === 1) {
|
546
576
|
const Class = args[0];
|
547
577
|
const metadata = getMetadata(Class);
|
578
|
+
const name = metadata.name;
|
548
579
|
const registration = {
|
549
|
-
name:
|
580
|
+
name: name,
|
550
581
|
// The provider is of type ClassProvider, initialized by getMetadata
|
551
582
|
provider: metadata.provider,
|
552
583
|
options: {
|
@@ -560,8 +591,12 @@ function isDisposable(value) {
|
|
560
591
|
// These tokens will point to the original Class token and will have the same scope.
|
561
592
|
for (const token of metadata.tokensRef.getRefTokens()){
|
562
593
|
this.myTokenRegistry.set(token, {
|
594
|
+
name: name,
|
563
595
|
provider: {
|
564
|
-
useExisting:
|
596
|
+
useExisting: {
|
597
|
+
token: Class,
|
598
|
+
name: name
|
599
|
+
}
|
565
600
|
}
|
566
601
|
});
|
567
602
|
}
|
@@ -571,9 +606,8 @@ function isDisposable(value) {
|
|
571
606
|
}
|
572
607
|
} else {
|
573
608
|
const [token, provider, options] = args;
|
574
|
-
const
|
575
|
-
|
576
|
-
check(name === undefined || name.trim(), `the name qualifier for token ${getTokenName(token)} must not be empty`);
|
609
|
+
const name = provider.name;
|
610
|
+
check(name === undefined || name.trim(), `name qualifier for token ${getTokenName(token)} must not be empty`);
|
577
611
|
if (isClassProvider(provider)) {
|
578
612
|
const metadata = getMetadata(provider.useClass);
|
579
613
|
const registration = {
|
@@ -593,8 +627,9 @@ function isDisposable(value) {
|
|
593
627
|
this.resolveProviderValue(token, registration);
|
594
628
|
}
|
595
629
|
} else {
|
596
|
-
if (
|
597
|
-
|
630
|
+
if (isExistingProvider(provider)) {
|
631
|
+
const [targetToken] = this.getTargetToken(provider);
|
632
|
+
check(token !== targetToken, `token ${getTokenName(token)} cannot alias itself via useExisting`);
|
598
633
|
}
|
599
634
|
this.myTokenRegistry.set(token, {
|
600
635
|
name: name,
|
@@ -631,10 +666,7 @@ function isDisposable(value) {
|
|
631
666
|
if (!registration && isConstructor(token)) {
|
632
667
|
registration = this.autoRegisterClass(token, localName);
|
633
668
|
}
|
634
|
-
|
635
|
-
return this.resolveRegistration(token, registration, localName);
|
636
|
-
}
|
637
|
-
return localOptional ? undefined : throwUnregisteredError(token, localName);
|
669
|
+
return this.resolveRegistration(token, registration, localOptional, localName);
|
638
670
|
}
|
639
671
|
resolveAll(token, optional) {
|
640
672
|
this.checkDisposed();
|
@@ -647,11 +679,13 @@ function isDisposable(value) {
|
|
647
679
|
];
|
648
680
|
}
|
649
681
|
}
|
650
|
-
if (registrations.length
|
651
|
-
|
652
|
-
|
682
|
+
if (registrations.length === 0 && !optional) {
|
683
|
+
throwUnregisteredError([
|
684
|
+
token
|
685
|
+
]);
|
653
686
|
}
|
654
|
-
return
|
687
|
+
return registrations //
|
688
|
+
.map((registration)=>this.resolveRegistration(token, registration, optional)).filter((value)=>value != null);
|
655
689
|
}
|
656
690
|
dispose() {
|
657
691
|
if (this.myDisposed) {
|
@@ -678,26 +712,57 @@ function isDisposable(value) {
|
|
678
712
|
// Allow values to be GCed
|
679
713
|
disposedRefs.clear();
|
680
714
|
}
|
681
|
-
resolveRegistration(token, registration, name) {
|
682
|
-
|
683
|
-
while(isExistingProvider(
|
684
|
-
const targetToken =
|
685
|
-
|
686
|
-
|
687
|
-
|
715
|
+
resolveRegistration(token, registration, optional, name) {
|
716
|
+
const aliases = [];
|
717
|
+
while(registration && isExistingProvider(registration.provider)){
|
718
|
+
const [targetToken, targetName] = this.getTargetToken(registration.provider);
|
719
|
+
if (aliases.some(([t])=>t === targetToken)) {
|
720
|
+
throwCircularAliasError([
|
721
|
+
[
|
722
|
+
token,
|
723
|
+
name
|
724
|
+
],
|
725
|
+
...aliases
|
726
|
+
]);
|
727
|
+
}
|
728
|
+
// eslint-disable-next-line no-param-reassign
|
729
|
+
registration = this.myTokenRegistry.get(targetToken, targetName);
|
730
|
+
aliases.push([
|
731
|
+
targetToken,
|
732
|
+
targetName
|
733
|
+
]);
|
734
|
+
if (!registration && !optional) {
|
735
|
+
throwTargetUnregisteredError([
|
736
|
+
token,
|
737
|
+
name
|
738
|
+
], aliases);
|
688
739
|
}
|
689
740
|
}
|
741
|
+
if (!registration) {
|
742
|
+
return optional ? undefined : throwUnregisteredError([
|
743
|
+
token,
|
744
|
+
name
|
745
|
+
]);
|
746
|
+
}
|
690
747
|
try {
|
691
|
-
return this.resolveProviderValue(token,
|
748
|
+
return this.resolveProviderValue(token, registration);
|
692
749
|
} catch (e) {
|
693
|
-
|
694
|
-
|
695
|
-
|
696
|
-
|
697
|
-
}
|
698
|
-
throw e;
|
750
|
+
throwResolutionError([
|
751
|
+
token,
|
752
|
+
name
|
753
|
+
], aliases, e);
|
699
754
|
}
|
700
755
|
}
|
756
|
+
getTargetToken(provider) {
|
757
|
+
const token = provider.useExisting;
|
758
|
+
return isType(token) || isConstructor(token) //
|
759
|
+
? [
|
760
|
+
token
|
761
|
+
] : [
|
762
|
+
token.token,
|
763
|
+
token.name
|
764
|
+
];
|
765
|
+
}
|
701
766
|
autoRegisterClass(Class, name) {
|
702
767
|
const metadata = getMetadata(Class);
|
703
768
|
const autoRegister = metadata.autoRegister ?? this.myOptions.autoRegister;
|
@@ -728,8 +793,7 @@ function isDisposable(value) {
|
|
728
793
|
if (isValueProvider(provider)) {
|
729
794
|
return provider.useValue;
|
730
795
|
}
|
731
|
-
check(
|
732
|
-
expectNever(provider);
|
796
|
+
check(false, "internal error: unexpected ExistingProvider");
|
733
797
|
}
|
734
798
|
resolveScopedValue(token, registration, factory) {
|
735
799
|
let context = useInjectionContext();
|
@@ -744,7 +808,9 @@ function isDisposable(value) {
|
|
744
808
|
if (resolution.stack.has(provider)) {
|
745
809
|
const dependentRef = resolution.dependents.get(provider);
|
746
810
|
check(dependentRef, ()=>{
|
747
|
-
const path = resolution.tokenStack.
|
811
|
+
const path = getTokenPath(resolution.tokenStack.concat(token).map((t)=>[
|
812
|
+
t
|
813
|
+
]));
|
748
814
|
return `circular dependency detected while resolving ${path}`;
|
749
815
|
});
|
750
816
|
return dependentRef.current;
|
@@ -808,7 +874,7 @@ function isDisposable(value) {
|
|
808
874
|
check(ctor.length === ctorDeps.length, ()=>{
|
809
875
|
const location = getLocation(ctor);
|
810
876
|
const msg = `${location} expected ${ctor.length} decorated constructor parameters`;
|
811
|
-
return msg
|
877
|
+
return `${msg}, but found ${ctorDeps.length}`;
|
812
878
|
});
|
813
879
|
return this.resolveArgs(ctorDeps, ctor);
|
814
880
|
}
|
@@ -830,7 +896,7 @@ function isDisposable(value) {
|
|
830
896
|
check(methodDeps.length === method.length, ()=>{
|
831
897
|
const location = getLocation(ctor, methodKey);
|
832
898
|
const msg = `${location} expected ${method.length} decorated method parameters`;
|
833
|
-
return msg
|
899
|
+
return `${msg}, but found ${methodDeps.length}`;
|
834
900
|
});
|
835
901
|
const args = this.resolveArgs(methodDeps, ctor, instance, methodKey);
|
836
902
|
method.bind(instance)(...args);
|
@@ -865,7 +931,7 @@ function isDisposable(value) {
|
|
865
931
|
}
|
866
932
|
}
|
867
933
|
checkDisposed() {
|
868
|
-
check(!this.myDisposed, "
|
934
|
+
check(!this.myDisposed, "container is disposed");
|
869
935
|
}
|
870
936
|
}
|
871
937
|
|
@@ -1070,7 +1136,7 @@ function InjectAll(token) {
|
|
1070
1136
|
*
|
1071
1137
|
* @__NO_SIDE_EFFECTS__
|
1072
1138
|
*/ function Named(name) {
|
1073
|
-
check(name.trim(), "
|
1139
|
+
check(name.trim(), "@Named qualifier must not be empty");
|
1074
1140
|
return function(target, propertyKey, parameterIndex) {
|
1075
1141
|
if (parameterIndex === undefined) {
|
1076
1142
|
// The decorator has been applied to the class
|