@codefast/di 0.11.0 → 0.12.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/CHANGELOG.md +111 -0
- package/README.md +26 -12
- package/dist/container/{binding-builders.d.ts → binding-chain.d.ts} +21 -28
- package/dist/container/{binding-builders.js → binding-chain.js} +53 -163
- package/dist/container/container.d.ts +11 -2
- package/dist/container/container.js +107 -64
- package/dist/core/binding-builders.d.ts +114 -0
- package/dist/core/binding-declaration.d.ts +7 -0
- package/dist/core/binding-declaration.js +3 -2
- package/dist/core/binding.d.ts +21 -149
- package/dist/core/binding.js +15 -25
- package/dist/core/module.d.ts +1 -1
- package/dist/core/registry.d.ts +9 -24
- package/dist/core/registry.js +30 -155
- package/dist/core/tag.js +1 -1
- package/dist/decorators/inject.js +5 -5
- package/dist/decorators/injectable.js +2 -2
- package/dist/decorators/{lifecycle-decorators.js → lifecycle.js} +3 -3
- package/dist/decorators/{decorator-metadata.js → metadata-record.js} +1 -1
- package/dist/{errors/errors.d.ts → errors.d.ts} +7 -6
- package/dist/{errors/errors.js → errors.js} +8 -7
- package/dist/index.d.ts +12 -10
- package/dist/index.js +6 -6
- package/dist/injection/descriptor.d.ts +1 -1
- package/dist/introspection/dependency-graph.d.ts +1 -1
- package/dist/introspection/dependency-graph.js +3 -4
- package/dist/introspection/explanation.d.ts +101 -0
- package/dist/introspection/explanation.js +151 -0
- package/dist/introspection/inspector.d.ts +9 -2
- package/dist/introspection/inspector.js +25 -20
- package/dist/lifecycle/{lifecycle-manager.d.ts → hooks.d.ts} +9 -2
- package/dist/lifecycle/{lifecycle-manager.js → hooks.js} +45 -28
- package/dist/lifecycle/{scope-manager.d.ts → scopes.d.ts} +3 -5
- package/dist/lifecycle/{scope-manager.js → scopes.js} +8 -19
- package/dist/metadata/{metadata-reader-token.d.ts → reader-token.d.ts} +1 -1
- package/dist/metadata/{symbol-metadata-reader.d.ts → symbol-reader.d.ts} +1 -1
- package/dist/metadata/{symbol-metadata-reader.js → symbol-reader.js} +1 -1
- package/dist/metadata/{metadata-types.d.ts → types.d.ts} +1 -1
- package/dist/metadata/types.js +1 -0
- package/dist/metadata/verify.d.ts +43 -0
- package/dist/metadata/verify.js +133 -0
- package/dist/metadata/{verifying-metadata-reader.d.ts → verifying-reader.d.ts} +1 -1
- package/dist/metadata/{verifying-metadata-reader.js → verifying-reader.js} +2 -2
- package/dist/resolution/cache/activation-need.d.ts +1 -1
- package/dist/resolution/cache/activation-need.js +5 -1
- package/dist/resolution/cache/class-introspector.d.ts +27 -36
- package/dist/resolution/cache/class-introspector.js +61 -159
- package/dist/resolution/cache/{binding-lookup-cache.d.ts → lookup.d.ts} +7 -2
- package/dist/resolution/cache/{binding-lookup-cache.js → lookup.js} +40 -8
- package/dist/resolution/context.d.ts +1 -1
- package/dist/resolution/context.js +1 -1
- package/dist/resolution/{path/resolution-path.js → path.js} +1 -1
- package/dist/resolution/plan/{plan-codegen.d.ts → codegen.d.ts} +2 -4
- package/dist/resolution/plan/{plan-codegen.js → codegen.js} +2 -4
- package/dist/resolution/plan/{instantiation-plan.d.ts → compiler.d.ts} +1 -1
- package/dist/resolution/plan/{instantiation-plan.js → compiler.js} +4 -4
- package/dist/resolution/resolver.d.ts +7 -7
- package/dist/resolution/resolver.js +308 -121
- package/dist/resolution/select/{binding-select.d.ts → candidates.d.ts} +30 -0
- package/dist/resolution/select/{binding-select.js → candidates.js} +51 -10
- package/dist/resolution/select/constraints.js +1 -1
- package/package.json +79 -71
- package/dist/core/binding-scope.d.ts +0 -12
- package/dist/core/binding-scope.js +0 -12
- /package/dist/{ambient/active-container.d.ts → ambient-container.d.ts} +0 -0
- /package/dist/{ambient/active-container.js → ambient-container.js} +0 -0
- /package/dist/{metadata/metadata-types.js → core/binding-builders.js} +0 -0
- /package/dist/decorators/{lifecycle-decorators.d.ts → lifecycle.d.ts} +0 -0
- /package/dist/decorators/{decorator-metadata.d.ts → metadata-record.d.ts} +0 -0
- /package/dist/injection/{resolve-options.d.ts → dependency-slot.d.ts} +0 -0
- /package/dist/injection/{resolve-options.js → dependency-slot.js} +0 -0
- /package/dist/{errors → introspection}/diagnostics.d.ts +0 -0
- /package/dist/{errors → introspection}/diagnostics.js +0 -0
- /package/dist/metadata/{metadata-keys.d.ts → keys.d.ts} +0 -0
- /package/dist/metadata/{metadata-keys.js → keys.js} +0 -0
- /package/dist/metadata/{metadata-reader-token.js → reader-token.js} +0 -0
- /package/dist/resolution/{path/resolution-path.d.ts → path.d.ts} +0 -0
|
@@ -1,19 +1,21 @@
|
|
|
1
1
|
import { bindingSlotToString, NO_INSTANCE } from "#core/binding";
|
|
2
2
|
import { slotNameCriterionOf } from "#core/tag";
|
|
3
3
|
import { tokenName } from "#core/token";
|
|
4
|
-
import { AsyncActivationError, AsyncResolutionError, CircularDependencyError, DisposedContainerError, InternalError, MissingMetadataError, MissingScopeContextError, NoMatchingBindingError, TokenNotBoundError, } from "#errors
|
|
5
|
-
import { loneTagBesideNameOf, resolveOptionsForSlot, singleCriterionForSlot, singleCriterionOnlyOf, } from "#injection/
|
|
6
|
-
import { SCOPED_MISS } from "#lifecycle/
|
|
4
|
+
import { AsyncActivationError, AsyncResolutionError, CircularDependencyError, DisposedContainerError, InternalError, MissingMetadataError, MissingScopeContextError, NoMatchingBindingError, TokenNotBoundError, } from "#errors";
|
|
5
|
+
import { loneTagBesideNameOf, resolveOptionsForSlot, singleCriterionForSlot, singleCriterionOnlyOf, } from "#injection/dependency-slot";
|
|
6
|
+
import { SCOPED_MISS } from "#lifecycle/scopes";
|
|
7
7
|
import { settleInOrder } from "#resolution/async-fan-out";
|
|
8
8
|
import { ActivationNeedCache } from "#resolution/cache/activation-need";
|
|
9
|
-
import { BindingLookupCache } from "#resolution/cache/binding-lookup-cache";
|
|
10
9
|
import { ClassIntrospector } from "#resolution/cache/class-introspector";
|
|
10
|
+
import { BindingLookupCache } from "#resolution/cache/lookup";
|
|
11
11
|
import { AsyncLevelContext, DefaultConstraintContext, DefaultResolutionContext } from "#resolution/context";
|
|
12
|
-
import { branchDepthOf, buildResolutionFrame, cycleNamesOf, enterSyncPath, extendResolutionBranch, leaveSyncPath, ROOT_BRANCH, UNOWNED_BRANCH, } from "#resolution/path
|
|
13
|
-
import { InstantiationPlanCompiler, PLAN_RETRY } from "#resolution/plan/
|
|
14
|
-
import { matchesSlot, selectAllBindings, selectBinding } from "#resolution/select/
|
|
12
|
+
import { branchDepthOf, buildResolutionFrame, cycleNamesOf, enterSyncPath, extendResolutionBranch, leaveSyncPath, ROOT_BRANCH, UNOWNED_BRANCH, } from "#resolution/path";
|
|
13
|
+
import { InstantiationPlanCompiler, PLAN_RETRY } from "#resolution/plan/compiler";
|
|
14
|
+
import { matchesSlot, selectAllBindings, selectBinding } from "#resolution/select/candidates";
|
|
15
15
|
const EMPTY_FRAME_LIST = [];
|
|
16
16
|
const EMPTY_PARAM_LIST = [];
|
|
17
|
+
// A class with no params is constructed with no arguments, so no dependency array is built for it.
|
|
18
|
+
const NO_ARGUMENTS = [];
|
|
17
19
|
/** Plans compiled so far, with the `null` unplannable marks left out. */
|
|
18
20
|
function countCompiledPlans(plans) {
|
|
19
21
|
let count = 0;
|
|
@@ -190,9 +192,9 @@ export class DependencyResolver {
|
|
|
190
192
|
* can answer still does.
|
|
191
193
|
*/
|
|
192
194
|
#forwardingAliasOrParent(token, options, resolutionStack, singleCriterion) {
|
|
193
|
-
const
|
|
194
|
-
if (
|
|
195
|
-
return { binding:
|
|
195
|
+
const defaultSlotAlias = this.#registry.defaultSlotAlias(token);
|
|
196
|
+
if (defaultSlotAlias !== undefined) {
|
|
197
|
+
return { binding: defaultSlotAlias, owner: this };
|
|
196
198
|
}
|
|
197
199
|
return this.#parent === undefined
|
|
198
200
|
? undefined
|
|
@@ -514,7 +516,7 @@ export class DependencyResolver {
|
|
|
514
516
|
}
|
|
515
517
|
return this.resolve(token, options, resolutionStack);
|
|
516
518
|
},
|
|
517
|
-
// Dispatches exactly as #
|
|
519
|
+
// Dispatches exactly as #settleDep does, for the async lane's escapes.
|
|
518
520
|
resolveEscapedAsync: (token, options, arity, resolutionStack) => {
|
|
519
521
|
if (arity === "all") {
|
|
520
522
|
return this.resolveAllAsync(token, options, resolutionStack, UNOWNED_BRANCH);
|
|
@@ -691,10 +693,13 @@ export class DependencyResolver {
|
|
|
691
693
|
case "dynamic-async":
|
|
692
694
|
throw asyncResolutionErrorFor(binding, resolutionStack);
|
|
693
695
|
case "class": {
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
696
|
+
// One lookup of the class's facts serves the params, the accessor need and the construction.
|
|
697
|
+
const introspector = this.#introspector();
|
|
698
|
+
const target = binding.target;
|
|
699
|
+
const facts = introspector.facts(target);
|
|
700
|
+
const params = this.#constructorParams(target, facts);
|
|
701
|
+
const needsActiveContainer = introspector.needsActiveContainerOf(target, facts);
|
|
702
|
+
return introspector.construct(target, params.length === 0 ? NO_ARGUMENTS : this.#resolveDeps(params, resolutionStack), needsActiveContainer, needsActiveContainer ? this.#ambientResolutionFor(resolutionStack) : undefined);
|
|
698
703
|
}
|
|
699
704
|
case "resolved": {
|
|
700
705
|
const deps = this.#resolveDeps(binding.deps, resolutionStack);
|
|
@@ -716,8 +721,12 @@ export class DependencyResolver {
|
|
|
716
721
|
* @remarks A class the metadata reader knows nothing about is constructible only if it declares
|
|
717
722
|
* no parameters; anything else is a missing `@injectable()`.
|
|
718
723
|
*/
|
|
719
|
-
#constructorParams(target) {
|
|
720
|
-
|
|
724
|
+
#constructorParams(target, facts) {
|
|
725
|
+
// Metadata is fixed once a class is defined, so the answer is too; only a throw is left unsettled.
|
|
726
|
+
return (facts.params ??= this.#settleConstructorParams(target, facts));
|
|
727
|
+
}
|
|
728
|
+
#settleConstructorParams(target, facts) {
|
|
729
|
+
const meta = this.#introspector().constructorMetadataOf(target, facts);
|
|
721
730
|
if (meta !== undefined) {
|
|
722
731
|
return meta.params;
|
|
723
732
|
}
|
|
@@ -824,30 +833,35 @@ export class DependencyResolver {
|
|
|
824
833
|
for (let index = 0; index < candidates.length; index += 1) {
|
|
825
834
|
resolved[index] = this.#resolveCandidateSync(candidates[index], undefined, resolutionStack);
|
|
826
835
|
}
|
|
827
|
-
|
|
828
|
-
this.#settleCollectionValues(memo);
|
|
836
|
+
this.#settleOnRepeat(memo);
|
|
829
837
|
return resolved;
|
|
830
838
|
}
|
|
831
|
-
/** The async twin of `resolveRootCollection`: a stable value list
|
|
839
|
+
/** The async twin of `resolveRootCollection`: a stable value list answers at once, candidates fan out as usual. */
|
|
832
840
|
resolveRootCollectionAsync(token) {
|
|
833
841
|
// The async lane appends to its branch and never unwinds, so it works on a stack of its own.
|
|
834
842
|
const resolutionStack = [];
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
843
|
+
try {
|
|
844
|
+
// Selection runs `when()` predicates and follows aliases, either of which may throw.
|
|
845
|
+
const memo = this.#rootCollection(token, resolutionStack);
|
|
846
|
+
if (memo.values !== undefined && memo.activationVersion === this.#chainActivationVersion()) {
|
|
847
|
+
return Promise.resolve(memo.values.slice());
|
|
848
|
+
}
|
|
849
|
+
return asPromise(this.#settleCandidates(memo.candidates, undefined, resolutionStack, UNOWNED_BRANCH, (values) => {
|
|
850
|
+
this.#settleOnRepeat(memo);
|
|
851
|
+
return values;
|
|
852
|
+
}));
|
|
853
|
+
}
|
|
854
|
+
catch (collectionError) {
|
|
855
|
+
return Promise.reject(collectionError);
|
|
838
856
|
}
|
|
839
|
-
return settleInOrder(memo.candidates.map((candidate) => this.#resolveCandidateAsync(candidate, undefined, resolutionStack, UNOWNED_BRANCH)), (values) => {
|
|
840
|
-
this.#settleCollectionValues(memo);
|
|
841
|
-
return values;
|
|
842
|
-
});
|
|
843
857
|
}
|
|
844
858
|
/**
|
|
845
859
|
* The memoized candidate list of a root-level, options-less collection, built on its first read.
|
|
846
860
|
*
|
|
847
861
|
* @remarks Sound because a `when()` predicate is pure over its context and the root context is a
|
|
848
862
|
* constant: the list can only change when a registry in the chain does, which is the version the
|
|
849
|
-
* memo is stamped with.
|
|
850
|
-
* and no activation hook exists anywhere in the chain.
|
|
863
|
+
* memo is stamped with. From the second read on, the value list is kept too while every member is
|
|
864
|
+
* a hook-free constant and no activation hook exists anywhere in the chain.
|
|
851
865
|
*/
|
|
852
866
|
#rootCollection(token, resolutionStack) {
|
|
853
867
|
const memo = this.#lookup.collection(token);
|
|
@@ -855,11 +869,20 @@ export class DependencyResolver {
|
|
|
855
869
|
return memo;
|
|
856
870
|
}
|
|
857
871
|
const candidates = this.#candidateBindings(token, undefined, resolutionStack);
|
|
858
|
-
const entry = { candidates, values: undefined, activationVersion: -1 };
|
|
859
|
-
this.#settleCollectionValues(entry);
|
|
872
|
+
const entry = { candidates, values: undefined, activationVersion: -1, readBefore: false };
|
|
860
873
|
this.#lookup.rememberCollection(token, entry);
|
|
861
874
|
return entry;
|
|
862
875
|
}
|
|
876
|
+
// The first read interprets and the read that repeats it settles the value list, as a plan compiles on repeat;
|
|
877
|
+
// the members either read materialised may have made the list stable for the next one.
|
|
878
|
+
#settleOnRepeat(entry) {
|
|
879
|
+
if (entry.readBefore) {
|
|
880
|
+
this.#settleCollectionValues(entry);
|
|
881
|
+
}
|
|
882
|
+
else {
|
|
883
|
+
entry.readBefore = true;
|
|
884
|
+
}
|
|
885
|
+
}
|
|
863
886
|
/**
|
|
864
887
|
* Fills a collection memo's value list once every member is stable: a hook-free constant, or a
|
|
865
888
|
* hook-free singleton whose instance is cached — anything that changes either bumps a registry
|
|
@@ -894,6 +917,18 @@ export class DependencyResolver {
|
|
|
894
917
|
const selected = selectAllBindings(allBindings, options, this.#makeConstraintContext(resolutionStack, options));
|
|
895
918
|
return this.#withoutDanglingAliases(selected, options, resolutionStack);
|
|
896
919
|
}
|
|
920
|
+
// A chain none of whose registries ever held an alias has none to drop, which spares the scan.
|
|
921
|
+
#chainHeldAlias() {
|
|
922
|
+
if (this.#registry.hasHeldAlias) {
|
|
923
|
+
return true;
|
|
924
|
+
}
|
|
925
|
+
for (let current = this.#parent; current !== undefined; current = current.#parent) {
|
|
926
|
+
if (current.#registry.hasHeldAlias) {
|
|
927
|
+
return true;
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
return false;
|
|
931
|
+
}
|
|
897
932
|
/**
|
|
898
933
|
* The candidates with any alias whose chain ends nowhere dropped, so a fan-out skips a dangling
|
|
899
934
|
* alias rather than throwing on it.
|
|
@@ -902,6 +937,9 @@ export class DependencyResolver {
|
|
|
902
937
|
* is returned untouched, so nothing is allocated for it.
|
|
903
938
|
*/
|
|
904
939
|
#withoutDanglingAliases(candidates, options, resolutionStack) {
|
|
940
|
+
if (!this.#chainHeldAlias()) {
|
|
941
|
+
return candidates;
|
|
942
|
+
}
|
|
905
943
|
let hasAlias = false;
|
|
906
944
|
for (let index = 0; index < candidates.length; index += 1) {
|
|
907
945
|
if (candidates[index].kind === "alias") {
|
|
@@ -936,35 +974,55 @@ export class DependencyResolver {
|
|
|
936
974
|
}
|
|
937
975
|
return this.#resolveAsyncDefaultEntry(entry.binding, entry.owner, resolutionStack, branchDepth);
|
|
938
976
|
}
|
|
977
|
+
// A `#settle*` lane answers with the value when its level completed in this tick and with a promise
|
|
978
|
+
// only where something had to be awaited, so a singleton or scoped instance built without yielding
|
|
979
|
+
// is cached before the fan-out that started it moves on to a sibling that may read it synchronously.
|
|
939
980
|
#resolveAsyncDefaultEntry(binding, owner, resolutionStack, branchDepth) {
|
|
981
|
+
try {
|
|
982
|
+
return asPromise(this.#settleDefaultEntry(binding, owner, resolutionStack, branchDepth));
|
|
983
|
+
}
|
|
984
|
+
catch (entryError) {
|
|
985
|
+
// Not `async`: a failure is turned into the rejection the entry point promises.
|
|
986
|
+
return Promise.reject(entryError);
|
|
987
|
+
}
|
|
988
|
+
}
|
|
989
|
+
/** `resolveAsyncFromContext` for a dependency slot: the same lanes, answered in this tick where they can be. */
|
|
990
|
+
#settleDefault(token, resolutionStack, branchDepth) {
|
|
991
|
+
const fastBinding = this.#registry.getFastDefault(token);
|
|
992
|
+
if (fastBinding !== undefined && fastBinding.kind !== "alias") {
|
|
993
|
+
return this.#settleDefaultEntry(fastBinding, this, resolutionStack, branchDepth);
|
|
994
|
+
}
|
|
995
|
+
const entry = this.#lookup.defaultEntry(token);
|
|
996
|
+
if (entry === null) {
|
|
997
|
+
return this.#settleRequest(token, undefined, resolutionStack, branchDepth);
|
|
998
|
+
}
|
|
999
|
+
return this.#settleDefaultEntry(entry.binding, entry.owner, resolutionStack, branchDepth);
|
|
1000
|
+
}
|
|
1001
|
+
#settleDefaultEntry(binding, owner, resolutionStack, branchDepth) {
|
|
940
1002
|
if (owner.#isPlainConstant(binding)) {
|
|
941
|
-
return
|
|
1003
|
+
return binding.value;
|
|
942
1004
|
}
|
|
943
1005
|
const scope = binding.scope;
|
|
944
1006
|
if (scope === "transient") {
|
|
945
1007
|
if ((binding.kind === "dynamic" || binding.kind === "dynamic-async") && !owner.#hasAnyActivation(binding)) {
|
|
946
|
-
return this.#
|
|
1008
|
+
return this.#settleTransientFactory(binding, resolutionStack, branchDepth);
|
|
947
1009
|
}
|
|
948
1010
|
}
|
|
949
1011
|
else if (scope === "singleton") {
|
|
950
1012
|
if (binding.instance !== NO_INSTANCE) {
|
|
951
|
-
return
|
|
1013
|
+
return binding.instance;
|
|
952
1014
|
}
|
|
953
1015
|
if (owner !== this) {
|
|
954
|
-
return owner.#
|
|
1016
|
+
return owner.#settleBinding(binding, undefined, resolutionStack, branchDepth, owner);
|
|
955
1017
|
}
|
|
956
1018
|
}
|
|
957
|
-
else
|
|
958
|
-
const cachedScoped = this.#
|
|
1019
|
+
else {
|
|
1020
|
+
const cachedScoped = this.#readScoped(binding);
|
|
959
1021
|
if (cachedScoped !== SCOPED_MISS) {
|
|
960
|
-
return
|
|
1022
|
+
return cachedScoped;
|
|
961
1023
|
}
|
|
962
1024
|
}
|
|
963
|
-
|
|
964
|
-
// Not `#readScoped`: this entry point reports failure as a rejection, never a sync throw.
|
|
965
|
-
return Promise.reject(new MissingScopeContextError(tokenName(binding.token)));
|
|
966
|
-
}
|
|
967
|
-
return this.#resolveBindingAsync(binding, undefined, resolutionStack, branchDepth, owner);
|
|
1025
|
+
return this.#settleBinding(binding, undefined, resolutionStack, branchDepth, owner);
|
|
968
1026
|
}
|
|
969
1027
|
/**
|
|
970
1028
|
* Instantiates one owned binding directly, bypassing selection.
|
|
@@ -973,18 +1031,38 @@ export class DependencyResolver {
|
|
|
973
1031
|
* could pick a different candidate whose criteria are a subset of them.
|
|
974
1032
|
*/
|
|
975
1033
|
warmBindingAsync(binding, options) {
|
|
976
|
-
|
|
1034
|
+
try {
|
|
1035
|
+
return asPromise(this.#settleBinding(binding, options, [], ROOT_BRANCH, this));
|
|
1036
|
+
}
|
|
1037
|
+
catch (bindingError) {
|
|
1038
|
+
return Promise.reject(bindingError);
|
|
1039
|
+
}
|
|
977
1040
|
}
|
|
978
|
-
|
|
1041
|
+
resolveAsync(token, options, resolutionStack, branchDepth = UNOWNED_BRANCH, precomputedCriterion) {
|
|
1042
|
+
try {
|
|
1043
|
+
return asPromise(this.#settleRequest(token, options, resolutionStack, branchDepth, precomputedCriterion));
|
|
1044
|
+
}
|
|
1045
|
+
catch (requestError) {
|
|
1046
|
+
return Promise.reject(requestError);
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
/** Selects the binding a request names and settles it on the async lane. */
|
|
1050
|
+
#settleRequest(token, options, resolutionStack, branchDepth, precomputedCriterion) {
|
|
979
1051
|
const path = ownPrefixOf(resolutionStack, branchDepth);
|
|
980
1052
|
const depth = path === resolutionStack ? branchDepth : branchDepthOf(path);
|
|
981
1053
|
const { binding, owner } = this.#requireBinding(token, options, path, precomputedCriterion);
|
|
982
1054
|
if (binding.scope === "singleton" && owner !== this) {
|
|
983
|
-
return owner.#
|
|
1055
|
+
return owner.#settleBinding(binding, options, path, depth, owner);
|
|
984
1056
|
}
|
|
985
|
-
return this.#
|
|
1057
|
+
return this.#settleBinding(binding, options, path, depth, owner);
|
|
986
1058
|
}
|
|
987
|
-
|
|
1059
|
+
/**
|
|
1060
|
+
* Materialises one binding on the async lane: the instance once nothing yielded, else its promise.
|
|
1061
|
+
*
|
|
1062
|
+
* @remarks A singleton or scoped instance built in this tick is cached in this tick, so a sibling
|
|
1063
|
+
* reading it synchronously finds it; only a materialisation still pending is published in flight.
|
|
1064
|
+
*/
|
|
1065
|
+
#settleBinding(binding, options, resolutionStack, branchDepth, owner) {
|
|
988
1066
|
if (owner.#isPlainConstant(binding)) {
|
|
989
1067
|
return binding.value;
|
|
990
1068
|
}
|
|
@@ -996,7 +1074,7 @@ export class DependencyResolver {
|
|
|
996
1074
|
// In-flight dedup: concurrent callers share the first creation.
|
|
997
1075
|
const inflight = this.#scope.getInflight(binding.identifier);
|
|
998
1076
|
if (inflight !== undefined) {
|
|
999
|
-
return inflight;
|
|
1077
|
+
return ownPromiseOf(inflight);
|
|
1000
1078
|
}
|
|
1001
1079
|
if (this.#scope.isClosed) {
|
|
1002
1080
|
throw new DisposedContainerError();
|
|
@@ -1010,7 +1088,7 @@ export class DependencyResolver {
|
|
|
1010
1088
|
// In-flight dedup, scoped flavor: one instance per scope even under concurrency.
|
|
1011
1089
|
const inflight = this.#scope.getInflight(binding.identifier);
|
|
1012
1090
|
if (inflight !== undefined) {
|
|
1013
|
-
return inflight;
|
|
1091
|
+
return ownPromiseOf(inflight);
|
|
1014
1092
|
}
|
|
1015
1093
|
if (this.#scope.isClosed) {
|
|
1016
1094
|
throw new DisposedContainerError();
|
|
@@ -1026,8 +1104,7 @@ export class DependencyResolver {
|
|
|
1026
1104
|
if (branchDepth === ROOT_BRANCH) {
|
|
1027
1105
|
this.#recentRootLevelContext = resolutionCtx;
|
|
1028
1106
|
}
|
|
1029
|
-
|
|
1030
|
-
return dynamicResult instanceof Promise ? await dynamicResult : dynamicResult;
|
|
1107
|
+
return runFactoryPrefix(binding, resolutionCtx, levelStack);
|
|
1031
1108
|
}
|
|
1032
1109
|
const resolutionCtx = needsActivation || requiresResolutionContext(binding)
|
|
1033
1110
|
? new AsyncLevelContext(this, levelStack, options)
|
|
@@ -1035,33 +1112,41 @@ export class DependencyResolver {
|
|
|
1035
1112
|
if (resolutionCtx !== undefined && branchDepth === ROOT_BRANCH) {
|
|
1036
1113
|
this.#recentRootLevelContext = resolutionCtx;
|
|
1037
1114
|
}
|
|
1115
|
+
const activated = this.#settleActivated(binding, resolutionCtx, levelStack, levelDepth, needsActivation, owner);
|
|
1116
|
+
if (scope === "transient") {
|
|
1117
|
+
return activated;
|
|
1118
|
+
}
|
|
1119
|
+
if (isPending(activated)) {
|
|
1120
|
+
return this.#publishInFlight(binding, activated, scope);
|
|
1121
|
+
}
|
|
1038
1122
|
if (scope === "singleton") {
|
|
1039
|
-
|
|
1040
|
-
|
|
1123
|
+
this.#scope.setSingleton(binding, activated);
|
|
1124
|
+
}
|
|
1125
|
+
else {
|
|
1126
|
+
this.#scope.setScoped(binding, activated);
|
|
1127
|
+
}
|
|
1128
|
+
return activated;
|
|
1129
|
+
}
|
|
1130
|
+
/**
|
|
1131
|
+
* Publishes a pending singleton or scoped materialisation so concurrent callers dedup onto it,
|
|
1132
|
+
* and caches what it settles to; the creator, like every joiner, is handed a promise of its own.
|
|
1133
|
+
*/
|
|
1134
|
+
#publishInFlight(binding, pending, scope) {
|
|
1135
|
+
const published = pending.then((activated) => {
|
|
1136
|
+
if (scope === "singleton") {
|
|
1041
1137
|
this.#scope.setSingleton(binding, activated);
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
}, (error) => {
|
|
1045
|
-
this.#scope.clearInflight(binding.identifier);
|
|
1046
|
-
throw error;
|
|
1047
|
-
});
|
|
1048
|
-
this.#scope.setInflight(binding.identifier, singletonPromise);
|
|
1049
|
-
return await singletonPromise;
|
|
1050
|
-
}
|
|
1051
|
-
if (scope === "scoped") {
|
|
1052
|
-
// Published before it settles, like the singleton lane: concurrent callers share one creation.
|
|
1053
|
-
const scopedPromise = this.#instantiateAndActivateAsync(binding, resolutionCtx, levelStack, levelDepth, needsActivation, owner).then((activated) => {
|
|
1138
|
+
}
|
|
1139
|
+
else {
|
|
1054
1140
|
this.#scope.setScoped(binding, activated);
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
return await this.#instantiateAndActivateAsync(binding, resolutionCtx, levelStack, levelDepth, needsActivation, owner);
|
|
1141
|
+
}
|
|
1142
|
+
this.#scope.clearInflight(binding.identifier);
|
|
1143
|
+
return activated;
|
|
1144
|
+
}, (error) => {
|
|
1145
|
+
this.#scope.clearInflight(binding.identifier);
|
|
1146
|
+
throw error;
|
|
1147
|
+
});
|
|
1148
|
+
this.#scope.setInflight(binding.identifier, published);
|
|
1149
|
+
return ownPromiseOf(published);
|
|
1065
1150
|
}
|
|
1066
1151
|
/**
|
|
1067
1152
|
* Settles this resolver's own `postConstruct` answer for a class binding a parent owns.
|
|
@@ -1077,15 +1162,23 @@ export class DependencyResolver {
|
|
|
1077
1162
|
this.#introspector().discoverPostConstruct(binding.target);
|
|
1078
1163
|
}
|
|
1079
1164
|
}
|
|
1080
|
-
|
|
1081
|
-
|
|
1165
|
+
/** The activated instance: built and activated in this tick when nothing yielded, else its promise. */
|
|
1166
|
+
#settleActivated(binding, ctx, resolutionStack, branchDepth, needsActivation, owner) {
|
|
1167
|
+
const instance = this.#settleInstance(binding, ctx, resolutionStack, branchDepth);
|
|
1168
|
+
if (isPending(instance)) {
|
|
1169
|
+
return instance.then((built) => this.#activateAsync(binding, ctx, built, needsActivation, owner));
|
|
1170
|
+
}
|
|
1171
|
+
return this.#activateAsync(binding, ctx, instance, needsActivation, owner);
|
|
1172
|
+
}
|
|
1173
|
+
#activateAsync(binding, ctx, instance, needsActivation, owner) {
|
|
1082
1174
|
this.#mirrorPostConstructFromOwner(binding, owner);
|
|
1083
1175
|
if (!owner.#activationNeed().refreshAfterFirstInstantiation(binding, needsActivation)) {
|
|
1084
1176
|
return instance;
|
|
1085
1177
|
}
|
|
1086
1178
|
return owner.#lifecycle.runActivation(ctx, binding, instance, owner.#metadataReader);
|
|
1087
1179
|
}
|
|
1088
|
-
|
|
1180
|
+
/** The instance a binding builds: in this tick when every dependency settled in it, else its promise. */
|
|
1181
|
+
#settleInstance(binding, ctx, resolutionStack, branchDepth) {
|
|
1089
1182
|
switch (binding.kind) {
|
|
1090
1183
|
case "constant":
|
|
1091
1184
|
return binding.value;
|
|
@@ -1094,59 +1187,94 @@ export class DependencyResolver {
|
|
|
1094
1187
|
if (ctx === undefined) {
|
|
1095
1188
|
throw new InternalError("dynamic binding requires resolution context");
|
|
1096
1189
|
}
|
|
1097
|
-
|
|
1098
|
-
return factoryResult instanceof Promise ? factoryResult : Promise.resolve(factoryResult);
|
|
1190
|
+
return runFactoryPrefix(binding, ctx, resolutionStack);
|
|
1099
1191
|
}
|
|
1100
1192
|
case "class": {
|
|
1101
|
-
const
|
|
1193
|
+
const introspector = this.#introspector();
|
|
1194
|
+
const target = binding.target;
|
|
1195
|
+
const facts = introspector.facts(target);
|
|
1196
|
+
const params = this.#constructorParams(target, facts);
|
|
1197
|
+
const needsActiveContainer = introspector.needsActiveContainerOf(target, facts);
|
|
1102
1198
|
// Accessor initializers resolve synchronously, so the branch-owned path serves them directly.
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1199
|
+
const ambient = needsActiveContainer ? this.#ambientResolutionFor(resolutionStack) : undefined;
|
|
1200
|
+
if (params.length === 0) {
|
|
1201
|
+
return introspector.construct(target, NO_ARGUMENTS, needsActiveContainer, ambient);
|
|
1202
|
+
}
|
|
1203
|
+
const deps = this.#settleDeps(params, resolutionStack, branchDepth);
|
|
1204
|
+
if (isPending(deps)) {
|
|
1205
|
+
return deps.then((settled) => introspector.construct(target, settled, needsActiveContainer, ambient));
|
|
1206
|
+
}
|
|
1207
|
+
return introspector.construct(target, deps, needsActiveContainer, ambient);
|
|
1106
1208
|
}
|
|
1107
1209
|
case "resolved": {
|
|
1108
|
-
const
|
|
1109
|
-
const
|
|
1110
|
-
return
|
|
1210
|
+
const factory = binding.factory;
|
|
1211
|
+
const deps = this.#settleDeps(binding.deps, resolutionStack, branchDepth);
|
|
1212
|
+
return isPending(deps) ? deps.then((settled) => factory(...settled)) : factory(...deps);
|
|
1111
1213
|
}
|
|
1112
1214
|
case "resolved-async": {
|
|
1113
|
-
const
|
|
1114
|
-
|
|
1215
|
+
const factory = binding.factory;
|
|
1216
|
+
const deps = this.#settleDeps(binding.deps, resolutionStack, branchDepth);
|
|
1217
|
+
return isPending(deps) ? deps.then((settled) => factory(...settled)) : factory(...deps);
|
|
1115
1218
|
}
|
|
1116
1219
|
case "alias":
|
|
1117
1220
|
throw new InternalError("alias should have been followed before instantiation");
|
|
1118
1221
|
}
|
|
1119
1222
|
}
|
|
1120
|
-
|
|
1223
|
+
/**
|
|
1224
|
+
* One level's dependencies: their values when every one settled in this tick, else the fan-out
|
|
1225
|
+
* that settles them in declaration order.
|
|
1226
|
+
*/
|
|
1227
|
+
#settleDeps(deps, resolutionStack, branchDepth) {
|
|
1121
1228
|
const count = deps.length;
|
|
1122
1229
|
if (count === 0) {
|
|
1123
1230
|
return [];
|
|
1124
1231
|
}
|
|
1125
1232
|
if (count === 1) {
|
|
1126
|
-
|
|
1233
|
+
const only = this.#settleDep(deps[0], resolutionStack, branchDepth);
|
|
1234
|
+
return isPending(only) ? only.then(settledAlone) : [only];
|
|
1127
1235
|
}
|
|
1128
|
-
// Siblings
|
|
1129
|
-
// place and the rest copy the prefix — no caller has to isolate them.
|
|
1236
|
+
// Siblings start concurrently and each extends the same branch, so the first appends in
|
|
1237
|
+
// place and the rest copy the prefix — no caller has to isolate them. A sibling's sync throw
|
|
1238
|
+
// becomes its slot's rejection so the ones after it still start, as on every other lane.
|
|
1130
1239
|
const pending = new Array(count);
|
|
1240
|
+
let yielded = false;
|
|
1131
1241
|
for (let index = 0; index < count; index += 1) {
|
|
1132
|
-
|
|
1242
|
+
let settled;
|
|
1243
|
+
try {
|
|
1244
|
+
settled = this.#settleDep(deps[index], resolutionStack, branchDepth);
|
|
1245
|
+
}
|
|
1246
|
+
catch (dependencyError) {
|
|
1247
|
+
settled = Promise.reject(dependencyError);
|
|
1248
|
+
}
|
|
1249
|
+
if (isPending(settled)) {
|
|
1250
|
+
yielded = true;
|
|
1251
|
+
}
|
|
1252
|
+
pending[index] = settled;
|
|
1133
1253
|
}
|
|
1134
|
-
return settleInOrder(pending, identity);
|
|
1254
|
+
return yielded ? settleInOrder(pending, identity) : pending;
|
|
1135
1255
|
}
|
|
1136
|
-
#
|
|
1256
|
+
#settleDep(dep, resolutionStack, branchDepth) {
|
|
1137
1257
|
const options = resolveOptionsForSlot(dep);
|
|
1138
1258
|
if (dep.multi) {
|
|
1139
|
-
return this
|
|
1259
|
+
return this.#settleCollection(dep.token, options, resolutionStack, branchDepth);
|
|
1140
1260
|
}
|
|
1141
1261
|
if (dep.optional) {
|
|
1142
|
-
return this
|
|
1262
|
+
return this.#settleOptionalRequest(dep.token, options, resolutionStack, branchDepth, singleCriterionForSlot(dep));
|
|
1143
1263
|
}
|
|
1144
1264
|
if (options === undefined) {
|
|
1145
|
-
return this
|
|
1265
|
+
return this.#settleDefault(dep.token, resolutionStack, branchDepth);
|
|
1146
1266
|
}
|
|
1147
|
-
return this
|
|
1267
|
+
return this.#settleRequest(dep.token, options, resolutionStack, branchDepth, singleCriterionForSlot(dep));
|
|
1148
1268
|
}
|
|
1149
|
-
|
|
1269
|
+
resolveOptionalAsync(token, options, resolutionStack, branchDepth = UNOWNED_BRANCH, precomputedCriterion) {
|
|
1270
|
+
try {
|
|
1271
|
+
return asPromise(this.#settleOptionalRequest(token, options, resolutionStack, branchDepth, precomputedCriterion));
|
|
1272
|
+
}
|
|
1273
|
+
catch (requestError) {
|
|
1274
|
+
return Promise.reject(requestError);
|
|
1275
|
+
}
|
|
1276
|
+
}
|
|
1277
|
+
#settleOptionalRequest(token, options, resolutionStack, branchDepth, precomputedCriterion) {
|
|
1150
1278
|
const path = ownPrefixOf(resolutionStack, branchDepth);
|
|
1151
1279
|
const depth = path === resolutionStack ? branchDepth : branchDepthOf(path);
|
|
1152
1280
|
const singleCriterion = precomputedCriterion === undefined ? singleCriterionOnlyOf(options) : (precomputedCriterion ?? undefined);
|
|
@@ -1161,19 +1289,46 @@ export class DependencyResolver {
|
|
|
1161
1289
|
}
|
|
1162
1290
|
const { binding, owner } = entry;
|
|
1163
1291
|
if (binding.scope === "singleton" && owner !== this) {
|
|
1164
|
-
return owner.#
|
|
1292
|
+
return owner.#settleBinding(binding, options, path, depth, owner);
|
|
1293
|
+
}
|
|
1294
|
+
return this.#settleBinding(binding, options, path, depth, owner);
|
|
1295
|
+
}
|
|
1296
|
+
resolveAllAsync(token, options, resolutionStack, branchDepth = UNOWNED_BRANCH) {
|
|
1297
|
+
try {
|
|
1298
|
+
return asPromise(this.#settleCollection(token, options, resolutionStack, branchDepth));
|
|
1299
|
+
}
|
|
1300
|
+
catch (collectionError) {
|
|
1301
|
+
return Promise.reject(collectionError);
|
|
1165
1302
|
}
|
|
1166
|
-
return this.#resolveBindingAsync(binding, options, path, depth, owner);
|
|
1167
1303
|
}
|
|
1168
|
-
|
|
1304
|
+
#settleCollection(token, options, resolutionStack, branchDepth) {
|
|
1169
1305
|
const path = ownPrefixOf(resolutionStack, branchDepth);
|
|
1170
1306
|
const depth = path === resolutionStack ? branchDepth : branchDepthOf(path);
|
|
1171
1307
|
const candidates = this.#candidateBindings(token, options, path);
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1308
|
+
return this.#settleCandidates(candidates, options, path, depth, identity);
|
|
1309
|
+
}
|
|
1310
|
+
/**
|
|
1311
|
+
* Every candidate of a fan-out, started in order: `apply` over their values when all settled in
|
|
1312
|
+
* this tick, else over the fan-out settled in declaration order.
|
|
1313
|
+
*/
|
|
1314
|
+
#settleCandidates(candidates, options, resolutionStack, branchDepth, apply) {
|
|
1315
|
+
const count = candidates.length;
|
|
1316
|
+
const pending = new Array(count);
|
|
1317
|
+
let yielded = false;
|
|
1318
|
+
for (let index = 0; index < count; index += 1) {
|
|
1319
|
+
let settled;
|
|
1320
|
+
try {
|
|
1321
|
+
settled = this.#settleCandidate(candidates[index], options, resolutionStack, branchDepth);
|
|
1322
|
+
}
|
|
1323
|
+
catch (memberError) {
|
|
1324
|
+
settled = Promise.reject(memberError);
|
|
1325
|
+
}
|
|
1326
|
+
if (isPending(settled)) {
|
|
1327
|
+
yielded = true;
|
|
1328
|
+
}
|
|
1329
|
+
pending[index] = settled;
|
|
1175
1330
|
}
|
|
1176
|
-
return settleInOrder(pending,
|
|
1331
|
+
return yielded ? settleInOrder(pending, apply) : apply(pending);
|
|
1177
1332
|
}
|
|
1178
1333
|
// ── Helpers ────────────────────────────────────────────────────────────────────────────────────────────────────────
|
|
1179
1334
|
#allBindingsFromChain(token) {
|
|
@@ -1254,7 +1409,7 @@ export class DependencyResolver {
|
|
|
1254
1409
|
if (!this.#scope.isChild) {
|
|
1255
1410
|
throw new MissingScopeContextError(tokenName(binding.token));
|
|
1256
1411
|
}
|
|
1257
|
-
return this.#scope.readScoped(binding.
|
|
1412
|
+
return this.#scope.readScoped(binding.scopedCacheKey);
|
|
1258
1413
|
}
|
|
1259
1414
|
// The shared root context answers every top-level request; building one is the rarer half and
|
|
1260
1415
|
// lives outside, so what a selection inlines is the test and not the literal.
|
|
@@ -1294,7 +1449,19 @@ export class DependencyResolver {
|
|
|
1294
1449
|
binding.inFlight = false;
|
|
1295
1450
|
}
|
|
1296
1451
|
}
|
|
1297
|
-
|
|
1452
|
+
/** A transient factory level with no activation: its factory's own result over the level's context. */
|
|
1453
|
+
#settleTransientFactory(binding, resolutionStack, branchDepth) {
|
|
1454
|
+
const frame = this.#getResolutionFrame(binding);
|
|
1455
|
+
const levelStack = extendResolutionBranch(resolutionStack, branchDepth, frame);
|
|
1456
|
+
// Nothing this level appended is ever removed, so no level observes its own settlement.
|
|
1457
|
+
const ctx = new AsyncLevelContext(this, levelStack, undefined);
|
|
1458
|
+
if (branchDepth === ROOT_BRANCH) {
|
|
1459
|
+
this.#recentRootLevelContext = ctx;
|
|
1460
|
+
}
|
|
1461
|
+
return runFactoryPrefix(binding, ctx, levelStack);
|
|
1462
|
+
}
|
|
1463
|
+
// Deliberately not `async`: that would allocate a state machine and a promise per level. The
|
|
1464
|
+
// dominant lane keeps its body whole rather than reaching the settle core through a call.
|
|
1298
1465
|
#resolveTransientDynamicAsyncFromContext(binding, resolutionStack, branchDepth) {
|
|
1299
1466
|
const frame = this.#getResolutionFrame(binding);
|
|
1300
1467
|
let levelStack;
|
|
@@ -1405,33 +1572,33 @@ export class DependencyResolver {
|
|
|
1405
1572
|
}
|
|
1406
1573
|
return this.#resolveBinding(binding, options, resolutionStack, owner);
|
|
1407
1574
|
}
|
|
1408
|
-
#
|
|
1575
|
+
#settleCandidate(binding, options, resolutionStack, branchDepth) {
|
|
1409
1576
|
if (binding.kind === "constant" && binding.activationHook === undefined && this.#chainActivationVersion() === 0) {
|
|
1410
|
-
return
|
|
1577
|
+
return binding.value;
|
|
1411
1578
|
}
|
|
1412
1579
|
const owner = this.#ownerOf(binding);
|
|
1413
1580
|
if (owner.#isPlainConstant(binding)) {
|
|
1414
|
-
return
|
|
1581
|
+
return binding.value;
|
|
1415
1582
|
}
|
|
1416
1583
|
if (binding.kind === "alias") {
|
|
1417
|
-
return this
|
|
1584
|
+
return this.#settleRequest(binding.target, options, resolutionStack, branchDepth);
|
|
1418
1585
|
}
|
|
1419
1586
|
if (binding.scope === "singleton") {
|
|
1420
1587
|
if (binding.instance !== NO_INSTANCE) {
|
|
1421
|
-
return
|
|
1588
|
+
return binding.instance;
|
|
1422
1589
|
}
|
|
1423
|
-
return owner.#
|
|
1590
|
+
return owner.#settleBinding(binding, options, resolutionStack, branchDepth, owner);
|
|
1424
1591
|
}
|
|
1425
1592
|
// The dominant collection member — a transient factory with no activation, asked with no
|
|
1426
|
-
// options — takes the
|
|
1427
|
-
//
|
|
1593
|
+
// options — takes the lane a single resolve takes, so a fan-out costs one factory result per
|
|
1594
|
+
// member rather than a promise on top of each.
|
|
1428
1595
|
if (options === undefined &&
|
|
1429
1596
|
binding.scope === "transient" &&
|
|
1430
1597
|
(binding.kind === "dynamic" || binding.kind === "dynamic-async") &&
|
|
1431
1598
|
!owner.#hasAnyActivation(binding)) {
|
|
1432
|
-
return this.#
|
|
1599
|
+
return this.#settleTransientFactory(binding, resolutionStack, branchDepth);
|
|
1433
1600
|
}
|
|
1434
|
-
return this.#
|
|
1601
|
+
return this.#settleBinding(binding, options, resolutionStack, branchDepth, owner);
|
|
1435
1602
|
}
|
|
1436
1603
|
/** The resolver whose registry holds `binding` — `this` (the common case) when it is own. */
|
|
1437
1604
|
#ownerOf(binding) {
|
|
@@ -1547,6 +1714,26 @@ function asyncResolutionErrorFor(binding, resolutionStack) {
|
|
|
1547
1714
|
function identity(values) {
|
|
1548
1715
|
return values;
|
|
1549
1716
|
}
|
|
1717
|
+
/** A settled value, as it is. */
|
|
1718
|
+
function settledValue(value) {
|
|
1719
|
+
return value;
|
|
1720
|
+
}
|
|
1721
|
+
/** A promise of its own over a shared in-flight one: the same settlement, never the stored object. */
|
|
1722
|
+
function ownPromiseOf(shared) {
|
|
1723
|
+
return shared.then(settledValue);
|
|
1724
|
+
}
|
|
1725
|
+
/** A lone dependency's settled value as the argument list its level applies. */
|
|
1726
|
+
function settledAlone(value) {
|
|
1727
|
+
return [value];
|
|
1728
|
+
}
|
|
1729
|
+
/** Whether an async-lane answer is still pending, narrowing it to the promise it is. */
|
|
1730
|
+
function isPending(answer) {
|
|
1731
|
+
return answer instanceof Promise;
|
|
1732
|
+
}
|
|
1733
|
+
/** A settled answer as the promise an async entry point hands out; a pending one passes through. */
|
|
1734
|
+
function asPromise(answer) {
|
|
1735
|
+
return answer instanceof Promise ? answer : Promise.resolve(answer);
|
|
1736
|
+
}
|
|
1550
1737
|
/** Only a factory is handed the resolution context; everything else gets its deps directly. */
|
|
1551
1738
|
function requiresResolutionContext(binding) {
|
|
1552
1739
|
return binding.kind === "dynamic" || binding.kind === "dynamic-async";
|