(function (root, factory) { const createUnfiledDraftSync = factory(); if (typeof module === 'object' && module.exports) module.exports = createUnfiledDraftSync; if (root) root.createUnfiledDraftSync = createUnfiledDraftSync; })(typeof globalThis !== 'undefined' ? globalThis : this, function () { function createUnfiledDraftSync(options, mountedFetch) { if (mountedFetch) return createUnfiledDraftSync.mount(options, mountedFetch); const {captures, fetchJson, storage, getLogin = () => '', onState = () => {}} = options; const key = 'stackchain.unfiled-draft-sync.v1'; let inFlight = null; let rerun = false; function read(login) { try { const all = JSON.parse(storage?.getItem(key) || '{}'); const state = all?.[login]; return { all:all && typeof all === 'object' ? all : {}, known:new Set(Array.isArray(state?.known) ? state.known : []), deleted:new Set(Array.isArray(state?.deleted) ? state.deleted : []), }; } catch (_error) { return {all:{}, known:new Set(), deleted:new Set()}; } } function write(login, state) { state.all[login] = {known:[...state.known], deleted:[...state.deleted]}; storage?.setItem(key, JSON.stringify(state.all)); } function publish(status, message) { onState({status, ...(message ? {message} : {})}); const visibleStatus = globalThis.document?.querySelector?.('#my-work-action-status'); if (message && visibleStatus) visibleStatus.textContent = message; } function sameDrafts(left, right) { return JSON.stringify(left) === JSON.stringify(right); } async function runSync(login) { const owner = String(login || '').trim(); if (!owner) return false; publish('syncing', 'Syncing Drafts…'); const state = read(owner); try { const remote = await fetchJson('api/v1/unfiled-drafts'); const remoteDrafts = Array.isArray(remote?.drafts) ? remote.drafts : []; const remoteIds = new Set(remoteDrafts.map(item => item.id)); const before = await captures.exportOwned(owner); for (const local of before) { if (state.known.has(local.id) && !remoteIds.has(local.id) && !state.deleted.has(local.id)) { await captures.discard(local.id); } } await captures.mergeRemote( remoteDrafts.filter(item => !state.deleted.has(item.id)), owner ); const local = await captures.exportOwned(owner); const merged = []; const byId = new Map(); for (const item of remoteDrafts) if (!state.deleted.has(item.id)) byId.set(item.id, item); for (const item of local) if (!state.deleted.has(item.id)) byId.set(item.id, item); for (const item of byId.values()) merged.push(item); merged.sort((left, right) => Number(right.saved_at) - Number(left.saved_at)); let finalSnapshot = remote; if (!sameDrafts(merged, remoteDrafts)) { finalSnapshot = await fetchJson('api/v1/unfiled-drafts', { method:'PUT', headers:{'Content-Type':'application/json'}, body:JSON.stringify({revision:Number(remote.revision || 0), drafts:merged}), }); } state.known = new Set((finalSnapshot.drafts || []).map(item => item.id)); state.deleted.clear(); write(owner, state); publish('ready', 'Drafts synced across devices.'); return true; } catch (error) { if (error?.status === 409) { publish('conflict', 'Drafts changed on another device. Sync again to combine them.'); } else { publish('pending', 'Saved on this device · sync pending'); } throw error; } } function sync(login) { const owner = String(login || '').trim(); if (!owner) return Promise.resolve(false); if (inFlight) { rerun = true; return inFlight; } inFlight = (async () => { let result = false; do { rerun = false; result = await runSync(owner); } while (rerun); return result; })().finally(() => { inFlight = null; }); return inFlight; } async function remove(id, login) { const owner = String(login || '').trim(); if (!owner) return false; const state = read(owner); state.deleted.add(String(id)); write(owner, state); await captures.discard(String(id)); return sync(owner); } captures.subscribe?.((id, removed) => { const login = getLogin(); if (login) void (removed ? remove(id, login) : sync(login)).catch(() => {}); }); return {sync, remove}; } createUnfiledDraftSync.mount = (captures, fetchJson) => { const getLogin = captures.currentLogin; const controller = createUnfiledDraftSync({captures, fetchJson, storage:globalThis.localStorage, getLogin}); const connect = () => { const login = getLogin(); if (login) void controller.sync(login).catch(() => {}); else globalThis.setTimeout(connect, 250); }; globalThis.setTimeout(connect, 0); globalThis.addEventListener?.('online', connect); return controller; }; return createUnfiledDraftSync; });