Spaces:
Running
Running
Download src/lib/server/ledger/fold.ts from assafvayner/splitwise: direct link, hf CLI and curl.
- Browser
- Download file 1.92 kB
-
https://huggingface.co/spaces/assafvayner/splitwise/resolve/main/src/lib/server/ledger/fold.ts
- Command line
-
hf download hf://spaces/assafvayner/splitwise/src/lib/server/ledger/fold.ts
-
curl -L -o fold.ts https://huggingface.co/spaces/assafvayner/splitwise/resolve/main/src/lib/server/ledger/fold.ts
1.92 kB
| 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; | |
| } | |