const LOCAL_HOST = /^(localhost|127(?:\.\d{1,3}){3}|\[::1\]|0\.0\.0\.0|[\w-]+\.local(?:host)?)$/i; /** * Turns what someone types in the address bar into an http(s) URL. Local dev servers default to * http, everything else to https. Returns null for input that is not an address. */ export function normalizeBrowserUrl(input: string): string | null { const value = input.trim(); if (!value || /\s/.test(value)) return null; const hasScheme = /^[a-z][a-z\d+.-]*:\/\//i.test(value); if (!hasScheme && /^[a-z][a-z\d+.-]*:(?!\d)/i.test(value)) return null; let candidate = value; if (!hasScheme) { const host = value.split(/[/?#]/, 1)[0]!.replace(/:\d+$/, ""); if (!LOCAL_HOST.test(host) && !host.includes(".") && !/^\d+$/.test(host)) return null; candidate = `${LOCAL_HOST.test(host) ? "http" : "https"}://${value}`; } try { const url = new URL(candidate); if (url.protocol !== "http:" && url.protocol !== "https:") return null; if (url.username || url.password || !url.hostname) return null; return url.toString(); } catch { return null; } } /** * Sandbox for the web fallback. A page on the app's own origin must not get same-origin * access, or it could reach into the app around the sandbox. */ export function browserFrameSandbox(url: string, appOrigin: string): string { const base = "allow-scripts allow-forms allow-popups allow-modals allow-downloads"; try { return new URL(url).origin === appOrigin ? base : `${base} allow-same-origin`; } catch { return base; } }