import { env } from "cloudflare:workers";
import { publicUrl } from "./events/emit";
import type { QueueMessage } from "./events/emit";
import { CATEGORY_INFO } from "~/lib/notification-categories";
import type { NotificationCategory } from "~/lib/notification-categories";
import { captureDevMail, devMailMode } from "./notify/dev-mail";
import { captureServerEvent } from "./analytics";
export type EmailQueueMessage = Extract<QueueMessage, { kind: "email.send" }>;
export type EmailContent = {
subject: string;
text: string;
html: string;
headers?: Record<string, string>;
/** Address replies go to; set on thread notifications so a reply becomes a comment. */
replyTo?: string;
};
type Action = { label: string; url: string };
/** Links appended to every notification email; `unsubscribeUrl` also drives the List-Unsubscribe headers. */
export type EmailFooter = { reason: string; manageUrl: string; unsubscribeUrl: string | null };
export type DigestEntry = { title: string; url: string };
const HTML_ESCAPES: Record<string, string> = { "&": "&", "<": "<", ">": ">", '"': """, "'": "'" };
const MAX_DETAIL_LENGTH = 500;
function escapeHtml(value: string): string {
return value.replace(/[&<>"']/g, (char) => HTML_ESCAPES[char] ?? char);
}
function forgeName(): string {
return env.FORGE_NAME || "gitfor";
}
const MAX_EXCERPT_LENGTH = 2000;
function clipExcerpt(value: string): string {
const trimmed = value.trim();
return trimmed.length > MAX_EXCERPT_LENGTH ? `${trimmed.slice(0, MAX_EXCERPT_LENGTH)}…` : trimmed;
}