first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -0,0 +1,69 @@
# upcaster
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## 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 for `eventType` at `fromVersion → toVersion`
- `upcastEvents(events, options?)` — walks the upcaster chain for each event, applying all registered migrations
- `clearUpcasters()` — resets the registry (for test isolation)
- `requireComplete` option — throws `UPCASTER_MISSING` if the chain does not terminate for an event type
- Chain limit of 100 steps prevents infinite loops from misconfigured upcasters
## Usage: Basic Example
```ts
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
```ts
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` — if `true`, throws `UPCASTER_MISSING` when 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.