@codefast/di 0.12.0 → 0.13.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.
@@ -9,7 +9,7 @@ import { ActivationNeedCache } from "#resolution/cache/activation-need";
9
9
  import { ClassIntrospector } from "#resolution/cache/class-introspector";
10
10
  import { BindingLookupCache } from "#resolution/cache/lookup";
11
11
  import { AsyncLevelContext, DefaultConstraintContext, DefaultResolutionContext } from "#resolution/context";
12
- import { branchDepthOf, buildResolutionFrame, cycleNamesOf, enterSyncPath, extendResolutionBranch, leaveSyncPath, ROOT_BRANCH, UNOWNED_BRANCH, } from "#resolution/path";
12
+ import { branchDepthOf, buildResolutionFrame, cycleNamesOf, enterSyncPath, extendResolutionBranch, leaveSyncPath, missPathOf, ROOT_BRANCH, UNOWNED_BRANCH, } from "#resolution/path";
13
13
  import { InstantiationPlanCompiler, PLAN_RETRY } from "#resolution/plan/compiler";
14
14
  import { matchesSlot, selectAllBindings, selectBinding } from "#resolution/select/candidates";
15
15
  const EMPTY_FRAME_LIST = [];
@@ -252,10 +252,11 @@ export class DependencyResolver {
252
252
  // dominated by that capture. Bindings under the token anywhere in the chain mean the request
253
253
  // matched none of them, so a child reports the same miss its parent would.
254
254
  const bound = this.#allBindingsFromChain(currentToken);
255
+ const path = missPathOf(resolutionStack, token, visitedAliasTokens);
255
256
  if (bound.length > 0) {
256
- throw new NoMatchingBindingError(tokenName(currentToken), options ?? {}, bound.map((binding) => bindingSlotToString(binding.slot)));
257
+ throw new NoMatchingBindingError(tokenName(currentToken), options ?? {}, bound.map((binding) => bindingSlotToString(binding.slot)), path);
257
258
  }
258
- throw new TokenNotBoundError(tokenName(currentToken));
259
+ throw new TokenNotBoundError(tokenName(currentToken), path);
259
260
  }
260
261
  return found;
261
262
  }
@@ -295,6 +296,36 @@ export class DependencyResolver {
295
296
  peekCandidateBindingsForValidate(token, options) {
296
297
  return this.#candidateBindings(token, options, []);
297
298
  }
299
+ /**
300
+ * The error `resolve` raises for a request nothing selects, or `undefined` while a `when()` candidate leaves it open.
301
+ *
302
+ * @remarks A predicate reads the resolution path, which a static walk does not have, so a slot-matching candidate
303
+ * that carries one may still be selected when the request is really made.
304
+ */
305
+ missForValidate(token, options, path) {
306
+ const bound = this.#allBindingsFromChain(token);
307
+ if (bound.length === 0) {
308
+ return new TokenNotBoundError(tokenName(token), path);
309
+ }
310
+ for (const binding of bound) {
311
+ if (binding.predicate !== undefined && !binding.isMany && matchesSlot(binding.slot, options)) {
312
+ return undefined;
313
+ }
314
+ }
315
+ return new NoMatchingBindingError(tokenName(token), options ?? {}, bound.map((binding) => bindingSlotToString(binding.slot)), path);
316
+ }
317
+ /** The resolver whose chain a binding this one can see resolves its dependencies from, as a singleton does. */
318
+ ownerForValidate(binding) {
319
+ return this.#ownerOf(binding);
320
+ }
321
+ /** Deactivates a scoped instance of a binding this resolver owns, with its hooks and reader, when it owes a teardown. */
322
+ deactivateScoped(binding, instance) {
323
+ const lifecycle = this.#lifecycle;
324
+ const reader = this.#metadataReader;
325
+ return lifecycle.owesDeactivation(binding, reader)
326
+ ? lifecycle.runDeactivation(binding, instance, reader)
327
+ : undefined;
328
+ }
298
329
  // ── Sync resolve ───────────────────────────────────────────────────────────────────────────────────────────────────
299
330
  resolveFromContext(token, resolutionStack) {
300
331
  // Hot lane: own-registry fast default. Fall back to the chain-versioned memo
@@ -647,7 +678,7 @@ export class DependencyResolver {
647
678
  this.#scope.setSingleton(binding, activated);
648
679
  }
649
680
  else if (scope === "scoped") {
650
- this.#scope.setScoped(binding, activated);
681
+ this.#scope.setScoped(binding, activated, owner);
651
682
  }
652
683
  return activated;
653
684
  }
@@ -1117,13 +1148,13 @@ export class DependencyResolver {
1117
1148
  return activated;
1118
1149
  }
1119
1150
  if (isPending(activated)) {
1120
- return this.#publishInFlight(binding, activated, scope);
1151
+ return this.#publishInFlight(binding, activated, scope, owner);
1121
1152
  }
1122
1153
  if (scope === "singleton") {
1123
1154
  this.#scope.setSingleton(binding, activated);
1124
1155
  }
1125
1156
  else {
1126
- this.#scope.setScoped(binding, activated);
1157
+ this.#scope.setScoped(binding, activated, owner);
1127
1158
  }
1128
1159
  return activated;
1129
1160
  }
@@ -1131,13 +1162,13 @@ export class DependencyResolver {
1131
1162
  * Publishes a pending singleton or scoped materialisation so concurrent callers dedup onto it,
1132
1163
  * and caches what it settles to; the creator, like every joiner, is handed a promise of its own.
1133
1164
  */
1134
- #publishInFlight(binding, pending, scope) {
1165
+ #publishInFlight(binding, pending, scope, owner) {
1135
1166
  const published = pending.then((activated) => {
1136
1167
  if (scope === "singleton") {
1137
1168
  this.#scope.setSingleton(binding, activated);
1138
1169
  }
1139
1170
  else {
1140
- this.#scope.setScoped(binding, activated);
1171
+ this.#scope.setScoped(binding, activated, owner);
1141
1172
  }
1142
1173
  this.#scope.clearInflight(binding.identifier);
1143
1174
  return activated;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@codefast/di",
3
- "version": "0.12.0",
4
- "description": "Lightweight dependency injection primitives for Codefast",
3
+ "version": "0.13.0",
4
+ "description": "Type-safe, zero-dependency dependency injection for TypeScript, on standard decorators",
5
5
  "keywords": [
6
6
  "codefast",
7
7
  "dependency-injection",