111 lines · 4.3 KB
// Thin wrapper around mail.tm's REST API. Verified live against the real
// API while building this:
//   GET  /domains                     -> list of usable domains
//   POST /accounts                    -> create a mailbox
//   POST /token                       -> auth, get a bearer token
//   GET  /messages                    -> list messages (no reliable seen=
//                                         filter despite the API accepting
//                                         it, so the poller just deletes
//                                         each message after processing
//                                         instead of tracking seen state)
//   GET  /sources/{id}                -> { data: "<raw MIME as a string>" },
//                                         which postal-mime accepts directly
//   DELETE /messages/{id}             -> 204
//   DELETE /accounts/{id}             -> 204
const API_BASE = "https://api.mail.tm";

export class MailtmApiError extends Error {
  status: number;

  constructor(status: number, message: string) {
    super(message);
    this.status = status;
  }
}

async function mailtmFetch(path: string, init: RequestInit = {}): Promise<Response> {
  const response = await fetch(`${API_BASE}${path}`, {
    ...init,
    headers: {
      "Content-Type": "application/json",
      ...init.headers,
    },
  });
  if (!response.ok) {
    const body = await response.text().catch(() => "");
    throw new MailtmApiError(response.status, `mail.tm ${path} failed: ${response.status} ${body}`);
  }
  return response;
}