export function extractRemediation(res) { return res?.remediation || null; } export function mergeRemediationOptions(into, remediation) { if (!Array.isArray(remediation?.options)) return; for (const option of remediation.options) { if (!option?.relation || !option?.object) continue; into.push(option); } } export function buildRemediation(remediation = null, options = {}) { if (!remediation && !Array.isArray(options.additional_options)) return null; const mergedOptions = []; mergeRemediationOptions(mergedOptions, remediation); if (Array.isArray(options.additional_options)) { for (const option of options.additional_options) { if (!option?.relation || !option?.object) continue; mergedOptions.push(option); } } if (mergedOptions.length === 0) return null; const deduped = []; const seen = new Set(); for (const option of mergedOptions) { const key = `${option.relation}|${option.object}|${option.url || ''}|${option.method || ''}`; if (seen.has(key)) continue; seen.add(key); deduped.push(option); } return { status: options.status || remediation?.status || 'required', ...(options.request_id ? { request_id: options.request_id } : (remediation?.request_id ? { request_id: remediation.request_id } : {})), options: deduped }; } export function buildRemediationFromChallenges(challengeRequirements, options = {}) { const list = Array.isArray(challengeRequirements) ? challengeRequirements : (challengeRequirements ? [challengeRequirements] : []); const derivedOptions = []; for (const requirement of list) { const challengeName = String(requirement?.name || '').trim().replace(/^!/, ''); if (!challengeName) continue; const metadata = {}; if (requirement?.subject) metadata.subject = requirement.subject; if (requirement?.withinMs !== undefined && requirement?.withinMs !== null) { metadata.within_ms = requirement.withinMs; } derivedOptions.push({ relation: 'challenge_satisfied', object: `challenge:${challengeName}`, ...(Object.keys(metadata).length > 0 ? { metadata } : {}) }); } return buildRemediation(null, { ...options, additional_options: derivedOptions }); }