import { t as getEnvAllowedOrigins } from "./public-url-Danv47cS.mjs";

//#region src/auth/allowed-origins.ts
/**
* Resolution and validation of multi-origin passkey verification.
*
* `allowedOrigins` lets one EmDash deployment accept passkey assertions from
* several hostnames sharing the same `rpId` (e.g. apex + preview/staging
* subdomains under one registrable parent). Origins come from two sources:
*
*   - `EmDashConfig.allowedOrigins` (declared in `astro.config.mjs`)
*   - `EMDASH_ALLOWED_ORIGINS` (comma-separated runtime env var)
*
* Sources are merged (union of permissions, deduplicated). Each entry is
* validated against `siteUrl` to fail loud on dead config the browser would
* never honor.
*/
/**
* Collect raw allowedOrigins from config and env, source-tagged.
*
* Returns raw values — the caller is expected to pass the result through
* `validateAllowedOrigins()` before use in passkey verification.
*/
function getConfiguredAllowedOrigins(config) {
	const tagged = [];
	if (config?.allowedOrigins) {
		for (const origin of config.allowedOrigins) if (origin) tagged.push({
			origin,
			source: "config.allowedOrigins"
		});
	}
	for (const origin of getEnvAllowedOrigins()) tagged.push({
		origin,
		source: "EMDASH_ALLOWED_ORIGINS"
	});
	return tagged;
}
/**
* Validate per-entry shape rules (no `siteUrl` needed):
*   - parses as `URL`
*   - protocol is `http:` or `https:`
*   - hostname has no trailing dot (`example.com.` rejected)
*   - hostname has no empty labels (`foo..example.com` rejected)
*
* Returns the deduplicated, normalized origin form (`URL.origin`) of every
* input, in input order. Throws on the first violation with a source-tagged
* error message.
*/
function validateOriginShape(tagged) {
	const normalized = [];
	const seen = /* @__PURE__ */ new Set();
	for (const { origin, source } of tagged) {
		let parsed;
		try {
			parsed = new URL(origin);
		} catch (e) {
			throw configError(source, `invalid URL: "${origin}"`, e);
		}
		if (parsed.protocol !== "http:" && parsed.protocol !== "https:") throw configError(source, `origin must be http or https: "${origin}" (got ${parsed.protocol})`);
		if (parsed.hostname.endsWith(".")) throw configError(source, `hostname has a trailing dot: "${origin}". Remove the trailing dot — assertion origins from the browser do not include it.`);
		if (parsed.hostname.split(".").includes("")) throw configError(source, `hostname has empty labels: "${origin}"`);
		if (!seen.has(parsed.origin)) {
			seen.add(parsed.origin);
			normalized.push(parsed.origin);
		}
	}
	return normalized;
}
/**
* Validate the effective merged allowedOrigins set against `siteUrl`.
*
* Performs `validateOriginShape()` plus the siteUrl-dependent rules:
*   - Rule A: non-empty origins ⇒ `siteUrl` is set
*   - `siteUrl` hostname is not an IP literal (multi-origin requires a domain)
*   - `siteUrl` hostname has no trailing dot (cannot match assertion origins)
*   - Rule B: each origin's hostname is `siteHost` exactly or a subdomain
*
* Throws on first violation. Returns the deduplicated normalized origins.
*
* Use this at the runtime chokepoint (where config + env are merged into the
* effective set). At Astro integration init, prefer `validateOriginShape()`
* for shape-only checks on `config.allowedOrigins`, since `siteUrl` may be
* supplied at runtime via `EMDASH_SITE_URL`.
*/
function validateAllowedOrigins(siteUrl, tagged) {
	const normalized = validateOriginShape(tagged);
	if (normalized.length === 0) return normalized;
	if (!siteUrl) throw new Error(`EmDash config error: allowedOrigins is set (${normalized.length} ${normalized.length === 1 ? "entry" : "entries"}) but siteUrl is not. Without a canonical siteUrl, rpId is derived from the request hostname, defeating multi-origin passkeys. Set siteUrl in astro.config.mjs or via EMDASH_SITE_URL.`);
	let siteHost;
	try {
		siteHost = new URL(siteUrl).hostname;
	} catch (e) {
		throw new Error(`EmDash config error: siteUrl is not a valid URL: "${siteUrl}"`, { cause: e });
	}
	if (siteHost.endsWith(".")) throw new Error(`EmDash config error: siteUrl "${siteUrl}" has a trailing-dot hostname, which cannot match assertion origins. Remove the trailing dot when using allowedOrigins.`);
	if (isIPLiteralHostname(siteHost)) throw new Error(`EmDash config error: siteUrl "${siteUrl}" uses an IP-literal hostname. Multi-origin passkeys require a domain-based siteUrl — IP addresses cannot have valid subdomains for WebAuthn rpId.`);
	for (const { origin, source } of tagged) {
		const h = new URL(origin).hostname;
		if (h !== siteHost && !h.endsWith("." + siteHost)) throw configError(source, `"${origin}" is not a subdomain of siteUrl "${siteUrl}". Allowed origins must be the same hostname as siteUrl or a subdomain of it.`);
	}
	return normalized;
}
function configError(source, detail, cause) {
	const err = /* @__PURE__ */ new Error(`EmDash config error in ${source}: ${detail}`);
	if (cause !== void 0) err.cause = cause;
	return err;
}
const IPV4_DOTTED_DECIMAL_RE = /^\d+(\.\d+){3}$/;
function isIPLiteralHostname(h) {
	if (h.startsWith("[")) return true;
	return IPV4_DOTTED_DECIMAL_RE.test(h);
}

//#endregion
export { validateAllowedOrigins as n, validateOriginShape as r, getConfiguredAllowedOrigins as t };
//# sourceMappingURL=allowed-origins-CRsJ6BPM.mjs.map