Relocate the ~6k-file docs tree from the repo root into plugins/document-navigator/docs/ so the plugin is self-contained and portable. docs/superpowers/ (specs and plans) stays at the repo root. Moved via cp+rm rather than git mv because directory rename() fails with EPERM on this WSL /mnt DrvFs mount; git rename-detection pairs the identical-content files at commit time. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@suaveplan/event-sourcing - Event sourcing patterns: aggregate root, typed event store, snapshot support, projections, upcasting, and OTel instrumentation
Summary
Event sourcing patterns for Suaveplan: typed aggregate root, in-memory event store with optimistic concurrency, snapshot support, projections, upcasting, and OTel instrumentation.
Documentation tree
- aggregate — (no submodules)
- aggregate — (no submodules)
- aggregate-repository — (no submodules)
- aggregate-repository — (no submodules)
- event-store — (no submodules)
- memory-event-store — (no submodules)
- errors — (no submodules)
- event-sourcing-error — (no submodules)
- builder — (no submodules)
- builder — (no submodules)
- schemas — (no submodules)
- event-sourcing.schemas — (no submodules)
- upcaster — (no submodules)
- upcaster — (no submodules)
Modules
- Aggregate — abstract base class for event-sourced aggregates
- MemoryEventStore — in-memory EventStore with concurrency control
- AggregateRepository — load/save orchestrator with snapshot support
- ProjectionBuilder — declarative read-model projections
- Upcaster — event schema migration registry
- EventSourcingError — structured errors with stable codes
- Schemas — Zod schemas for all wire types
- Types — shared TypeScript interfaces
Quick Start
import {
Aggregate,
createMemoryEventStore,
createAggregateRepository,
createProjection,
} from "@suaveplan/event-sourcing";
import type { DomainEvent } from "@suaveplan/event-sourcing";
class OrderAggregate extends Aggregate<{ total: number }> {
constructor(id: string) { super(id, { total: 0 }); }
place(total: number) { this.raise("OrderPlaced", { total }); }
protected applyEvent(e: DomainEvent) {
if (e.type === "OrderPlaced") {
this.state = { total: (e.payload as { total: number }).total };
}
}
get total() { return this.state.total; }
}
const store = createMemoryEventStore();
const repo = createAggregateRepository({ store, factory: (id) => new OrderAggregate(id) });
const order = new OrderAggregate("order-1");
order.place(250);
await repo.save(order);
const loaded = await repo.load("order-1");
console.log(loaded.total); // 250