@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/cjs/index.d.ts
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/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,26 @@ 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([token, name]) {
|
59
|
+
const tokenName = token.name || "<unnamed>";
|
60
|
+
return name ? `${tokenName}["${name}"]` : tokenName;
|
61
|
+
}
|
62
|
+
function getCause(error) {
|
63
|
+
if (!error) {
|
64
|
+
return "";
|
65
|
+
}
|
66
|
+
const msg = isError(error) ? error.message : String(error);
|
67
|
+
return `\n [cause] ${untag(msg)}`;
|
68
|
+
}
|
43
69
|
function isError(value) {
|
44
|
-
return value
|
70
|
+
return value?.stack && typeof value?.message === "string";
|
45
71
|
}
|
46
72
|
function tag(message) {
|
47
73
|
return `[di-wise-neo] ${message}`;
|
@@ -56,8 +82,7 @@ class KeyedStack {
|
|
56
82
|
return this.myKeys.has(key);
|
57
83
|
}
|
58
84
|
peek() {
|
59
|
-
|
60
|
-
return entry?.value;
|
85
|
+
return this.myEntries.at(-1)?.value;
|
61
86
|
}
|
62
87
|
push(key, value) {
|
63
88
|
check(!this.has(key), "invariant violation");
|
@@ -105,7 +130,7 @@ class WeakRefMap {
|
|
105
130
|
// @internal
|
106
131
|
function createResolution() {
|
107
132
|
return {
|
108
|
-
|
133
|
+
tokens: [],
|
109
134
|
stack: new KeyedStack(),
|
110
135
|
values: new WeakRefMap(),
|
111
136
|
dependents: new WeakRefMap()
|
@@ -387,22 +412,22 @@ class TokenRegistry {
|
|
387
412
|
}
|
388
413
|
delete(token, name) {
|
389
414
|
const registrations = this.myMap.get(token);
|
390
|
-
if (registrations) {
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
415
|
+
if (!registrations) {
|
416
|
+
return [];
|
417
|
+
}
|
418
|
+
if (name !== undefined) {
|
419
|
+
const removed = [];
|
420
|
+
const updated = [];
|
421
|
+
for (const registration of registrations){
|
422
|
+
(registration.name === name ? removed : updated).push(registration);
|
423
|
+
}
|
424
|
+
if (removed.length > 0) {
|
425
|
+
this.myMap.set(token, updated);
|
426
|
+
return removed;
|
402
427
|
}
|
403
|
-
this.myMap.delete(token);
|
404
428
|
}
|
405
|
-
|
429
|
+
this.myMap.delete(token);
|
430
|
+
return registrations;
|
406
431
|
}
|
407
432
|
deleteAll() {
|
408
433
|
const tokens = Array.from(this.myMap.keys());
|
@@ -431,8 +456,7 @@ class TokenRegistry {
|
|
431
456
|
return Array.from(values);
|
432
457
|
}
|
433
458
|
getAllFromParent(token, name) {
|
434
|
-
|
435
|
-
let registrations = thisRegistrations || this.myParent?.getAllFromParent(token, name);
|
459
|
+
let registrations = this.myMap.get(token) || this.myParent?.getAllFromParent(token, name);
|
436
460
|
if (registrations && name !== undefined) {
|
437
461
|
registrations = registrations.filter((r)=>r.name === name);
|
438
462
|
check(registrations.length < 2, `internal error: more than one registration named '${name}'`);
|
@@ -547,8 +571,9 @@ function isDisposable(value) {
|
|
547
571
|
if (args.length === 1) {
|
548
572
|
const Class = args[0];
|
549
573
|
const metadata = getMetadata(Class);
|
574
|
+
const name = metadata.name;
|
550
575
|
const registration = {
|
551
|
-
name:
|
576
|
+
name: name,
|
552
577
|
// The provider is of type ClassProvider, initialized by getMetadata
|
553
578
|
provider: metadata.provider,
|
554
579
|
options: {
|
@@ -562,8 +587,12 @@ function isDisposable(value) {
|
|
562
587
|
// These tokens will point to the original Class token and will have the same scope.
|
563
588
|
for (const token of metadata.tokensRef.getRefTokens()){
|
564
589
|
this.myTokenRegistry.set(token, {
|
590
|
+
name: name,
|
565
591
|
provider: {
|
566
|
-
useExisting:
|
592
|
+
useExisting: [
|
593
|
+
Class,
|
594
|
+
name
|
595
|
+
]
|
567
596
|
}
|
568
597
|
});
|
569
598
|
}
|
@@ -573,14 +602,13 @@ function isDisposable(value) {
|
|
573
602
|
}
|
574
603
|
} else {
|
575
604
|
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`);
|
605
|
+
const name = provider.name;
|
606
|
+
check(name === undefined || name.trim(), `name qualifier for token ${getTokenName(token)} must not be empty`);
|
579
607
|
if (isClassProvider(provider)) {
|
580
608
|
const metadata = getMetadata(provider.useClass);
|
581
609
|
const registration = {
|
582
610
|
// An explicit provider name overrides what is specified via @Named
|
583
|
-
name: metadata.name ??
|
611
|
+
name: metadata.name ?? name,
|
584
612
|
provider: metadata.provider,
|
585
613
|
options: {
|
586
614
|
// Explicit registration options override what is specified via class decorators (e.g., @Scoped)
|
@@ -595,8 +623,9 @@ function isDisposable(value) {
|
|
595
623
|
this.resolveProviderValue(token, registration);
|
596
624
|
}
|
597
625
|
} else {
|
598
|
-
if (
|
599
|
-
|
626
|
+
if (isExistingProvider(provider)) {
|
627
|
+
const [targetToken] = this.getTargetToken(provider);
|
628
|
+
check(token !== targetToken, `token ${getTokenName(token)} cannot alias itself via useExisting`);
|
600
629
|
}
|
601
630
|
this.myTokenRegistry.set(token, {
|
602
631
|
name: name,
|
@@ -633,10 +662,7 @@ function isDisposable(value) {
|
|
633
662
|
if (!registration && isConstructor(token)) {
|
634
663
|
registration = this.autoRegisterClass(token, localName);
|
635
664
|
}
|
636
|
-
|
637
|
-
return this.resolveRegistration(token, registration, localName);
|
638
|
-
}
|
639
|
-
return localOptional ? undefined : throwUnregisteredError(token, localName);
|
665
|
+
return this.resolveRegistration(token, registration, localOptional, localName);
|
640
666
|
}
|
641
667
|
resolveAll(token, optional) {
|
642
668
|
this.checkDisposed();
|
@@ -649,11 +675,13 @@ function isDisposable(value) {
|
|
649
675
|
];
|
650
676
|
}
|
651
677
|
}
|
652
|
-
if (registrations.length
|
653
|
-
|
654
|
-
|
678
|
+
if (registrations.length === 0 && !optional) {
|
679
|
+
throwUnregisteredError([
|
680
|
+
token
|
681
|
+
]);
|
655
682
|
}
|
656
|
-
return
|
683
|
+
return registrations //
|
684
|
+
.map((registration)=>this.resolveRegistration(token, registration, optional)).filter((value)=>value != null);
|
657
685
|
}
|
658
686
|
dispose() {
|
659
687
|
if (this.myDisposed) {
|
@@ -680,26 +708,53 @@ function isDisposable(value) {
|
|
680
708
|
// Allow values to be GCed
|
681
709
|
disposedRefs.clear();
|
682
710
|
}
|
683
|
-
resolveRegistration(token, registration, name) {
|
684
|
-
|
685
|
-
while(isExistingProvider(
|
686
|
-
const targetToken =
|
687
|
-
|
688
|
-
|
689
|
-
|
711
|
+
resolveRegistration(token, registration, optional, name) {
|
712
|
+
const aliases = [];
|
713
|
+
while(registration && isExistingProvider(registration.provider)){
|
714
|
+
const [targetToken, targetName] = this.getTargetToken(registration.provider);
|
715
|
+
if (aliases.some(([t])=>t === targetToken)) {
|
716
|
+
throwCircularAliasError([
|
717
|
+
[
|
718
|
+
token,
|
719
|
+
name
|
720
|
+
],
|
721
|
+
...aliases
|
722
|
+
]);
|
690
723
|
}
|
724
|
+
// eslint-disable-next-line no-param-reassign
|
725
|
+
registration = this.myTokenRegistry.get(targetToken, targetName);
|
726
|
+
aliases.push([
|
727
|
+
targetToken,
|
728
|
+
targetName
|
729
|
+
]);
|
730
|
+
if (!registration && !optional) {
|
731
|
+
throwTargetUnregisteredError([
|
732
|
+
token,
|
733
|
+
name
|
734
|
+
], aliases);
|
735
|
+
}
|
736
|
+
}
|
737
|
+
if (!registration) {
|
738
|
+
return optional ? undefined : throwUnregisteredError([
|
739
|
+
token,
|
740
|
+
name
|
741
|
+
]);
|
691
742
|
}
|
692
743
|
try {
|
693
|
-
return this.resolveProviderValue(token,
|
744
|
+
return this.resolveProviderValue(token, registration);
|
694
745
|
} catch (e) {
|
695
|
-
|
696
|
-
|
697
|
-
|
698
|
-
|
699
|
-
}
|
700
|
-
throw e;
|
746
|
+
throwResolutionError([
|
747
|
+
token,
|
748
|
+
name
|
749
|
+
], aliases, e);
|
701
750
|
}
|
702
751
|
}
|
752
|
+
getTargetToken(provider) {
|
753
|
+
const token = provider.useExisting;
|
754
|
+
return Array.isArray(token) ? token : [
|
755
|
+
token
|
756
|
+
];
|
757
|
+
}
|
703
758
|
autoRegisterClass(Class, name) {
|
704
759
|
const metadata = getMetadata(Class);
|
705
760
|
const autoRegister = metadata.autoRegister ?? this.myOptions.autoRegister;
|
@@ -730,8 +785,7 @@ function isDisposable(value) {
|
|
730
785
|
if (isValueProvider(provider)) {
|
731
786
|
return provider.useValue;
|
732
787
|
}
|
733
|
-
check(
|
734
|
-
expectNever(provider);
|
788
|
+
check(false, "internal error: unexpected ExistingProvider");
|
735
789
|
}
|
736
790
|
resolveScopedValue(token, registration, factory) {
|
737
791
|
let context = useInjectionContext();
|
@@ -746,7 +800,9 @@ function isDisposable(value) {
|
|
746
800
|
if (resolution.stack.has(provider)) {
|
747
801
|
const dependentRef = resolution.dependents.get(provider);
|
748
802
|
check(dependentRef, ()=>{
|
749
|
-
const path = resolution.
|
803
|
+
const path = getTokenPath(resolution.tokens.concat(token).map((t)=>[
|
804
|
+
t
|
805
|
+
]));
|
750
806
|
return `circular dependency detected while resolving ${path}`;
|
751
807
|
});
|
752
808
|
return dependentRef.current;
|
@@ -754,7 +810,7 @@ function isDisposable(value) {
|
|
754
810
|
const scope = registration.options?.scope ?? this.myOptions.defaultScope;
|
755
811
|
const cleanups = [
|
756
812
|
provideInjectionContext(context),
|
757
|
-
resolution.
|
813
|
+
resolution.tokens.push(token) && (()=>resolution.tokens.pop()),
|
758
814
|
!isBuilder(provider) && resolution.stack.push(provider, {
|
759
815
|
provider,
|
760
816
|
scope
|
@@ -810,7 +866,7 @@ function isDisposable(value) {
|
|
810
866
|
check(ctor.length === ctorDeps.length, ()=>{
|
811
867
|
const location = getLocation(ctor);
|
812
868
|
const msg = `${location} expected ${ctor.length} decorated constructor parameters`;
|
813
|
-
return msg
|
869
|
+
return `${msg}, but found ${ctorDeps.length}`;
|
814
870
|
});
|
815
871
|
return this.resolveArgs(ctorDeps, ctor);
|
816
872
|
}
|
@@ -832,7 +888,7 @@ function isDisposable(value) {
|
|
832
888
|
check(methodDeps.length === method.length, ()=>{
|
833
889
|
const location = getLocation(ctor, methodKey);
|
834
890
|
const msg = `${location} expected ${method.length} decorated method parameters`;
|
835
|
-
return msg
|
891
|
+
return `${msg}, but found ${methodDeps.length}`;
|
836
892
|
});
|
837
893
|
const args = this.resolveArgs(methodDeps, ctor, instance, methodKey);
|
838
894
|
method.bind(instance)(...args);
|
@@ -867,7 +923,7 @@ function isDisposable(value) {
|
|
867
923
|
}
|
868
924
|
}
|
869
925
|
checkDisposed() {
|
870
|
-
check(!this.myDisposed, "
|
926
|
+
check(!this.myDisposed, "container is disposed");
|
871
927
|
}
|
872
928
|
}
|
873
929
|
|
@@ -940,8 +996,7 @@ function isDisposable(value) {
|
|
940
996
|
function forwardRef(token) {
|
941
997
|
return {
|
942
998
|
getRefTokens: ()=>{
|
943
|
-
// Normalize the single token here
|
944
|
-
// to do it at every getRefTokens call site
|
999
|
+
// Normalize the single token here so that we don't have to do it at every getRefTokens call site
|
945
1000
|
const tokenOrTokens = token();
|
946
1001
|
const tokensArray = Array.isArray(tokenOrTokens) ? tokenOrTokens : [
|
947
1002
|
tokenOrTokens
|
@@ -1072,7 +1127,7 @@ function InjectAll(token) {
|
|
1072
1127
|
*
|
1073
1128
|
* @__NO_SIDE_EFFECTS__
|
1074
1129
|
*/ function Named(name) {
|
1075
|
-
check(name.trim(), "
|
1130
|
+
check(name.trim(), "@Named qualifier must not be empty");
|
1076
1131
|
return function(target, propertyKey, parameterIndex) {
|
1077
1132
|
if (parameterIndex === undefined) {
|
1078
1133
|
// The decorator has been applied to the class
|