@codefast/di 0.10.1 → 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 +376 -0
- package/README.md +94 -17
- package/dist/{ambient/active-container.d.ts → ambient-container.d.ts} +7 -2
- package/dist/{ambient/active-container.js → ambient-container.js} +6 -1
- package/dist/container/{binding-builders.d.ts → binding-chain.d.ts} +27 -16
- package/dist/container/binding-chain.js +254 -0
- package/dist/container/container.d.ts +11 -2
- package/dist/container/container.js +293 -73
- package/dist/core/binding-builders.d.ts +114 -0
- package/dist/core/binding-declaration.d.ts +127 -0
- package/dist/core/binding-declaration.js +187 -0
- package/dist/core/binding.d.ts +62 -138
- package/dist/core/binding.js +54 -17
- package/dist/core/module.d.ts +8 -5
- package/dist/core/module.js +17 -3
- package/dist/core/registry.d.ts +17 -28
- package/dist/core/registry.js +92 -133
- package/dist/core/state-epoch.d.ts +18 -1
- package/dist/core/state-epoch.js +17 -0
- package/dist/core/tag.js +1 -1
- package/dist/decorators/inject.js +6 -5
- package/dist/decorators/injectable.js +4 -2
- package/dist/decorators/{lifecycle-decorators.js → lifecycle.js} +9 -3
- package/dist/decorators/metadata-record.d.ts +9 -0
- package/dist/decorators/metadata-record.js +20 -0
- package/dist/{errors/errors.d.ts → errors.d.ts} +84 -9
- package/dist/{errors/errors.js → errors.js} +101 -17
- package/dist/index.d.ts +14 -10
- package/dist/index.js +7 -6
- package/dist/injection/{resolve-options.js → dependency-slot.js} +6 -4
- package/dist/injection/descriptor.d.ts +1 -1
- package/dist/injection/descriptor.js +3 -7
- package/dist/introspection/dependency-graph.d.ts +8 -3
- package/dist/introspection/dependency-graph.js +48 -26
- package/dist/introspection/explanation.d.ts +101 -0
- package/dist/introspection/explanation.js +151 -0
- package/dist/introspection/graph-adapters/reactflow.js +6 -4
- package/dist/introspection/inspector.d.ts +9 -2
- package/dist/introspection/inspector.js +30 -28
- package/dist/lifecycle/{lifecycle-manager.d.ts → hooks.d.ts} +9 -2
- package/dist/lifecycle/{lifecycle-manager.js → hooks.js} +59 -30
- package/dist/lifecycle/{scope-manager.d.ts → scopes.d.ts} +3 -5
- package/dist/lifecycle/{scope-manager.js → scopes.js} +36 -29
- 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} +5 -4
- package/dist/metadata/verifying-reader.js +53 -0
- package/dist/resolution/async-fan-out.d.ts +12 -0
- package/dist/resolution/async-fan-out.js +26 -0
- package/dist/resolution/cache/activation-need.d.ts +1 -2
- package/dist/resolution/cache/activation-need.js +16 -19
- package/dist/resolution/cache/class-introspector.d.ts +38 -36
- package/dist/resolution/cache/class-introspector.js +79 -159
- package/dist/resolution/cache/{binding-lookup-cache.d.ts → lookup.d.ts} +7 -9
- package/dist/resolution/cache/{binding-lookup-cache.js → lookup.js} +70 -25
- package/dist/resolution/context.d.ts +16 -24
- package/dist/resolution/context.js +47 -56
- package/dist/resolution/path.d.ts +128 -0
- package/dist/resolution/path.js +167 -0
- package/dist/resolution/plan/{plan-codegen.d.ts → codegen.d.ts} +5 -4
- package/dist/resolution/plan/{plan-codegen.js → codegen.js} +58 -32
- package/dist/resolution/plan/{instantiation-plan.d.ts → compiler.d.ts} +1 -1
- package/dist/resolution/plan/{instantiation-plan.js → compiler.js} +64 -24
- package/dist/resolution/resolver.d.ts +11 -12
- package/dist/resolution/resolver.js +573 -356
- 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 +87 -67
- package/dist/container/binding-builders.js +0 -305
- package/dist/core/binding-scope.d.ts +0 -12
- package/dist/core/binding-scope.js +0 -12
- package/dist/metadata/verifying-metadata-reader.js +0 -31
- package/dist/resolution/path/resolution-path.d.ts +0 -93
- package/dist/resolution/path/resolution-path.js +0 -116
- /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/injection/{resolve-options.d.ts → dependency-slot.d.ts} +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
|
@@ -3,16 +3,18 @@
|
|
|
3
3
|
*
|
|
4
4
|
* @remarks One object plays every role: each builder step, the record the registry stores, and the
|
|
5
5
|
* binding the resolver reads. The binding fields are declared first and in a fixed order, so every
|
|
6
|
-
* binding in the process shares one hidden class; the return type of each step
|
|
7
|
-
*
|
|
6
|
+
* binding in the process shares one hidden class; the return type of each step pins the chain's
|
|
7
|
+
* order, slot before strategy, so the binding registers once and is never re-slotted.
|
|
8
8
|
*/
|
|
9
|
-
import type {
|
|
9
|
+
import type { Binding, BindingSlot } from "#core/binding";
|
|
10
|
+
import type { AliasBindingBuilder, BindingBuilder, BindToBuilder, ConstantBindingBuilder, ScopedBindingBuilder, SingletonBindingBuilder, SingletonLifecycleBuilder, TransientBindingBuilder } from "#core/binding-builders";
|
|
11
|
+
import type { DeclaredBinding } from "#core/binding-declaration";
|
|
10
12
|
import type { BindingRegistry } from "#core/registry";
|
|
11
13
|
import type { BindingTag } from "#core/tag";
|
|
12
14
|
import type { Token } from "#core/token";
|
|
13
15
|
import type { ActivationHandler, BindingConstraint, BindingIdentifier, BindingKind, BindingScope, Constructor, DeactivationHandler, ResolutionContext, ResolutionFrame } from "#core/types";
|
|
14
16
|
import type { InjectableDependency, InjectionDescriptor, ResolvedDependencyValue } from "#injection/descriptor";
|
|
15
|
-
import type { ScopeManager } from "#lifecycle/
|
|
17
|
+
import type { ScopeManager } from "#lifecycle/scopes";
|
|
16
18
|
/**
|
|
17
19
|
* Where a chain registers, and on whose behalf.
|
|
18
20
|
*
|
|
@@ -25,24 +27,31 @@ export interface BindingRegistration {
|
|
|
25
27
|
readonly registry: BindingRegistry;
|
|
26
28
|
readonly scope: ScopeManager;
|
|
27
29
|
readonly moduleBindingIds: Array<BindingIdentifier> | undefined;
|
|
30
|
+
/** Runs for a binding this registration displaces, deactivating it on the spot. */
|
|
31
|
+
readonly deactivateDisplaced?: ((binding: Binding) => void) | undefined;
|
|
32
|
+
/** Notes a displaced binding so the container can still tear it down, when nothing deactivates it on the spot. */
|
|
33
|
+
readonly onDisplaced?: ((binding: Binding) => void) | undefined;
|
|
28
34
|
}
|
|
29
35
|
/**
|
|
30
36
|
* The one builder behind `bind()` and every `to*()` return type — and the binding it registers.
|
|
31
37
|
*
|
|
32
38
|
* @remarks The binding fields come first, in the order every binding shares, and the chain's own
|
|
33
|
-
* bookkeeping follows as private
|
|
34
|
-
*
|
|
35
|
-
*
|
|
39
|
+
* bookkeeping follows as a private field. The slot steps write the fields before anything registers;
|
|
40
|
+
* `to*()` fills the strategy in place and hands this object to the registry once, so a bind is one
|
|
41
|
+
* allocation and one add. A scope or hook afterwards writes the registered object in place.
|
|
36
42
|
*
|
|
37
43
|
* @since 0.5.0-canary.8
|
|
38
44
|
*/
|
|
39
|
-
export declare class BindingChain<Value, Names extends string = string> implements AliasBindingBuilder
|
|
45
|
+
export declare class BindingChain<Value, Names extends string = string> implements AliasBindingBuilder, BindingBuilder<Value>, BindToBuilder<Value, Names>, ConstantBindingBuilder<Value>, ScopedBindingBuilder<Value>, SingletonBindingBuilder<Value>, SingletonLifecycleBuilder<Value>, TransientBindingBuilder<Value> {
|
|
40
46
|
#private;
|
|
41
47
|
kind: BindingKind;
|
|
42
48
|
readonly identifier: BindingIdentifier;
|
|
43
49
|
inFlight: boolean;
|
|
44
50
|
frame: ResolutionFrame | undefined;
|
|
51
|
+
rootContext: ResolutionContext | undefined;
|
|
52
|
+
activationStamp: number;
|
|
45
53
|
instance: unknown;
|
|
54
|
+
scopedCacheKey: BindingIdentifier;
|
|
46
55
|
readonly token: Token<Value, Names> | Constructor<Value>;
|
|
47
56
|
slot: BindingSlot;
|
|
48
57
|
predicate: BindingConstraint | undefined;
|
|
@@ -55,18 +64,20 @@ export declare class BindingChain<Value, Names extends string = string> implemen
|
|
|
55
64
|
activationHook: ActivationHandler<Value> | undefined;
|
|
56
65
|
deactivationHook: DeactivationHandler<Value> | undefined;
|
|
57
66
|
constructor(token: Token<Value, Names> | Constructor<Value>, registration: BindingRegistration);
|
|
58
|
-
to(
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
67
|
+
/** Registers a declared module's bindings in list order, each a chain of its own, handled as `to*()` handles one. */
|
|
68
|
+
static registerDeclared(declarations: ReadonlyArray<DeclaredBinding>, registration: BindingRegistration): void;
|
|
69
|
+
to(type: Constructor<Value>): BindingBuilder<Value>;
|
|
70
|
+
toSelf(): BindingBuilder<Value>;
|
|
71
|
+
toConstantValue(value: Value): ConstantBindingBuilder<Value>;
|
|
72
|
+
toDynamic(factory: (ctx: ResolutionContext) => Value): BindingBuilder<Value>;
|
|
73
|
+
toDynamicAsync(factory: (ctx: ResolutionContext) => Promise<Value>): BindingBuilder<Value>;
|
|
63
74
|
toResolved<const Deps extends ReadonlyArray<InjectableDependency>>(factory: (...args: {
|
|
64
75
|
[K in keyof Deps]: ResolvedDependencyValue<NoInfer<Deps>[K]>;
|
|
65
|
-
}) => Value, deps: Deps): BindingBuilder<Value
|
|
76
|
+
}) => Value, deps: Deps): BindingBuilder<Value>;
|
|
66
77
|
toResolvedAsync<const Deps extends ReadonlyArray<InjectableDependency>>(factory: (...args: {
|
|
67
78
|
[K in keyof Deps]: ResolvedDependencyValue<NoInfer<Deps>[K]>;
|
|
68
|
-
}) => Promise<Value>, deps: Deps): BindingBuilder<Value
|
|
69
|
-
toAlias(target: Token<Value> | Constructor<Value>): AliasBindingBuilder
|
|
79
|
+
}) => Promise<Value>, deps: Deps): BindingBuilder<Value>;
|
|
80
|
+
toAlias(target: Token<Value> | Constructor<Value>): AliasBindingBuilder;
|
|
70
81
|
when(predicate: BindingConstraint): this;
|
|
71
82
|
whenNamed(name: Names): this;
|
|
72
83
|
whenTagged(criterion: BindingTag): this;
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
import { clearBindingFrame, NO_ACTIVATION_STAMP, DEFAULT_BINDING_SLOT, generateBindingId, NO_INSTANCE, withSlotCriterion, } from "#core/binding";
|
|
2
|
+
import { mergingConstraintRequirements } from "#core/constraint-requirement";
|
|
3
|
+
import { slotName } from "#core/tag";
|
|
4
|
+
import { tokenName } from "#core/token";
|
|
5
|
+
import { ChainAlreadyRegisteredError, ChainNotRegisteredError, ManyBindingSlotError, SelfBindingRequiresClassError, } from "#errors";
|
|
6
|
+
import { normalizeToDescriptor } from "#injection/descriptor";
|
|
7
|
+
// ── BindingChain ─────────────────────────────────────────────────────────────────────────────────────────────────────
|
|
8
|
+
/**
|
|
9
|
+
* The one builder behind `bind()` and every `to*()` return type — and the binding it registers.
|
|
10
|
+
*
|
|
11
|
+
* @remarks The binding fields come first, in the order every binding shares, and the chain's own
|
|
12
|
+
* bookkeeping follows as a private field. The slot steps write the fields before anything registers;
|
|
13
|
+
* `to*()` fills the strategy in place and hands this object to the registry once, so a bind is one
|
|
14
|
+
* allocation and one add. A scope or hook afterwards writes the registered object in place.
|
|
15
|
+
*
|
|
16
|
+
* @since 0.5.0-canary.8
|
|
17
|
+
*/
|
|
18
|
+
export class BindingChain {
|
|
19
|
+
// The binding. Every field, every kind, written in this order and nowhere else: one hidden class.
|
|
20
|
+
kind = "constant";
|
|
21
|
+
identifier = generateBindingId();
|
|
22
|
+
inFlight = false;
|
|
23
|
+
frame = undefined;
|
|
24
|
+
rootContext = undefined;
|
|
25
|
+
activationStamp = NO_ACTIVATION_STAMP;
|
|
26
|
+
instance = NO_INSTANCE;
|
|
27
|
+
scopedCacheKey = this.identifier;
|
|
28
|
+
token;
|
|
29
|
+
slot = DEFAULT_BINDING_SLOT;
|
|
30
|
+
predicate = undefined;
|
|
31
|
+
isMany = false;
|
|
32
|
+
scope = "singleton";
|
|
33
|
+
target = undefined;
|
|
34
|
+
factory = undefined;
|
|
35
|
+
deps = undefined;
|
|
36
|
+
value = undefined;
|
|
37
|
+
activationHook = undefined;
|
|
38
|
+
deactivationHook = undefined;
|
|
39
|
+
// The chain. Set by the first `to*()`, which is the only one a chain accepts.
|
|
40
|
+
#isRegistered = false;
|
|
41
|
+
#registration;
|
|
42
|
+
constructor(token, registration) {
|
|
43
|
+
this.token = token;
|
|
44
|
+
this.#registration = registration;
|
|
45
|
+
}
|
|
46
|
+
/** This object as the registry and the resolver see it: the value type erased once, here. */
|
|
47
|
+
get #binding() {
|
|
48
|
+
return this;
|
|
49
|
+
}
|
|
50
|
+
/** Loud failure for a scope, a hook or `id()` before `to*()`. */
|
|
51
|
+
#requireRegistered() {
|
|
52
|
+
if (!this.#isRegistered) {
|
|
53
|
+
throw new ChainNotRegisteredError(tokenName(this.token));
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/** Loud failure for a slot step or a second `to*()` once registered, raised before a field is overwritten. */
|
|
57
|
+
#requireUnregistered() {
|
|
58
|
+
if (this.#isRegistered) {
|
|
59
|
+
throw new ChainAlreadyRegisteredError(tokenName(this.token));
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
#register(kind, scope) {
|
|
63
|
+
this.kind = kind;
|
|
64
|
+
this.scope = scope;
|
|
65
|
+
this.#isRegistered = true;
|
|
66
|
+
const registration = this.#registration;
|
|
67
|
+
const displaced = registration.registry.add(this.#binding);
|
|
68
|
+
if (displaced !== undefined) {
|
|
69
|
+
if (registration.deactivateDisplaced !== undefined) {
|
|
70
|
+
registration.deactivateDisplaced(displaced);
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
registration.onDisplaced?.(displaced);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
if (registration.moduleBindingIds !== undefined) {
|
|
77
|
+
registration.moduleBindingIds.push(this.identifier);
|
|
78
|
+
}
|
|
79
|
+
return this;
|
|
80
|
+
}
|
|
81
|
+
/** Registers a declared module's bindings in list order, each a chain of its own, handled as `to*()` handles one. */
|
|
82
|
+
static registerDeclared(declarations, registration) {
|
|
83
|
+
const { registry, moduleBindingIds, deactivateDisplaced, onDisplaced } = registration;
|
|
84
|
+
for (let index = 0; index < declarations.length; index += 1) {
|
|
85
|
+
const declaration = declarations[index];
|
|
86
|
+
const binding = BindingChain.#fromDeclaration(declaration, registration);
|
|
87
|
+
const displaced = registry.add(binding);
|
|
88
|
+
if (displaced !== undefined) {
|
|
89
|
+
if (deactivateDisplaced === undefined) {
|
|
90
|
+
onDisplaced?.(displaced);
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
deactivateDisplaced(displaced);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
moduleBindingIds?.push(binding.identifier);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* A registered chain in the shape a declaration's steps would have left.
|
|
101
|
+
*
|
|
102
|
+
* @remarks Typed `unknown` like the declaration it copies, so the value type is never asserted here.
|
|
103
|
+
*/
|
|
104
|
+
static #fromDeclaration(declaration, registration) {
|
|
105
|
+
const chain = new BindingChain(declaration.token, registration);
|
|
106
|
+
chain.kind = declaration.kind;
|
|
107
|
+
chain.slot = declaration.slot;
|
|
108
|
+
chain.predicate = declaration.predicate;
|
|
109
|
+
chain.isMany = declaration.isMany;
|
|
110
|
+
chain.scope = declaration.scope;
|
|
111
|
+
chain.target = declaration.target;
|
|
112
|
+
chain.factory = declaration.factory;
|
|
113
|
+
chain.deps = declaration.deps;
|
|
114
|
+
chain.value = declaration.value;
|
|
115
|
+
chain.activationHook = declaration.activationHook;
|
|
116
|
+
chain.deactivationHook = declaration.deactivationHook;
|
|
117
|
+
chain.#isRegistered = true;
|
|
118
|
+
return chain.#binding;
|
|
119
|
+
}
|
|
120
|
+
// ── Registration ───────────────────────────────────────────────────────────────────────────────────────────────────
|
|
121
|
+
to(type) {
|
|
122
|
+
this.#requireUnregistered();
|
|
123
|
+
this.target = type;
|
|
124
|
+
return this.#register("class", "transient");
|
|
125
|
+
}
|
|
126
|
+
toSelf() {
|
|
127
|
+
this.#requireUnregistered();
|
|
128
|
+
if (typeof this.token !== "function") {
|
|
129
|
+
throw new SelfBindingRequiresClassError(tokenName(this.token));
|
|
130
|
+
}
|
|
131
|
+
this.target = this.token;
|
|
132
|
+
return this.#register("class", "transient");
|
|
133
|
+
}
|
|
134
|
+
toConstantValue(value) {
|
|
135
|
+
this.#requireUnregistered();
|
|
136
|
+
this.value = value;
|
|
137
|
+
return this.#register("constant", "singleton");
|
|
138
|
+
}
|
|
139
|
+
toDynamic(factory) {
|
|
140
|
+
this.#requireUnregistered();
|
|
141
|
+
this.factory = factory;
|
|
142
|
+
return this.#register("dynamic", "transient");
|
|
143
|
+
}
|
|
144
|
+
toDynamicAsync(factory) {
|
|
145
|
+
this.#requireUnregistered();
|
|
146
|
+
this.factory = factory;
|
|
147
|
+
return this.#register("dynamic-async", "transient");
|
|
148
|
+
}
|
|
149
|
+
toResolved(factory, deps) {
|
|
150
|
+
this.#requireUnregistered();
|
|
151
|
+
this.factory = factory;
|
|
152
|
+
this.deps = deps.map((dependency) => normalizeToDescriptor(dependency));
|
|
153
|
+
return this.#register("resolved", "transient");
|
|
154
|
+
}
|
|
155
|
+
toResolvedAsync(factory, deps) {
|
|
156
|
+
this.#requireUnregistered();
|
|
157
|
+
this.factory = factory;
|
|
158
|
+
this.deps = deps.map((dependency) => normalizeToDescriptor(dependency));
|
|
159
|
+
return this.#register("resolved-async", "transient");
|
|
160
|
+
}
|
|
161
|
+
toAlias(target) {
|
|
162
|
+
this.#requireUnregistered();
|
|
163
|
+
this.target = target;
|
|
164
|
+
return this.#register("alias", "transient");
|
|
165
|
+
}
|
|
166
|
+
// ── Slot ───────────────────────────────────────────────────────────────────────────────────────────────────────────
|
|
167
|
+
// SPEC calls a candidate a binding that passes *all* of a chain's predicates, so a second `when()`
|
|
168
|
+
// narrows rather than replaces.
|
|
169
|
+
when(predicate) {
|
|
170
|
+
this.#requireUnregistered();
|
|
171
|
+
const previous = this.predicate;
|
|
172
|
+
// The composite carries both sides' requirements, so validate() still sees them.
|
|
173
|
+
this.predicate =
|
|
174
|
+
previous === undefined
|
|
175
|
+
? predicate
|
|
176
|
+
: mergingConstraintRequirements((ctx) => previous(ctx) && predicate(ctx), previous, predicate);
|
|
177
|
+
return this;
|
|
178
|
+
}
|
|
179
|
+
whenNamed(name) {
|
|
180
|
+
return this.whenTagged(slotName.of(name));
|
|
181
|
+
}
|
|
182
|
+
whenTagged(criterion) {
|
|
183
|
+
this.#requireUnregistered();
|
|
184
|
+
if (this.isMany) {
|
|
185
|
+
throw new ManyBindingSlotError(tokenName(this.token));
|
|
186
|
+
}
|
|
187
|
+
this.slot = withSlotCriterion(this.slot, criterion);
|
|
188
|
+
return this;
|
|
189
|
+
}
|
|
190
|
+
many() {
|
|
191
|
+
this.#requireUnregistered();
|
|
192
|
+
if (this.slot.tags.length !== 0) {
|
|
193
|
+
throw new ManyBindingSlotError(tokenName(this.token));
|
|
194
|
+
}
|
|
195
|
+
this.isMany = true;
|
|
196
|
+
return this;
|
|
197
|
+
}
|
|
198
|
+
whenDefault() {
|
|
199
|
+
// The default slot is what a fresh chain already has; the check keeps it a slot step like the others.
|
|
200
|
+
this.#requireUnregistered();
|
|
201
|
+
return this;
|
|
202
|
+
}
|
|
203
|
+
// ── Scope and lifecycle ────────────────────────────────────────────────────────────────────────────────────────────
|
|
204
|
+
#withScope(scope) {
|
|
205
|
+
this.#requireRegistered();
|
|
206
|
+
// Nothing a cache, a frame or the version answers for depends on a scope that did not change.
|
|
207
|
+
if (this.scope === scope) {
|
|
208
|
+
return this;
|
|
209
|
+
}
|
|
210
|
+
// An instance cached under the old scope must not survive the change — a later flip back to that
|
|
211
|
+
// scope would resurrect it. A child's scoped cache is out of reach here, so leaving `scoped` retires
|
|
212
|
+
// the key every child filed its instance under.
|
|
213
|
+
const registration = this.#registration;
|
|
214
|
+
if (this.instance !== NO_INSTANCE) {
|
|
215
|
+
registration.scope.deleteSingleton(this.#binding);
|
|
216
|
+
}
|
|
217
|
+
if (this.scope === "scoped") {
|
|
218
|
+
registration.scope.deleteScoped(this.scopedCacheKey);
|
|
219
|
+
this.scopedCacheKey = generateBindingId();
|
|
220
|
+
}
|
|
221
|
+
this.scope = scope;
|
|
222
|
+
// The frame reports the scope, so a resolve before this call memoized the previous one.
|
|
223
|
+
if (this.frame !== undefined || this.rootContext !== undefined) {
|
|
224
|
+
clearBindingFrame(this.#binding);
|
|
225
|
+
}
|
|
226
|
+
registration.registry.touch();
|
|
227
|
+
return this;
|
|
228
|
+
}
|
|
229
|
+
singleton() {
|
|
230
|
+
return this.#withScope("singleton");
|
|
231
|
+
}
|
|
232
|
+
transient() {
|
|
233
|
+
return this.#withScope("transient");
|
|
234
|
+
}
|
|
235
|
+
scoped() {
|
|
236
|
+
return this.#withScope("scoped");
|
|
237
|
+
}
|
|
238
|
+
onActivation(fn) {
|
|
239
|
+
this.#requireRegistered();
|
|
240
|
+
this.activationHook = fn;
|
|
241
|
+
this.#registration.registry.touch();
|
|
242
|
+
return this;
|
|
243
|
+
}
|
|
244
|
+
onDeactivation(fn) {
|
|
245
|
+
this.#requireRegistered();
|
|
246
|
+
this.deactivationHook = fn;
|
|
247
|
+
this.#registration.registry.touch();
|
|
248
|
+
return this;
|
|
249
|
+
}
|
|
250
|
+
id() {
|
|
251
|
+
this.#requireRegistered();
|
|
252
|
+
return this.identifier;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import type { BindToBuilder } from "#core/binding";
|
|
1
|
+
import type { BindToBuilder } from "#core/binding-builders";
|
|
2
2
|
import type { AsyncModule, SyncModule } from "#core/module";
|
|
3
3
|
import type { Token } from "#core/token";
|
|
4
4
|
import type { ActivationHandler, BindingIdentifier, Constructor, DeactivationHandler, ResolveOptions } from "#core/types";
|
|
5
5
|
import type { AutoRegisterRegistry } from "#decorators/injectable";
|
|
6
6
|
import type { ContainerGraphJson, GraphOptions } from "#introspection/dependency-graph";
|
|
7
|
+
import type { ExplainOptions, ResolutionExplanation } from "#introspection/explanation";
|
|
7
8
|
import type { BindingSnapshot, ContainerSnapshot } from "#introspection/inspector";
|
|
8
|
-
import type { MetadataReader } from "#metadata/
|
|
9
|
+
import type { MetadataReader } from "#metadata/types";
|
|
9
10
|
/**
|
|
10
11
|
* The public surface: binding, resolving, modules, lifecycle hooks, child scopes, and disposal.
|
|
11
12
|
*
|
|
@@ -42,6 +43,14 @@ export interface Container {
|
|
|
42
43
|
hasOwn<Names extends string = string>(token: Token<unknown, Names> | Constructor, options?: NoInfer<ResolveOptions<Names>>): boolean;
|
|
43
44
|
lookupBindings<Value>(token: Token<Value> | Constructor<Value>): ReadonlyArray<BindingSnapshot>;
|
|
44
45
|
inspect(): ContainerSnapshot;
|
|
46
|
+
/**
|
|
47
|
+
* Explains why a request selects the binding it does, reading each registry the way `resolve` does and deciding
|
|
48
|
+
* by the same rules, without instantiating anything.
|
|
49
|
+
*
|
|
50
|
+
* @remarks A `when()` predicate runs here as it would in `resolve`. `options.ancestors` names the resolutions the
|
|
51
|
+
* request is nested in, so a predicate that reads the parent sees the one it would.
|
|
52
|
+
*/
|
|
53
|
+
explain<Names extends string = string>(token: Token<unknown, Names> | Constructor, options?: NoInfer<ExplainOptions<Names>>): ResolutionExplanation;
|
|
45
54
|
generateDependencyGraph(options?: GraphOptions): ContainerGraphJson;
|
|
46
55
|
}
|
|
47
56
|
/**
|