Versionsmigration
InferDI dokumentiert inkompatible Änderungen nach Hauptversion. Maßgeblich bleibt packages/inferdi/MIGRATION.md; diese Seite fasst den aktuellen Upgrade-Pfad zusammen.
Migration auf 6.0
Diese Zusammenfassung geht von der stabilen Version 5.0.7 aus. Aktualisiere alle installierten @inferdi/*-Pakete auf 6.0.0; die Adapter benötigen @inferdi/inferdi@^6.0.0.
- Ersetze
RegistrationKinddurchLifetimeundSpec.kinddurchSpec.lifetime. Es gibt keine veralteten Aliase. - Das stabile v5 hatte keine
registerFactory-Überladung mitdeps. V6 ergänztregisterFactory(key, factory, deps, ...). Nur Nutzer der v6-Vorabversion mitregisterFactory(key, deps, factory, ...)müssen Argumente umordnen. Ein synchroner Factory-Begleiter verlangt eine explizite Lebensdauer, auch'singleton'. - Ersetze v5
{strict: false}durch{fast: true}und{strict: true}durch den Standard oder{fast: false}. Die Boolesche Bedeutung ist umgekehrt. Die Voraboptionmodewurde entfernt. - Benannte
Module<TRequirements, TProvides>akzeptieren und erhalten zusätzliche Registrierungen, prüfen Anforderungen exakt und verbieten Ausgabekollisionen.new Container(parent)ist nicht mehr öffentlich; nutzecreateScope(). - Registrierung verbietet jeden Schlüsseltyp, der einen vorhandenen Haupt- oder Lazy-Schlüssel überlappen könnte. Grenze breite oder Union-Schlüssel auf einen neuen Wert ein; für gezielten Ersatz nutze
.override(). - V6 ergänzt rein typseitige Scope-Eingaben mit
declareScopeInputs<Inputs>()undcreateScope(inputs). Scopes ohne Argumente behalten ihr v5-Verhalten. - V6 ergänzt
registerAsyncFactory,AsyncSpecundgetAsync()für deklarative asynchrone Abhängigkeiten. Promise-Werte vonregisterFactorybleiben synchrone Grapheneinträge mitget(). - Asynchrone Freigabe meldet dasselbe Ablehnungsobjekt einmal, wenn ein Abhängigkeitsfehler mehrere gecachte Promises betrifft. Synchrone Freigabe beobachtet native Promise-Ablehnungen vor dem Fehler über asynchronen Fehlgebrauch.
Generische Resolver verwenden bereite Schlüssel
.get() akzeptiert jetzt bereite synchrone Schlüssel mit erfüllten Scope-Anforderungen. Konkrete Container ohne Scope-Eingaben behalten dieselbe synchrone Schlüsselmenge. Generische Helfer mit K extends keyof T müssen Bereitschaft und Async-Status erhalten.
// Before
function resolve<T extends DependenciesMap, K extends keyof T>(
container: Container<T>,
key: K
) {
return container.get(key)
}
// After
function resolve<
T extends DependenciesMap,
K extends Container.SyncReadyKeys<Container<T>>
>(container: Container<T>, key: K) {
return container.get(key)
}Nutze Container.ReadyKeys<Container<T>> in generischen Helfern für getAsync(). Ein generisches T extends DependenciesMap kann deklarative asynchrone Einträge oder durch fehlende Scope-Eingaben blockierte Services enthalten.
Benannte Specs für Lazy-Begleiter
LazySpec trägt nun eine private rein typseitige Modusmarke; v6 ergänzt AsyncLazySpec. Explizite Container- und Module-Typen müssen diese Exporte nutzen, statt {type, lifetime, lazyOf} nachzubauen. Die Marke besitzt kein Laufzeitfeld.
registerAsyncFactory akzeptiert ein fünftes lazyKey und erzeugt AsyncLazy<T>. Asynchron propagierte Klassen nutzen denselben Wrapper; gemischte Klassen liefern Lazy<T> | AsyncLazy<T>. Begleiter Promise-wertiger registerFactory bleiben Lazy<Promise<T>>. Container.ResolveUnwrapped entpackt verwaltete synchrone, asynchrone und gemischte Begleiter distributiv.
API-Übersicht, Scope-Eingaben und Asynchrone Abhängigkeiten beschreiben die neuen Schlüsselmengen.
Migration auf 5.0
Die erste v5-Veröffentlichung betraf nur Adapter. Der Versionssprung hält alle Pakete synchron und vereinheitlicht ihren Freigabevertrag. Spätere v5-Builds verschärften auch die Verantwortung von Kind-Scopes und den unten beschriebenen {fast: true}-Vertrag.
Die Adapter teilen nun diese Regeln:
- Gemeinsame Begriffe:
createScope,setupScope,disposeScope,autoDispose,onDisposeError. - Gemeinsame Exporte:
MaybePromise,InferdiScope,InferdiRoot,InferdiScopeOf. - Scheitert
setupScope, bleibt nur der ursprüngliche Setup-Fehler sichtbar. - Freigabefehler während eines Setup-Abbruchs gehen an
onDisposeErroroder den Adapterempfänger. - Fehlgeschlagene Requests geben trotz
skipInferdiDisposefrei, mit der dokumentierten Express-Ausnahme. - Freigabehooks sehen während ihrer Ausführung den öffentlichen Scope-Zugriff.
Scoped Auflösung benötigt einen Kind-Scope
Bei standardmäßigem {fast: false} wirft scoped Auflösung aus dem Root nun Scoped "key" cannot be resolved from the root container. Use createScope(). Erstelle mit const scope = root.createScope() ein Kind, rufe scope.get(scopedKey) auf und gib es an seiner Lebenszyklusgrenze frei. {fast: true} überspringt diese Laufzeitprüfung. Anwendungscode muss scoped Schlüssel dennoch aus Kind-Scopes auflösen.
Fester Graph mit fast: true
new Container({fast: true}) liest aus Scopes direkt die unveränderbare Root-Registrierung, vermeidet Elternketten und spiegelt delegierte Singletons im Scope-Cache. Standard-Scopes durchlaufen bei jedem lokalen Cachemiss ihre exakte Elternkette, statt Suchergebnisse zu speichern. Änderungen bleiben ohne Invalidierungsbuchführung oder zusätzliche Suchmetadaten sichtbar. Beide Modi deduplizieren eigene Instanzen bei der Freigabe. Registriere jeden Laufzeitschlüssel einmal in einer linearen Kette, beende den Aufbau vor der ersten Auflösung oder Scope-Erstellung, halte den aktivierten Baum unveränderbar und gib Kinder vor Vorfahren frei. Für Hot Reload oder veränderbare Bäume nutze {fast: false}.
Adapterhinweise
| Paket | Migrationshinweise |
|---|---|
@inferdi/fastify | logDisposeError wird onDisposeError; InferdiScope.dispose() darf void oder Promise<void> liefern. Neu: disposeScope, autoDispose, skipInferdiDispose, InferdiScopeOf. |
@inferdi/hono | Freigabefehler nach next() werden protokolliert oder an onDisposeError gesendet; sie ersetzen keine erfolgreiche Antwort mehr. Setup-Freigabe wirft kein AggregateError mehr. |
@inferdi/express | onDisposeError empfängt einzelne Fehler bei Setup-Freigabe und Antwortabschluss. Ein übersprungener Scope kann bei behandeltem Routenfehler nicht zwangsweise freigegeben werden. |
@inferdi/koa | Setup-Freigabe zeigt nur den Setup-Fehler. Ein nachgelagerter Fehler gibt auch nach skipInferdiDispose(ctx) frei. |
@inferdi/elysia | Setup-Freigabe zeigt nur den Setup-Fehler. Freigabefehler gehen an onDisposeError oder console.error. |
Migration auf 4.0
V4 präzisiert die Lebensdauer von Lazy<T>. Ein verwalteter Begleiter behält die Ziellebensdauer; ein Singleton darf nur Lazy<singleton> injizieren.
Die wichtigsten Änderungen:
AllowedDeps<T, 'singleton'>akzeptiert kein beliebigesLazy<V>mehr.LazySpec<V, TargetKind>wird öffentlich für explizite Container- und Modultypen.- Die Lazy-Ausnahme zur Laufzeit gilt nur für Zielart
singleton. - Ein Singleton mit
Lazy<scoped>oderLazy<transient>muss die Lebensdauer des Ziels oder des Verbrauchers ändern.
Typische Anpassungen:
// 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 auf 3.0
V3 verlagert Lebensdauersicherheit ins Typsystem. Das Laufzeitverhalten bleibt kompatibel; Standardprüfungen dienen weiter als zusätzliche Absicherung.
Die wichtigsten Änderungen:
DependenciesMapverwendetSpec<V, Kind>statt bloßer Servicetypen.RegistrationKind,Spec<V, K>undSpecMap<M, K>werden öffentlich exportiert.registerFactorybeschränktcbei Singleton-Factories.registerClassfiltertdepsfür Singleton-Registrierungen.override(key, value)erhält die ursprüngliche Lebensdauer.new Container({fast: true})kann nach Graphenprüfung Laufzeitprüfungen für Zyklen und Lebensdauern deaktivieren.
Typische Anpassungen:
// 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 auf 2.0
V2 enthält zwei mechanisch umsetzbare inkompatible Änderungen.
container.cradle wurde entfernt
Nutze .get(key):
// 1.x
const { db, logger } = container.cradle
// 2.x
const db = container.get('db')
const logger = container.get('logger')registerClass(..., lazy: true) wurde zu lazyKey
Übergib den Begleitschlüssel:
// 1.x
.registerClass('clock', Clock, [], 'transient', true)
// 2.x
.registerClass('clock', Clock, [], 'transient', 'clockLazy')V2 ergänzte außerdem String- und Symbolschlüssel bei allen Registrierungsmethoden und verbesserte Diagnosen für freigegebene Vorfahren.
Einheitliche Versionen
Alle veröffentlichten InferDI-Pakete tragen dieselbe Version:
@inferdi/inferdi@inferdi/fastify@inferdi/hono@inferdi/koa@inferdi/express@inferdi/elysia@inferdi/react
Halte beim Upgrade Adapter und @inferdi/inferdi auf derselben Hauptversion.
Upgrade-Checkliste
- Lies die Hinweise zu jeder übersprungenen Hauptversion.
- Aktualisiere
@inferdi/inferdiund alle installierten Adapter gemeinsam. - Führe Typtests oder
tsc --noEmitaus, um Graphenänderungen zu erkennen. - Führe Laufzeittests im geprüften Standardmodus aus.
- Prüfe die Scope-Verantwortung bei
skipInferdiDispose,autoDispose: falseoder eigenemdisposeScope.
Stabile Grenzen
Der Kern bleibt dekoratorfrei und ohne Abhängigkeiten. Framework-Lebenszyklen gehören in Adapterpakete, nicht in @inferdi/inferdi.
