Spaces:
Running
Running
File size: 1,923 Bytes
a896df9 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | import type { Expense, Settlement } from '../../domain/types';
import type { LedgerEvent } from './events';
export interface FoldedState {
expenses: Map<string, Expense>;
settlements: Map<string, Settlement>;
/** Winning event per entity id (may be a tombstone). */
latest: Map<string, LedgerEvent>;
/** All events per entity id, sorted by version. */
history: Map<string, LedgerEvent[]>;
eventIds: Set<string>;
}
export function newFoldedState(): FoldedState {
return { expenses: new Map(), settlements: new Map(), latest: new Map(), history: new Map(), eventIds: new Set() };
}
/** >0 if a should win over b. Higher version wins; ties by ts, then id. */
export function compareEvents(a: LedgerEvent, b: LedgerEvent): number {
if (a.version !== b.version) return a.version - b.version;
if (a.ts !== b.ts) return a.ts < b.ts ? -1 : 1;
return a.id < b.id ? -1 : a.id > b.id ? 1 : 0;
}
/** Applies one event. Returns false if the event id was already applied. */
export function applyEvent(state: FoldedState, event: LedgerEvent): boolean {
if (state.eventIds.has(event.id)) return false;
state.eventIds.add(event.id);
const hist = state.history.get(event.entityId) ?? [];
hist.push(event);
hist.sort(compareEvents);
state.history.set(event.entityId, hist);
const current = state.latest.get(event.entityId);
if (current && compareEvents(event, current) <= 0) return true;
state.latest.set(event.entityId, event);
if (event.kind === 'expense') {
if (event.op === 'upsert') state.expenses.set(event.entityId, event.data);
else state.expenses.delete(event.entityId);
} else {
if (event.op === 'upsert') state.settlements.set(event.entityId, event.data);
else state.settlements.delete(event.entityId);
}
return true;
}
export function foldEvents(events: Iterable<LedgerEvent>): FoldedState {
const state = newFoldedState();
for (const e of events) applyEvent(state, e);
return state;
}
|