function createProgressiveHumanGates(options = {}) { const document = options.document || globalThis.document; const location = options.location || globalThis.location; const history = options.history || globalThis.history; const storage = options.storage || globalThis.localStorage; const isOnline = options.isOnline || (() => globalThis.navigator?.onLine !== false); const fetchJson = options.fetchJson || (async (path, requestOptions = {}) => { const response = await fetch(path, requestOptions); const payload = await response.json().catch(() => ({})); if (!response.ok) throw new Error(payload.error || payload.detail?.message || payload.detail || 'Review request failed.'); return payload; }); const liveSnapshot = options.liveSnapshot || null; const getIdentity = options.getIdentity || (async () => { const response = liveSnapshot ? await liveSnapshot.acquire({requireIdentity:true}) : await fetchJson('api/v1/live', {headers:{Accept:'application/json'}}); const user = response?.context?.user || {}; const login = String(user.login || '').trim(); return {login, accountKey:login && user.id ? String(user.id) + ':' + login : login}; }); const query = selector => document.querySelector(selector); const nodes = { count:query('#human-gates-count'), list:query('#human-gates-list'), status:query('#human-gates-status'), panel:query('#human-gates'), detail:query('#human-gate-detail'), pendingTab:query('#human-gates-pending'), historyTab:query('#human-gates-history'), }; let login = ''; let accountKey = ''; let started = false; let startFlight = null; const controller = createHumanGates({ storage, isOnline, location, fetchJson, getLogin:() => login, getAccountKey:() => accountKey, nodes, }); const showError = error => { if (nodes.status) nodes.status.textContent = error?.message || 'Human Gates are unavailable.'; }; const open = () => start(true).catch(showError); query('#open-human-gates')?.addEventListener?.('click', open); query('#close-human-gates')?.addEventListener?.('click', () => { if (nodes.panel) nodes.panel.hidden = true; if (location.hash === '#/my-work/human-gates') history.replaceState({}, '', '#/my-work'); }); nodes.list?.addEventListener?.('click', event => { const card = event.target?.closest?.('[data-human-gate-id]'); if (card) controller.select(card.dataset.humanGateId); }); nodes.detail?.addEventListener?.('click', event => { const recovery = event.target?.closest?.('[data-gate-recover]'); if (recovery) { recovery.disabled = true; controller.recoverDecision().catch(error => { showError(error); recovery.disabled = false; }); return; } const decision = event.target?.closest?.('[data-gate-decision]')?.dataset.gateDecision; if (!decision) return; controller.submitDecision(decision).catch(error => { if (!error?.targetSelector) showError(error); }); }); nodes.pendingTab?.addEventListener?.('click', () => controller.showPending()); nodes.historyTab?.addEventListener?.('click', () => controller.showHistory().catch(showError)); async function start(force = false) { if (!force && location.hash !== '#/my-work/human-gates') return false; if (started) return controller.open().then(() => true); if (startFlight) return startFlight; startFlight = (async () => { const identity = await getIdentity(); login = String(identity?.login || '').trim(); accountKey = String(identity?.accountKey || login).trim(); if (!login) throw new Error('Authenticated account identity is required.'); await controller.open(); started = true; return true; })(); try { return await startFlight; } finally { startFlight = null; } } return { start, handoff() { return { controller, started, adoptIdentity(nextLogin, nextAccountKey) { login = String(nextLogin || '').trim(); accountKey = String(nextAccountKey || login).trim(); }, }; }, }; } if (typeof window !== 'undefined' && typeof document !== 'undefined') { window.stackchainProgressiveHumanGates = createProgressiveHumanGates({ document, liveSnapshot:window.stackchainProgressiveLiveSnapshot, }); void window.stackchainProgressiveHumanGates.start().catch(() => {}); } if (typeof module !== 'undefined' && module.exports) { globalThis.createHumanGates = globalThis.createHumanGates || require('./human-gates.js'); module.exports = createProgressiveHumanGates; }