import { SecurityError } from "./errors.ts"; const TRUSTED_HTML = Symbol("wrnexus.trusted-html"); export interface TrustedHtmlPolicy { /** Stable policy name used in diagnostics and CSP/Trusted Types integration. */ name: string; /** Application-supplied, reviewed sanitizer. It must return sanitized HTML. */ sanitize(input: string): string; } export interface TrustedHtmlValue { readonly policy: string; readonly value: string; readonly [TRUSTED_HTML]: true; } export function createTrustedHtml(input: string, policy: TrustedHtmlPolicy): TrustedHtmlValue { if (!policy?.name?.trim() || typeof policy.sanitize !== "function") { throw new SecurityError( "WRN-SEC-TRUSTED-HTML-POLICY", "Trusted HTML requires a named sanitizer policy.", ); } const value = policy.sanitize(String(input)); if (typeof value !== "string") { throw new SecurityError( "WRN-SEC-TRUSTED-HTML-RESULT", `Trusted HTML policy '${policy.name}' must return a string.`, ); } return Object.freeze({ policy: policy.name.trim(), value, [TRUSTED_HTML]: true as const }); } export function isTrustedHtml(value: unknown): value is TrustedHtmlValue { return Boolean( value && typeof value === "object" && (value as Partial)[TRUSTED_HTML] === true && typeof (value as Partial).value === "string", ); } export function unwrapTrustedHtml(value: TrustedHtmlValue): string { if (!isTrustedHtml(value)) { throw new SecurityError( "WRN-SEC-TRUSTED-HTML-REQUIRED", "Raw HTML must be produced by createTrustedHtml().", ); } return value.value; }