// The origin the BROWSER sees for a document request — what origin-derived UI// (the connect card's MCP URL, the API client's base) must be built from.//// On Cloudflare `request.url` already carries the external `https://` origin,// so this is a no-op there. Behind a TLS-terminating reverse proxy that// forwards plain HTTP upstream — `tailscale serve` for the dev-share workflow,// or an nginx/Caddy front — `request.url` is `http://` while the browser is on// `https://`; honoring the proxy's `X-Forwarded-Proto`/`-Host` recovers the// real origin (otherwise the HTTPS page would seed an `http://` API base and// the browser blocks it as mixed content).//// Trusting these headers is safe: the result only shapes the origin rendered// back to that same requester, never a cross-tenant decision (org access is// enforced server-side regardless of the URL the client was handed).//// Pure (no `cloudflare:workers`) so it unit-tests without the worker runtime.export const browserOriginFromRequest = (request: Request): string => { const url = new URL(request.url); const proto = request.headers.get("x-forwarded-proto")?.split(",")[0]?.trim(); const host = request.headers.get("x-forwarded-host")?.split(",")[0]?.trim(); if (!proto && !host) return url.origin; return `${proto || url.protocol.replace(":", "")}://${host || url.host}`;};
// The origin the BROWSER sees for a document request — what origin-derived UI
// (the connect card's MCP URL, the API client's base) must be built from.
//
// On Cloudflare `request.url` already carries the external `https://` origin,
// so this is a no-op there. Behind a TLS-terminating reverse proxy that
// forwards plain HTTP upstream — `tailscale serve` for the dev-share workflow,
// or an nginx/Caddy front — `request.url` is `http://` while the browser is on
// `https://`; honoring the proxy's `X-Forwarded-Proto`/`-Host` recovers the
// real origin (otherwise the HTTPS page would seed an `http://` API base and
// the browser blocks it as mixed content).
//
// Trusting these headers is safe: the result only shapes the origin rendered
// back to that same requester, never a cross-tenant decision (org access is
// enforced server-side regardless of the URL the client was handed).
//
// Pure (no `cloudflare:workers`) so it unit-tests without the worker runtime.
export const browserOriginFromRequest = (request: Request): string => {
const url = new URL(request.url);
const proto = request.headers.get("x-forwarded-proto")?.split(",")[0]?.trim();
const host = request.headers.get("x-forwarded-host")?.split(",")[0]?.trim();
if (!proto && !host) return url.origin;
return `${proto || url.protocol.replace(":", "")}://${host || url.host}`;
};