export const HEALTH_DRAFT_TTL_MS = 24 * 60 * 60 * 1000; export function createDraftKey({ userId, sourceRef, metricSet, now = Date.now() } = {}) { const ref = String(sourceRef ?? 'manual').slice(0, 64); return `${String(userId)}:${metricSet}:${ref}:${now}`; } export function createInMemoryHealthObservationDraftStore() { const rowsByUser = new Map(); let nextId = 1; return { async findByKey(userId, draftKey) { const rows = rowsByUser.get(String(userId)) ?? []; return rows.find((row) => row.draftKey === draftKey && row.status === 'pending') ?? null; }, async findPendingBySourceRef(userId, sourceRef) { const rows = rowsByUser.get(String(userId)) ?? []; const now = Date.now(); return ( rows.find( (row) => row.status === 'pending' && row.sourceRef === sourceRef && row.expiresAt > now, ) ?? null ); }, async insert(userId, draft) { const row = { id: nextId++, userId: String(userId), draftKey: draft.draftKey, metricSet: draft.metricSet, extracted: draft.extracted, validation: draft.validation, status: 'pending', channel: draft.channel ?? 'h5', sourceRef: draft.sourceRef ?? null, createdAt: draft.createdAt ?? Date.now(), expiresAt: draft.expiresAt ?? Date.now() + HEALTH_DRAFT_TTL_MS, committedAt: null, }; const list = rowsByUser.get(row.userId) ?? []; list.unshift(row); rowsByUser.set(row.userId, list); return row; }, async update(userId, draftKey, patch) { const rows = rowsByUser.get(String(userId)) ?? []; const index = rows.findIndex((row) => row.draftKey === draftKey); if (index < 0) return null; rows[index] = { ...rows[index], ...patch }; return rows[index]; }, }; }