mirror of
https://github.com/ksyasuda/dotfiles.git
synced 2026-03-21 18:11:27 -07:00
update skills
This commit is contained in:
237
.agents/skills/cloudflare-deploy/references/email-workers/api.md
Normal file
237
.agents/skills/cloudflare-deploy/references/email-workers/api.md
Normal file
@@ -0,0 +1,237 @@
|
||||
# Email Workers API Reference
|
||||
|
||||
Complete API reference for Cloudflare Email Workers runtime.
|
||||
|
||||
## ForwardableEmailMessage Interface
|
||||
|
||||
The main interface passed to email handlers.
|
||||
|
||||
```typescript
|
||||
interface ForwardableEmailMessage {
|
||||
readonly from: string; // Envelope MAIL FROM (SMTP sender)
|
||||
readonly to: string; // Envelope RCPT TO (SMTP recipient)
|
||||
readonly headers: Headers; // Web-standard Headers object
|
||||
readonly raw: ReadableStream; // Raw MIME message (single-use stream)
|
||||
readonly rawSize: number; // Total message size in bytes
|
||||
|
||||
setReject(reason: string): void;
|
||||
forward(rcptTo: string, headers?: Headers): Promise<void>;
|
||||
reply(message: EmailMessage): Promise<void>;
|
||||
}
|
||||
```
|
||||
|
||||
### Properties
|
||||
|
||||
| Property | Type | Description |
|
||||
|----------|------|-------------|
|
||||
| `from` | string | Envelope sender (SMTP MAIL FROM) - use for security |
|
||||
| `to` | string | Envelope recipient (SMTP RCPT TO) |
|
||||
| `headers` | Headers | Message headers (Subject, Message-ID, etc.) |
|
||||
| `raw` | ReadableStream | Raw MIME message (**single-use**, buffer first) |
|
||||
| `rawSize` | number | Message size in bytes |
|
||||
|
||||
### Methods
|
||||
|
||||
#### setReject(reason: string): void
|
||||
|
||||
Reject with permanent SMTP 5xx error. Email not delivered, sender may receive bounce.
|
||||
|
||||
```typescript
|
||||
if (blockList.includes(message.from)) {
|
||||
message.setReject('Sender blocked');
|
||||
}
|
||||
```
|
||||
|
||||
#### forward(rcptTo: string, headers?: Headers): Promise<void>
|
||||
|
||||
Forward to verified destination. Only `X-*` custom headers allowed.
|
||||
|
||||
```typescript
|
||||
await message.forward('inbox@example.com');
|
||||
|
||||
// With custom headers
|
||||
const h = new Headers();
|
||||
h.set('X-Processed-By', 'worker');
|
||||
await message.forward('inbox@example.com', h);
|
||||
```
|
||||
|
||||
#### reply(message: EmailMessage): Promise<void>
|
||||
|
||||
Send a reply to the original sender (March 2025 feature).
|
||||
|
||||
```typescript
|
||||
import { EmailMessage } from 'cloudflare:email';
|
||||
import { createMimeMessage } from 'mimetext';
|
||||
|
||||
const msg = createMimeMessage();
|
||||
msg.setSender({ name: 'Support', addr: 'support@example.com' });
|
||||
msg.setRecipient(message.from);
|
||||
msg.setSubject(`Re: ${message.headers.get('Subject')}`);
|
||||
msg.setHeader('In-Reply-To', message.headers.get('Message-ID'));
|
||||
msg.setHeader('References', message.headers.get('References') || '');
|
||||
msg.addMessage({
|
||||
contentType: 'text/plain',
|
||||
data: 'Thank you for your message.'
|
||||
});
|
||||
|
||||
await message.reply(new EmailMessage(
|
||||
'support@example.com',
|
||||
message.from,
|
||||
msg.asRaw()
|
||||
));
|
||||
```
|
||||
|
||||
**Requirements**:
|
||||
- Incoming email needs valid DMARC
|
||||
- Reply once per event, recipient = `message.from`
|
||||
- Sender domain = receiving domain, with DMARC/SPF/DKIM
|
||||
- Max 100 `References` entries
|
||||
- Threading: `In-Reply-To` (original Message-ID), `References`, new `Message-ID`
|
||||
|
||||
## EmailMessage Constructor
|
||||
|
||||
```typescript
|
||||
import { EmailMessage } from 'cloudflare:email';
|
||||
|
||||
new EmailMessage(from: string, to: string, raw: ReadableStream | string)
|
||||
```
|
||||
|
||||
Used for sending emails (replies or via SendEmail binding). Domain must be verified.
|
||||
|
||||
## SendEmail Interface
|
||||
|
||||
```typescript
|
||||
interface SendEmail {
|
||||
send(message: EmailMessage): Promise<void>;
|
||||
}
|
||||
|
||||
// Usage
|
||||
await env.EMAIL.send(new EmailMessage(from, to, mimeContent));
|
||||
```
|
||||
|
||||
## SendEmail Binding Types
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"send_email": [
|
||||
{ "name": "EMAIL" }, // Type 1: Any verified address
|
||||
{ "name": "LOGS", "destination_address": "logs@example.com" }, // Type 2: Single dest
|
||||
{ "name": "TEAM", "allowed_destination_addresses": ["a@ex.com", "b@ex.com"] }, // Type 3: Dest allowlist
|
||||
{ "name": "NOREPLY", "allowed_sender_addresses": ["noreply@ex.com"] } // Type 4: Sender allowlist
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## postal-mime Parsed Output
|
||||
|
||||
postal-mime v2.7.3 parses incoming emails into structured data.
|
||||
|
||||
```typescript
|
||||
interface ParsedEmail {
|
||||
headers: Array<{ key: string; value: string }>;
|
||||
from: { name: string; address: string } | null;
|
||||
to: Array<{ name: string; address: string }> | { name: string; address: string } | null;
|
||||
cc: Array<{ name: string; address: string }> | null;
|
||||
bcc: Array<{ name: string; address: string }> | null;
|
||||
subject: string;
|
||||
messageId: string | null;
|
||||
inReplyTo: string | null;
|
||||
references: string | null;
|
||||
date: string | null;
|
||||
html: string | null;
|
||||
text: string | null;
|
||||
attachments: Array<{
|
||||
filename: string;
|
||||
mimeType: string;
|
||||
disposition: string | null;
|
||||
related: boolean;
|
||||
contentId: string | null;
|
||||
content: Uint8Array;
|
||||
}>;
|
||||
}
|
||||
```
|
||||
|
||||
### Usage
|
||||
|
||||
```typescript
|
||||
import PostalMime from 'postal-mime';
|
||||
|
||||
const buffer = await new Response(message.raw).arrayBuffer();
|
||||
const email = await PostalMime.parse(buffer);
|
||||
|
||||
console.log(email.subject);
|
||||
console.log(email.from?.address);
|
||||
console.log(email.text);
|
||||
console.log(email.attachments.length);
|
||||
```
|
||||
|
||||
## mimetext API Quick Reference
|
||||
|
||||
mimetext v3.0.27 composes outgoing emails.
|
||||
|
||||
```typescript
|
||||
import { createMimeMessage } from 'mimetext';
|
||||
|
||||
const msg = createMimeMessage();
|
||||
|
||||
// Sender
|
||||
msg.setSender({ name: 'John Doe', addr: 'john@example.com' });
|
||||
|
||||
// Recipients
|
||||
msg.setRecipient('alice@example.com');
|
||||
msg.setRecipients(['bob@example.com', 'carol@example.com']);
|
||||
msg.setCc('manager@example.com');
|
||||
msg.setBcc(['audit@example.com']);
|
||||
|
||||
// Headers
|
||||
msg.setSubject('Meeting Notes');
|
||||
msg.setHeader('In-Reply-To', '<previous-message-id>');
|
||||
msg.setHeader('References', '<msg1> <msg2>');
|
||||
msg.setHeader('Message-ID', `<${crypto.randomUUID()}@example.com>`);
|
||||
|
||||
// Content
|
||||
msg.addMessage({
|
||||
contentType: 'text/plain',
|
||||
data: 'Plain text content'
|
||||
});
|
||||
|
||||
msg.addMessage({
|
||||
contentType: 'text/html',
|
||||
data: '<p>HTML content</p>'
|
||||
});
|
||||
|
||||
// Attachments
|
||||
msg.addAttachment({
|
||||
filename: 'report.pdf',
|
||||
contentType: 'application/pdf',
|
||||
data: pdfBuffer // Uint8Array or base64 string
|
||||
});
|
||||
|
||||
// Generate raw MIME
|
||||
const raw = msg.asRaw(); // Returns string
|
||||
```
|
||||
|
||||
## TypeScript Types
|
||||
|
||||
```typescript
|
||||
import {
|
||||
ForwardableEmailMessage,
|
||||
EmailMessage
|
||||
} from 'cloudflare:email';
|
||||
|
||||
interface Env {
|
||||
EMAIL: SendEmail;
|
||||
EMAIL_ARCHIVE: KVNamespace;
|
||||
ALLOWED_SENDERS: KVNamespace;
|
||||
}
|
||||
|
||||
export default {
|
||||
async email(
|
||||
message: ForwardableEmailMessage,
|
||||
env: Env,
|
||||
ctx: ExecutionContext
|
||||
): Promise<void> {
|
||||
// Fully typed
|
||||
}
|
||||
};
|
||||
```
|
||||
Reference in New Issue
Block a user