@codefast/di 0.5.0-canary.7 → 0.5.0-canary.8
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 +68 -0
- package/README.md +3 -1
- package/dist/binding.d.ts +47 -5
- package/dist/binding.d.ts.map +1 -1
- package/dist/binding.js +44 -0
- package/dist/binding.js.map +1 -1
- package/dist/constructor-type.d.ts +4 -5
- package/dist/constructor-type.d.ts.map +1 -1
- package/dist/container/binding-builders.d.ts +32 -11
- package/dist/container/binding-builders.d.ts.map +1 -1
- package/dist/container/binding-builders.js +140 -191
- package/dist/container/binding-builders.js.map +1 -1
- package/dist/container/container.d.ts.map +1 -1
- package/dist/container/container.js +75 -92
- package/dist/container/container.js.map +1 -1
- package/dist/decorators/inject.d.ts +2 -4
- package/dist/decorators/inject.d.ts.map +1 -1
- package/dist/decorators/inject.js.map +1 -1
- package/dist/errors.d.ts +14 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +17 -0
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/introspection/inspector.js +1 -1
- package/dist/introspection/inspector.js.map +1 -1
- package/dist/metadata/metadata-keys.d.ts +3 -6
- package/dist/metadata/metadata-keys.d.ts.map +1 -1
- package/dist/metadata/metadata-keys.js +3 -6
- package/dist/metadata/metadata-keys.js.map +1 -1
- package/dist/registry.d.ts +14 -2
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +35 -45
- package/dist/registry.js.map +1 -1
- package/dist/resolution/activation-need.d.ts +25 -0
- package/dist/resolution/activation-need.d.ts.map +1 -0
- package/dist/resolution/activation-need.js +64 -0
- package/dist/resolution/activation-need.js.map +1 -0
- package/dist/resolution/binding-lookup-cache.d.ts +41 -0
- package/dist/resolution/binding-lookup-cache.d.ts.map +1 -0
- package/dist/resolution/binding-lookup-cache.js +102 -0
- package/dist/resolution/binding-lookup-cache.js.map +1 -0
- package/dist/resolution/binding-select.js +1 -4
- package/dist/resolution/binding-select.js.map +1 -1
- package/dist/resolution/class-introspector.d.ts +27 -0
- package/dist/resolution/class-introspector.d.ts.map +1 -0
- package/dist/resolution/class-introspector.js +60 -0
- package/dist/resolution/class-introspector.js.map +1 -0
- package/dist/resolution/diagnostics.d.ts +41 -0
- package/dist/resolution/diagnostics.d.ts.map +1 -0
- package/dist/resolution/diagnostics.js +18 -0
- package/dist/resolution/diagnostics.js.map +1 -0
- package/dist/resolution/environment.d.ts +22 -1
- package/dist/resolution/environment.d.ts.map +1 -1
- package/dist/resolution/environment.js +27 -1
- package/dist/resolution/environment.js.map +1 -1
- package/dist/resolution/instantiation-plan.d.ts +15 -15
- package/dist/resolution/instantiation-plan.d.ts.map +1 -1
- package/dist/resolution/instantiation-plan.js +68 -48
- package/dist/resolution/instantiation-plan.js.map +1 -1
- package/dist/resolution/lifecycle.d.ts +2 -0
- package/dist/resolution/lifecycle.d.ts.map +1 -1
- package/dist/resolution/lifecycle.js +16 -11
- package/dist/resolution/lifecycle.js.map +1 -1
- package/dist/resolution/resolution-path.d.ts +4 -13
- package/dist/resolution/resolution-path.d.ts.map +1 -1
- package/dist/resolution/resolution-path.js +3 -16
- package/dist/resolution/resolution-path.js.map +1 -1
- package/dist/resolution/resolver.d.ts +7 -2
- package/dist/resolution/resolver.d.ts.map +1 -1
- package/dist/resolution/resolver.js +116 -328
- package/dist/resolution/resolver.js.map +1 -1
- package/dist/resolution/scope.d.ts +7 -15
- package/dist/resolution/scope.d.ts.map +1 -1
- package/dist/resolution/scope.js +48 -44
- package/dist/resolution/scope.js.map +1 -1
- package/package.json +7 -97
- package/src/binding.ts +104 -5
- package/src/constructor-type.ts +4 -5
- package/src/container/binding-builders.ts +180 -283
- package/src/container/container.ts +90 -103
- package/src/decorators/inject.ts +3 -5
- package/src/errors.ts +21 -0
- package/src/index.ts +1 -0
- package/src/introspection/inspector.ts +1 -1
- package/src/metadata/metadata-keys.ts +3 -6
- package/src/registry.ts +38 -59
- package/src/resolution/activation-need.ts +81 -0
- package/src/resolution/binding-lookup-cache.ts +132 -0
- package/src/resolution/binding-select.ts +1 -4
- package/src/resolution/class-introspector.ts +74 -0
- package/src/resolution/diagnostics.ts +43 -0
- package/src/resolution/environment.ts +31 -1
- package/src/resolution/instantiation-plan.ts +113 -61
- package/src/resolution/lifecycle.ts +16 -11
- package/src/resolution/resolution-path.ts +5 -20
- package/src/resolution/resolver.ts +142 -371
- package/src/resolution/scope.ts +50 -49
|
@@ -1,14 +1,14 @@
|
|
|
1
|
+
import { NO_INSTANCE } from "#/binding";
|
|
1
2
|
import { AsyncActivationError, AsyncResolutionError, CircularDependencyError, InternalError, MissingMetadataError, MissingScopeContextError, NoMatchingBindingError, TokenNotBoundError, } from "#/errors";
|
|
3
|
+
import { ActivationNeedCache } from "#/resolution/activation-need";
|
|
4
|
+
import { BindingLookupCache } from "#/resolution/binding-lookup-cache";
|
|
2
5
|
import { selectAllBindings, selectBinding } from "#/resolution/binding-select";
|
|
3
|
-
import {
|
|
6
|
+
import { ClassIntrospector } from "#/resolution/class-introspector";
|
|
7
|
+
import { buildResolutionFrame, DefaultResolutionContext } from "#/resolution/environment";
|
|
4
8
|
import { InstantiationPlanCompiler, PLAN_RETRY } from "#/resolution/instantiation-plan";
|
|
5
9
|
import { enterResolutionPath, exitResolutionPath } from "#/resolution/resolution-path";
|
|
6
10
|
import { injectionSlotToResolveOptions } from "#/resolution/resolve-options";
|
|
7
|
-
import { SINGLETON_MISS } from "#/resolution/scope";
|
|
8
11
|
import { tokenName } from "#/token";
|
|
9
|
-
// Fast-lane alias folding gives up past this many hops and defers to the resolve()
|
|
10
|
-
// loop, whose Set-based traversal detects genuine cycles exactly (no arbitrary cap).
|
|
11
|
-
const ALIAS_HOP_LIMIT = 32;
|
|
12
12
|
const EMPTY_STRING_LIST = [];
|
|
13
13
|
const EMPTY_FRAME_LIST = [];
|
|
14
14
|
const ROOT_CONSTRAINT_CONTEXT = {
|
|
@@ -23,51 +23,10 @@ const ROOT_CONSTRAINT_CONTEXT = {
|
|
|
23
23
|
*/
|
|
24
24
|
export class DependencyResolver {
|
|
25
25
|
#syncResolutionContextPool = [];
|
|
26
|
-
//
|
|
27
|
-
//
|
|
28
|
-
|
|
29
|
-
//
|
|
30
|
-
//
|
|
31
|
-
// Every level of a SEQUENTIAL async chain shares the same resolutionPath and resolutionStack
|
|
32
|
-
// arrays (passed by reference through ctx.resolveAsync), and the context stores references
|
|
33
|
-
// rather than snapshots, so one DefaultResolutionContext can serve the whole chain — the arrays
|
|
34
|
-
// reflect the current state automatically as levels push and pop.
|
|
35
|
-
//
|
|
36
|
-
// #asyncChainCtx: the shared context, created on first use and reset at each new root call.
|
|
37
|
-
// Inner levels of the same chain reuse it with zero setup.
|
|
38
|
-
// #asyncChainCtxPath: identity of the resolutionPath array owning the shared context — same
|
|
39
|
-
// reference means an inner level of that chain, a different one means a concurrent chain
|
|
40
|
-
// (e.g. Promise.all) which gets its own context instead.
|
|
41
|
-
// #asyncChainActiveLevels: active levels of the OWNING chain, so the path pointer is released
|
|
42
|
-
// when the last one settles. Concurrent fallback calls are not counted.
|
|
43
|
-
//
|
|
44
|
-
// The method is NOT declared async: that would allocate an async state machine and an implicit
|
|
45
|
-
// promise per level, where a `.then(settle, settle)` side listener costs neither.
|
|
46
|
-
#asyncChainCtx;
|
|
47
|
-
#asyncChainCtxPath;
|
|
48
|
-
#asyncChainActiveLevels = 0;
|
|
49
|
-
// Settle callback shared by every level of the owning async chain: all of them pop the same
|
|
50
|
-
// resolutionPath and decrement the same counter, so one closure serves the whole chain instead
|
|
51
|
-
// of allocating one per level (the async lane's dominant per-level allocation).
|
|
52
|
-
#asyncChainSettle;
|
|
53
|
-
#classHasPostConstruct = new WeakMap();
|
|
54
|
-
#classNeedsActiveContainer = new WeakMap();
|
|
55
|
-
#classConstructorMetadata = new WeakMap();
|
|
56
|
-
#activationNeedByBindingId = new Map();
|
|
57
|
-
#activationCacheVersion = -1;
|
|
58
|
-
// Options-less lookup memo across the parent chain: token → terminal {binding, owner}
|
|
59
|
-
// (alias hops folded). `null` = token must take the slow lookup path. Invalidated when
|
|
60
|
-
// any registry in the chain mutates (monotonic version sum).
|
|
61
|
-
#defaultLookupByToken = new Map();
|
|
62
|
-
#defaultLookupVersion = -1;
|
|
63
|
-
// Name-only lookup memo (token → name → entry) with the same chain-version
|
|
64
|
-
// invalidation. `null` = shape needs the full selection path.
|
|
65
|
-
#namedLookupByToken = new Map();
|
|
66
|
-
#namedLookupVersion = -1;
|
|
67
|
-
// Compiled transient-class plans (Dagger-style): a pure-static subgraph (class/constant/
|
|
68
|
-
// cached-singleton deps only) compiles once into a nested-constructor closure — cycle
|
|
69
|
-
// checking happens at compile time, so execution skips all per-resolve bookkeeping.
|
|
70
|
-
// `null` = binding is not plannable under the current versions.
|
|
26
|
+
// Pure allocation pool: a chain's own context is threaded through the call, so nothing here
|
|
27
|
+
// identifies a chain.
|
|
28
|
+
#asyncChainContextPool = [];
|
|
29
|
+
// Compiled plans; `null` marks a binding as unplannable under the current cache versions.
|
|
71
30
|
#classPlanByBindingId = new Map();
|
|
72
31
|
#classPlanRegistryVersion = -1;
|
|
73
32
|
#classPlanActivationVersion = -1;
|
|
@@ -75,15 +34,33 @@ export class DependencyResolver {
|
|
|
75
34
|
#scope;
|
|
76
35
|
#lifecycle;
|
|
77
36
|
#metadataReader;
|
|
78
|
-
#container;
|
|
79
37
|
#parent;
|
|
38
|
+
#lookup;
|
|
39
|
+
#classes;
|
|
40
|
+
#activation;
|
|
80
41
|
constructor(registry, scope, lifecycle, metadataReader, container, parent) {
|
|
81
42
|
this.#registry = registry;
|
|
82
43
|
this.#scope = scope;
|
|
83
44
|
this.#lifecycle = lifecycle;
|
|
84
45
|
this.#metadataReader = metadataReader;
|
|
85
|
-
this.#container = container;
|
|
86
46
|
this.#parent = parent;
|
|
47
|
+
this.#lookup = new BindingLookupCache(registry, this, parent === undefined ? undefined : parent.#lookup);
|
|
48
|
+
this.#classes = new ClassIntrospector(metadataReader, container);
|
|
49
|
+
this.#activation = new ActivationNeedCache(lifecycle, this.#classes);
|
|
50
|
+
}
|
|
51
|
+
/** Structural counts for {@link RESOLUTION_DIAGNOSTICS}; see `resolution/diagnostics.ts`. */
|
|
52
|
+
describeCaches() {
|
|
53
|
+
let compiledPlanCount = 0;
|
|
54
|
+
for (const plan of this.#classPlanByBindingId.values()) {
|
|
55
|
+
if (plan !== null) {
|
|
56
|
+
compiledPlanCount += 1;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return {
|
|
60
|
+
asyncContextPoolSize: this.#asyncChainContextPool.length,
|
|
61
|
+
compiledPlanCount,
|
|
62
|
+
syncContextPoolSize: this.#syncResolutionContextPool.length,
|
|
63
|
+
};
|
|
87
64
|
}
|
|
88
65
|
// ── Binding lookup ─────────────────────────────────────────────────────────
|
|
89
66
|
#findBinding(token, options, resolutionPath, resolutionStack) {
|
|
@@ -161,7 +138,7 @@ export class DependencyResolver {
|
|
|
161
138
|
if (fastBinding !== undefined && fastBinding.kind !== "alias") {
|
|
162
139
|
return this.#resolveDefaultEntry(fastBinding, this, resolutionPath, resolutionStack);
|
|
163
140
|
}
|
|
164
|
-
const entry = this.#
|
|
141
|
+
const entry = this.#lookup.defaultEntry(token);
|
|
165
142
|
if (entry === null) {
|
|
166
143
|
return this.resolve(token, undefined, resolutionPath, resolutionStack);
|
|
167
144
|
}
|
|
@@ -191,8 +168,8 @@ export class DependencyResolver {
|
|
|
191
168
|
}
|
|
192
169
|
}
|
|
193
170
|
else if (scope === "singleton") {
|
|
194
|
-
const cachedSingleton =
|
|
195
|
-
if (cachedSingleton !==
|
|
171
|
+
const cachedSingleton = binding.instance;
|
|
172
|
+
if (cachedSingleton !== NO_INSTANCE) {
|
|
196
173
|
return cachedSingleton;
|
|
197
174
|
}
|
|
198
175
|
if (owner !== this) {
|
|
@@ -248,83 +225,8 @@ export class DependencyResolver {
|
|
|
248
225
|
resolutionSet?.delete(tokenDisplayName);
|
|
249
226
|
}
|
|
250
227
|
}
|
|
251
|
-
#chainRegistryVersion() {
|
|
252
|
-
let version = this.#registry.version;
|
|
253
|
-
for (let resolver = this.#parent; resolver !== undefined; resolver = resolver.#parent) {
|
|
254
|
-
version += resolver.#registry.version;
|
|
255
|
-
}
|
|
256
|
-
return version;
|
|
257
|
-
}
|
|
258
|
-
#lookupDefaultEntry(token) {
|
|
259
|
-
const version = this.#chainRegistryVersion();
|
|
260
|
-
if (version !== this.#defaultLookupVersion) {
|
|
261
|
-
this.#defaultLookupByToken.clear();
|
|
262
|
-
this.#defaultLookupVersion = version;
|
|
263
|
-
}
|
|
264
|
-
let entry = this.#defaultLookupByToken.get(token);
|
|
265
|
-
if (entry === undefined) {
|
|
266
|
-
entry = this.#computeDefaultEntry(token);
|
|
267
|
-
this.#defaultLookupByToken.set(token, entry);
|
|
268
|
-
}
|
|
269
|
-
return entry;
|
|
270
|
-
}
|
|
271
|
-
#computeDefaultEntry(token) {
|
|
272
|
-
let current = token;
|
|
273
|
-
for (let hop = 0; hop < ALIAS_HOP_LIMIT; hop += 1) {
|
|
274
|
-
const entry = this.#findDefaultEntryInChain(current);
|
|
275
|
-
if (entry === null) {
|
|
276
|
-
return null;
|
|
277
|
-
}
|
|
278
|
-
if (entry.binding.kind === "alias") {
|
|
279
|
-
current = entry.binding.target;
|
|
280
|
-
continue;
|
|
281
|
-
}
|
|
282
|
-
return entry;
|
|
283
|
-
}
|
|
284
|
-
return null;
|
|
285
|
-
}
|
|
286
|
-
#lookupNamedEntry(token, name) {
|
|
287
|
-
const version = this.#chainRegistryVersion();
|
|
288
|
-
if (version !== this.#namedLookupVersion) {
|
|
289
|
-
this.#namedLookupByToken.clear();
|
|
290
|
-
this.#namedLookupVersion = version;
|
|
291
|
-
}
|
|
292
|
-
// ✓ TS6.0: Map.getOrInsert (ES2025)
|
|
293
|
-
const entriesByName = this.#namedLookupByToken.getOrInsert(token, new Map());
|
|
294
|
-
let entry = entriesByName.get(name);
|
|
295
|
-
if (entry === undefined) {
|
|
296
|
-
entry = this.#findNamedEntryInChain(token, name);
|
|
297
|
-
entriesByName.set(name, entry);
|
|
298
|
-
}
|
|
299
|
-
return entry;
|
|
300
|
-
}
|
|
301
|
-
#findNamedEntryInChain(token, name) {
|
|
302
|
-
const named = this.#registry.getSimpleNamed(token, name);
|
|
303
|
-
if (named !== undefined) {
|
|
304
|
-
// Predicates need a live context; aliases carry options through the full path.
|
|
305
|
-
if (named.predicate !== undefined || named.kind === "alias") {
|
|
306
|
-
return null;
|
|
307
|
-
}
|
|
308
|
-
return { binding: named, owner: this };
|
|
309
|
-
}
|
|
310
|
-
if (this.#registry.has(token)) {
|
|
311
|
-
return null;
|
|
312
|
-
}
|
|
313
|
-
return this.#parent === undefined ? null : this.#parent.#findNamedEntryInChain(token, name);
|
|
314
|
-
}
|
|
315
|
-
#findDefaultEntryInChain(token) {
|
|
316
|
-
const fast = this.#registry.getFastDefault(token);
|
|
317
|
-
if (fast !== undefined) {
|
|
318
|
-
return { binding: fast, owner: this };
|
|
319
|
-
}
|
|
320
|
-
// A level with non-fast bindings (multi-slot / predicate) needs full selection — bail.
|
|
321
|
-
if (this.#registry.has(token)) {
|
|
322
|
-
return null;
|
|
323
|
-
}
|
|
324
|
-
return this.#parent === undefined ? null : this.#parent.#findDefaultEntryInChain(token);
|
|
325
|
-
}
|
|
326
228
|
#getInstantiationPlan(binding) {
|
|
327
|
-
const registryVersion = this.#
|
|
229
|
+
const registryVersion = this.#lookup.chainVersion();
|
|
328
230
|
const activationVersion = this.#lifecycle.activationVersion;
|
|
329
231
|
if (registryVersion !== this.#classPlanRegistryVersion || activationVersion !== this.#classPlanActivationVersion) {
|
|
330
232
|
this.#classPlanByBindingId.clear();
|
|
@@ -346,22 +248,28 @@ export class DependencyResolver {
|
|
|
346
248
|
// Compiler behind #getClassPlan — cold path, so the host indirection costs nothing hot.
|
|
347
249
|
#planCompiler = new InstantiationPlanCompiler({
|
|
348
250
|
hasActivationHandlers: (token) => this.#lifecycle.hasActivationHandlers(token),
|
|
349
|
-
knownPostConstruct: (target) => this.#
|
|
350
|
-
needsActiveContainer: (target) =>
|
|
351
|
-
|
|
352
|
-
if (needsActiveContainer === undefined) {
|
|
353
|
-
const accessorMetadata = this.#metadataReader.getAccessorMetadata?.(target);
|
|
354
|
-
needsActiveContainer = (accessorMetadata?.length ?? 0) > 0;
|
|
355
|
-
this.#classNeedsActiveContainer.set(target, needsActiveContainer);
|
|
356
|
-
}
|
|
357
|
-
return needsActiveContainer;
|
|
358
|
-
},
|
|
359
|
-
getConstructorMetadata: (target) => this.#getConstructorMetadata(target),
|
|
251
|
+
knownPostConstruct: (target) => this.#classes.knownPostConstruct(target),
|
|
252
|
+
needsActiveContainer: (target) => this.#classes.needsActiveContainer(target),
|
|
253
|
+
getConstructorMetadata: (target) => this.#classes.constructorMetadata(target),
|
|
360
254
|
lookupDependencyEntry: (token) => {
|
|
361
|
-
const entry = this.#
|
|
362
|
-
return entry === null ? null : { binding: entry.binding
|
|
255
|
+
const entry = this.#lookup.defaultEntry(token);
|
|
256
|
+
return entry === null ? null : { binding: entry.binding };
|
|
257
|
+
},
|
|
258
|
+
getResolutionFrame: (binding) => this.#getResolutionFrame(binding),
|
|
259
|
+
// Dispatches exactly as #resolveClassDeps does, so an escaped dep is indistinguishable
|
|
260
|
+
// from the same dep on a fully interpreted resolve.
|
|
261
|
+
resolveEscaped: (token, options, arity, resolutionPath, resolutionStack) => {
|
|
262
|
+
if (arity === "all") {
|
|
263
|
+
return this.resolveAll(token, options, resolutionPath, resolutionStack);
|
|
264
|
+
}
|
|
265
|
+
if (arity === "optional") {
|
|
266
|
+
return this.resolveOptional(token, options, resolutionPath, resolutionStack);
|
|
267
|
+
}
|
|
268
|
+
if (options === undefined) {
|
|
269
|
+
return this.resolveFromContext(token, resolutionPath, resolutionStack);
|
|
270
|
+
}
|
|
271
|
+
return this.resolve(token, options, resolutionPath, resolutionStack);
|
|
363
272
|
},
|
|
364
|
-
resolveFallback: (token) => this.resolve(token, undefined, [], []),
|
|
365
273
|
});
|
|
366
274
|
resolve(token, options, resolutionPath, resolutionStack) {
|
|
367
275
|
// Name-only fast lane: memoized lookup, dispatching just the shapes whose
|
|
@@ -370,7 +278,7 @@ export class DependencyResolver {
|
|
|
370
278
|
options.name !== undefined &&
|
|
371
279
|
options.tag === undefined &&
|
|
372
280
|
(options.tags === undefined || options.tags.length === 0)) {
|
|
373
|
-
const namedEntry = this.#
|
|
281
|
+
const namedEntry = this.#lookup.namedEntry(token, options.name);
|
|
374
282
|
if (namedEntry !== null) {
|
|
375
283
|
const namedBinding = namedEntry.binding;
|
|
376
284
|
if (namedBinding.kind === "constant" &&
|
|
@@ -380,9 +288,8 @@ export class DependencyResolver {
|
|
|
380
288
|
}
|
|
381
289
|
const namedScope = namedBinding.scope ?? "transient";
|
|
382
290
|
if (namedScope === "singleton") {
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
return cachedSingleton;
|
|
291
|
+
if (namedBinding.instance !== NO_INSTANCE) {
|
|
292
|
+
return namedBinding.instance;
|
|
386
293
|
}
|
|
387
294
|
}
|
|
388
295
|
// Everything else keeps the full path (context, activation, guards).
|
|
@@ -427,10 +334,8 @@ export class DependencyResolver {
|
|
|
427
334
|
}
|
|
428
335
|
const scope = binding.scope ?? "transient";
|
|
429
336
|
// Singleton cache check
|
|
430
|
-
if (scope === "singleton") {
|
|
431
|
-
|
|
432
|
-
return this.#scope.getSingleton(binding.id);
|
|
433
|
-
}
|
|
337
|
+
if (scope === "singleton" && binding.instance !== NO_INSTANCE) {
|
|
338
|
+
return binding.instance;
|
|
434
339
|
}
|
|
435
340
|
// Scoped cache check
|
|
436
341
|
if (scope === "scoped") {
|
|
@@ -445,7 +350,7 @@ export class DependencyResolver {
|
|
|
445
350
|
const tokenDisplayName = frame.tokenName;
|
|
446
351
|
const resolutionSet = enterResolutionPath(resolutionPath, tokenDisplayName, false);
|
|
447
352
|
resolutionStack.push(frame);
|
|
448
|
-
const needsActivation = this.#needsActivation(binding);
|
|
353
|
+
const needsActivation = this.#activation.needsActivation(binding);
|
|
449
354
|
if (!needsActivation && scope === "transient" && binding.kind === "dynamic") {
|
|
450
355
|
const resolutionCtx = this.#acquireSyncResolutionContext(resolutionPath, resolutionStack, options);
|
|
451
356
|
try {
|
|
@@ -471,13 +376,13 @@ export class DependencyResolver {
|
|
|
471
376
|
? this.#acquireSyncResolutionContext(resolutionPath, resolutionStack, options)
|
|
472
377
|
: undefined;
|
|
473
378
|
const instance = this.#instantiateSync(binding, resolutionCtx, resolutionPath, resolutionStack);
|
|
474
|
-
const shouldActivate = this.#
|
|
379
|
+
const shouldActivate = this.#activation.refreshAfterFirstInstantiation(binding, needsActivation);
|
|
475
380
|
const activated = shouldActivate
|
|
476
381
|
? this.#lifecycle.runActivationSync(resolutionCtx, binding, instance, this.#metadataReader)
|
|
477
382
|
: instance;
|
|
478
383
|
// Cache by scope
|
|
479
384
|
if (scope === "singleton") {
|
|
480
|
-
this.#scope.setSingleton(binding
|
|
385
|
+
this.#scope.setSingleton(binding, activated);
|
|
481
386
|
}
|
|
482
387
|
else if (scope === "scoped") {
|
|
483
388
|
this.#scope.setScoped(binding.id, activated);
|
|
@@ -508,7 +413,7 @@ export class DependencyResolver {
|
|
|
508
413
|
throw new AsyncResolutionError(tokenName(binding.token), tokenName(binding.token));
|
|
509
414
|
case "class": {
|
|
510
415
|
const deps = this.#resolveClassDeps(binding.target, resolutionPath, resolutionStack);
|
|
511
|
-
const instance = this.#
|
|
416
|
+
const instance = this.#classes.instantiate(binding.target, deps);
|
|
512
417
|
return instance;
|
|
513
418
|
}
|
|
514
419
|
case "resolved": {
|
|
@@ -526,7 +431,7 @@ export class DependencyResolver {
|
|
|
526
431
|
}
|
|
527
432
|
}
|
|
528
433
|
#resolveClassDeps(target, resolutionPath, resolutionStack) {
|
|
529
|
-
const meta = this.#
|
|
434
|
+
const meta = this.#classes.constructorMetadata(target);
|
|
530
435
|
if (meta === undefined) {
|
|
531
436
|
if (target.length === 0) {
|
|
532
437
|
return [];
|
|
@@ -620,7 +525,7 @@ export class DependencyResolver {
|
|
|
620
525
|
return resolved;
|
|
621
526
|
}
|
|
622
527
|
// ── Async resolve ──────────────────────────────────────────────────────────
|
|
623
|
-
resolveAsyncFromContext(token, resolutionPath, resolutionStack) {
|
|
528
|
+
resolveAsyncFromContext(token, resolutionPath, resolutionStack, callerContext) {
|
|
624
529
|
// Hot lane: own-registry fast default (async chains resolve sibling dynamic bindings).
|
|
625
530
|
// Fall back to the chain-versioned memo only on miss or alias.
|
|
626
531
|
const fastBinding = this.#registry.getFastDefault(token);
|
|
@@ -630,17 +535,17 @@ export class DependencyResolver {
|
|
|
630
535
|
fastBinding.scope === "transient" &&
|
|
631
536
|
fastBinding.onActivation === undefined &&
|
|
632
537
|
(this.#lifecycle.activationVersion === 0 || !this.#lifecycle.hasActivationHandlers(fastBinding.token))) {
|
|
633
|
-
return this.#resolveTransientDynamicAsyncFromContext(fastBinding, resolutionPath, resolutionStack);
|
|
538
|
+
return this.#resolveTransientDynamicAsyncFromContext(fastBinding, resolutionPath, resolutionStack, callerContext);
|
|
634
539
|
}
|
|
635
|
-
return this.#resolveAsyncDefaultEntry(fastBinding, this, resolutionPath, resolutionStack);
|
|
540
|
+
return this.#resolveAsyncDefaultEntry(fastBinding, this, resolutionPath, resolutionStack, callerContext);
|
|
636
541
|
}
|
|
637
|
-
const entry = this.#
|
|
542
|
+
const entry = this.#lookup.defaultEntry(token);
|
|
638
543
|
if (entry === null) {
|
|
639
544
|
return this.resolveAsync(token, undefined, resolutionPath, resolutionStack);
|
|
640
545
|
}
|
|
641
|
-
return this.#resolveAsyncDefaultEntry(entry.binding, entry.owner, resolutionPath, resolutionStack);
|
|
546
|
+
return this.#resolveAsyncDefaultEntry(entry.binding, entry.owner, resolutionPath, resolutionStack, callerContext);
|
|
642
547
|
}
|
|
643
|
-
#resolveAsyncDefaultEntry(binding, owner, resolutionPath, resolutionStack) {
|
|
548
|
+
#resolveAsyncDefaultEntry(binding, owner, resolutionPath, resolutionStack, callerContext) {
|
|
644
549
|
if (binding.kind === "constant" &&
|
|
645
550
|
binding.onActivation === undefined &&
|
|
646
551
|
(this.#lifecycle.activationVersion === 0 || !this.#lifecycle.hasActivationHandlers(binding.token))) {
|
|
@@ -651,13 +556,12 @@ export class DependencyResolver {
|
|
|
651
556
|
if ((binding.kind === "dynamic" || binding.kind === "dynamic-async") &&
|
|
652
557
|
binding.onActivation === undefined &&
|
|
653
558
|
(this.#lifecycle.activationVersion === 0 || !this.#lifecycle.hasActivationHandlers(binding.token))) {
|
|
654
|
-
return this.#resolveTransientDynamicAsyncFromContext(binding, resolutionPath, resolutionStack);
|
|
559
|
+
return this.#resolveTransientDynamicAsyncFromContext(binding, resolutionPath, resolutionStack, callerContext);
|
|
655
560
|
}
|
|
656
561
|
}
|
|
657
562
|
else if (scope === "singleton") {
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
return Promise.resolve(cachedSingleton);
|
|
563
|
+
if (binding.instance !== NO_INSTANCE) {
|
|
564
|
+
return Promise.resolve(binding.instance);
|
|
661
565
|
}
|
|
662
566
|
if (owner !== this) {
|
|
663
567
|
return owner.#resolveBindingAsync(binding, undefined, resolutionPath, resolutionStack);
|
|
@@ -718,8 +622,8 @@ export class DependencyResolver {
|
|
|
718
622
|
const scope = binding.scope ?? "transient";
|
|
719
623
|
// Singleton cache
|
|
720
624
|
if (scope === "singleton") {
|
|
721
|
-
if (
|
|
722
|
-
return
|
|
625
|
+
if (binding.instance !== NO_INSTANCE) {
|
|
626
|
+
return binding.instance;
|
|
723
627
|
}
|
|
724
628
|
// In-flight dedup
|
|
725
629
|
const inflight = this.#scope.getInflight(binding.id);
|
|
@@ -740,7 +644,7 @@ export class DependencyResolver {
|
|
|
740
644
|
const frameName = frame.tokenName;
|
|
741
645
|
const resolutionSet = enterResolutionPath(resolutionPath, frameName, false);
|
|
742
646
|
resolutionStack.push(frame);
|
|
743
|
-
const needsActivation = this.#needsActivation(binding);
|
|
647
|
+
const needsActivation = this.#activation.needsActivation(binding);
|
|
744
648
|
if (!needsActivation && scope === "transient" && (binding.kind === "dynamic" || binding.kind === "dynamic-async")) {
|
|
745
649
|
const resolutionCtx = new DefaultResolutionContext(this, resolutionPath, resolutionStack, options);
|
|
746
650
|
try {
|
|
@@ -764,11 +668,12 @@ export class DependencyResolver {
|
|
|
764
668
|
if (scope === "singleton") {
|
|
765
669
|
const createSingletonPromise = async () => {
|
|
766
670
|
const instance = await this.#instantiateAsync(binding, resolutionCtx, resolutionPath, resolutionStack);
|
|
767
|
-
const shouldActivate = this.#
|
|
671
|
+
const shouldActivate = this.#activation.refreshAfterFirstInstantiation(binding, needsActivation);
|
|
768
672
|
const activated = shouldActivate
|
|
769
673
|
? await this.#lifecycle.runActivation(resolutionCtx, binding, instance, this.#metadataReader)
|
|
770
674
|
: instance;
|
|
771
|
-
this.#scope.setSingleton(binding
|
|
675
|
+
this.#scope.setSingleton(binding, activated);
|
|
676
|
+
binding.instance = activated;
|
|
772
677
|
this.#scope.clearInflight(binding.id);
|
|
773
678
|
return activated;
|
|
774
679
|
};
|
|
@@ -780,7 +685,7 @@ export class DependencyResolver {
|
|
|
780
685
|
return await singletonPromise;
|
|
781
686
|
}
|
|
782
687
|
const instance = await this.#instantiateAsync(binding, resolutionCtx, resolutionPath, resolutionStack);
|
|
783
|
-
const shouldActivate = this.#
|
|
688
|
+
const shouldActivate = this.#activation.refreshAfterFirstInstantiation(binding, needsActivation);
|
|
784
689
|
const activated = shouldActivate
|
|
785
690
|
? await this.#lifecycle.runActivation(resolutionCtx, binding, instance, this.#metadataReader)
|
|
786
691
|
: instance;
|
|
@@ -813,7 +718,7 @@ export class DependencyResolver {
|
|
|
813
718
|
return binding.factory(ctx);
|
|
814
719
|
case "class": {
|
|
815
720
|
const deps = await this.#resolveClassDepsAsync(binding.target, resolutionPath, resolutionStack);
|
|
816
|
-
const instance = this.#
|
|
721
|
+
const instance = this.#classes.instantiate(binding.target, deps);
|
|
817
722
|
return instance;
|
|
818
723
|
}
|
|
819
724
|
case "resolved": {
|
|
@@ -833,7 +738,7 @@ export class DependencyResolver {
|
|
|
833
738
|
}
|
|
834
739
|
}
|
|
835
740
|
async #resolveClassDepsAsync(target, resolutionPath, resolutionStack) {
|
|
836
|
-
const meta = this.#
|
|
741
|
+
const meta = this.#classes.constructorMetadata(target);
|
|
837
742
|
if (meta === undefined) {
|
|
838
743
|
if (target.length === 0) {
|
|
839
744
|
return [];
|
|
@@ -1022,28 +927,6 @@ export class DependencyResolver {
|
|
|
1022
927
|
#getTokenName(token) {
|
|
1023
928
|
return tokenName(token);
|
|
1024
929
|
}
|
|
1025
|
-
#getConstructorMetadata(target) {
|
|
1026
|
-
const cached = this.#classConstructorMetadata.get(target);
|
|
1027
|
-
if (cached !== undefined) {
|
|
1028
|
-
return cached === null ? undefined : cached;
|
|
1029
|
-
}
|
|
1030
|
-
const metadata = this.#metadataReader.getConstructorMetadata(target);
|
|
1031
|
-
this.#classConstructorMetadata.set(target, metadata ?? null);
|
|
1032
|
-
return metadata;
|
|
1033
|
-
}
|
|
1034
|
-
#instantiateClass(target, deps) {
|
|
1035
|
-
let needsActiveContainer = this.#classNeedsActiveContainer.get(target);
|
|
1036
|
-
if (needsActiveContainer === undefined) {
|
|
1037
|
-
const accessorMetadata = this.#metadataReader.getAccessorMetadata?.(target);
|
|
1038
|
-
needsActiveContainer = (accessorMetadata?.length ?? 0) > 0;
|
|
1039
|
-
this.#classNeedsActiveContainer.set(target, needsActiveContainer);
|
|
1040
|
-
}
|
|
1041
|
-
const invokable = target;
|
|
1042
|
-
if (!needsActiveContainer) {
|
|
1043
|
-
return new invokable(...deps);
|
|
1044
|
-
}
|
|
1045
|
-
return runWithContainer(this.#container, () => new invokable(...deps));
|
|
1046
|
-
}
|
|
1047
930
|
#matchesRequestedTag(tagKey, tagValue, requestedTags, singleRequestedTag) {
|
|
1048
931
|
if (singleRequestedTag !== undefined &&
|
|
1049
932
|
singleRequestedTag[0] === tagKey &&
|
|
@@ -1062,10 +945,7 @@ export class DependencyResolver {
|
|
|
1062
945
|
return false;
|
|
1063
946
|
}
|
|
1064
947
|
#resolveTransientDynamicSyncFromContext(binding, resolutionPath, resolutionStack) {
|
|
1065
|
-
// One lane at every depth.
|
|
1066
|
-
// an O(depth) `resolutionPath.includes()` scan, which had to be escaped past ~32 levels; with
|
|
1067
|
-
// the O(1) `binding.inFlight` mark there is nothing to escape, so the depth split — and the
|
|
1068
|
-
// divergent behaviour it caused — is gone.
|
|
948
|
+
// One lane at every depth: `binding.inFlight` is O(1), so there is nothing to escape.
|
|
1069
949
|
const frame = this.#getResolutionFrame(binding);
|
|
1070
950
|
const tokenDisplayName = frame.tokenName;
|
|
1071
951
|
if (binding.inFlight) {
|
|
@@ -1088,19 +968,10 @@ export class DependencyResolver {
|
|
|
1088
968
|
binding.inFlight = false;
|
|
1089
969
|
}
|
|
1090
970
|
}
|
|
1091
|
-
//
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
// One lane at every depth. Cycle detection goes through `enterResolutionPath`, which is
|
|
1096
|
-
// path-scoped — the only mechanism that stays correct when chains interleave (Promise.all) —
|
|
1097
|
-
// and adapts on its own: a linear scan while the path is short, an attached Set past
|
|
1098
|
-
// RESOLUTION_SET_THRESHOLD. That removes the depth split, and with it a silent change of
|
|
1099
|
-
// behaviour (context identity, stack frames, promise shape) at the old threshold.
|
|
1100
|
-
//
|
|
1101
|
-
// For a sequential chain every level shares one resolutionPath/resolutionStack, so a single
|
|
1102
|
-
// DefaultResolutionContext serves the whole chain: inner levels of the owning chain allocate
|
|
1103
|
-
// nothing. A concurrent chain is detected by path identity and gets its own context.
|
|
971
|
+
// Deliberately not `async`: that would allocate a state machine and a promise per level.
|
|
972
|
+
#resolveTransientDynamicAsyncFromContext(binding, resolutionPath, resolutionStack, callerContext) {
|
|
973
|
+
// Path-scoped cycle detection, because async chains interleave — see ARCHITECTURE.md.
|
|
974
|
+
const pool = this.#asyncChainContextPool;
|
|
1104
975
|
const frame = this.#getResolutionFrame(binding);
|
|
1105
976
|
const tokenDisplayName = frame.tokenName;
|
|
1106
977
|
try {
|
|
@@ -1110,32 +981,12 @@ export class DependencyResolver {
|
|
|
1110
981
|
// This method is not `async`; keep failures as rejections rather than sync throws.
|
|
1111
982
|
return Promise.reject(cycleError);
|
|
1112
983
|
}
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
else if (this.#asyncChainCtxPath === undefined) {
|
|
1120
|
-
const existing = this.#asyncChainCtx;
|
|
1121
|
-
if (existing === undefined) {
|
|
1122
|
-
ctx = new DefaultResolutionContext(this, resolutionPath, resolutionStack, undefined);
|
|
1123
|
-
this.#asyncChainCtx = ctx;
|
|
1124
|
-
}
|
|
1125
|
-
else {
|
|
1126
|
-
existing.reset(this, resolutionPath, resolutionStack, undefined);
|
|
1127
|
-
ctx = existing;
|
|
1128
|
-
}
|
|
1129
|
-
this.#asyncChainCtxPath = resolutionPath;
|
|
1130
|
-
isOwnerLevel = true;
|
|
1131
|
-
}
|
|
1132
|
-
else {
|
|
1133
|
-
ctx = new DefaultResolutionContext(this, resolutionPath, resolutionStack, undefined);
|
|
1134
|
-
isOwnerLevel = false;
|
|
1135
|
-
}
|
|
1136
|
-
if (isOwnerLevel) {
|
|
1137
|
-
this.#asyncChainActiveLevels++;
|
|
1138
|
-
}
|
|
984
|
+
// An inner level reuses the context its caller passed down; only a chain's first level
|
|
985
|
+
// borrows from the pool. Pooling is load-bearing here — see ARCHITECTURE.md.
|
|
986
|
+
const ctx = callerContext !== undefined && callerContext.owner === this
|
|
987
|
+
? callerContext
|
|
988
|
+
: this.#acquireAsyncChainContext(resolutionPath, resolutionStack);
|
|
989
|
+
ctx.chainLevels += 1;
|
|
1139
990
|
// Invoke the factory synchronously to get its Promise (or a resolved value for "dynamic").
|
|
1140
991
|
let factoryPromise;
|
|
1141
992
|
try {
|
|
@@ -1151,40 +1002,33 @@ export class DependencyResolver {
|
|
|
1151
1002
|
catch (factoryError) {
|
|
1152
1003
|
// Synchronous throw from the factory (rare) — clean up immediately.
|
|
1153
1004
|
exitResolutionPath(resolutionPath);
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1005
|
+
ctx.chainLevels -= 1;
|
|
1006
|
+
if (ctx.chainLevels === 0) {
|
|
1007
|
+
pool.push(ctx);
|
|
1157
1008
|
}
|
|
1158
1009
|
return Promise.reject(factoryError);
|
|
1159
1010
|
}
|
|
1160
|
-
//
|
|
1161
|
-
//
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
// failing resolveAsync no longer surfaces as an unhandledRejection — callers are expected to
|
|
1165
|
-
// await (or .catch) the returned promise.
|
|
1166
|
-
//
|
|
1167
|
-
// Every level of the owning chain unwinds identically, so one closure serves them all.
|
|
1168
|
-
let settle;
|
|
1169
|
-
if (isOwnerLevel) {
|
|
1170
|
-
settle =
|
|
1171
|
-
this.#asyncChainSettle ??
|
|
1172
|
-
(this.#asyncChainSettle = () => {
|
|
1173
|
-
exitResolutionPath(resolutionPath);
|
|
1174
|
-
if (--this.#asyncChainActiveLevels === 0) {
|
|
1175
|
-
this.#asyncChainCtxPath = undefined;
|
|
1176
|
-
this.#asyncChainSettle = undefined;
|
|
1177
|
-
}
|
|
1178
|
-
});
|
|
1179
|
-
}
|
|
1180
|
-
else {
|
|
1181
|
-
settle = () => {
|
|
1011
|
+
// A side listener, not a derived chain: FIFO puts it before the awaiting caller resumes.
|
|
1012
|
+
// It marks a rejection as handled, so callers must await or `.catch` the returned promise.
|
|
1013
|
+
const settle = ctx.chainSettle ??
|
|
1014
|
+
(ctx.chainSettle = () => {
|
|
1182
1015
|
exitResolutionPath(resolutionPath);
|
|
1183
|
-
|
|
1184
|
-
|
|
1016
|
+
ctx.chainLevels -= 1;
|
|
1017
|
+
if (ctx.chainLevels === 0) {
|
|
1018
|
+
pool.push(ctx);
|
|
1019
|
+
}
|
|
1020
|
+
});
|
|
1185
1021
|
factoryPromise.then(settle, settle);
|
|
1186
1022
|
return factoryPromise;
|
|
1187
1023
|
}
|
|
1024
|
+
#acquireAsyncChainContext(resolutionPath, resolutionStack) {
|
|
1025
|
+
const pooled = this.#asyncChainContextPool.pop();
|
|
1026
|
+
if (pooled === undefined) {
|
|
1027
|
+
return new DefaultResolutionContext(this, resolutionPath, resolutionStack, undefined);
|
|
1028
|
+
}
|
|
1029
|
+
pooled.reset(this, resolutionPath, resolutionStack, undefined);
|
|
1030
|
+
return pooled;
|
|
1031
|
+
}
|
|
1188
1032
|
#resolveCandidateSync(binding, options, resolutionPath, resolutionStack) {
|
|
1189
1033
|
if (binding.kind === "constant" &&
|
|
1190
1034
|
binding.onActivation === undefined &&
|
|
@@ -1195,8 +1039,8 @@ export class DependencyResolver {
|
|
|
1195
1039
|
return this.resolve(binding.target, options, resolutionPath, resolutionStack);
|
|
1196
1040
|
}
|
|
1197
1041
|
const scope = binding.scope ?? "transient";
|
|
1198
|
-
if (scope === "singleton" &&
|
|
1199
|
-
return
|
|
1042
|
+
if (scope === "singleton" && binding.instance !== NO_INSTANCE) {
|
|
1043
|
+
return binding.instance;
|
|
1200
1044
|
}
|
|
1201
1045
|
if (scope === "scoped") {
|
|
1202
1046
|
if (!this.#scope.isChild) {
|
|
@@ -1220,8 +1064,8 @@ export class DependencyResolver {
|
|
|
1220
1064
|
return this.resolveAsync(binding.target, options, isolatedPath, isolatedStack);
|
|
1221
1065
|
}
|
|
1222
1066
|
const scope = binding.scope ?? "transient";
|
|
1223
|
-
if (scope === "singleton" &&
|
|
1224
|
-
return Promise.resolve(
|
|
1067
|
+
if (scope === "singleton" && binding.instance !== NO_INSTANCE) {
|
|
1068
|
+
return Promise.resolve(binding.instance);
|
|
1225
1069
|
}
|
|
1226
1070
|
if (scope === "scoped") {
|
|
1227
1071
|
if (!this.#scope.isChild) {
|
|
@@ -1246,62 +1090,6 @@ export class DependencyResolver {
|
|
|
1246
1090
|
binding.frame = frame;
|
|
1247
1091
|
return frame;
|
|
1248
1092
|
}
|
|
1249
|
-
#needsActivation(binding) {
|
|
1250
|
-
const lifecycleVersion = this.#lifecycle.activationVersion;
|
|
1251
|
-
if (lifecycleVersion === 0 &&
|
|
1252
|
-
binding.kind !== "class" &&
|
|
1253
|
-
binding.kind !== "alias" &&
|
|
1254
|
-
binding.onActivation === undefined) {
|
|
1255
|
-
return false;
|
|
1256
|
-
}
|
|
1257
|
-
if (this.#activationCacheVersion !== lifecycleVersion) {
|
|
1258
|
-
this.#activationNeedByBindingId.clear();
|
|
1259
|
-
this.#activationCacheVersion = lifecycleVersion;
|
|
1260
|
-
}
|
|
1261
|
-
const cached = this.#activationNeedByBindingId.get(binding.id);
|
|
1262
|
-
if (cached !== undefined) {
|
|
1263
|
-
return cached;
|
|
1264
|
-
}
|
|
1265
|
-
if (binding.kind === "class") {
|
|
1266
|
-
let hasActivation = this.#lifecycle.hasActivationHandlers(binding.token) || binding.onActivation !== undefined;
|
|
1267
|
-
const cachedPostConstruct = this.#classHasPostConstruct.get(binding.target);
|
|
1268
|
-
// Unknown class lifecycle metadata: activate once, then cache after first instantiation.
|
|
1269
|
-
if (cachedPostConstruct === undefined) {
|
|
1270
|
-
hasActivation = true;
|
|
1271
|
-
}
|
|
1272
|
-
else if (cachedPostConstruct) {
|
|
1273
|
-
hasActivation = true;
|
|
1274
|
-
}
|
|
1275
|
-
this.#activationNeedByBindingId.set(binding.id, hasActivation);
|
|
1276
|
-
return hasActivation;
|
|
1277
|
-
}
|
|
1278
|
-
let hasActivation = false;
|
|
1279
|
-
if (binding.kind !== "alias" && binding.onActivation !== undefined) {
|
|
1280
|
-
hasActivation = true;
|
|
1281
|
-
}
|
|
1282
|
-
else if (this.#lifecycle.hasActivationHandlers(binding.token)) {
|
|
1283
|
-
hasActivation = true;
|
|
1284
|
-
}
|
|
1285
|
-
this.#activationNeedByBindingId.set(binding.id, hasActivation);
|
|
1286
|
-
return hasActivation;
|
|
1287
|
-
}
|
|
1288
|
-
#refreshClassPostConstructCache(target) {
|
|
1289
|
-
const lifecycle = this.#metadataReader.getLifecycleMetadata(target);
|
|
1290
|
-
const hasPostConstruct = lifecycle !== undefined && lifecycle.postConstruct !== undefined && lifecycle.postConstruct.length > 0;
|
|
1291
|
-
this.#classHasPostConstruct.set(target, hasPostConstruct);
|
|
1292
|
-
}
|
|
1293
|
-
/**
|
|
1294
|
-
* Refreshes the post-construct cache for class bindings on first instantiation and
|
|
1295
|
-
* returns the (possibly updated) shouldActivate flag.
|
|
1296
|
-
*/
|
|
1297
|
-
#refreshActivationCacheIfNeeded(binding, needsActivation) {
|
|
1298
|
-
if (binding.kind === "class" && this.#classHasPostConstruct.get(binding.target) === undefined) {
|
|
1299
|
-
this.#refreshClassPostConstructCache(binding.target);
|
|
1300
|
-
this.#activationNeedByBindingId.delete(binding.id);
|
|
1301
|
-
return this.#needsActivation(binding);
|
|
1302
|
-
}
|
|
1303
|
-
return needsActivation;
|
|
1304
|
-
}
|
|
1305
1093
|
#requiresResolutionContext(binding) {
|
|
1306
1094
|
return binding.kind === "dynamic" || binding.kind === "dynamic-async";
|
|
1307
1095
|
}
|