@codefast/di 0.11.0 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/CHANGELOG.md +111 -0
  2. package/README.md +26 -12
  3. package/dist/container/{binding-builders.d.ts → binding-chain.d.ts} +21 -28
  4. package/dist/container/{binding-builders.js → binding-chain.js} +53 -163
  5. package/dist/container/container.d.ts +11 -2
  6. package/dist/container/container.js +107 -64
  7. package/dist/core/binding-builders.d.ts +114 -0
  8. package/dist/core/binding-declaration.d.ts +7 -0
  9. package/dist/core/binding-declaration.js +3 -2
  10. package/dist/core/binding.d.ts +21 -149
  11. package/dist/core/binding.js +15 -25
  12. package/dist/core/module.d.ts +1 -1
  13. package/dist/core/registry.d.ts +9 -24
  14. package/dist/core/registry.js +30 -155
  15. package/dist/core/tag.js +1 -1
  16. package/dist/decorators/inject.js +5 -5
  17. package/dist/decorators/injectable.js +2 -2
  18. package/dist/decorators/{lifecycle-decorators.js → lifecycle.js} +3 -3
  19. package/dist/decorators/{decorator-metadata.js → metadata-record.js} +1 -1
  20. package/dist/{errors/errors.d.ts → errors.d.ts} +7 -6
  21. package/dist/{errors/errors.js → errors.js} +8 -7
  22. package/dist/index.d.ts +12 -10
  23. package/dist/index.js +6 -6
  24. package/dist/injection/descriptor.d.ts +1 -1
  25. package/dist/introspection/dependency-graph.d.ts +1 -1
  26. package/dist/introspection/dependency-graph.js +3 -4
  27. package/dist/introspection/explanation.d.ts +101 -0
  28. package/dist/introspection/explanation.js +151 -0
  29. package/dist/introspection/inspector.d.ts +9 -2
  30. package/dist/introspection/inspector.js +25 -20
  31. package/dist/lifecycle/{lifecycle-manager.d.ts → hooks.d.ts} +9 -2
  32. package/dist/lifecycle/{lifecycle-manager.js → hooks.js} +45 -28
  33. package/dist/lifecycle/{scope-manager.d.ts → scopes.d.ts} +3 -5
  34. package/dist/lifecycle/{scope-manager.js → scopes.js} +8 -19
  35. package/dist/metadata/{metadata-reader-token.d.ts → reader-token.d.ts} +1 -1
  36. package/dist/metadata/{symbol-metadata-reader.d.ts → symbol-reader.d.ts} +1 -1
  37. package/dist/metadata/{symbol-metadata-reader.js → symbol-reader.js} +1 -1
  38. package/dist/metadata/{metadata-types.d.ts → types.d.ts} +1 -1
  39. package/dist/metadata/types.js +1 -0
  40. package/dist/metadata/verify.d.ts +43 -0
  41. package/dist/metadata/verify.js +133 -0
  42. package/dist/metadata/{verifying-metadata-reader.d.ts → verifying-reader.d.ts} +1 -1
  43. package/dist/metadata/{verifying-metadata-reader.js → verifying-reader.js} +2 -2
  44. package/dist/resolution/cache/activation-need.d.ts +1 -1
  45. package/dist/resolution/cache/activation-need.js +5 -1
  46. package/dist/resolution/cache/class-introspector.d.ts +27 -36
  47. package/dist/resolution/cache/class-introspector.js +61 -159
  48. package/dist/resolution/cache/{binding-lookup-cache.d.ts → lookup.d.ts} +7 -2
  49. package/dist/resolution/cache/{binding-lookup-cache.js → lookup.js} +40 -8
  50. package/dist/resolution/context.d.ts +1 -1
  51. package/dist/resolution/context.js +1 -1
  52. package/dist/resolution/{path/resolution-path.js → path.js} +1 -1
  53. package/dist/resolution/plan/{plan-codegen.d.ts → codegen.d.ts} +2 -4
  54. package/dist/resolution/plan/{plan-codegen.js → codegen.js} +2 -4
  55. package/dist/resolution/plan/{instantiation-plan.d.ts → compiler.d.ts} +1 -1
  56. package/dist/resolution/plan/{instantiation-plan.js → compiler.js} +4 -4
  57. package/dist/resolution/resolver.d.ts +7 -7
  58. package/dist/resolution/resolver.js +308 -121
  59. package/dist/resolution/select/{binding-select.d.ts → candidates.d.ts} +30 -0
  60. package/dist/resolution/select/{binding-select.js → candidates.js} +51 -10
  61. package/dist/resolution/select/constraints.js +1 -1
  62. package/package.json +79 -71
  63. package/dist/core/binding-scope.d.ts +0 -12
  64. package/dist/core/binding-scope.js +0 -12
  65. /package/dist/{ambient/active-container.d.ts → ambient-container.d.ts} +0 -0
  66. /package/dist/{ambient/active-container.js → ambient-container.js} +0 -0
  67. /package/dist/{metadata/metadata-types.js → core/binding-builders.js} +0 -0
  68. /package/dist/decorators/{lifecycle-decorators.d.ts → lifecycle.d.ts} +0 -0
  69. /package/dist/decorators/{decorator-metadata.d.ts → metadata-record.d.ts} +0 -0
  70. /package/dist/injection/{resolve-options.d.ts → dependency-slot.d.ts} +0 -0
  71. /package/dist/injection/{resolve-options.js → dependency-slot.js} +0 -0
  72. /package/dist/{errors → introspection}/diagnostics.d.ts +0 -0
  73. /package/dist/{errors → introspection}/diagnostics.js +0 -0
  74. /package/dist/metadata/{metadata-keys.d.ts → keys.d.ts} +0 -0
  75. /package/dist/metadata/{metadata-keys.js → keys.js} +0 -0
  76. /package/dist/metadata/{metadata-reader-token.js → reader-token.js} +0 -0
  77. /package/dist/resolution/{path/resolution-path.d.ts → path.d.ts} +0 -0
@@ -3,175 +3,69 @@
3
3
  *
4
4
  * @remarks Metadata cannot change once a class is defined, so nothing here needs version stamping.
5
5
  */
6
- import { constructWithAmbientResolution, runWithAmbientResolution } from "#ambient/active-container";
7
- import { InvalidMetadataError } from "#errors/errors";
8
- // Verified pairs, not verified classes: two readers may disagree about the same class, and a reader
9
- // that goes out of scope takes its record with it.
10
- const verifiedTargets = new WeakMap();
11
- const verifiedLifecycleTargets = new WeakMap();
12
- const verifiedAccessorTargets = new WeakMap();
13
- function isVerified(cache, reader, target) {
14
- return cache.get(reader)?.has(target) ?? false;
15
- }
16
- function markVerified(cache, reader, target) {
17
- let verified = cache.get(reader);
18
- if (verified === undefined) {
19
- verified = new WeakSet();
20
- cache.set(reader, verified);
21
- }
22
- verified.add(target);
23
- }
24
- /**
25
- * Runs `validate` against a reader's metadata the first time this process sees the pair, then remembers it.
26
- *
27
- * @remarks An absent metadata answer needs no check; a verified pair is returned untouched, so the
28
- * shape assertion runs once per `(reader, target)` rather than per container.
29
- */
30
- function verifyOnce(cache, reader, target, metadata, validate) {
31
- if (metadata === undefined || isVerified(cache, reader, target)) {
32
- return metadata;
33
- }
34
- validate(metadata);
35
- markVerified(cache, reader, target);
36
- return metadata;
37
- }
38
- /**
39
- * A reader's constructor metadata for a class, verified the first time this process asks.
40
- *
41
- * @remarks Metadata cannot change once a class is defined, so re-checking per container would charge
42
- * every fresh container for a fact already established — a per-request child or a cold boot pays
43
- * that repeatedly.
44
- *
45
- * @since 0.6.0
46
- */
47
- export function verifyConstructorMetadata(reader, target) {
48
- return verifyOnce(verifiedTargets, reader, target, reader.getConstructorMetadata(target), (metadata) => {
49
- assertConstructorMetadata(metadata, target);
50
- });
51
- }
52
- /**
53
- * Verifies what a reader claims about a class, since a `MetadataReader` is a public seam.
54
- *
55
- * @remarks Every path that reads constructor metadata comes through here, so a reader that answers
56
- * wrongly is a named error at the class it described rather than a `TypeError` raised later inside a
57
- * resolve. Only what a consumer dereferences is checked: `params` and each entry's `token`.
58
- * `optional`/`multi` degrade to falsy without crashing, and `index` is decorative — dependencies are
59
- * consumed positionally.
60
- *
61
- * @since 0.6.0
62
- */
63
- export function assertConstructorMetadata(metadata, target) {
64
- if (metadata === undefined) {
65
- return undefined;
66
- }
67
- if (typeof metadata !== "object" || metadata === null) {
68
- throw new InvalidMetadataError(target.name, `constructor metadata: expected an object, received ${typeof metadata}`);
69
- }
70
- const params = Reflect.get(metadata, "params");
71
- if (!Array.isArray(params)) {
72
- throw new InvalidMetadataError(target.name, "constructor metadata: params is not an array");
73
- }
74
- for (const [position, param] of params.entries()) {
75
- if (typeof param !== "object" || param === null) {
76
- throw new InvalidMetadataError(target.name, `constructor metadata: params[${String(position)}] is not an object`);
77
- }
78
- const dependency = Reflect.get(param, "token");
79
- if (typeof dependency !== "object" && typeof dependency !== "function") {
80
- throw new InvalidMetadataError(target.name, `constructor metadata: params[${String(position)}].token is not a token or a class`);
81
- }
82
- }
83
- return metadata;
84
- }
85
- /**
86
- * A reader's lifecycle metadata for a class, verified the first time this process asks.
87
- *
88
- * @since 0.6.0
89
- */
90
- export function verifyLifecycleMetadata(reader, target) {
91
- return verifyOnce(verifiedLifecycleTargets, reader, target, reader.getLifecycleMetadata(target), (metadata) => {
92
- if (typeof metadata !== "object" || metadata === null) {
93
- throw new InvalidMetadataError(target.name, `lifecycle metadata: expected an object, received ${typeof metadata}`);
94
- }
95
- for (const phase of ["postConstruct", "preDestroy"]) {
96
- const methods = Reflect.get(metadata, phase);
97
- if (methods === undefined) {
98
- continue;
99
- }
100
- if (!Array.isArray(methods)) {
101
- throw new InvalidMetadataError(target.name, `lifecycle metadata: ${phase} is not an array`);
102
- }
103
- for (const [position, name] of methods.entries()) {
104
- if (typeof name !== "string") {
105
- throw new InvalidMetadataError(target.name, `lifecycle metadata: ${phase}[${String(position)}] is not a string`);
106
- }
107
- }
108
- }
109
- });
110
- }
111
- /**
112
- * A reader's accessor metadata for a class, verified the first time this process asks.
113
- *
114
- * @since 0.6.0
115
- */
116
- export function verifyAccessorMetadata(reader, target) {
117
- return verifyOnce(verifiedAccessorTargets, reader, target, reader.getAccessorMetadata?.(target), (metadata) => {
118
- if (!Array.isArray(metadata)) {
119
- throw new InvalidMetadataError(target.name, "accessor metadata: expected an array");
120
- }
121
- for (const [position, entry] of metadata.entries()) {
122
- if (typeof entry !== "object" || entry === null) {
123
- throw new InvalidMetadataError(target.name, `accessor metadata: [${String(position)}] is not an object`);
124
- }
125
- const key = Reflect.get(entry, "key");
126
- if (typeof key !== "string" && typeof key !== "symbol") {
127
- throw new InvalidMetadataError(target.name, `accessor metadata: [${String(position)}].key is not a string or symbol`);
128
- }
129
- const descriptor = Reflect.get(entry, "descriptor");
130
- if (typeof descriptor !== "object" || descriptor === null) {
131
- throw new InvalidMetadataError(target.name, `accessor metadata: [${String(position)}].descriptor is not an object`);
132
- }
133
- const dependency = Reflect.get(descriptor, "token");
134
- if (typeof dependency !== "object" && typeof dependency !== "function") {
135
- throw new InvalidMetadataError(target.name, `accessor metadata: [${String(position)}].descriptor.token is not a token or a class`);
136
- }
137
- }
138
- });
139
- }
140
- const cachesByReader = new WeakMap();
141
- function cachesFor(reader) {
142
- let caches = cachesByReader.get(reader);
143
- if (caches === undefined) {
144
- caches = { constructorMetadata: undefined, hasPostConstruct: undefined, needsActiveContainer: undefined };
145
- cachesByReader.set(reader, caches);
6
+ import { constructWithAmbientResolution, runWithAmbientResolution } from "#ambient-container";
7
+ // A reader answers from the class alone, so its facts are shared by every container reading through it.
8
+ const factsByReader = new WeakMap();
9
+ function factsFor(reader) {
10
+ let byClass = factsByReader.get(reader);
11
+ if (byClass === undefined) {
12
+ byClass = new WeakMap();
13
+ factsByReader.set(reader, byClass);
146
14
  }
147
- return caches;
15
+ return byClass;
148
16
  }
149
17
  /**
150
18
  * A per-class cache of constructor metadata and the activation facts derived from it.
151
19
  *
20
+ * @remarks A child inheriting its parent's reader takes the parent's facts too, and resolves a class
21
+ * the parent already met without reading its metadata again; a root finds them by reader on its first
22
+ * question.
23
+ *
152
24
  * @since 0.5.0-canary.8
153
25
  */
154
26
  export class ClassIntrospector {
155
- #caches;
27
+ #byClass;
28
+ // The class asked about last and its record: a cold resolve asks about one class several times in a row.
29
+ #lastTarget;
30
+ #lastFacts;
156
31
  #reader;
157
32
  #container;
158
33
  constructor(reader, container, inherited) {
159
- this.#caches = inherited !== undefined && inherited.#reader === reader ? inherited.#caches : undefined;
34
+ this.#byClass = inherited !== undefined && inherited.#reader === reader ? inherited.#byClass : undefined;
160
35
  this.#reader = reader;
161
36
  this.#container = container;
162
37
  }
163
- #shared() {
164
- return (this.#caches ??= cachesFor(this.#reader));
38
+ /** The record of what this reader has answered about a class, allocated on the class's first question. */
39
+ facts(target) {
40
+ if (target === this.#lastTarget) {
41
+ return this.#lastFacts;
42
+ }
43
+ const byClass = (this.#byClass ??= factsFor(this.#reader));
44
+ let facts = byClass.get(target);
45
+ if (facts === undefined) {
46
+ facts = {
47
+ constructorMetadata: undefined,
48
+ hasPostConstruct: undefined,
49
+ needsActiveContainer: undefined,
50
+ params: undefined,
51
+ };
52
+ byClass.set(target, facts);
53
+ }
54
+ this.#lastTarget = target;
55
+ this.#lastFacts = facts;
56
+ return facts;
165
57
  }
166
58
  constructorMetadata(target) {
167
- const caches = this.#shared();
168
- const cached = caches.constructorMetadata?.get(target);
169
- if (cached !== undefined) {
170
- return cached === null ? undefined : cached;
59
+ return this.constructorMetadataOf(target, this.facts(target));
60
+ }
61
+ /** The constructor metadata of a class whose facts the caller already holds. */
62
+ constructorMetadataOf(target, facts) {
63
+ let metadata = facts.constructorMetadata;
64
+ if (metadata === undefined) {
65
+ metadata = this.#reader.getConstructorMetadata(target) ?? null;
66
+ facts.constructorMetadata = metadata;
171
67
  }
172
- const metadata = this.#reader.getConstructorMetadata(target);
173
- (caches.constructorMetadata ??= new WeakMap()).set(target, metadata ?? null);
174
- return metadata;
68
+ return metadata === null ? undefined : metadata;
175
69
  }
176
70
  /**
177
71
  * The nearest ancestor's own constructor metadata, for a subclass that declares none of its own.
@@ -197,26 +91,34 @@ export class ClassIntrospector {
197
91
  * @remarks Callers treat unknown as "assume it does", so the first activation settles it.
198
92
  */
199
93
  knownPostConstruct(target) {
200
- return this.#shared().hasPostConstruct?.get(target);
94
+ return this.facts(target).hasPostConstruct;
201
95
  }
202
96
  discoverPostConstruct(target) {
203
97
  const lifecycle = this.#reader.getLifecycleMetadata(target);
204
- (this.#shared().hasPostConstruct ??= new WeakMap()).set(target, lifecycle !== undefined && lifecycle.postConstruct !== undefined && lifecycle.postConstruct.length > 0);
98
+ this.facts(target).hasPostConstruct =
99
+ lifecycle !== undefined && lifecycle.postConstruct !== undefined && lifecycle.postConstruct.length > 0;
205
100
  }
206
101
  /** True when the class has accessor injection, which reads the container during construction. */
207
102
  needsActiveContainer(target) {
208
- const caches = this.#shared();
209
- let needsActiveContainer = caches.needsActiveContainer?.get(target);
103
+ return this.needsActiveContainerOf(target, this.facts(target));
104
+ }
105
+ /** Whether a class whose facts the caller already holds reads the container during construction. */
106
+ needsActiveContainerOf(target, facts) {
107
+ let needsActiveContainer = facts.needsActiveContainer;
210
108
  if (needsActiveContainer === undefined) {
211
109
  needsActiveContainer = (this.#reader.getAccessorMetadata?.(target)?.length ?? 0) > 0;
212
- (caches.needsActiveContainer ??= new WeakMap()).set(target, needsActiveContainer);
110
+ facts.needsActiveContainer = needsActiveContainer;
213
111
  }
214
112
  return needsActiveContainer;
215
113
  }
216
114
  instantiate(target, deps, resolution) {
115
+ return this.construct(target, deps, this.needsActiveContainer(target), resolution);
116
+ }
117
+ /** Builds an instance whose accessor need the caller already knows; an empty `deps` is a call with no arguments. */
118
+ construct(target, deps, needsActiveContainer, resolution) {
217
119
  const invokable = target;
218
- if (!this.needsActiveContainer(target)) {
219
- return new invokable(...deps);
120
+ if (!needsActiveContainer) {
121
+ return deps.length === 0 ? new invokable() : new invokable(...deps);
220
122
  }
221
123
  if (resolution === undefined) {
222
124
  return runWithAmbientResolution(this.#container, undefined, () => new invokable(...deps));
@@ -25,7 +25,8 @@ export interface DefaultLookupEntry<Owner> {
25
25
  *
26
26
  * @remarks `values` is kept only while every member is a hook-free constant or a hook-free singleton
27
27
  * whose instance is cached, and `activationVersion` is the chain's activation version that promise was
28
- * made under.
28
+ * made under. It is settled on the read that repeats the first, which `readBefore` records, so a
29
+ * container read once never builds it.
29
30
  *
30
31
  * @since 0.10.0
31
32
  */
@@ -33,6 +34,7 @@ export interface CollectionEntry {
33
34
  readonly candidates: ReadonlyArray<Binding>;
34
35
  values: ReadonlyArray<unknown> | undefined;
35
36
  activationVersion: number;
37
+ readBefore: boolean;
36
38
  }
37
39
  /**
38
40
  * A version-stamped cache of binding lookups by token and criterion across the container chain.
@@ -65,6 +67,9 @@ export declare class BindingLookupCache<Owner> {
65
67
  namedTaggedEntry(token: Token<unknown> | Constructor, nameCriterion: BindingTag, tag: BindingTag): DefaultLookupEntry<Owner> | null;
66
68
  /** The memoized root-level collection for a token, or `undefined` once the chain changed since it was stored. */
67
69
  collection(token: Token<unknown> | Constructor): CollectionEntry | undefined;
68
- /** Stores a root-level collection under the chain version the last `collection()` read stamped. */
70
+ /**
71
+ * Stores a root-level collection under the chain version the last `collection()` read stamped, so a
72
+ * predicate that binds during the gather leaves the entry stale rather than current.
73
+ */
69
74
  rememberCollection(token: Token<unknown> | Constructor, entry: CollectionEntry): void;
70
75
  }
@@ -29,9 +29,9 @@ export class BindingLookupCache {
29
29
  #lastTagToken;
30
30
  #lastTag;
31
31
  #lastTaggedEntry = null;
32
- // Root-level collections by token, stamped with the chain version like the two memos above.
32
+ // Root-level collections, stamped with the chain version like the memos above. Deferred into one object,
33
+ // because every per-request child builds this cache and almost none reads a collection at the root.
33
34
  #collections;
34
- #collectionsVersion = -1;
35
35
  #registry;
36
36
  #owner;
37
37
  #parent;
@@ -171,16 +171,48 @@ export class BindingLookupCache {
171
171
  /** The memoized root-level collection for a token, or `undefined` once the chain changed since it was stored. */
172
172
  collection(token) {
173
173
  const version = this.chainVersion();
174
- if (version !== this.#collectionsVersion) {
175
- this.#collections?.clear();
176
- this.#collectionsVersion = version;
174
+ const memo = this.#collections;
175
+ if (memo === undefined) {
176
+ this.#collections = { version, lastToken: undefined, lastEntry: undefined, byToken: undefined };
177
177
  return undefined;
178
178
  }
179
- return this.#collections?.get(token);
179
+ if (version !== memo.version) {
180
+ memo.version = version;
181
+ memo.byToken?.clear();
182
+ // The rebuild that follows can throw, so the entry in front must not outlive the generation.
183
+ memo.lastToken = undefined;
184
+ return undefined;
185
+ }
186
+ if (token === memo.lastToken) {
187
+ return memo.lastEntry;
188
+ }
189
+ const entry = memo.byToken?.get(token);
190
+ if (entry !== undefined) {
191
+ memo.lastToken = token;
192
+ memo.lastEntry = entry;
193
+ }
194
+ return entry;
180
195
  }
181
- /** Stores a root-level collection under the chain version the last `collection()` read stamped. */
196
+ /**
197
+ * Stores a root-level collection under the chain version the last `collection()` read stamped, so a
198
+ * predicate that binds during the gather leaves the entry stale rather than current.
199
+ */
182
200
  rememberCollection(token, entry) {
183
- (this.#collections ??= new Map()).set(token, entry);
201
+ const memo = this.#collections;
202
+ if (memo === undefined) {
203
+ return;
204
+ }
205
+ if (memo.byToken !== undefined) {
206
+ memo.byToken.set(token, entry);
207
+ }
208
+ else if (memo.lastToken !== undefined && memo.lastEntry !== undefined) {
209
+ memo.byToken = new Map([
210
+ [memo.lastToken, memo.lastEntry],
211
+ [token, entry],
212
+ ]);
213
+ }
214
+ memo.lastToken = token;
215
+ memo.lastEntry = entry;
184
216
  }
185
217
  /**
186
218
  * Follows alias hops to the terminal entry, or declines with `null` on a cycle, which the full
@@ -1,7 +1,7 @@
1
1
  /** The `ResolutionContext` a factory is handed, and the callbacks the resolver answers it with. */
2
2
  import type { Token } from "#core/token";
3
3
  import type { ConstraintContext, Constructor, ResolutionFrame, ResolutionContext, ResolveOptions } from "#core/types";
4
- import type { BranchDepth, OwnedBranchStack } from "#resolution/path/resolution-path";
4
+ import type { BranchDepth, OwnedBranchStack } from "#resolution/path";
5
5
  /**
6
6
  * The engine surface a resolution context calls back into to resolve further dependencies.
7
7
  *
@@ -1,4 +1,4 @@
1
- import { bindingsOf, enterSeededPath, leaveSeededPath, UNOWNED_BRANCH } from "#resolution/path/resolution-path";
1
+ import { bindingsOf, enterSeededPath, leaveSeededPath, UNOWNED_BRANCH } from "#resolution/path";
2
2
  /**
3
3
  * The `ResolutionContext` handed to factories and hooks, backed by the engine's callbacks.
4
4
  *
@@ -1,5 +1,5 @@
1
1
  /** Cycle-detection bookkeeping carried on the resolution stack itself. */
2
- import { CircularDependencyError } from "#errors/errors";
2
+ import { CircularDependencyError } from "#errors";
3
3
  /** The key under which a resolver-built frame carries its binding, for marking a seeded path. */
4
4
  const FRAME_BINDING = Symbol("di:frame-binding");
5
5
  /**
@@ -6,10 +6,8 @@ import type { ConstructorInvocation } from "#core/constructor-type";
6
6
  /**
7
7
  * The number of runs a plan's closure makes before the plan is generated as its own function.
8
8
  *
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.
9
+ * @remarks A measured policy: the generated function costs a compile and a warm-up, so it repays only over
10
+ * many runs of one plan in one container, and a cold container or a per-request child keeps the closure.
13
11
  *
14
12
  * @since 0.10.0
15
13
  */
@@ -3,10 +3,8 @@ import { settleInOrder } from "#resolution/async-fan-out";
3
3
  /**
4
4
  * The number of runs a plan's closure makes before the plan is generated as its own function.
5
5
  *
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.
6
+ * @remarks A measured policy: the generated function costs a compile and a warm-up, so it repays only over
7
+ * many runs of one plan in one container, and a cold container or a per-request child keeps the closure.
10
8
  *
11
9
  * @since 0.10.0
12
10
  */
@@ -8,7 +8,7 @@ import type { Binding } from "#core/binding";
8
8
  import type { ConstructorInvocation } from "#core/constructor-type";
9
9
  import type { Token } from "#core/token";
10
10
  import type { Constructor, ResolutionFrame, ResolveOptions } from "#core/types";
11
- import type { ConstructorMetadata } from "#metadata/metadata-types";
11
+ import type { ConstructorMetadata } from "#metadata/types";
12
12
  /**
13
13
  * Compilation asked to retry later (class lifecycle metadata not discovered yet).
14
14
  *
@@ -1,10 +1,10 @@
1
1
  import { NO_INSTANCE } from "#core/binding";
2
2
  import { tokenName } from "#core/token";
3
- import { AsyncResolutionError } from "#errors/errors";
4
- import { injectionSlotToResolveOptions } from "#injection/resolve-options";
3
+ import { AsyncResolutionError } from "#errors";
4
+ import { injectionSlotToResolveOptions } from "#injection/dependency-slot";
5
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";
6
+ import { enterSeededPath, leaveSeededPath } from "#resolution/path";
7
+ import { generateAsyncPlan, generatePlan, isPlanCodegenAvailable, PLAN_CODEGEN_THRESHOLD, } from "#resolution/plan/codegen";
8
8
  /**
9
9
  * Compilation asked to retry later (class lifecycle metadata not discovered yet).
10
10
  *
@@ -3,13 +3,13 @@ import type { Binding } from "#core/binding";
3
3
  import type { BindingRegistry } from "#core/registry";
4
4
  import type { Token } from "#core/token";
5
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";
6
+ import type { ResolutionDiagnostics } from "#introspection/diagnostics";
7
+ import type { LifecycleManager } from "#lifecycle/hooks";
8
+ import type { ScopeManager } from "#lifecycle/scopes";
9
+ import type { MetadataReader } from "#metadata/types";
10
+ import type { DefaultLookupEntry } from "#resolution/cache/lookup";
11
11
  import type { ResolverCallbacks } from "#resolution/context";
12
- import type { BranchDepth } from "#resolution/path/resolution-path";
12
+ import type { BranchDepth } from "#resolution/path";
13
13
  /**
14
14
  * The resolution engine driving binding selection, instantiation, scoping, and lifecycle hooks.
15
15
  *
@@ -51,7 +51,7 @@ export declare class DependencyResolver implements ResolverCallbacks {
51
51
  * shape it had; the container routes a top-level read with no options here.
52
52
  */
53
53
  resolveRootCollection<Value>(token: Token<Value> | Constructor<Value>): ReadonlyArray<Value>;
54
- /** The async twin of `resolveRootCollection`: a stable value list settles at once, candidates fan out as usual. */
54
+ /** The async twin of `resolveRootCollection`: a stable value list answers at once, candidates fan out as usual. */
55
55
  resolveRootCollectionAsync<Value>(token: Token<Value> | Constructor<Value>): Promise<ReadonlyArray<Value>>;
56
56
  resolveAsyncFromContext<Value>(token: Token<Value> | Constructor<Value>, resolutionStack: Array<ResolutionFrame>, branchDepth: BranchDepth): Promise<Value>;
57
57
  /**