@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.
- package/CHANGELOG.md +40 -0
- package/README.md +102 -19
- package/dist/container/container.d.ts +0 -1
- package/dist/container/container.js +72 -41
- package/dist/core/binding-builders.d.ts +5 -2
- package/dist/core/binding-declaration.d.ts +2 -2
- package/dist/core/binding-declaration.js +2 -2
- package/dist/decorators/inject.d.ts +3 -1
- package/dist/decorators/lifecycle.d.ts +9 -3
- package/dist/decorators/lifecycle.js +5 -1
- package/dist/errors.d.ts +22 -13
- package/dist/errors.js +35 -18
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/introspection/explanation.js +26 -8
- package/dist/lifecycle/hooks.d.ts +2 -0
- package/dist/lifecycle/hooks.js +6 -0
- package/dist/lifecycle/scopes.d.ts +21 -2
- package/dist/lifecycle/scopes.js +86 -2
- package/dist/metadata/keys.d.ts +1 -1
- package/dist/metadata/keys.js +1 -1
- package/dist/resolution/path.d.ts +12 -2
- package/dist/resolution/path.js +22 -2
- package/dist/resolution/resolver.d.ts +12 -0
- package/dist/resolution/resolver.js +39 -8
- package/package.json +2 -2
|
@@ -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.
|
|
4
|
-
"description": "
|
|
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",
|