Merge branch 'email-cleanup'
Some checks failed
CI / ci (push) Has been cancelled

This commit is contained in:
igm
2026-01-12 17:23:04 +08:00
16 changed files with 60 additions and 61 deletions

View File

@@ -1,9 +1,9 @@
import { ServerClient } from "postmark";
import type {
ClientSendParams,
ClientSendResult,
EmailClient,
} from "./types.js";
import { ServerClient } from "postmark";
export function createPostmarkClient(apiKey: string): EmailClient {
if (!apiKey) {

View File

@@ -1,39 +1,36 @@
export type {
LoginConfirmationEmailParams,
SendLoginConfirmationEmailParams,
} from "./login-confirmation.js";
export type {
OrgInviteEmailParams,
SendOrgInviteEmailParams,
} from "./org-invite.js";
export type {
PasswordResetEmailParams,
SendPasswordResetEmailParams,
} from "./password-reset.js";
export type {
SendVerificationEmailParams,
VerificationEmailParams,
} from "./verification.js";
export {
buildLoginConfirmationEmailHtml,
buildLoginConfirmationEmailText,
sendLoginConfirmationEmail,
} from "./login-confirmation.js";
export type {
LoginConfirmationEmailParams,
SendLoginConfirmationEmailParams,
} from "./login-confirmation.js";
export {
buildOrgInviteEmailHtml,
buildOrgInviteEmailText,
sendOrgInviteEmail,
} from "./org-invite.js";
export type {
OrgInviteEmailParams,
SendOrgInviteEmailParams,
} from "./org-invite.js";
export {
buildPasswordResetEmailHtml,
buildPasswordResetEmailText,
sendPasswordResetEmail,
} from "./password-reset.js";
export type {
PasswordResetEmailParams,
SendPasswordResetEmailParams,
} from "./password-reset.js";
export {
buildVerificationEmailHtml,
buildVerificationEmailText,
sendVerificationEmail,
} from "./verification.js";
export type {
SendVerificationEmailParams,
VerificationEmailParams,
} from "./verification.js";

View File

@@ -1,5 +1,5 @@
import { describe, expect, it, mock, beforeEach } from "bun:test";
import type { EmailClient } from "../types.js";
import { beforeEach, describe, expect, it, mock } from "bun:test";
import {
buildLoginConfirmationEmailHtml,
buildLoginConfirmationEmailText,

View File

@@ -1,3 +1,4 @@
import type { EmailClient, EmailResult } from "../types.js";
import { buildUrl, formatExpiryMinutes } from "../helpers.js";
import { sendEmail } from "../send.js";
import {
@@ -8,7 +9,6 @@ import {
headingStyles,
paragraphStyles,
} from "../styles.js";
import type { EmailClient, EmailResult } from "../types.js";
export interface LoginConfirmationEmailParams {
confirmUrl: string;

View File

@@ -1,5 +1,5 @@
import { describe, expect, it, mock, beforeEach } from "bun:test";
import type { EmailClient } from "../types.js";
import { beforeEach, describe, expect, it, mock } from "bun:test";
import {
buildOrgInviteEmailHtml,
buildOrgInviteEmailText,
@@ -93,9 +93,7 @@ describe("buildOrgInviteEmailText", () => {
it("should include the invite URL", () => {
const text = buildOrgInviteEmailText(params);
expect(text).toContain(
"https://example.com/invite/accept?token=invite123",
);
expect(text).toContain("https://example.com/invite/accept?token=invite123");
});
it("should include the organization name", () => {

View File

@@ -1,4 +1,5 @@
import type { OrgRole } from "@reviq/db-schema";
import type { EmailClient, EmailResult } from "../types.js";
import {
buildUrl,
escapeHtml,
@@ -15,7 +16,6 @@ import {
headingStyles,
paragraphStyles,
} from "../styles.js";
import type { EmailClient, EmailResult } from "../types.js";
export interface OrgInviteEmailParams {
email: string;

View File

@@ -1,5 +1,5 @@
import { describe, expect, it, mock, beforeEach } from "bun:test";
import type { EmailClient } from "../types.js";
import { beforeEach, describe, expect, it, mock } from "bun:test";
import {
buildPasswordResetEmailHtml,
buildPasswordResetEmailText,

View File

@@ -1,3 +1,4 @@
import type { EmailClient, EmailResult } from "../types.js";
import { buildUrl, formatExpiryHours } from "../helpers.js";
import { sendEmail } from "../send.js";
import {
@@ -8,7 +9,6 @@ import {
headingStyles,
paragraphStyles,
} from "../styles.js";
import type { EmailClient, EmailResult } from "../types.js";
export interface PasswordResetEmailParams {
resetUrl: string;

View File

@@ -1,5 +1,5 @@
import { describe, expect, it, mock, beforeEach } from "bun:test";
import type { EmailClient } from "../types.js";
import { beforeEach, describe, expect, it, mock } from "bun:test";
import {
buildVerificationEmailHtml,
buildVerificationEmailText,
@@ -14,7 +14,9 @@ describe("buildVerificationEmailHtml", () => {
it("should include the verify URL", () => {
const html = buildVerificationEmailHtml(params);
expect(html).toContain('href="https://example.com/auth/verify?token=abc123"');
expect(html).toContain(
'href="https://example.com/auth/verify?token=abc123"',
);
});
it("should include the expiry time", () => {

View File

@@ -1,3 +1,4 @@
import type { EmailClient, EmailResult } from "../types.js";
import { buildUrl, formatExpiryHours } from "../helpers.js";
import { sendEmail } from "../send.js";
import {
@@ -8,7 +9,6 @@ import {
headingStyles,
paragraphStyles,
} from "../styles.js";
import type { EmailClient, EmailResult } from "../types.js";
export interface VerificationEmailParams {
verifyUrl: string;

View File

@@ -1,5 +1,5 @@
import { DurationFormat } from "@formatjs/intl-durationformat";
import type { OrgRole } from "@reviq/db-schema";
import { DurationFormat } from "@formatjs/intl-durationformat";
export function buildUrl(
baseUrl: string,

View File

@@ -1,21 +1,4 @@
// Client factories
export { createPostmarkClient } from "./client.js";
export { createLoggingEmailClient } from "./logging-client.js";
// Core types
export type { EmailClient, EmailResult } from "./types.js";
// Base send function
export { sendEmail } from "./send.js";
export type { SendEmailParams } from "./send.js";
// Email-specific send functions
export {
sendLoginConfirmationEmail,
sendOrgInviteEmail,
sendPasswordResetEmail,
sendVerificationEmail,
} from "./emails/index.js";
// Email param types
export type {
@@ -24,3 +7,17 @@ export type {
SendPasswordResetEmailParams,
SendVerificationEmailParams,
} from "./emails/index.js";
export type { SendEmailParams } from "./send.js";
// Core types
export type { EmailClient, EmailResult } from "./types.js";
export { createPostmarkClient } from "./client.js";
// Email-specific send functions
export {
sendLoginConfirmationEmail,
sendOrgInviteEmail,
sendPasswordResetEmail,
sendVerificationEmail,
} from "./emails/index.js";
export { createLoggingEmailClient } from "./logging-client.js";
// Base send function
export { sendEmail } from "./send.js";

View File

@@ -1,4 +1,4 @@
import { describe, expect, it, mock, beforeEach, afterEach } from "bun:test";
import { afterEach, beforeEach, describe, expect, it, mock } from "bun:test";
import { createLoggingEmailClient } from "./logging-client.js";
describe("createLoggingEmailClient", () => {
@@ -30,9 +30,15 @@ describe("createLoggingEmailClient", () => {
expect(result.messageId).toMatch(/^dev-mode-\d+$/);
expect(logOutput).toContain("=== DEV MODE EMAIL ===");
expect(logOutput.some((line) => line.includes("From: noreply@example.com"))).toBe(true);
expect(logOutput.some((line) => line.includes("To: user@example.com"))).toBe(true);
expect(logOutput.some((line) => line.includes("Subject: Test Subject"))).toBe(true);
expect(
logOutput.some((line) => line.includes("From: noreply@example.com")),
).toBe(true);
expect(
logOutput.some((line) => line.includes("To: user@example.com")),
).toBe(true);
expect(
logOutput.some((line) => line.includes("Subject: Test Subject")),
).toBe(true);
expect(logOutput.some((line) => line.includes("Hello"))).toBe(true);
expect(logOutput).toContain("======================");
});

View File

@@ -17,7 +17,9 @@ export function createLoggingEmailClient(): EmailClient {
console.log("======================");
messageIdCounter++;
return Promise.resolve({ messageId: `dev-mode-${messageIdCounter.toString()}` });
return Promise.resolve({
messageId: `dev-mode-${messageIdCounter.toString()}`,
});
},
};
}

View File

@@ -1,5 +1,5 @@
import { describe, expect, it, mock } from "bun:test";
import type { EmailClient } from "./types.js";
import { describe, expect, it, mock } from "bun:test";
import { sendEmail } from "./send.js";
describe("sendEmail", () => {