//#region src/http-cache-headers.ts
var CacheHeaderError = class extends Error {
	code;
	details;
	constructor(code, message, details = {}) {
		super(message);
		this.name = "CacheHeaderError";
		this.code = code;
		this.details = details;
	}
};
const CACHE_TAG_MAX_COUNT = 1e3;
const CACHE_TAG_MAX_LENGTH = 16384;
const CDN_HEADER_BY_PROFILE = {
	cloudflare: "Cloudflare-CDN-Cache-Control",
	generic: "CDN-Cache-Control",
	fastly: "Surrogate-Control"
};
function cacheHeaders(input) {
	const headers = {};
	const profile = input.profile ?? "cloudflare";
	const cdnHeaderName = CDN_HEADER_BY_PROFILE[profile];
	if (cdnHeaderName === void 0) throw new CacheHeaderError("invalid_cdn_profile", "Unsupported CDN cache header profile.", { profile });
	if (input.cdn?.sMaxage !== void 0 && input.cdn.staleWhileRevalidate !== void 0) throw new CacheHeaderError("cdn_smaxage_with_stale_while_revalidate", "CDN s-maxage cannot be combined with stale-while-revalidate.", { cdn: input.cdn });
	if (input.cdn !== void 0 && (input.browser?.private === true || input.browser?.noStore === true)) throw new CacheHeaderError("private_browser_with_cdn", "Browser private or no-store directives cannot be combined with CDN directives.", {
		browser: input.browser,
		cdn: input.cdn
	});
	if (input.browser !== void 0) {
		const browser = serializeDirectives(input.browser, "browser");
		if (browser !== "") headers["Cache-Control"] = browser;
	} else if (input.cdn !== void 0) headers["Cache-Control"] = "max-age=0, must-revalidate";
	if (input.cdn !== void 0) {
		const cdn = serializeDirectives(input.cdn, "cdn");
		if (cdn !== "") headers[cdnHeaderName] = cdn;
	}
	if (input.tags !== void 0 && input.tags.length > 0) headers["Cache-Tag"] = serializeTags(input.tags);
	return headers;
}
function etagFrom(hash, opts) {
	return `${opts?.weak === true ? "W/" : ""}"${hash}"`;
}
function notModified(ifNoneMatch, etag) {
	if (ifNoneMatch === null) return false;
	const trimmed = ifNoneMatch.trim();
	if (trimmed === "*") return true;
	const target = normalizeEtag(etag);
	return splitEtags(trimmed).map((candidate) => normalizeEtag(candidate.trim())).some((candidate) => candidate !== "" && candidate === target);
}
function serializeDirectives(directives, audience) {
	const parts = [];
	if (directives.noStore === true) parts.push("no-store");
	if (directives.noCache === true) parts.push("no-cache");
	if (directives.private === true) parts.push("private");
	addSeconds(parts, "max-age", directives.maxAge);
	if (audience === "cdn") addSeconds(parts, "s-maxage", directives.sMaxage);
	addSeconds(parts, "stale-while-revalidate", directives.staleWhileRevalidate);
	addSeconds(parts, "stale-if-error", directives.staleIfError);
	if (directives.mustRevalidate === true) parts.push("must-revalidate");
	if (directives.immutable === true) parts.push("immutable");
	return parts.join(", ");
}
function addSeconds(parts, name, value) {
	if (value === void 0) return;
	if (!Number.isInteger(value) || value < 0) throw new CacheHeaderError("invalid_cache_directive", "Cache directive seconds must be a non-negative integer.", {
		directive: name,
		value
	});
	parts.push(`${name}=${value}`);
}
function serializeTags(tags) {
	if (tags.length > CACHE_TAG_MAX_COUNT) throw new CacheHeaderError("too_many_cache_tags", "Cache-Tag count exceeds the Cloudflare cap.", {
		count: tags.length,
		max: CACHE_TAG_MAX_COUNT
	});
	for (const tag of tags) if (tag === "" || /\s/.test(tag)) throw new CacheHeaderError("invalid_cache_tag", "Cache tags must be non-empty and contain no spaces.", { tag });
	const joined = tags.join(",");
	if (joined.length > CACHE_TAG_MAX_LENGTH) throw new CacheHeaderError("cache_tags_too_long", "Cache-Tag header exceeds the Cloudflare length cap.", {
		length: joined.length,
		max: CACHE_TAG_MAX_LENGTH
	});
	return joined;
}
function normalizeEtag(value) {
	return (value.startsWith("W/") ? value.slice(2) : value).trim();
}
function splitEtags(value) {
	const tags = [];
	let current = "";
	let inQuotes = false;
	for (let index = 0; index < value.length; index += 1) {
		const char = value[index];
		if (char === "\"") inQuotes = !inQuotes;
		if (char === "," && !inQuotes) {
			tags.push(current);
			current = "";
		} else current += char;
	}
	tags.push(current);
	return tags;
}
//#endregion
export { CacheHeaderError, cacheHeaders, etagFrom, notModified };
