diff --git a/README.md b/README.md
index abe6e84..4b917c8 100644
--- a/README.md
+++ b/README.md
@@ -2,16 +2,17 @@
📧 **Template-based email system with scheduling and job processing for PayloadCMS**
-⚠️ **Pre-release Warning**: This package is currently in active development (v0.0.x). Breaking changes may occur before v1.0.0. Not recommended for production use.
+✨ **Simplified API**: Starting from v0.1.0, this plugin uses a simplified API that leverages PayloadCMS collections directly for better type safety and flexibility.
## Features
-✅ **Template System**: Create reusable email templates with Handlebars syntax
-✅ **Outbox Scheduling**: Schedule emails for future delivery
-✅ **Job Integration**: Automatic processing via PayloadCMS jobs queue
-✅ **Retry Failed Sends**: Automatic retry mechanism for failed emails
-✅ **Template Variables**: Dynamic content with validation
-✅ **Developer API**: Simple methods for sending emails programmatically
+✅ **Template System**: Create reusable email templates with LiquidJS, Mustache, or simple variables
+✅ **Type Safety**: Full TypeScript support using your generated Payload types
+✅ **Flexible Template Engines**: LiquidJS, Mustache, or bring your own template renderer
+✅ **Email Scheduling**: Schedule emails for future delivery using Payload collections
+✅ **Job Integration**: Automatic processing via PayloadCMS jobs queue
+✅ **Retry Failed Sends**: Automatic retry mechanism for failed emails
+✅ **Payload Native**: Uses Payload collections directly - no custom APIs to learn
## Installation
@@ -49,53 +50,124 @@ export default buildConfig({
})
```
-### 2. Send emails in your code
+### 2. Send emails using Payload collections
```typescript
-import { sendEmail, scheduleEmail } from '@xtr-dev/payload-mailing'
+import { renderTemplate } from '@xtr-dev/payload-mailing'
-// Send immediately using template slug
-const emailId = await sendEmail(payload, {
- templateSlug: 'welcome-email',
- to: 'user@example.com',
- variables: {
- firstName: 'John',
- welcomeUrl: 'https://yoursite.com/welcome'
+// Option 1: Using templates with variables
+const { html, text, subject } = await renderTemplate(payload, 'welcome-email', {
+ firstName: 'John',
+ welcomeUrl: 'https://yoursite.com/welcome'
+})
+
+// Create email using Payload's collection API (full type safety!)
+const email = await payload.create({
+ collection: 'emails',
+ data: {
+ to: ['user@example.com'],
+ subject,
+ html,
+ text,
+ // Schedule for later (optional)
+ scheduledAt: new Date(Date.now() + 24 * 60 * 60 * 1000),
+ // Add any custom fields you've defined
+ priority: 1,
+ customField: 'your-value', // Your custom collection fields work!
}
})
-// Schedule for later
-const scheduledId = await scheduleEmail(payload, {
- templateSlug: 'reminder-email',
- to: 'user@example.com',
- scheduledAt: new Date(Date.now() + 24 * 60 * 60 * 1000), // 24 hours
- variables: {
- eventName: 'Product Launch',
- eventDate: new Date('2024-01-15')
+// Option 2: Direct HTML email (no template needed)
+const directEmail = await payload.create({
+ collection: 'emails',
+ data: {
+ to: ['user@example.com'],
+ subject: 'Welcome!',
+ html: '
Welcome John!
Thanks for joining!
',
+ text: 'Welcome John! Thanks for joining!',
}
})
```
+## Why This Approach is Better
+
+- ✅ **Full Type Safety**: Use your generated Payload types
+- ✅ **No Learning Curve**: Just use `payload.create()` like any collection
+- ✅ **Maximum Flexibility**: Add any custom fields to your email collection
+- ✅ **Payload Integration**: Leverage validation, hooks, access control
+- ✅ **Consistent API**: One way to create data in Payload
+
## Configuration
### Plugin Options
```typescript
-interface MailingPluginConfig {
- collections?: {
- templates?: string // default: 'email-templates'
- emails?: string // default: 'emails'
+mailingPlugin({
+ // Template engine (optional)
+ templateEngine: 'liquidjs', // 'liquidjs' | 'mustache' | 'simple'
+
+ // Custom template renderer (optional)
+ templateRenderer: async (template: string, variables: Record) => {
+ return yourCustomEngine.render(template, variables)
+ },
+
+ // Email transport
+ transport: {
+ host: 'smtp.gmail.com',
+ port: 587,
+ auth: { user: '...', pass: '...' }
+ },
+
+ // Collection names (optional)
+ collections: {
+ templates: 'email-templates', // default
+ emails: 'emails' // default
+ },
+
+ // Sending options
+ defaultFrom: 'noreply@yoursite.com',
+ defaultFromName: 'Your Site',
+ retryAttempts: 3, // default
+ retryDelay: 300000, // 5 minutes (default)
+
+ // Advanced options
+ emailWrapper: (email) => ({ // optional layout wrapper
+ ...email,
+ html: `${email.html}`
+ }),
+ richTextEditor: lexicalEditor(), // optional custom editor
+ onReady: async (payload) => { // optional initialization hook
+ console.log('Mailing plugin ready!')
}
- defaultFrom?: string
- transport?: Transporter | MailingTransportConfig
- queue?: string // default: 'default'
- retryAttempts?: number // default: 3
- retryDelay?: number // default: 300000 (5 minutes)
- emailWrapper?: EmailWrapperHook // optional email layout wrapper
- richTextEditor?: RichTextField['editor'] // optional custom rich text editor
- onReady?: (payload: any) => Promise // optional callback after plugin initialization
- initOrder?: 'before' | 'after' // default: 'before'
-}
+})
+```
+
+### Template Engine Options
+
+Choose your preferred template engine:
+
+```typescript
+// LiquidJS (default) - Modern syntax with logic
+mailingPlugin({
+ templateEngine: 'liquidjs' // {% if user.isPremium %}Premium!{% endif %}
+})
+
+// Mustache - Logic-less templates
+mailingPlugin({
+ templateEngine: 'mustache' // {{#user.isPremium}}Premium!{{/user.isPremium}}
+})
+
+// Simple variable replacement
+mailingPlugin({
+ templateEngine: 'simple' // Just {{variable}} replacement
+})
+
+// Custom template renderer
+mailingPlugin({
+ templateRenderer: async (template, variables) => {
+ return handlebars.compile(template)(variables) // Bring your own!
+ }
+})
```
### Transport Configuration
@@ -473,71 +545,50 @@ mailingPlugin({
})
```
-## Handlebars Helpers
+## Template Syntax Reference
-The plugin includes several built-in helpers:
+Depending on your chosen template engine, you can use different syntax:
-- `{{formatDate date 'short'}}` - Format dates (short, long, or default)
-- `{{formatCurrency amount 'USD'}}` - Format currency
-- `{{capitalize string}}` - Capitalize first letter
-- `{{#ifEquals value1 value2}}...{{/ifEquals}}` - Conditional equality
+### LiquidJS (Default)
+- Variables: `{{ user.name }}`
+- Logic: `{% if user.isPremium %}Premium content{% endif %}`
+- Loops: `{% for item in items %}{{ item.name }}{% endfor %}`
+- Filters: `{{ amount | formatCurrency }}`, `{{ date | formatDate: "short" }}`
-## API Methods
+### Mustache
+- Variables: `{{ user.name }}`
+- Logic: `{{#user.isPremium}}Premium content{{/user.isPremium}}`
+- Loops: `{{#items}}{{ name }}{{/items}}`
+- No built-in filters (use variables with pre-formatted data)
-### sendEmail(payload, options)
+### Simple
+- Variables only: `{{ user.name }}`, `{{ amount }}`, `{{ date }}`
-Send an email immediately:
+### Built-in Filters (LiquidJS only)
+- `formatDate` - Format dates: `{{ createdAt | formatDate: "short" }}`
+- `formatCurrency` - Format currency: `{{ amount | formatCurrency: "USD" }}`
+- `capitalize` - Capitalize first letter: `{{ name | capitalize }}`
+
+## Available Helper Functions
```typescript
-const emailId = await sendEmail(payload, {
- templateSlug: 'order-confirmation', // optional - use template slug
- to: ['customer@example.com'], // string or array of emails
- cc: ['manager@example.com'], // optional - array of emails
- bcc: ['archive@example.com'], // optional - array of emails
- from: 'orders@yoursite.com', // optional, uses default
- replyTo: 'support@yoursite.com', // optional
- subject: 'Custom subject', // required if no template
- html: 'Custom HTML
', // required if no template
- text: 'Custom text version', // optional
- variables: { // template variables
- orderNumber: '12345',
- customerName: 'John Doe'
- },
- priority: 1 // optional, 1-10 (1 = highest)
+import {
+ renderTemplate, // Render email templates with variables
+ processEmails, // Process pending emails manually
+ retryFailedEmails, // Retry failed emails
+ getMailing // Get mailing service instance
+} from '@xtr-dev/payload-mailing'
+
+// Render a template
+const { html, text, subject } = await renderTemplate(payload, 'welcome', {
+ name: 'John',
+ activationUrl: 'https://example.com/activate'
})
-```
-### scheduleEmail(payload, options)
-
-Schedule an email for later delivery:
-
-```typescript
-const emailId = await scheduleEmail(payload, {
- templateSlug: 'newsletter',
- to: ['user1@example.com', 'user2@example.com'],
- scheduledAt: new Date('2024-01-15T10:00:00Z'),
- variables: {
- month: 'January',
- highlights: ['Feature A', 'Feature B']
- }
-})
-```
-
-### processEmails(payload)
-
-Manually process pending emails:
-
-```typescript
-import { processEmails } from '@xtr-dev/payload-mailing'
+// Process emails manually
await processEmails(payload)
-```
-### retryFailedEmails(payload)
-
-Manually retry failed emails:
-
-```typescript
-import { retryFailedEmails } from '@xtr-dev/payload-mailing'
+// Retry failed emails
await retryFailedEmails(payload)
```
@@ -716,35 +767,85 @@ import {
} from '@xtr-dev/payload-mailing'
```
+## Migration Guide (v0.0.x → v0.1.0)
+
+**🚨 BREAKING CHANGES**: The API has been simplified to use Payload collections directly.
+
+### Before (v0.0.x)
+```typescript
+import { sendEmail, scheduleEmail } from '@xtr-dev/payload-mailing'
+
+// Old way
+const emailId = await sendEmail(payload, {
+ templateSlug: 'welcome',
+ to: 'user@example.com',
+ variables: { name: 'John' }
+})
+
+const scheduledId = await scheduleEmail(payload, {
+ templateSlug: 'reminder',
+ to: 'user@example.com',
+ scheduledAt: new Date('2024-01-15T10:00:00Z'),
+ variables: { eventName: 'Launch' }
+})
+```
+
+### After (v0.1.0+)
+```typescript
+import { renderTemplate } from '@xtr-dev/payload-mailing'
+
+// New way - render template first
+const { html, text, subject } = await renderTemplate(payload, 'welcome', {
+ name: 'John'
+})
+
+// Then create email using Payload collections (full type safety!)
+const email = await payload.create({
+ collection: 'emails',
+ data: {
+ to: ['user@example.com'],
+ subject,
+ html,
+ text,
+ // For scheduling
+ scheduledAt: new Date('2024-01-15T10:00:00Z'),
+ // Add any custom fields from your collection
+ customField: 'value',
+ }
+})
+```
+
+### Benefits of Migration
+- ✅ **Full TypeScript support** with your generated Payload types
+- ✅ **Use any custom fields** you add to your email collection
+- ✅ **Leverage Payload's features**: validation, hooks, access control
+- ✅ **One consistent API** - just use `payload.create()`
+- ✅ **No wrapper methods** - direct access to Payload's power
+
## Recent Changes
-### v0.0.x (Latest)
+### v0.1.0 (Latest - Breaking Changes)
-**🔄 Breaking Changes:**
-- Removed email layouts system in favor of `emailWrapper` hook for better flexibility
-- Email fields (`to`, `cc`, `bcc`) now use `hasMany: true` for proper array handling
-- Templates now use slug-based lookup instead of ID-based for developer-friendly API
-- Email collection renamed from "outbox" to "emails"
-- Unified job processing: single `process-email-queue` job handles both pending and failed emails
+**🚀 Major API Simplification:**
+- **REMOVED**: `sendEmail()` and `scheduleEmail()` wrapper methods
+- **REMOVED**: `SendEmailOptions` custom types
+- **ADDED**: Direct Payload collection usage with full type safety
+- **ADDED**: `renderTemplate()` helper for template rendering
+- **ADDED**: Support for LiquidJS, Mustache, and custom template engines
+- **IMPROVED**: Webpack compatibility with proper dynamic imports
-**✨ New Features:**
-- Rich text editor with automatic HTML/text conversion
-- Template slugs for easier template reference
-- `emailWrapper` hook for consistent email layouts
-- Custom rich text editor configuration support
-- Initialization hooks (`onReady`, `initOrder`) for better plugin lifecycle control
-- Improved Handlebars variable interpolation with defensive programming
+**Template Engine Enhancements:**
+- **NEW**: LiquidJS support (default) with modern syntax and logic
+- **NEW**: Mustache support for logic-less templates
+- **NEW**: Custom template renderer hook for maximum flexibility
+- **NEW**: Simple variable replacement as fallback
+- **FIXED**: All webpack compatibility issues resolved
-**🐛 Bug Fixes:**
-- Fixed text version uppercase conversion in headings
-- Fixed Handlebars interpolation issues in text version
-- Improved plugin initialization order to prevent timing issues
-
-**💡 Improvements:**
-- Better admin UI with proper array input controls
-- More robust error handling and logging
-- Enhanced TypeScript definitions
-- Simplified template creation workflow
+**Developer Experience:**
+- **IMPROVED**: Full TypeScript inference using generated Payload types
+- **IMPROVED**: Comprehensive migration guide and documentation
+- **IMPROVED**: Better error handling and async patterns
+- **SIMPLIFIED**: Cleaner codebase with fewer abstractions
## License
diff --git a/package.json b/package.json
index 71c4844..27d97aa 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@xtr-dev/payload-mailing",
- "version": "0.1.0",
+ "version": "0.1.1",
"description": "Template-based email system with scheduling and job processing for PayloadCMS",
"type": "module",
"main": "dist/index.js",