Quickstart
This sends one email through an in-memory node:sqlite store and a fake
transport. Swap the transport for a real provider and the store for your Durable
Object storage when you deploy.
import { DatabaseSync, type SQLOutputValue } from "node:sqlite";import { createCarrier } from "@fungi.computer/carrier";import type { WatchdogSqliteOwner, WatchdogSqliteValue,} from "@fungi.computer/watchdog";
const database = new DatabaseSync(":memory:");
const toSqlite = (value: WatchdogSqliteValue) => value instanceof ArrayBuffer ? new Uint8Array(value) : value;const fromSqlite = (value: SQLOutputValue): WatchdogSqliteValue => { if (typeof value === "bigint") return Number(value); if (value instanceof Uint8Array) return value.slice().buffer; return value;};
const storage: WatchdogSqliteOwner = { sql: { exec: (statement, ...bindings) => { const prepared = database.prepare(statement); const values = bindings.map(toSqlite); if (!/^\s*(?:SELECT|PRAGMA|WITH)\b/iu.test(statement)) { prepared.run(...values); return { toArray: () => [] }; } const rows = prepared .all(...values) .map((row) => Object.fromEntries( Object.entries(row).map(([key, value]) => [key, fromSqlite(value)]), ), ); return { toArray: () => rows }; }, }, transactionSync: (operation) => { database.exec("BEGIN"); try { const result = operation(); database.exec("COMMIT"); return result; } catch (error) { database.exec("ROLLBACK"); throw error; } },};
const carrier = await createCarrier({ storage, now: Date.now, host: { // Commit the mutation together with a durable wake. A demo needs no wake. commit: async (mutation) => mutation(), requestWake: async () => {}, }, transport: async (email) => { console.log(`provider got "${email.subject}"`); return { kind: "provider_accepted", providerMessageId: "demo-1" }; },});
const receipt = await carrier.send({ id: "welcome:user-123:v1", from: { email: "hello@example.com", name: "Example" }, to: ["ada@example.net"], subject: "Welcome", text: "Your account is ready.",});console.log(receipt.status); // "queued"
await carrier.tick(); // your wake calls thisconsole.log((await carrier.get(receipt.id))?.status); // "provider_accepted"
database.close();send() only accepts the email. The provider call happens on tick(), which a
real host runs from its durable wake. The Cloudflare adapter does this for you
from the Durable Object alarm.