wasiliano
v0.1.7
Published
Minimal, SMTP-first email core library for Bun and Next.js
Maintainers
Readme
Wasiliano
Wasiliano (Swahili for "Communication") is a minimal, SMTP-first email core library for Bun and Next.js. It's designed for transactional emails and newsletters using plain SMTP (Nodemailer-compatible) without SaaS lock-in.
Features
- SMTP-first: Provider-agnostic. Use Namecheap, Gmail, Resend, or your own SMTP server.
- Bun Optimized: Native ESM, fast performance.
- Next.js Ready: Works in App Router API routes and Server Actions.
- Bulk Sending: Built-in concurrency control and rate limiting.
- Template Rendering: Simple string-based HTML templates.
- Unsubscribe Tokens: HMAC-signed token generation and verification.
- Notion Adapter: Use Notion as your CMS for emails and subscribers.
Installation
bun add wasilianoQuickstart
1. Configure Transport
import { createTransport } from "wasiliano";
const transport = createTransport({
host: "smtp.example.com",
port: 465,
secure: true,
auth: {
user: process.env.SMTP_USER,
pass: process.env.SMTP_PASS,
},
});2. Send an Email
import { sendEmail } from "wasiliano";
await sendEmail(transport, {
from: '"Sender Name" <[email protected]>',
to: "[email protected]",
subject: "Welcome to Wasiliano",
html: "<h1>Hello!</h1>",
});Notion CMS Guide
Wasiliano allows you to use Notion as a powerful, sovereign CMS for your emails.
1. Database Schema
Create two databases in Notion:
Emails DB:
Title(Title): Subject line.Body(Rich Text): Newsletter content.Status(Select):Draft,Scheduled,Sent.SendAt(Date): When to send.From(Email): Optional sender override.
Subscribers DB:
Email(Email): Recipient address.Name(Rich Text): Recipient name.Status(Select):Active,Unsubscribed.
2. Automated Sync Logic
You can run a sync script to automatically fetch "Scheduled" emails and send them to "Active" subscribers.
import { createTransport, sendBulk } from "wasiliano";
import { createNotionClient, getScheduledEmails, updateEmailStatus, getActiveSubscribers, notionToHTML } from "wasiliano/notion";
// 1. Setup
const transport = createTransport({/*...*/});
const notion = createNotionClient(process.env.NOTION_API_KEY);
// 2. Fetch
const emails = await getScheduledEmails(notion, process.env.NOTION_EMAIL_DB);
const subs = await getActiveSubscribers(notion, process.env.NOTION_SUB_DB);
// 3. Process
for (const email of emails) {
const html = notionToHTML(email.body);
const recipients = subs.map(s => ({ to: s.email, subject: email.subject, html }));
const result = await sendBulk(transport, recipients);
if (result.errorCount === 0) {
// Automatically update Notion status to "Sent"
await updateEmailStatus(notion, email.id, "Sent");
}
}Next.js Integration Guide
Wasiliano is built for modern Next.js applications using the App Router.
⚙️ Configuration
Add your SMTP and Secret keys to .env.local:
SMTP_HOST=mail.privateemail.com
SMTP_PORT=465
[email protected]
SMTP_PASS=your-password
UNSUB_SECRET=your-random-long-string📧 Shared Client
Create a shared transport utility to reuse across your app.
// lib/wasiliano.ts
import { createTransport } from "wasiliano";
export const transport = createTransport({
host: process.env.SMTP_HOST!,
port: parseInt(process.env.SMTP_PORT!),
secure: true,
auth: {
user: process.env.SMTP_USER!,
pass: process.env.SMTP_PASS!,
},
});🛠️ Usage Patterns
1. Server Actions (Onboarding)
Perfect for sending welcome emails directly from a registration form.
// app/actions/register.ts
"use server";
import { sendEmail } from "wasiliano";
import { transport } from "@/lib/wasiliano";
export async function registerUser(email: string) {
// ... your DB logic
await sendEmail(transport, {
from: "[email protected]",
to: email,
subject: "Welcome Aboard!",
html: "<h1>Welcome!</h1><p>We're glad to have you.</p>",
});
}2. API Routes (Contact Forms)
Great for traditional form submissions or external integrations.
// app/api/send/route.ts
import { NextResponse } from "next/server";
import { sendEmail } from "wasiliano";
import { transport } from "@/lib/wasiliano";
export async function POST(req: Request) {
const { to, subject, body } = await req.json();
try {
await sendEmail(transport, {
from: "[email protected]",
to,
subject,
html: `<p>${body}</p>`,
});
return NextResponse.json({ ok: true });
} catch (err) {
return NextResponse.json({ error: "Failed to send" }, { status: 500 });
}
}3. Secure Unsubscribe Page
Handle unsubscriptions securely using HMAC tokens without exposing your database.
// app/unsubscribe/page.tsx
import { verifyUnsubscribeToken } from "wasiliano";
export default function Unsubscribe({ searchParams }: { searchParams: { email: string; token: string } }) {
const { email, token } = searchParams;
const isValid = verifyUnsubscribeToken(email, token, process.env.UNSUB_SECRET!);
if (!isValid) {
return (
<div className="p-8 text-center">
<h1 className="text-red-500">Invalid Link</h1>
<p>This link is invalid or has expired.</p>
</div>
);
}
// 1. Update your Notion or local DB here to mark as Unsubscribed
// 2. Show confirmation
return (
<div className="p-8 text-center">
<h1>Unsubscribed</h1>
<p>You will no longer receive emails at <strong>{email}</strong>.</p>
</div>
);
}Unsubscribe Flow
Wasiliano provides HMAC-signed tokens to handle secure unsubscriptions without a database for the links.
- Generate:
generateUnsubscribeToken(email, secret) - Verify:
verifyUnsubscribeToken(email, token, secret)
License: MIT
