Skip to content

Add first-class lazy class registration - #24

Open
kburov-sc wants to merge 1 commit into
mainfrom
kburov/lazy-class
Open

Add first-class lazy class registration#24
kburov-sc wants to merge 1 commit into
mainfrom
kburov/lazy-class

Conversation

@kburov-sc

@kburov-sc kburov-sc commented Aug 21, 2026

Copy link
Copy Markdown
Collaborator

Background

providesClass currently reads a class and its dependency metadata while registering the service. With cache-on-first-read module namespaces such as Valdi's lazy export loader, that lookup can evaluate modules even when the service is never resolved.

This adds a thunk form that defers class lookup until first resolution while preserving the existing direct-class API and its eager runtime behavior. The thunk defers ts-inject's lookup; it cannot defer evaluation of an ordinary static ESM import, because native ESM evaluates imports before registration code runs.

Change

  • Add lazy class providers across the class-registration surface:
    • Container.providesClass("service", () => Service)
    • PartialContainer.providesClass("service", () => Service)
    • appendClass("services", () => Service)
    • the exported low-level ClassInjectable
  • Cache the first successfully returned constructor so dependency metadata and construction always use the same class.
    • Direct Container registration avoids reading fn.dependencies until resolution.
    • Existing override, self-dependency, service memoization, and eager direct-class behavior remain unchanged.
    • Provider failures retry the provider; after a constructor is returned, dependency or construction failures retry service creation with that cached constructor.
  • Add runtime and type-level coverage for direct and partial containers, composition, Proxy-backed namespace reads, dependency validation, append behavior, overrides, self-dependencies, constructor identity, and error/retry semantics.
  • Extend npm run bench with 10, 100, and 1,000-class chains.
    • A cache-on-first-read Proxy loader records registration-time namespace reads and export evaluations.
    • An already-loaded-class case isolates portable thunk/caching overhead.
    • Results use 5 warmups and medians from 15 samples and are recorded in benchmarks/LAZY_CLASS_RESULTS.md.
    • Lazy registration reduced simulated registration reads and evaluations from one per class to zero. The deferred work appears in cold resolution, while memoized get() performance remains effectively unchanged; ordinary already-loaded classes do not show a general performance win.
  • Bump the library version from 1.0.1 to 1.1.0 for the additive API release.

Test Plan

  • npm test -- --runInBand — 6 suites and 118 tests pass with 100% coverage.
  • npm run compile
  • npm run styleguide
  • npm run bench

@kburov-sc
kburov-sc marked this pull request as ready for review August 21, 2026 06:36
Add lazy class-provider overloads across Container, PartialContainer,
appendClass, and ClassInjectable while preserving existing eager behavior.

Defer class lookup and dependency metadata until first resolution, cache the
returned constructor, and retain existing override, self-dependency, retry,
and service memoization semantics.

Add runtime and type-level coverage, lazy-loader benchmarks and documentation,
and bump the library version to 1.1.0.

@ecoronadosc ecoronadosc left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good to me.
I would add a caveat somewhere that this is beneficial for lazy evaluated module loaders, but the common web ones may not be.. however having it is a win regardless.

@kburov-sc

Copy link
Copy Markdown
Collaborator Author

🤖 From AI Assistant — Review 4996399000: This caveat is documented in the README’s “Lazy Class Lookup” section, which contrasts Valdi-style lazy export lookup with ordinary static ESM evaluation. The benchmark results also call out that already-loaded/static ESM classes do not show a general performance win.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants