Files
opportunities-extension/dist/firefox/background.js

3428 lines
155 KiB
JavaScript

(function () {
"use strict";
// Source: src/shared/time-dates.js
function createTimeDates() {
const months = "JAN FEB MAR APR MAY JUN JUL AUG SEP OCT NOV DEC".split(" ");
function parse(value) {
const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(String(value));
if (!match) return null;
const date = new Date(`${value}T00:00:00.000Z`);
return Number.isFinite(date.getTime()) && date.toISOString().slice(0, 10) === value ? date : null;
}
function fromApi(value) {
const match = /^(\d{2})-([A-Z]{3})-(\d{4})$/.exec(String(value));
if (!match) return null;
const month = months.indexOf(match[2]) + 1;
return parse(`${match[3]}-${String(month).padStart(2, "0")}-${match[1]}`);
}
function toApi(value) {
const date = parse(value);
if (!date) throw new Error("Select a valid reference date.");
return `${String(date.getUTCDate()).padStart(2, "0")}-${months[date.getUTCMonth()]}-${date.getUTCFullYear()}`;
}
function shift(value, days) {
const date = parse(value);
if (!date) return value;
date.setUTCDate(date.getUTCDate() + days);
return date.toISOString().slice(0, 10);
}
function today() {
const date = new Date();
return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, "0")}-${String(date.getDate()).padStart(2, "0")}`;
}
return { parse, fromApi, toApi, shift, today };
}
// Source: src/services/fusion/time-entries.js
function createTimeEntries({ fetchJson, dates, getResourceUser, saveHeaders }) {
const endpoint = "https://comcipapic-oalprod.integration.ocp.oraclecloud.com/ic/builder/rt/oalset_timeentrymobile/live;profile=PROD/services/auth/1.1/proxy/oke_seaas/uri/https/gxpap.oracle.com/oalcrm/service/set/seaas/mgmt/timeEntries/weekDetails";
async function requestWeekDetails(message) {
const debugRequests = [];
try {
const date = dates.toApi(message.dateReference);
const items = await fetchJson(`${endpoint}?dateValue=${encodeURIComponent(date)}`, {
method: "GET", credentials: "include", headers: { Authorization: "Session", Accept: "application/json" }
}, "requestWeekDetails", debugRequests);
if (!Array.isArray(items) || items.length !== 7 || items.some(item => !dates.fromApi(item?.Date) || !dates.fromApi(item?.EndDate))
|| new Set(items.map(item => item.Date)).size !== 7) throw new Error("Invalid week details response.");
return { ok: true, items, debugRequests };
} catch (error) {
error.debugRequests = debugRequests;
throw error;
}
}
async function requestTimeEntries(message) {
const debugRequests = [];
try {
if (!/^\d+$/.test(String(message.weekId)) || Number(message.weekId) <= 0) throw new Error("Missing or invalid WeekId.");
const resource = await getResourceUser(message);
debugRequests.push(...resource.debugRequests);
const url = endpoint.replace(/\/weekDetails$/, "") + `?resourceId=${encodeURIComponent(resource.resource.resourcePartyId)}&sortBy=createdDate&sortOrder=ASC&weekId=${encodeURIComponent(message.weekId)}`;
const items = await fetchJson(url, { method: "GET", credentials: "include", headers: { Authorization: "Session", Accept: "application/json" } }, "requestTimeEntries", debugRequests);
if (!Array.isArray(items)) throw new Error("Invalid time entries response.");
return { ok: true, items, debugRequests };
} catch (error) { error.debugRequests = [...debugRequests, ...(error.debugRequests || [])]; throw error; }
}
async function requestTimeEntriesDelete(message) {
const debugRequests = [];
try {
if (!/^[1-9]\d*$/.test(String(message.id))) throw new Error("Missing or invalid time entry ID.");
const url = endpoint.replace(/\/weekDetails$/, "") + `/${message.id}`;
const headers = await saveHeaders(url);
const result = await fetchJson(url, { method: "DELETE", credentials: "include", headers },
"requestTimeEntriesDelete", debugRequests, { allowEmpty: true });
return { ok: true, result, debugRequests };
} catch (error) { error.debugRequests = debugRequests; throw error; }
}
async function requestTimeEntriesSave(message) {
const debugRequests = [];
try {
if (!["POST", "PUT"].includes(message.method)) throw new Error("Invalid time entry save method.");
if (!Array.isArray(message.items) || !message.items.length) throw new Error("No time entries to save.");
const hours = ["saturdayTaskHours", "sundayTaskHours", "mondayTaskHours", "tuesdayTaskHours", "wednesdayTaskHours", "thursdayTaskHours", "fridayTaskHours"];
for (const item of message.items) {
if (!item || !["SERVICE", "NON_SERVICE"].includes(item.tag) || !item.taskType
|| (item.tag === "SERVICE" && (!/^SR\d+$/.test(item.srNumber) || !item.activityCode))
|| !/^\d+$/.test(String(item.weekId)) || Number(item.weekId) <= 0
|| hours.some(key => typeof item[key] !== "number" || !Number.isFinite(item[key]) || item[key] < 0 || item[key] > 24)
|| !hours.some(key => item[key] > 0)
|| (message.method === "PUT" && (!/^\d+$/.test(String(item.id)) || Number(item.id) <= 0))) throw new Error("Invalid or incomplete time entry.");
}
const resource = await getResourceUser(message);
debugRequests.push(...resource.debugRequests);
const items = message.items.map(item => ({
...(message.method === "PUT" ? { id: item.id } : {}),
srNumber: item.tag === "NON_SERVICE" ? "non-service-sr" : item.srNumber,
activityCode: item.activityCode || "", taskType: item.taskType, deliveryType: item.deliveryType || "",
...Object.fromEntries(hours.map(key => [key, item[key]])), notes: String(item.notes || ""),
weekId: item.weekId, resourceId: String(resource.resource.resourcePartyId), tag: item.tag
}));
const url = endpoint.replace(/\/weekDetails$/, "");
const headers = await saveHeaders(url);
const result = await fetchJson(url, {
method: message.method, credentials: "include",
headers, body: JSON.stringify(items)
}, "requestTimeEntriesSave", debugRequests, { allowEmpty: true });
return { ok: true, result, debugRequests };
} catch (error) { error.debugRequests = [...debugRequests, ...(error.debugRequests || [])]; throw error; }
}
async function requestActiveServiceRequests(message) {
const debugRequests = [];
try {
if (!/^\d+$/.test(String(message.weekId)) || Number(message.weekId) <= 0) throw new Error("Missing or invalid WeekId.");
const resource = await getResourceUser(message);
debugRequests.push(...resource.debugRequests);
const url = endpoint.replace(/weekDetails$/, "activeServiceRequests") + `?resourceId=${encodeURIComponent(resource.resource.resourcePartyId)}&weekId=${encodeURIComponent(message.weekId)}`;
const items = await fetchJson(url, { method: "GET", credentials: "include", headers: { Authorization: "Session", Accept: "application/json" } }, "requestActiveServiceRequests", debugRequests);
if (!Array.isArray(items)) throw new Error("Invalid active Service Requests response.");
return { ok: true, items, debugRequests };
} catch (error) { error.debugRequests = [...debugRequests, ...(error.debugRequests || [])]; throw error; }
}
async function requestServiceRequestExpand(message) {
const debugRequests = [];
try {
if (!Array.isArray(message.srNumbers)) throw new Error("Missing Service Request numbers.");
const numbers = [...new Set(message.srNumbers.map(value => String(value || "").trim()).filter(Boolean))].filter(value => value !== "non-service-sr");
if (numbers.some(value => !/^SR\d+$/.test(value))) throw new Error("Invalid Service Request number.");
const batches = [];
for (let index = 0; index < numbers.length; index += 50) batches.push(numbers.slice(index, index + 50));
if (!batches.length || batches[batches.length - 1].length === 50) batches.push([]);
batches[batches.length - 1].push("non-service-sr");
const base = "https://comcipapic-oalprod.integration.ocp.oraclecloud.com/ic/builder/rt/oalset_timeentrymobile/live;profile=PROD/services/auth/1.1/proxy/crmRestApiSearchResourcesLatestCustomActions/uri/https/eeho.fa.us2.oraclecloud.com/crmRestApi/resources/latest/serviceRequests";
const fields = "SrNumber,SRActivities_c,ProblemDescription,AccountPartyUniqueName,ServiceNew_c,OptyOwner_c,OptyName_c,OptyNumber_c,ReportedByPartyUniqueName,StatusCdMeaning,CountryText_c,AssigneePersonName,OptyStatus_c";
const items = [];
for (const batch of batches) {
const query = `SrNumber in (${batch.map(value => `'${value}'`).join(",")})`;
const url = `${base}?expand=ServiceRequest_SRToInternalSR_Tgt&fields=${encodeURIComponent(fields)}&limit=200&onlyData=true&q=${encodeURIComponent(query).replace(/'/g, "%27")}`;
const result = await fetchJson(url, { method: "GET", credentials: "include", headers: { Authorization: "Session", Accept: "application/json", "vb-proxy-header-rest-framework-version": "3" } }, "requestServiceRequestExpand", debugRequests);
if (!Array.isArray(result?.items)) throw new Error("Invalid Service Request expansion response.");
items.push(...result.items);
}
return { ok: true, items, debugRequests };
} catch (error) { error.debugRequests = debugRequests; throw error; }
}
async function requestLookupTaskType() {
const debugRequests = [];
try {
const url = endpoint.replace(/mgmt\/timeEntries\/weekDetails$/, "crm/lookups?lookupType=SCTA_TASK_TYPE&type=CUSTOM");
const result = await fetchJson(url, { method: "GET", credentials: "include", headers: { Authorization: "Session", Accept: "application/json" } }, "requestLookupTaskType", debugRequests);
if (!Array.isArray(result?.items)) throw new Error("Invalid Task Type lookup response.");
return { ok: true, items: result.items, debugRequests };
} catch (error) { error.debugRequests = debugRequests; throw error; }
}
return { requestWeekDetails, requestTimeEntries, requestTimeEntriesSave, requestTimeEntriesDelete, requestActiveServiceRequests, requestServiceRequestExpand, requestLookupTaskType };
}
// Source: src/shared/messages.js
/** Stable wire names. All payloads remain plain, serializable objects. */
function createMessageTypes() {
return Object.freeze({
requestWeekDetails: "opportunitiesExtension.requestWeekDetails",
requestTimeEntries: "opportunitiesExtension.requestTimeEntries",
requestTimeEntriesSave: "opportunitiesExtension.requestTimeEntriesSave",
requestTimeEntriesDelete: "opportunitiesExtension.requestTimeEntriesDelete",
requestLookupTaskType: "opportunitiesExtension.requestLookupTaskType",
requestServiceRequestExpand: "opportunitiesExtension.requestServiceRequestExpand",
requestResourceUser: "opportunitiesExtension.requestResourceUser",
requestActiveServiceRequests: "opportunitiesExtension.requestActiveServiceRequests",
getXsrfToken: "opportunitiesExtension.getXsrfToken",
refreshXsrfCookie: "opportunitiesExtension.refreshXsrfCookie",
requestOpptyProducts: "opportunitiesExtension.requestOpptyProducts",
opptyProductsProgress: "opportunitiesExtension.opptyProductsProgress",
opptyProductsRequestDebug: "opportunitiesExtension.opptyProductsRequestDebug",
requestConsumerTokenService: "opportunitiesExtension.requestConsumerTokenService",
requestAccountPlans: "opportunitiesExtension.requestAccountPlans",
requestInstallBase: "opportunitiesExtension.requestInstallBase",
requestCustomerServiceRequest: "opportunitiesExtension.requestCustomerServiceRequest",
requestWlActions: "opportunitiesExtension.requestWlActions",
requestWorkloads: "opportunitiesExtension.requestWorkloads",
requestWorkloadForecastUpdate: "opportunitiesExtension.requestWorkloadForecastUpdate",
requestActionTeamMembers: "opportunitiesExtension.requestActionTeamMembers",
requestActionFindLookup: "opportunitiesExtension.requestActionFindLookup",
requestMgmtAction: "opportunitiesExtension.requestMgmtAction",
requestInternalServiceRequest: "opportunitiesExtension.requestInternalServiceRequest",
requestSrLookupPrimaryPillar: "opportunitiesExtension.requestSrLookupPrimaryPillar",
requestPostServiceRequest: "opportunitiesExtension.requestPostServiceRequest",
requestPostInternalServiceRequest: "opportunitiesExtension.requestPostInternalServiceRequest",
prepareSrSession: "opportunitiesExtension.prepareSrSession",
requestSrLookupLanguage: "opportunitiesExtension.requestSrLookupLanguage",
requestSrLookupTechPlatform: "opportunitiesExtension.requestSrLookupTechPlatform",
requestLookupTypePlatform: "opportunitiesExtension.requestLookupTypePlatform",
requestInternalSrServices: "opportunitiesExtension.requestInternalSrServices",
requestSrLookupOpportunityBusiness: "opportunitiesExtension.requestSrLookupOpportunityBusiness",
requestCurrentUser: "opportunitiesExtension.requestCurrentUser",
invalidateProductsCache: "opportunitiesExtension.invalidateProductsCache",
requestConsumptionTrend: "opportunitiesExtension.requestConsumptionTrend",
requestConsumptionPlanDetail: "opportunitiesExtension.requestConsumptionPlanDetail",
requestRatecardByPlan: "opportunitiesExtension.requestRatecardByPlan"
});
}
// Source: src/shared/debug-body.js
function createDebugBodyPreview(text) {
return { value: text || "(empty response body)", truncated: false };
}
// Source: src/background/contracts.js
function createBackgroundContracts() {
const ORACLE_DOMAIN = "eeho.fa.us2.oraclecloud.com";
const ORACLE_COOKIE_URLS = [
"https://eeho.fa.us2.oraclecloud.com/",
"https://eeho.fa.us2.oraclecloud.com/hcmUI/faces/FuseWelcome",
"https://eeho.fa.us2.oraclecloud.com/fscmUI/faces/FuseWelcome",
"https://eeho.fa.us2.oraclecloud.com/fscmRestApi/tokenrelay"
];
const XSRF_COOKIE_NAME = "XSRF-TOKEN-US2DZ2V_F";
const XSRF_COOKIE_PREFIX = "XSRF-TOKEN-";
const OPPORTUNITIES_LIST_URL = "https://eeho.fa.us2.oraclecloud.com/fscmUI/redwood/cx-sales/application/container/opportunities/opportunities-list";
const COOKIE_REFRESH_TIMEOUT_MS = 15000;
const COOKIE_REFRESH_POLL_MS = 250;
const OPPTY_PRODUCTS_CACHE_KEY = "opportunitiesExtension.opptyProductsCache.v1";
const OPPTY_PRODUCTS_PAGE_LIMIT = 15;
const OPPTY_PRODUCTS_MAX_PAGES = 250;
const OPPTY_PRODUCTS_CONCURRENCY = 4;
const CUSTOMER_TOKEN_SERVICE_URL = "https://spa.oracle.com/oalcrm/web/api/g2m-consumer-application/consumerTokenService";
const CUSTOMER_TOKEN_SESSION_URL = "https://spa.oracle.com/";
const CUSTOMER_TOKEN_MAX_ATTEMPTS = 5;
const CUSTOMER_TOKEN_RETRY_BASE_DELAY_MS = 750;
const CUSTOMER_TOKEN_RETRY_MAX_DELAY_MS = 4000;
const ACCOUNT_SUMMARY_URL = "https://spa.oracle.com/oalcrm/web/api/provider-proxy/g2m-account/service/accountsummary";
const ACCOUNT_PLANS_URL = "https://spa.oracle.com/oalcrm/web/api/provider-proxy/g2m-consumption/service/v1/account/accountPlans";
const CONSUMPTION_TREND_URL = "https://spa.oracle.com/oalcrm/web/api/provider-proxy/g2m-consumption/service/v1/plan/trend";
const CONSUMPTION_TREND_REQUESTS = {
quarterly: "requestConsumptionServiceQuarterly",
monthly: "requestConsumptionServiceMonthly",
weekly: "requestConsumptionServiceWeekly",
daily: "requestConsumptionServiceDaily"
};
const ACCOUNT_PLANS_PAYLOAD = {
hierarchy: "single",
products: "all",
language: "english",
planStatus: [
"active",
"awaiting_provisioning",
"awaiting_provisioning_error",
"draft",
"expired",
"final_billing",
"onhold",
"provisioned",
"signed",
"superseded",
"superseded_pending_final_billing",
"suspended",
"terminated",
"terminated_pending_final_billing"
]
};
const DEBUG_BODY_LIMIT = 100000;
const OPPTY_PRODUCTS_FIELDS = [
"ProductType", "Description", "InventoryItemId", "ProdGroupName", "ProdGroupId", "OwnerLockAsgnFlag",
"Quantity", "RecurTypeCode", "RevnAmountCurcyCode", "UnitPrice", "RevnAmount", "PriceTypeCode",
"EffectiveDate", "Name1", "NonRecurringRevenue", "OpportunityOwnerPartyName", "OpportunityOwnerResourcePartyId",
"OptyId", "OptyNumber", "PartyName2", "PrTerritoryVersionId", "PrTerritoryVersionIdForManual",
"RecurEndDate", "RecurFrequencyCode", "RecurNumberPeriods", "RecurRevenue", "ResourcePartyId", "RevnId",
"RevnNumber", "SplitPercent", "SalesCreditTypeCode", "SplitTypeCode", "TerrOwnerPartyName", "UpsideAmount",
"UsageRevenue", "StatusCode", "BUOrgId", "TypeCode", "WinProb", "ForecastType_c", "ARRLocalCurrency_c",
"CPQLastSyncDate_c", "CPQOperationType_c", "ProdGLID_c", "ProposalNumber_c", "QuoteNumber_c",
"ServicesPeriod_c", "CPQIntLastUpdatedBy_c", "CPQUpdatedAmount_c", "CPQUpdatedQuantity_c",
"CPQUpdatedRevnType_c", "CPQUpdatedServicePeriod_c", "CPQUpdatedStatus_c", "OrderNumber_c", "PrevAmount_c",
"PrevQuantity_c", "PrevRevenueType_c", "PrevServicePeriod_c", "PrevStatus_c", "SubscriptionID_c",
"WorkloadName_c", "ConsumptionStartDate_c", "RampMonths_c"
];
return { ORACLE_DOMAIN, ORACLE_COOKIE_URLS, XSRF_COOKIE_NAME, XSRF_COOKIE_PREFIX, OPPORTUNITIES_LIST_URL, COOKIE_REFRESH_TIMEOUT_MS, COOKIE_REFRESH_POLL_MS, OPPTY_PRODUCTS_CACHE_KEY, OPPTY_PRODUCTS_PAGE_LIMIT, OPPTY_PRODUCTS_MAX_PAGES, OPPTY_PRODUCTS_CONCURRENCY, CUSTOMER_TOKEN_SERVICE_URL, CUSTOMER_TOKEN_SESSION_URL, CUSTOMER_TOKEN_MAX_ATTEMPTS, CUSTOMER_TOKEN_RETRY_BASE_DELAY_MS, CUSTOMER_TOKEN_RETRY_MAX_DELAY_MS, ACCOUNT_SUMMARY_URL, ACCOUNT_PLANS_URL, CONSUMPTION_TREND_URL, CONSUMPTION_TREND_REQUESTS, ACCOUNT_PLANS_PAYLOAD, DEBUG_BODY_LIMIT, OPPTY_PRODUCTS_FIELDS };
}
// Source: src/features/products/cache-state.js
function createProductsCacheState() {
return {
productsCachePromise: null,
productsCacheWritePromise: Promise.resolve(),
inFlightProducts: new Map()
};
}
// Source: src/auth/consumer-session.js
function createAuthConsumerSession({
CUSTOMER_TOKEN_MAX_ATTEMPTS,
CUSTOMER_TOKEN_RETRY_BASE_DELAY_MS,
CUSTOMER_TOKEN_RETRY_MAX_DELAY_MS,
CUSTOMER_TOKEN_SERVICE_URL,
CUSTOMER_TOKEN_SESSION_URL,
extractTokenServiceAuthorization,
fetchJson,
supportsFirefoxCookieStoreTabs,
tabsCreate,
tabsUpdate,
tabsRemove,
responseHeaders,
cookieChanges,
inspectPage,
setTimeout,
clearTimeout,
wait
}) {
const sessionTabs = new Map();
async function requestConsumerTokenService(sender) {
const debugRequests = [];
try {
const tokenResponse = await fetchConsumerTokenServiceUntilJson(sender, debugRequests);
const accountSummaryAuthorization = extractTokenServiceAuthorization(
tokenResponse,
"ACCP-TS",
"account-ti-authorization"
);
const accountPlansAuthorization = extractTokenServiceAuthorization(
tokenResponse,
"OCICONS-TS",
"g2m-authorization"
);
const tokenRequestDebugEntry = debugRequests[debugRequests.length - 1];
if (tokenRequestDebugEntry) {
tokenRequestDebugEntry.metadata = {
...tokenRequestDebugEntry.metadata,
accountSummaryTokenService: "ACCP-TS",
accountSummaryAuthorizationHeader: accountSummaryAuthorization.name || "(missing)",
accountSummaryAuthorizationValue: accountSummaryAuthorization.value
? `(present, ${accountSummaryAuthorization.value.length} characters)`
: "(missing)",
accountSummaryAuthorizationExpires: accountSummaryAuthorization.expires || "(not provided)",
accountPlansTokenService: "OCICONS-TS",
accountPlansAuthorizationHeader: accountPlansAuthorization.name || "(missing)",
accountPlansAuthorizationValue: accountPlansAuthorization.value
? `(present, ${accountPlansAuthorization.value.length} characters)`
: "(missing)",
accountPlansAuthorizationExpires: accountPlansAuthorization.expires || "(not provided)"
};
}
if (!accountSummaryAuthorization.value) {
throw new Error("ACCP-TS uiaasHeader.value was not found in requestConsumerTokenService.");
}
if (!accountPlansAuthorization.value) {
throw new Error("OCICONS-TS uiaasHeader.value was not found in requestConsumerTokenService.");
}
return {
ok: true,
authorizations: {
accountSummary: accountSummaryAuthorization,
accountPlans: accountPlansAuthorization,
installBase: extractTokenServiceAuthorization(tokenResponse, "SAIB-TS", ""),
customerServiceRequests: extractTokenServiceAuthorization(tokenResponse, "SPA-ORANGE", ""),
workloadActions: extractTokenServiceAuthorization(tokenResponse, "WWB-TS", "Wwb-Provider-Authorization")
},
debugRequests
};
} catch (error) {
error.debugRequests = debugRequests;
throw error;
}
}
async function fetchConsumerTokenServiceUntilJson(sender, debugRequests) {
let attempt = 0;
let sessionTab = null;
let receivedValidJson = false;
try {
while (attempt < CUSTOMER_TOKEN_MAX_ATTEMPTS) {
attempt += 1;
try {
const tokenResponse = await fetchJson(CUSTOMER_TOKEN_SERVICE_URL, {
method: "GET",
credentials: "include",
cache: "no-store",
headers: {
Accept: "application/json",
"Cache-Control": "no-cache, no-store, max-age=0",
Pragma: "no-cache"
}
}, "requestConsumerTokenService", debugRequests);
const debugEntry = debugRequests[debugRequests.length - 1];
if (debugEntry) {
debugEntry.metadata.attempt = String(attempt);
}
receivedValidJson = true;
return tokenResponse;
} catch (error) {
const isHtmlFallback = error
&& error.code === "INVALID_JSON_RESPONSE"
&& error.httpStatus === 200;
const isSessionUnauthorized = error && (error.httpStatus === 401 || error.httpStatus === 403);
const needsSessionRecovery = isHtmlFallback || isSessionUnauthorized;
const retryReason = isSessionUnauthorized
? `HTTP ${error.httpStatus} response requires SPA session recovery`
: "HTTP 200 response did not contain valid JSON";
const debugEntry = debugRequests[debugRequests.length - 1];
if (!needsSessionRecovery || attempt >= CUSTOMER_TOKEN_MAX_ATTEMPTS) {
if (debugEntry) {
debugEntry.metadata.attempt = String(attempt);
if (needsSessionRecovery) {
debugEntry.metadata.retryLimitReached = "true";
debugEntry.metadata.retryReason = retryReason;
debugEntry.error = isHtmlFallback
? `requestConsumerTokenService returned invalid JSON after ${CUSTOMER_TOKEN_MAX_ATTEMPTS} attempts.`
: `requestConsumerTokenService failed with status ${error.httpStatus} after ${CUSTOMER_TOKEN_MAX_ATTEMPTS} attempts.`;
error.message = debugEntry.error;
}
}
throw error;
}
if (!sessionTab) {
try {
sessionTab = await openConsumerTokenSessionTabWithRetry(sender, debugEntry);
if (debugEntry) {
debugEntry.metadata.sessionBootstrapUrl = CUSTOMER_TOKEN_SESSION_URL;
debugEntry.metadata.sessionBootstrapState = "opened-inactive-tab";
}
} catch (sessionError) {
if (debugEntry) {
debugEntry.metadata.sessionBootstrapUrl = CUSTOMER_TOKEN_SESSION_URL;
debugEntry.metadata.sessionBootstrapState = `tab-open-failed: ${sessionError.message || "unknown error"}`;
}
}
} else if (debugEntry) {
debugEntry.metadata.sessionBootstrapState = "waiting-for-valid-session";
}
const retryDelayMs = getConsumerTokenRetryDelay(attempt);
if (debugEntry) {
debugEntry.metadata.attempt = String(attempt);
debugEntry.metadata.retryReason = retryReason;
debugEntry.metadata.nextRetryDelayMs = String(retryDelayMs);
}
if (sessionTab) await waitForConsumerTokenSession(sessionTab, debugEntry, retryDelayMs);
else await wait(retryDelayMs);
}
}
throw new Error("requestConsumerTokenService retry loop ended unexpectedly.");
} finally {
if (sessionTab && Number.isInteger(sessionTab.id)) {
const debugEntry = debugRequests[debugRequests.length - 1];
try {
await closeConsumerTokenSessionTab(sessionTab.id);
if (debugEntry) {
debugEntry.metadata.sessionBootstrapState = receivedValidJson
? "closed-after-valid-json"
: "closed-after-retry-finished";
}
} catch (error) {
if (debugEntry) {
debugEntry.metadata.sessionBootstrapState = `tab-close-failed: ${error.message || "unknown error"}`;
}
}
}
}
}
function getConsumerTokenRetryDelay(attempt) {
return Math.min(
CUSTOMER_TOKEN_RETRY_BASE_DELAY_MS * (2 ** Math.max(attempt - 1, 0)),
CUSTOMER_TOKEN_RETRY_MAX_DELAY_MS
);
}
async function openConsumerTokenSessionTabWithRetry(sender, debugEntry) {
let attempt = 0;
// Opening failures must not consume the token service's five attempts.
while (true) {
attempt += 1;
if (debugEntry) {
debugEntry.metadata.sessionBootstrapUrl = CUSTOMER_TOKEN_SESSION_URL;
debugEntry.metadata.sessionTabOpenAttempt = String(attempt);
}
try {
const tab = await openConsumerTokenSessionTab(sender);
if (debugEntry) {
debugEntry.metadata.sessionBootstrapState = "opened-inactive-tab";
delete debugEntry.metadata.sessionTabNextRetryDelayMs;
}
return tab;
} catch (error) {
const retryDelayMs = getConsumerTokenRetryDelay(Math.min(attempt, 5));
if (debugEntry) {
debugEntry.metadata.sessionBootstrapState = "retrying-tab-open";
debugEntry.metadata.sessionTabLastOpenError = error.message || "unknown error";
debugEntry.metadata.sessionTabNextRetryDelayMs = String(retryDelayMs);
}
await wait(retryDelayMs);
}
}
}
async function openConsumerTokenSessionTab(sender) {
const details = {
url: CUSTOMER_TOKEN_SESSION_URL,
active: false
};
if (sender && sender.tab && Number.isInteger(sender.tab.windowId)) {
details.windowId = sender.tab.windowId;
}
if (supportsFirefoxCookieStoreTabs() && sender && sender.tab && sender.tab.cookieStoreId) {
details.cookieStoreId = sender.tab.cookieStoreId;
}
const tab = await tabsCreate(details);
if (Number.isInteger(tab?.id)) {
const state = { failedStatus: 0, reloads: 0, lastReloadAt: -Infinity, readyVersion: 0, consumedVersion: 0, closed: false };
const signal = reason => {
state.readyVersion++;
state.readyReason = reason;
state.wake?.();
};
state.listener = details => {
if (details.tabId !== tab.id
|| !/^https:\/\/spa\.oracle\.com\//.test(details.url)) return;
if (details.type === "main_frame") {
if (details.statusCode === 401 || details.statusCode === 403) {
state.failedStatus = details.statusCode;
state.consumedVersion = state.readyVersion;
state.wake?.();
} else if (details.statusCode >= 200 && details.statusCode < 300) {
state.failedStatus = 0;
if (!details.url.startsWith("https://spa.oracle.com/cloudgate/")) signal("spa-document-response");
}
} else if (details.type === "xmlhttprequest" && details.statusCode === 200
&& details.url.split("?")[0] === "https://spa.oracle.com/oalcrm/web/api/identity-management/users/current"
&& details.responseHeaders?.some(header => header.name.toLowerCase() === "content-type" && /application\/json/i.test(header.value))) {
state.failedStatus = 0;
signal("spa-identity-json-response");
}
};
state.cookieListener = change => {
const cookie = change.cookie;
if (change.removed || !cookie?.value || !cookie.secure || !cookie.httpOnly
|| cookie.domain?.replace(/^\./, "") !== "spa.oracle.com" || !/session/i.test(cookie.name)
|| (cookie.expirationDate != null && cookie.expirationDate <= Date.now() / 1000)
|| (sender?.tab?.cookieStoreId && cookie.storeId !== sender.tab.cookieStoreId)) return;
signal("spa-session-cookie-changed");
};
try {
responseHeaders?.addListener(state.listener, { urls: ["https://spa.oracle.com/*"], types: ["main_frame", "xmlhttprequest"] }, ["responseHeaders"]);
cookieChanges?.addListener(state.cookieListener);
sessionTabs.set(tab.id, state);
} catch (error) {
responseHeaders?.removeListener?.(state.listener);
cookieChanges?.removeListener(state.cookieListener);
await tabsRemove(tab.id).catch(() => {});
throw error;
}
}
return tab;
}
async function recoverConsumerTokenSessionTab(tab, debugEntry, reason = "") {
const state = sessionTabs.get(tab.id);
if (!state || (!state.failedStatus && !reason) || Date.now() - state.lastReloadAt < 2000) return false;
const status = state.failedStatus;
state.failedStatus = 0;
state.consumedVersion = state.readyVersion;
state.lastReloadAt = Date.now();
state.reloads++;
if (debugEntry) {
debugEntry.metadata.sessionBootstrapReloadCount = String(state.reloads);
debugEntry.metadata.sessionBootstrapReloadReason = reason || `SPA navigation returned HTTP ${status}`;
}
try {
// Restart at the entry point; OAuth callback URLs may contain single-use state.
await tabsUpdate(tab.id, { url: CUSTOMER_TOKEN_SESSION_URL, active: false });
if (debugEntry) debugEntry.metadata.sessionBootstrapReloadState = "restarted-at-spa-home";
return true;
} catch (error) {
state.failedStatus = status;
if (debugEntry) debugEntry.metadata.sessionBootstrapReloadState = `restart-failed: ${error.message || "unknown error"}`;
return false;
}
}
async function waitForSpaPage(tab, debugEntry) {
const startedAt = Date.now();
const deadline = startedAt + 120000;
let restartedDocument = null;
let restarts = 0;
if (debugEntry) {
delete debugEntry.metadata.nextRetryDelayMs;
debugEntry.metadata.sessionWaitTimeoutMs = "120000";
debugEntry.metadata.sessionWaitReason = "inspecting-spa-document";
}
while (Date.now() < deadline) {
const state = sessionTabs.get(tab.id);
if (state?.closed) break;
let page;
try { page = await inspectPage(tab); } catch { /* Cross-origin SSO pages have no SPA receiver. */ }
if (debugEntry) debugEntry.metadata.spaPageState = page?.status || "loading-or-login";
const documentId = typeof page?.documentId === "string" ? page.documentId : null;
const alreadyRead = documentId && documentId === tab.spaDocumentId;
if (state?.failedStatus || page?.status === "error" || alreadyRead) {
if (restarts < 3 && (state?.failedStatus || documentId !== restartedDocument)) {
const previousReloads = state?.reloads || 0;
const restarted = await recoverConsumerTokenSessionTab(tab, debugEntry,
state?.failedStatus ? "" : alreadyRead ? "Identity rejected in this SPA document" : "SPA document displays an authorization error");
restarts += (state?.reloads || 0) - previousReloads;
if (restarted) restartedDocument = documentId;
}
} else if (page?.status === "ready" && documentId) {
// Confirm a stable, fully loaded document, not a transient SAML page.
await wait(500);
let confirmed;
try { confirmed = await inspectPage(tab); } catch { /* A redirect started during validation. */ }
if (confirmed?.status === "ready" && confirmed.documentId === documentId && !state?.failedStatus) {
tab.spaDocumentId = documentId;
if (debugEntry) {
debugEntry.metadata.sessionWaitReason = "spa-document-verified";
debugEntry.metadata.sessionWaitDurationMs = String(Date.now() - startedAt);
}
return;
}
}
await wait(1000);
}
const error = new Error("SPA page did not finish authentication. No further identity request was sent.");
error.code = "SPA_PAGE_NOT_READY";
throw error;
}
async function waitForConsumerTokenSession(tab, debugEntry, retryDelay) {
const state = sessionTabs.get(tab.id);
if (!state || !responseHeaders?.addListener || !setTimeout) return wait(retryDelay);
const startedAt = Date.now();
const deadline = startedAt + 20000;
if (debugEntry) {
delete debugEntry.metadata.nextRetryDelayMs;
debugEntry.metadata.sessionWaitTimeoutMs = "20000";
}
let restarts = 0;
let reason = "timeout-fallback";
// Do not issue API probes while SSO redirects are still establishing the session.
while (!state.closed && Date.now() < deadline) {
if (state.failedStatus) {
if (restarts >= 3) {
await wait(Math.max(0, deadline - Date.now()));
break;
}
await recoverConsumerTokenSessionTab(tab, debugEntry);
restarts++;
if (state.failedStatus) await wait(2000);
continue;
}
if (state.readyVersion > state.consumedVersion) {
await wait(250);
if (state.failedStatus) continue;
state.consumedVersion = state.readyVersion;
reason = state.readyReason;
break;
}
let timer;
try {
await new Promise(resolve => {
state.wake = resolve;
timer = setTimeout(resolve, Math.max(0, deadline - Date.now()));
});
} finally {
clearTimeout?.(timer);
state.wake = null;
}
}
if (debugEntry) {
debugEntry.metadata.sessionWaitReason = reason;
debugEntry.metadata.sessionWaitDurationMs = String(Date.now() - startedAt);
}
}
async function closeConsumerTokenSessionTab(id) {
const state = sessionTabs.get(id);
sessionTabs.delete(id);
if (state) {
state.closed = true;
state.wake?.();
responseHeaders?.removeListener?.(state.listener);
cookieChanges?.removeListener(state.cookieListener);
}
await tabsRemove(id);
}
return { requestConsumerTokenService, fetchConsumerTokenServiceUntilJson, getConsumerTokenRetryDelay, openConsumerTokenSessionTabWithRetry, openConsumerTokenSessionTab, recoverConsumerTokenSessionTab, waitForConsumerTokenSession, waitForSpaPage, closeConsumerTokenSessionTab };
}
// Source: src/auth/spa-credentials.js
function createAuthSpaCredentials({
findNestedObject
}) {
function normalizeMessageAuthorization(authorization, fallbackHeaderName) {
const headerName = authorization && typeof authorization.name === "string"
? authorization.name.trim()
: "";
const headerValue = authorization && typeof authorization.value === "string"
? authorization.value.trim()
: "";
return {
name: headerName || fallbackHeaderName,
value: headerValue
};
}
function extractTokenServiceAuthorization(response, tokenServiceName, fallbackHeaderName) {
const tokenService = Array.isArray(response)
? response.find((entry) => entry && entry.tokenService === tokenServiceName)
: findNestedObject(response, (entry) => entry.tokenService === tokenServiceName);
const authorizationHeader = tokenService && tokenService.uiaasHeader;
const headerName = authorizationHeader && typeof authorizationHeader.label === "string"
? authorizationHeader.label.trim()
: "";
const headerValue = authorizationHeader && typeof authorizationHeader.value === "string"
? authorizationHeader.value.trim()
: "";
return {
name: headerName || fallbackHeaderName,
value: headerValue,
expires: authorizationHeader && authorizationHeader.expires
? String(authorizationHeader.expires)
: ""
};
}
return { normalizeMessageAuthorization, extractTokenServiceAuthorization };
}
// Source: src/auth/sr-request-headers.js
function createSrRequestHeaders({ runtimeApi }) {
const origin = "https://comcipapic-oalprod.integration.ocp.oraclecloud.com";
const appPath = "/ic/builder/rt/SeaaS_initiation/";
const internalPath = "/ic/builder/rt/SeaaS_SR_management/live;profile=PROD/services/auth/1.1/proxy/oalsetCRMRestAPICloudAccount/uri/https/eeho.fa.us2.oraclecloud.com/crmRestApi/resources/latest/serviceRequests";
const storageKey = "opportunitiesExtension.srAppBuilderClientId.v1";
const maxAge = 60 * 60 * 1000;
let observed = null;
let started = false;
function valid(record) {
return record?.origin === origin && typeof record.value === "string"
&& /^[\x21-\x7e]{1,2048}$/.test(record.value)
&& Number.isFinite(record.observedAt) && record.observedAt <= Date.now()
&& Date.now() - record.observedAt < maxAge;
}
function observeHeaders(rawUrl, headers) {
let url;
try { url = new URL(rawUrl); } catch { return; }
if (url.origin !== origin || !/^\/ic\/builder\/rt\/(?:SeaaS_initiation|SeaaS_SR_management|oalset_timeentrymobile)\/live(?:\/|;)/.test(url.pathname)) return;
// Read only the client ID, not cookies, bodies or authorization.
const value = typeof headers?.get === "function" ? headers.get("x-appbuilder-client-id")
: headers?.find(item => item.name.toLowerCase() === "x-appbuilder-client-id")?.value;
const record = { origin, value: value?.trim(), observedAt: Date.now() };
if (!valid(record)) return;
observed = record;
// Session storage survives service-worker suspension without persisting across browser sessions.
runtimeApi.storage?.session?.set({ [storageKey]: record }).catch(() => {});
}
function start() {
if (started || !runtimeApi?.webRequest) return;
started = true;
const filter = { urls: [`${origin}${appPath}*`, `${origin}/ic/builder/rt/SeaaS_SR_management/*`, `${origin}/ic/builder/rt/oalset_timeentrymobile/*`], types: ["xmlhttprequest"] };
runtimeApi.webRequest.onBeforeSendHeaders?.addListener(details => {
if (details.tabId >= 0 && !details.incognito) observeHeaders(details.url, details.requestHeaders);
}, filter, ["requestHeaders"]);
runtimeApi.webRequest.onHeadersReceived?.addListener(details => {
if (!details.incognito) observeHeaders(details.url, details.responseHeaders);
}, filter, ["responseHeaders"]);
}
async function previewTokenHeaders(url) {
const target = new URL(url);
if (target.origin !== origin || !target.pathname.startsWith(appPath) || !/\/Catalogue_c(?:\/\d+)?$/.test(target.pathname)) {
throw new Error("Unexpected Service Request token URL.");
}
return sessionHeaders(target.origin, "application/vnd.oracle.adf.action+json");
}
async function sessionHeaders(targetOrigin, contentType) {
if (!valid(observed)) {
try {
const stored = await runtimeApi.storage?.session?.get(storageKey);
if (valid(stored?.[storageKey]) && !valid(observed)) observed = stored[storageKey];
} catch { /* A missing capture must not prevent trying the other required headers. */ }
}
return {
Accept: "application/json", "Content-Type": contentType,
Authorization: "Session", Origin: targetOrigin,
...(valid(observed) ? { "x-appbuilder-client-id": observed.value } : {})
};
}
async function tokenHeaders(url, serviceRequestUrl) {
const headers = await previewTokenHeaders(url);
if (!/\/Catalogue_c\/\d+$/.test(new URL(url).pathname)) throw new Error("Missing Catalogue Id.");
const serviceTarget = new URL(serviceRequestUrl);
const isServiceRequest = serviceTarget.pathname.startsWith(appPath) && serviceTarget.pathname.endsWith("/set/seaas/mgmt/serviceRequests");
if (serviceTarget.origin !== headers.Origin || !isServiceRequest || serviceTarget.search || serviceTarget.hash) {
throw new Error("Unexpected Service Request URL.");
}
if (!runtimeApi?.declarativeNetRequest?.updateSessionRules || !runtimeApi.runtime?.getURL) {
throw new Error("Reload the extension to enable the Service Request token header permissions.");
}
const extensionDomain = new URL(runtimeApi.runtime.getURL("/")).hostname;
const catalogUrl = url.slice(0, url.lastIndexOf("/") + 1);
const escapedCatalogUrl = catalogUrl.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
const escapedServiceUrl = serviceTarget.href.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
const ruleIds = [7201, 7202];
await runtimeApi.declarativeNetRequest.updateSessionRules({
removeRuleIds: ruleIds,
addRules: [`^${escapedCatalogUrl}[0-9]+$`, `^${escapedServiceUrl}$`].map((regexFilter, index) => ({
id: ruleIds[index], priority: 1,
action: { type: "modifyHeaders", requestHeaders: [{ header: "Origin", operation: "set", value: headers.Origin }] },
condition: { regexFilter, initiatorDomains: [extensionDomain],
requestMethods: ["post"], resourceTypes: ["xmlhttprequest"] }
}))
});
return headers;
}
async function internalServiceRequestHeaders(url) {
const target = new URL(url);
if (target.origin !== origin || target.pathname !== internalPath || target.search || target.hash) {
throw new Error("Unexpected Internal Service Request URL.");
}
if (!runtimeApi?.declarativeNetRequest?.updateSessionRules || !runtimeApi.runtime?.getURL) {
throw new Error("Reload the extension to enable the Internal Service Request header permissions.");
}
const headers = await sessionHeaders(target.origin, "application/json");
const extensionDomain = new URL(runtimeApi.runtime.getURL("/")).hostname;
const escapedUrl = target.href.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
await runtimeApi.declarativeNetRequest.updateSessionRules({
removeRuleIds: [7203],
addRules: [{
id: 7203, priority: 1,
action: { type: "modifyHeaders", requestHeaders: [{ header: "Origin", operation: "set", value: headers.Origin }] },
condition: { regexFilter: `^${escapedUrl}$`, initiatorDomains: [extensionDomain],
requestMethods: ["post"], resourceTypes: ["xmlhttprequest"] }
}]
});
return headers;
}
async function timeEntriesHeaders(url) {
const target = new URL(url);
const path = "/ic/builder/rt/oalset_timeentrymobile/live;profile=PROD/services/auth/1.1/proxy/oke_seaas/uri/https/gxpap.oracle.com/oalcrm/service/set/seaas/mgmt/timeEntries";
const isDelete = target.pathname.startsWith(`${path}/`) && /^[1-9]\d*$/.test(target.pathname.slice(path.length + 1));
if (target.origin !== origin || (target.pathname !== path && !isDelete) || target.search || target.hash) {
throw new Error("Unexpected time entries URL.");
}
if (!runtimeApi?.declarativeNetRequest?.updateSessionRules || !runtimeApi.runtime?.getURL) {
throw new Error("Reload the extension to enable the time entry header permissions.");
}
const escapedUrl = target.href.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
await runtimeApi.declarativeNetRequest.updateSessionRules({
removeRuleIds: [isDelete ? 7205 : 7204],
addRules: [{
id: isDelete ? 7205 : 7204, priority: 1,
action: { type: "modifyHeaders", requestHeaders: [{ header: "Origin", operation: "set", value: target.origin }] },
condition: { regexFilter: `^${escapedUrl}$`, initiatorDomains: [new URL(runtimeApi.runtime.getURL("/")).hostname],
requestMethods: isDelete ? ["delete"] : ["post", "put"], resourceTypes: ["xmlhttprequest"] }
}]
});
return sessionHeaders(target.origin, "application/json");
}
return { start, tokenHeaders, previewTokenHeaders, internalServiceRequestHeaders, timeEntriesHeaders, observeResponseHeaders: observeHeaders };
}
// Source: src/services/spa/accounts.js
function createSpaAccounts({
ACCOUNT_PLANS_PAYLOAD,
ACCOUNT_PLANS_URL,
ACCOUNT_SUMMARY_URL,
extractAccountPlans,
fetchJson,
findAccountPlanId,
normalizeMessageAuthorization
}) {
async function requestAccountPlans(message) {
const partyNumber = typeof message.partyNumber === "string" ? message.partyNumber.trim() : "";
const accountSummaryAuthorization = normalizeMessageAuthorization(
message.authorizations && message.authorizations.accountSummary,
"account-ti-authorization"
);
const accountPlansAuthorization = normalizeMessageAuthorization(
message.authorizations && message.authorizations.accountPlans,
"g2m-authorization"
);
const debugRequests = [];
if (!partyNumber) {
throw new Error("Customer Party Number is required.");
}
if (!accountSummaryAuthorization.value) {
throw new Error("ACCP-TS authorization is unavailable. requestConsumerTokenService must complete first.");
}
if (!accountPlansAuthorization.value) {
throw new Error("OCICONS-TS authorization is unavailable. requestConsumerTokenService must complete first.");
}
try {
const accountSummary = await fetchJson(ACCOUNT_SUMMARY_URL, {
method: "POST",
credentials: "include",
headers: {
Accept: "application/json",
[accountSummaryAuthorization.name]: accountSummaryAuthorization.value,
"Content-Type": "application/json"
},
body: JSON.stringify([{ key: "regId", value: partyNumber }])
}, "requestAccountSummary", debugRequests);
const accountPlanId = findAccountPlanId(accountSummary);
if (!accountPlanId) {
throw new Error("Account plan id was not found in requestAccountSummary.");
}
const accountPlansResponse = await fetchJson(`${ACCOUNT_PLANS_URL}/${encodeURIComponent(String(accountPlanId))}`, {
method: "POST",
credentials: "include",
headers: {
Accept: "application/json",
[accountPlansAuthorization.name]: accountPlansAuthorization.value,
"Content-Type": "application/json"
},
body: JSON.stringify(ACCOUNT_PLANS_PAYLOAD)
}, "requestAccountPlans", debugRequests);
return {
ok: true,
accountPlanId: String(accountPlanId),
plans: extractAccountPlans(accountPlansResponse),
debugRequests
};
} catch (error) {
error.debugRequests = debugRequests;
throw error;
}
}
return { requestAccountPlans };
}
// Source: src/services/spa/install-base.js
function createSpaInstallBase({ ACCOUNT_SUMMARY_URL, fetchJson, findNestedObject, normalizeMessageAuthorization }) {
const endpoint = "https://spa.oracle.com/oalcrm/web/api/provider-proxy/g2m-installbase-provider/service/account/";
async function requestInstallBase(message) {
const debugRequests = [];
try {
const partyNumber = String(message.partyNumber || "").trim();
const summaryAuth = normalizeMessageAuthorization(message.authorizations?.accountSummary, "account-ti-authorization");
const installAuth = normalizeMessageAuthorization(message.authorizations?.installBase, "");
if (!partyNumber) throw new Error("Customer Party Number is required.");
if (!summaryAuth.value) throw new Error("ACCP-TS authorization is unavailable.");
if (!installAuth.name || !installAuth.value) throw new Error("SAIB-TS uiaasHeader.label/value is unavailable.");
const summary = await fetchJson(ACCOUNT_SUMMARY_URL, {
method: "POST", credentials: "include",
headers: { Accept: "application/json", "Content-Type": "application/json", [summaryAuth.name]: summaryAuth.value },
body: JSON.stringify([{ key: "regId", value: partyNumber }])
}, "requestAccountSummary", debugRequests);
const account = findNestedObject(summary, value =>
(typeof value.registryId === "string" || typeof value.registryId === "number") && String(value.registryId).trim() !== "");
if (!account) throw new Error("registryId was not found in requestAccountSummary.");
const payload = {
text: "", accountHierarchy: "single", accountScope: "all", productScope: "all",
installStatus: ["active", "inactive", "unsupported"], productIds: null,
contractEndDate: { to: null, from: null }, contractStartDate: { to: null, from: null },
purchaseStartDate: { to: null, from: null }, registryIds: [], gecPartyNumbers: null
};
const items = [];
let offset = 0;
// Bound malformed pagination without silently returning a partial result.
for (let page = 0; page < 1000; page += 1) {
const url = `${endpoint}${encodeURIComponent(String(account.registryId).trim())}?orderBy=currentSupport%3Adesc&limit=25&offset=${offset}`;
const result = await fetchJson(url, {
method: "POST", credentials: "include",
headers: { Accept: "application/json", "Content-Type": "application/json", [installAuth.name]: installAuth.value },
body: JSON.stringify(payload)
}, "requestInstallBase", debugRequests);
if (!result || !Array.isArray(result.items) || typeof result.hasMore !== "boolean"
|| result.items.some(item => !item || typeof item !== "object" || Array.isArray(item))) {
throw new Error("requestInstallBase returned an invalid collection.");
}
if (result.offset !== undefined && result.offset !== offset) throw new Error("requestInstallBase returned an unexpected offset.");
items.push(...result.items);
if (!result.hasMore) return { ok: true, items, debugRequests };
if (!result.items.length) throw new Error("requestInstallBase pagination made no progress.");
offset += result.items.length;
}
throw new Error("requestInstallBase pagination limit reached.");
} catch (error) {
error.debugRequests = debugRequests;
throw error;
} finally {
// The provider chooses the credential header name; never rely on its spelling for redaction.
for (const entry of debugRequests) {
for (const key of Object.keys(entry.requestHeaders || {})) {
if (!/^(accept|content-type)$/i.test(key)) entry.requestHeaders[key] = "(present)";
}
}
}
}
return { requestInstallBase };
}
// Source: src/services/spa/customer-service-requests.js
function createSpaCustomerServiceRequests({ ACCOUNT_SUMMARY_URL, fetchJson, findAccountPlanId, normalizeMessageAuthorization }) {
const endpoint = "https://spa.oracle.com/oalcrm/web/api/provider-proxy/g2m-custsr-provider/service/oscSr/";
const filter = encodeURIComponent(JSON.stringify({ hierarchy: "single", status: "open" }));
async function requestCustomerServiceRequest(message) {
const debugRequests = [];
try {
const partyNumber = String(message.partyNumber || "").trim();
const summaryAuth = normalizeMessageAuthorization(message.authorizations?.accountSummary, "account-ti-authorization");
const srAuth = normalizeMessageAuthorization(message.authorizations?.customerServiceRequests, "");
if (!partyNumber) throw new Error("Customer Party Number is required.");
if (!summaryAuth.value) throw new Error("ACCP-TS authorization is unavailable.");
if (!srAuth.value) throw new Error("SPA-ORANGE uiaasHeader.value is unavailable.");
const summary = await fetchJson(ACCOUNT_SUMMARY_URL, {
method: "POST", credentials: "include",
headers: { Accept: "application/json", "Content-Type": "application/json", [summaryAuth.name]: summaryAuth.value },
body: JSON.stringify([{ key: "regId", value: partyNumber }])
}, "requestAccountSummary", debugRequests);
const accountSummaryId = findAccountPlanId(summary);
if ((typeof accountSummaryId !== "string" && typeof accountSummaryId !== "number") || !String(accountSummaryId).trim()) {
throw new Error("Account id was not found in requestAccountSummary.");
}
const items = [];
let offset = 0;
// This endpoint uses totalResults/count rather than hasMore.
for (let page = 0; page < 1000; page += 1) {
const url = `${endpoint}${encodeURIComponent(String(accountSummaryId))}?customerSRFilter=${filter}&orderBy=creationDate&orderByMode=desc&limit=40&offset=${offset}`;
const result = await fetchJson(url, {
method: "GET", credentials: "include",
// SPA-ORANGE's label may be lead-auth; this provider requires custsr-auth.
headers: { Accept: "application/json", "custsr-auth": srAuth.value }
}, "requestCustomerServiceRequest", debugRequests);
if (!result || !Array.isArray(result.objects)
|| result.objects.some(item => !item || typeof item !== "object" || Array.isArray(item))
|| !Number.isSafeInteger(result.totalResults) || result.totalResults < 0
|| !Number.isSafeInteger(result.count) || result.count !== result.objects.length) {
throw new Error("requestCustomerServiceRequest returned an invalid collection.");
}
if (result.offset !== offset) throw new Error("requestCustomerServiceRequest returned an unexpected offset.");
items.push(...result.objects);
offset += result.count;
if (offset >= result.totalResults) return { ok: true, items, debugRequests };
if (!result.count) throw new Error("requestCustomerServiceRequest pagination made no progress.");
}
throw new Error("requestCustomerServiceRequest pagination limit reached.");
} catch (error) {
error.debugRequests = debugRequests;
throw error;
} finally {
for (const entry of debugRequests) {
for (const key of Object.keys(entry.requestHeaders || {})) {
if (!/^(accept|content-type)$/i.test(key)) entry.requestHeaders[key] = "(present)";
}
}
}
}
return { requestCustomerServiceRequest };
}
// Source: src/services/spa/workload-actions.js
function createSpaWorkloadActions({ fetchJson }) {
async function requestWlActions(message) {
const debugRequests = [];
try {
const revnId = String(message.revnId ?? "").trim();
const token = message.authorization?.value;
if (!/^\d+$/.test(revnId)) throw new Error("A numeric RevnId is required.");
if (typeof token !== "string" || !token.trim()) throw new Error("WWB-TS uiaasHeader.value is unavailable.");
const url = `https://spa.oracle.com/oalcrm/web/api/provider-proxy/wwb-provider/service/workbench-proxy/wwb/mgmt/actions?parentType=WORKLOAD&parentId=${encodeURIComponent(revnId)}`;
const result = await fetchJson(url, {
method: "GET", credentials: "include",
headers: { Accept: "application/json", "Wwb-Provider-Authorization": token.trim() }
}, "requestWlActions", debugRequests);
if (!result || !Array.isArray(result.actions) || typeof result.hasNext !== "boolean"
|| result.actions.some(item => !item || typeof item !== "object" || Array.isArray(item))) {
throw new Error("requestWlActions returned an invalid collection.");
}
return { ok: true, actions: result.actions, hasNext: result.hasNext, debugRequests };
} catch (error) {
error.debugRequests = debugRequests;
throw error;
} finally {
for (const entry of debugRequests) {
if (entry.requestHeaders?.["Wwb-Provider-Authorization"]) entry.requestHeaders["Wwb-Provider-Authorization"] = "(present)";
}
}
}
return { requestWlActions };
}
// Source: src/services/spa/workloads.js
function createSpaWorkloads({ fetchJson, getCachedWorkloads, normalizeCacheMaxAge, invalidateWorkloadsCache }) {
async function requestWorkloadForecastUpdate(message) {
const debugRequests = [];
try {
const id = String(message.workloadId ?? "");
const token = message.authorization?.value;
if (!/^\d+$/.test(id)) throw new Error("A numeric workload ID is required.");
if (typeof token !== "string" || !token.trim()) throw new Error("WWB-TS uiaasHeader.value is unavailable.");
if (!Array.isArray(message.payload) || !message.payload.length) throw new Error("Monthly estimates are required.");
const periods = new Set();
const payload = message.payload.map(({ year, month, amount, updatedBy }) => {
if (!Number.isInteger(year) || year < 1000 || year > 9999 || !Number.isInteger(month) || month < 1 || month > 12
|| typeof amount !== "number" || !Number.isFinite(amount) || amount < 0
|| typeof updatedBy !== "string" || !updatedBy.trim()) throw new Error("Invalid monthly estimate.");
const period = `${year}-${month}`;
if (periods.has(period)) throw new Error("Duplicate forecast month.");
periods.add(period);
return { year, month, amount, updatedBy: updatedBy.trim() };
});
const result = await fetchJson(`https://spa.oracle.com/oalcrm/web/api/provider-proxy/wwb-provider/service/workbench-proxy/wwb/mgmt/workloads/${id}/forecast`, {
method: "PATCH", credentials: "include",
headers: { Accept: "application/json", "Content-Type": "application/json", "wwb-provider-authorization": token.trim() },
body: JSON.stringify(payload)
}, "requestWorkloadForecastUpdate", debugRequests, { allowEmpty: true });
await invalidateWorkloadsCache().catch(() => {});
return { ok: true, result, debugRequests };
} catch (error) {
error.debugRequests = debugRequests;
throw error;
}
}
async function requestWorkloads(message) {
const customerId = String(message.customerId ?? "").trim();
const token = message.authorization?.value;
if (!/^\d+$/.test(customerId)) throw new Error("A numeric CustomerAccount.PartyId is required.");
if (typeof token !== "string" || !token.trim()) throw new Error("WWB-TS uiaasHeader.value is unavailable.");
const result = await getCachedWorkloads(customerId, token.trim(), normalizeCacheMaxAge(message.cacheMaxAgeMs), message.principal);
return { ok: true, customerId, ...result };
}
async function fetchAllWorkloads(customerId, token) {
const debugRequests = [];
const workloads = new Map();
let offset = 0;
let limit = 500;
try {
for (let page = 0; page < 1000; page += 1) {
const url = new URL(`https://spa.oracle.com/oalcrm/web/api/provider-proxy/wwb-provider/service/workbench-proxy/wwb/mgmt/customers/${customerId}/workloads`);
for (const [name, value] of Object.entries({ workloadStatus: "Pipeline,Implementation,Live", opportunityForecastType: "All",
includeForEstimates: "ALL", financialYear: "ROLLING_4_QTRS", offset, limit })) url.searchParams.set(name, String(value));
const result = await fetchJson(url.toString(), {
method: "GET", credentials: "include",
headers: { Accept: "application/json", "wwb-provider-authorization": token }
}, "requestWorkloads", debugRequests);
if (!result || !Array.isArray(result.workloads) || typeof result.hasNext !== "boolean"
|| result.workloads.some(item => !item || !/^\d+$/.test(String(item.id ?? "")))) {
throw new Error("requestWorkloads returned an invalid collection.");
}
const countBefore = workloads.size;
for (const item of result.workloads) workloads.set(String(item.id), item);
if (!result.hasNext) return { workloads: [...workloads.values()], debugRequests };
const responseOffset = Number(result.offset ?? offset);
const responseLimit = Number(result.limit ?? limit);
if (workloads.size === countBefore || !Number.isSafeInteger(responseOffset) || responseOffset !== offset
|| !Number.isSafeInteger(responseLimit) || responseLimit <= 0 || !Number.isSafeInteger(responseOffset + responseLimit)) {
throw new Error("requestWorkloads pagination did not advance.");
}
offset = responseOffset + responseLimit;
limit = responseLimit;
}
throw new Error("requestWorkloads exceeded the pagination limit.");
} catch (error) {
error.debugRequests = debugRequests;
throw error;
}
}
return { requestWorkloads, fetchAllWorkloads, requestWorkloadForecastUpdate };
}
// Source: src/features/products/workloads-cache.js
function createWorkloadsCache({ fetchAllWorkloads, storageLocalGet, storageLocalSet }) {
const KEY = "opportunitiesExtension.workloadsCache.v1";
let cachePromise = null;
let writePromise = Promise.resolve();
let generation = 0;
const inFlight = new Map();
function read() {
if (!cachePromise) cachePromise = storageLocalGet(KEY)
.then(result => result?.[KEY] && typeof result[KEY] === "object" && !Array.isArray(result[KEY]) ? result[KEY] : {})
.catch(() => ({}));
return cachePromise;
}
function write(cache) {
writePromise = writePromise.catch(() => {}).then(() => storageLocalSet({ [KEY]: cache }));
return writePromise;
}
async function invalidate() {
generation += 1;
inFlight.clear();
cachePromise = Promise.resolve({});
await write({});
}
async function get(customerId, token, maxAgeMs, principal) {
const scope = typeof principal === "string" ? principal.trim().toLowerCase() : "";
// isMyWorkload is user-specific. Never persist a result without a known principal.
if (!scope) return { ...await fetchAllWorkloads(customerId, token), fromCache: false, cachedAt: Date.now() };
const key = JSON.stringify([scope, customerId]);
const currentGeneration = generation;
const cache = await read();
if (generation !== currentGeneration) return get(customerId, token, maxAgeMs, principal);
const entry = cache[key];
const age = Date.now() - Number(entry?.fetchedAt);
if (Array.isArray(entry?.items) && age >= 0 && age < maxAgeMs) {
return { workloads: entry.items, cachedAt: Number(entry.fetchedAt), fromCache: true, debugRequests: [] };
}
if (!inFlight.has(key)) {
const flight = fetchAllWorkloads(customerId, token).then(async result => {
const cachedAt = Date.now();
if (generation === currentGeneration) {
cache[key] = { fetchedAt: cachedAt, items: result.workloads };
await write(cache).catch(() => {});
}
return { ...result, cachedAt, fromCache: false };
}).finally(() => { if (inFlight.get(key) === flight) inFlight.delete(key); });
inFlight.set(key, flight);
}
return inFlight.get(key);
}
return { get, invalidate };
}
// Source: src/services/spa/action-options.js
function createSpaActionOptions({ fetchJson }) {
const baseUrl = "https://spa.oracle.com/oalcrm/web/api/provider-proxy/wwb-provider/service/workbench-proxy/wwb/mgmt/workloads";
const lookupKeys = ["WWB_ACTION_ROLE", "WWB_ACTION_NAME", "WWB_ACTION_TEAM", "WWB_ACTION_STATUS", "WWB_ACTION_COMPLEXITY"];
async function request(message, name, path, normalize) {
const debugRequests = [];
try {
const token = message.authorization?.value;
if (typeof token !== "string" || !token.trim()) throw new Error("WWB-TS uiaasHeader.value is unavailable.");
const result = await fetchJson(`${baseUrl}/${path()}`, {
method: "GET", credentials: "include",
headers: { Accept: "application/json", "Wwb-Provider-Authorization": token.trim() }
}, name, debugRequests);
if (debugRequests.at(-1)?.httpStatus !== 200) throw new Error(`${name} requires HTTP 200.`);
return { ok: true, ...normalize(result), debugRequests };
} catch (error) {
const entry = debugRequests.at(-1);
if (entry) { entry.status = "failed"; entry.error = error.message; }
error.debugRequests = debugRequests;
throw error;
} finally {
for (const entry of debugRequests) {
if (entry.requestHeaders?.["Wwb-Provider-Authorization"]) entry.requestHeaders["Wwb-Provider-Authorization"] = "(present)";
}
}
}
function requestActionTeamMembers(message) {
return request(message, "requestActionTeamMembers", () => {
const id = String(message.workloadId ?? "").trim();
if (!/^\d+$/.test(id)) throw new Error("A numeric RevnId is required.");
return `${encodeURIComponent(id)}/teamMembers?includeLeads=true`;
}, result => {
if (!Array.isArray(result) || result.some(item => typeof item?.email !== "string" || !item.email.trim())) {
throw new Error("requestActionTeamMembers returned an invalid collection.");
}
return { members: result.map(item => ({ email: item.email })) };
});
}
function requestActionFindLookup(message) {
return request(message, "requestActionFindLookup", () => "fndLookup", result => {
const lookups = {};
for (const key of lookupKeys) {
if (!Array.isArray(result?.[key]) || result[key].some(item => typeof item?.code !== "string" || !item.code.trim()
|| typeof item?.name !== "string" || !item.name.trim())) {
throw new Error(`requestActionFindLookup returned an invalid ${key} collection.`);
}
lookups[key] = result[key].map(({ code, name }) => ({ code, name }));
}
return { lookups };
});
}
async function requestMgmtAction(message) {
const debugRequests = [];
try {
const token = message.authorization?.value;
const payload = message.payload;
const action = payload?.action;
if (!token || typeof token !== "string") throw new Error("WWB-TS uiaasHeader.value is unavailable.");
if (payload?.parentType !== "WORKLOAD" || !/^\d+$/.test(String(payload.parentId))) throw new Error("A numeric RevnId is required.");
for (const key of ["ownerEmail", "role", "name", "team", "status", "complexity", "createdBy", "updatedBy"]) {
if (typeof action?.[key] !== "string" || !action[key].trim()) throw new Error(`Missing ${key}.`);
}
for (const key of ["startDate", "endDate"]) {
if (typeof action[key] !== "string" || !/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/.test(action[key]) || !Number.isFinite(Date.parse(action[key]))) throw new Error(`Invalid ${key}.`);
}
if (action.endDate < action.startDate) throw new Error("End date must not precede start date.");
if (!Array.isArray(action.notes) || action.notes.some(note => typeof note !== "string")) throw new Error("Invalid notes.");
const result = await fetchJson("https://spa.oracle.com/oalcrm/web/api/provider-proxy/wwb-provider/service/workbench-proxy/wwb/mgmt/actions", {
method: "POST", credentials: "include",
headers: { Accept: "application/json", "Content-Type": "application/json", "Wwb-Provider-Authorization": token.trim() },
body: JSON.stringify(payload)
}, "requestMgmtAction", debugRequests, { allowEmpty: true });
return { ok: true, result, debugRequests };
} catch (error) {
error.debugRequests = debugRequests;
throw error;
} finally {
for (const entry of debugRequests) {
if (entry.requestHeaders?.["Wwb-Provider-Authorization"]) entry.requestHeaders["Wwb-Provider-Authorization"] = "(present)";
}
}
}
return { requestActionTeamMembers, requestActionFindLookup, requestMgmtAction };
}
// Source: src/services/fusion/sr-details.js
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") };
}
// Source: src/services/fusion/sr-create.js
function createFusionSrCreate({ fetchJson, tokenHeaders, previewTokenHeaders, internalServiceRequestHeaders, runtimeApi }) {
const resourceRequests = new Map();
let resourceGeneration = 0;
const resourceCacheKey = "opportunitiesExtension.resourceUsers.v1";
const base = "https://comcipapic-oalprod.integration.ocp.oraclecloud.com/ic/builder/rt/SeaaS_initiation/live;profile=PROD/services/auth/1.1/proxy/";
const catalog = base + "crmRestApiResources/uri/https/eeho.fa.us2.oraclecloud.com/crmRestApi/resources/11.13.18.05/Catalogue_c";
const resourceUsers = base + "oke_seaas/uri/https/gxpap.oracle.com/oalcrm/service/set/seaas/crm/resourceUsers";
const endpoint = base + "oke_seaas/uri/https/gxpap.oracle.com/oalcrm/service/set/seaas/mgmt/serviceRequests";
function numericId(value, name) {
if (!/^\d+$/.test(String(value ?? "")) || !Number.isSafeInteger(Number(value)) || Number(value) <= 0) throw new Error(`Missing or invalid ${name}.`);
return Number(value);
}
function text(value, name) {
if (typeof value !== "string" || !value.trim()) throw new Error(`Missing ${name}.`);
return value.trim();
}
function dateOnly(value, name) {
const date = text(value, name);
if (!/^\d{4}-\d{2}-\d{2}$/.test(date) || !Number.isFinite(Date.parse(`${date}T00:00:00Z`))
|| new Date(`${date}T00:00:00Z`).toISOString().slice(0, 10) !== date) throw new Error(`Invalid ${name}.`);
return date;
}
function payloadFor(input) {
const payload = {};
for (const name of ["AccountPartyId", "OpportunityName_Id_c", "ServiceNew_Id_c"]) payload[name] = numericId(input?.[name], name);
for (const name of ["Title", "RegionText_c", "ProblemDescription", "Language_c", "CustomerDeadline_c", "TechPlatformLookUp_c", "CountryText_c", "PrimaryPillarNew_c"]) payload[name] = text(input?.[name], name);
dateOnly(payload.CustomerDeadline_c, "Customer deadline");
return { ...payload, SvcQuestionsCollection_c: [], SourceCd: "SVC_OPPORTUNITY_SCREEN", SeverityCd: "ORA_SVC_SEV2", ChannelTypeCd: "ORA_SVC_WEB" };
}
async function requestResourceUser(message, debugRequests) {
try {
const result = await getResourceUser(message);
debugRequests.push(...result.debugRequests);
return numericId(result.resource.resourcePartyId, "resourcePartyId");
} catch (error) { debugRequests.push(...(error.debugRequests || [])); throw error; }
}
async function getResourceUser(message) {
const emailAddress = text(message.emailAddress, "current user email").toLowerCase();
const duration = Number(message.cacheMaxAgeMs) > 0 ? Math.min(Number(message.cacheMaxAgeMs), 168 * 3600000) : 24 * 3600000;
const fresh = timestamp => Number.isFinite(timestamp) && Date.now() >= timestamp && Date.now() - timestamp < duration;
const current = resourceRequests.get(emailAddress);
if (current && (current.fetchedAt === null || fresh(current.fetchedAt))) return { ok: true, resource: await current.promise, debugRequests: [] };
const generation = resourceGeneration;
const record = { promise: null, fetchedAt: null };
const debugRequests = [];
const pending = (async () => {
try {
const stored = await runtimeApi?.storage?.session?.get(resourceCacheKey);
const cached = stored?.[resourceCacheKey];
if (cached?.emailAddress === emailAddress && cached.resource?.resourcePartyId && fresh(cached.fetchedAt)) {
record.fetchedAt = cached.fetchedAt;
return cached.resource;
}
} catch { /* Session storage is optional; retain the in-memory cache. */ }
const resource = await fetchJson(`${resourceUsers}?emailAddress=${encodeURIComponent(emailAddress)}`, {
method: "GET", credentials: "include", headers: { Accept: "application/json", Authorization: "Session" }
}, "requestResourceUser", debugRequests);
numericId(resource?.resourcePartyId, "resourcePartyId");
record.fetchedAt = Date.now();
if (generation === resourceGeneration) {
try { await runtimeApi?.storage?.session?.set({ [resourceCacheKey]: { emailAddress, resource, fetchedAt: record.fetchedAt } }); } catch { /* Keep the successful response in memory. */ }
}
return resource;
})();
record.promise = pending;
resourceRequests.set(emailAddress, record);
try { return { ok: true, resource: await pending, debugRequests }; }
catch (error) { if (resourceRequests.get(emailAddress) === record) resourceRequests.delete(emailAddress); error.debugRequests = debugRequests; throw error; }
}
async function requestPostInternalServiceRequest(message) {
const debugRequests = [];
try {
const source = message.payload;
const parentSrId = numericId(source?.ServiceRequest_Id_SRToInternalSR, "parent SrId");
const payload = {
Title: text(source?.Title, "Title"),
ServiceNew_Id_c: numericId(source?.ServiceNew_Id_c, "ServiceNew_Id_c"),
ProblemDescription: text(source?.ProblemDescription, "Description"),
CustomerDeadline_c: dateOnly(source?.CustomerDeadline_c, "Customer deadline"),
TechPlatformLookUp_c: text(source?.TechPlatformLookUp_c, "Platform"),
InternalDeadline_c: `${dateOnly(source?.InternalDeadline_c, "Internal Deadline")}T00:00:00.000Z`,
SvcQuestionsCollection_c: []
};
const parent = await requestSRInfo(parentSrId, debugRequests);
payload.ServiceRequest_Id_SRToInternalSR = String(parentSrId);
for (const name of ["AccountPartyId", "OpportunityName_Id_c"]) payload[name] = numericId(parent[name], name);
for (const name of ["OpportunityName_c", "RegionText_c", "SvcHealth_c", "Language_c", "CountryText_c", "PrimaryPillarNew_c"]) payload[name] = text(parent[name], name);
payload.SecondaryPillarNew_c = parent.SecondaryPillarNew_c == null ? null : text(parent.SecondaryPillarNew_c, "SecondaryPillarNew_c");
payload.ReportedByPartyId = await requestResourceUser(message, debugRequests);
const url = "https://comcipapic-oalprod.integration.ocp.oraclecloud.com/ic/builder/rt/SeaaS_SR_management/live;profile=PROD/services/auth/1.1/proxy/oalsetCRMRestAPICloudAccount/uri/https/eeho.fa.us2.oraclecloud.com/crmRestApi/resources/latest/serviceRequests";
const result = await fetchJson(url, { method: "POST", credentials: "include",
headers: await internalServiceRequestHeaders(url),
body: JSON.stringify(payload)
}, "requestPostInternalServiceRequest", debugRequests, { allowEmpty: true });
return { ok: true, result, debugRequests };
} catch (error) { error.debugRequests = debugRequests; throw error; }
}
async function requestSRInfo(parentSrId, debugRequests) {
const url = new 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");
url.searchParams.set("fields", "PrimaryPillarNew_c,SecondaryPillarNew_c,SvcHealth_c,SrNumber,AccountPartyId,CountryText_c,Language_c,OpportunityName_Id_c,OpportunityName_c,RegionText_c,ServiceNew_Id_c");
url.searchParams.set("onlyData", "true");
url.searchParams.set("orderBy", "LastUpdateDate:desc");
url.searchParams.set("q", `SrId=${parentSrId}`);
const result = await fetchJson(url.href, {
method: "GET", credentials: "include", headers: { Accept: "application/json", Authorization: "Session" }
}, "requestSRInfo", debugRequests);
if (!Array.isArray(result?.items) || result.items.length !== 1 || !result.items[0]
|| (result.items[0].SrId != null && String(result.items[0].SrId) !== String(parentSrId))) {
throw new Error("requestSRInfo did not return the selected parent Service Request.");
}
return result.items[0];
}
async function requestPostServiceRequest(message) {
const debugRequests = [];
try {
if (message.phase !== "prepare" && message.phase !== "confirm") throw new Error("Service Request confirmation is required.");
const payload = payloadFor(message.payload);
if (message.phase === "prepare") {
payload.ReportedByPartyId = await requestResourceUser(message, debugRequests);
const sharedHeaders = await previewTokenHeaders(catalog);
return { ok: true, payload, debugRequests, headers: {
requestSrTokenCatalog: sharedHeaders,
requestPostServiceRequest: { ...sharedHeaders, "vb-proxy-header-x-oracle-ectoken": "(generated after confirmation)" }
} };
}
payload.ReportedByPartyId = numericId(message.payload?.ReportedByPartyId, "ReportedByPartyId");
const headers = await requestCreationHeaders(message.catalogId, endpoint, debugRequests);
const result = await fetchJson(endpoint, {
method: "POST", credentials: "include", headers,
body: JSON.stringify(payload)
}, "requestPostServiceRequest", debugRequests, { allowEmpty: true });
return { ok: true, result, debugRequests };
} catch (error) {
error.debugRequests = debugRequests;
throw error;
} finally {
annotateSigningRequest(debugRequests);
}
}
async function requestCreationHeaders(id, target, debugRequests) {
if (id == null) {
const result = await fetchJson(`${catalog}?onlyData=true&fields=Id&limit=1`, {
method: "GET", credentials: "include", headers: { Accept: "application/json", Authorization: "Session" }
}, "requestSrCatalog", debugRequests);
id = result?.items?.[0]?.Id;
}
id = numericId(id, "Catalogue Id");
const tokenUrl = `${catalog}/${id}`;
let catalogHeaders = await tokenHeaders(tokenUrl, target);
const requestToken = () => fetchJson(tokenUrl, {
method: "POST", credentials: "include", headers: catalogHeaders, body: JSON.stringify({ name: "getJwtToken" })
}, "requestSrTokenCatalog", debugRequests);
let token;
try {
token = await requestToken();
} catch (error) {
if (error.httpStatus !== 403) throw error;
const refreshed = await previewTokenHeaders(tokenUrl);
if (!refreshed["x-appbuilder-client-id"] || refreshed["x-appbuilder-client-id"] === catalogHeaders["x-appbuilder-client-id"]) throw error;
catalogHeaders = refreshed;
token = await requestToken();
}
if (debugRequests.at(-1)?.httpStatus !== 200 || typeof token?.result !== "string" || !token.result.trim()) {
throw new Error("requestSrTokenCatalog did not return HTTP 200 with a token.");
}
return { ...catalogHeaders, "vb-proxy-header-x-oracle-ectoken": token.result.trim() };
}
function annotateSigningRequest(debugRequests) {
for (const entry of debugRequests) {
if (entry.name === "requestSrTokenCatalog") {
entry.metadata.appBuilderClientId = entry.requestHeaders?.["x-appbuilder-client-id"] ? "Observed in SeaaS requests" : "Not observed in SeaaS requests";
}
}
}
async function invalidateResourceUser() {
resourceGeneration++;
resourceRequests.clear();
await runtimeApi?.storage?.session?.set({ [resourceCacheKey]: null });
}
return { requestPostServiceRequest, requestPostInternalServiceRequest, getResourceUser, invalidateResourceUser };
}
// Source: src/services/spa/current-user.js
function createSpaCurrentUser({ fetchJson, fetchInTab, openSessionTab, waitForSession, closeSessionTab, sessionUrl }) {
const pending = new Map();
function requestCurrentUser(sender) {
const key = JSON.stringify([sender?.tab?.windowId ?? null, sender?.tab?.cookieStoreId ?? "", Boolean(sender?.tab?.incognito)]);
if (!pending.has(key)) {
pending.set(key, loadCurrentUser(sender).finally(() => pending.delete(key)));
}
return pending.get(key);
}
async function loadCurrentUser(sender) {
const debugRequests = [];
let sessionTab = null;
let receivedValidJson = false;
try {
// Bound each bootstrap cycle so a failed sign-in never leaves an orphan tab.
// The content-side gate can start another cycle without releasing dependent requests.
for (let attempt = 1; attempt <= 6; attempt++) {
try {
const result = sessionTab ? await fetchInTab(sessionTab, debugRequests) : await fetchJson("https://spa.oracle.com/oalcrm/web/api/identity-management/users/current", {
method: "GET", credentials: "include", cache: "no-store", headers: { Accept: "application/json" }
}, "requestCurrentUser", debugRequests);
const validText = value => typeof value === "string" && value.trim().length > 0;
if (!result || typeof result !== "object" || Array.isArray(result)
|| !result.user || typeof result.user !== "object" || Array.isArray(result.user)
|| !validText(result.user.id) || !validText(result.user.name) || !validText(result.token)
|| !Array.isArray(result.user.roles) || result.user.roles.some(role => !validText(role))
|| ["onWhiteList", "provisioned", "administrator"].some(key => key in result.user && typeof result.user[key] !== "boolean")) {
const error = new Error("requestCurrentUser returned an invalid user/token response.");
error.code = "INVALID_CURRENT_USER_RESPONSE";
throw error;
}
const entry = debugRequests[debugRequests.length - 1];
if (entry) entry.metadata.attempt = String(attempt);
receivedValidJson = true;
const user = { id: result.user.id, name: result.user.name, roles: result.user.roles };
return { ok: true, user, debugRequests };
} catch (error) {
const entry = debugRequests[debugRequests.length - 1];
if (entry) {
entry.status = "failed";
entry.error = error.message;
entry.metadata.attempt = String(attempt);
}
const needsSession = error.code === "INVALID_JSON_RESPONSE" || error.code === "INVALID_CURRENT_USER_RESPONSE"
|| error.httpStatus === 401 || error.httpStatus === 403 || !error.httpStatus;
if (!needsSession || attempt === 6) {
if (entry && needsSession) entry.metadata.retryLimitReached = "true";
throw error;
}
if (entry) {
entry.metadata.sessionBootstrapUrl = sessionUrl;
entry.metadata.retryReason = "Current user requires a valid SPA session";
}
if (!sessionTab) {
try {
const tab = await openSessionTab(sender);
if (!Number.isInteger(tab?.id)) throw new Error("SPA session tab was not created.");
sessionTab = tab;
if (entry) entry.metadata.sessionBootstrapState = "opened-inactive-tab";
} catch (sessionError) {
if (entry) entry.metadata.sessionBootstrapState = `tab-open-failed: ${sessionError.message || "unknown error"}`;
throw sessionError;
}
} else if (entry) {
entry.metadata.sessionBootstrapState = "waiting-for-valid-session";
}
await waitForSession(sessionTab, entry);
}
}
} catch (error) {
error.debugRequests = debugRequests;
throw error;
} finally {
if (sessionTab) {
const entry = debugRequests[debugRequests.length - 1];
try {
await closeSessionTab(sessionTab.id);
if (entry) entry.metadata.sessionBootstrapState = receivedValidJson
? "closed-after-valid-json" : "closed-after-retry-finished";
} catch (error) {
if (entry) entry.metadata.sessionBootstrapState = `tab-close-failed: ${error.message || "unknown error"}`;
}
}
}
}
return { requestCurrentUser };
}
// Source: src/services/spa/page-client.js
function createSpaPageClient({ sendMessage, getHttp }) {
const options = { frameId: 0 };
const inspect = tab => sendMessage(tab.id, { type: "opportunitiesExtension.inspectSpaPage" }, options);
async function requestIdentity(tab, debugRequests) {
const http = getHttp();
const url = "https://spa.oracle.com/oalcrm/web/api/identity-management/users/current";
const entry = http.createBackgroundDebugEntry("requestCurrentUser", url,
{ method: "GET", headers: { Accept: "application/json" } }, new Date());
entry.metadata.executionContext = "spa-tab";
try {
const result = await sendMessage(tab.id, { type: "opportunitiesExtension.readSpaIdentity", documentId: tab.spaDocumentId }, options);
if (!result || result.error || !Number.isInteger(result.status) || typeof result.body !== "string") {
throw new Error(result?.error || "Invalid SPA identity response.");
}
const response = { status: result.status, statusText: result.statusText || "", ok: result.status >= 200 && result.status < 300,
headers: { forEach: callback => Object.entries(result.headers || { "content-type": result.contentType || "" }).forEach(([key, value]) => callback(value, key)) } };
http.completeBackgroundDebugEntry(entry, response, http.createDebugBody(result.body), "");
entry.metadata.responseContentType = result.contentType || "(not provided)";
entry.metadata.responseLength = String(result.body.length);
if (result.status !== 200) {
const error = new Error(`requestCurrentUser failed with status ${result.status}.`);
error.httpStatus = result.status;
throw error;
}
return http.parseJsonResponse(result.body, "requestCurrentUser");
} catch (error) {
if (!entry.completedAt) http.completeBackgroundDebugEntry(entry, null, { value: "", truncated: false }, error.message);
entry.status = "failed";
entry.error = error.message;
throw error;
} finally { debugRequests.push(entry); }
}
return { inspect, requestIdentity };
}
// Source: src/services/spa/consumption.js
function createSpaConsumption({
CONSUMPTION_TREND_REQUESTS,
CONSUMPTION_TREND_URL,
fetchJson,
normalizeMessageAuthorization
}) {
async function requestConsumptionTrend(message) {
const subPlanNum = typeof message.subPlanNum === "string" ? message.subPlanNum.trim() : "";
const period = typeof message.period === "string" ? message.period.toLowerCase() : "";
const requestName = CONSUMPTION_TREND_REQUESTS[period];
const authorization = normalizeMessageAuthorization(message.authorization, "g2m-authorization");
const debugRequests = [];
if (!subPlanNum) {
throw new Error("Sub plan number is required for the consumption trend.");
}
if (!requestName) {
throw new Error(`Unsupported consumption trend period: ${period || "(missing)"}.`);
}
if (!authorization.value) {
throw new Error("OCICONS-TS authorization is unavailable for the consumption trend.");
}
const url = `${CONSUMPTION_TREND_URL}/${encodeURIComponent(subPlanNum)}?quarter=All&timePeriod=${period}`;
try {
const response = await fetchJson(url, {
method: "GET",
credentials: "include",
cache: "no-store",
headers: {
Accept: "application/json",
"G2m-Authorization": authorization.value
}
}, requestName, debugRequests);
const items = Array.isArray(response)
? response
: Array.isArray(response && response.items)
? response.items
: [];
return {
ok: true,
subPlanNum,
period,
items,
debugRequests
};
} catch (error) {
error.debugRequests = debugRequests;
throw error;
}
}
async function requestConsumptionPlanDetail(message) {
const debugRequests = [];
try {
const subPlanNum = typeof message.subPlanNum === "string" ? message.subPlanNum.trim() : "";
const authorization = normalizeMessageAuthorization(message.authorization, "g2m-authorization");
if (!subPlanNum) throw new Error("Sub plan number is required for plan details.");
if (!authorization.value) throw new Error("OCICONS-TS authorization is unavailable for plan details.");
const url = `https://spa.oracle.com/oalcrm/web/api/provider-proxy/g2m-consumption/service/v1/plan/plandetail/${encodeURIComponent(subPlanNum)}?active=true`;
const items = await fetchJson(url, {
method: "GET", credentials: "include", cache: "no-store",
headers: { Accept: "application/json", "G2m-Authorization": authorization.value }
}, "requestConsumptionPlanDetail", debugRequests);
const validLines = (lines, depth = 0) => Array.isArray(lines) && depth <= 20 && lines.every(line =>
line && typeof line === "object" && !Array.isArray(line)
&& (line.clines === undefined || line.clines === null || validLines(line.clines, depth + 1)));
if (!validLines(items)) throw new Error("requestConsumptionPlanDetail returned invalid plan details.");
return { ok: true, subPlanNum, items, debugRequests };
} catch (error) {
const entry = debugRequests[debugRequests.length - 1];
if (entry && entry.status === "success") { entry.status = "failed"; entry.error = error.message; }
error.debugRequests = debugRequests;
throw error;
}
}
async function requestRatecardByPlan(message) {
const debugRequests = [];
try {
const planNumber = typeof message.planNumber === "string" ? message.planNumber.trim() : "";
const authorization = normalizeMessageAuthorization(message.authorization, "g2m-authorization");
if (!planNumber) throw new Error("Plan number is required for the rate card.");
if (!authorization.value) throw new Error("OCICONS-TS authorization is unavailable for the rate card.");
const items = await fetchJson("https://spa.oracle.com/oalcrm/web/api/provider-proxy/g2m-consumption/service/v1/ratecard/byPlan", {
method: "POST", credentials: "include", cache: "no-store",
headers: { Accept: "application/json", "Content-Type": "application/json", "G2m-Authorization": authorization.value },
body: JSON.stringify({ planNumber, recordsSize: 10000 })
}, "requestRatecardByPlan", debugRequests);
if (!Array.isArray(items) || items.some(item => !item || typeof item !== "object" || Array.isArray(item))) {
throw new Error("requestRatecardByPlan returned invalid rate card items.");
}
return { ok: true, planNumber, items, debugRequests };
} catch (error) {
const entry = debugRequests[debugRequests.length - 1];
if (entry && entry.status === "success") { entry.status = "failed"; entry.error = error.message; }
error.debugRequests = debugRequests;
throw error;
}
}
return { requestConsumptionTrend, requestConsumptionPlanDetail, requestRatecardByPlan };
}
// Source: src/services/spa/response-shapes.js
function createSpaResponseShapes() {
function findNestedObject(value, predicate, visited = new Set()) {
if (!value || typeof value !== "object" || visited.has(value)) {
return null;
}
visited.add(value);
if (!Array.isArray(value) && predicate(value)) {
return value;
}
for (const child of Object.values(value)) {
const match = findNestedObject(child, predicate, visited);
if (match) {
return match;
}
}
return null;
}
function findAccountPlanId(response) {
const account = findNestedObject(response, (value) => (
Object.prototype.hasOwnProperty.call(value, "id")
&& value.id !== null
&& value.id !== undefined
&& value.id !== ""
));
return account ? account.id : "";
}
function extractAccountPlans(response) {
const plans = [];
const visited = new Set();
const visit = (value) => {
if (!value || typeof value !== "object" || visited.has(value)) {
return;
}
visited.add(value);
if (!Array.isArray(value) && (
Object.prototype.hasOwnProperty.call(value, "planType")
|| Object.prototype.hasOwnProperty.call(value, "subPlanNum")
)) {
plans.push(value);
return;
}
Object.values(value).forEach(visit);
};
visit(response);
return plans;
}
return { findNestedObject, findAccountPlanId, extractAccountPlans };
}
// Source: src/services/http/background-json.js
function createHttpBackgroundJson({
previewDebugBody,
observeResponseHeaders = () => {},
environment
}) {
const { fetch } = environment;
async function fetchJson(url, options, requestName, debugRequests = null, { allowEmpty = false } = {}) {
const startedAt = new Date();
const debugEntry = createBackgroundDebugEntry(requestName, url, options, startedAt);
let response;
try {
response = await fetch(url, options);
} catch (error) {
completeBackgroundDebugEntry(debugEntry, null, "", error.message || "Network request failed.");
appendBackgroundDebugEntry(debugRequests, debugEntry);
throw error;
}
observeResponseHeaders(response.url || url, response.headers);
const responsePayload = await readResponsePayload(response);
const responseBody = createDebugBody(responsePayload.text);
completeBackgroundDebugEntry(debugEntry, response, responseBody, "");
debugEntry.metadata.responseContentType = response.headers.get("content-type") || "(not provided)";
debugEntry.metadata.responseLength = String(responsePayload.text.length);
appendBackgroundDebugEntry(debugRequests, debugEntry);
if (!response.ok) {
const error = new Error(`${requestName} failed with status ${response.status}.`);
error.httpStatus = response.status;
debugEntry.error = error.message;
throw error;
}
if (responsePayload.error) {
throw new Error(`${requestName} response body could not be read.`);
}
try {
if (allowEmpty && !responsePayload.text.trim()) return null;
return parseJsonResponse(responsePayload.text, requestName);
} catch (error) {
error.httpStatus = response.status;
debugEntry.status = "failed";
debugEntry.error = error.message;
throw error;
}
}
function createBackgroundDebugEntry(requestName, url, options, startedAt) {
return {
name: requestName,
method: options.method || "GET",
url,
status: "pending",
startedAt: startedAt.toISOString(),
completedAt: null,
durationMs: null,
httpStatus: null,
statusText: "",
requestHeaders: normalizeDebugHeaders(options.headers),
requestBody: formatDebugRequestBody(options.body),
metadata: {
executionContext: "background"
},
responseHeaders: {},
responseBody: "",
responseBodyTruncated: false,
error: ""
};
}
function completeBackgroundDebugEntry(entry, response, responseBody, errorMessage) {
const completedAt = new Date();
entry.completedAt = completedAt.toISOString();
entry.durationMs = completedAt.getTime() - new Date(entry.startedAt).getTime();
entry.error = errorMessage;
if (!response) {
entry.status = "failed";
return;
}
entry.status = response.ok ? "success" : "failed";
entry.httpStatus = response.status;
entry.statusText = response.statusText || "";
entry.responseHeaders = headersToObject(response.headers);
entry.responseBody = responseBody.value;
entry.responseBodyTruncated = responseBody.truncated;
}
function appendBackgroundDebugEntry(debugRequests, entry) {
if (Array.isArray(debugRequests)) {
debugRequests.push(entry);
}
}
async function readResponsePayload(response) {
try {
return {
text: await response.text(),
error: ""
};
} catch (error) {
return {
text: `Unable to read response body: ${error.message || "unknown error"}`,
error: error.message || "unknown error"
};
}
}
function parseJsonResponse(text, requestName) {
const normalizedText = String(text)
.replace(/^\uFEFF/, "")
.trimStart()
.replace(/^\)\]\}',?\s*/, "")
.replace(/^while\s*\(1\);\s*/, "");
try {
return JSON.parse(normalizedText);
} catch (error) {
const invalidJsonError = new Error(`${requestName} returned an invalid JSON response.`);
invalidJsonError.code = "INVALID_JSON_RESPONSE";
throw invalidJsonError;
}
}
function createDebugBody(text) {
return previewDebugBody(text);
}
function normalizeDebugHeaders(headers) {
const normalized = {};
Object.entries(headers || {}).forEach(([key, value]) => {
normalized[key] = String(value);
});
return normalized;
}
function headersToObject(headers) {
const values = {};
if (headers && typeof headers.forEach === "function") {
headers.forEach((value, key) => {
values[key] = value;
});
}
return values;
}
function formatDebugRequestBody(body) {
if (!body) {
return "";
}
try {
return JSON.stringify(JSON.parse(body), null, 2);
} catch (error) {
return String(body);
}
}
return { fetchJson, createBackgroundDebugEntry, completeBackgroundDebugEntry, appendBackgroundDebugEntry, readResponsePayload, parseJsonResponse, createDebugBody, normalizeDebugHeaders, headersToObject, formatDebugRequestBody };
}
// Source: src/features/products/background-jobs.js
function createProductsBackgroundJobs({
messageTypes,
OPPTY_PRODUCTS_CONCURRENCY,
getOpptyProducts,
normalizeCacheMaxAge,
tabsSendMessage
}) {
async function requestOpptyProducts(message, sender) {
const requestId = typeof message.requestId === "string" ? message.requestId : "";
const accessToken = typeof message.accessToken === "string" ? message.accessToken : "";
const cacheMaxAgeMs = normalizeCacheMaxAge(message.cacheMaxAgeMs);
const optyNumbers = Array.from(new Set(
(Array.isArray(message.optyNumbers) ? message.optyNumbers : [])
.filter(Boolean)
.map(String)
));
if (!requestId || !accessToken) {
throw new Error("A request id and access token are required for requestOpptyProducts.");
}
let nextIndex = 0;
let completed = 0;
let failed = 0;
let cacheHits = 0;
let debugDelivery = Promise.resolve();
const reportRequest = entry => {
const message = {
type: messageTypes.opptyProductsRequestDebug,
requestId,
entry: { ...entry, debugKey: JSON.stringify([requestId, entry.metadata.optyNumber, entry.metadata.offset]) }
};
debugDelivery = debugDelivery.catch(() => {}).then(() => sendProductsProgress(sender, message));
};
const runWorker = async () => {
while (nextIndex < optyNumbers.length) {
const index = nextIndex;
nextIndex += 1;
const optyNumber = optyNumbers[index];
try {
const result = await getOpptyProducts(optyNumber, accessToken, cacheMaxAgeMs, reportRequest);
completed += 1;
if (result.fromCache) {
cacheHits += 1;
}
await sendProductsProgress(sender, {
type: messageTypes.opptyProductsProgress,
requestId,
state: "running",
optyNumber,
items: result.items,
fromCache: result.fromCache,
cachedAt: result.cachedAt,
completed,
total: optyNumbers.length,
failed,
cacheHits
});
} catch (error) {
completed += 1;
failed += 1;
await sendProductsProgress(sender, {
type: messageTypes.opptyProductsProgress,
requestId,
state: "running",
optyNumber,
items: [],
error: error.message || "Unable to load products.",
completed,
total: optyNumbers.length,
failed,
cacheHits
});
}
}
};
const workerCount = Math.min(OPPTY_PRODUCTS_CONCURRENCY, Math.max(optyNumbers.length, 1));
await Promise.all(Array.from({ length: workerCount }, runWorker));
await debugDelivery.catch(() => {});
const state = failed > 0 ? "complete-with-errors" : "complete";
await sendProductsProgress(sender, {
type: messageTypes.opptyProductsProgress,
requestId,
state,
completed,
total: optyNumbers.length,
failed,
cacheHits
});
return {
ok: failed === 0,
requestId,
completed,
total: optyNumbers.length,
failed,
cacheHits
};
}
function sendProductsProgress(sender, message) {
if (!sender || !sender.tab || !Number.isInteger(sender.tab.id)) {
return Promise.resolve();
}
const options = Number.isInteger(sender.frameId) ? { frameId: sender.frameId } : undefined;
return tabsSendMessage(sender.tab.id, message, options).catch(() => {});
}
return { requestOpptyProducts, sendProductsProgress };
}
// Source: src/features/products/cache.js
function createProductsCache({
OPPTY_PRODUCTS_CACHE_KEY,
fetchAllOpptyProducts,
productsCacheState,
storageLocalGet,
storageLocalSet
}) {
let generation = 0;
async function invalidate() {
generation += 1;
productsCacheState.inFlightProducts.clear();
productsCacheState.productsCachePromise = Promise.resolve({});
await saveProductsCache({});
}
async function getOpptyProducts(optyNumber, accessToken, cacheMaxAgeMs, onDebug = () => {}) {
const currentGeneration = generation;
const cache = await getProductsCache();
if (generation !== currentGeneration) return getOpptyProducts(optyNumber, accessToken, cacheMaxAgeMs, onDebug);
const cachedEntry = cache[optyNumber];
if (cachedEntry && Array.isArray(cachedEntry.items) && Date.now() - Number(cachedEntry.fetchedAt) < cacheMaxAgeMs) {
return { items: cachedEntry.items, fromCache: true, cachedAt: Number(cachedEntry.fetchedAt) };
}
if (!productsCacheState.inFlightProducts.has(optyNumber)) {
const flight = { listeners: new Set(), entries: new Map(), promise: null };
productsCacheState.inFlightProducts.set(optyNumber, flight);
flight.promise = fetchAllOpptyProducts(optyNumber, accessToken, entry => {
flight.entries.set(entry.metadata.offset, entry);
for (const listener of flight.listeners) {
try { listener(entry); } catch (_) {}
}
})
.then(async (items) => {
const fetchedAt = Date.now();
if (generation === currentGeneration) {
cache[optyNumber] = { fetchedAt, items };
await saveProductsCache(cache).catch(() => {});
}
return { items, fetchedAt };
})
.finally(() => {
if (productsCacheState.inFlightProducts.get(optyNumber) === flight) productsCacheState.inFlightProducts.delete(optyNumber);
});
}
const flight = productsCacheState.inFlightProducts.get(optyNumber);
// A second tab/job shares the fetch but still receives its current diagnostics.
flight.listeners.add(onDebug);
for (const entry of flight.entries.values()) {
try { onDebug(entry); } catch (_) {}
}
try {
const fetchedResult = await flight.promise;
return { items: fetchedResult.items, cachedAt: fetchedResult.fetchedAt, fromCache: false };
} finally {
flight.listeners.delete(onDebug);
}
}
function normalizeCacheMaxAge(value) {
const maxAge = Number(value);
const defaultMaxAge = 24 * 60 * 60 * 1000;
const maximumMaxAge = 30 * 24 * 60 * 60 * 1000;
return Number.isFinite(maxAge) && maxAge > 0
? Math.min(maxAge, maximumMaxAge)
: defaultMaxAge;
}
async function getProductsCache() {
if (!productsCacheState.productsCachePromise) {
productsCacheState.productsCachePromise = storageLocalGet(OPPTY_PRODUCTS_CACHE_KEY)
.then((result) => result && typeof result[OPPTY_PRODUCTS_CACHE_KEY] === "object"
? result[OPPTY_PRODUCTS_CACHE_KEY]
: {})
.catch(() => ({}));
}
return productsCacheState.productsCachePromise;
}
function saveProductsCache(cache) {
productsCacheState.productsCacheWritePromise = productsCacheState.productsCacheWritePromise
.catch(() => {})
.then(() => storageLocalSet({ [OPPTY_PRODUCTS_CACHE_KEY]: cache }));
return productsCacheState.productsCacheWritePromise;
}
return { invalidate, getOpptyProducts, normalizeCacheMaxAge, getProductsCache, saveProductsCache };
}
// Source: src/services/fusion/products.js
function createFusionProducts({
OPPTY_PRODUCTS_FIELDS,
OPPTY_PRODUCTS_MAX_PAGES,
OPPTY_PRODUCTS_PAGE_LIMIT,
ORACLE_DOMAIN,
createBackgroundDebugEntry,
completeBackgroundDebugEntry,
createDebugBody,
headersToObject,
environment
}) {
const { fetch } = environment;
async function fetchAllOpptyProducts(optyNumber, accessToken, onDebug = () => {}) {
let offset = 0;
let page = 0;
const items = [];
while (page < OPPTY_PRODUCTS_MAX_PAGES) {
const url = createOpptyProductsUrl(optyNumber, offset);
const options = {
method: "GET",
credentials: "include",
headers: {
Accept: "application/json",
Authorization: `Bearer ${accessToken}`
}
};
const entry = createBackgroundDebugEntry("requestOpptyProducts", url, {
...options, headers: { Accept: "application/json", Authorization: "(present)" }
}, new Date());
entry.metadata = { executionContext: "background", optyNumber, offset, page: page + 1 };
const report = () => {
// Diagnostics must not block requests or transmit the Bearer credential.
try { onDebug({ ...entry, metadata: { ...entry.metadata } }); } catch (_) {}
};
let response;
let responseBody = { value: "", truncated: false };
let responseData;
report();
try {
response = await fetch(url, options);
entry.httpStatus = response.status;
entry.statusText = response.statusText || "";
entry.responseHeaders = headersToObject(response.headers);
Object.keys(entry.responseHeaders).forEach(key => {
if (/authorization|cookie|token/i.test(key)) entry.responseHeaders[key] = "(present)";
});
entry.metadata.phase = "reading-response";
report();
const text = await response.text();
responseBody = createDebugBody(text);
if (!response.ok) throw new Error(`requestOpptyProducts failed with status ${response.status}.`);
responseData = JSON.parse(text);
if (!responseData || typeof responseData !== "object") throw new Error("requestOpptyProducts returned an invalid response.");
completeBackgroundDebugEntry(entry, response, responseBody, "");
} catch (error) {
completeBackgroundDebugEntry(entry, response, responseBody, error.message || "Unable to load products.");
entry.status = "failed";
throw error;
} finally {
Object.keys(entry.responseHeaders).forEach(key => {
if (/authorization|cookie|token/i.test(key)) entry.responseHeaders[key] = "(present)";
});
entry.metadata.phase = "complete";
report();
}
const pageItems = Array.isArray(responseData.items) ? responseData.items : [];
items.push(...pageItems);
page += 1;
if (!responseData.hasMore) {
return items;
}
const responseOffset = Number(responseData.offset);
const responseCount = Number(responseData.count);
const receivedCount = Number.isFinite(responseCount) ? responseCount : pageItems.length;
const currentOffset = Number.isFinite(responseOffset) ? responseOffset : offset;
if (receivedCount <= 0) {
throw new Error("requestOpptyProducts returned hasMore without additional results.");
}
offset = currentOffset + receivedCount;
}
throw new Error("requestOpptyProducts exceeded the pagination safety limit.");
}
function createOpptyProductsUrl(optyNumber, offset) {
const url = new URL(`https://${ORACLE_DOMAIN}/crmRestApi/rest/rv:be91c002-2e5d-4ed2-a37e-e0c837bf141f/en/11.13.18.05:9/opportunities/${encodeURIComponent(optyNumber)}/child/ChildRevenue`);
url.searchParams.set("onlyData", "true");
url.searchParams.set("q", "(SplitTypeCode!='DETAILCHILDSPLIT') AND (RecurTypeCode!='CHILDRECUR')");
url.searchParams.set("totalResults", "false");
url.searchParams.set("fields", OPPTY_PRODUCTS_FIELDS.join(","));
url.searchParams.set("orderBy", "CreationDate:desc");
url.searchParams.set("limit", String(OPPTY_PRODUCTS_PAGE_LIMIT));
url.searchParams.set("offset", String(offset));
return url.toString();
}
return { fetchAllOpptyProducts, createOpptyProductsUrl };
}
// Source: src/auth/xsrf-recovery.js
function createAuthXsrfRecovery({
COOKIE_REFRESH_POLL_MS,
COOKIE_REFRESH_TIMEOUT_MS,
OPPORTUNITIES_LIST_URL,
getXsrfTokenCookie,
supportsFirefoxCookieStoreTabs,
tabsCreate,
tabsRemove,
wait
}) {
async function refreshXsrfCookie(sender) {
const startedAt = Date.now();
const previous = (await getXsrfTokenCookie()).cookie;
const createProperties = {
url: OPPORTUNITIES_LIST_URL,
active: false
};
if (sender && sender.tab && Number.isInteger(sender.tab.windowId)) {
createProperties.windowId = sender.tab.windowId;
}
if (supportsFirefoxCookieStoreTabs() && sender && sender.tab && sender.tab.cookieStoreId) {
createProperties.cookieStoreId = sender.tab.cookieStoreId;
}
let refreshTab;
try {
refreshTab = await tabsCreate(createProperties);
const current = await waitForXsrfCookieChange(previous);
const refreshed = hasCookieChanged(previous, current);
return {
ok: Boolean(current),
refreshed,
cookieName: current ? current.name : "",
durationMs: Date.now() - startedAt,
error: refreshed ? "" : "XSRF cookie was not created or changed before timeout."
};
} finally {
if (refreshTab && Number.isInteger(refreshTab.id)) {
try {
await tabsRemove(refreshTab.id);
} catch (error) {
// The temporary tab may already have been closed.
}
}
}
}
async function waitForXsrfCookieChange(previousCookie) {
const deadline = Date.now() + COOKIE_REFRESH_TIMEOUT_MS;
let currentCookie = null;
while (Date.now() < deadline) {
currentCookie = (await getXsrfTokenCookie()).cookie;
if (hasCookieChanged(previousCookie, currentCookie)) {
return currentCookie;
}
await wait(COOKIE_REFRESH_POLL_MS);
}
return currentCookie;
}
function hasCookieChanged(previousCookie, currentCookie) {
if (!currentCookie || !currentCookie.value) {
return false;
}
if (!previousCookie) {
return true;
}
return previousCookie.name !== currentCookie.name
|| previousCookie.value !== currentCookie.value
|| previousCookie.expirationDate !== currentCookie.expirationDate;
}
return { refreshXsrfCookie, waitForXsrfCookieChange, hasCookieChanged };
}
// Source: src/auth/xsrf-cookies.js
function createAuthXsrfCookies({
ORACLE_COOKIE_URLS,
ORACLE_DOMAIN,
XSRF_COOKIE_NAME,
XSRF_COOKIE_PREFIX,
cookiesGet,
cookiesGetAll,
cookiesGetAllCookieStores,
removeUndefinedValues
}) {
async function getXsrfTokenCookie() {
const lookupDetails = [];
const allCookies = [];
const stores = await getCookieStores(lookupDetails);
for (const store of stores) {
const exactCookie = await getExactCookieFromUrls(store.id, lookupDetails);
if (exactCookie) {
allCookies.push(exactCookie);
}
}
for (const store of stores) {
await collectCookies({
name: XSRF_COOKIE_NAME,
storeId: store.id
}, allCookies, lookupDetails);
}
for (const store of stores) {
await collectCookies({
domain: ORACLE_DOMAIN,
storeId: store.id
}, allCookies, lookupDetails);
await collectCookies({
domain: `.${ORACLE_DOMAIN}`,
storeId: store.id
}, allCookies, lookupDetails);
}
for (const store of stores) {
for (const url of ORACLE_COOKIE_URLS) {
await collectCookies({
url,
storeId: store.id
}, allCookies, lookupDetails);
}
}
const uniqueCookies = dedupeCookies(allCookies);
const xsrfCookies = uniqueCookies.filter((cookie) => cookie.name.startsWith(XSRF_COOKIE_PREFIX));
const exactCookie = xsrfCookies.find((cookie) => cookie.name === XSRF_COOKIE_NAME);
const hostCookie = xsrfCookies.find((cookie) => cookie.domain === ORACLE_DOMAIN || cookie.domain === `.${ORACLE_DOMAIN}`);
return {
cookie: exactCookie || hostCookie || xsrfCookies[0] || null,
matchedCookieNames: xsrfCookies.map((cookie) => `${cookie.name} (${cookie.domain}${cookie.path})`),
lookupDetails
};
}
async function getExactCookieFromUrls(storeId, lookupDetails) {
for (const url of ORACLE_COOKIE_URLS) {
const cookie = await cookiesGet({
url,
name: XSRF_COOKIE_NAME,
storeId
});
lookupDetails.push(`${JSON.stringify({ url, name: XSRF_COOKIE_NAME, storeId })} => ${cookie ? "found" : "not found"}`);
if (cookie) {
return cookie;
}
}
return null;
}
async function getCookieStores(lookupDetails) {
try {
const stores = await cookiesGetAllCookieStores();
lookupDetails.push(`getAllCookieStores => ${stores.length} store(s)`);
return stores.length ? stores : [{ id: undefined }];
} catch (error) {
lookupDetails.push(`getAllCookieStores => unavailable (${error.message || "unknown error"})`);
return [{ id: undefined }];
}
}
async function collectCookies(details, target, lookupDetails) {
const cleanDetails = removeUndefinedValues(details);
const cookies = await cookiesGetAll(cleanDetails);
target.push(...cookies);
lookupDetails.push(`${JSON.stringify(cleanDetails)} => ${cookies.length} cookie(s)`);
}
function dedupeCookies(cookies) {
const seen = new Set();
return cookies.filter((cookie) => {
const key = `${cookie.name}|${cookie.domain}|${cookie.path}|${cookie.storeId || ""}`;
if (seen.has(key)) {
return false;
}
seen.add(key);
return true;
});
}
return { getXsrfTokenCookie, getExactCookieFromUrls, getCookieStores, collectCookies, dedupeCookies };
}
// Source: src/platform/background-api.js
function createPlatformBackgroundApi({
runtimeApi,
environment
}) {
const { browser, setTimeout } = environment;
function supportsFirefoxCookieStoreTabs() {
return Boolean(
runtimeApi.runtime
&& typeof runtimeApi.runtime.getBrowserInfo === "function"
);
}
function tabsCreate(details) {
if (runtimeApi.tabs.create.length <= 1) {
return runtimeApi.tabs.create(details);
}
return new Promise((resolve, reject) => {
runtimeApi.tabs.create(details, (tab) => {
const lastError = runtimeApi.runtime.lastError;
if (lastError) {
reject(new Error(lastError.message));
return;
}
resolve(tab);
});
});
}
function tabsUpdate(tabId, details) {
if (runtimeApi.tabs.update.length <= 2) {
return runtimeApi.tabs.update(tabId, details);
}
return new Promise((resolve, reject) => {
runtimeApi.tabs.update(tabId, details, tab => {
const lastError = runtimeApi.runtime.lastError;
if (lastError) reject(new Error(lastError.message));
else resolve(tab);
});
});
}
function tabsRemove(tabId) {
if (runtimeApi.tabs.remove.length <= 1) {
return runtimeApi.tabs.remove(tabId);
}
return new Promise((resolve, reject) => {
runtimeApi.tabs.remove(tabId, () => {
const lastError = runtimeApi.runtime.lastError;
if (lastError) {
reject(new Error(lastError.message));
return;
}
resolve();
});
});
}
function tabsSendMessage(tabId, message, options) {
if (typeof browser !== "undefined") {
return options
? runtimeApi.tabs.sendMessage(tabId, message, options)
: runtimeApi.tabs.sendMessage(tabId, message);
}
return new Promise((resolve, reject) => {
const callback = (response) => {
const lastError = runtimeApi.runtime.lastError;
if (lastError) {
reject(new Error(lastError.message));
return;
}
resolve(response);
};
if (options) {
runtimeApi.tabs.sendMessage(tabId, message, options, callback);
} else {
runtimeApi.tabs.sendMessage(tabId, message, callback);
}
});
}
function storageLocalGet(key) {
if (typeof browser !== "undefined") {
return runtimeApi.storage.local.get(key);
}
return new Promise((resolve, reject) => {
runtimeApi.storage.local.get(key, (result) => {
const lastError = runtimeApi.runtime.lastError;
if (lastError) {
reject(new Error(lastError.message));
return;
}
resolve(result);
});
});
}
function storageLocalSet(value) {
if (typeof browser !== "undefined") {
return runtimeApi.storage.local.set(value);
}
return new Promise((resolve, reject) => {
runtimeApi.storage.local.set(value, () => {
const lastError = runtimeApi.runtime.lastError;
if (lastError) {
reject(new Error(lastError.message));
return;
}
resolve();
});
});
}
function wait(durationMs) {
return new Promise((resolve) => setTimeout(resolve, durationMs));
}
function cookiesGetAll(details) {
if (runtimeApi.cookies.getAll.length <= 1) {
return runtimeApi.cookies.getAll(details);
}
return new Promise((resolve, reject) => {
runtimeApi.cookies.getAll(details, (cookies) => {
const lastError = runtimeApi.runtime.lastError;
if (lastError) {
reject(new Error(lastError.message));
return;
}
resolve(cookies);
});
});
}
function cookiesGet(details) {
const cleanDetails = removeUndefinedValues(details);
if (runtimeApi.cookies.get.length <= 1) {
return runtimeApi.cookies.get(cleanDetails);
}
return new Promise((resolve, reject) => {
runtimeApi.cookies.get(cleanDetails, (cookie) => {
const lastError = runtimeApi.runtime.lastError;
if (lastError) {
reject(new Error(lastError.message));
return;
}
resolve(cookie);
});
});
}
function cookiesGetAllCookieStores() {
if (!runtimeApi.cookies.getAllCookieStores) {
return Promise.resolve([{ id: undefined }]);
}
if (runtimeApi.cookies.getAllCookieStores.length === 0) {
return runtimeApi.cookies.getAllCookieStores();
}
return new Promise((resolve, reject) => {
runtimeApi.cookies.getAllCookieStores((stores) => {
const lastError = runtimeApi.runtime.lastError;
if (lastError) {
reject(new Error(lastError.message));
return;
}
resolve(stores);
});
});
}
function removeUndefinedValues(details) {
return Object.fromEntries(
Object.entries(details).filter((entry) => entry[1] !== undefined)
);
}
return { supportsFirefoxCookieStoreTabs, tabsCreate, tabsUpdate, tabsRemove, tabsSendMessage, storageLocalGet, storageLocalSet, wait, cookiesGetAll, cookiesGet, cookiesGetAllCookieStores, removeUndefinedValues };
}
// Source: src/background/main.js
function startBackgroundApplication(dependencies) {
registerBackgroundMessages(dependencies);
}
// Source: src/background/messages.js
/** Register synchronously; replies retain the WebExtension sendResponse contract. */
function registerBackgroundMessages({
messageTypes,
requestWeekDetails,
requestTimeEntries,
requestTimeEntriesSave,
requestTimeEntriesDelete,
requestLookupTaskType,
requestServiceRequestExpand,
requestResourceUser,
requestActiveServiceRequests,
invalidateProductsCache,
invalidateResourceUser = async () => {},
getXsrfTokenCookie,
refreshXsrfCookie,
requestAccountPlans,
requestInstallBase,
requestCustomerServiceRequest,
requestWlActions,
requestWorkloads,
requestWorkloadForecastUpdate,
invalidateWorkloadsCache,
requestActionTeamMembers,
requestActionFindLookup,
requestMgmtAction,
requestPostServiceRequest,
requestPostInternalServiceRequest,
requestInternalServiceRequest,
requestSrLookupPrimaryPillar,
requestSrLookupWithSession,
prepareSrSession,
requestSrLookupLanguage,
requestSrLookupTechPlatform,
requestLookupTypePlatform,
requestInternalSrServices,
requestSrLookupOpportunityBusiness,
requestCurrentUser,
requestConsumerTokenService,
requestConsumptionTrend,
requestConsumptionPlanDetail,
requestRatecardByPlan,
requestOpptyProducts,
runtimeApi
}) {
runtimeApi.runtime.onMessage.addListener((message, sender, sendResponse) => {
if (!message || !message.type) {
return false;
}
if (message.type === messageTypes.requestLookupTaskType) {
requestLookupTaskType().then(sendResponse).catch(error => sendResponse({ ok: false,
error: error.message, httpStatus: error.httpStatus, debugRequests: error.debugRequests || [] }));
return true;
}
if (message.type === messageTypes.requestTimeEntriesSave) {
requestTimeEntriesSave(message).then(sendResponse).catch(error => sendResponse({ ok: false,
error: error.message, httpStatus: error.httpStatus, debugRequests: error.debugRequests || [] }));
return true;
}
if (message.type === messageTypes.requestTimeEntriesDelete) {
requestTimeEntriesDelete(message).then(sendResponse).catch(error => sendResponse({ ok: false,
error: error.message, httpStatus: error.httpStatus, debugRequests: error.debugRequests || [] }));
return true;
}
if ([messageTypes.requestTimeEntries, messageTypes.requestServiceRequestExpand, messageTypes.requestWeekDetails, messageTypes.requestResourceUser, messageTypes.requestActiveServiceRequests].includes(message.type)) {
const request = message.type === messageTypes.requestTimeEntries ? requestTimeEntries : message.type === messageTypes.requestServiceRequestExpand ? requestServiceRequestExpand : message.type === messageTypes.requestWeekDetails ? requestWeekDetails : message.type === messageTypes.requestResourceUser ? requestResourceUser : requestActiveServiceRequests;
request(message).then(sendResponse).catch(error => sendResponse({ ok: false,
error: error.message, httpStatus: error.httpStatus, debugRequests: error.debugRequests || [] }));
return true;
}
if (message.type === messageTypes.requestPostInternalServiceRequest) {
requestPostInternalServiceRequest(message).then(sendResponse).catch(error => sendResponse({ ok: false,
error: error.message, debugRequests: error.debugRequests || [] }));
return true;
}
if (message.type === messageTypes.requestPostServiceRequest) {
requestPostServiceRequest(message).then(sendResponse).catch(error => sendResponse({ ok: false,
error: error.message, debugRequests: error.debugRequests || [] }));
return true;
}
if (message.type === messageTypes.prepareSrSession) {
prepareSrSession(sender).then(sendResponse).catch(error => sendResponse({ ok: false,
error: error.message, debugRequests: error.debugRequests || [] }));
return true;
}
const srLookups = {
[messageTypes.requestLookupTypePlatform]: requestLookupTypePlatform,
[messageTypes.requestInternalSrServices]: requestInternalSrServices,
[messageTypes.requestSrLookupPrimaryPillar]: requestSrLookupPrimaryPillar,
[messageTypes.requestSrLookupLanguage]: requestSrLookupLanguage,
[messageTypes.requestSrLookupTechPlatform]: requestSrLookupTechPlatform,
[messageTypes.requestSrLookupOpportunityBusiness]: requestSrLookupOpportunityBusiness
};
if (Object.hasOwn(srLookups, message.type)) {
requestSrLookupWithSession(srLookups[message.type], message, sender).then(sendResponse).catch(error => sendResponse({ ok: false,
httpStatus: error.httpStatus, error: error.message, debugRequests: error.debugRequests || [] }));
return true;
}
if (message.type === messageTypes.invalidateProductsCache) {
Promise.all([invalidateProductsCache(), invalidateWorkloadsCache()]).then(() => invalidateResourceUser()).then(() => sendResponse({ ok: true })).catch(error => sendResponse({ ok: false, error: error.message || "Unable to clear products cache." }));
return true;
}
if (message.type === messageTypes.getXsrfToken) {
getXsrfTokenCookie()
.then((result) => {
sendResponse({
ok: true,
cookieName: result.cookie ? result.cookie.name : "",
token: result.cookie ? result.cookie.value : "",
matchedCookieNames: result.matchedCookieNames,
lookupDetails: result.lookupDetails
});
})
.catch((error) => {
sendResponse({
ok: false,
cookieName: "",
token: "",
matchedCookieNames: [],
lookupDetails: [],
error: error.message || "Unable to read cookies."
});
});
return true;
}
if (message.type === messageTypes.refreshXsrfCookie) {
refreshXsrfCookie(sender)
.then(sendResponse)
.catch((error) => {
sendResponse({
ok: false,
refreshed: false,
cookieName: "",
durationMs: 0,
error: error.message || "Unable to refresh XSRF cookie."
});
});
return true;
}
if (message.type === messageTypes.requestOpptyProducts) {
requestOpptyProducts(message, sender)
.then(sendResponse)
.catch((error) => {
sendResponse({
ok: false,
requestId: message.requestId || "",
error: error.message || "Unable to load opportunity products."
});
});
return true;
}
if (message.type === messageTypes.requestConsumerTokenService) {
requestConsumerTokenService(sender)
.then(sendResponse)
.catch((error) => {
sendResponse({
ok: false,
error: error.message || "Unable to load consumer token service.",
debugRequests: Array.isArray(error.debugRequests) ? error.debugRequests : []
});
});
return true;
}
if (message.type === messageTypes.requestCurrentUser) {
requestCurrentUser(sender).then(sendResponse).catch(error => sendResponse({
ok: false, error: error.message || "Unable to load current user.",
debugRequests: Array.isArray(error.debugRequests) ? error.debugRequests : []
}));
return true;
}
if ([messageTypes.requestActionTeamMembers, messageTypes.requestActionFindLookup, messageTypes.requestMgmtAction, messageTypes.requestInternalServiceRequest].includes(message.type)) {
const request = message.type === messageTypes.requestInternalServiceRequest ? requestInternalServiceRequest : message.type === messageTypes.requestMgmtAction ? requestMgmtAction : message.type === messageTypes.requestActionTeamMembers ? requestActionTeamMembers : requestActionFindLookup;
request(message, sender).then(sendResponse).catch(error => sendResponse({
ok: false, httpStatus: error.httpStatus, error: error.message || "Unable to complete request.",
debugRequests: Array.isArray(error.debugRequests) ? error.debugRequests : []
}));
return true;
}
if ([messageTypes.requestWorkloads, messageTypes.requestWorkloadForecastUpdate].includes(message.type)) {
const request = message.type === messageTypes.requestWorkloads ? requestWorkloads : requestWorkloadForecastUpdate;
request(message).then(sendResponse).catch(error => sendResponse({
ok: false, error: error.message || "Unable to load workloads.", httpStatus: error.httpStatus,
debugRequests: Array.isArray(error.debugRequests) ? error.debugRequests : []
}));
return true;
}
if (message.type === messageTypes.requestWlActions) {
requestWlActions(message).then(sendResponse).catch(error => sendResponse({
ok: false, error: error.message || "Unable to load workload actions.",
debugRequests: Array.isArray(error.debugRequests) ? error.debugRequests : []
}));
return true;
}
if (message.type === messageTypes.requestCustomerServiceRequest) {
requestCustomerServiceRequest(message).then(sendResponse).catch(error => sendResponse({
ok: false, error: error.message || "Unable to load service requests.",
debugRequests: Array.isArray(error.debugRequests) ? error.debugRequests : []
}));
return true;
}
if (message.type === messageTypes.requestInstallBase) {
requestInstallBase(message).then(sendResponse).catch(error => sendResponse({
ok: false, error: error.message || "Unable to load installed base.",
debugRequests: Array.isArray(error.debugRequests) ? error.debugRequests : []
}));
return true;
}
if (message.type === messageTypes.requestAccountPlans) {
requestAccountPlans(message)
.then(sendResponse)
.catch((error) => {
sendResponse({
ok: false,
error: error.message || "Unable to load account plans.",
debugRequests: Array.isArray(error.debugRequests) ? error.debugRequests : []
});
});
return true;
}
if (message.type === messageTypes.requestRatecardByPlan) {
requestRatecardByPlan(message).then(sendResponse).catch(error => sendResponse({
ok: false, error: error.message || "Unable to load rate card.",
debugRequests: Array.isArray(error.debugRequests) ? error.debugRequests : []
}));
return true;
}
if (message.type === messageTypes.requestConsumptionPlanDetail) {
requestConsumptionPlanDetail(message).then(sendResponse).catch(error => sendResponse({
ok: false, error: error.message || "Unable to load consumption plan details.",
debugRequests: Array.isArray(error.debugRequests) ? error.debugRequests : []
}));
return true;
}
if (message.type === messageTypes.requestConsumptionTrend) {
requestConsumptionTrend(message)
.then(sendResponse)
.catch((error) => {
sendResponse({
ok: false,
error: error.message || "Unable to load consumption trend.",
debugRequests: Array.isArray(error.debugRequests) ? error.debugRequests : []
});
});
return true;
}
return false;
});
}
// Source: src/background/application.js
function createBackgroundApplication(overrides = {}) {
const environment = {
fetch: globalThis.fetch,
browser: globalThis.browser,
chrome: globalThis.chrome,
setTimeout: globalThis.setTimeout,
clearTimeout: globalThis.clearTimeout,
...overrides
};
const messageTypes = createMessageTypes();
const timeEntries = createTimeEntries({ dates: createTimeDates(), fetchJson: (...args) => httpBackgroundJson.fetchJson(...args), getResourceUser: message => fusionSrCreate.getResourceUser(message), saveHeaders: url => srRequestHeaders.timeEntriesHeaders(url) });
const contracts = createBackgroundContracts();
const spaPageClient = createSpaPageClient({
sendMessage: (...args) => platformBackgroundApi.tabsSendMessage(...args),
getHttp: () => httpBackgroundJson
});
const spaCurrentUser = createSpaCurrentUser({
fetchJson: (...args) => httpBackgroundJson.fetchJson(...args),
fetchInTab: (tab, logs) => spaPageClient.requestIdentity(tab, logs),
openSessionTab: sender => authConsumerSession.openConsumerTokenSessionTab(sender),
waitForSession: (tab, entry) => authConsumerSession.waitForSpaPage(tab, entry),
closeSessionTab: id => authConsumerSession.closeConsumerTokenSessionTab(id),
sessionUrl: contracts.CUSTOMER_TOKEN_SESSION_URL
});
const requestCurrentUser = sender => spaCurrentUser.requestCurrentUser(sender);
const productsCacheState = createProductsCacheState();
const authConsumerSession = createAuthConsumerSession({
CUSTOMER_TOKEN_MAX_ATTEMPTS: contracts.CUSTOMER_TOKEN_MAX_ATTEMPTS,
CUSTOMER_TOKEN_RETRY_BASE_DELAY_MS: contracts.CUSTOMER_TOKEN_RETRY_BASE_DELAY_MS,
CUSTOMER_TOKEN_RETRY_MAX_DELAY_MS: contracts.CUSTOMER_TOKEN_RETRY_MAX_DELAY_MS,
CUSTOMER_TOKEN_SERVICE_URL: contracts.CUSTOMER_TOKEN_SERVICE_URL,
CUSTOMER_TOKEN_SESSION_URL: contracts.CUSTOMER_TOKEN_SESSION_URL,
extractTokenServiceAuthorization: (...args) => authSpaCredentials.extractTokenServiceAuthorization(...args),
fetchJson: (...args) => httpBackgroundJson.fetchJson(...args),
supportsFirefoxCookieStoreTabs: (...args) => platformBackgroundApi.supportsFirefoxCookieStoreTabs(...args),
tabsCreate: (...args) => platformBackgroundApi.tabsCreate(...args),
tabsUpdate: (...args) => platformBackgroundApi.tabsUpdate(...args),
tabsRemove: (...args) => platformBackgroundApi.tabsRemove(...args),
responseHeaders: (environment.browser || environment.chrome)?.webRequest?.onHeadersReceived,
cookieChanges: (environment.browser || environment.chrome)?.cookies?.onChanged,
inspectPage: tab => spaPageClient.inspect(tab),
setTimeout: environment.setTimeout,
clearTimeout: environment.clearTimeout,
wait: (...args) => platformBackgroundApi.wait(...args)
});
const authSpaCredentials = createAuthSpaCredentials({
findNestedObject: (...args) => spaResponseShapes.findNestedObject(...args)
});
const spaAccounts = createSpaAccounts({
ACCOUNT_PLANS_PAYLOAD: contracts.ACCOUNT_PLANS_PAYLOAD,
ACCOUNT_PLANS_URL: contracts.ACCOUNT_PLANS_URL,
ACCOUNT_SUMMARY_URL: contracts.ACCOUNT_SUMMARY_URL,
extractAccountPlans: (...args) => spaResponseShapes.extractAccountPlans(...args),
fetchJson: (...args) => httpBackgroundJson.fetchJson(...args),
findAccountPlanId: (...args) => spaResponseShapes.findAccountPlanId(...args),
normalizeMessageAuthorization: (...args) => authSpaCredentials.normalizeMessageAuthorization(...args)
});
const spaInstallBase = createSpaInstallBase({
ACCOUNT_SUMMARY_URL: contracts.ACCOUNT_SUMMARY_URL,
fetchJson: (...args) => httpBackgroundJson.fetchJson(...args),
findNestedObject: (...args) => spaResponseShapes.findNestedObject(...args),
normalizeMessageAuthorization: (...args) => authSpaCredentials.normalizeMessageAuthorization(...args)
});
const spaCustomerServiceRequests = createSpaCustomerServiceRequests({
ACCOUNT_SUMMARY_URL: contracts.ACCOUNT_SUMMARY_URL,
fetchJson: (...args) => httpBackgroundJson.fetchJson(...args),
findAccountPlanId: (...args) => spaResponseShapes.findAccountPlanId(...args),
normalizeMessageAuthorization: (...args) => authSpaCredentials.normalizeMessageAuthorization(...args)
});
const spaConsumption = createSpaConsumption({
CONSUMPTION_TREND_REQUESTS: contracts.CONSUMPTION_TREND_REQUESTS,
CONSUMPTION_TREND_URL: contracts.CONSUMPTION_TREND_URL,
fetchJson: (...args) => httpBackgroundJson.fetchJson(...args),
normalizeMessageAuthorization: (...args) => authSpaCredentials.normalizeMessageAuthorization(...args)
});
const spaResponseShapes = createSpaResponseShapes();
const httpBackgroundJson = createHttpBackgroundJson({
observeResponseHeaders: (...args) => srRequestHeaders.observeResponseHeaders(...args),
previewDebugBody: createDebugBodyPreview,
environment
});
const productsBackgroundJobs = createProductsBackgroundJobs({
messageTypes,
OPPTY_PRODUCTS_CONCURRENCY: contracts.OPPTY_PRODUCTS_CONCURRENCY,
getOpptyProducts: (...args) => productsCache.getOpptyProducts(...args),
normalizeCacheMaxAge: (...args) => productsCache.normalizeCacheMaxAge(...args),
tabsSendMessage: (...args) => platformBackgroundApi.tabsSendMessage(...args)
});
const productsCache = createProductsCache({
OPPTY_PRODUCTS_CACHE_KEY: contracts.OPPTY_PRODUCTS_CACHE_KEY,
fetchAllOpptyProducts: (...args) => fusionProducts.fetchAllOpptyProducts(...args),
productsCacheState,
storageLocalGet: (...args) => platformBackgroundApi.storageLocalGet(...args),
storageLocalSet: (...args) => platformBackgroundApi.storageLocalSet(...args)
});
const fusionProducts = createFusionProducts({
createBackgroundDebugEntry: (...args) => httpBackgroundJson.createBackgroundDebugEntry(...args),
completeBackgroundDebugEntry: (...args) => httpBackgroundJson.completeBackgroundDebugEntry(...args),
createDebugBody: (...args) => httpBackgroundJson.createDebugBody(...args),
headersToObject: (...args) => httpBackgroundJson.headersToObject(...args),
OPPTY_PRODUCTS_FIELDS: contracts.OPPTY_PRODUCTS_FIELDS,
OPPTY_PRODUCTS_MAX_PAGES: contracts.OPPTY_PRODUCTS_MAX_PAGES,
OPPTY_PRODUCTS_PAGE_LIMIT: contracts.OPPTY_PRODUCTS_PAGE_LIMIT,
ORACLE_DOMAIN: contracts.ORACLE_DOMAIN,
environment
});
const authXsrfRecovery = createAuthXsrfRecovery({
COOKIE_REFRESH_POLL_MS: contracts.COOKIE_REFRESH_POLL_MS,
COOKIE_REFRESH_TIMEOUT_MS: contracts.COOKIE_REFRESH_TIMEOUT_MS,
OPPORTUNITIES_LIST_URL: contracts.OPPORTUNITIES_LIST_URL,
getXsrfTokenCookie: (...args) => authXsrfCookies.getXsrfTokenCookie(...args),
supportsFirefoxCookieStoreTabs: (...args) => platformBackgroundApi.supportsFirefoxCookieStoreTabs(...args),
tabsCreate: (...args) => platformBackgroundApi.tabsCreate(...args),
tabsRemove: (...args) => platformBackgroundApi.tabsRemove(...args),
wait: (...args) => platformBackgroundApi.wait(...args)
});
const authXsrfCookies = createAuthXsrfCookies({
ORACLE_COOKIE_URLS: contracts.ORACLE_COOKIE_URLS,
ORACLE_DOMAIN: contracts.ORACLE_DOMAIN,
XSRF_COOKIE_NAME: contracts.XSRF_COOKIE_NAME,
XSRF_COOKIE_PREFIX: contracts.XSRF_COOKIE_PREFIX,
cookiesGet: (...args) => platformBackgroundApi.cookiesGet(...args),
cookiesGetAll: (...args) => platformBackgroundApi.cookiesGetAll(...args),
cookiesGetAllCookieStores: (...args) => platformBackgroundApi.cookiesGetAllCookieStores(...args),
removeUndefinedValues: (...args) => platformBackgroundApi.removeUndefinedValues(...args)
});
const platformBackgroundApi = createPlatformBackgroundApi({
runtimeApi: environment.browser || environment.chrome,
environment
});
const getXsrfTokenCookie = (...args) => authXsrfCookies.getXsrfTokenCookie(...args);
const refreshXsrfCookie = (...args) => authXsrfRecovery.refreshXsrfCookie(...args);
const requestAccountPlans = (...args) => spaAccounts.requestAccountPlans(...args);
const requestInstallBase = (...args) => spaInstallBase.requestInstallBase(...args);
const spaWorkloadActions = createSpaWorkloadActions({ fetchJson: (...args) => httpBackgroundJson.fetchJson(...args) });
const workloadsCache = createWorkloadsCache({
fetchAllWorkloads: (...args) => spaWorkloads.fetchAllWorkloads(...args),
storageLocalGet: (...args) => platformBackgroundApi.storageLocalGet(...args),
storageLocalSet: (...args) => platformBackgroundApi.storageLocalSet(...args)
});
const spaWorkloads = createSpaWorkloads({
invalidateWorkloadsCache: () => workloadsCache.invalidate(),
fetchJson: (...args) => httpBackgroundJson.fetchJson(...args),
getCachedWorkloads: (...args) => workloadsCache.get(...args),
normalizeCacheMaxAge: value => productsCache.normalizeCacheMaxAge(value)
});
const requestWlActions = (...args) => spaWorkloadActions.requestWlActions(...args);
const spaActionOptions = createSpaActionOptions({ fetchJson: (...args) => httpBackgroundJson.fetchJson(...args) });
const requestActionTeamMembers = (...args) => spaActionOptions.requestActionTeamMembers(...args);
const requestActionFindLookup = (...args) => spaActionOptions.requestActionFindLookup(...args);
const requestCustomerServiceRequest = (...args) => spaCustomerServiceRequests.requestCustomerServiceRequest(...args);
const requestConsumerTokenService = (...args) => authConsumerSession.requestConsumerTokenService(...args);
const requestConsumptionTrend = (...args) => spaConsumption.requestConsumptionTrend(...args);
const requestConsumptionPlanDetail = (...args) => spaConsumption.requestConsumptionPlanDetail(...args);
const requestRatecardByPlan = (...args) => spaConsumption.requestRatecardByPlan(...args);
const requestOpptyProducts = (...args) => productsBackgroundJobs.requestOpptyProducts(...args);
const runtimeApi = environment.browser || environment.chrome;
const srRequestHeaders = createSrRequestHeaders({ runtimeApi });
const fusionSrCreate = createFusionSrCreate({
runtimeApi,
fetchJson: (...args) => httpBackgroundJson.fetchJson(...args),
tokenHeaders: (...args) => srRequestHeaders.tokenHeaders(...args),
internalServiceRequestHeaders: url => srRequestHeaders.internalServiceRequestHeaders(url),
previewTokenHeaders: url => srRequestHeaders.previewTokenHeaders(url)
});
const fusionSrDetails = createFusionSrDetails({
cookieChanges: runtimeApi?.cookies?.onChanged,
cookiesGetAll: (...args) => platformBackgroundApi.cookiesGetAll(...args),
tabsCreate: (...args) => platformBackgroundApi.tabsCreate(...args),
tabsRemove: (...args) => platformBackgroundApi.tabsRemove(...args),
supportsFirefoxCookieStoreTabs: () => platformBackgroundApi.supportsFirefoxCookieStoreTabs(),
wait: (...args) => platformBackgroundApi.wait(...args),
fetchJson: (...args) => httpBackgroundJson.fetchJson(...args)
});
function start() {
srRequestHeaders.start();
startBackgroundApplication({
requestWorkloads: message => spaWorkloads.requestWorkloads(message),
requestWorkloadForecastUpdate: message => spaWorkloads.requestWorkloadForecastUpdate(message),
invalidateWorkloadsCache: () => workloadsCache.invalidate(),
invalidateResourceUser: () => fusionSrCreate.invalidateResourceUser(),
requestWeekDetails: message => timeEntries.requestWeekDetails(message),
requestTimeEntries: message => timeEntries.requestTimeEntries(message),
requestTimeEntriesSave: message => timeEntries.requestTimeEntriesSave(message),
requestTimeEntriesDelete: message => timeEntries.requestTimeEntriesDelete(message),
requestLookupTaskType: () => timeEntries.requestLookupTaskType(),
requestServiceRequestExpand: message => timeEntries.requestServiceRequestExpand(message),
requestResourceUser: message => fusionSrCreate.getResourceUser(message),
requestActiveServiceRequests: message => timeEntries.requestActiveServiceRequests(message),
requestLookupTypePlatform: () => fusionSrDetails.requestLookupTypePlatform(),
requestPostInternalServiceRequest: message => fusionSrCreate.requestPostInternalServiceRequest(message),
requestInternalSrServices: message => fusionSrDetails.requestInternalSrServices(message),
requestPostServiceRequest: message => fusionSrCreate.requestPostServiceRequest(message), prepareSrSession: sender => fusionSrDetails.prepareSession(sender), requestSrLookupWithSession: (...args) => fusionSrDetails.requestLookupWithSession(...args), messageTypes, requestSrLookupLanguage: () => fusionSrDetails.requestSrLookupLanguage(), requestSrLookupTechPlatform: () => fusionSrDetails.requestSrLookupTechPlatform(), requestSrLookupOpportunityBusiness: message => fusionSrDetails.requestSrLookupOpportunityBusiness(message), requestSrLookupPrimaryPillar: () => fusionSrDetails.requestSrLookupPrimaryPillar(), requestInternalServiceRequest: (message, sender) => fusionSrDetails.requestInternalServiceRequest(message, sender), requestMgmtAction: message => spaActionOptions.requestMgmtAction(message), invalidateProductsCache: () => productsCache.invalidate(), requestCurrentUser, getXsrfTokenCookie, refreshXsrfCookie, requestAccountPlans, requestInstallBase, requestCustomerServiceRequest, requestWlActions, requestActionTeamMembers, requestActionFindLookup, requestConsumerTokenService, requestConsumptionTrend, requestConsumptionPlanDetail, requestRatecardByPlan, requestOpptyProducts, runtimeApi });
}
return {
start, contracts,
productsCacheState,
authConsumerSession,
authSpaCredentials,
spaAccounts,
spaInstallBase,
spaCustomerServiceRequests,
spaConsumption,
spaResponseShapes,
httpBackgroundJson,
productsBackgroundJobs,
productsCache,
workloadsCache,
spaWorkloads,
fusionProducts,
authXsrfRecovery,
authXsrfCookies,
platformBackgroundApi
};
}
createBackgroundApplication().start();
})();