@codefast/di 0.10.0 → 0.10.1

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 (68) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/README.md +3 -2
  3. package/dist/ambient/active-container.d.ts +6 -9
  4. package/dist/ambient/active-container.js +2 -5
  5. package/dist/container/binding-builders.d.ts +7 -7
  6. package/dist/container/binding-builders.js +36 -27
  7. package/dist/container/container.d.ts +8 -8
  8. package/dist/container/container.js +20 -20
  9. package/dist/core/binding-scope.d.ts +2 -2
  10. package/dist/core/binding.d.ts +6 -6
  11. package/dist/core/binding.js +1 -1
  12. package/dist/core/constraint-requirement.d.ts +1 -1
  13. package/dist/core/module.d.ts +3 -3
  14. package/dist/core/registry.d.ts +6 -6
  15. package/dist/core/registry.js +36 -24
  16. package/dist/core/token.d.ts +1 -1
  17. package/dist/core/types.d.ts +5 -5
  18. package/dist/decorators/inject.d.ts +3 -3
  19. package/dist/decorators/inject.js +5 -5
  20. package/dist/decorators/injectable.d.ts +2 -2
  21. package/dist/decorators/injectable.js +2 -2
  22. package/dist/decorators/lifecycle-decorators.js +2 -2
  23. package/dist/errors/errors.d.ts +8 -11
  24. package/dist/errors/errors.js +7 -10
  25. package/dist/index.d.ts +35 -35
  26. package/dist/index.js +19 -19
  27. package/dist/injection/descriptor.d.ts +3 -3
  28. package/dist/injection/resolve-options.d.ts +3 -3
  29. package/dist/injection/resolve-options.js +1 -1
  30. package/dist/introspection/dependency-graph.d.ts +3 -3
  31. package/dist/introspection/dependency-graph.js +5 -5
  32. package/dist/introspection/graph-adapters/cytoscape.d.ts +1 -1
  33. package/dist/introspection/graph-adapters/dot.d.ts +1 -1
  34. package/dist/introspection/graph-adapters/mermaid.d.ts +1 -1
  35. package/dist/introspection/graph-adapters/reactflow.d.ts +1 -1
  36. package/dist/introspection/inspector.d.ts +4 -4
  37. package/dist/introspection/inspector.js +3 -3
  38. package/dist/lifecycle/lifecycle-manager.d.ts +4 -4
  39. package/dist/lifecycle/lifecycle-manager.js +4 -4
  40. package/dist/lifecycle/scope-manager.d.ts +2 -2
  41. package/dist/lifecycle/scope-manager.js +3 -3
  42. package/dist/metadata/metadata-reader-token.d.ts +2 -2
  43. package/dist/metadata/metadata-reader-token.js +1 -1
  44. package/dist/metadata/metadata-types.d.ts +3 -3
  45. package/dist/metadata/symbol-metadata-reader.d.ts +3 -3
  46. package/dist/metadata/symbol-metadata-reader.js +1 -1
  47. package/dist/metadata/verifying-metadata-reader.d.ts +1 -1
  48. package/dist/metadata/verifying-metadata-reader.js +2 -2
  49. package/dist/resolution/cache/activation-need.d.ts +4 -4
  50. package/dist/resolution/cache/binding-lookup-cache.d.ts +5 -5
  51. package/dist/resolution/cache/binding-lookup-cache.js +2 -2
  52. package/dist/resolution/cache/class-introspector.d.ts +5 -5
  53. package/dist/resolution/cache/class-introspector.js +55 -56
  54. package/dist/resolution/context.d.ts +3 -3
  55. package/dist/resolution/context.js +1 -1
  56. package/dist/resolution/path/resolution-path.d.ts +1 -1
  57. package/dist/resolution/path/resolution-path.js +1 -1
  58. package/dist/resolution/plan/instantiation-plan.d.ts +5 -5
  59. package/dist/resolution/plan/instantiation-plan.js +5 -5
  60. package/dist/resolution/plan/plan-codegen.d.ts +3 -3
  61. package/dist/resolution/plan/plan-codegen.js +2 -2
  62. package/dist/resolution/resolver.d.ts +12 -12
  63. package/dist/resolution/resolver.js +33 -20
  64. package/dist/resolution/select/binding-select.d.ts +6 -5
  65. package/dist/resolution/select/binding-select.js +5 -4
  66. package/dist/resolution/select/constraints.d.ts +3 -3
  67. package/dist/resolution/select/constraints.js +4 -4
  68. package/package.json +3 -3
@@ -1,7 +1,7 @@
1
1
  /** The `ResolutionContext` a factory is handed, and the callbacks the resolver answers it with. */
2
- import type { Token } from "#/core/token";
3
- import type { BindingIdentifier, BindingKind, BindingScope, ConstraintContext, Constructor, ResolutionFrame, ResolutionContext, ResolveOptions } from "#/core/types";
4
- import type { BranchDepth, OwnedBranchStack } from "#/resolution/path/resolution-path";
2
+ import type { Token } from "#core/token";
3
+ import type { BindingIdentifier, BindingKind, BindingScope, ConstraintContext, Constructor, ResolutionFrame, ResolutionContext, ResolveOptions } from "#core/types";
4
+ import type { BranchDepth, OwnedBranchStack } from "#resolution/path/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 { UNOWNED_BRANCH } from "#/resolution/path/resolution-path";
1
+ import { UNOWNED_BRANCH } from "#resolution/path/resolution-path";
2
2
  /**
3
3
  * The `ResolutionContext` handed to factories and hooks, backed by the engine's callbacks.
4
4
  *
@@ -1,4 +1,4 @@
1
- import type { BindingIdentifier, ResolutionFrame } from "#/core/types";
1
+ import type { BindingIdentifier, ResolutionFrame } from "#core/types";
2
2
  /**
3
3
  * Where the cycle check switches from a linear frame scan to an attached Set.
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/errors";
3
3
  const RESOLUTION_SET_KEY = Symbol("di:resolution-set");
4
4
  /**
5
5
  * Where the cycle check switches from a linear frame scan to an attached Set.
@@ -4,11 +4,11 @@
4
4
  * @remarks A dependency the compiler cannot inline escapes to a resolver callback instead; the
5
5
  * closure must stay callable for it, which is what bounds what may be inlined.
6
6
  */
7
- import type { Binding } from "#/core/binding";
8
- import type { ConstructorInvocation } from "#/core/constructor-type";
9
- import type { Token } from "#/core/token";
10
- import type { Constructor, ResolutionFrame, ResolveOptions } from "#/core/types";
11
- import type { ConstructorMetadata } from "#/metadata/metadata-types";
7
+ import type { Binding } from "#core/binding";
8
+ import type { ConstructorInvocation } from "#core/constructor-type";
9
+ import type { Token } from "#core/token";
10
+ import type { Constructor, ResolutionFrame, ResolveOptions } from "#core/types";
11
+ import type { ConstructorMetadata } from "#metadata/metadata-types";
12
12
  /**
13
13
  * Compilation asked to retry later (class lifecycle metadata not discovered yet).
14
14
  *
@@ -1,8 +1,8 @@
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";
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
6
  // Past this depth a dependency escapes to the runtime path rather than inlining further —
7
7
  // compiled closures nest one JS frame per level, and pathological graphs are the runtime's job.
8
8
  const PLAN_DEPTH_LIMIT = 32;
@@ -1,10 +1,10 @@
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
9
  * @remarks Below it a plan stays a closure, which is all a cold container or a per-request child
10
10
  * ever runs; above it a plan pays one compile for call sites nothing else feeds.
@@ -1,6 +1,6 @@
1
- import { NO_INSTANCE } from "#/core/binding";
1
+ import { NO_INSTANCE } from "#core/binding";
2
2
  /**
3
- * Runs a plan's closure makes before the plan is generated as its own function.
3
+ * The number of runs a plan's closure makes before the plan is generated as its own function.
4
4
  *
5
5
  * @remarks Below it a plan stays a closure, which is all a cold container or a per-request child
6
6
  * ever runs; above it a plan pays one compile for call sites nothing else feeds.
@@ -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
  *
@@ -1,16 +1,16 @@
1
- import { NO_INSTANCE } from "#/core/binding";
2
- import { NO_TAG_KEYS, slotNameCriterionOf } from "#/core/tag";
3
- import { tokenName } from "#/core/token";
4
- import { AsyncActivationError, AsyncResolutionError, CircularDependencyError, DisposedContainerError, InternalError, MissingMetadataError, MissingScopeContextError, NoMatchingBindingError, TokenNotBoundError, } from "#/errors/errors";
5
- import { loneTagBesideNameOf, resolveOptionsForSlot, singleCriterionForSlot, singleCriterionOnlyOf, } from "#/injection/resolve-options";
6
- import { SCOPED_MISS } from "#/lifecycle/scope-manager";
7
- import { ActivationNeedCache } from "#/resolution/cache/activation-need";
8
- import { BindingLookupCache } from "#/resolution/cache/binding-lookup-cache";
9
- import { ClassIntrospector } from "#/resolution/cache/class-introspector";
10
- import { AsyncCascadeContext, AsyncLevelContext, buildResolutionFrame, DefaultResolutionContext, } from "#/resolution/context";
11
- import { branchDepthOf, cycleNamesOf, enterResolutionPath, extendResolutionBranch, ROOT_BRANCH, UNOWNED_BRANCH, } from "#/resolution/path/resolution-path";
12
- import { InstantiationPlanCompiler, PLAN_RETRY } from "#/resolution/plan/instantiation-plan";
13
- import { matchesSlot, requestedTagKeyMask, selectAllBindings, selectBinding } from "#/resolution/select/binding-select";
1
+ import { NO_INSTANCE } from "#core/binding";
2
+ import { NO_TAG_KEYS, slotNameCriterionOf } from "#core/tag";
3
+ import { tokenName } from "#core/token";
4
+ import { AsyncActivationError, AsyncResolutionError, CircularDependencyError, DisposedContainerError, InternalError, MissingMetadataError, MissingScopeContextError, NoMatchingBindingError, TokenNotBoundError, } from "#errors/errors";
5
+ import { loneTagBesideNameOf, resolveOptionsForSlot, singleCriterionForSlot, singleCriterionOnlyOf, } from "#injection/resolve-options";
6
+ import { SCOPED_MISS } from "#lifecycle/scope-manager";
7
+ import { ActivationNeedCache } from "#resolution/cache/activation-need";
8
+ import { BindingLookupCache } from "#resolution/cache/binding-lookup-cache";
9
+ import { ClassIntrospector } from "#resolution/cache/class-introspector";
10
+ import { AsyncCascadeContext, AsyncLevelContext, buildResolutionFrame, DefaultResolutionContext, } from "#resolution/context";
11
+ import { branchDepthOf, cycleNamesOf, enterResolutionPath, extendResolutionBranch, ROOT_BRANCH, UNOWNED_BRANCH, } from "#resolution/path/resolution-path";
12
+ import { InstantiationPlanCompiler, PLAN_RETRY } from "#resolution/plan/instantiation-plan";
13
+ import { matchesSlot, requestedTagKeyMask, selectAllBindings, selectBinding } from "#resolution/select/binding-select";
14
14
  // Where a multi-tag resolve switches from scanning the token's list to walking the tag indexes.
15
15
  const MULTI_TAG_INDEX_THRESHOLD = 8;
16
16
  const EMPTY_STRING_LIST = [];
@@ -411,7 +411,12 @@ export class DependencyResolver {
411
411
  // that never resolves a class or a resolved factory never pays for the host.
412
412
  #planCompiler;
413
413
  #compiler() {
414
- return (this.#planCompiler ??= new InstantiationPlanCompiler({
414
+ return (this.#planCompiler ??= new InstantiationPlanCompiler(this.#buildPlanCompilerHost()));
415
+ }
416
+ // The behaviour the plan compiler needs from this resolver — lookups, escapes, plan swaps, and the
417
+ // accessor construction path. Built once with the compiler, so each closure is allocated once.
418
+ #buildPlanCompilerHost() {
419
+ return {
415
420
  hasActivationHandlers: (binding) => this.#ownerOf(binding).#lifecycle.hasActivationHandlers(binding.token),
416
421
  knownPostConstruct: (target) => this.#classes.knownPostConstruct(target),
417
422
  needsActiveContainer: (target) => this.#classes.needsActiveContainer(target),
@@ -488,7 +493,7 @@ export class DependencyResolver {
488
493
  }
489
494
  return this.resolveAsync(token, options, resolutionStack, UNOWNED_BRANCH);
490
495
  },
491
- }));
496
+ };
492
497
  }
493
498
  /** The async lane's plan for a statically-visible transient binding, mirroring the sync getter. */
494
499
  #getAsyncInstantiationPlan(binding) {
@@ -608,9 +613,12 @@ export class DependencyResolver {
608
613
  resolutionSet?.delete(frame.bindingId);
609
614
  }
610
615
  }
611
- /** Path-continuing resolution handed to the ambient slot while an accessor class constructs. */
612
- // The ambient resolution a top-level construction hands its accessors: the lent root stack is one
613
- // array for the resolver's lifetime, so the pair of closures over it is built once and reused.
616
+ /**
617
+ * Path-continuing resolution handed to the ambient slot while an accessor class constructs.
618
+ *
619
+ * @remarks The lent root stack is one array for the resolver's lifetime, so the closure pair over
620
+ * it is built once and reused.
621
+ */
614
622
  #rootAmbientResolution;
615
623
  #ambientResolutionFor(resolutionStack) {
616
624
  if (resolutionStack === this.rootStack) {
@@ -1464,8 +1472,13 @@ export class DependencyResolver {
1464
1472
  return created;
1465
1473
  }
1466
1474
  }
1467
- /** A constant whose value is its answer on every read: no own hook, and the caller has ruled out container hooks. */
1468
- // A cached singleton reads like a constant until a registry change evicts it, which also drops the memo.
1475
+ /**
1476
+ * Whether a binding answers a collection read with a fixed value: a hook-free constant, or a
1477
+ * singleton whose instance is already cached.
1478
+ *
1479
+ * @remarks A cached singleton reads like a constant until a registry change evicts it, which also
1480
+ * drops the memo.
1481
+ */
1469
1482
  function isStableCollectionMember(binding) {
1470
1483
  if (binding.kind === "alias" || binding.activationHook !== undefined) {
1471
1484
  return false;
@@ -1,9 +1,10 @@
1
- import type { Binding, BindingSlot } from "#/core/binding";
2
- import type { TagKeyMask } from "#/core/tag";
3
- import type { ConstraintContext, ResolveOptions } from "#/core/types";
1
+ import type { Binding, BindingSlot } from "#core/binding";
2
+ import type { TagKeyMask } from "#core/tag";
3
+ import type { ConstraintContext, ResolveOptions } from "#core/types";
4
4
  /**
5
- * Selects a single candidate from a list of bindings using slot matching + predicates.
6
- * Returns undefined if no match, throws AmbiguousBindingError if multiple match.
5
+ * The single candidate a request selects from a list, by slot match then predicate.
6
+ *
7
+ * @remarks `undefined` when nothing matches; throws {@link AmbiguousBindingError} when several do.
7
8
  *
8
9
  * @since 0.3.16-canary.0
9
10
  */
@@ -1,8 +1,9 @@
1
- import { coversTagKeys, NO_TAG_KEYS, slotName, slotNameCriterionOf } from "#/core/tag";
2
- import { AmbiguousBindingError } from "#/errors/errors";
1
+ import { coversTagKeys, NO_TAG_KEYS, slotName, slotNameCriterionOf } from "#core/tag";
2
+ import { AmbiguousBindingError } from "#errors/errors";
3
3
  /**
4
- * Selects a single candidate from a list of bindings using slot matching + predicates.
5
- * Returns undefined if no match, throws AmbiguousBindingError if multiple match.
4
+ * The single candidate a request selects from a list, by slot match then predicate.
5
+ *
6
+ * @remarks `undefined` when nothing matches; throws {@link AmbiguousBindingError} when several do.
6
7
  *
7
8
  * @since 0.3.16-canary.0
8
9
  */
@@ -1,6 +1,6 @@
1
- import type { BindingTag } from "#/core/tag";
2
- import type { Token } from "#/core/token";
3
- import type { BindingConstraint, Constructor } from "#/core/types";
1
+ import type { BindingTag } from "#core/tag";
2
+ import type { Token } from "#core/token";
3
+ import type { BindingConstraint, Constructor } from "#core/types";
4
4
  /**
5
5
  * Matches when the direct parent frame resolves the given token.
6
6
  *
@@ -1,7 +1,7 @@
1
- import { requiringAncestorSlotName, requiringAncestorSlotNames } from "#/core/constraint-requirement";
2
- import { coversTagKeys, slotName, tagKeyMaskOf } from "#/core/tag";
3
- import { tokenName } from "#/core/token";
4
- import { EmptyTagCriteriaError } from "#/errors/errors";
1
+ import { requiringAncestorSlotName, requiringAncestorSlotNames } from "#core/constraint-requirement";
2
+ import { coversTagKeys, slotName, tagKeyMaskOf } from "#core/tag";
3
+ import { tokenName } from "#core/token";
4
+ import { EmptyTagCriteriaError } from "#errors/errors";
5
5
  /**
6
6
  * Matches when the direct parent frame resolves the given token.
7
7
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codefast/di",
3
- "version": "0.10.0",
3
+ "version": "0.10.1",
4
4
  "description": "Lightweight dependency injection primitives for Codefast",
5
5
  "keywords": [
6
6
  "codefast",
@@ -32,7 +32,7 @@
32
32
  "module": "./dist/index.js",
33
33
  "types": "./dist/index.d.ts",
34
34
  "imports": {
35
- "#/*": {
35
+ "#*": {
36
36
  "types": "./dist/*.d.ts",
37
37
  "default": "./dist/*.js"
38
38
  }
@@ -224,6 +224,6 @@
224
224
  "access": "public"
225
225
  },
226
226
  "engines": {
227
- "node": ">=24.0.0"
227
+ "node": ">=22.12.0"
228
228
  }
229
229
  }