import { ok, err, type ReadonlyDB } from "@tailor-platform/erp-kit/core"; import { parseVariableSchema } from "../command/createNotificationTemplate"; import type { DB } from "../generated/kysely-tailordb"; import { TemplateNotFoundError, TemplateVarValidationFailedError } from "../lib/errors.generated"; import { interpolate, validatePayloadVars } from "../lib/templateRendering"; export interface RenderNotificationTemplateInput { eventType: string; channelId: string; locale: string; payloadVars?: Record; /** Optional default-locale override; falls back to `en-US`. */ defaultLocale?: string; } const DEFAULT_LOCALE = "en-US"; /** * Renders a template's subject/body (and optional HTML body) for an (eventType, channelId, locale) with default-locale fallback and variable-schema validation. */ export async function run(db: ReadonlyDB, input: RenderNotificationTemplateInput) { const { eventType, channelId, locale, payloadVars = {} } = input; const defaultLocale = input.defaultLocale ?? DEFAULT_LOCALE; let template = await db .selectFrom("NotificationTemplate") .selectAll() .where("eventType", "=", eventType) .where("channelId", "=", channelId) .where("locale", "=", locale) .executeTakeFirst(); if (!template && locale !== defaultLocale) { template = await db .selectFrom("NotificationTemplate") .selectAll() .where("eventType", "=", eventType) .where("channelId", "=", channelId) .where("locale", "=", defaultLocale) .executeTakeFirst(); } if (!template) { return err(new TemplateNotFoundError(eventType)); } const schema = parseVariableSchema(template.variableSchema); if (!schema) { return err(new TemplateVarValidationFailedError("invalid variableSchema")); } const validationError = validatePayloadVars(schema, payloadVars); if (validationError) { return err(new TemplateVarValidationFailedError(validationError)); } const subject = interpolate(template.subject, payloadVars, false); const body = interpolate(template.body, payloadVars, false); const htmlBody = template.htmlBody !== null && template.htmlBody !== undefined ? interpolate(template.htmlBody, payloadVars, true) : undefined; return ok({ subject, body, htmlBody }); }