fix(email-renderer): preserve fallbacks when AMP validation is unavailable
Quality / quality (ubuntu-latest) (push) Failing after 1s
Quality / quality (windows-latest) (push) Canceled after 0s

This commit is contained in:
Clintchiz committed 2026-09-05 13:24:26 +05:30
1 parent 2f735b267b
commit 42b91f765d
3 files changed
+30 -3

No files matched your search

+18 -2
View File
@@ -110,6 +110,9 @@ export async function renderEmail(
variables?: Record<string, unknown>;
requireVariables?: boolean;
countdownImageUrl?: string;
/** Trusted host injection; never sourced from the editable document. */
ampValidator?: (content: string) => Promise<ValidationReport>;
ampValidationTimeoutMs?: number;
} = {},
): Promise<Result<EmailRenderBundle>> {
const personalized = resolveDocumentVariables(document, emailKind.variables, options.variables, {
@@ -164,10 +167,23 @@ export async function renderEmail(
const amp = await renderTree(source, emailKind, registry, { ...base, target: "ampEmail", data: options.variables });
if (amp.ok) {
const content = ampHead(source, amp.value.content, base.direction);
const validation = await validateAmpEmail(content);
let timer: ReturnType<typeof setTimeout> | undefined;
let validation: ValidationReport;
const requestedTimeout = options.ampValidationTimeoutMs ?? 2000;
const timeout = Number.isFinite(requestedTimeout) ? Math.min(10000, Math.max(10, requestedTimeout)) : 2000;
try {
validation = await Promise.race([
Promise.resolve().then(() => (options.ampValidator ?? validateAmpEmail)(content)),
new Promise<never>((_, reject) => { timer = setTimeout(() => reject(new Error("AMP validation timed out.")), timeout); }),
]);
} catch {
validation = { valid: false, diagnostics: [{code:"amp.validatorUnavailable",message:"AMP validation was unavailable or timed out. HTML and plain text remain available.",severity:"warning"}] };
} finally {
if (timer !== undefined) clearTimeout(timer);
}
if (validation.valid)
bundle.parts.amp = { contentType: "text/x-amp-html; charset=utf-8", content, validation };
else warnings.push(...validation.diagnostics);
else warnings.push(...validation.diagnostics.map(diagnostic => ({...diagnostic,severity:"warning" as const})));
} else
warnings.push(...amp.errors.map((error) => ({ ...error, severity: "warning" as const })));
}