@volchoklv/newsletter-kit 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +505 -0
- package/dist/adapters/email/index.d.ts +119 -0
- package/dist/adapters/email/index.js +417 -0
- package/dist/adapters/email/index.js.map +1 -0
- package/dist/adapters/storage/index.d.ts +215 -0
- package/dist/adapters/storage/index.js +415 -0
- package/dist/adapters/storage/index.js.map +1 -0
- package/dist/components/index.d.ts +198 -0
- package/dist/components/index.js +505 -0
- package/dist/components/index.js.map +1 -0
- package/dist/index.d.ts +73 -0
- package/dist/index.js +1762 -0
- package/dist/index.js.map +1 -0
- package/dist/server/index.d.ts +77 -0
- package/dist/server/index.js +530 -0
- package/dist/server/index.js.map +1 -0
- package/dist/types-BmajlhNp.d.ts +226 -0
- package/package.json +95 -0
package/README.md
ADDED
|
@@ -0,0 +1,505 @@
|
|
|
1
|
+
# @volchoklv/newsletter-kit
|
|
2
|
+
|
|
3
|
+
Drop-in newsletter subscription components and API handlers for Next.js with adapter support for email providers and storage backends.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 🎨 **shadcn/ui compatible** - Styled components that match your design system
|
|
8
|
+
- 🔌 **Adapter pattern** - Swap email providers and storage backends without changing code
|
|
9
|
+
- 🛡️ **Built-in protection** - Honeypot bot detection, rate limiting, email validation
|
|
10
|
+
- 📊 **Source tracking** - Track where subscribers come from
|
|
11
|
+
- ✅ **Double opt-in** - Optional email confirmation flow
|
|
12
|
+
- 🎯 **TypeScript first** - Full type safety
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install @volchoklv/newsletter-kit
|
|
18
|
+
# or
|
|
19
|
+
pnpm add @volchoklv/newsletter-kit
|
|
20
|
+
# or
|
|
21
|
+
yarn add @volchoklv/newsletter-kit
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Install peer dependencies based on your choices:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
# For Resend
|
|
28
|
+
npm install resend
|
|
29
|
+
|
|
30
|
+
# For Nodemailer/SMTP
|
|
31
|
+
npm install nodemailer
|
|
32
|
+
|
|
33
|
+
# For Prisma storage
|
|
34
|
+
npm install @prisma/client
|
|
35
|
+
|
|
36
|
+
# For Supabase storage
|
|
37
|
+
npm install @supabase/supabase-js
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Quick Start
|
|
41
|
+
|
|
42
|
+
### 1. Create the newsletter handler
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
// lib/newsletter.ts
|
|
46
|
+
import { createNewsletterHandlers } from '@volchoklv/newsletter-kit/server';
|
|
47
|
+
import { createResendAdapter } from '@volchoklv/newsletter-kit/adapters/email';
|
|
48
|
+
import { createPrismaAdapter } from '@volchoklv/newsletter-kit/adapters/storage';
|
|
49
|
+
import { prisma } from '@/lib/prisma';
|
|
50
|
+
|
|
51
|
+
export const newsletter = createNewsletterHandlers({
|
|
52
|
+
emailAdapter: createResendAdapter({
|
|
53
|
+
apiKey: process.env.RESEND_API_KEY!,
|
|
54
|
+
from: 'newsletter@yourdomain.com',
|
|
55
|
+
adminEmail: 'you@yourdomain.com', // Get notified of new subscribers
|
|
56
|
+
}),
|
|
57
|
+
storageAdapter: createPrismaAdapter({ prisma }),
|
|
58
|
+
baseUrl: process.env.NEXT_PUBLIC_URL!,
|
|
59
|
+
doubleOptIn: true,
|
|
60
|
+
honeypotField: 'website',
|
|
61
|
+
rateLimit: {
|
|
62
|
+
max: 5,
|
|
63
|
+
windowSeconds: 60,
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### 2. Create API routes
|
|
69
|
+
|
|
70
|
+
```ts
|
|
71
|
+
// app/api/newsletter/subscribe/route.ts
|
|
72
|
+
import { newsletter } from '@/lib/newsletter';
|
|
73
|
+
|
|
74
|
+
export const POST = newsletter.subscribe;
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
```ts
|
|
78
|
+
// app/api/newsletter/confirm/route.ts
|
|
79
|
+
import { newsletter } from '@/lib/newsletter';
|
|
80
|
+
|
|
81
|
+
export const GET = newsletter.confirm;
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
```ts
|
|
85
|
+
// app/api/newsletter/unsubscribe/route.ts
|
|
86
|
+
import { newsletter } from '@/lib/newsletter';
|
|
87
|
+
|
|
88
|
+
export const POST = newsletter.unsubscribe;
|
|
89
|
+
export const GET = newsletter.unsubscribe;
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### 3. Add the form component
|
|
93
|
+
|
|
94
|
+
```tsx
|
|
95
|
+
// components/footer.tsx
|
|
96
|
+
import { NewsletterForm } from '@volchoklv/newsletter-kit/components';
|
|
97
|
+
|
|
98
|
+
export function Footer() {
|
|
99
|
+
return (
|
|
100
|
+
<footer>
|
|
101
|
+
<NewsletterForm
|
|
102
|
+
endpoint="/api/newsletter/subscribe"
|
|
103
|
+
source="footer"
|
|
104
|
+
placeholder="Enter your email"
|
|
105
|
+
buttonText="Subscribe"
|
|
106
|
+
/>
|
|
107
|
+
</footer>
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### 4. Add Prisma schema
|
|
113
|
+
|
|
114
|
+
**PostgreSQL / Neon / MySQL:**
|
|
115
|
+
|
|
116
|
+
```prisma
|
|
117
|
+
model NewsletterSubscriber {
|
|
118
|
+
id String @id @default(cuid())
|
|
119
|
+
email String @unique
|
|
120
|
+
status String @default("pending")
|
|
121
|
+
token String? @unique
|
|
122
|
+
source String?
|
|
123
|
+
tags String[] @default([])
|
|
124
|
+
metadata Json?
|
|
125
|
+
consentIp String?
|
|
126
|
+
consentAt DateTime?
|
|
127
|
+
confirmedAt DateTime?
|
|
128
|
+
unsubscribedAt DateTime?
|
|
129
|
+
createdAt DateTime @default(now())
|
|
130
|
+
updatedAt DateTime @updatedAt
|
|
131
|
+
|
|
132
|
+
@@index([status])
|
|
133
|
+
@@index([source])
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
**MongoDB:**
|
|
138
|
+
|
|
139
|
+
```prisma
|
|
140
|
+
model NewsletterSubscriber {
|
|
141
|
+
id String @id @default(auto()) @map("_id") @db.ObjectId
|
|
142
|
+
email String @unique
|
|
143
|
+
status String @default("pending")
|
|
144
|
+
token String? @unique
|
|
145
|
+
source String?
|
|
146
|
+
tags String[] @default([])
|
|
147
|
+
metadata Json?
|
|
148
|
+
consentIp String?
|
|
149
|
+
consentAt DateTime?
|
|
150
|
+
confirmedAt DateTime?
|
|
151
|
+
unsubscribedAt DateTime?
|
|
152
|
+
createdAt DateTime @default(now())
|
|
153
|
+
updatedAt DateTime @updatedAt
|
|
154
|
+
|
|
155
|
+
@@index([status])
|
|
156
|
+
@@index([source])
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
> **Note:** The adapter code works identically for both — Prisma handles the database differences.
|
|
161
|
+
|
|
162
|
+
## Components
|
|
163
|
+
|
|
164
|
+
### NewsletterForm
|
|
165
|
+
|
|
166
|
+
Basic form component with full customization:
|
|
167
|
+
|
|
168
|
+
```tsx
|
|
169
|
+
<NewsletterForm
|
|
170
|
+
endpoint="/api/newsletter/subscribe"
|
|
171
|
+
source="homepage"
|
|
172
|
+
tags={['marketing', 'product-updates']}
|
|
173
|
+
placeholder="Enter your email"
|
|
174
|
+
buttonText="Subscribe"
|
|
175
|
+
loadingText="Subscribing..."
|
|
176
|
+
successMessage="Check your inbox to confirm!"
|
|
177
|
+
onSuccess={(email, message) => console.log('Subscribed:', email)}
|
|
178
|
+
onError={(error) => console.error('Error:', error)}
|
|
179
|
+
className="max-w-md"
|
|
180
|
+
inputClassName="border-2"
|
|
181
|
+
buttonClassName="bg-blue-600 hover:bg-blue-700"
|
|
182
|
+
/>
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### NewsletterBlock
|
|
186
|
+
|
|
187
|
+
Full-width section for landing pages:
|
|
188
|
+
|
|
189
|
+
```tsx
|
|
190
|
+
<NewsletterBlock
|
|
191
|
+
endpoint="/api/newsletter/subscribe"
|
|
192
|
+
title="Stay in the loop"
|
|
193
|
+
description="Get weekly updates on the latest features and news."
|
|
194
|
+
source="landing-page"
|
|
195
|
+
/>
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### NewsletterCard
|
|
199
|
+
|
|
200
|
+
Card variant for sidebars:
|
|
201
|
+
|
|
202
|
+
```tsx
|
|
203
|
+
<NewsletterCard
|
|
204
|
+
endpoint="/api/newsletter/subscribe"
|
|
205
|
+
title="Newsletter"
|
|
206
|
+
description="Don't miss out on updates."
|
|
207
|
+
source="sidebar"
|
|
208
|
+
/>
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### NewsletterFooter
|
|
212
|
+
|
|
213
|
+
Optimized for site footers:
|
|
214
|
+
|
|
215
|
+
```tsx
|
|
216
|
+
<NewsletterFooter
|
|
217
|
+
endpoint="/api/newsletter/subscribe"
|
|
218
|
+
title="Newsletter"
|
|
219
|
+
description="Stay up to date."
|
|
220
|
+
source="footer"
|
|
221
|
+
privacyText="We respect your privacy."
|
|
222
|
+
privacyLink="/privacy"
|
|
223
|
+
/>
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
### NewsletterModalContent
|
|
227
|
+
|
|
228
|
+
For use in dialogs/modals:
|
|
229
|
+
|
|
230
|
+
```tsx
|
|
231
|
+
import { Dialog, DialogContent, DialogTrigger } from '@/components/ui/dialog';
|
|
232
|
+
import { NewsletterModalContent } from '@volchoklv/newsletter-kit/components';
|
|
233
|
+
|
|
234
|
+
export function NewsletterModal() {
|
|
235
|
+
const [open, setOpen] = useState(false);
|
|
236
|
+
|
|
237
|
+
return (
|
|
238
|
+
<Dialog open={open} onOpenChange={setOpen}>
|
|
239
|
+
<DialogTrigger asChild>
|
|
240
|
+
<Button>Subscribe</Button>
|
|
241
|
+
</DialogTrigger>
|
|
242
|
+
<DialogContent>
|
|
243
|
+
<NewsletterModalContent
|
|
244
|
+
endpoint="/api/newsletter/subscribe"
|
|
245
|
+
source="popup"
|
|
246
|
+
onSuccess={() => setTimeout(() => setOpen(false), 2000)}
|
|
247
|
+
/>
|
|
248
|
+
</DialogContent>
|
|
249
|
+
</Dialog>
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
## Hook
|
|
255
|
+
|
|
256
|
+
For custom implementations:
|
|
257
|
+
|
|
258
|
+
```tsx
|
|
259
|
+
import { useNewsletter } from '@volchoklv/newsletter-kit/components';
|
|
260
|
+
|
|
261
|
+
export function CustomForm() {
|
|
262
|
+
const { subscribe, isLoading, isSuccess, isError, message } = useNewsletter({
|
|
263
|
+
endpoint: '/api/newsletter/subscribe',
|
|
264
|
+
source: 'custom',
|
|
265
|
+
onSuccess: (email) => {
|
|
266
|
+
// Track in analytics
|
|
267
|
+
},
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
return (
|
|
271
|
+
<form onSubmit={(e) => {
|
|
272
|
+
e.preventDefault();
|
|
273
|
+
subscribe(e.currentTarget.email.value);
|
|
274
|
+
}}>
|
|
275
|
+
{/* Your custom UI */}
|
|
276
|
+
</form>
|
|
277
|
+
);
|
|
278
|
+
}
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
## Email Adapters
|
|
282
|
+
|
|
283
|
+
### Resend
|
|
284
|
+
|
|
285
|
+
```ts
|
|
286
|
+
import { createResendAdapter } from '@volchoklv/newsletter-kit/adapters/email';
|
|
287
|
+
|
|
288
|
+
const emailAdapter = createResendAdapter({
|
|
289
|
+
apiKey: process.env.RESEND_API_KEY!,
|
|
290
|
+
from: 'newsletter@yourdomain.com',
|
|
291
|
+
replyTo: 'hello@yourdomain.com',
|
|
292
|
+
adminEmail: 'you@yourdomain.com',
|
|
293
|
+
templates: {
|
|
294
|
+
confirmation: {
|
|
295
|
+
subject: 'Please confirm your subscription',
|
|
296
|
+
html: ({ confirmUrl, email }) => `
|
|
297
|
+
<h1>Confirm your subscription</h1>
|
|
298
|
+
<a href="${confirmUrl}">Click here to confirm</a>
|
|
299
|
+
`,
|
|
300
|
+
},
|
|
301
|
+
welcome: {
|
|
302
|
+
subject: 'Welcome aboard! 🎉',
|
|
303
|
+
html: ({ email }) => `
|
|
304
|
+
<h1>You're subscribed!</h1>
|
|
305
|
+
<p>Thanks for joining us.</p>
|
|
306
|
+
`,
|
|
307
|
+
},
|
|
308
|
+
},
|
|
309
|
+
});
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
### Nodemailer (SMTP)
|
|
313
|
+
|
|
314
|
+
```ts
|
|
315
|
+
import { createNodemailerAdapter } from '@volchoklv/newsletter-kit/adapters/email';
|
|
316
|
+
|
|
317
|
+
const emailAdapter = createNodemailerAdapter({
|
|
318
|
+
smtp: {
|
|
319
|
+
host: 'smtp.example.com',
|
|
320
|
+
port: 587,
|
|
321
|
+
secure: false,
|
|
322
|
+
auth: {
|
|
323
|
+
user: process.env.SMTP_USER!,
|
|
324
|
+
pass: process.env.SMTP_PASS!,
|
|
325
|
+
},
|
|
326
|
+
},
|
|
327
|
+
from: 'newsletter@yourdomain.com',
|
|
328
|
+
});
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
### Mailchimp
|
|
332
|
+
|
|
333
|
+
```ts
|
|
334
|
+
import { createMailchimpAdapter } from '@volchoklv/newsletter-kit/adapters/email';
|
|
335
|
+
|
|
336
|
+
const emailAdapter = createMailchimpAdapter({
|
|
337
|
+
apiKey: process.env.MAILCHIMP_API_KEY!,
|
|
338
|
+
server: 'us1',
|
|
339
|
+
listId: 'your-list-id',
|
|
340
|
+
from: 'newsletter@yourdomain.com',
|
|
341
|
+
useAsStorage: true, // Use Mailchimp as storage too
|
|
342
|
+
});
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
## Storage Adapters
|
|
346
|
+
|
|
347
|
+
### Prisma
|
|
348
|
+
|
|
349
|
+
```ts
|
|
350
|
+
import { createPrismaAdapter } from '@volchoklv/newsletter-kit/adapters/storage';
|
|
351
|
+
import { prisma } from '@/lib/prisma';
|
|
352
|
+
|
|
353
|
+
const storageAdapter = createPrismaAdapter({ prisma });
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
### Supabase
|
|
357
|
+
|
|
358
|
+
```ts
|
|
359
|
+
import { createSupabaseAdapter } from '@volchoklv/newsletter-kit/adapters/storage';
|
|
360
|
+
import { createClient } from '@supabase/supabase-js';
|
|
361
|
+
|
|
362
|
+
const supabase = createClient(
|
|
363
|
+
process.env.SUPABASE_URL!,
|
|
364
|
+
process.env.SUPABASE_SERVICE_KEY!
|
|
365
|
+
);
|
|
366
|
+
|
|
367
|
+
const storageAdapter = createSupabaseAdapter({ supabase });
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
### In-Memory (Development/Testing)
|
|
371
|
+
|
|
372
|
+
```ts
|
|
373
|
+
import { createMemoryAdapter } from '@volchoklv/newsletter-kit/adapters/storage';
|
|
374
|
+
|
|
375
|
+
const storageAdapter = createMemoryAdapter();
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
### No Storage (Email Only)
|
|
379
|
+
|
|
380
|
+
When using Mailchimp or similar that handles storage:
|
|
381
|
+
|
|
382
|
+
```ts
|
|
383
|
+
import { createNoopAdapter } from '@volchoklv/newsletter-kit/adapters/storage';
|
|
384
|
+
|
|
385
|
+
const storageAdapter = createNoopAdapter();
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
## Configuration
|
|
389
|
+
|
|
390
|
+
Full configuration options:
|
|
391
|
+
|
|
392
|
+
```ts
|
|
393
|
+
const newsletter = createNewsletterHandlers({
|
|
394
|
+
// Required
|
|
395
|
+
emailAdapter: createResendAdapter({ ... }),
|
|
396
|
+
baseUrl: 'https://yourdomain.com',
|
|
397
|
+
|
|
398
|
+
// Optional storage (defaults to in-memory)
|
|
399
|
+
storageAdapter: createPrismaAdapter({ prisma }),
|
|
400
|
+
|
|
401
|
+
// Double opt-in (default: true)
|
|
402
|
+
doubleOptIn: true,
|
|
403
|
+
|
|
404
|
+
// API paths (defaults shown)
|
|
405
|
+
confirmPath: '/api/newsletter/confirm',
|
|
406
|
+
unsubscribePath: '/api/newsletter/unsubscribe',
|
|
407
|
+
|
|
408
|
+
// Bot protection
|
|
409
|
+
honeypotField: 'website',
|
|
410
|
+
|
|
411
|
+
// Rate limiting
|
|
412
|
+
rateLimit: {
|
|
413
|
+
max: 5,
|
|
414
|
+
windowSeconds: 60,
|
|
415
|
+
},
|
|
416
|
+
|
|
417
|
+
// Custom email validation
|
|
418
|
+
validateEmail: async (email) => {
|
|
419
|
+
// Block disposable emails, etc.
|
|
420
|
+
return !email.includes('tempmail.com');
|
|
421
|
+
},
|
|
422
|
+
|
|
423
|
+
// Allowed sources for tracking
|
|
424
|
+
allowedSources: ['footer', 'sidebar', 'popup', 'landing-page'],
|
|
425
|
+
|
|
426
|
+
// Default tags for all subscribers
|
|
427
|
+
defaultTags: ['newsletter'],
|
|
428
|
+
|
|
429
|
+
// Callbacks
|
|
430
|
+
onSubscribe: async (subscriber) => {
|
|
431
|
+
// Track in analytics
|
|
432
|
+
},
|
|
433
|
+
onConfirm: async (subscriber) => {
|
|
434
|
+
// Send to CRM
|
|
435
|
+
},
|
|
436
|
+
onUnsubscribe: async (email) => {
|
|
437
|
+
// Update CRM
|
|
438
|
+
},
|
|
439
|
+
onError: async (error, context) => {
|
|
440
|
+
// Log to error tracking
|
|
441
|
+
},
|
|
442
|
+
});
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
## API Reference
|
|
446
|
+
|
|
447
|
+
### Server Methods
|
|
448
|
+
|
|
449
|
+
```ts
|
|
450
|
+
// Route handlers
|
|
451
|
+
newsletter.subscribe // POST handler
|
|
452
|
+
newsletter.confirm // GET handler
|
|
453
|
+
newsletter.unsubscribe // POST/GET handler
|
|
454
|
+
|
|
455
|
+
// Direct access
|
|
456
|
+
newsletter.handlers.subscribe(req)
|
|
457
|
+
newsletter.handlers.confirm(token)
|
|
458
|
+
newsletter.handlers.unsubscribe(email)
|
|
459
|
+
|
|
460
|
+
// Storage access
|
|
461
|
+
newsletter.storage.listSubscribers({ status: 'confirmed' })
|
|
462
|
+
newsletter.getSubscriber('email@example.com')
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
## Tailwind CSS
|
|
466
|
+
|
|
467
|
+
The components use Tailwind CSS with shadcn/ui-compatible class names. Make sure your `tailwind.config.js` includes:
|
|
468
|
+
|
|
469
|
+
```js
|
|
470
|
+
module.exports = {
|
|
471
|
+
content: [
|
|
472
|
+
// ...
|
|
473
|
+
'./node_modules/@volchoklv/newsletter-kit/**/*.{js,ts,jsx,tsx}',
|
|
474
|
+
],
|
|
475
|
+
theme: {
|
|
476
|
+
extend: {
|
|
477
|
+
colors: {
|
|
478
|
+
border: 'hsl(var(--border))',
|
|
479
|
+
input: 'hsl(var(--input))',
|
|
480
|
+
ring: 'hsl(var(--ring))',
|
|
481
|
+
background: 'hsl(var(--background))',
|
|
482
|
+
foreground: 'hsl(var(--foreground))',
|
|
483
|
+
primary: {
|
|
484
|
+
DEFAULT: 'hsl(var(--primary))',
|
|
485
|
+
foreground: 'hsl(var(--primary-foreground))',
|
|
486
|
+
},
|
|
487
|
+
muted: {
|
|
488
|
+
DEFAULT: 'hsl(var(--muted))',
|
|
489
|
+
foreground: 'hsl(var(--muted-foreground))',
|
|
490
|
+
},
|
|
491
|
+
card: {
|
|
492
|
+
DEFAULT: 'hsl(var(--card))',
|
|
493
|
+
foreground: 'hsl(var(--card-foreground))',
|
|
494
|
+
},
|
|
495
|
+
},
|
|
496
|
+
},
|
|
497
|
+
},
|
|
498
|
+
};
|
|
499
|
+
```
|
|
500
|
+
|
|
501
|
+
Or use the `className` props to apply your own styles.
|
|
502
|
+
|
|
503
|
+
## License
|
|
504
|
+
|
|
505
|
+
MIT
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { a as EmailAdapterConfig, E as EmailAdapter } from '../../types-BmajlhNp.js';
|
|
2
|
+
export { b as EmailTemplates } from '../../types-BmajlhNp.js';
|
|
3
|
+
|
|
4
|
+
interface ResendAdapterConfig extends EmailAdapterConfig {
|
|
5
|
+
apiKey: string;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Email adapter for Resend (https://resend.com)
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```ts
|
|
12
|
+
* import { createResendAdapter } from '@volchok/newsletter-kit/adapters/email';
|
|
13
|
+
*
|
|
14
|
+
* const emailAdapter = createResendAdapter({
|
|
15
|
+
* apiKey: process.env.RESEND_API_KEY!,
|
|
16
|
+
* from: 'newsletter@yourdomain.com',
|
|
17
|
+
* adminEmail: 'you@yourdomain.com', // optional
|
|
18
|
+
* });
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
declare function createResendAdapter(config: ResendAdapterConfig): EmailAdapter;
|
|
22
|
+
|
|
23
|
+
interface SMTPConfig {
|
|
24
|
+
host: string;
|
|
25
|
+
port: number;
|
|
26
|
+
secure?: boolean;
|
|
27
|
+
auth?: {
|
|
28
|
+
user: string;
|
|
29
|
+
pass: string;
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
interface NodemailerAdapterConfig extends EmailAdapterConfig {
|
|
33
|
+
smtp: SMTPConfig;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Email adapter for Nodemailer/SMTP
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ```ts
|
|
40
|
+
* import { createNodemailerAdapter } from '@volchok/newsletter-kit/adapters/email';
|
|
41
|
+
*
|
|
42
|
+
* const emailAdapter = createNodemailerAdapter({
|
|
43
|
+
* smtp: {
|
|
44
|
+
* host: 'smtp.example.com',
|
|
45
|
+
* port: 587,
|
|
46
|
+
* secure: false,
|
|
47
|
+
* auth: {
|
|
48
|
+
* user: process.env.SMTP_USER!,
|
|
49
|
+
* pass: process.env.SMTP_PASS!,
|
|
50
|
+
* },
|
|
51
|
+
* },
|
|
52
|
+
* from: 'newsletter@yourdomain.com',
|
|
53
|
+
* adminEmail: 'you@yourdomain.com', // optional
|
|
54
|
+
* });
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
57
|
+
declare function createNodemailerAdapter(config: NodemailerAdapterConfig): EmailAdapter;
|
|
58
|
+
|
|
59
|
+
interface MailchimpAdapterConfig extends EmailAdapterConfig {
|
|
60
|
+
apiKey: string;
|
|
61
|
+
server: string;
|
|
62
|
+
listId: string;
|
|
63
|
+
/** If true, use Mailchimp for storage too (adds to list on subscribe) */
|
|
64
|
+
useAsStorage?: boolean;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Email adapter for Mailchimp
|
|
68
|
+
*
|
|
69
|
+
* Note: Mailchimp works differently from other adapters. It manages its own
|
|
70
|
+
* subscriber list, so this adapter can optionally act as both email AND storage.
|
|
71
|
+
*
|
|
72
|
+
* @example
|
|
73
|
+
* ```ts
|
|
74
|
+
* import { createMailchimpAdapter } from '@volchok/newsletter-kit/adapters/email';
|
|
75
|
+
*
|
|
76
|
+
* const emailAdapter = createMailchimpAdapter({
|
|
77
|
+
* apiKey: process.env.MAILCHIMP_API_KEY!,
|
|
78
|
+
* server: 'us1',
|
|
79
|
+
* listId: 'your-list-id',
|
|
80
|
+
* from: 'newsletter@yourdomain.com',
|
|
81
|
+
* useAsStorage: true, // Use Mailchimp's list as storage
|
|
82
|
+
* });
|
|
83
|
+
* ```
|
|
84
|
+
*/
|
|
85
|
+
declare function createMailchimpAdapter(config: MailchimpAdapterConfig): EmailAdapter;
|
|
86
|
+
/**
|
|
87
|
+
* Create a combined Mailchimp adapter that handles both email and storage
|
|
88
|
+
*
|
|
89
|
+
* This is useful when you want Mailchimp to be your single source of truth
|
|
90
|
+
* for subscribers.
|
|
91
|
+
*/
|
|
92
|
+
declare function createMailchimpStorageAdapter(config: MailchimpAdapterConfig): {
|
|
93
|
+
createSubscriber(input: {
|
|
94
|
+
email: string;
|
|
95
|
+
source?: string;
|
|
96
|
+
tags?: string[];
|
|
97
|
+
}): Promise<{
|
|
98
|
+
id: string;
|
|
99
|
+
email: string;
|
|
100
|
+
status: "pending";
|
|
101
|
+
source: string | undefined;
|
|
102
|
+
tags: string[] | undefined;
|
|
103
|
+
createdAt: Date;
|
|
104
|
+
updatedAt: Date;
|
|
105
|
+
}>;
|
|
106
|
+
getSubscriberByEmail(email: string): Promise<{
|
|
107
|
+
id: string;
|
|
108
|
+
email: string;
|
|
109
|
+
status: string;
|
|
110
|
+
tags: string[] | undefined;
|
|
111
|
+
createdAt: Date;
|
|
112
|
+
updatedAt: Date;
|
|
113
|
+
} | null>;
|
|
114
|
+
getSubscriberByToken(): Promise<null>;
|
|
115
|
+
confirmSubscriber(_token: string): Promise<null>;
|
|
116
|
+
unsubscribe(email: string): Promise<boolean>;
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
export { EmailAdapter, EmailAdapterConfig, createMailchimpAdapter, createMailchimpStorageAdapter, createNodemailerAdapter, createResendAdapter };
|