// services/enrichment/domainEnrichment.js // // Shared helpers for domain (RED + OptiSigns) attachment and fetching. // This unifies the previously duplicated logic for attaching RED/OptiSigns // across chat (deviceService), rich build (avDeviceBuilder), and dashboard (avEnricher). // // Uses the per-type matchers and creators for consistency. // Incremental step toward unified top-level AV enrichment. import { getOptiSignStatus } from '../../integrations/optisigns/client.js'; import { getREDStatusForStore } from '../../integrations/red/players.js'; import { findBestRedMatch } from './redMatcher.js'; import { findBestOptiSignsMatch, createOptiSignsDisplay, createRichOptiSigns } from './optisignsMatcher.js'; /** * Fetch RED and OptiSigns data for a store (parallel, safe). * Returns lists ready for attachment. */ export async function fetchDomainData(storeNum) { const [optiData, redPlayers] = await Promise.allSettled([ getOptiSignStatus(storeNum), getREDStatusForStore(storeNum) ]); return { optiDevices: optiData.status === 'fulfilled' ? (optiData.value?.devices || []) : [], redList: redPlayers.status === 'fulfilled' ? (redPlayers.value || []) : [] }; } /** * Attach RED and OptiSigns to a list of devices in-place. * Supports different shapes for different consumers (chat vs rich vs raw for dashboard). * * @param {Array} devices - list of device objects (must have identifier or friendlyName/name) * @param {Array} redList * @param {Array} optiList * @param {Object} [options] * @param {string} [options.identifierField='identifier'] - field to use for lookup (fallback to friendlyName/name) * @param {string} [options.optiShape='rich'] - 'rich' | 'display' | 'raw' (raw = just the matched device obj) * @param {boolean} [options.onlyForMSCRed=true] * @param {boolean} [options.onlyForVWLEDopti=true] * @returns {Array} the devices (mutated) */ export function attachDomainData(devices, redList = [], optiList = [], options = {}) { const { identifierField = 'identifier', optiShape = 'rich', onlyForMSCRed = true, onlyForVWLEDopti = true } = options; const redPlayers = redList || []; const optiDevices = optiList || []; for (const device of devices) { const id = device[identifierField] || device.friendlyName || device.name || ''; const idUpper = (id || '').toUpperCase(); // RED for MSC* if (!onlyForMSCRed || idUpper.includes('MSC')) { device.red = findBestRedMatch(id, redPlayers) || null; } else { device.red = null; } // Opti for VW/LED if (!onlyForVWLEDopti || idUpper.includes('VW') || idUpper.includes('LED')) { const rawOpti = findBestOptiSignsMatch(id, optiDevices); if (optiShape === 'raw') { device.optisigns = rawOpti || null; } else if (optiShape === 'display') { device.optisigns = createOptiSignsDisplay(rawOpti); } else { device.optisigns = createRichOptiSigns(rawOpti); } } else { device.optisigns = null; } } return devices; } /** * Convenience: fetch + attach for a list of devices. * Used by rich paths that want to do domain last. */ export async function enrichDomainData(devices, storeNum, options = {}) { const { optiDevices, redList } = await fetchDomainData(storeNum); return attachDomainData(devices, redList, optiDevices, options); }