function createLaterWork({ storage, getLogin, now = () => new Date(), setTimer = setTimeout, clearTimer = clearTimeout, onWake = () => {} }) { const prefix = 'stackchain.later-work.v1.'; let timer = null; function identity(item) { if (!item) return ''; const kind = item.is_review ? 'review' : (item.kind || 'work'); const number = Number.isInteger(item.number) ? item.number : ''; const notification = Number.isInteger(item.notification_id) ? item.notification_id : ''; return [kind, item.repository || '', number, notification].join(':'); } function storageKey() { const login = String(getLogin?.() || '').trim().toLowerCase(); return login ? prefix + encodeURIComponent(login) : ''; } function read() { const key = storageKey(); if (!key || !storage) return {}; try { const value = JSON.parse(storage.getItem(key) || '{}'); return value && typeof value === 'object' && !Array.isArray(value) ? value : {}; } catch (_error) { return {}; } } function write(records) { const key = storageKey(); if (!key || !storage) return false; try { if (Object.keys(records).length) storage.setItem(key, JSON.stringify(records)); else storage.removeItem(key); return true; } catch (_error) { return false; } } function defer(item, until) { const key = storageKey(); const id = identity(item); const wake = new Date(until); if (!key || !id || Number.isNaN(wake.getTime()) || wake <= now()) return false; const records = read(); records[id] = wake.toISOString(); write(records); return true; } function presetUntil(preset) { const current = new Date(now()); if (preset === 'today') return new Date(current.getTime() + 4 * 60 * 60 * 1000); if (preset === 'tomorrow') { const wake = new Date(current); wake.setDate(wake.getDate() + 1); wake.setHours(9, 0, 0, 0); return wake; } return new Date(NaN); } function restore(item) { const id = identity(item); const records = read(); if (!id || !Object.prototype.hasOwnProperty.call(records, id)) return false; delete records[id]; write(records); return true; } function schedule(wakeTimes, current) { if (timer !== null) clearTimer(timer); timer = null; if (!wakeTimes.length) return; const delay = Math.max(0, Math.min(...wakeTimes) - current); timer = setTimer(() => { timer = null; onWake(); }, Math.min(delay, 2147483647)); } function partition(items, { pruneMissing = true } = {}) { const current = now().getTime(); const records = read(); const available = new Map((items || []).map(item => [identity(item), item])); const retained = {}; const wakeTimes = []; Object.entries(records).forEach(([id, wake]) => { const wakeTime = new Date(wake).getTime(); if (!Number.isFinite(wakeTime) || wakeTime <= current) return; if (pruneMissing && !available.has(id)) return; retained[id] = new Date(wakeTime).toISOString(); wakeTimes.push(wakeTime); }); if (JSON.stringify(retained) !== JSON.stringify(records)) write(retained); schedule(wakeTimes, current); const deferredIds = new Set(Object.keys(retained)); const active = (items || []).filter(item => !deferredIds.has(identity(item))); const later = (items || []).flatMap(item => { const wake = retained[identity(item)]; return wake ? [{ ...item, deferred_until: wake }] : []; }); return { active, later }; } return { identity, defer, restore, presetUntil, partition }; } if (typeof module !== 'undefined' && module.exports) module.exports = createLaterWork;