@lppedd/di-wise-neo 0.10.0 → 0.11.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.d.ts +33 -15
- package/dist/cjs/index.js +126 -71
- package/dist/cjs/index.js.map +1 -1
- package/dist/es/index.d.mts +33 -15
- package/dist/es/index.mjs +126 -71
- package/dist/es/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/es/index.d.mts
CHANGED
@@ -116,11 +116,11 @@ interface FactoryProvider<Value> {
|
|
116
116
|
/**
|
117
117
|
* Provides a static - already constructed - value for a token.
|
118
118
|
*/
|
119
|
-
interface ValueProvider<
|
119
|
+
interface ValueProvider<Value> {
|
120
120
|
/**
|
121
121
|
* The static value to associate with the token.
|
122
122
|
*/
|
123
|
-
readonly useValue:
|
123
|
+
readonly useValue: Value;
|
124
124
|
/**
|
125
125
|
* An optional name to qualify this provider.
|
126
126
|
* If specified, the token must be resolved using the same name.
|
@@ -147,7 +147,23 @@ interface ExistingProvider<Value> {
|
|
147
147
|
/**
|
148
148
|
* The existing token to alias.
|
149
149
|
*/
|
150
|
-
readonly useExisting: Token<Value
|
150
|
+
readonly useExisting: Token<Value> | [Token<Value>, string?];
|
151
|
+
/**
|
152
|
+
* An optional name to qualify this provider.
|
153
|
+
* If specified, the token must be resolved using the same name.
|
154
|
+
*
|
155
|
+
* @example
|
156
|
+
* ```ts
|
157
|
+
* export class ExtensionContext {
|
158
|
+
* // Decorator-based injection
|
159
|
+
* constructor(@Inject(ISecretStorage) @Named("persistent") secretStorage: SecretStorage) {}
|
160
|
+
*
|
161
|
+
* // Function-based injection
|
162
|
+
* constructor(secretStorage = inject(ISecretStorage, "persistent")) {}
|
163
|
+
* }
|
164
|
+
* ```
|
165
|
+
*/
|
166
|
+
readonly name?: string;
|
151
167
|
}
|
152
168
|
/**
|
153
169
|
* A token provider.
|
@@ -324,7 +340,8 @@ interface Container {
|
|
324
340
|
*
|
325
341
|
* If the class is not registered, but it is decorated with {@link AutoRegister},
|
326
342
|
* or {@link ContainerOptions.autoRegister} is true, the class is registered automatically.
|
327
|
-
* Otherwise, resolution fails.
|
343
|
+
* Otherwise, the resolution fails.
|
344
|
+
*
|
328
345
|
* The scope for the automatic registration is determined by either
|
329
346
|
* the {@link Scoped} decorator on the class, or {@link ContainerOptions.defaultScope}.
|
330
347
|
*
|
@@ -372,7 +389,8 @@ interface Container {
|
|
372
389
|
*
|
373
390
|
* If the class is not registered, but it is decorated with {@link AutoRegister},
|
374
391
|
* or {@link ContainerOptions.autoRegister} is true, the class is registered automatically.
|
375
|
-
* Otherwise, resolution fails.
|
392
|
+
* Otherwise, the resolution fails.
|
393
|
+
*
|
376
394
|
* The scope for the automatic registration is determined by either
|
377
395
|
* the {@link Scoped} decorator on the class, or {@link ContainerOptions.defaultScope}.
|
378
396
|
*
|
@@ -476,12 +494,12 @@ interface TokenRef<Value = any> {
|
|
476
494
|
readonly getRefToken: () => Token<Value>;
|
477
495
|
}
|
478
496
|
/**
|
479
|
-
* Allows referencing tokens
|
497
|
+
* Allows referencing tokens declared later in the file by wrapping them
|
480
498
|
* in a lazily evaluated function.
|
481
499
|
*/
|
482
500
|
declare function forwardRef<Value>(token: () => Tokens<Value>): TokensRef<Value>;
|
483
501
|
/**
|
484
|
-
* Allows referencing a token
|
502
|
+
* Allows referencing a token declared later in the file by wrapping it
|
485
503
|
* in a lazily evaluated function.
|
486
504
|
*/
|
487
505
|
declare function forwardRef<Value>(token: () => Token<Value>): TokenRef<Value>;
|
@@ -501,8 +519,8 @@ declare function Inject<Value>(token: Token<Value>): ParameterDecorator;
|
|
501
519
|
/**
|
502
520
|
* Parameter decorator that injects the value associated with the given token.
|
503
521
|
*
|
504
|
-
* Allows referencing a token
|
505
|
-
*
|
522
|
+
* Allows referencing a token declared later in the file by using the
|
523
|
+
* {@link forwardRef} helper function.
|
506
524
|
*
|
507
525
|
* Throws an error if the token is not registered in the container.
|
508
526
|
*
|
@@ -567,8 +585,8 @@ declare function InjectAll<Value>(token: Token<Value>): ParameterDecorator;
|
|
567
585
|
* Parameter decorator that injects all values provided by the registrations
|
568
586
|
* associated with the given token.
|
569
587
|
*
|
570
|
-
* Allows referencing a token
|
571
|
-
*
|
588
|
+
* Allows referencing a token declared later in the file by using the
|
589
|
+
* {@link forwardRef} helper function.
|
572
590
|
*
|
573
591
|
* Throws an error if the token is not registered in the container.
|
574
592
|
*
|
@@ -617,8 +635,8 @@ declare function Optional<Value>(token: Token<Value>): ParameterDecorator;
|
|
617
635
|
* Parameter decorator that injects the value associated with the given token,
|
618
636
|
* or `undefined` if the token is not registered in the container.
|
619
637
|
*
|
620
|
-
* Allows referencing a token
|
621
|
-
*
|
638
|
+
* Allows referencing a token declared later in the file by using the
|
639
|
+
* {@link forwardRef} helper function.
|
622
640
|
*
|
623
641
|
* @example
|
624
642
|
* ```ts
|
@@ -648,8 +666,8 @@ declare function OptionalAll<Value>(token: Token<Value>): ParameterDecorator;
|
|
648
666
|
* associated with the given token, or an empty array if the token is not registered
|
649
667
|
* in the container.
|
650
668
|
*
|
651
|
-
* Allows referencing a token
|
652
|
-
*
|
669
|
+
* Allows referencing a token declared later in the file by using the
|
670
|
+
* {@link forwardRef} helper function.
|
653
671
|
*
|
654
672
|
* @example
|
655
673
|
* ```ts
|
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,26 @@ 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([token, name]) {
|
57
|
+
const tokenName = token.name || "<unnamed>";
|
58
|
+
return name ? `${tokenName}["${name}"]` : tokenName;
|
59
|
+
}
|
60
|
+
function getCause(error) {
|
61
|
+
if (!error) {
|
62
|
+
return "";
|
63
|
+
}
|
64
|
+
const msg = isError(error) ? error.message : String(error);
|
65
|
+
return `\n [cause] ${untag(msg)}`;
|
66
|
+
}
|
41
67
|
function isError(value) {
|
42
|
-
return value
|
68
|
+
return value?.stack && typeof value?.message === "string";
|
43
69
|
}
|
44
70
|
function tag(message) {
|
45
71
|
return `[di-wise-neo] ${message}`;
|
@@ -54,8 +80,7 @@ class KeyedStack {
|
|
54
80
|
return this.myKeys.has(key);
|
55
81
|
}
|
56
82
|
peek() {
|
57
|
-
|
58
|
-
return entry?.value;
|
83
|
+
return this.myEntries.at(-1)?.value;
|
59
84
|
}
|
60
85
|
push(key, value) {
|
61
86
|
check(!this.has(key), "invariant violation");
|
@@ -103,7 +128,7 @@ class WeakRefMap {
|
|
103
128
|
// @internal
|
104
129
|
function createResolution() {
|
105
130
|
return {
|
106
|
-
|
131
|
+
tokens: [],
|
107
132
|
stack: new KeyedStack(),
|
108
133
|
values: new WeakRefMap(),
|
109
134
|
dependents: new WeakRefMap()
|
@@ -385,22 +410,22 @@ class TokenRegistry {
|
|
385
410
|
}
|
386
411
|
delete(token, name) {
|
387
412
|
const registrations = this.myMap.get(token);
|
388
|
-
if (registrations) {
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
413
|
+
if (!registrations) {
|
414
|
+
return [];
|
415
|
+
}
|
416
|
+
if (name !== undefined) {
|
417
|
+
const removed = [];
|
418
|
+
const updated = [];
|
419
|
+
for (const registration of registrations){
|
420
|
+
(registration.name === name ? removed : updated).push(registration);
|
421
|
+
}
|
422
|
+
if (removed.length > 0) {
|
423
|
+
this.myMap.set(token, updated);
|
424
|
+
return removed;
|
400
425
|
}
|
401
|
-
this.myMap.delete(token);
|
402
426
|
}
|
403
|
-
|
427
|
+
this.myMap.delete(token);
|
428
|
+
return registrations;
|
404
429
|
}
|
405
430
|
deleteAll() {
|
406
431
|
const tokens = Array.from(this.myMap.keys());
|
@@ -429,8 +454,7 @@ class TokenRegistry {
|
|
429
454
|
return Array.from(values);
|
430
455
|
}
|
431
456
|
getAllFromParent(token, name) {
|
432
|
-
|
433
|
-
let registrations = thisRegistrations || this.myParent?.getAllFromParent(token, name);
|
457
|
+
let registrations = this.myMap.get(token) || this.myParent?.getAllFromParent(token, name);
|
434
458
|
if (registrations && name !== undefined) {
|
435
459
|
registrations = registrations.filter((r)=>r.name === name);
|
436
460
|
check(registrations.length < 2, `internal error: more than one registration named '${name}'`);
|
@@ -545,8 +569,9 @@ function isDisposable(value) {
|
|
545
569
|
if (args.length === 1) {
|
546
570
|
const Class = args[0];
|
547
571
|
const metadata = getMetadata(Class);
|
572
|
+
const name = metadata.name;
|
548
573
|
const registration = {
|
549
|
-
name:
|
574
|
+
name: name,
|
550
575
|
// The provider is of type ClassProvider, initialized by getMetadata
|
551
576
|
provider: metadata.provider,
|
552
577
|
options: {
|
@@ -560,8 +585,12 @@ function isDisposable(value) {
|
|
560
585
|
// These tokens will point to the original Class token and will have the same scope.
|
561
586
|
for (const token of metadata.tokensRef.getRefTokens()){
|
562
587
|
this.myTokenRegistry.set(token, {
|
588
|
+
name: name,
|
563
589
|
provider: {
|
564
|
-
useExisting:
|
590
|
+
useExisting: [
|
591
|
+
Class,
|
592
|
+
name
|
593
|
+
]
|
565
594
|
}
|
566
595
|
});
|
567
596
|
}
|
@@ -571,14 +600,13 @@ function isDisposable(value) {
|
|
571
600
|
}
|
572
601
|
} else {
|
573
602
|
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`);
|
603
|
+
const name = provider.name;
|
604
|
+
check(name === undefined || name.trim(), `name qualifier for token ${getTokenName(token)} must not be empty`);
|
577
605
|
if (isClassProvider(provider)) {
|
578
606
|
const metadata = getMetadata(provider.useClass);
|
579
607
|
const registration = {
|
580
608
|
// An explicit provider name overrides what is specified via @Named
|
581
|
-
name: metadata.name ??
|
609
|
+
name: metadata.name ?? name,
|
582
610
|
provider: metadata.provider,
|
583
611
|
options: {
|
584
612
|
// Explicit registration options override what is specified via class decorators (e.g., @Scoped)
|
@@ -593,8 +621,9 @@ function isDisposable(value) {
|
|
593
621
|
this.resolveProviderValue(token, registration);
|
594
622
|
}
|
595
623
|
} else {
|
596
|
-
if (
|
597
|
-
|
624
|
+
if (isExistingProvider(provider)) {
|
625
|
+
const [targetToken] = this.getTargetToken(provider);
|
626
|
+
check(token !== targetToken, `token ${getTokenName(token)} cannot alias itself via useExisting`);
|
598
627
|
}
|
599
628
|
this.myTokenRegistry.set(token, {
|
600
629
|
name: name,
|
@@ -631,10 +660,7 @@ function isDisposable(value) {
|
|
631
660
|
if (!registration && isConstructor(token)) {
|
632
661
|
registration = this.autoRegisterClass(token, localName);
|
633
662
|
}
|
634
|
-
|
635
|
-
return this.resolveRegistration(token, registration, localName);
|
636
|
-
}
|
637
|
-
return localOptional ? undefined : throwUnregisteredError(token, localName);
|
663
|
+
return this.resolveRegistration(token, registration, localOptional, localName);
|
638
664
|
}
|
639
665
|
resolveAll(token, optional) {
|
640
666
|
this.checkDisposed();
|
@@ -647,11 +673,13 @@ function isDisposable(value) {
|
|
647
673
|
];
|
648
674
|
}
|
649
675
|
}
|
650
|
-
if (registrations.length
|
651
|
-
|
652
|
-
|
676
|
+
if (registrations.length === 0 && !optional) {
|
677
|
+
throwUnregisteredError([
|
678
|
+
token
|
679
|
+
]);
|
653
680
|
}
|
654
|
-
return
|
681
|
+
return registrations //
|
682
|
+
.map((registration)=>this.resolveRegistration(token, registration, optional)).filter((value)=>value != null);
|
655
683
|
}
|
656
684
|
dispose() {
|
657
685
|
if (this.myDisposed) {
|
@@ -678,26 +706,53 @@ function isDisposable(value) {
|
|
678
706
|
// Allow values to be GCed
|
679
707
|
disposedRefs.clear();
|
680
708
|
}
|
681
|
-
resolveRegistration(token, registration, name) {
|
682
|
-
|
683
|
-
while(isExistingProvider(
|
684
|
-
const targetToken =
|
685
|
-
|
686
|
-
|
687
|
-
|
709
|
+
resolveRegistration(token, registration, optional, name) {
|
710
|
+
const aliases = [];
|
711
|
+
while(registration && isExistingProvider(registration.provider)){
|
712
|
+
const [targetToken, targetName] = this.getTargetToken(registration.provider);
|
713
|
+
if (aliases.some(([t])=>t === targetToken)) {
|
714
|
+
throwCircularAliasError([
|
715
|
+
[
|
716
|
+
token,
|
717
|
+
name
|
718
|
+
],
|
719
|
+
...aliases
|
720
|
+
]);
|
688
721
|
}
|
722
|
+
// eslint-disable-next-line no-param-reassign
|
723
|
+
registration = this.myTokenRegistry.get(targetToken, targetName);
|
724
|
+
aliases.push([
|
725
|
+
targetToken,
|
726
|
+
targetName
|
727
|
+
]);
|
728
|
+
if (!registration && !optional) {
|
729
|
+
throwTargetUnregisteredError([
|
730
|
+
token,
|
731
|
+
name
|
732
|
+
], aliases);
|
733
|
+
}
|
734
|
+
}
|
735
|
+
if (!registration) {
|
736
|
+
return optional ? undefined : throwUnregisteredError([
|
737
|
+
token,
|
738
|
+
name
|
739
|
+
]);
|
689
740
|
}
|
690
741
|
try {
|
691
|
-
return this.resolveProviderValue(token,
|
742
|
+
return this.resolveProviderValue(token, registration);
|
692
743
|
} catch (e) {
|
693
|
-
|
694
|
-
|
695
|
-
|
696
|
-
|
697
|
-
}
|
698
|
-
throw e;
|
744
|
+
throwResolutionError([
|
745
|
+
token,
|
746
|
+
name
|
747
|
+
], aliases, e);
|
699
748
|
}
|
700
749
|
}
|
750
|
+
getTargetToken(provider) {
|
751
|
+
const token = provider.useExisting;
|
752
|
+
return Array.isArray(token) ? token : [
|
753
|
+
token
|
754
|
+
];
|
755
|
+
}
|
701
756
|
autoRegisterClass(Class, name) {
|
702
757
|
const metadata = getMetadata(Class);
|
703
758
|
const autoRegister = metadata.autoRegister ?? this.myOptions.autoRegister;
|
@@ -728,8 +783,7 @@ function isDisposable(value) {
|
|
728
783
|
if (isValueProvider(provider)) {
|
729
784
|
return provider.useValue;
|
730
785
|
}
|
731
|
-
check(
|
732
|
-
expectNever(provider);
|
786
|
+
check(false, "internal error: unexpected ExistingProvider");
|
733
787
|
}
|
734
788
|
resolveScopedValue(token, registration, factory) {
|
735
789
|
let context = useInjectionContext();
|
@@ -744,7 +798,9 @@ function isDisposable(value) {
|
|
744
798
|
if (resolution.stack.has(provider)) {
|
745
799
|
const dependentRef = resolution.dependents.get(provider);
|
746
800
|
check(dependentRef, ()=>{
|
747
|
-
const path = resolution.
|
801
|
+
const path = getTokenPath(resolution.tokens.concat(token).map((t)=>[
|
802
|
+
t
|
803
|
+
]));
|
748
804
|
return `circular dependency detected while resolving ${path}`;
|
749
805
|
});
|
750
806
|
return dependentRef.current;
|
@@ -752,7 +808,7 @@ function isDisposable(value) {
|
|
752
808
|
const scope = registration.options?.scope ?? this.myOptions.defaultScope;
|
753
809
|
const cleanups = [
|
754
810
|
provideInjectionContext(context),
|
755
|
-
resolution.
|
811
|
+
resolution.tokens.push(token) && (()=>resolution.tokens.pop()),
|
756
812
|
!isBuilder(provider) && resolution.stack.push(provider, {
|
757
813
|
provider,
|
758
814
|
scope
|
@@ -808,7 +864,7 @@ function isDisposable(value) {
|
|
808
864
|
check(ctor.length === ctorDeps.length, ()=>{
|
809
865
|
const location = getLocation(ctor);
|
810
866
|
const msg = `${location} expected ${ctor.length} decorated constructor parameters`;
|
811
|
-
return msg
|
867
|
+
return `${msg}, but found ${ctorDeps.length}`;
|
812
868
|
});
|
813
869
|
return this.resolveArgs(ctorDeps, ctor);
|
814
870
|
}
|
@@ -830,7 +886,7 @@ function isDisposable(value) {
|
|
830
886
|
check(methodDeps.length === method.length, ()=>{
|
831
887
|
const location = getLocation(ctor, methodKey);
|
832
888
|
const msg = `${location} expected ${method.length} decorated method parameters`;
|
833
|
-
return msg
|
889
|
+
return `${msg}, but found ${methodDeps.length}`;
|
834
890
|
});
|
835
891
|
const args = this.resolveArgs(methodDeps, ctor, instance, methodKey);
|
836
892
|
method.bind(instance)(...args);
|
@@ -865,7 +921,7 @@ function isDisposable(value) {
|
|
865
921
|
}
|
866
922
|
}
|
867
923
|
checkDisposed() {
|
868
|
-
check(!this.myDisposed, "
|
924
|
+
check(!this.myDisposed, "container is disposed");
|
869
925
|
}
|
870
926
|
}
|
871
927
|
|
@@ -938,8 +994,7 @@ function isDisposable(value) {
|
|
938
994
|
function forwardRef(token) {
|
939
995
|
return {
|
940
996
|
getRefTokens: ()=>{
|
941
|
-
// Normalize the single token here
|
942
|
-
// to do it at every getRefTokens call site
|
997
|
+
// Normalize the single token here so that we don't have to do it at every getRefTokens call site
|
943
998
|
const tokenOrTokens = token();
|
944
999
|
const tokensArray = Array.isArray(tokenOrTokens) ? tokenOrTokens : [
|
945
1000
|
tokenOrTokens
|
@@ -1070,7 +1125,7 @@ function InjectAll(token) {
|
|
1070
1125
|
*
|
1071
1126
|
* @__NO_SIDE_EFFECTS__
|
1072
1127
|
*/ function Named(name) {
|
1073
|
-
check(name.trim(), "
|
1128
|
+
check(name.trim(), "@Named qualifier must not be empty");
|
1074
1129
|
return function(target, propertyKey, parameterIndex) {
|
1075
1130
|
if (parameterIndex === undefined) {
|
1076
1131
|
// The decorator has been applied to the class
|