import { appendFile, readFile } from 'node:fs/promises'; import { existsSync } from 'node:fs'; import type { VideoProjectWorkspace } from './workspace.js'; export interface VideoProjectEvent { type: string; recordedAt: string; payload?: Record; } export async function appendProjectEvent( workspace: VideoProjectWorkspace, event: Omit & { recordedAt?: string }, ): Promise { const record: VideoProjectEvent = { ...event, recordedAt: event.recordedAt ?? new Date().toISOString(), }; await appendFile(workspace.eventsPath, `${JSON.stringify(record)}\n`); } export async function readProjectEvents( workspace: VideoProjectWorkspace, ): Promise { if (!existsSync(workspace.eventsPath)) return []; const raw = await readFile(workspace.eventsPath, 'utf-8'); const events: VideoProjectEvent[] = []; for (const line of raw.split('\n')) { const trimmed = line.trim(); if (!trimmed) continue; try { events.push(JSON.parse(trimmed) as VideoProjectEvent); } catch { // Append-only logs can be left with a torn line when a writer is // interrupted or a concurrent append exceeds the atomic-write size // (PIPE_BUF). One bad line must not poison the whole timeline that // status/report/obsidian-export consume — skip it and keep the rest. continue; } } return events; }