// shared/logEvent.js — canonical event shape for UI/download/replay/analyze. // One source of truth. All four consumers import this. // // Canonical event: // { // id, ts, type, message, // encounterId, encounterName, encounterPath, // payload, // forward patch (null for no-op) // undo_payload, // { encounterPath, updates, redo } or null // undone, // bool // snapshot // { round, currentTurnParticipantId, turnOrderIds, activeIds } or null // } // // Old logs (pre-refactor): { timestamp, message, encounterName, undo }. // normalizeEvent fills defaults + lifts legacy undo into undo_payload. const DEFAULTS = { ts: 0, type: 'unknown', message: '', encounterId: null, encounterName: null, encounterPath: null, payload: null, undo_payload: null, undone: false, snapshot: null, }; // Canonical lean event shape: // { // id, ts, type, message, // encounterId, encounterName, encounterPath, // participantId, participantName, // nullable (pause/nextTurn have none) // delta, // type-specific scalars (nullable) // undo, // id-based inverse (nullable) // undone, // bool // snapshot // {round, currentTurnParticipantId, turnOrderIds, activeIds} // } // Legacy logs (pre-refactor, no type): display message only. Not undoable. // Old bloated logs (payload/undo_payload): normalized to lean — payload dropped, // undo_payload lifted to undo. Both display fine; undo expands via expandUndo. function normalizeEvent(raw) { if (!raw) return null; if (!raw.type || raw.type === 'unknown') return null; const id = raw.id || (raw.path ? raw.path.split('/').pop() : null); return { id, ts: raw.ts || raw.timestamp || 0, type: raw.type, message: raw.message || '', encounterId: raw.encounterId || null, encounterName: raw.encounterName || null, encounterPath: raw.encounterPath || null, participantId: raw.participantId || null, participantName: raw.participantName || null, delta: raw.delta || null, undo: raw.undo || raw.undo_payload || null, undone: !!raw.undone, snapshot: raw.snapshot || null, }; } // Serialize array of raw log entries (DB rows) → canonical JSON string. // Legacy logs (no type) dropped by normalizeEvent. Sorted ascending by ts. function serializeEvents(rawLogs) { return JSON.stringify( (rawLogs || []) .map(normalizeEvent) .filter(Boolean) .sort((a, b) => a.ts - b.ts), null, 2 ); } module.exports = { normalizeEvent, serializeEvents, DEFAULTS };