@lppedd/di-wise-neo 0.9.4 → 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/README.md +6 -6
- package/dist/cjs/index.d.ts +25 -5
- package/dist/cjs/index.js +125 -75
- package/dist/cjs/index.js.map +1 -1
- package/dist/es/index.d.mts +25 -5
- package/dist/es/index.mjs +125 -75
- package/dist/es/index.mjs.map +1 -1
- package/package.json +4 -4
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.
|
@@ -155,7 +174,6 @@ interface ExistingProvider<Value> {
|
|
155
174
|
type Provider<Value = any> = ClassProvider<Value & object> | FactoryProvider<Value> | ValueProvider<Value> | ExistingProvider<Value>;
|
156
175
|
|
157
176
|
declare const Scope: {
|
158
|
-
readonly Inherited: "Inherited";
|
159
177
|
readonly Transient: "Transient";
|
160
178
|
readonly Resolution: "Resolution";
|
161
179
|
readonly Container: "Container";
|
@@ -203,7 +221,7 @@ interface ContainerOptions {
|
|
203
221
|
/**
|
204
222
|
* The default scope for registrations.
|
205
223
|
*
|
206
|
-
* @defaultValue Scope.
|
224
|
+
* @defaultValue Scope.Transient
|
207
225
|
*/
|
208
226
|
readonly defaultScope: Scope;
|
209
227
|
}
|
@@ -325,7 +343,8 @@ interface Container {
|
|
325
343
|
*
|
326
344
|
* If the class is not registered, but it is decorated with {@link AutoRegister},
|
327
345
|
* or {@link ContainerOptions.autoRegister} is true, the class is registered automatically.
|
328
|
-
* Otherwise, resolution fails.
|
346
|
+
* Otherwise, the resolution fails.
|
347
|
+
*
|
329
348
|
* The scope for the automatic registration is determined by either
|
330
349
|
* the {@link Scoped} decorator on the class, or {@link ContainerOptions.defaultScope}.
|
331
350
|
*
|
@@ -373,7 +392,8 @@ interface Container {
|
|
373
392
|
*
|
374
393
|
* If the class is not registered, but it is decorated with {@link AutoRegister},
|
375
394
|
* or {@link ContainerOptions.autoRegister} is true, the class is registered automatically.
|
376
|
-
* Otherwise, resolution fails.
|
395
|
+
* Otherwise, the resolution fails.
|
396
|
+
*
|
377
397
|
* The scope for the automatic registration is determined by either
|
378
398
|
* the {@link Scoped} decorator on the class, or {@link ContainerOptions.defaultScope}.
|
379
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 =
|
27
|
-
|
28
|
-
|
34
|
+
const tokenName = getFullTokenName([
|
35
|
+
dependency.tokenRef.getRefToken(),
|
36
|
+
dependency.name
|
37
|
+
]);
|
38
|
+
const msg = tag(`failed to resolve dependency for ${location}(parameter #${dependency.index}: ${tokenName})`);
|
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}`;
|
@@ -296,15 +323,13 @@ function isExistingProvider(provider) {
|
|
296
323
|
}
|
297
324
|
|
298
325
|
const Scope = {
|
299
|
-
Inherited: "Inherited",
|
300
326
|
Transient: "Transient",
|
301
327
|
Resolution: "Resolution",
|
302
328
|
Container: "Container"
|
303
329
|
};
|
304
330
|
|
331
|
+
const typeSymbol = Symbol("di-wise-neo.typeToken");
|
305
332
|
/**
|
306
|
-
* Type API.
|
307
|
-
*/ /**
|
308
333
|
* Creates a type token.
|
309
334
|
*
|
310
335
|
* @example
|
@@ -318,6 +343,7 @@ const Scope = {
|
|
318
343
|
name: `Type<${typeName}>`,
|
319
344
|
inter: createType,
|
320
345
|
union: createType,
|
346
|
+
__type: typeSymbol,
|
321
347
|
toString () {
|
322
348
|
return type.name;
|
323
349
|
}
|
@@ -325,6 +351,10 @@ const Scope = {
|
|
325
351
|
return type;
|
326
352
|
}
|
327
353
|
// @internal
|
354
|
+
function isType(token) {
|
355
|
+
return token.__type === typeSymbol;
|
356
|
+
}
|
357
|
+
// @internal
|
328
358
|
function isConstructor(token) {
|
329
359
|
return typeof token === "function";
|
330
360
|
}
|
@@ -430,8 +460,7 @@ class TokenRegistry {
|
|
430
460
|
return Array.from(values);
|
431
461
|
}
|
432
462
|
getAllFromParent(token, name) {
|
433
|
-
|
434
|
-
let registrations = thisRegistrations || this.myParent?.getAllFromParent(token, name);
|
463
|
+
let registrations = this.myMap.get(token) || this.myParent?.getAllFromParent(token, name);
|
435
464
|
if (registrations && name !== undefined) {
|
436
465
|
registrations = registrations.filter((r)=>r.name === name);
|
437
466
|
check(registrations.length < 2, `internal error: more than one registration named '${name}'`);
|
@@ -476,7 +505,7 @@ function isDisposable(value) {
|
|
476
505
|
this.myParent = parent;
|
477
506
|
this.myOptions = {
|
478
507
|
autoRegister: false,
|
479
|
-
defaultScope: Scope.
|
508
|
+
defaultScope: Scope.Transient,
|
480
509
|
...options
|
481
510
|
};
|
482
511
|
this.myTokenRegistry = new TokenRegistry(this.myParent?.myTokenRegistry);
|
@@ -546,8 +575,9 @@ function isDisposable(value) {
|
|
546
575
|
if (args.length === 1) {
|
547
576
|
const Class = args[0];
|
548
577
|
const metadata = getMetadata(Class);
|
578
|
+
const name = metadata.name;
|
549
579
|
const registration = {
|
550
|
-
name:
|
580
|
+
name: name,
|
551
581
|
// The provider is of type ClassProvider, initialized by getMetadata
|
552
582
|
provider: metadata.provider,
|
553
583
|
options: {
|
@@ -561,8 +591,12 @@ function isDisposable(value) {
|
|
561
591
|
// These tokens will point to the original Class token and will have the same scope.
|
562
592
|
for (const token of metadata.tokensRef.getRefTokens()){
|
563
593
|
this.myTokenRegistry.set(token, {
|
594
|
+
name: name,
|
564
595
|
provider: {
|
565
|
-
useExisting:
|
596
|
+
useExisting: {
|
597
|
+
token: Class,
|
598
|
+
name: name
|
599
|
+
}
|
566
600
|
}
|
567
601
|
});
|
568
602
|
}
|
@@ -572,9 +606,8 @@ function isDisposable(value) {
|
|
572
606
|
}
|
573
607
|
} else {
|
574
608
|
const [token, provider, options] = args;
|
575
|
-
const
|
576
|
-
|
577
|
-
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`);
|
578
611
|
if (isClassProvider(provider)) {
|
579
612
|
const metadata = getMetadata(provider.useClass);
|
580
613
|
const registration = {
|
@@ -594,8 +627,9 @@ function isDisposable(value) {
|
|
594
627
|
this.resolveProviderValue(token, registration);
|
595
628
|
}
|
596
629
|
} else {
|
597
|
-
if (
|
598
|
-
|
630
|
+
if (isExistingProvider(provider)) {
|
631
|
+
const [targetToken] = this.getTargetToken(provider);
|
632
|
+
check(token !== targetToken, `token ${getTokenName(token)} cannot alias itself via useExisting`);
|
599
633
|
}
|
600
634
|
this.myTokenRegistry.set(token, {
|
601
635
|
name: name,
|
@@ -632,10 +666,7 @@ function isDisposable(value) {
|
|
632
666
|
if (!registration && isConstructor(token)) {
|
633
667
|
registration = this.autoRegisterClass(token, localName);
|
634
668
|
}
|
635
|
-
|
636
|
-
return this.resolveRegistration(token, registration, localName);
|
637
|
-
}
|
638
|
-
return localOptional ? undefined : throwUnregisteredError(token, localName);
|
669
|
+
return this.resolveRegistration(token, registration, localOptional, localName);
|
639
670
|
}
|
640
671
|
resolveAll(token, optional) {
|
641
672
|
this.checkDisposed();
|
@@ -648,11 +679,13 @@ function isDisposable(value) {
|
|
648
679
|
];
|
649
680
|
}
|
650
681
|
}
|
651
|
-
if (registrations.length
|
652
|
-
|
653
|
-
|
682
|
+
if (registrations.length === 0 && !optional) {
|
683
|
+
throwUnregisteredError([
|
684
|
+
token
|
685
|
+
]);
|
654
686
|
}
|
655
|
-
return
|
687
|
+
return registrations //
|
688
|
+
.map((registration)=>this.resolveRegistration(token, registration, optional)).filter((value)=>value != null);
|
656
689
|
}
|
657
690
|
dispose() {
|
658
691
|
if (this.myDisposed) {
|
@@ -679,26 +712,57 @@ function isDisposable(value) {
|
|
679
712
|
// Allow values to be GCed
|
680
713
|
disposedRefs.clear();
|
681
714
|
}
|
682
|
-
resolveRegistration(token, registration, name) {
|
683
|
-
|
684
|
-
while(isExistingProvider(
|
685
|
-
const targetToken =
|
686
|
-
|
687
|
-
|
688
|
-
|
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);
|
689
739
|
}
|
690
740
|
}
|
741
|
+
if (!registration) {
|
742
|
+
return optional ? undefined : throwUnregisteredError([
|
743
|
+
token,
|
744
|
+
name
|
745
|
+
]);
|
746
|
+
}
|
691
747
|
try {
|
692
|
-
return this.resolveProviderValue(token,
|
748
|
+
return this.resolveProviderValue(token, registration);
|
693
749
|
} catch (e) {
|
694
|
-
|
695
|
-
|
696
|
-
|
697
|
-
|
698
|
-
}
|
699
|
-
throw e;
|
750
|
+
throwResolutionError([
|
751
|
+
token,
|
752
|
+
name
|
753
|
+
], aliases, e);
|
700
754
|
}
|
701
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
|
+
}
|
702
766
|
autoRegisterClass(Class, name) {
|
703
767
|
const metadata = getMetadata(Class);
|
704
768
|
const autoRegister = metadata.autoRegister ?? this.myOptions.autoRegister;
|
@@ -729,8 +793,7 @@ function isDisposable(value) {
|
|
729
793
|
if (isValueProvider(provider)) {
|
730
794
|
return provider.useValue;
|
731
795
|
}
|
732
|
-
check(
|
733
|
-
expectNever(provider);
|
796
|
+
check(false, "internal error: unexpected ExistingProvider");
|
734
797
|
}
|
735
798
|
resolveScopedValue(token, registration, factory) {
|
736
799
|
let context = useInjectionContext();
|
@@ -742,16 +805,17 @@ function isDisposable(value) {
|
|
742
805
|
}
|
743
806
|
const resolution = context.resolution;
|
744
807
|
const provider = registration.provider;
|
745
|
-
const options = registration.options;
|
746
808
|
if (resolution.stack.has(provider)) {
|
747
809
|
const dependentRef = resolution.dependents.get(provider);
|
748
810
|
check(dependentRef, ()=>{
|
749
|
-
const path = resolution.tokenStack.
|
750
|
-
|
811
|
+
const path = getTokenPath(resolution.tokenStack.concat(token).map((t)=>[
|
812
|
+
t
|
813
|
+
]));
|
814
|
+
return `circular dependency detected while resolving ${path}`;
|
751
815
|
});
|
752
816
|
return dependentRef.current;
|
753
817
|
}
|
754
|
-
const scope =
|
818
|
+
const scope = registration.options?.scope ?? this.myOptions.defaultScope;
|
755
819
|
const cleanups = [
|
756
820
|
provideInjectionContext(context),
|
757
821
|
resolution.tokenStack.push(token) && (()=>resolution.tokenStack.pop()),
|
@@ -798,13 +862,6 @@ function isDisposable(value) {
|
|
798
862
|
cleanups.forEach((cleanup)=>cleanup && cleanup());
|
799
863
|
}
|
800
864
|
}
|
801
|
-
resolveScope(scope = this.myOptions.defaultScope, context = useInjectionContext()) {
|
802
|
-
if (scope === Scope.Inherited) {
|
803
|
-
const dependentFrame = context?.resolution.stack.peek();
|
804
|
-
return dependentFrame?.scope || Scope.Transient;
|
805
|
-
}
|
806
|
-
return scope;
|
807
|
-
}
|
808
865
|
resolveCtorDependencies(registration) {
|
809
866
|
const dependencies = registration.dependencies;
|
810
867
|
if (dependencies) {
|
@@ -817,7 +874,7 @@ function isDisposable(value) {
|
|
817
874
|
check(ctor.length === ctorDeps.length, ()=>{
|
818
875
|
const location = getLocation(ctor);
|
819
876
|
const msg = `${location} expected ${ctor.length} decorated constructor parameters`;
|
820
|
-
return msg
|
877
|
+
return `${msg}, but found ${ctorDeps.length}`;
|
821
878
|
});
|
822
879
|
return this.resolveArgs(ctorDeps, ctor);
|
823
880
|
}
|
@@ -839,7 +896,7 @@ function isDisposable(value) {
|
|
839
896
|
check(methodDeps.length === method.length, ()=>{
|
840
897
|
const location = getLocation(ctor, methodKey);
|
841
898
|
const msg = `${location} expected ${method.length} decorated method parameters`;
|
842
|
-
return msg
|
899
|
+
return `${msg}, but found ${methodDeps.length}`;
|
843
900
|
});
|
844
901
|
const args = this.resolveArgs(methodDeps, ctor, instance, methodKey);
|
845
902
|
method.bind(instance)(...args);
|
@@ -874,7 +931,7 @@ function isDisposable(value) {
|
|
874
931
|
}
|
875
932
|
}
|
876
933
|
checkDisposed() {
|
877
|
-
check(!this.myDisposed, "
|
934
|
+
check(!this.myDisposed, "container is disposed");
|
878
935
|
}
|
879
936
|
}
|
880
937
|
|
@@ -882,7 +939,7 @@ function isDisposable(value) {
|
|
882
939
|
* Creates a new container.
|
883
940
|
*/ function createContainer(options = {
|
884
941
|
autoRegister: false,
|
885
|
-
defaultScope: Scope.
|
942
|
+
defaultScope: Scope.Transient
|
886
943
|
}) {
|
887
944
|
return new ContainerImpl(undefined, options);
|
888
945
|
}
|
@@ -1079,7 +1136,7 @@ function InjectAll(token) {
|
|
1079
1136
|
*
|
1080
1137
|
* @__NO_SIDE_EFFECTS__
|
1081
1138
|
*/ function Named(name) {
|
1082
|
-
check(name.trim(), "
|
1139
|
+
check(name.trim(), "@Named qualifier must not be empty");
|
1083
1140
|
return function(target, propertyKey, parameterIndex) {
|
1084
1141
|
if (parameterIndex === undefined) {
|
1085
1142
|
// The decorator has been applied to the class
|
@@ -1181,22 +1238,15 @@ function OptionalAll(token) {
|
|
1181
1238
|
* ```
|
1182
1239
|
*/ const Injector = /*@__PURE__*/ build(()=>{
|
1183
1240
|
const context = ensureInjectionContext("Injector factory");
|
1184
|
-
const resolution = context.resolution;
|
1185
|
-
const dependentFrame = resolution.stack.peek();
|
1186
|
-
const dependentRef = dependentFrame && resolution.dependents.get(dependentFrame.provider);
|
1187
1241
|
const runInContext = (fn)=>{
|
1188
1242
|
if (useInjectionContext()) {
|
1189
1243
|
return fn();
|
1190
1244
|
}
|
1191
|
-
const
|
1192
|
-
provideInjectionContext(context),
|
1193
|
-
dependentFrame && resolution.stack.push(dependentFrame.provider, dependentFrame),
|
1194
|
-
dependentRef && resolution.dependents.set(dependentFrame.provider, dependentRef)
|
1195
|
-
];
|
1245
|
+
const cleanup = provideInjectionContext(context);
|
1196
1246
|
try {
|
1197
1247
|
return fn();
|
1198
1248
|
} finally{
|
1199
|
-
|
1249
|
+
cleanup();
|
1200
1250
|
}
|
1201
1251
|
};
|
1202
1252
|
return {
|