function createFusionSrDetails({ fetchJson, tabsCreate, tabsRemove, supportsFirefoxCookieStoreTabs, cookiesGetAll, cookieChanges, wait }) { const sessions = new Map(); function cookiePart(cookie) { if (!cookie) return null; const match = /^ORA_OCIS_CG_SESSION_idcs-([a-zA-Z0-9-]+)_comcipapic-oalprod\.integration\.ocp\.oraclecloud\.com_([12])$/.exec(cookie.name); const domain = "comcipapic-oalprod.integration.ocp.oraclecloud.com"; const cookieDomain = cookie.domain?.replace(/^\./, ""); if (!match || (cookieDomain && cookieDomain !== domain && !domain.endsWith(`.${cookieDomain}`))) return null; return { key: `${match[1]}|${cookie.path || "/"}|${cookie.storeId || ""}`, part: match[2], valid: Boolean(cookie.value) && (cookie.expirationDate == null || cookie.expirationDate > Date.now() / 1000) }; } function watchSession(session, sender) { const pairs = new Map(); let available; session.cookieReady = new Promise(resolve => { available = resolve; }); const listener = change => { const cookie = change.cookie; const part = cookiePart(cookie); if (!part) return; if (sender?.tab?.cookieStoreId && cookie.storeId !== sender.tab.cookieStoreId) return; if (!pairs.has(part.key)) pairs.set(part.key, new Map()); const pair = pairs.get(part.key); if (change.removed || !part.valid) pair.delete(part.part); else pair.set(part.part, cookie); if (pair.size === 2 && [...pair.values()].every(item => cookiePart(item)?.valid)) available(new Set([part.key])); session.wake?.(); }; cookieChanges?.addListener(listener); session.unwatch = () => cookieChanges?.removeListener(listener); } function waitForSessionCookies(session, sender) { if (session.cookieWait) return session.cookieWait; session.cookieWait = (async () => { const deadline = Date.now() + 1000; for (let attempt = 0; attempt < 5; attempt += 1) { // Install the wake-up before reading so a renewal during getAll is not missed. const changed = new Promise(resolve => { session.wake = resolve; }); const current = await sessionCookies(sender); const renewed = [...current].some(pair => !session.baseline.has(pair)); if (renewed || Date.now() >= deadline) { session.baseline = current; return current; } await Promise.race([changed, wait(250)]); } return sessionCookies(sender); })().finally(() => { session.cookieWait = null; session.wake = null; }); return session.cookieWait; } async function sessionCookies(sender) { const domain = "comcipapic-oalprod.integration.ocp.oraclecloud.com"; const details = { domain }; if (sender?.tab?.cookieStoreId) details.storeId = sender.tab.cookieStoreId; try { const groups = new Map(); for (const cookie of await cookiesGetAll(details)) { const part = cookiePart(cookie); if (!part?.valid) continue; if (!groups.has(part.key)) groups.set(part.key, {}); groups.get(part.key)[part.part] = [cookie.name, cookie.value, cookie.expirationDate ?? null]; } // Fingerprints remain in memory only; the browser sends the actual Cookie header. return new Set([...groups.values()].filter(pair => pair[1] && pair[2]).map(pair => JSON.stringify([pair[1], pair[2]]))); } catch { return new Set(); } } async function requestLookupWithSession(operation, message, sender, initialize = false) { const existingCookies = initialize ? await sessionCookies(sender) : null; if (existingCookies?.size) return { ok: true, debugRequests: [] }; const logs = []; const query = async () => { try { const result = await operation(message); logs.push(...(result.debugRequests || [])); return { ...result, debugRequests: logs }; } catch (error) { logs.push(...(error.debugRequests || [])); error.debugRequests = logs; throw error; } }; if (!initialize) { try { return await query(); } catch (error) { if (error.httpStatus !== 401) throw error; } } const key = sender?.tab?.cookieStoreId || "default"; let session = sessions.get(key); if (!session) { const details = { url: "https://comcipapic-oalprod.integration.ocp.oraclecloud.com/ic/builder/rt/SeaaS_initiation/live/webApps/external/", active: false }; if (Number.isInteger(sender?.tab?.windowId)) details.windowId = sender.tab.windowId; if (supportsFirefoxCookieStoreTabs() && sender?.tab?.cookieStoreId) details.cookieStoreId = sender.tab.cookieStoreId; session = { users: 0, closed: null, baseline: null }; watchSession(session, sender); session.tab = (async () => { session.baseline = existingCookies || await sessionCookies(sender); return tabsCreate(details); })(); sessions.set(key, session); } session.users += 1; let tab; try { tab = await session.tab; const deadline = Date.now() + 60000; for (let attempt = 0; attempt < 240 && Date.now() < deadline; attempt += 1) { const pendingCookies = initialize && attempt === 0 ? sessionCookies(sender) : waitForSessionCookies(session, sender); const cookies = await (initialize ? Promise.race([session.cookieReady, pendingCookies]) : pendingCookies); if (initialize) { // Proactive preparation observes cookies only; API requests belong to the form. if (!cookies.size) continue; await closeSessionTab(); return { ok: true, debugRequests: [] }; } try { const result = await query(); // Any successful lookup confirms the shared session; stop the page immediately. await closeSessionTab(); return result; } catch (error) { if (error.httpStatus !== 401) throw error; } } throw new Error("Service Request session could not be established after 60 seconds. Sign in to Oracle and retry."); } catch (error) { error.debugRequests = logs; throw error; } finally { session.users -= 1; if (session.users === 0) { sessions.delete(key); session.unwatch(); await closeSessionTab(); } } function closeSessionTab() { if (!session.closed && Number.isInteger(tab?.id)) { session.closed = tabsRemove(tab.id).catch(() => {}); } return session.closed; } } async function requestInternalServiceRequest(message) { const debugRequests = []; try { const id = String(message.srId ?? ""); if (!/^\d+$/.test(id)) throw new Error("A numeric SrId is required."); const fields = "Title,SrNumber,SrId,OpportunityName_c,QueueManagers_c,StatusCd,resourceMembers;ServiceRequest_SRToInternalSR_Tgt:ReportedHours_c,Title,OpportunityName_c,StatusCd,SrNumber,ServiceNew_c,AccountPartyName,LastUpdateDate,SvcHealth_c,AssigneePersonName,OpenDate,SrId"; const url = "https://comcipapic-oalprod.integration.ocp.oraclecloud.com/ic/builder/rt/SeaaS_SR_management/live;profile=PROD/services/auth/1.1/proxy/oalsetCRMRestAPI/uri/https/eeho.fa.us2.oraclecloud.com/crmRestApi/resources/latest/serviceRequests" + `?fields=${encodeURIComponent(fields)}&onlyData=true&q=${encodeURIComponent(`SrId=${id}`)}`; const query = async () => { const result = await fetchJson(url, { method: "GET", credentials: "include", headers: { Accept: "application/json", Authorization: "Session" } }, "requestInternalServiceRequest", debugRequests); if (!Array.isArray(result?.items)) throw new Error("Invalid SR details response."); return result; }; const result = await query(); const items = result.items.flatMap(item => { const children = item.ServiceRequest_SRToInternalSR_Tgt; if (children == null) return []; if (!Array.isArray(children) || children.some(child => !child || typeof child !== "object")) throw new Error("Invalid linked SR collection."); return children; }); return { ok: true, items, debugRequests }; } catch (error) { error.debugRequests = debugRequests; throw error; } } async function requestLookup(lookupType, requestName) { const debugRequests = []; try { const url = `https://comcipapic-oalprod.integration.ocp.oraclecloud.com/ic/builder/rt/SeaaS_initiation/live;profile=PROD/services/auth/1.1/proxy/oke_seaas/uri/https/gxpap.oracle.com/oalcrm/service/set/seaas/crm/lookups?lookupType=${lookupType}&type=STANDARD`; const result = await fetchJson(url, { method: "GET", credentials: "include", headers: { Accept: "application/json", Authorization: "Session" } }, requestName, debugRequests); if (!Array.isArray(result?.items) || result.items.some(item => typeof item?.code !== "string" || typeof item?.name !== "string")) throw new Error(`Invalid ${requestName} response.`); const items = result.items.filter(item => item.enabledFlag !== "N") .sort((a, b) => (Number(a.displaySequence) || 0) - (Number(b.displaySequence) || 0)) .map(({ code, name }) => ({ code, name })); return { ok: true, items, debugRequests }; } catch (error) { error.debugRequests = debugRequests; throw error; } } async function requestSrLookupOpportunityBusiness(message) { const debugRequests = []; try { if (typeof message.emailAddress !== "string" || !message.emailAddress.trim()) throw new Error("Current user email is unavailable."); const url = "https://comcipapic-oalprod.integration.ocp.oraclecloud.com/ic/builder/rt/SeaaS_initiation/live;profile=PROD/services/auth/1.1/proxy/oke_seaas/uri/https/gxpap.oracle.com/oalcrm/service/set/seaas/crm/services" + `?emailAddress=${encodeURIComponent(message.emailAddress.trim())}&salesObject=OPPORTUNITY&serviceType=BUSINESS`; const result = await fetchJson(url, { method: "GET", credentials: "include", headers: { Accept: "application/json", Authorization: "Session" } }, "requestSrLookupOpportunityBusiness", debugRequests); if (!Array.isArray(result?.items) || result.items.some(item => !/^\d+$/.test(String(item?.Id ?? "")) || typeof item?.RecordName !== "string")) throw new Error("Invalid Services response."); return { ok: true, items: result.items.map(({ Id, RecordName, Abstract_c }) => ({ Id: String(Id), RecordName, Abstract_c: Abstract_c == null ? "" : String(Abstract_c) })), debugRequests }; } catch (error) { error.debugRequests = debugRequests; throw error; } } async function requestInternalSrOptions(message, services = false) { const debugRequests = []; const name = services ? "requestInternalSrServices" : "requestLookupTypePlatform"; try { const base = "https://comcipapic-oalprod.integration.ocp.oraclecloud.com/ic/builder/rt/SeaaS_SR_management/live;profile=PROD/services/auth/1.1/proxy/oalsetSeaaSOKECustomRestAPI/uri/https/gxpap.oracle.com/oalcrm/service/set/seaas/crm/"; let path = "lookups?type=STANDARD&lookupType=SENGG_TECH_PLATFORM"; if (services) { const id = String(message.parentServiceId ?? ""); if (!/^\d+$/.test(id)) throw new Error("A numeric parent SrId is required."); if (typeof message.emailAddress !== "string" || !message.emailAddress.trim()) throw new Error("Current user email is unavailable."); path = `services?emailAddress=${encodeURIComponent(message.emailAddress.trim())}&parentServiceId=${encodeURIComponent(id)}&serviceType=INTERNAL`; } const result = await fetchJson(base + path, { method: "GET", credentials: "include", headers: { Accept: "application/json", Authorization: "Session" } }, name, debugRequests); if (!Array.isArray(result?.items) || result.items.some(item => services ? !/^\d+$/.test(String(item?.Id ?? "")) || typeof item?.RecordName !== "string" : typeof item?.code !== "string" || typeof item?.name !== "string")) throw new Error(`Invalid ${name} response.`); const items = services ? result.items.map(item => ({ Id: String(item.Id), RecordName: item.RecordName, Description_c: String(item.Description_c ?? "") })) : result.items.filter(item => item.enabledFlag !== "N").map(({ code, name }) => ({ code, name })); return { ok: true, items, debugRequests }; } catch (error) { error.debugRequests = debugRequests; throw error; } } return { requestLookupWithSession, requestInternalServiceRequest, requestSrLookupOpportunityBusiness, requestLookupTypePlatform: () => requestInternalSrOptions({}), requestInternalSrServices: message => requestInternalSrOptions(message, true), prepareSession: sender => requestLookupWithSession(null, {}, sender, true), requestSrLookupPrimaryPillar: () => requestLookup("SENGG_PRIMARY_PILLAR", "requestSrLookupPrimaryPillar"), requestSrLookupLanguage: () => requestLookup("SENGG_LANGUAGE", "requestSrLookupLanguage"), requestSrLookupTechPlatform: () => requestLookup("SENGG_TECH_PLATFORM", "requestSrLookupTechPlatform") }; }