@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/cjs/index.d.ts
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/cjs/index.js
CHANGED
@@ -7,27 +7,38 @@ function check(condition, message) {
|
|
7
7
|
}
|
8
8
|
}
|
9
9
|
// @internal
|
10
|
-
function
|
11
|
-
throw new
|
10
|
+
function throwUnregisteredError(tokenInfo) {
|
11
|
+
throw new Error(tag(`unregistered token ${getFullTokenName(tokenInfo)}`));
|
12
12
|
}
|
13
13
|
// @internal
|
14
|
-
function
|
15
|
-
const
|
16
|
-
|
14
|
+
function throwTargetUnregisteredError(tokenInfo, aliases) {
|
15
|
+
const path = aliases.length > 0 ? ` (alias for ${getTokenPath(aliases)})` : "";
|
16
|
+
const desc = getFullTokenName(tokenInfo) + path;
|
17
|
+
const cause = `\n [cause] useExisting points to unregistered token ${getFullTokenName(aliases.at(-1))}`;
|
18
|
+
throw new Error(tag(`failed to resolve token ${desc}`) + cause);
|
17
19
|
}
|
18
20
|
// @internal
|
19
|
-
function
|
20
|
-
const
|
21
|
-
throw
|
21
|
+
function throwCircularAliasError(aliases) {
|
22
|
+
const path = getTokenPath(aliases);
|
23
|
+
throw new Error(tag(`circular alias detected while resolving ${path}`));
|
24
|
+
}
|
25
|
+
// @internal
|
26
|
+
function throwResolutionError(tokenInfo, aliases, cause) {
|
27
|
+
const path = aliases.length > 0 ? ` (alias for ${getTokenPath(aliases)})` : "";
|
28
|
+
const desc = getFullTokenName(tokenInfo) + path;
|
29
|
+
throw new Error(tag(`failed to resolve token ${desc}`) + getCause(cause), {
|
22
30
|
cause
|
23
|
-
})
|
31
|
+
});
|
24
32
|
}
|
25
33
|
// @internal
|
26
34
|
function throwParameterResolutionError(ctor, methodKey, dependency, cause) {
|
27
35
|
const location = getLocation(ctor, methodKey);
|
28
|
-
const tokenName =
|
36
|
+
const tokenName = getFullTokenName([
|
37
|
+
dependency.tokenRef.getRefToken(),
|
38
|
+
dependency.name
|
39
|
+
]);
|
29
40
|
const msg = tag(`failed to resolve dependency for ${location}(parameter #${dependency.index}: ${tokenName})`);
|
30
|
-
throw new Error(
|
41
|
+
throw new Error(msg + getCause(cause), {
|
31
42
|
cause
|
32
43
|
});
|
33
44
|
}
|
@@ -37,11 +48,27 @@ function getLocation(ctor, methodKey) {
|
|
37
48
|
return methodKey ? `${ctorName}.${String(methodKey)}` : ctorName;
|
38
49
|
}
|
39
50
|
// @internal
|
51
|
+
function getTokenPath(tokens) {
|
52
|
+
return tokens.map(getFullTokenName).join(" \u2192 ");
|
53
|
+
}
|
54
|
+
// @internal
|
40
55
|
function getTokenName(token) {
|
41
56
|
return token.name || "<unnamed>";
|
42
57
|
}
|
58
|
+
function getFullTokenName(tokenInfo) {
|
59
|
+
const [token, name] = tokenInfo;
|
60
|
+
const tokenName = token.name || "<unnamed>";
|
61
|
+
return name ? `${tokenName}["${name}"]` : tokenName;
|
62
|
+
}
|
63
|
+
function getCause(error) {
|
64
|
+
if (!error) {
|
65
|
+
return "";
|
66
|
+
}
|
67
|
+
const msg = isError(error) ? error.message : String(error);
|
68
|
+
return `\n [cause] ${untag(msg)}`;
|
69
|
+
}
|
43
70
|
function isError(value) {
|
44
|
-
return value
|
71
|
+
return value?.stack && typeof value?.message === "string";
|
45
72
|
}
|
46
73
|
function tag(message) {
|
47
74
|
return `[di-wise-neo] ${message}`;
|
@@ -303,9 +330,8 @@ const Scope = {
|
|
303
330
|
Container: "Container"
|
304
331
|
};
|
305
332
|
|
333
|
+
const typeSymbol = Symbol("di-wise-neo.typeToken");
|
306
334
|
/**
|
307
|
-
* Type API.
|
308
|
-
*/ /**
|
309
335
|
* Creates a type token.
|
310
336
|
*
|
311
337
|
* @example
|
@@ -319,6 +345,7 @@ const Scope = {
|
|
319
345
|
name: `Type<${typeName}>`,
|
320
346
|
inter: createType,
|
321
347
|
union: createType,
|
348
|
+
__type: typeSymbol,
|
322
349
|
toString () {
|
323
350
|
return type.name;
|
324
351
|
}
|
@@ -326,6 +353,10 @@ const Scope = {
|
|
326
353
|
return type;
|
327
354
|
}
|
328
355
|
// @internal
|
356
|
+
function isType(token) {
|
357
|
+
return token.__type === typeSymbol;
|
358
|
+
}
|
359
|
+
// @internal
|
329
360
|
function isConstructor(token) {
|
330
361
|
return typeof token === "function";
|
331
362
|
}
|
@@ -431,8 +462,7 @@ class TokenRegistry {
|
|
431
462
|
return Array.from(values);
|
432
463
|
}
|
433
464
|
getAllFromParent(token, name) {
|
434
|
-
|
435
|
-
let registrations = thisRegistrations || this.myParent?.getAllFromParent(token, name);
|
465
|
+
let registrations = this.myMap.get(token) || this.myParent?.getAllFromParent(token, name);
|
436
466
|
if (registrations && name !== undefined) {
|
437
467
|
registrations = registrations.filter((r)=>r.name === name);
|
438
468
|
check(registrations.length < 2, `internal error: more than one registration named '${name}'`);
|
@@ -547,8 +577,9 @@ function isDisposable(value) {
|
|
547
577
|
if (args.length === 1) {
|
548
578
|
const Class = args[0];
|
549
579
|
const metadata = getMetadata(Class);
|
580
|
+
const name = metadata.name;
|
550
581
|
const registration = {
|
551
|
-
name:
|
582
|
+
name: name,
|
552
583
|
// The provider is of type ClassProvider, initialized by getMetadata
|
553
584
|
provider: metadata.provider,
|
554
585
|
options: {
|
@@ -562,8 +593,12 @@ function isDisposable(value) {
|
|
562
593
|
// These tokens will point to the original Class token and will have the same scope.
|
563
594
|
for (const token of metadata.tokensRef.getRefTokens()){
|
564
595
|
this.myTokenRegistry.set(token, {
|
596
|
+
name: name,
|
565
597
|
provider: {
|
566
|
-
useExisting:
|
598
|
+
useExisting: {
|
599
|
+
token: Class,
|
600
|
+
name: name
|
601
|
+
}
|
567
602
|
}
|
568
603
|
});
|
569
604
|
}
|
@@ -573,9 +608,8 @@ function isDisposable(value) {
|
|
573
608
|
}
|
574
609
|
} else {
|
575
610
|
const [token, provider, options] = args;
|
576
|
-
const
|
577
|
-
|
578
|
-
check(name === undefined || name.trim(), `the name qualifier for token ${getTokenName(token)} must not be empty`);
|
611
|
+
const name = provider.name;
|
612
|
+
check(name === undefined || name.trim(), `name qualifier for token ${getTokenName(token)} must not be empty`);
|
579
613
|
if (isClassProvider(provider)) {
|
580
614
|
const metadata = getMetadata(provider.useClass);
|
581
615
|
const registration = {
|
@@ -595,8 +629,9 @@ function isDisposable(value) {
|
|
595
629
|
this.resolveProviderValue(token, registration);
|
596
630
|
}
|
597
631
|
} else {
|
598
|
-
if (
|
599
|
-
|
632
|
+
if (isExistingProvider(provider)) {
|
633
|
+
const [targetToken] = this.getTargetToken(provider);
|
634
|
+
check(token !== targetToken, `token ${getTokenName(token)} cannot alias itself via useExisting`);
|
600
635
|
}
|
601
636
|
this.myTokenRegistry.set(token, {
|
602
637
|
name: name,
|
@@ -633,10 +668,7 @@ function isDisposable(value) {
|
|
633
668
|
if (!registration && isConstructor(token)) {
|
634
669
|
registration = this.autoRegisterClass(token, localName);
|
635
670
|
}
|
636
|
-
|
637
|
-
return this.resolveRegistration(token, registration, localName);
|
638
|
-
}
|
639
|
-
return localOptional ? undefined : throwUnregisteredError(token, localName);
|
671
|
+
return this.resolveRegistration(token, registration, localOptional, localName);
|
640
672
|
}
|
641
673
|
resolveAll(token, optional) {
|
642
674
|
this.checkDisposed();
|
@@ -649,11 +681,13 @@ function isDisposable(value) {
|
|
649
681
|
];
|
650
682
|
}
|
651
683
|
}
|
652
|
-
if (registrations.length
|
653
|
-
|
654
|
-
|
684
|
+
if (registrations.length === 0 && !optional) {
|
685
|
+
throwUnregisteredError([
|
686
|
+
token
|
687
|
+
]);
|
655
688
|
}
|
656
|
-
return
|
689
|
+
return registrations //
|
690
|
+
.map((registration)=>this.resolveRegistration(token, registration, optional)).filter((value)=>value != null);
|
657
691
|
}
|
658
692
|
dispose() {
|
659
693
|
if (this.myDisposed) {
|
@@ -680,26 +714,57 @@ function isDisposable(value) {
|
|
680
714
|
// Allow values to be GCed
|
681
715
|
disposedRefs.clear();
|
682
716
|
}
|
683
|
-
resolveRegistration(token, registration, name) {
|
684
|
-
|
685
|
-
while(isExistingProvider(
|
686
|
-
const targetToken =
|
687
|
-
|
688
|
-
|
689
|
-
|
717
|
+
resolveRegistration(token, registration, optional, name) {
|
718
|
+
const aliases = [];
|
719
|
+
while(registration && isExistingProvider(registration.provider)){
|
720
|
+
const [targetToken, targetName] = this.getTargetToken(registration.provider);
|
721
|
+
if (aliases.some(([t])=>t === targetToken)) {
|
722
|
+
throwCircularAliasError([
|
723
|
+
[
|
724
|
+
token,
|
725
|
+
name
|
726
|
+
],
|
727
|
+
...aliases
|
728
|
+
]);
|
729
|
+
}
|
730
|
+
// eslint-disable-next-line no-param-reassign
|
731
|
+
registration = this.myTokenRegistry.get(targetToken, targetName);
|
732
|
+
aliases.push([
|
733
|
+
targetToken,
|
734
|
+
targetName
|
735
|
+
]);
|
736
|
+
if (!registration && !optional) {
|
737
|
+
throwTargetUnregisteredError([
|
738
|
+
token,
|
739
|
+
name
|
740
|
+
], aliases);
|
690
741
|
}
|
691
742
|
}
|
743
|
+
if (!registration) {
|
744
|
+
return optional ? undefined : throwUnregisteredError([
|
745
|
+
token,
|
746
|
+
name
|
747
|
+
]);
|
748
|
+
}
|
692
749
|
try {
|
693
|
-
return this.resolveProviderValue(token,
|
750
|
+
return this.resolveProviderValue(token, registration);
|
694
751
|
} catch (e) {
|
695
|
-
|
696
|
-
|
697
|
-
|
698
|
-
|
699
|
-
}
|
700
|
-
throw e;
|
752
|
+
throwResolutionError([
|
753
|
+
token,
|
754
|
+
name
|
755
|
+
], aliases, e);
|
701
756
|
}
|
702
757
|
}
|
758
|
+
getTargetToken(provider) {
|
759
|
+
const token = provider.useExisting;
|
760
|
+
return isType(token) || isConstructor(token) //
|
761
|
+
? [
|
762
|
+
token
|
763
|
+
] : [
|
764
|
+
token.token,
|
765
|
+
token.name
|
766
|
+
];
|
767
|
+
}
|
703
768
|
autoRegisterClass(Class, name) {
|
704
769
|
const metadata = getMetadata(Class);
|
705
770
|
const autoRegister = metadata.autoRegister ?? this.myOptions.autoRegister;
|
@@ -730,8 +795,7 @@ function isDisposable(value) {
|
|
730
795
|
if (isValueProvider(provider)) {
|
731
796
|
return provider.useValue;
|
732
797
|
}
|
733
|
-
check(
|
734
|
-
expectNever(provider);
|
798
|
+
check(false, "internal error: unexpected ExistingProvider");
|
735
799
|
}
|
736
800
|
resolveScopedValue(token, registration, factory) {
|
737
801
|
let context = useInjectionContext();
|
@@ -746,7 +810,9 @@ function isDisposable(value) {
|
|
746
810
|
if (resolution.stack.has(provider)) {
|
747
811
|
const dependentRef = resolution.dependents.get(provider);
|
748
812
|
check(dependentRef, ()=>{
|
749
|
-
const path = resolution.tokenStack.
|
813
|
+
const path = getTokenPath(resolution.tokenStack.concat(token).map((t)=>[
|
814
|
+
t
|
815
|
+
]));
|
750
816
|
return `circular dependency detected while resolving ${path}`;
|
751
817
|
});
|
752
818
|
return dependentRef.current;
|
@@ -810,7 +876,7 @@ function isDisposable(value) {
|
|
810
876
|
check(ctor.length === ctorDeps.length, ()=>{
|
811
877
|
const location = getLocation(ctor);
|
812
878
|
const msg = `${location} expected ${ctor.length} decorated constructor parameters`;
|
813
|
-
return msg
|
879
|
+
return `${msg}, but found ${ctorDeps.length}`;
|
814
880
|
});
|
815
881
|
return this.resolveArgs(ctorDeps, ctor);
|
816
882
|
}
|
@@ -832,7 +898,7 @@ function isDisposable(value) {
|
|
832
898
|
check(methodDeps.length === method.length, ()=>{
|
833
899
|
const location = getLocation(ctor, methodKey);
|
834
900
|
const msg = `${location} expected ${method.length} decorated method parameters`;
|
835
|
-
return msg
|
901
|
+
return `${msg}, but found ${methodDeps.length}`;
|
836
902
|
});
|
837
903
|
const args = this.resolveArgs(methodDeps, ctor, instance, methodKey);
|
838
904
|
method.bind(instance)(...args);
|
@@ -867,7 +933,7 @@ function isDisposable(value) {
|
|
867
933
|
}
|
868
934
|
}
|
869
935
|
checkDisposed() {
|
870
|
-
check(!this.myDisposed, "
|
936
|
+
check(!this.myDisposed, "container is disposed");
|
871
937
|
}
|
872
938
|
}
|
873
939
|
|
@@ -1072,7 +1138,7 @@ function InjectAll(token) {
|
|
1072
1138
|
*
|
1073
1139
|
* @__NO_SIDE_EFFECTS__
|
1074
1140
|
*/ function Named(name) {
|
1075
|
-
check(name.trim(), "
|
1141
|
+
check(name.trim(), "@Named qualifier must not be empty");
|
1076
1142
|
return function(target, propertyKey, parameterIndex) {
|
1077
1143
|
if (parameterIndex === undefined) {
|
1078
1144
|
// The decorator has been applied to the class
|