1(function attachDashboardClient(root) { 2 "use strict"; 3 4 function normalizeHost(value) { 5 const raw = String(value || "").trim(); 6 if (!raw) return ""; 7 try { 8 const parsed = new URL(raw.includes("://") ? raw : `https://${raw}`); 9 return parsed.hostname.toLowerCase().replace(/\.$/, ""); 10 } catch (_error) { 11 return ""; 12 } 13 } 14 15 function dashboardSitePath(route, host) { 16 return `/api/dashboard/${route}?url=${encodeURIComponent(`https://${host}`)}`; 17 } 18 19 function createDashboardClient(options) { 20 const request = options && options.request; 21 if (typeof request !== "function") throw new TypeError("dashboard_request_required"); 22 let allowedSites = new Map(); 23 let generation = 0; 24 const stale = () => Object.assign(new Error("dashboard_request_superseded"), {code: "dashboard_request_superseded"}); 25 26 function publicSites() { 27 return [...allowedSites.values()].map((site) => ({...site})); 28 } 29 30 async function loadSite(value) { 31 const epoch = ++generation; 32 const host = normalizeHost(value); 33 if (!host || !allowedSites.has(host)) { 34 const error = new Error("dashboard_site_not_authorized"); 35 error.code = "dashboard_site_not_authorized"; 36 throw error; 37 } 38 const scoped = async (route) => { 39 try { 40 const result = await request(dashboardSitePath(route, host), {cache: "no-store"}); 41 if (epoch !== generation) throw stale(); 42 const returnedHost = normalizeHost(result?.host || result?.customer?.host); 43 if ((route === "data" && !returnedHost) || (returnedHost && returnedHost !== host)) { 44 throw new Error("dashboard_response_subject_mismatch"); 45 } 46 return result; 47 } catch (error) { 48 if (epoch !== generation) throw stale(); 49 if (error.status === 401) { allowedSites.clear(); generation++; } 50 throw error; 51 } 52 }; 53 const data = await scoped("data"); 54 const connectors = await scoped("connectors"); 55 const tasks = await scoped("tasks"); 56 const billing = await scoped("billing"); 57 const decision = await scoped("decision"); 58 return {host, data, connectors, tasks, billing, decision}; 59 } 60 61 async function bootstrap(pathname, requestedSite) { 62 allowedSites.clear(); 63 generation++; 64 const normalizedPath = String(pathname || "").replace(/\/+$/, "") || "/"; 65 if (normalizedPath === "/dashboard/demo") { 66 const data = await request("/api/demo/dashboard"); 67 return {mode: "demo", selectedHost: normalizeHost(data?.customer?.host) || "xagent.id", data}; 68 } 69 try { 70 await request("/api/dashboard/session"); 71 } catch (error) { 72 if (error && (error.status === 401 || String(error.message || "").includes("dashboard_session_invalid_or_expired"))) { 73 allowedSites = new Map(); 74 return {mode: "login_required", reason: "dashboard_session_invalid_or_expired", sites: []}; 75 } 76 throw error; 77 } 78 const payload = await request("/api/dashboard/sites"); 79 allowedSites = new Map(); 80 for (const item of payload?.sites || []) { 81 const host = normalizeHost(item?.host); 82 if (host && !allowedSites.has(host)) allowedSites.set(host, {...item, host}); 83 } 84 if (!allowedSites.size) { 85 const error = new Error("dashboard_has_no_linked_sites"); 86 error.code = "dashboard_has_no_linked_sites"; 87 throw error; 88 } 89 const requestedHost = normalizeHost(requestedSite); 90 const requestedSiteRejected = Boolean(requestedHost && !allowedSites.has(requestedHost)); 91 const selectedHost = requestedHost && allowedSites.has(requestedHost) 92 ? requestedHost 93 : allowedSites.keys().next().value; 94 const bundle = await loadSite(selectedHost); 95 return { 96 mode: "customer", 97 sites: publicSites(), 98 selectedHost, 99 requestedSiteRejected, 100 bundle, 101 }; 102 } 103 104 async function logout() { 105 allowedSites = new Map(); 106 generation++; 107 return request("/api/dashboard/logout", {method: "POST"}); 108 } 109 110 return {bootstrap, loadSite, logout, sites: publicSites}; 111 } 112 113 const api = {createDashboardClient, normalizeHost}; 114 if (typeof module === "object" && module.exports) module.exports = api; 115 if (root) root.AgentIdDashboardClient = api; 116})(typeof window !== "undefined" ? window : globalThis);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.