src/archive.js (2645 bytes)
1 // Each change of the archive goes through here: to the archive on this machine, or to the ideamine 2 // server when sync is on. Callers await each function in both cases. When the server does not 3 // answer, a change resolves to { queued: true, reason }: it waits in the outbox and goes later. 4 5 import os from 'node:os'; 6 import { absolute, addIdeas, applyTriage, findIdea, load, removeIdeas, updateIdea } from './store.js'; 7 import * as sync from './sync.js'; 8 9 let offline = null; 10 11 /** 12 * The archive, fresh from the server when sync is on. When the server does not answer, the copy on 13 * this machine, and offline() tells why. 14 */ 15 export async function fresh({ timeoutMs = 1500 } = {}) { 16 offline = null; 17 if (sync.enabled()) { 18 try { 19 await sync.pull({ timeoutMs }); 20 } catch (e) { 21 if (!(e instanceof sync.SyncError)) throw e; 22 offline = e.message; 23 } 24 } 25 return load(); 26 } 27 28 /** Why the last fresh() gave the copy on this machine, or null. */ 29 export const offlineReason = () => offline; 30 31 export async function add(texts, { source = 'mcp', project = null, session = null, tags = [] } = {}) { 32 const host = os.hostname(); 33 if (!sync.enabled()) return addIdeas(texts, { source, project, session, tags, host }); 34 const items = (Array.isArray(texts) ? texts : [texts]).map((t) => String(t ?? '').trim()).filter(Boolean); 35 if (!items.length) throw new Error('idea text is empty'); 36 const out = await sync.change({ op: 'add', texts: items, source, project: project ? absolute(project) : null, session, tags, host }); 37 if (out.queued) return out; 38 const db = load(); 39 return out.value.map((r) => ({ idea: findIdea(db, r.id), similar: r.similar })); 40 } 41 42 export async function update(id, patch = {}) { 43 if (!sync.enabled()) return updateIdea(id, patch); 44 const clean = patch.project ? { ...patch, project: absolute(patch.project) } : patch; 45 const out = await sync.change({ op: 'update', id, patch: clean }); 46 return out.queued ? out : findIdea(load(), out.value.id); 47 } 48 49 export async function remove(ids) { 50 if (!sync.enabled()) return removeIdeas(ids); 51 const out = await sync.change({ op: 'remove', ids }); 52 return out.queued ? out : out.value; 53 } 54 55 export async function triage(verdicts, { by = null, projects = null } = {}) { 56 if (!sync.enabled()) return applyTriage(verdicts, { by, projects }); 57 const out = await sync.change({ op: 'triage', verdicts, by, projects }); 58 return out.queued ? out : out.value; 59 } 60 61 /** The text for a change that waits in the outbox. */ 62 export const queuedText = (out) => `The ideamine server does not answer (${out.reason}). The change waits here and goes to the server later.`;