importScripts("src/background/config.js"); const { COMCIP_ORIGIN, COMCIP_APP_URL, COMCIP_QUERY_URL, COMCIP_CLIENT_ID_PROBE_URL, COMCIP_TIME_ENTRY_APP_URL, COMCIP_TIME_ENTRY_CLIENT_ID_PROBE_URL, COMCIP_TIME_ENTRY_APP_VERSION, COMCIP_TAB_URL_PATTERN, COMCIP_TIME_ENTRY_TAB_URL_PATTERN, SPA_WORKBENCH_FRAME_URL, SPA_WORKBENCH_FRAME_ID, SPA_WORKBENCH_FRAME_HOST_ID, SPA_FRAME_DNR_RULE_ID, } = globalThis.ArchPanelBackgroundConfig; void installSpaFrameHeaderRules(); chrome.runtime.onInstalled.addListener(() => { void installSpaFrameHeaderRules(); }); chrome.runtime.onStartup.addListener(() => { void installSpaFrameHeaderRules(); }); async function installSpaFrameHeaderRules() { if (!chrome.declarativeNetRequest?.updateDynamicRules) { return; } await chrome.declarativeNetRequest.updateDynamicRules({ removeRuleIds: [SPA_FRAME_DNR_RULE_ID], addRules: [ { id: SPA_FRAME_DNR_RULE_ID, priority: 1, action: { type: "modifyHeaders", responseHeaders: [ { header: "x-frame-options", operation: "remove", }, { header: "frame-options", operation: "remove", }, { header: "content-security-policy", operation: "remove", }, { header: "content-security-policy-report-only", operation: "remove", }, ], }, condition: { initiatorDomains: ["eeho.fa.us2.oraclecloud.com"], requestDomains: ["spa.oracle.com"], resourceTypes: ["sub_frame"], urlFilter: "||spa.oracle.com/oalcrm/web/api/g2m-consumer-application/ui/index.html", }, }, ], }).catch(() => {}); } chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { if (message?.type === "ARCH_PANEL_SPA_FRAME_FETCH") { executeSpaFrameFetch({ senderTabId: sender?.tab?.id, url: message.url, options: message.options || {}, }) .then(sendResponse) .catch((error) => { sendResponse({ ok: false, status: 0, error: error instanceof Error ? error.message : String(error), }); }); return true; } if (message?.type === "ARCH_PANEL_EXTENSION_FETCH") { executeExtensionFetch(message.url, message.options || {}) .then(sendResponse) .catch((error) => { sendResponse({ ok: false, status: 0, error: error instanceof Error ? error.message : String(error), }); }); return true; } if ( !message || (message.type !== "ARCH_PANEL_COMCIP_POST" && message.type !== "ARCH_PANEL_COMCIP_REQUEST") ) { return false; } requestComcipFromPage({ url: message.url || COMCIP_QUERY_URL, method: message.method || "POST", payload: message.payload, headers: message.headers, senderTabId: sender?.tab?.id, useTimeEntryFrame: Boolean(message.useTimeEntryFrame), }) .then(sendResponse) .catch((error) => { sendResponse({ ok: false, status: 0, error: error instanceof Error ? error.message : String(error), }); }); return true; }); async function executeExtensionFetch(requestUrl, options = {}) { const method = options.method || "GET"; const headers = { ...(options.headers || {}), }; const fetchOptions = { method, credentials: "include", cache: "no-store", headers, }; if (options.body !== undefined && options.body !== null) { fetchOptions.body = options.body; } const response = await fetch(requestUrl, fetchOptions); const text = await response.text(); return { ok: response.ok, status: response.status, statusText: response.statusText, headers: pickExtensionResponseHeaders(response.headers), payload: parseExtensionResponseText(text), debug: { url: requestUrl, headerKeys: Object.keys(headers), extensionFetch: true, }, }; } async function executeSpaFrameFetch({ senderTabId, url, options = {} }) { if (!senderTabId) { throw new Error("Unable to locate the current tab for the SPA frame request."); } await installSpaFrameHeaderRules(); await ensureSpaFrameElementInTab(senderTabId); await waitForSpaFramePresent(senderTabId); await ensureSpaBridgeInFrames(senderTabId); await waitForSpaFrameCapturedAuth(senderTabId, url); return executeSpaFrameScriptFetch(senderTabId, url, options); } async function ensureSpaFrameElementInTab(tabId, options = {}) { await chrome.scripting.executeScript({ target: { tabId, frameIds: [0] }, world: "MAIN", args: [ SPA_WORKBENCH_FRAME_HOST_ID, SPA_WORKBENCH_FRAME_ID, SPA_WORKBENCH_FRAME_URL, Boolean(options.forceReload), ], func: (hostId, frameId, frameUrl, forceReload) => { const desiredParent = document.body || document.documentElement; if (!desiredParent) { return false; } let host = document.getElementById(hostId); if (!host) { host = document.createElement("div"); host.id = hostId; host.setAttribute("aria-hidden", "true"); } Object.assign(host.style, { position: "fixed", width: "1440px", height: "1000px", overflow: "hidden", opacity: "0", pointerEvents: "none", border: "0", left: "-20000px", top: "0", zIndex: "0", }); if (host.parentElement !== desiredParent) { desiredParent.appendChild(host); } let frame = document.getElementById(frameId); if (!(frame instanceof HTMLIFrameElement)) { frame = document.createElement("iframe"); frame.id = frameId; frame.name = frameId; frame.setAttribute("aria-hidden", "true"); frame.setAttribute("referrerpolicy", "no-referrer-when-downgrade"); frame.tabIndex = -1; } frame.name = frameId; frame.dataset.archPanelFrame = "spa-workbench"; Object.assign(frame.style, { position: "absolute", width: "1440px", height: "1000px", opacity: "0", pointerEvents: "none", border: "0", left: "0", top: "0", }); if (frame.parentElement !== host) { host.appendChild(frame); } if ( forceReload || !frame.src || frame.src === "about:blank" || frame.src !== frameUrl ) { if (forceReload) { frame.src = "about:blank"; } frame.src = frameUrl; } return true; }, }).catch(() => {}); } async function waitForSpaFramePresent(tabId) { const startedAt = Date.now(); let lastFrames = ""; let lastEnsureAt = 0; let forceReloaded = false; while (Date.now() - startedAt < 45000) { const elapsed = Date.now() - startedAt; if (Date.now() - lastEnsureAt > 2500) { await ensureSpaFrameElementInTab(tabId, { forceReload: elapsed > 7000 && !forceReloaded, }); lastEnsureAt = Date.now(); if (elapsed > 7000) { forceReloaded = true; } } const frames = await getFrameHints(tabId).catch(() => []); const spaFrame = frames.find((frame) => isSpaFrameHint(frame)); lastFrames = frames .map((frame) => `${frame.frameId}:${frame.url || frame.name || "blank"}`) .slice(0, 8) .join(" | "); if (spaFrame) { return spaFrame; } await delay(500); } throw new Error( `SPA Workbench iframe was not found.${lastFrames ? ` Frames: ${lastFrames}` : ""}` ); } async function getFrameHints(tabId) { const injectionResults = await chrome.scripting.executeScript({ target: { tabId, allFrames: true }, world: "MAIN", func: () => ({ href: String(window.location.href || ""), name: String(window.name || ""), }), }); return (injectionResults || []).map((item) => ({ frameId: item.frameId, url: item.result?.href || "", name: item.result?.name || "", })); } function isSpaFrameHint(frame) { const url = String(frame?.url || ""); const name = String(frame?.name || ""); return ( url.startsWith("https://spa.oracle.com/") || name === "arch-panel-extension-spa-frame" ); } async function ensureSpaBridgeInFrames(tabId) { await chrome.scripting.executeScript({ target: { tabId, allFrames: true }, world: "MAIN", files: ["page-bridge.js"], }).catch(() => {}); } async function waitForSpaFrameCapturedAuth(tabId, requestUrl) { const startedAt = Date.now(); let lastHeaderKeys = ""; let lastSnapshot = null; const requiresWwbToken = String(requestUrl || "").includes("/provider-proxy/wwb-provider/"); while (Date.now() - startedAt < 90000) { await ensureSpaBridgeInFrames(tabId); const snapshot = await readSpaFrameCapturedHeaders(tabId, requestUrl).catch(() => null); const headerKeys = Object.keys(snapshot || {}); lastSnapshot = snapshot; lastHeaderKeys = headerKeys.join(", "); if (requiresWwbToken && snapshot?.["wwb-provider-authorization"]) { return snapshot; } if ( !requiresWwbToken && (snapshot?.["wwb-provider-authorization"] || snapshot?.["spa-ts-authorization"] || snapshot?.["authorization"]) ) { return snapshot; } await delay(1000); } return lastSnapshot || {}; } async function readSpaFrameCapturedHeaders(tabId, requestUrl) { const injectionResults = await chrome.scripting.executeScript({ target: { tabId, allFrames: true }, world: "MAIN", args: [requestUrl], func: (url) => { const href = String(window.location.href || ""); const frameName = String(window.name || ""); const isSpaFrame = href.startsWith("https://spa.oracle.com/") || frameName === "arch-panel-extension-spa-frame"; if (!isSpaFrame) { return { matched: false, }; } return { matched: true, headers: window.__archPanelExtensionBridge?.buildHeaderSnapshot?.({ url, includeAllAuth: true, }) || {}, }; }, }); const matchedResult = (injectionResults || []) .map((item) => item.result) .find((result) => result?.matched); return matchedResult?.headers || null; } async function executeSpaFrameScriptFetch(tabId, requestUrl, options = {}) { const startedAt = Date.now(); let lastError = ""; while (Date.now() - startedAt < 45000) { await ensureSpaBridgeInFrames(tabId); const response = await executeSpaFrameScriptFetchOnce(tabId, requestUrl, options).catch( (error) => { lastError = error instanceof Error ? error.message : String(error); return null; } ); if (response?.matched) { return response; } await delay(700); } throw new Error( `SPA Workbench frame did not return a result.${lastError ? ` ${lastError}` : ""}` ); } async function executeSpaFrameScriptFetchOnce(tabId, requestUrl, options = {}) { const injectionResults = await chrome.scripting.executeScript({ target: { tabId, allFrames: true }, world: "MAIN", args: [ requestUrl, options?.method || "GET", options?.body ?? null, options?.headers || {}, ], func: async (requestUrl, requestMethod, requestBody, sourceHeaders) => { function isSpaFrame() { const href = String(window.location.href || ""); const frameName = String(window.name || ""); return ( href.startsWith("https://spa.oracle.com/") || frameName === "arch-panel-extension-spa-frame" ); } if (!isSpaFrame()) { return { matched: false, frameUrl: window.location.href, frameName: window.name || "", }; } const bridge = window.__archPanelExtensionBridge; if (bridge?.fetchWithCapturedHeaders) { const response = await bridge.fetchWithCapturedHeaders(requestUrl, { method: requestMethod || "GET", headers: sourceHeaders || {}, body: requestBody, }); return { matched: true, ...response, debug: { ...(response.debug || {}), frameUrl: window.location.href, frameName: window.name || "", spaFrameFetch: true, }, }; } function parseResponseText(text) { if (!text) { return null; } try { return JSON.parse(text); } catch { return text; } } function pickResponseHeaders(responseHeaders) { const safeHeaders = {}; const exposedHeaders = ["content-type", "x-request-id", "x-oracle-dms-ecid"]; for (const key of exposedHeaders) { const value = responseHeaders.get(key); if (value) { safeHeaders[key] = value; } } return safeHeaders; } function normalizeHeaders(headers) { const normalized = {}; for (const [key, value] of Object.entries(headers || {})) { const normalizedKey = String(key || "").toLowerCase(); if (!normalizedKey || value === undefined || value === null || value === "") { continue; } normalized[normalizedKey] = String(value); } normalized.accept = normalized.accept || "application/json, text/plain, */*"; normalized["x-requested-with"] = normalized["x-requested-with"] || "XMLHttpRequest"; return normalized; } const headers = normalizeHeaders(sourceHeaders); const normalizedMethod = String(requestMethod || "GET").toUpperCase(); const fetchOptions = { method: normalizedMethod, credentials: "include", cache: "no-store", headers, }; if (normalizedMethod !== "GET" && normalizedMethod !== "HEAD") { if (!headers["content-type"]) { headers["content-type"] = "application/json"; } fetchOptions.body = requestBody; } const response = await fetch(requestUrl, fetchOptions); const text = await response.text(); return { matched: true, ok: response.ok, status: response.status, statusText: response.statusText, headers: pickResponseHeaders(response.headers), payload: parseResponseText(text), debug: { url: requestUrl, frameUrl: window.location.href, frameName: window.name || "", headerKeys: Object.keys(headers), spaFrameFetch: true, }, }; }, }); return (injectionResults || []) .map((item) => item.result) .find((result) => result?.matched) || null; } function stripGenericAuthHeaders(headers = {}) { const nextHeaders = {}; for (const [key, value] of Object.entries(headers || {})) { const normalizedKey = String(key || "").toLowerCase(); if ( normalizedKey === "authorization" || normalizedKey === "x-id-token" || normalizedKey === "spa-ts-authorization" ) { continue; } nextHeaders[key] = value; } return nextHeaders; } function parseExtensionResponseText(text) { if (!text) { return null; } try { return JSON.parse(text); } catch { return text; } } function pickExtensionResponseHeaders(responseHeaders) { const safeHeaders = {}; const exposedHeaders = ["content-type", "x-request-id", "x-oracle-dms-ecid"]; for (const key of exposedHeaders) { const value = responseHeaders.get(key); if (value) { safeHeaders[key] = value; } } return safeHeaders; } async function requestComcipFromPage({ url, method = "POST", payload, headers = {}, senderTabId, useTimeEntryFrame = false, }) { const route = getComcipRoute(url); if (route.useSenderFrame || useTimeEntryFrame) { if (!senderTabId) { throw new Error("Unable to locate the current dashboard tab for COMCIP Time Entry."); } await waitForComcipFrameSessionReady(senderTabId, headers, route); let response = await executeComcipFetchFromFrame( senderTabId, url, method, payload, headers, route ); if (isAuthorizationFailure(response) || isOutdatedApplicationFailure(response)) { await delay(1500); await waitForComcipFrameSessionReady(senderTabId, headers, route); response = await executeComcipFetchFromFrame( senderTabId, url, method, payload, headers, route ); } return response; } const activeTab = await captureActiveTab(); const target = await getComcipTab(route, activeTab); try { await restoreActiveTab(activeTab); await waitForComcipTabReady(target.tab.id); await waitForComcipSessionReady(target.tab.id, headers, route); let response = await executeComcipFetch( target.tab.id, url, method, payload, headers, route ); if (isAuthorizationFailure(response)) { await delay(1500); await waitForComcipSessionReady(target.tab.id, headers, route); response = await executeComcipFetch( target.tab.id, url, method, payload, headers, route ); } return response; } finally { if (target.created && target.tab.id) { await chrome.tabs.remove(target.tab.id).catch(() => {}); } await restoreActiveTab(activeTab); } } function getComcipRoute(requestUrl) { const normalizedUrl = String(requestUrl || ""); if (normalizedUrl.includes("/ic/builder/rt/oalset_timeentrymobile/live")) { return { appUrl: COMCIP_TIME_ENTRY_APP_URL, clientIdProbeUrl: COMCIP_TIME_ENTRY_CLIENT_ID_PROBE_URL, tabPattern: COMCIP_TIME_ENTRY_TAB_URL_PATTERN, useSenderFrame: true, appVersion: COMCIP_TIME_ENTRY_APP_VERSION, }; } return { appUrl: COMCIP_APP_URL, clientIdProbeUrl: COMCIP_CLIENT_ID_PROBE_URL, tabPattern: COMCIP_TAB_URL_PATTERN, }; } async function getComcipTab(route, activeTab) { const tabs = await chrome.tabs.query({ url: route.tabPattern }); const existing = tabs.find((tab) => { const tabUrl = String(tab.url || ""); return tab.id && !tab.discarded && !tabUrl.includes("/services/"); }); if (existing) { return { tab: existing, created: false, }; } if (route.requireExistingTab) { throw new Error( "A sessao COMCIP Time Entry nao esta pronta. Abra/autentique o Time Entry uma vez e tente novamente." ); } const created = await chrome.tabs.create({ url: route.appUrl, active: false, windowId: activeTab?.windowId, }); await restoreActiveTab(activeTab); return { tab: created, created: true, }; } async function captureActiveTab() { const [activeTab] = await chrome.tabs.query({ active: true, currentWindow: true, }); if (!activeTab?.id) { return null; } return { id: activeTab.id, windowId: activeTab.windowId, }; } async function restoreActiveTab(activeTab) { if (!activeTab?.id) { return; } await chrome.windows.update(activeTab.windowId, { focused: true }).catch(() => {}); await chrome.tabs.update(activeTab.id, { active: true }).catch(() => {}); } async function waitForComcipTabReady(tabId) { const startedAt = Date.now(); while (Date.now() - startedAt < 45000) { const tab = await chrome.tabs.get(tabId); const url = String(tab.url || ""); if (tab.status === "complete" && url.startsWith(COMCIP_ORIGIN)) { await delay(1000); return; } await delay(500); } throw new Error("Timed out while loading the COMCIP origin page."); } async function waitForComcipSessionReady(tabId, headers = {}, route) { const startedAt = Date.now(); let lastStatus = ""; while (Date.now() - startedAt < 60000) { const probe = await executeComcipProbe(tabId, headers, route).catch((error) => ({ ok: false, status: 0, error: error instanceof Error ? error.message : String(error), })); if (probe?.ok && probe?.appBuilderClientId) { return probe; } lastStatus = probe?.status ? `${probe.status} ${probe.statusText || ""}`.trim() : probe?.error || "not ready"; await delay(1000); } throw new Error(`Timed out while waiting for COMCIP authenticated session (${lastStatus}).`); } async function waitForComcipFrameSessionReady(tabId, headers = {}, route) { const startedAt = Date.now(); let lastStatus = ""; while (Date.now() - startedAt < 60000) { const probe = await executeComcipFrameProbe(tabId, headers, route).catch((error) => ({ ok: false, status: 0, error: error instanceof Error ? error.message : String(error), })); if (probe?.ok && probe?.matched) { return probe; } lastStatus = probe?.status ? `${probe.status} ${probe.statusText || ""}`.trim() : probe?.error || "not ready"; await delay(1000); } throw new Error( `Timed out while waiting for COMCIP Time Entry frame session (${lastStatus}).` ); } async function executeComcipFrameProbe(tabId, headers = {}, route) { const injectionResults = await chrome.scripting.executeScript({ target: { tabId, allFrames: true }, world: "MAIN", args: [ route?.clientIdProbeUrl || COMCIP_CLIENT_ID_PROBE_URL, headers || {}, route?.appVersion || "", ], func: async (probeUrl, sourceHeaders, routeAppVersion) => { function isTimeEntryFrame() { const href = String(window.location.href || ""); const frameName = String(window.name || ""); const isComcipOrigin = href.startsWith( "https://comcipapic-oalprod.integration.ocp.oraclecloud.com/" ); return ( (frameName === "arch-panel-extension-comcip-timeentry-frame" && isComcipOrigin) || href.includes("oalset_timeentrymobile") || href.includes("/webApps/timeentry") ); } if (!isTimeEntryFrame()) { return { matched: false, href: window.location.href, frameName: window.name || "", }; } const response = await fetch(probeUrl, { method: "GET", credentials: "include", cache: "no-store", headers: { accept: "*/*", authorization: "Session", "accept-language": sourceHeaders["accept-language"] || navigator.language || "pt-BR", "vb-proxy-header-rest-framework-version": "3", "x-vb-application-version": routeAppVersion || sourceHeaders["x-vb-application-version"] || "", }, }); return { matched: true, ok: response.ok, status: response.status, statusText: response.statusText, appBuilderClientId: response.headers.get("x-appbuilder-client-id") || "", href: window.location.href, frameName: window.name || "", }; }, }); const matchedResult = (injectionResults || []) .map((item) => item.result) .find((result) => result?.matched); if (!matchedResult) { const frameHints = (injectionResults || []) .map((item) => item.result) .filter(Boolean) .map((result) => result.href || result.frameName || "unknown") .slice(0, 5) .join(" | "); throw new Error( `COMCIP Time Entry frame was not found in the dashboard tab.${ frameHints ? ` Frames: ${frameHints}` : "" }` ); } return matchedResult; } async function executeComcipProbe(tabId, headers = {}, route) { const [injectionResult] = await chrome.scripting.executeScript({ target: { tabId }, world: "MAIN", args: [route?.clientIdProbeUrl || COMCIP_CLIENT_ID_PROBE_URL, headers || {}], func: async (probeUrl, sourceHeaders) => { const APP_VERSION = "version_1754044416761"; const response = await fetch(probeUrl, { method: "GET", credentials: "include", cache: "no-store", headers: { accept: "*/*", authorization: "Session", "accept-language": sourceHeaders["accept-language"] || navigator.language || "pt-BR", "x-vb-application-version": sourceHeaders["x-vb-application-version"] || APP_VERSION, }, }); return { ok: response.ok, status: response.status, statusText: response.statusText, appBuilderClientId: response.headers.get("x-appbuilder-client-id") || "", href: window.location.href, }; }, }); if (!injectionResult) { throw new Error("COMCIP probe did not return a result."); } return injectionResult.result; } async function executeComcipFetch(tabId, requestUrl, method, payload, headers, route) { const [injectionResult] = await chrome.scripting.executeScript({ target: { tabId }, world: "MAIN", args: [ requestUrl, method || "POST", payload || null, headers || {}, route?.clientIdProbeUrl || COMCIP_CLIENT_ID_PROBE_URL, route?.appVersion || "", ], func: async ( requestUrl, requestMethod, requestPayload, sourceHeaders, clientIdProbeUrl, routeAppVersion ) => { const APP_VERSION = "version_1754044416761"; function parseResponseText(text) { if (!text) { return null; } try { return JSON.parse(text); } catch { return text; } } function pickResponseHeaders(headers) { const safeHeaders = {}; const exposedHeaders = [ "content-type", "x-appbuilder-client-id", "x-invalid-appbuilder-client-id", "x-appbuilder-repeat-request", "vb-proxy-status-actual", "vb-proxy-version", ]; for (const key of exposedHeaders) { const value = headers.get(key); if (value) { safeHeaders[key] = value; } } return safeHeaders; } async function resolveAppBuilderClientId() { try { const response = await fetch(clientIdProbeUrl, { method: "GET", credentials: "include", cache: "no-store", headers: { accept: "*/*", authorization: "Session", "accept-language": sourceHeaders["accept-language"] || navigator.language || "pt-BR", "x-vb-application-version": sourceHeaders["x-vb-application-version"] || APP_VERSION, }, }); return response.headers.get("x-appbuilder-client-id") || ""; } catch { return ""; } } const appBuilderClientId = sourceHeaders["x-appbuilder-client-id"] || (await resolveAppBuilderClientId()); const requestHeaders = { accept: "*/*", authorization: "Session", "vb-proxy-header-preference": "transient", "x-vb-application-version": sourceHeaders["x-vb-application-version"] || APP_VERSION, "accept-language": sourceHeaders["accept-language"] || navigator.language || "pt-BR", }; if (appBuilderClientId) { requestHeaders["x-appbuilder-client-id"] = appBuilderClientId; } const normalizedMethod = String(requestMethod || "POST").toUpperCase(); const fetchOptions = { method: normalizedMethod, credentials: "include", cache: "no-store", headers: requestHeaders, }; if ( normalizedMethod !== "GET" && normalizedMethod !== "HEAD" && requestPayload !== undefined && requestPayload !== null ) { requestHeaders["content-type"] = "application/json"; fetchOptions.body = JSON.stringify(requestPayload); } const response = await fetch(requestUrl, { ...fetchOptions, }); const text = await response.text(); return { ok: response.ok, status: response.status, statusText: response.statusText, headers: pickResponseHeaders(response.headers), payload: parseResponseText(text), debug: { href: window.location.href, origin: window.location.origin, headerKeys: Object.keys(requestHeaders), }, }; }, }); if (!injectionResult) { throw new Error("COMCIP page script did not return a result."); } return injectionResult.result; } async function executeComcipFetchFromFrame( tabId, requestUrl, method, payload, headers, route ) { const injectionResults = await chrome.scripting.executeScript({ target: { tabId, allFrames: true }, world: "MAIN", args: [ requestUrl, method || "POST", payload || null, headers || {}, route?.clientIdProbeUrl || COMCIP_CLIENT_ID_PROBE_URL, route?.appVersion || "", ], func: async ( requestUrl, requestMethod, requestPayload, sourceHeaders, clientIdProbeUrl, routeAppVersion ) => { function isTimeEntryFrame() { const href = String(window.location.href || ""); const frameName = String(window.name || ""); const isComcipOrigin = href.startsWith( "https://comcipapic-oalprod.integration.ocp.oraclecloud.com/" ); return ( (frameName === "arch-panel-extension-comcip-timeentry-frame" && isComcipOrigin) || href.includes("oalset_timeentrymobile") || href.includes("/webApps/timeentry") ); } if (!isTimeEntryFrame()) { return { matched: false, href: window.location.href, frameName: window.name || "", }; } function parseResponseText(text) { if (!text) { return null; } try { return JSON.parse(text); } catch { return text; } } function pickResponseHeaders(responseHeaders) { const safeHeaders = {}; const exposedHeaders = [ "content-type", "x-appbuilder-client-id", "x-invalid-appbuilder-client-id", "x-appbuilder-repeat-request", "vb-proxy-status-actual", "vb-proxy-version", ]; for (const key of exposedHeaders) { const value = responseHeaders.get(key); if (value) { safeHeaders[key] = value; } } return safeHeaders; } async function resolveAppBuilderClientId() { try { const response = await fetch(clientIdProbeUrl, { method: "GET", credentials: "include", cache: "no-store", headers: { accept: "*/*", authorization: "Session", "accept-language": sourceHeaders["accept-language"] || navigator.language || "pt-BR", "vb-proxy-header-rest-framework-version": "3", "x-vb-application-version": routeAppVersion || sourceHeaders["x-vb-application-version"] || "", }, }); return response.headers.get("x-appbuilder-client-id") || ""; } catch { return ""; } } const appBuilderClientId = sourceHeaders["x-appbuilder-client-id"] || (await resolveAppBuilderClientId()); const requestHeaders = { accept: "*/*", authorization: "Session", "vb-proxy-header-preference": "transient", "vb-proxy-header-rest-framework-version": "3", "x-vb-application-version": routeAppVersion || sourceHeaders["x-vb-application-version"] || "", "accept-language": sourceHeaders["accept-language"] || navigator.language || "pt-BR", }; if (appBuilderClientId) { requestHeaders["x-appbuilder-client-id"] = appBuilderClientId; } const normalizedMethod = String(requestMethod || "POST").toUpperCase(); const fetchOptions = { method: normalizedMethod, credentials: "include", cache: "no-store", headers: requestHeaders, }; if ( normalizedMethod !== "GET" && normalizedMethod !== "HEAD" && requestPayload !== undefined && requestPayload !== null ) { requestHeaders["content-type"] = "application/json"; fetchOptions.body = JSON.stringify(requestPayload); } const response = await fetch(requestUrl, fetchOptions); const text = await response.text(); return { matched: true, ok: response.ok, status: response.status, statusText: response.statusText, headers: pickResponseHeaders(response.headers), payload: parseResponseText(text), debug: { href: window.location.href, origin: window.location.origin, frameName: window.name || "", headerKeys: Object.keys(requestHeaders), frameFetch: true, }, }; }, }); const matchedResult = (injectionResults || []) .map((item) => item.result) .find((result) => result?.matched); if (!matchedResult) { const frameHints = (injectionResults || []) .map((item) => item.result) .filter(Boolean) .map((result) => result.href || result.frameName || "unknown") .slice(0, 5) .join(" | "); throw new Error( `COMCIP Time Entry frame did not return a result.${ frameHints ? ` Frames: ${frameHints}` : "" }` ); } return matchedResult; } async function executeComcipDirectFetch( requestUrl, method = "GET", payload = null, headers = {}, route ) { const APP_VERSION = "version_1754044416761"; function parseResponseText(text) { if (!text) { return null; } try { return JSON.parse(text); } catch { return text; } } function pickResponseHeaders(responseHeaders) { const safeHeaders = {}; const exposedHeaders = [ "content-type", "x-appbuilder-client-id", "x-invalid-appbuilder-client-id", "x-appbuilder-repeat-request", "vb-proxy-status-actual", "vb-proxy-version", ]; for (const key of exposedHeaders) { const value = responseHeaders.get(key); if (value) { safeHeaders[key] = value; } } return safeHeaders; } async function resolveAppBuilderClientId() { try { const response = await fetch(route?.clientIdProbeUrl || COMCIP_CLIENT_ID_PROBE_URL, { method: "GET", credentials: "include", cache: "no-store", headers: { accept: "*/*", authorization: "Session", "accept-language": headers["accept-language"] || "pt-BR", }, }); return response.headers.get("x-appbuilder-client-id") || ""; } catch { return ""; } } const appBuilderClientId = headers["x-appbuilder-client-id"] || (await resolveAppBuilderClientId()); const requestHeaders = { accept: "*/*", authorization: "Session", "vb-proxy-header-preference": "transient", "accept-language": headers["accept-language"] || "pt-BR", }; if (!route?.omitAppVersion && headers["x-vb-application-version"]) { requestHeaders["x-vb-application-version"] = headers["x-vb-application-version"]; } if (appBuilderClientId) { requestHeaders["x-appbuilder-client-id"] = appBuilderClientId; } const normalizedMethod = String(method || "GET").toUpperCase(); const fetchOptions = { method: normalizedMethod, credentials: "include", cache: "no-store", headers: requestHeaders, }; if ( normalizedMethod !== "GET" && normalizedMethod !== "HEAD" && payload !== undefined && payload !== null ) { requestHeaders["content-type"] = "application/json"; fetchOptions.body = JSON.stringify(payload); } const response = await fetch(requestUrl, fetchOptions); const text = await response.text(); return { ok: response.ok, status: response.status, statusText: response.statusText, headers: pickResponseHeaders(response.headers), payload: parseResponseText(text), debug: { directFetch: true, origin: chrome.runtime.getURL(""), headerKeys: Object.keys(requestHeaders), }, }; } function isAuthorizationFailure(response) { if (!response) { return false; } const payloadText = typeof response.payload === "string" ? response.payload : JSON.stringify(response.payload || ""); return ( response.status === 401 || response.status === 500 && /vb:\/\/trap\/model\/no_auth/i.test(payloadText) || /No user authentication for the service/i.test(payloadText) || /missingAnonymous/i.test(payloadText) || /401 Authorization Required/i.test(payloadText) || /Authorization Required/i.test(payloadText) ); } function isOutdatedApplicationFailure(response) { if (!response) { return false; } const payloadText = typeof response.payload === "string" ? response.payload : JSON.stringify(response.payload || ""); return /out-dated version of the application/i.test(payloadText); } function delay(milliseconds) { return new Promise((resolve) => { setTimeout(resolve, milliseconds); }); }