Migration
InferDI records breaking changes by major version. The source of truth remains packages/inferdi/MIGRATION.md, but the current migration path is summarized here.
Migration to 5.0
v5 is an adapter release. The core package did not change. The version bump keeps all published packages in lockstep and aligns framework adapters around one cleanup contract.
Adapter contracts now share these rules:
createScope,setupScope,disposeScope,autoDispose, andonDisposeErroruse the same vocabulary.MaybePromise,InferdiScope,InferdiRoot, andInferdiScopeOfare exported across adapters.- If
setupScopefails, the adapter surfaces only the original setup error. - Cleanup failures during setup teardown go to
onDisposeErroror the adapter sink. - A failed request disposes its scope even after
skipInferdiDispose, except for the documented Express limitation. - Cleanup hooks see the public scope slot while they run.
Adapter Notes
| Package | Migration notes |
|---|---|
@inferdi/fastify | Rename logDisposeError to onDisposeError; InferdiScope.dispose() may return void or Promise<void>; disposeScope, autoDispose, skipInferdiDispose, and InferdiScopeOf were added. |
@inferdi/hono | Cleanup failures after next() are logged or sent to onDisposeError; they no longer replace a successful response. Setup teardown no longer throws AggregateError. |
@inferdi/express | onDisposeError is now a per-error sink for setup teardown and response completion. Express cannot force-dispose a skipped scope on a handled route error. |
@inferdi/koa | Setup teardown surfaces only the setup error. A downstream error disposes even after skipInferdiDispose(ctx). |
@inferdi/elysia | Setup teardown surfaces only the setup error. Cleanup failure goes to onDisposeError or console.error. |
Migration to 4.0
v4 tightens Lazy<T> lifetime semantics. A managed lazy companion now preserves the target lifetime. A singleton may inject only Lazy<singleton>.
Main changes:
AllowedDeps<T, 'singleton'>no longer accepts arbitraryLazy<V>.LazySpec<V, TargetKind>became a public type for explicit container and module shapes.- The runtime lazy exemption applies only when the target kind is
singleton. - A singleton that injected
Lazy<scoped>orLazy<transient>must change either the target lifetime or the consumer lifetime.
Common fixes:
// v3
.registerClass('req', RequestContext, [], 'scoped', 'reqLazy')
.registerClass('app', AppService, ['reqLazy'], 'singleton')
// v4: make the consumer scoped
.registerClass('req', RequestContext, [], 'scoped', 'reqLazy')
.registerClass('app', AppService, ['reqLazy'], 'scoped')// v3
type Deps = SpecMap<{ clock: Clock }> & {
clockLazy: Spec<Lazy<Clock>, 'transient'>
}
// v4
type Deps = SpecMap<{ clock: Clock }> & {
clockLazy: LazySpec<Clock, 'singleton'>
}Migration to 3.0
v3 moves lifetime safety into the type system. Runtime behavior stays compatible, and strict runtime guards remain defense-in-depth.
Main changes:
DependenciesMapentries becameSpec<V, Kind>instead of bare service types.RegistrationKind,Spec<V, K>, andSpecMap<M, K>became public exports.registerFactorynarrows itscparameter for singleton factories.registerClassfiltersdepsfor singleton registrations.override(key, value)preserves the original lifetime kind.new Container({ strict: false })can disable runtime cycle and lifetime guards after a graph audit.
Common fixes:
// v2
const c = new Container() as Container<{ a: A; b: B }>
// v3
const c = new Container() as Container<SpecMap<{ a: A; b: B }>>// v2
const mod: Module<{ cfg: Config }, { db: Db }> = (c) => ...
// v3
const mod: Module<
SpecMap<{ cfg: Config }>,
SpecMap<{ db: Db }>
> = (c) => ...Migration to 2.0
v2 has two mechanical breaking changes.
container.cradle was removed
Use .get(key):
// 1.x
const { db, logger } = container.cradle
// 2.x
const db = container.get('db')
const logger = container.get('logger')registerClass(..., lazy: true) became lazyKey
Pass the companion key:
// 1.x
.registerClass('clock', Clock, [], 'transient', true)
// 2.x
.registerClass('clock', Clock, [], 'transient', 'clockLazy')v2 also added string or symbol keys to every registration method and improved disposed-ancestor diagnostics.
Version Lockstep
All published InferDI packages share the same version:
When upgrading adapters, keep the adapter package and @inferdi/inferdi on matching major versions.
Upgrade Checklist
- Read the migration notes for every major version crossed.
- Upgrade
@inferdi/inferdiand all installed adapters together. - Run type tests or
tsc --noEmitto catch graph-shape changes. - Run runtime tests in strict mode.
- Review request-scope ownership if you use
skipInferdiDispose,autoDispose: false, or customdisposeScope.
Stable Boundaries
The core package remains decorator-free and zero-dependency. Framework lifecycle behavior lives in adapter packages, not in @inferdi/inferdi.
