2.5 KiB
upcaster
(no submodules)
Purpose
Provides event schema migration via a registry-based upcaster pattern. When an aggregate's event payloads evolve over time, old events stored in the event store can be transparently migrated to newer schemas on load. Each upcaster migrates from one payload version to the next, forming a chain that upcastEvents walks automatically.
Features
registerUpcaster(options)— adds a migration function foreventTypeatfromVersion → toVersionupcastEvents(events, options?)— walks the upcaster chain for each event, applying all registered migrationsclearUpcasters()— resets the registry (for test isolation)requireCompleteoption — throwsUPCASTER_MISSINGif the chain does not terminate for an event type- Chain limit of 100 steps prevents infinite loops from misconfigured upcasters
Usage: Basic Example
import { registerUpcaster, upcastEvents } from "@suaveplan/event-sourcing";
registerUpcaster({
eventType: "OrderPlaced",
fromVersion: 1,
toVersion: 2,
upcast: (payload) => ({
...(payload as object),
currency: "USD", // added in v2
}),
});
const migrated = upcastEvents(oldEvents);
Usage: Advanced Example
import { registerUpcaster, upcastEvents, clearUpcasters } from "@suaveplan/event-sourcing";
// Chain: v1 -> v2 -> v3
registerUpcaster({ eventType: "OrderPlaced", fromVersion: 1, toVersion: 2, upcast: addCurrency });
registerUpcaster({ eventType: "OrderPlaced", fromVersion: 2, toVersion: 3, upcast: addTaxId });
const events = await store.load("order-1");
const migrated = upcastEvents(events);
// In tests: clean up
clearUpcasters();
API Reference
registerUpcaster(options: UpcasterOptions)
Registers options.upcast to migrate payloads from options.fromVersion to options.toVersion for options.eventType.
upcastEvents(events, options?)
options.requireComplete— iftrue, throwsUPCASTER_MISSINGwhen chain has gaps- Returns a new array of events with migrated payloads (original events are not mutated)
clearUpcasters()
Resets the global registry. Use in afterEach during tests.
Implementation Notes
The upcaster registry is module-level (global singleton). This matches the typical pattern for event schema registries. The 100-step chain limit prevents runaway loops while supporting realistic migration depths. The requireComplete check is a best-effort safeguard and does not guarantee the upcaster graph has no cycles.