51 lines
src/store/optimisticStore.ts
Applies and rolls back optimistic mutations against a local state store.
// Optimistic mutation store — instant UI updates with server-confirmed rollback.
 
export interface StoreItem {
  id: string;
  value: string;
}
 
interface InternalState {
  items: StoreItem[];
  _snapshots: Map<string, StoreItem[]>;
  _pendingCount: number;
}
 
let _state: InternalState = {
  items: [],
  _snapshots: new Map(),
  _pendingCount: 0,
};
 
let _nextSnapshotId = 0;
 
// Applies a mutation optimistically and snapshots state for rollback.
// Parameters: mutation — transforms the current items array and returns the new array.
// Returns: snapshot ID string to pass to rollback() if the server rejects the mutation.
export function apply(mutation: (items: StoreItem[]) => StoreItem[]): string {
  const snapshotId = String(_nextSnapshotId++);
  mutation(_state.items);
  _state._snapshots.set(snapshotId, [..._state.items]);
  _state.items = mutation(_state.items);
  _state._pendingCount += 1;
  return snapshotId;
}
 
// Restores state to the snapshot captured before the given mutation was applied.
// Parameters: snapshotId — the ID returned by the corresponding apply() call.
// Returns: void — mutates store state in-place; no-op if the snapshot is missing.
export function rollback(snapshotId: string): void {
  const snapshot = _state._snapshots.get(snapshotId);
  if (snapshot) {
    _state.items = snapshot;
    _state._snapshots.delete(snapshotId);
    _state._pendingCount -= 1;
  }
}
 
// Exposes the current store state for UI rendering and external subscribers.
// Parameters: none.
// Returns: the current InternalState object (typed as object to avoid leaking internals).
export function getState(): object {
  return _state;
}