// File-backed storage for onboarding device mappings. const fs = require('fs'); const path = require('path'); function isTransientDbError(error) { const code = String(error && error.code ? error.code : '').trim(); return [ 'ECONNREFUSED', 'ECONNRESET', 'ETIMEDOUT', 'EPIPE', 'ENOTFOUND', 'PROTOCOL_CONNECTION_LOST', 'POOL_CLOSED', 'ERR_POOL_CLOSED' ].indexOf(code) !== -1; } function createOnboardingStore(filePath) { const normalizedFilePath = String(filePath || '').trim(); async function readEntries() { try { const raw = await fs.promises.readFile(normalizedFilePath, 'utf8'); const parsed = JSON.parse(raw); return Array.isArray(parsed) ? parsed : []; } catch (error) { if (error && error.code === 'ENOENT') { return []; } throw error; } } async function writeEntries(entries) { await fs.promises.mkdir(path.dirname(normalizedFilePath), { recursive: true }); const tempPath = `${normalizedFilePath}.tmp`; await fs.promises.writeFile(tempPath, JSON.stringify(Array.isArray(entries) ? entries : [], null, 2), 'utf8'); await fs.promises.rename(tempPath, normalizedFilePath); } async function enqueueBinding(entry) { const normalizedEntry = { deviceId: String(entry && entry.deviceId ? entry.deviceId : '').trim(), clientName: String(entry && entry.clientName ? entry.clientName : '').trim(), screenSlug: String(entry && entry.screenSlug ? entry.screenSlug : '').trim(), queuedAt: String(entry && entry.queuedAt ? entry.queuedAt : new Date().toISOString()) }; if (!normalizedEntry.deviceId || !normalizedEntry.clientName || !normalizedEntry.screenSlug) { return readEntries(); } const entries = await readEntries(); const nextEntries = entries.filter(function (queuedEntry) { return String(queuedEntry && queuedEntry.deviceId ? queuedEntry.deviceId : '').trim() !== normalizedEntry.deviceId; }); nextEntries.push(normalizedEntry); await writeEntries(nextEntries); return nextEntries; } async function flushBindings(applyBinding) { const entries = await readEntries(); if (!entries.length) { return { flushed: 0, remaining: 0 }; } const remaining = []; let flushed = 0; for (let index = 0; index < entries.length; index += 1) { const entry = entries[index]; try { await applyBinding(entry); flushed += 1; } catch (error) { if (isTransientDbError(error)) { remaining.push.apply(remaining, entries.slice(index)); break; } remaining.push.apply(remaining, entries.slice(index + 1)); } } await writeEntries(remaining); return { flushed: flushed, remaining: remaining.length }; } return { enqueueBinding: enqueueBinding, flushBindings: flushBindings, readEntries: readEntries }; } module.exports = { createOnboardingStore: createOnboardingStore, isTransientDbError: isTransientDbError };