@codefast/di 0.10.0 → 0.11.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.
Files changed (81) hide show
  1. package/CHANGELOG.md +293 -0
  2. package/README.md +71 -7
  3. package/dist/ambient/active-container.d.ts +13 -11
  4. package/dist/ambient/active-container.js +8 -6
  5. package/dist/container/binding-builders.d.ts +25 -7
  6. package/dist/container/binding-builders.js +110 -42
  7. package/dist/container/container.d.ts +8 -8
  8. package/dist/container/container.js +244 -67
  9. package/dist/core/binding-declaration.d.ts +120 -0
  10. package/dist/core/binding-declaration.js +186 -0
  11. package/dist/core/binding-scope.d.ts +2 -2
  12. package/dist/core/binding.d.ts +61 -9
  13. package/dist/core/binding.js +49 -2
  14. package/dist/core/constraint-requirement.d.ts +1 -1
  15. package/dist/core/module.d.ts +10 -7
  16. package/dist/core/module.js +17 -3
  17. package/dist/core/registry.d.ts +17 -13
  18. package/dist/core/registry.js +155 -59
  19. package/dist/core/state-epoch.d.ts +18 -1
  20. package/dist/core/state-epoch.js +17 -0
  21. package/dist/core/tag.js +1 -1
  22. package/dist/core/token.d.ts +1 -1
  23. package/dist/core/types.d.ts +5 -5
  24. package/dist/decorators/decorator-metadata.d.ts +9 -0
  25. package/dist/decorators/decorator-metadata.js +20 -0
  26. package/dist/decorators/inject.d.ts +3 -3
  27. package/dist/decorators/inject.js +7 -6
  28. package/dist/decorators/injectable.d.ts +2 -2
  29. package/dist/decorators/injectable.js +5 -3
  30. package/dist/decorators/lifecycle-decorators.js +9 -3
  31. package/dist/errors/errors.d.ts +85 -14
  32. package/dist/errors/errors.js +100 -20
  33. package/dist/index.d.ts +37 -35
  34. package/dist/index.js +20 -19
  35. package/dist/injection/descriptor.d.ts +3 -3
  36. package/dist/injection/descriptor.js +3 -7
  37. package/dist/injection/resolve-options.d.ts +3 -3
  38. package/dist/injection/resolve-options.js +7 -5
  39. package/dist/introspection/dependency-graph.d.ts +10 -5
  40. package/dist/introspection/dependency-graph.js +51 -28
  41. package/dist/introspection/graph-adapters/cytoscape.d.ts +1 -1
  42. package/dist/introspection/graph-adapters/dot.d.ts +1 -1
  43. package/dist/introspection/graph-adapters/mermaid.d.ts +1 -1
  44. package/dist/introspection/graph-adapters/reactflow.d.ts +1 -1
  45. package/dist/introspection/graph-adapters/reactflow.js +6 -4
  46. package/dist/introspection/inspector.d.ts +4 -4
  47. package/dist/introspection/inspector.js +9 -12
  48. package/dist/lifecycle/lifecycle-manager.d.ts +4 -4
  49. package/dist/lifecycle/lifecycle-manager.js +18 -6
  50. package/dist/lifecycle/scope-manager.d.ts +2 -2
  51. package/dist/lifecycle/scope-manager.js +31 -13
  52. package/dist/metadata/metadata-reader-token.d.ts +2 -2
  53. package/dist/metadata/metadata-reader-token.js +1 -1
  54. package/dist/metadata/metadata-types.d.ts +3 -3
  55. package/dist/metadata/symbol-metadata-reader.d.ts +3 -3
  56. package/dist/metadata/symbol-metadata-reader.js +1 -1
  57. package/dist/metadata/verifying-metadata-reader.d.ts +5 -4
  58. package/dist/metadata/verifying-metadata-reader.js +30 -8
  59. package/dist/resolution/async-fan-out.d.ts +12 -0
  60. package/dist/resolution/async-fan-out.js +26 -0
  61. package/dist/resolution/cache/activation-need.d.ts +4 -5
  62. package/dist/resolution/cache/activation-need.js +11 -18
  63. package/dist/resolution/cache/binding-lookup-cache.d.ts +5 -12
  64. package/dist/resolution/cache/binding-lookup-cache.js +32 -19
  65. package/dist/resolution/cache/class-introspector.d.ts +16 -5
  66. package/dist/resolution/cache/class-introspector.js +73 -56
  67. package/dist/resolution/context.d.ts +17 -25
  68. package/dist/resolution/context.js +47 -56
  69. package/dist/resolution/path/resolution-path.d.ts +48 -13
  70. package/dist/resolution/path/resolution-path.js +90 -39
  71. package/dist/resolution/plan/instantiation-plan.d.ts +5 -5
  72. package/dist/resolution/plan/instantiation-plan.js +66 -26
  73. package/dist/resolution/plan/plan-codegen.d.ts +10 -7
  74. package/dist/resolution/plan/plan-codegen.js +62 -34
  75. package/dist/resolution/resolver.d.ts +16 -17
  76. package/dist/resolution/resolver.js +316 -273
  77. package/dist/resolution/select/binding-select.d.ts +6 -5
  78. package/dist/resolution/select/binding-select.js +5 -4
  79. package/dist/resolution/select/constraints.d.ts +3 -3
  80. package/dist/resolution/select/constraints.js +4 -4
  81. package/package.json +14 -2
@@ -1,11 +1,10 @@
1
- import { NO_INSTANCE } from "#/core/binding";
2
- import { tokenName } from "#/core/token";
3
- import { AsyncResolutionError } from "#/errors/errors";
4
- import { injectionSlotToResolveOptions } from "#/injection/resolve-options";
5
- import { generateAsyncPlan, generatePlan, isPlanCodegenAvailable, PLAN_CODEGEN_THRESHOLD, } from "#/resolution/plan/plan-codegen";
6
- // Past this depth a dependency escapes to the runtime path rather than inlining further —
7
- // compiled closures nest one JS frame per level, and pathological graphs are the runtime's job.
8
- const PLAN_DEPTH_LIMIT = 32;
1
+ import { NO_INSTANCE } from "#core/binding";
2
+ import { tokenName } from "#core/token";
3
+ import { AsyncResolutionError } from "#errors/errors";
4
+ import { injectionSlotToResolveOptions } from "#injection/resolve-options";
5
+ import { settleInOrder } from "#resolution/async-fan-out";
6
+ import { enterSeededPath, leaveSeededPath } from "#resolution/path/resolution-path";
7
+ import { generateAsyncPlan, generatePlan, isPlanCodegenAvailable, PLAN_CODEGEN_THRESHOLD, } from "#resolution/plan/plan-codegen";
9
8
  /**
10
9
  * Compilation asked to retry later (class lifecycle metadata not discovered yet).
11
10
  *
@@ -21,12 +20,28 @@ function allSynchronous(deps) {
21
20
  return true;
22
21
  }
23
22
  /**
24
- * The promise-aware combinator: run every dep thunk, await them together, then apply.
23
+ * The promise-aware combinator: run every dep thunk, settle them together, then apply.
25
24
  *
26
25
  * @remarks A dep's sync throw becomes that slot's rejection so its siblings still start — the
27
- * interpreted path starts every sibling before the first rejection propagates, and so does this.
26
+ * interpreted path starts every sibling before any failure is reported, and so does this; a failure
27
+ * is the first in declaration order, as it is on every other lane.
28
28
  */
29
29
  function settleThenApply(deps, apply) {
30
+ if (deps.length === 1) {
31
+ // One dependency has one outcome, so there is nothing to order and no fan-out to settle.
32
+ const only = deps[0].run;
33
+ const applyOne = (value) => apply([value]);
34
+ return () => {
35
+ let pending;
36
+ try {
37
+ pending = only();
38
+ }
39
+ catch (dependencyError) {
40
+ pending = Promise.reject(dependencyError);
41
+ }
42
+ return Promise.resolve(pending).then(applyOne);
43
+ };
44
+ }
30
45
  return () => {
31
46
  const pending = new Array(deps.length);
32
47
  for (let index = 0; index < deps.length; index += 1) {
@@ -37,7 +52,7 @@ function settleThenApply(deps, apply) {
37
52
  pending[index] = Promise.reject(dependencyError);
38
53
  }
39
54
  }
40
- return Promise.all(pending).then(apply);
55
+ return settleInOrder(pending, apply);
41
56
  };
42
57
  }
43
58
  /**
@@ -94,24 +109,52 @@ export class InstantiationPlanCompiler {
94
109
  * root-stack rule one level down: every sync lane pops what it pushes, so the owned array still
95
110
  * holds exactly the seed when a call returns. A reentrant call finds it claimed and mints its
96
111
  * own copy; a return that did not restore the length hands nothing back, so the next call mints.
112
+ * The plan pushed no frame for the ancestors it inlined, so the seed's bindings are marked in
113
+ * flight around the call: an escaped factory cycling back into one is caught as the interpreted
114
+ * path would catch it.
97
115
  */
98
116
  #compileEscapeThunk(token, ancestors, arity = "single", options) {
99
117
  const host = this.#host;
100
118
  const frames = ancestors.map((ancestor) => host.getResolutionFrame(ancestor));
101
119
  const depth = frames.length;
102
120
  let owned = [...frames];
103
- const run = () => {
104
- const stack = owned ?? [...frames];
105
- owned = undefined;
106
- try {
107
- return host.resolveEscaped(token, options, arity, stack);
108
- }
109
- finally {
110
- if (stack.length === depth) {
111
- owned = stack;
121
+ // A plan root's own opaque dependency has one ancestor, and it is the common escape, so its
122
+ // marking is written out: one flag read and at most two writes, no loop and no call.
123
+ const only = ancestors.length === 1 ? ancestors[0] : undefined;
124
+ const run = only === undefined
125
+ ? () => {
126
+ const stack = owned ?? [...frames];
127
+ owned = undefined;
128
+ const alreadyInFlight = enterSeededPath(ancestors);
129
+ try {
130
+ return host.resolveEscaped(token, options, arity, stack);
131
+ }
132
+ finally {
133
+ leaveSeededPath(ancestors, alreadyInFlight);
134
+ if (stack.length === depth) {
135
+ owned = stack;
136
+ }
112
137
  }
113
138
  }
114
- };
139
+ : () => {
140
+ const stack = owned ?? [...frames];
141
+ owned = undefined;
142
+ const wasInFlight = only.inFlight;
143
+ if (!wasInFlight) {
144
+ only.inFlight = true;
145
+ }
146
+ try {
147
+ return host.resolveEscaped(token, options, arity, stack);
148
+ }
149
+ finally {
150
+ if (!wasInFlight) {
151
+ only.inFlight = false;
152
+ }
153
+ if (stack.length === depth) {
154
+ owned = stack;
155
+ }
156
+ }
157
+ };
115
158
  return { run, node: { kind: "thunk", run } };
116
159
  }
117
160
  // A resolved binding declares its deps as explicit descriptors — same rules as
@@ -282,10 +325,7 @@ export class InstantiationPlanCompiler {
282
325
  node: { kind: "singleton", binding: singletonBinding, escape },
283
326
  };
284
327
  }
285
- if (scope === "transient" &&
286
- binding.kind === "class" &&
287
- depth < PLAN_DEPTH_LIMIT &&
288
- !compileStack.has(binding.identifier)) {
328
+ if (scope === "transient" && binding.kind === "class" && !compileStack.has(binding.identifier)) {
289
329
  const inlined = this.#compileClassPlan(binding, compileStack, depth + 1, ancestors);
290
330
  if (inlined !== null) {
291
331
  return inlined;
@@ -476,7 +516,7 @@ export class InstantiationPlanCompiler {
476
516
  node: { kind: "singleton", binding: singletonBinding, escape },
477
517
  };
478
518
  }
479
- if (scope === "transient" && depth < PLAN_DEPTH_LIMIT && !compileStack.has(binding.identifier)) {
519
+ if (scope === "transient" && !compileStack.has(binding.identifier)) {
480
520
  let inlined = null;
481
521
  if (binding.kind === "class") {
482
522
  inlined = this.#compileAsyncClassNode(binding, compileStack, depth + 1, ancestors);
@@ -1,17 +1,19 @@
1
1
  /**
2
2
  * Generates a hot instantiation plan as a function of its own, so its call sites answer for one plan only.
3
3
  */
4
- import type { Binding } from "#/core/binding";
5
- import type { ConstructorInvocation } from "#/core/constructor-type";
4
+ import type { Binding } from "#core/binding";
5
+ import type { ConstructorInvocation } from "#core/constructor-type";
6
6
  /**
7
- * Runs a plan's closure makes before the plan is generated as its own function.
7
+ * The number of runs a plan's closure makes before the plan is generated as its own function.
8
8
  *
9
- * @remarks Below it a plan stays a closure, which is all a cold container or a per-request child
10
- * ever runs; above it a plan pays one compile for call sites nothing else feeds.
9
+ * @remarks A measured policy, not a machine width, a contract value or bind-time data: generating
10
+ * costs some fifty closure runs and the new function runs cold for thirty more, so it repays only
11
+ * over runs in the thousands. Below it a plan stays a closure, which is all a cold container or a
12
+ * per-request child ever runs.
11
13
  *
12
14
  * @since 0.10.0
13
15
  */
14
- export declare const PLAN_CODEGEN_THRESHOLD = 32;
16
+ export declare const PLAN_CODEGEN_THRESHOLD = 1024;
15
17
  /**
16
18
  * The shape of a compiled sync plan: what its closure does, stated as data the generator can read.
17
19
  *
@@ -49,7 +51,8 @@ export type PlanNode = {
49
51
  *
50
52
  * @remarks A node that `awaits` has a dependency that may yield a promise, so it runs as the
51
53
  * interpreted async path does: every dependency starts in order, a sync throw becomes that slot's
52
- * rejection, and the constructor or factory runs on the settled values.
54
+ * rejection, the constructor or factory runs on the settled values, and a failure is reported in
55
+ * declaration order.
53
56
  *
54
57
  * @since 0.10.0
55
58
  */
@@ -1,13 +1,16 @@
1
- import { NO_INSTANCE } from "#/core/binding";
1
+ import { NO_INSTANCE } from "#core/binding";
2
+ import { settleInOrder } from "#resolution/async-fan-out";
2
3
  /**
3
- * Runs a plan's closure makes before the plan is generated as its own function.
4
+ * The number of runs a plan's closure makes before the plan is generated as its own function.
4
5
  *
5
- * @remarks Below it a plan stays a closure, which is all a cold container or a per-request child
6
- * ever runs; above it a plan pays one compile for call sites nothing else feeds.
6
+ * @remarks A measured policy, not a machine width, a contract value or bind-time data: generating
7
+ * costs some fifty closure runs and the new function runs cold for thirty more, so it repays only
8
+ * over runs in the thousands. Below it a plan stays a closure, which is all a cold container or a
9
+ * per-request child ever runs.
7
10
  *
8
11
  * @since 0.10.0
9
12
  */
10
- export const PLAN_CODEGEN_THRESHOLD = 32;
13
+ export const PLAN_CODEGEN_THRESHOLD = 1024;
11
14
  const rejectWith = (error) => Promise.reject(error);
12
15
  let codegenAvailable;
13
16
  let generatedCount = 0;
@@ -59,10 +62,10 @@ export function generateAsyncPlan(node) {
59
62
  const emitter = new PlanEmitter();
60
63
  return compileRendered(emitter, emitter.asyncExpression(node));
61
64
  }
62
- function compileRendered(emitter, expression) {
65
+ function compileRendered(emitter, result) {
63
66
  generatedCount += 1;
64
67
  const locals = emitter.locals.length === 0 ? "" : `let ${emitter.locals.join(", ")};`;
65
- const body = `"use strict";/* plan ${String(generatedCount)} */${emitter.hoisted.join("")}return () => {${locals}return ${expression};};`;
68
+ const body = `"use strict";/* plan ${String(generatedCount)} */${emitter.hoisted.join("")}return () => {${locals}${emitter.statements.join("")}return ${result};};`;
66
69
  try {
67
70
  // Compiling from source is the mechanism: one function literal per plan is what gives it its own feedback.
68
71
  // oxlint-disable-next-line typescript/no-implied-eval
@@ -74,40 +77,50 @@ function compileRendered(emitter, expression) {
74
77
  }
75
78
  }
76
79
  /**
77
- * Renders a plan tree as one expression over parameters that carry every value the plan closes over.
80
+ * Renders a plan tree as a flat sequence of statements over parameters that carry every value the plan closes over.
78
81
  *
79
- * @remarks A node that awaits its dependencies renders as an inner function of the same source, so
80
- * every plan's awaiting nodes have call sites of their own too.
82
+ * @remarks Each node becomes one assignment to a local after its dependencies' assignments, in declaration
83
+ * order, so the generated function evaluates exactly as the nested closure did while nesting nothing: a
84
+ * graph of any depth renders as that many statements, never as an expression that deep. A node that awaits
85
+ * its dependencies renders as an inner function of the same shape, so every plan's awaiting nodes have call
86
+ * sites of their own too.
81
87
  */
82
88
  class PlanEmitter {
83
89
  names = [];
84
90
  values = [];
85
91
  hoisted = [];
86
- #localsByFunction = [[]];
92
+ #frames = [
93
+ { locals: [], statements: [] },
94
+ ];
87
95
  #hoistedCount = 0;
88
96
  #slotByValue = new Map();
89
97
  /** The plan function's own temporaries. */
90
98
  get locals() {
91
- return this.#localsByFunction[0];
99
+ return this.#frames[0].locals;
92
100
  }
101
+ /** The plan function's statements, in evaluation order. */
102
+ get statements() {
103
+ return this.#frames[0].statements;
104
+ }
105
+ /** Emits a node's statements and returns the reference that holds its value. */
93
106
  expression(node) {
94
107
  switch (node.kind) {
95
108
  case "construct":
96
- return `new ${this.#slot(node.target, "C")}(${this.#list(node.deps)})`;
109
+ return this.#define(`new ${this.#slot(node.target, "C")}(${this.#list(node.deps)})`);
97
110
  case "accessors":
98
- return `${this.#slot(node.construct, "A")}([${this.#list(node.deps)}])`;
111
+ return this.#define(`${this.#slot(node.construct, "A")}([${this.#list(node.deps)}])`);
99
112
  case "call": {
100
- // The settle only ever throws, so it runs on the promise branch alone and the plain result returns as is.
101
- const local = this.#local();
102
- const call = `${this.#slot(node.factory, "F")}(${this.#list(node.deps)})`;
103
- return `((${local} = ${call}) instanceof ${this.#slot(Promise, "P")} ? ${this.#slot(node.settle, "S")}(${local}) : ${local})`;
113
+ // The settle only ever throws, so it runs on the promise branch alone and the plain result stands.
114
+ const local = this.#define(`${this.#slot(node.factory, "F")}(${this.#list(node.deps)})`);
115
+ this.#statement(`if (${local} instanceof ${this.#slot(Promise, "P")}) ${local} = ${this.#slot(node.settle, "S")}(${local});`);
116
+ return local;
104
117
  }
105
118
  case "value":
106
119
  return this.#slot(node.value, "V");
107
120
  case "singleton":
108
121
  return this.#singletonRead(node.binding, node.escape);
109
122
  case "thunk":
110
- return `${this.#slot(node.run, "T")}()`;
123
+ return this.#define(`${this.#slot(node.run, "T")}()`);
111
124
  }
112
125
  }
113
126
  asyncExpression(node) {
@@ -116,20 +129,20 @@ class PlanEmitter {
116
129
  const target = this.#slot(node.target, "C");
117
130
  return node.awaits
118
131
  ? this.#settled(node.deps, (values) => `new ${target}(${values})`)
119
- : `new ${target}(${this.#asyncList(node.deps)})`;
132
+ : this.#define(`new ${target}(${this.#asyncList(node.deps)})`);
120
133
  }
121
134
  case "call": {
122
135
  const factory = this.#slot(node.factory, "F");
123
136
  return node.awaits
124
137
  ? this.#settled(node.deps, (values) => `${factory}(${values})`)
125
- : `${factory}(${this.#asyncList(node.deps)})`;
138
+ : this.#define(`${factory}(${this.#asyncList(node.deps)})`);
126
139
  }
127
140
  case "value":
128
141
  return this.#slot(node.value, "V");
129
142
  case "singleton":
130
143
  return this.#singletonRead(node.binding, node.escape);
131
144
  case "thunk":
132
- return `${this.#slot(node.run, "T")}()`;
145
+ return this.#define(`${this.#slot(node.run, "T")}()`);
133
146
  }
134
147
  }
135
148
  #list(deps) {
@@ -146,30 +159,45 @@ class PlanEmitter {
146
159
  const name = `n${String(index)}`;
147
160
  const applyName = `a${String(index)}`;
148
161
  const reject = this.#slot(rejectWith, "R");
149
- const promise = this.#slot(Promise, "P");
150
- this.#localsByFunction.push([]);
162
+ const frame = { locals: [], statements: [] };
163
+ this.#frames.push(frame);
151
164
  const pendings = [];
152
- const statements = [];
153
165
  for (let position = 0; position < deps.length; position += 1) {
154
166
  const pending = `p${String(position)}`;
155
167
  pendings.push(pending);
156
- statements.push(`try{${pending}=${this.asyncExpression(deps[position])};}catch(e){${pending}=${reject}(e);}`);
168
+ // A dependency's own statements run inside its try, so its sync throw is its rejection alone.
169
+ const mark = frame.statements.length;
170
+ const reference = this.asyncExpression(deps[position]);
171
+ const inner = frame.statements.splice(mark).join("");
172
+ frame.statements.push(`try{${inner}${pending}=${reference};}catch(e){${pending}=${reject}(e);}`);
157
173
  }
158
- const locals = [...pendings, ...this.#localsByFunction.pop()];
159
- const values = deps.map((_dep, position) => `v[${String(position)}]`).join(",");
160
- this.hoisted.push(`const ${applyName}=(v)=>${apply(values)};const ${name}=()=>{let ${locals.join(",")};${statements.join("")}return ${promise}.all([${pendings.join(",")}]).then(${applyName});};`);
161
- return `${name}()`;
174
+ this.#frames.pop();
175
+ const locals = [...pendings, ...frame.locals];
176
+ // One dependency has one outcome, so there is nothing to order and no fan-out to settle.
177
+ const settled = deps.length === 1
178
+ ? `const ${applyName}=(v0)=>${apply("v0")};const ${name}=()=>{let ${locals.join(",")};${frame.statements.join("")}return ${this.#slot(Promise, "P")}.resolve(p0).then(${applyName});};`
179
+ : `const ${applyName}=(v)=>${apply(deps.map((_dep, position) => `v[${String(position)}]`).join(","))};const ${name}=()=>{let ${locals.join(",")};${frame.statements.join("")}return ${this.#slot(settleInOrder, "W")}([${pendings.join(",")}],${applyName});};`;
180
+ this.hoisted.push(settled);
181
+ return this.#define(`${name}()`);
182
+ }
183
+ #define(expression) {
184
+ const local = this.#local();
185
+ this.#statement(`${local} = ${expression};`);
186
+ return local;
187
+ }
188
+ #statement(statement) {
189
+ this.#frames.at(-1).statements.push(statement);
162
190
  }
163
191
  #local() {
164
- const locals = this.#localsByFunction.at(-1);
192
+ const locals = this.#frames.at(-1).locals;
165
193
  const local = `t${String(locals.length)}`;
166
194
  locals.push(local);
167
195
  return local;
168
196
  }
169
197
  #singletonRead(binding, escape) {
170
- const local = this.#local();
171
- const slot = this.#slot(binding, "B");
172
- return `((${local} = ${slot}.instance) === ${this.#slot(NO_INSTANCE, "N")} ? ${this.#slot(escape, "E")}() : ${local})`;
198
+ const local = this.#define(`${this.#slot(binding, "B")}.instance`);
199
+ this.#statement(`if (${local} === ${this.#slot(NO_INSTANCE, "N")}) ${local} = ${this.#slot(escape, "E")}();`);
200
+ return local;
173
201
  }
174
202
  // One parameter per distinct value, so a class constructed four times is one constructor with four sites.
175
203
  #slot(value, prefix) {
@@ -1,15 +1,15 @@
1
- import type { Container } from "#/container/container";
2
- import type { Binding } from "#/core/binding";
3
- import type { BindingRegistry } from "#/core/registry";
4
- import type { Token } from "#/core/token";
5
- import type { BindingTag, Constructor, ResolutionFrame, ResolveOptions } from "#/core/types";
6
- import type { ResolutionDiagnostics } from "#/errors/diagnostics";
7
- import type { LifecycleManager } from "#/lifecycle/lifecycle-manager";
8
- import type { ScopeManager } from "#/lifecycle/scope-manager";
9
- import type { MetadataReader } from "#/metadata/metadata-types";
10
- import type { DefaultLookupEntry } from "#/resolution/cache/binding-lookup-cache";
11
- import type { ResolverCallbacks } from "#/resolution/context";
12
- import type { BranchDepth } from "#/resolution/path/resolution-path";
1
+ import type { Container } from "#container/container";
2
+ import type { Binding } from "#core/binding";
3
+ import type { BindingRegistry } from "#core/registry";
4
+ import type { Token } from "#core/token";
5
+ import type { BindingTag, Constructor, ResolutionFrame, ResolveOptions } from "#core/types";
6
+ import type { ResolutionDiagnostics } from "#errors/diagnostics";
7
+ import type { LifecycleManager } from "#lifecycle/lifecycle-manager";
8
+ import type { ScopeManager } from "#lifecycle/scope-manager";
9
+ import type { MetadataReader } from "#metadata/metadata-types";
10
+ import type { DefaultLookupEntry } from "#resolution/cache/binding-lookup-cache";
11
+ import type { ResolverCallbacks } from "#resolution/context";
12
+ import type { BranchDepth } from "#resolution/path/resolution-path";
13
13
  /**
14
14
  * The resolution engine driving binding selection, instantiation, scoping, and lifecycle hooks.
15
15
  *
@@ -65,12 +65,11 @@ export declare class DependencyResolver implements ResolverCallbacks {
65
65
  resolveOptionalAsync<Value>(token: Token<Value> | Constructor<Value>, options: ResolveOptions | undefined, resolutionStack: Array<ResolutionFrame>, branchDepth?: BranchDepth, precomputedCriterion?: BindingTag | null): Promise<Value | undefined>;
66
66
  resolveAllAsync<Value>(token: Token<Value> | Constructor<Value>, options: ResolveOptions | undefined, resolutionStack: Array<ResolutionFrame>, branchDepth?: BranchDepth): Promise<ReadonlyArray<Value>>;
67
67
  /**
68
- * Entry for a request a factory makes from inside an open synchronous cascade.
68
+ * Entry for an options-less resolve the container starts: the root of a branch of its own.
69
69
  *
70
- * @remarks A request arriving with no cascade open came out of a continuation, so its ancestors
71
- * are on no call stack — it escapes to the branch lane.
70
+ * @remarks A statically-visible transient graph answers from its compiled async plan; everything
71
+ * else opens a branch over a fresh array, so every level's context keeps its own ancestors for as
72
+ * long as the factory holds it — across an `await` included.
72
73
  */
73
- resolveAsyncFromCascade(token: Token<unknown> | Constructor): Promise<unknown>;
74
- /** Entry for a resolve the container starts, which opens the cascade rather than joining one. */
75
74
  resolveAsyncFromRoot(token: Token<unknown> | Constructor): Promise<unknown>;
76
75
  }