794 lines
20 KiB
JavaScript
794 lines
20 KiB
JavaScript
(() => {
|
|
if (window.__archPanelPageBridgeInstalled) {
|
|
return;
|
|
}
|
|
|
|
window.__archPanelPageBridgeInstalled = true;
|
|
|
|
const MESSAGE_SOURCE = "arch-panel-extension";
|
|
const API_PATH_FRAGMENT = "/oalcrm/web/api/";
|
|
const EXTRA_CAPTURE_ORIGINS = new Set([
|
|
"https://comcipapic-oalprod.integration.ocp.oraclecloud.com",
|
|
]);
|
|
const ALLOWED_TOP_MESSAGE_ORIGINS = new Set([
|
|
"https://eeho.fa.us2.oraclecloud.com",
|
|
"https://spa.oracle.com",
|
|
]);
|
|
const AUTH_STORAGE_HINT_PATTERN =
|
|
/(authorization|token|wwb|wlwb|provider|spa-ts|spats)/i;
|
|
const SAFE_HEADER_BLOCKLIST = new Set([
|
|
"accept-encoding",
|
|
"accept-language",
|
|
"connection",
|
|
"content-length",
|
|
"cookie",
|
|
"host",
|
|
"origin",
|
|
"referer",
|
|
"sec-ch-ua",
|
|
"sec-ch-ua-mobile",
|
|
"sec-ch-ua-platform",
|
|
"sec-fetch-dest",
|
|
"sec-fetch-mode",
|
|
"sec-fetch-site",
|
|
"user-agent",
|
|
]);
|
|
|
|
const bridgeState = {
|
|
globalHeaders: new Map(),
|
|
scopedHeaders: new Map(),
|
|
};
|
|
|
|
function getSafeTargetOrigin() {
|
|
const origin = String(window.location?.origin || "");
|
|
|
|
return origin && origin !== "null" ? origin : "*";
|
|
}
|
|
|
|
function safePostMessage(payload, targetWindow = window, targetOrigin = getSafeTargetOrigin()) {
|
|
try {
|
|
targetWindow.postMessage(payload, targetOrigin);
|
|
return true;
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
function getAllowedTopMessageOrigin() {
|
|
const candidates = [];
|
|
|
|
if (window.top === window) {
|
|
return getSafeTargetOrigin();
|
|
}
|
|
|
|
try {
|
|
const referrerOrigin = document.referrer
|
|
? new URL(document.referrer).origin
|
|
: "";
|
|
|
|
if (referrerOrigin) {
|
|
candidates.push(referrerOrigin);
|
|
}
|
|
} catch {
|
|
// Referrer parsing is best-effort; fall back to ancestorOrigins/current origin.
|
|
}
|
|
|
|
try {
|
|
const ancestorOrigins = Array.from(window.location?.ancestorOrigins || []);
|
|
candidates.push(...ancestorOrigins);
|
|
} catch {
|
|
// ancestorOrigins is browser-specific and may be unavailable.
|
|
}
|
|
|
|
for (const origin of candidates) {
|
|
if (ALLOWED_TOP_MESSAGE_ORIGINS.has(origin)) {
|
|
return origin;
|
|
}
|
|
}
|
|
|
|
return "";
|
|
}
|
|
|
|
function safePostToTop(payload) {
|
|
const targetOrigin = getAllowedTopMessageOrigin();
|
|
|
|
if (!targetOrigin) {
|
|
return false;
|
|
}
|
|
|
|
return safePostMessage(payload, window.top, targetOrigin);
|
|
}
|
|
|
|
const nativeFetch = window.fetch.bind(window);
|
|
const originalOpen = window.XMLHttpRequest.prototype.open;
|
|
const originalSetRequestHeader = window.XMLHttpRequest.prototype.setRequestHeader;
|
|
const originalSend = window.XMLHttpRequest.prototype.send;
|
|
|
|
function isIntegrationOrigin(url) {
|
|
try {
|
|
return (
|
|
new URL(url, window.location.origin).origin ===
|
|
"https://comcipapic-oalprod.integration.ocp.oraclecloud.com"
|
|
);
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
function normalizeUrl(input) {
|
|
try {
|
|
return new URL(input, window.location.origin).toString();
|
|
} catch {
|
|
return "";
|
|
}
|
|
}
|
|
|
|
function shouldCapture(url) {
|
|
if (!url) {
|
|
return false;
|
|
}
|
|
|
|
if (url.includes(API_PATH_FRAGMENT)) {
|
|
return true;
|
|
}
|
|
|
|
try {
|
|
return EXTRA_CAPTURE_ORIGINS.has(new URL(url).origin);
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
function normalizeHeaders(input) {
|
|
const normalized = new Map();
|
|
|
|
if (!input) {
|
|
return normalized;
|
|
}
|
|
|
|
const headers = new Headers(input);
|
|
|
|
headers.forEach((value, key) => {
|
|
const lowerKey = key.toLowerCase();
|
|
|
|
if (SAFE_HEADER_BLOCKLIST.has(lowerKey)) {
|
|
return;
|
|
}
|
|
|
|
if (lowerKey.startsWith("sec-")) {
|
|
return;
|
|
}
|
|
|
|
normalized.set(lowerKey, value);
|
|
});
|
|
|
|
return normalized;
|
|
}
|
|
|
|
function getScopeKeys(url) {
|
|
const normalizedUrl = normalizeUrl(url);
|
|
|
|
if (!normalizedUrl) {
|
|
return [];
|
|
}
|
|
|
|
try {
|
|
const parsedUrl = new URL(normalizedUrl);
|
|
const path = parsedUrl.pathname;
|
|
const keys = [`path:${path}`];
|
|
const providerProxyMatch = path.match(
|
|
/\/provider-proxy\/([^/]+)\//
|
|
);
|
|
const serviceMatch = path.match(
|
|
/\/provider-proxy\/[^/]+\/service\/([^/]+)\//
|
|
);
|
|
|
|
if (path.includes("/provider-proxy/")) {
|
|
keys.push("scope:provider-proxy");
|
|
}
|
|
|
|
if (path.includes("/identity-management/")) {
|
|
keys.push("scope:identity-management");
|
|
}
|
|
|
|
if (path.includes("/workbench-proxy/")) {
|
|
keys.push("scope:workbench-proxy");
|
|
}
|
|
|
|
if (path.includes("/elastic/")) {
|
|
keys.push("scope:elastic");
|
|
}
|
|
|
|
if (providerProxyMatch?.[1]) {
|
|
keys.push(`provider:${providerProxyMatch[1]}`);
|
|
}
|
|
|
|
if (serviceMatch?.[1]) {
|
|
keys.push(`service:${serviceMatch[1]}`);
|
|
}
|
|
|
|
keys.push("scope:api");
|
|
|
|
return keys;
|
|
} catch {
|
|
return [];
|
|
}
|
|
}
|
|
|
|
function mergeIntoBucket(bucket, headers) {
|
|
headers.forEach((value, key) => {
|
|
bucket.set(key, value);
|
|
});
|
|
}
|
|
|
|
function isAuthorizationLikeHeader(key) {
|
|
const normalizedKey = String(key || "").toLowerCase();
|
|
|
|
return (
|
|
normalizedKey === "authorization" ||
|
|
normalizedKey.endsWith("-authorization") ||
|
|
normalizedKey.includes("token")
|
|
);
|
|
}
|
|
|
|
function rememberHeaders(url, input) {
|
|
const headers = normalizeHeaders(input);
|
|
|
|
if (headers.size === 0) {
|
|
return;
|
|
}
|
|
|
|
headers.forEach((value, key) => {
|
|
if (!isAuthorizationLikeHeader(key)) {
|
|
bridgeState.globalHeaders.set(key, value);
|
|
}
|
|
});
|
|
|
|
for (const scopeKey of getScopeKeys(url)) {
|
|
const bucket = bridgeState.scopedHeaders.get(scopeKey) || new Map();
|
|
mergeIntoBucket(bucket, headers);
|
|
bridgeState.scopedHeaders.set(scopeKey, bucket);
|
|
}
|
|
|
|
publishCapturedAuthHeaders(url, headers);
|
|
}
|
|
|
|
function publishCapturedAuthHeaders(url, headers) {
|
|
const authHeaders = {};
|
|
|
|
headers.forEach((value, key) => {
|
|
if (isAuthorizationLikeHeader(key)) {
|
|
authHeaders[key] = value;
|
|
}
|
|
});
|
|
|
|
if (!Object.keys(authHeaders).length) {
|
|
return;
|
|
}
|
|
|
|
safePostToTop(
|
|
{
|
|
source: MESSAGE_SOURCE,
|
|
type: "ARCH_PANEL_WORKBENCH_AUTH_HEADERS",
|
|
url: normalizeUrl(url),
|
|
headers: authHeaders,
|
|
}
|
|
);
|
|
}
|
|
|
|
function buildHeaders(url, input, body) {
|
|
const merged = new Headers();
|
|
const activeScopeKeys = getScopeKeys(url);
|
|
|
|
bridgeState.globalHeaders.forEach((value, key) => {
|
|
merged.set(key, value);
|
|
});
|
|
|
|
for (const scopeKey of activeScopeKeys) {
|
|
const bucket = bridgeState.scopedHeaders.get(scopeKey);
|
|
|
|
if (!bucket) {
|
|
continue;
|
|
}
|
|
|
|
bucket.forEach((value, key) => {
|
|
merged.set(key, value);
|
|
});
|
|
}
|
|
|
|
normalizeHeaders(input).forEach((value, key) => {
|
|
merged.set(key, value);
|
|
});
|
|
|
|
if (activeScopeKeys.includes("scope:provider-proxy")) {
|
|
collectStoredAuthHeaders().forEach((value, key) => {
|
|
if (!merged.has(key)) {
|
|
merged.set(key, value);
|
|
}
|
|
});
|
|
}
|
|
|
|
if (!merged.has("accept")) {
|
|
merged.set("accept", "application/json, text/plain, */*");
|
|
}
|
|
|
|
if (!merged.has("x-requested-with")) {
|
|
merged.set("x-requested-with", "XMLHttpRequest");
|
|
}
|
|
|
|
if (activeScopeKeys.includes("scope:provider-proxy")) {
|
|
const allowedProviderHeaders = new Set([
|
|
"accept",
|
|
"authorization",
|
|
"content-type",
|
|
"spa-ts-authorization",
|
|
"wwb-provider-authorization",
|
|
"x-id-token",
|
|
"x-requested-with",
|
|
]);
|
|
|
|
for (const key of Array.from(merged.keys())) {
|
|
if (key.endsWith("-authorization") && !allowedProviderHeaders.has(key)) {
|
|
merged.delete(key);
|
|
}
|
|
}
|
|
|
|
if (merged.has("wwb-provider-authorization")) {
|
|
merged.delete("authorization");
|
|
}
|
|
}
|
|
|
|
if (isIntegrationOrigin(url)) {
|
|
const allowedIntegrationHeaders = new Set([
|
|
"accept",
|
|
"authorization",
|
|
"content-type",
|
|
"spa-ts-authorization",
|
|
"wwb-provider-authorization",
|
|
"x-id-token",
|
|
"x-requested-with",
|
|
]);
|
|
|
|
for (const key of Array.from(merged.keys())) {
|
|
if (!allowedIntegrationHeaders.has(key)) {
|
|
merged.delete(key);
|
|
}
|
|
}
|
|
|
|
if (merged.has("wwb-provider-authorization")) {
|
|
merged.delete("authorization");
|
|
}
|
|
}
|
|
|
|
if (!body) {
|
|
merged.delete("content-type");
|
|
}
|
|
|
|
return merged;
|
|
}
|
|
|
|
function buildHeaderSnapshot(options = {}) {
|
|
const snapshot = new Headers();
|
|
const scopeUrl = options.url || window.location.href;
|
|
const merged = buildHeaders(scopeUrl, options.headers, options.body);
|
|
|
|
merged.forEach((value, key) => {
|
|
snapshot.set(key, value);
|
|
});
|
|
|
|
if (options.includeAllAuth) {
|
|
bridgeState.scopedHeaders.forEach((bucket) => {
|
|
bucket.forEach((value, key) => {
|
|
if (isAuthorizationLikeHeader(key)) {
|
|
snapshot.set(key, value);
|
|
}
|
|
});
|
|
});
|
|
|
|
collectStoredAuthHeaders().forEach((value, key) => {
|
|
if (!snapshot.has(key)) {
|
|
snapshot.set(key, value);
|
|
}
|
|
});
|
|
}
|
|
|
|
return Object.fromEntries(snapshot.entries());
|
|
}
|
|
|
|
function collectStoredAuthHeaders() {
|
|
const headers = new Map();
|
|
const candidates = [];
|
|
|
|
for (const storage of [window.localStorage, window.sessionStorage]) {
|
|
try {
|
|
for (let index = 0; index < storage.length; index += 1) {
|
|
const key = storage.key(index) || "";
|
|
const value = storage.getItem(key) || "";
|
|
|
|
if (isAuthStorageCandidate(key, value)) {
|
|
candidates.push([key, value]);
|
|
}
|
|
}
|
|
} catch {
|
|
// Ignore storage access restrictions.
|
|
}
|
|
}
|
|
|
|
for (const [key, value] of candidates) {
|
|
collectAuthFromStorageEntry(headers, key, value);
|
|
}
|
|
|
|
return headers;
|
|
}
|
|
|
|
function isAuthStorageCandidate(key, value) {
|
|
const normalizedKey = String(key || "");
|
|
const normalizedValue = String(value || "").trim();
|
|
|
|
if (!normalizedKey && !normalizedValue) {
|
|
return false;
|
|
}
|
|
|
|
if (AUTH_STORAGE_HINT_PATTERN.test(normalizedKey)) {
|
|
return true;
|
|
}
|
|
|
|
if (looksLikeJwt(normalizedValue)) {
|
|
return true;
|
|
}
|
|
|
|
return AUTH_STORAGE_HINT_PATTERN.test(normalizedValue);
|
|
}
|
|
|
|
function collectAuthFromStorageEntry(headers, key, value) {
|
|
const normalizedKey = String(key || "").toLowerCase();
|
|
const normalizedValue = String(value || "").trim();
|
|
|
|
if (!normalizedValue) {
|
|
return;
|
|
}
|
|
|
|
if (
|
|
normalizedKey === "wwb-provider-authorization" ||
|
|
normalizedKey.includes("wwb-provider-authorization")
|
|
) {
|
|
headers.set("wwb-provider-authorization", normalizedValue);
|
|
}
|
|
|
|
if (
|
|
normalizedKey === "spa-ts-authorization" ||
|
|
normalizedKey.includes("spa-ts-authorization")
|
|
) {
|
|
headers.set("spa-ts-authorization", normalizedValue);
|
|
}
|
|
|
|
if (normalizedKey === "authorization") {
|
|
headers.set("authorization", normalizedValue);
|
|
}
|
|
|
|
collectAuthFromTokenString(headers, normalizedValue);
|
|
|
|
if (!(normalizedValue.startsWith("{") || normalizedValue.startsWith("["))) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
collectAuthFromObject(headers, JSON.parse(normalizedValue));
|
|
} catch {
|
|
// Ignore non-JSON values.
|
|
}
|
|
}
|
|
|
|
function collectAuthFromObject(headers, value, depth = 0) {
|
|
if (!value || typeof value !== "object" || depth > 5) {
|
|
return;
|
|
}
|
|
|
|
if (Array.isArray(value)) {
|
|
value.forEach((item) => collectAuthFromObject(headers, item, depth + 1));
|
|
return;
|
|
}
|
|
|
|
for (const [key, entryValue] of Object.entries(value)) {
|
|
const normalizedKey = String(key || "").toLowerCase();
|
|
|
|
if (typeof entryValue === "string" && entryValue.trim()) {
|
|
collectAuthFromTokenString(headers, entryValue.trim());
|
|
|
|
if (
|
|
normalizedKey === "wwb-provider-authorization" ||
|
|
normalizedKey.includes("wwbprovider") ||
|
|
normalizedKey.includes("wwb-provider")
|
|
) {
|
|
headers.set("wwb-provider-authorization", entryValue.trim());
|
|
}
|
|
|
|
if (
|
|
normalizedKey === "spa-ts-authorization" ||
|
|
normalizedKey.includes("spats") ||
|
|
normalizedKey.includes("spa-ts")
|
|
) {
|
|
headers.set("spa-ts-authorization", entryValue.trim());
|
|
}
|
|
|
|
if (normalizedKey === "authorization") {
|
|
headers.set("authorization", entryValue.trim());
|
|
}
|
|
}
|
|
|
|
collectAuthFromObject(headers, entryValue, depth + 1);
|
|
}
|
|
}
|
|
|
|
function collectAuthFromTokenString(headers, value) {
|
|
const token = String(value || "").trim();
|
|
|
|
if (!looksLikeJwt(token)) {
|
|
return;
|
|
}
|
|
|
|
const payload = decodeJwtPayload(token);
|
|
|
|
if (!payload) {
|
|
return;
|
|
}
|
|
|
|
const issuedFor = String(payload.issuedFor || payload.aud || "").toUpperCase();
|
|
const authorities = Array.isArray(payload.authorities)
|
|
? payload.authorities.map((authority) => String(authority || "").toUpperCase())
|
|
: [];
|
|
const scopes = Array.isArray(payload.scope)
|
|
? payload.scope.map((scope) => String(scope || "").toUpperCase())
|
|
: String(payload.scope || "")
|
|
.split(/\s+/)
|
|
.map((scope) => scope.toUpperCase())
|
|
.filter(Boolean);
|
|
const isWwbToken =
|
|
issuedFor.includes("WWB") ||
|
|
authorities.some((authority) => authority.startsWith("WLWB.")) ||
|
|
scopes.some((scope) => scope.includes("WWB") || scope.includes("WLWB"));
|
|
|
|
if (isWwbToken && !headers.has("wwb-provider-authorization")) {
|
|
headers.set("wwb-provider-authorization", token);
|
|
}
|
|
}
|
|
|
|
function looksLikeJwt(value) {
|
|
return /^[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+$/.test(
|
|
String(value || "").trim()
|
|
);
|
|
}
|
|
|
|
function decodeJwtPayload(token) {
|
|
try {
|
|
const payloadPart = String(token).split(".")[1];
|
|
const base64 = payloadPart.replace(/-/g, "+").replace(/_/g, "/");
|
|
const paddedBase64 = base64.padEnd(
|
|
base64.length + (4 - (base64.length % 4 || 4)),
|
|
"="
|
|
);
|
|
const json = decodeURIComponent(
|
|
Array.from(atob(paddedBase64), (char) => {
|
|
return `%${char.charCodeAt(0).toString(16).padStart(2, "0")}`;
|
|
}).join("")
|
|
);
|
|
|
|
return JSON.parse(json);
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
async function fetchWithCapturedHeaders(url, options = {}) {
|
|
const mergedHeaders = buildHeaders(url, options.headers, options.body);
|
|
const response = await nativeFetch(url, {
|
|
method: options.method || "GET",
|
|
credentials: "include",
|
|
cache: "no-store",
|
|
headers: mergedHeaders,
|
|
body: options.body,
|
|
});
|
|
const rawText = await response.text();
|
|
let payload;
|
|
|
|
try {
|
|
payload = rawText ? JSON.parse(rawText) : null;
|
|
} catch {
|
|
payload = rawText;
|
|
}
|
|
|
|
return {
|
|
ok: response.ok,
|
|
status: response.status,
|
|
statusText: response.statusText,
|
|
payload,
|
|
debug: {
|
|
url,
|
|
headerKeys: Array.from(mergedHeaders.keys()),
|
|
scopeKeys: getScopeKeys(url),
|
|
spaTabFetch: true,
|
|
},
|
|
};
|
|
}
|
|
|
|
window.__archPanelExtensionBridge = {
|
|
buildHeaderSnapshot,
|
|
fetchWithCapturedHeaders,
|
|
};
|
|
|
|
const trackedFetch = async function archPanelTrackedFetch(input, init) {
|
|
const url = normalizeUrl(typeof input === "string" ? input : input?.url);
|
|
|
|
if (shouldCapture(url)) {
|
|
rememberHeaders(url, init?.headers || input?.headers);
|
|
}
|
|
|
|
return nativeFetch(input, init);
|
|
};
|
|
|
|
window.fetch = trackedFetch;
|
|
|
|
window.XMLHttpRequest.prototype.open = function archPanelTrackedOpen(method, url, ...rest) {
|
|
this.__archPanelTrackedUrl = normalizeUrl(url);
|
|
this.__archPanelTrackedHeaders = new Headers();
|
|
return originalOpen.call(this, method, url, ...rest);
|
|
};
|
|
|
|
window.XMLHttpRequest.prototype.setRequestHeader = function archPanelTrackedSetRequestHeader(name, value) {
|
|
if (this.__archPanelTrackedHeaders) {
|
|
this.__archPanelTrackedHeaders.set(name, value);
|
|
}
|
|
|
|
return originalSetRequestHeader.call(this, name, value);
|
|
};
|
|
|
|
window.XMLHttpRequest.prototype.send = function archPanelTrackedSend(body) {
|
|
if (shouldCapture(this.__archPanelTrackedUrl)) {
|
|
rememberHeaders(this.__archPanelTrackedUrl, this.__archPanelTrackedHeaders);
|
|
}
|
|
|
|
return originalSend.call(this, body);
|
|
};
|
|
|
|
function requestViaOriginalXhr(url, options, headers) {
|
|
return new Promise((resolve, reject) => {
|
|
const xhr = new window.XMLHttpRequest();
|
|
|
|
originalOpen.call(xhr, options?.method || "GET", url, true);
|
|
xhr.withCredentials = true;
|
|
xhr.responseType = "text";
|
|
xhr.timeout = 60000;
|
|
|
|
headers.forEach((value, key) => {
|
|
originalSetRequestHeader.call(xhr, key, value);
|
|
});
|
|
|
|
xhr.onload = () => {
|
|
resolve({
|
|
ok: xhr.status >= 200 && xhr.status < 300,
|
|
status: xhr.status,
|
|
text: xhr.responseText || "",
|
|
});
|
|
};
|
|
|
|
xhr.onerror = () => {
|
|
reject(new Error("Failed to fetch"));
|
|
};
|
|
|
|
xhr.onabort = () => {
|
|
reject(new Error("Request aborted"));
|
|
};
|
|
|
|
xhr.ontimeout = () => {
|
|
reject(new Error(`Timed out while requesting ${url}`));
|
|
};
|
|
|
|
originalSend.call(xhr, options?.body ?? null);
|
|
});
|
|
}
|
|
|
|
window.addEventListener("message", async (event) => {
|
|
if (event.source !== window) {
|
|
return;
|
|
}
|
|
|
|
const message = event.data;
|
|
|
|
if (!message || message.source !== MESSAGE_SOURCE) {
|
|
return;
|
|
}
|
|
|
|
if (message.type === "ARCH_PANEL_HEADERS") {
|
|
safePostMessage(
|
|
{
|
|
source: MESSAGE_SOURCE,
|
|
type: "ARCH_PANEL_HEADERS_RESULT",
|
|
requestId: message.requestId,
|
|
headers: buildHeaderSnapshot(message.options),
|
|
}
|
|
);
|
|
return;
|
|
}
|
|
|
|
if (message.type !== "ARCH_PANEL_FETCH") {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const mergedHeaders = buildHeaders(
|
|
message.url,
|
|
message.options?.headers,
|
|
message.options?.body
|
|
);
|
|
const useXhr = message.options?.transport === "xhr";
|
|
const response = useXhr
|
|
? await requestViaOriginalXhr(message.url, message.options, mergedHeaders)
|
|
: await (async () => {
|
|
const activeFetch =
|
|
typeof window.fetch === "function"
|
|
? window.fetch.bind(window)
|
|
: nativeFetch;
|
|
const fetchResponse = await activeFetch(message.url, {
|
|
method: message.options?.method || "GET",
|
|
credentials: "include",
|
|
headers: mergedHeaders,
|
|
body: message.options?.body,
|
|
});
|
|
|
|
return {
|
|
ok: fetchResponse.ok,
|
|
status: fetchResponse.status,
|
|
text: await fetchResponse.text(),
|
|
};
|
|
})();
|
|
|
|
const rawText = response.text;
|
|
let payload;
|
|
|
|
try {
|
|
payload = rawText ? JSON.parse(rawText) : null;
|
|
} catch {
|
|
payload = rawText;
|
|
}
|
|
|
|
safePostMessage(
|
|
{
|
|
source: MESSAGE_SOURCE,
|
|
type: "ARCH_PANEL_FETCH_RESULT",
|
|
requestId: message.requestId,
|
|
ok: response.ok,
|
|
status: response.status,
|
|
debug: {
|
|
headerKeys: Array.from(mergedHeaders.keys()),
|
|
scopeKeys: getScopeKeys(message.url),
|
|
transport: useXhr ? "xhr" : "fetch",
|
|
},
|
|
payload,
|
|
}
|
|
);
|
|
} catch (error) {
|
|
safePostMessage(
|
|
{
|
|
source: MESSAGE_SOURCE,
|
|
type: "ARCH_PANEL_FETCH_RESULT",
|
|
requestId: message.requestId,
|
|
ok: false,
|
|
status: 0,
|
|
debug: {
|
|
scopeKeys: getScopeKeys(message.url),
|
|
transport: message.options?.transport || "fetch",
|
|
},
|
|
error: error instanceof Error ? error.message : String(error),
|
|
}
|
|
);
|
|
}
|
|
});
|
|
|
|
safePostMessage(
|
|
{
|
|
source: MESSAGE_SOURCE,
|
|
type: "ARCH_PANEL_BRIDGE_READY",
|
|
}
|
|
);
|
|
})();
|