@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
|
@@ -1,305 +0,0 @@
|
|
|
1
|
-
import { clearBindingFrame, createBindingSlot, DEFAULT_BINDING_SLOT, generateBindingId, NO_INSTANCE, } 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/errors";
|
|
6
|
-
import { normalizeToDescriptor } from "#injection/descriptor";
|
|
7
|
-
/** One criterion per key: re-tagging the same key replaces it rather than asking for both values. */
|
|
8
|
-
function updateSlotTag(slot, criterion) {
|
|
9
|
-
const tags = [...slot.tags];
|
|
10
|
-
const existingIndex = tags.findIndex((existing) => existing.key === criterion.key);
|
|
11
|
-
if (existingIndex === -1) {
|
|
12
|
-
tags.push(criterion);
|
|
13
|
-
}
|
|
14
|
-
else {
|
|
15
|
-
tags[existingIndex] = criterion;
|
|
16
|
-
}
|
|
17
|
-
return createBindingSlot(tags);
|
|
18
|
-
}
|
|
19
|
-
// ── BindingChain ─────────────────────────────────────────────────────────────────────────────────────────────────────
|
|
20
|
-
/**
|
|
21
|
-
* The one builder behind `bind()` and every `to*()` return type — and the binding it registers.
|
|
22
|
-
*
|
|
23
|
-
* @remarks The binding fields come first, in the order every binding shares, and the chain's own
|
|
24
|
-
* bookkeeping follows as private fields; `to*()` fills the fields in place and hands this object to
|
|
25
|
-
* the registry, so a plain bind is one allocation. Refinements write the registered object: a scope
|
|
26
|
-
* or hook in place, a slot or predicate through the registry so its indexes follow.
|
|
27
|
-
*
|
|
28
|
-
* @since 0.5.0-canary.8
|
|
29
|
-
*/
|
|
30
|
-
export class BindingChain {
|
|
31
|
-
// The binding. Every field, every kind, written in this order and nowhere else: one hidden class.
|
|
32
|
-
kind = "constant";
|
|
33
|
-
identifier = generateBindingId();
|
|
34
|
-
inFlight = false;
|
|
35
|
-
frame = undefined;
|
|
36
|
-
instance = NO_INSTANCE;
|
|
37
|
-
token;
|
|
38
|
-
slot = DEFAULT_BINDING_SLOT;
|
|
39
|
-
predicate = undefined;
|
|
40
|
-
isMany = false;
|
|
41
|
-
scope = "singleton";
|
|
42
|
-
target = undefined;
|
|
43
|
-
factory = undefined;
|
|
44
|
-
deps = undefined;
|
|
45
|
-
value = undefined;
|
|
46
|
-
activationHook = undefined;
|
|
47
|
-
deactivationHook = undefined;
|
|
48
|
-
// The chain. Set by the first `to*()`, which is the only one a chain accepts.
|
|
49
|
-
#isRegistered = false;
|
|
50
|
-
// Allocated only by a chain that actually displaces something — most never do.
|
|
51
|
-
#displacedByChain;
|
|
52
|
-
// Registry version after this chain's last write — a mismatch means someone else wrote in between.
|
|
53
|
-
#versionAfterLastWrite = -1;
|
|
54
|
-
#registration;
|
|
55
|
-
constructor(token, registration) {
|
|
56
|
-
this.token = token;
|
|
57
|
-
this.#registration = registration;
|
|
58
|
-
}
|
|
59
|
-
/** This object as the registry and the resolver see it: the value type erased once, here. */
|
|
60
|
-
get #binding() {
|
|
61
|
-
return this;
|
|
62
|
-
}
|
|
63
|
-
/** Loud failure for a refinement before `to*()`. */
|
|
64
|
-
#requireRegistered() {
|
|
65
|
-
if (!this.#isRegistered) {
|
|
66
|
-
throw new ChainNotRegisteredError(tokenName(this.token));
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
/** Loud failure for a second `to*()`, raised before the first one's fields can be overwritten. */
|
|
70
|
-
#requireUnregistered() {
|
|
71
|
-
if (this.#isRegistered) {
|
|
72
|
-
throw new ChainAlreadyRegisteredError(tokenName(this.token));
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
#register(kind, scope) {
|
|
76
|
-
this.kind = kind;
|
|
77
|
-
this.scope = scope;
|
|
78
|
-
this.#isRegistered = true;
|
|
79
|
-
this.#commit(undefined);
|
|
80
|
-
return this;
|
|
81
|
-
}
|
|
82
|
-
// ── Registration ───────────────────────────────────────────────────────────────────────────────────────────────────
|
|
83
|
-
to(type) {
|
|
84
|
-
this.#requireUnregistered();
|
|
85
|
-
this.target = type;
|
|
86
|
-
return this.#register("class", "transient");
|
|
87
|
-
}
|
|
88
|
-
toSelf() {
|
|
89
|
-
this.#requireUnregistered();
|
|
90
|
-
if (typeof this.token !== "function") {
|
|
91
|
-
throw new SelfBindingRequiresClassError(tokenName(this.token));
|
|
92
|
-
}
|
|
93
|
-
this.target = this.token;
|
|
94
|
-
return this.#register("class", "transient");
|
|
95
|
-
}
|
|
96
|
-
toConstantValue(value) {
|
|
97
|
-
this.#requireUnregistered();
|
|
98
|
-
this.value = value;
|
|
99
|
-
return this.#register("constant", "singleton");
|
|
100
|
-
}
|
|
101
|
-
toDynamic(factory) {
|
|
102
|
-
this.#requireUnregistered();
|
|
103
|
-
this.factory = factory;
|
|
104
|
-
return this.#register("dynamic", "transient");
|
|
105
|
-
}
|
|
106
|
-
toDynamicAsync(factory) {
|
|
107
|
-
this.#requireUnregistered();
|
|
108
|
-
this.factory = factory;
|
|
109
|
-
return this.#register("dynamic-async", "transient");
|
|
110
|
-
}
|
|
111
|
-
toResolved(factory, deps) {
|
|
112
|
-
this.#requireUnregistered();
|
|
113
|
-
this.factory = factory;
|
|
114
|
-
this.deps = deps.map((dependency) => normalizeToDescriptor(dependency));
|
|
115
|
-
return this.#register("resolved", "transient");
|
|
116
|
-
}
|
|
117
|
-
toResolvedAsync(factory, deps) {
|
|
118
|
-
this.#requireUnregistered();
|
|
119
|
-
this.factory = factory;
|
|
120
|
-
this.deps = deps.map((dependency) => normalizeToDescriptor(dependency));
|
|
121
|
-
return this.#register("resolved-async", "transient");
|
|
122
|
-
}
|
|
123
|
-
toAlias(target) {
|
|
124
|
-
this.#requireUnregistered();
|
|
125
|
-
this.target = target;
|
|
126
|
-
return this.#register("alias", "transient");
|
|
127
|
-
}
|
|
128
|
-
// ── Refinement ─────────────────────────────────────────────────────────────────────────────────────────────────────
|
|
129
|
-
/**
|
|
130
|
-
* Whether this chain provably still owns the registry's last write with nothing parked.
|
|
131
|
-
*
|
|
132
|
-
* @remarks When it does, a refinement rewrites the live binding in place; otherwise `#reslot`
|
|
133
|
-
* re-checks liveness and restores what the new shape frees.
|
|
134
|
-
*/
|
|
135
|
-
get #isProvablyLive() {
|
|
136
|
-
return this.#registration.registry.version === this.#versionAfterLastWrite && this.#displacedByChain === undefined;
|
|
137
|
-
}
|
|
138
|
-
/** Stamps this chain's last-write version after a mutation it made in place. */
|
|
139
|
-
#recordWrite() {
|
|
140
|
-
this.#versionAfterLastWrite = this.#registration.registry.version;
|
|
141
|
-
}
|
|
142
|
-
/** Bumps the registry for an index-neutral mutation (scope, hook) and records this chain's write. */
|
|
143
|
-
#touchAndRecordWrite() {
|
|
144
|
-
this.#registration.registry.touch();
|
|
145
|
-
this.#recordWrite();
|
|
146
|
-
}
|
|
147
|
-
// Slot and predicate are what the registry indexes on, so a re-slot takes the binding out of the
|
|
148
|
-
// registry, rewrites the two fields while it is out, and registers it again — same object, same id.
|
|
149
|
-
#reslot(slot, predicate) {
|
|
150
|
-
this.#requireRegistered();
|
|
151
|
-
this.#commit(() => {
|
|
152
|
-
this.slot = slot;
|
|
153
|
-
this.predicate = predicate;
|
|
154
|
-
// The frame reports the slot, so a resolve before this call memoized the previous one.
|
|
155
|
-
clearBindingFrame(this.#binding);
|
|
156
|
-
});
|
|
157
|
-
return this;
|
|
158
|
-
}
|
|
159
|
-
#withScope(scope) {
|
|
160
|
-
this.#requireRegistered();
|
|
161
|
-
if (this.scope !== scope) {
|
|
162
|
-
// An instance cached under the old scope must not survive the change — a later flip back
|
|
163
|
-
// to that scope would resurrect it.
|
|
164
|
-
this.#registration.scope.deleteSingleton(this.#binding);
|
|
165
|
-
this.#registration.scope.deleteScoped(this.identifier);
|
|
166
|
-
this.scope = scope;
|
|
167
|
-
}
|
|
168
|
-
// The frame reports the scope, so a resolve before this call memoized the previous one.
|
|
169
|
-
clearBindingFrame(this.#binding);
|
|
170
|
-
this.#touchAndRecordWrite();
|
|
171
|
-
return this;
|
|
172
|
-
}
|
|
173
|
-
// SPEC calls a candidate a binding that passes *all* of a chain's predicates, and the chain type
|
|
174
|
-
// reads as refinement, so a second `when()` narrows rather than replaces.
|
|
175
|
-
when(predicate) {
|
|
176
|
-
this.#requireRegistered();
|
|
177
|
-
const previous = this.predicate;
|
|
178
|
-
// The composite carries both sides' requirements, so validate() still sees them.
|
|
179
|
-
const narrowed = previous === undefined
|
|
180
|
-
? predicate
|
|
181
|
-
: mergingConstraintRequirements((ctx) => previous(ctx) && predicate(ctx), previous, predicate);
|
|
182
|
-
// The slot is unchanged, so a provably-live binding just takes the predicate in place.
|
|
183
|
-
if (this.#isProvablyLive) {
|
|
184
|
-
this.#registration.registry.setPredicate(this.#binding, narrowed);
|
|
185
|
-
this.#recordWrite();
|
|
186
|
-
return this;
|
|
187
|
-
}
|
|
188
|
-
return this.#reslot(this.slot, narrowed);
|
|
189
|
-
}
|
|
190
|
-
whenNamed(name) {
|
|
191
|
-
return this.whenTagged(slotName.of(name));
|
|
192
|
-
}
|
|
193
|
-
whenTagged(criterion) {
|
|
194
|
-
this.#requireRegistered();
|
|
195
|
-
if (this.isMany) {
|
|
196
|
-
throw new ManyBindingSlotError(tokenName(this.token));
|
|
197
|
-
}
|
|
198
|
-
return this.#reslot(updateSlotTag(this.slot, criterion), this.predicate);
|
|
199
|
-
}
|
|
200
|
-
many() {
|
|
201
|
-
this.#requireRegistered();
|
|
202
|
-
if (this.slot.tags.length !== 0) {
|
|
203
|
-
throw new ManyBindingSlotError(tokenName(this.token));
|
|
204
|
-
}
|
|
205
|
-
if (this.isMany) {
|
|
206
|
-
return this;
|
|
207
|
-
}
|
|
208
|
-
// A provably-live binding takes membership in place; the registry only moves it out of the lone
|
|
209
|
-
// map. Otherwise `#commit` re-checks liveness and restores what the freed slot lets back in.
|
|
210
|
-
if (this.#isProvablyLive) {
|
|
211
|
-
this.#registration.registry.setMany(this.#binding);
|
|
212
|
-
this.#recordWrite();
|
|
213
|
-
return this;
|
|
214
|
-
}
|
|
215
|
-
this.#commit(() => {
|
|
216
|
-
this.isMany = true;
|
|
217
|
-
});
|
|
218
|
-
return this;
|
|
219
|
-
}
|
|
220
|
-
whenDefault() {
|
|
221
|
-
// The default slot is what a fresh registration already has, so there is nothing to re-slot —
|
|
222
|
-
// but an unregistered chain must fail here exactly as it does in every other refinement.
|
|
223
|
-
this.#requireRegistered();
|
|
224
|
-
return this;
|
|
225
|
-
}
|
|
226
|
-
singleton() {
|
|
227
|
-
return this.#withScope("singleton");
|
|
228
|
-
}
|
|
229
|
-
transient() {
|
|
230
|
-
return this.#withScope("transient");
|
|
231
|
-
}
|
|
232
|
-
scoped() {
|
|
233
|
-
return this.#withScope("scoped");
|
|
234
|
-
}
|
|
235
|
-
onActivation(fn) {
|
|
236
|
-
this.#requireRegistered();
|
|
237
|
-
this.activationHook = fn;
|
|
238
|
-
this.#touchAndRecordWrite();
|
|
239
|
-
return this;
|
|
240
|
-
}
|
|
241
|
-
onDeactivation(fn) {
|
|
242
|
-
this.#requireRegistered();
|
|
243
|
-
this.deactivationHook = fn;
|
|
244
|
-
this.#touchAndRecordWrite();
|
|
245
|
-
return this;
|
|
246
|
-
}
|
|
247
|
-
id() {
|
|
248
|
-
this.#requireRegistered();
|
|
249
|
-
return this.identifier;
|
|
250
|
-
}
|
|
251
|
-
// ── Registry ───────────────────────────────────────────────────────────────────────────────────────────────────────
|
|
252
|
-
/**
|
|
253
|
-
* Registers this binding; with `rewrite` given, first takes the live binding out, rewrites it, and
|
|
254
|
-
* registers it again.
|
|
255
|
-
*
|
|
256
|
-
* @remarks A `when*()` that follows `to*()` re-slots an already-live binding and can displace one
|
|
257
|
-
* the final shape would never conflict with. Those stay parked in `#displacedByChain` until the
|
|
258
|
-
* chain settles, then get restored.
|
|
259
|
-
*/
|
|
260
|
-
#commit(rewrite) {
|
|
261
|
-
const { registry, moduleBindingIds } = this.#registration;
|
|
262
|
-
const registered = this.#binding;
|
|
263
|
-
if (rewrite !== undefined) {
|
|
264
|
-
// A registry someone else wrote since this chain's last write invalidates the parked
|
|
265
|
-
// snapshot: restoring it could undo an unbind or shadow a newer binding.
|
|
266
|
-
if (registry.version !== this.#versionAfterLastWrite) {
|
|
267
|
-
this.#displacedByChain = undefined;
|
|
268
|
-
}
|
|
269
|
-
if (!registry.reslot(registered, rewrite)) {
|
|
270
|
-
// The chain's binding is no longer live (unbound or displaced) — a refinement must not
|
|
271
|
-
// resurrect it, so the chain goes inert against the registry.
|
|
272
|
-
this.#displacedByChain = undefined;
|
|
273
|
-
this.#versionAfterLastWrite = registry.version;
|
|
274
|
-
return;
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
else {
|
|
278
|
-
// Each `to*()` starts its own registration, so anything a previous one displaced is not this
|
|
279
|
-
// registration's to restore.
|
|
280
|
-
this.#displacedByChain = undefined;
|
|
281
|
-
}
|
|
282
|
-
const displaced = registry.add(registered);
|
|
283
|
-
if (displaced !== undefined) {
|
|
284
|
-
(this.#displacedByChain ??= []).push(displaced);
|
|
285
|
-
}
|
|
286
|
-
if (rewrite !== undefined && this.#displacedByChain !== undefined) {
|
|
287
|
-
this.#restoreNonConflicting(this.#displacedByChain);
|
|
288
|
-
}
|
|
289
|
-
if (rewrite === undefined && moduleBindingIds !== undefined) {
|
|
290
|
-
moduleBindingIds.push(this.identifier);
|
|
291
|
-
}
|
|
292
|
-
this.#versionAfterLastWrite = registry.version;
|
|
293
|
-
}
|
|
294
|
-
#restoreNonConflicting(displaced) {
|
|
295
|
-
for (let index = displaced.length - 1; index >= 0; index -= 1) {
|
|
296
|
-
const candidate = displaced[index];
|
|
297
|
-
// A restore must never displace: a slot that has been re-occupied keeps its occupant, and
|
|
298
|
-
// the candidate stays parked.
|
|
299
|
-
if (!this.#registration.registry.hasSlotOccupant(candidate)) {
|
|
300
|
-
this.#registration.registry.add(candidate);
|
|
301
|
-
displaced.splice(index, 1);
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import type { Binding } from "#core/binding";
|
|
2
|
-
import type { BindingScope } from "#core/types";
|
|
3
|
-
/**
|
|
4
|
-
* The scope a binding resolves under.
|
|
5
|
-
*
|
|
6
|
-
* @remarks Every kind declares one — an alias declares `transient`, since it defers scoping to the
|
|
7
|
-
* binding it points at — so this is a field read, kept as a named function because it is the
|
|
8
|
-
* vocabulary validation and introspection speak.
|
|
9
|
-
*
|
|
10
|
-
* @since 0.3.16-canary.0
|
|
11
|
-
*/
|
|
12
|
-
export declare function effectiveBindingScope(binding: Binding): BindingScope;
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The scope a binding resolves under.
|
|
3
|
-
*
|
|
4
|
-
* @remarks Every kind declares one — an alias declares `transient`, since it defers scoping to the
|
|
5
|
-
* binding it points at — so this is a field read, kept as a named function because it is the
|
|
6
|
-
* vocabulary validation and introspection speak.
|
|
7
|
-
*
|
|
8
|
-
* @since 0.3.16-canary.0
|
|
9
|
-
*/
|
|
10
|
-
export function effectiveBindingScope(binding) {
|
|
11
|
-
return binding.scope;
|
|
12
|
-
}
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Wraps a foreign {@link MetadataReader} so its answers are verified before anything dereferences
|
|
3
|
-
* them.
|
|
4
|
-
*/
|
|
5
|
-
import { defaultMetadataReader } from "#metadata/symbol-metadata-reader";
|
|
6
|
-
import { verifyAccessorMetadata, verifyConstructorMetadata, verifyLifecycleMetadata, } from "#resolution/cache/class-introspector";
|
|
7
|
-
// Wrapping a wrapper would stack a layer per child container, so each one is remembered.
|
|
8
|
-
const verifyingReaders = new WeakSet();
|
|
9
|
-
/**
|
|
10
|
-
* The reader a container should hand its resolver: verified when it came from outside.
|
|
11
|
-
*
|
|
12
|
-
* @remarks The decorator reader writes the metadata it later reads, so there is nothing to check and
|
|
13
|
-
* nothing to pay — a container that supplies no reader of its own is left on the same code path it
|
|
14
|
-
* has always taken. A supplied reader is a claim, and only its callers can be charged for checking.
|
|
15
|
-
*
|
|
16
|
-
* @since 0.6.0
|
|
17
|
-
*/
|
|
18
|
-
export function verifyingMetadataReader(reader) {
|
|
19
|
-
if (reader === defaultMetadataReader || verifyingReaders.has(reader)) {
|
|
20
|
-
return reader;
|
|
21
|
-
}
|
|
22
|
-
const verifying = {
|
|
23
|
-
getConstructorMetadata: (target) => verifyConstructorMetadata(reader, target),
|
|
24
|
-
getLifecycleMetadata: (target) => verifyLifecycleMetadata(reader, target),
|
|
25
|
-
...(reader.getAccessorMetadata === undefined
|
|
26
|
-
? {}
|
|
27
|
-
: { getAccessorMetadata: (target) => verifyAccessorMetadata(reader, target) }),
|
|
28
|
-
};
|
|
29
|
-
verifyingReaders.add(verifying);
|
|
30
|
-
return verifying;
|
|
31
|
-
}
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
import type { BindingIdentifier, ResolutionFrame } from "#core/types";
|
|
2
|
-
/**
|
|
3
|
-
* Where the cycle check switches from a linear frame scan to an attached Set.
|
|
4
|
-
*
|
|
5
|
-
* @remarks Measured rather than guessed: below this depth the linear scan wins, above it the Set does.
|
|
6
|
-
*
|
|
7
|
-
* @since 0.5.0-canary.7
|
|
8
|
-
*/
|
|
9
|
-
export declare const RESOLUTION_SET_THRESHOLD = 32;
|
|
10
|
-
/**
|
|
11
|
-
* The token names along a stack plus the frame that closed the cycle — built only to throw.
|
|
12
|
-
*
|
|
13
|
-
* @since 0.6.0
|
|
14
|
-
*/
|
|
15
|
-
export declare function cycleNamesOf(resolutionStack: ReadonlyArray<ResolutionFrame>, closingName: string): Array<string>;
|
|
16
|
-
/**
|
|
17
|
-
* Marks a level as in-flight on this stack, throwing if its binding is already an ancestor.
|
|
18
|
-
*
|
|
19
|
-
* @remarks The check keys on binding identity — two distinct tokens may share a display name — and
|
|
20
|
-
* the names an error prints are derived from the frames at the throw site. Unmark by popping the
|
|
21
|
-
* stack plus `set?.delete(frame.bindingId)`. Sync only — the async lane never removes an entry, so
|
|
22
|
-
* it extends a branch instead; see {@link extendResolutionBranch}.
|
|
23
|
-
*
|
|
24
|
-
* @returns the membership set once the stack is deep enough to carry one, else `undefined`.
|
|
25
|
-
*
|
|
26
|
-
* @since 0.5.0-canary.7
|
|
27
|
-
*/
|
|
28
|
-
export declare function enterResolutionPath(resolutionStack: Array<ResolutionFrame>, frame: ResolutionFrame): Set<BindingIdentifier> | undefined;
|
|
29
|
-
declare const BRANCH_BRAND: unique symbol;
|
|
30
|
-
/**
|
|
31
|
-
* A resolution stack one async branch owns, so appending to it cannot disturb another branch.
|
|
32
|
-
*
|
|
33
|
-
* @remarks Only {@link extendResolutionBranch} mints one. That is what makes "may this lane append
|
|
34
|
-
* to this array" a question the compiler answers instead of a rule in a doc — a sync frame's stack,
|
|
35
|
-
* which that frame will pop, is a plain `Array<ResolutionFrame>` and cannot reach a level that owns
|
|
36
|
-
* its own.
|
|
37
|
-
*
|
|
38
|
-
* @since 0.5.0-canary.9
|
|
39
|
-
*/
|
|
40
|
-
export type OwnedBranchStack = Array<ResolutionFrame> & {
|
|
41
|
-
readonly [BRANCH_BRAND]: true;
|
|
42
|
-
};
|
|
43
|
-
declare const BRANCH_DEPTH_BRAND: unique symbol;
|
|
44
|
-
/**
|
|
45
|
-
* How many leading entries of a stack belong to one async branch.
|
|
46
|
-
*
|
|
47
|
-
* @remarks Branded so a bare number cannot be passed: a depth from anywhere but this branch silently
|
|
48
|
-
* re-parents a level.
|
|
49
|
-
*
|
|
50
|
-
* @since 0.5.0-canary.9
|
|
51
|
-
*/
|
|
52
|
-
export type OwnedBranchDepth = number & {
|
|
53
|
-
readonly [BRANCH_DEPTH_BRAND]: true;
|
|
54
|
-
};
|
|
55
|
-
/**
|
|
56
|
-
* A stack no async branch owns yet, so its first extension must copy rather than append.
|
|
57
|
-
*
|
|
58
|
-
* @since 0.5.0-canary.9
|
|
59
|
-
*/
|
|
60
|
-
export declare const UNOWNED_BRANCH = -1;
|
|
61
|
-
/**
|
|
62
|
-
* How far into a stack one extension may reach: this branch's own depth, or nobody's.
|
|
63
|
-
*
|
|
64
|
-
* @remarks A union rather than a sentinel hidden inside the branded number, so the two cases are
|
|
65
|
-
* visible at every signature that takes one and `=== UNOWNED_BRANCH` narrows to the owned case.
|
|
66
|
-
*
|
|
67
|
-
* @since 0.5.0-canary.9
|
|
68
|
-
*/
|
|
69
|
-
export type BranchDepth = OwnedBranchDepth | typeof UNOWNED_BRANCH;
|
|
70
|
-
/**
|
|
71
|
-
* The depth a chain's first level extends from, over an array its caller just minted.
|
|
72
|
-
*
|
|
73
|
-
* @since 0.5.0-canary.9
|
|
74
|
-
*/
|
|
75
|
-
export declare const ROOT_BRANCH: OwnedBranchDepth;
|
|
76
|
-
/**
|
|
77
|
-
* A branch's own depth: the length its stack had when this level took it.
|
|
78
|
-
*
|
|
79
|
-
* @since 0.5.0-canary.9
|
|
80
|
-
*/
|
|
81
|
-
export declare function branchDepthOf(branch: OwnedBranchStack): OwnedBranchDepth;
|
|
82
|
-
/**
|
|
83
|
-
* Extends one branch of an append-only stack, throwing if the binding is already an ancestor.
|
|
84
|
-
*
|
|
85
|
-
* @remarks The check compares binding ids on the frames; the names an error prints are derived at
|
|
86
|
-
* the throw site. Appends in place while this branch still owns the next slot, and copies its own
|
|
87
|
-
* prefix once a sibling has claimed it. Nothing is ever removed, so no async level has to observe
|
|
88
|
-
* its own settlement to unwind.
|
|
89
|
-
*
|
|
90
|
-
* @since 0.5.0-canary.9
|
|
91
|
-
*/
|
|
92
|
-
export declare function extendResolutionBranch(resolutionStack: Array<ResolutionFrame>, branchDepth: BranchDepth, frame: ResolutionFrame): OwnedBranchStack;
|
|
93
|
-
export {};
|
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
/** Cycle-detection bookkeeping carried on the resolution stack itself. */
|
|
2
|
-
import { CircularDependencyError } from "#errors/errors";
|
|
3
|
-
const RESOLUTION_SET_KEY = Symbol("di:resolution-set");
|
|
4
|
-
/**
|
|
5
|
-
* Where the cycle check switches from a linear frame scan to an attached Set.
|
|
6
|
-
*
|
|
7
|
-
* @remarks Measured rather than guessed: below this depth the linear scan wins, above it the Set does.
|
|
8
|
-
*
|
|
9
|
-
* @since 0.5.0-canary.7
|
|
10
|
-
*/
|
|
11
|
-
export const RESOLUTION_SET_THRESHOLD = 32;
|
|
12
|
-
/**
|
|
13
|
-
* The token names along a stack plus the frame that closed the cycle — built only to throw.
|
|
14
|
-
*
|
|
15
|
-
* @since 0.6.0
|
|
16
|
-
*/
|
|
17
|
-
export function cycleNamesOf(resolutionStack, closingName) {
|
|
18
|
-
const names = new Array(resolutionStack.length + 1);
|
|
19
|
-
for (let index = 0; index < resolutionStack.length; index += 1) {
|
|
20
|
-
names[index] = resolutionStack[index].tokenName;
|
|
21
|
-
}
|
|
22
|
-
names[resolutionStack.length] = closingName;
|
|
23
|
-
return names;
|
|
24
|
-
}
|
|
25
|
-
/**
|
|
26
|
-
* Marks a level as in-flight on this stack, throwing if its binding is already an ancestor.
|
|
27
|
-
*
|
|
28
|
-
* @remarks The check keys on binding identity — two distinct tokens may share a display name — and
|
|
29
|
-
* the names an error prints are derived from the frames at the throw site. Unmark by popping the
|
|
30
|
-
* stack plus `set?.delete(frame.bindingId)`. Sync only — the async lane never removes an entry, so
|
|
31
|
-
* it extends a branch instead; see {@link extendResolutionBranch}.
|
|
32
|
-
*
|
|
33
|
-
* @returns the membership set once the stack is deep enough to carry one, else `undefined`.
|
|
34
|
-
*
|
|
35
|
-
* @since 0.5.0-canary.7
|
|
36
|
-
*/
|
|
37
|
-
export function enterResolutionPath(resolutionStack, frame) {
|
|
38
|
-
const stackWithSet = resolutionStack;
|
|
39
|
-
let resolutionSet = stackWithSet[RESOLUTION_SET_KEY];
|
|
40
|
-
// A live set mirrors the stack exactly, so a size that disagrees means it is holding ids of
|
|
41
|
-
// frames that unwound: the ones already on the stack when it attached were handed no set to
|
|
42
|
-
// delete from. Dropped rather than repaired, because the next deep frame rebuilds it.
|
|
43
|
-
if (resolutionSet !== undefined && resolutionSet.size !== resolutionStack.length) {
|
|
44
|
-
resolutionSet = undefined;
|
|
45
|
-
stackWithSet[RESOLUTION_SET_KEY] = undefined;
|
|
46
|
-
}
|
|
47
|
-
if (resolutionSet === undefined && resolutionStack.length >= RESOLUTION_SET_THRESHOLD) {
|
|
48
|
-
resolutionSet = new Set();
|
|
49
|
-
for (let index = 0; index < resolutionStack.length; index += 1) {
|
|
50
|
-
resolutionSet.add(resolutionStack[index].bindingId);
|
|
51
|
-
}
|
|
52
|
-
stackWithSet[RESOLUTION_SET_KEY] = resolutionSet;
|
|
53
|
-
}
|
|
54
|
-
if (resolutionSet === undefined) {
|
|
55
|
-
for (let index = 0; index < resolutionStack.length; index += 1) {
|
|
56
|
-
if (resolutionStack[index].bindingId === frame.bindingId) {
|
|
57
|
-
throw new CircularDependencyError(cycleNamesOf(resolutionStack, frame.tokenName));
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
else if (resolutionSet.has(frame.bindingId)) {
|
|
62
|
-
throw new CircularDependencyError(cycleNamesOf(resolutionStack, frame.tokenName));
|
|
63
|
-
}
|
|
64
|
-
resolutionStack.push(frame);
|
|
65
|
-
resolutionSet?.add(frame.bindingId);
|
|
66
|
-
return resolutionSet;
|
|
67
|
-
}
|
|
68
|
-
/**
|
|
69
|
-
* A stack no async branch owns yet, so its first extension must copy rather than append.
|
|
70
|
-
*
|
|
71
|
-
* @since 0.5.0-canary.9
|
|
72
|
-
*/
|
|
73
|
-
export const UNOWNED_BRANCH = -1;
|
|
74
|
-
/**
|
|
75
|
-
* The depth a chain's first level extends from, over an array its caller just minted.
|
|
76
|
-
*
|
|
77
|
-
* @since 0.5.0-canary.9
|
|
78
|
-
*/
|
|
79
|
-
export const ROOT_BRANCH = 0;
|
|
80
|
-
/**
|
|
81
|
-
* A branch's own depth: the length its stack had when this level took it.
|
|
82
|
-
*
|
|
83
|
-
* @since 0.5.0-canary.9
|
|
84
|
-
*/
|
|
85
|
-
export function branchDepthOf(branch) {
|
|
86
|
-
return branch.length;
|
|
87
|
-
}
|
|
88
|
-
/**
|
|
89
|
-
* Extends one branch of an append-only stack, throwing if the binding is already an ancestor.
|
|
90
|
-
*
|
|
91
|
-
* @remarks The check compares binding ids on the frames; the names an error prints are derived at
|
|
92
|
-
* the throw site. Appends in place while this branch still owns the next slot, and copies its own
|
|
93
|
-
* prefix once a sibling has claimed it. Nothing is ever removed, so no async level has to observe
|
|
94
|
-
* its own settlement to unwind.
|
|
95
|
-
*
|
|
96
|
-
* @since 0.5.0-canary.9
|
|
97
|
-
*/
|
|
98
|
-
export function extendResolutionBranch(resolutionStack, branchDepth, frame) {
|
|
99
|
-
const depth = branchDepth === UNOWNED_BRANCH ? resolutionStack.length : branchDepth;
|
|
100
|
-
for (let index = 0; index < depth; index += 1) {
|
|
101
|
-
if (resolutionStack[index].bindingId === frame.bindingId) {
|
|
102
|
-
throw new CircularDependencyError(cycleNamesOf(resolutionStack.slice(0, depth), frame.tokenName));
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
// An unowned array belongs to a sync frame that will pop it, or carries a membership Set this
|
|
106
|
-
// lane cannot keep true; copying is what makes the branch's own appends safe.
|
|
107
|
-
if (branchDepth === resolutionStack.length) {
|
|
108
|
-
// The sole mint: appending in place needs a depth that came from a branch already owned, or
|
|
109
|
-
// ROOT_BRANCH over an array its caller minted for this chain alone.
|
|
110
|
-
resolutionStack.push(frame);
|
|
111
|
-
return resolutionStack;
|
|
112
|
-
}
|
|
113
|
-
const branch = resolutionStack.slice(0, depth);
|
|
114
|
-
branch.push(frame);
|
|
115
|
-
return branch;
|
|
116
|
-
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|