@nixxie-cms/email 1.0.1
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/LICENSE +23 -0
- package/dist/declarations/src/EmailService.d.ts +29 -0
- package/dist/declarations/src/EmailService.d.ts.map +1 -0
- package/dist/declarations/src/index.d.ts +7 -0
- package/dist/declarations/src/index.d.ts.map +1 -0
- package/dist/declarations/src/types.d.ts +193 -0
- package/dist/declarations/src/types.d.ts.map +1 -0
- package/dist/nixxie-cms-email.cjs.d.ts +2 -0
- package/dist/nixxie-cms-email.cjs.js +994 -0
- package/dist/nixxie-cms-email.esm.js +983 -0
- package/package.json +41 -0
- package/src/EmailService.ts +263 -0
- package/src/index.ts +35 -0
- package/src/queue/EmailQueue.ts +134 -0
- package/src/queue/index.ts +1 -0
- package/src/templates/HandlebarsEngine.ts +152 -0
- package/src/templates/PugEngine.ts +101 -0
- package/src/templates/TemplateManager.ts +180 -0
- package/src/templates/index.ts +4 -0
- package/src/transports/ConsoleTransport.ts +50 -0
- package/src/transports/MailgunTransport.ts +58 -0
- package/src/transports/ResendTransport.ts +83 -0
- package/src/transports/SendGridTransport.ts +54 -0
- package/src/transports/SesTransport.ts +58 -0
- package/src/transports/SmtpTransport.ts +66 -0
- package/src/transports/index.ts +30 -0
- package/src/types.ts +279 -0
package/src/types.ts
ADDED
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
NixxieEmailAttachment,
|
|
3
|
+
NixxieEmailQueueOptions,
|
|
4
|
+
NixxieEmailRecipient,
|
|
5
|
+
NixxieEmailSendOptions,
|
|
6
|
+
NixxieEmailSentInfo,
|
|
7
|
+
NixxieEmailQueueStats,
|
|
8
|
+
} from '@nixxie-cms/core/types'
|
|
9
|
+
|
|
10
|
+
// ── Re-export the shared types so consumers only need @nixxie-cms/email ──
|
|
11
|
+
export type {
|
|
12
|
+
NixxieEmailAttachment as EmailAttachment,
|
|
13
|
+
NixxieEmailQueueOptions as QueueEmailOptions,
|
|
14
|
+
NixxieEmailRecipient as EmailRecipient,
|
|
15
|
+
NixxieEmailSendOptions as SendEmailOptions,
|
|
16
|
+
NixxieEmailSentInfo as SentEmailInfo,
|
|
17
|
+
NixxieEmailQueueStats as EmailQueueStats,
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// ─────────────────────────────────────────────────────────────
|
|
21
|
+
// Transport configs
|
|
22
|
+
// ─────────────────────────────────────────────────────────────
|
|
23
|
+
|
|
24
|
+
export type SmtpTransportConfig = {
|
|
25
|
+
type: 'smtp'
|
|
26
|
+
host: string
|
|
27
|
+
port?: number
|
|
28
|
+
secure?: boolean
|
|
29
|
+
auth?: {
|
|
30
|
+
user: string
|
|
31
|
+
pass: string
|
|
32
|
+
}
|
|
33
|
+
tls?: {
|
|
34
|
+
rejectUnauthorized?: boolean
|
|
35
|
+
ciphers?: string
|
|
36
|
+
}
|
|
37
|
+
connectionTimeout?: number
|
|
38
|
+
greetingTimeout?: number
|
|
39
|
+
socketTimeout?: number
|
|
40
|
+
pool?: boolean
|
|
41
|
+
maxConnections?: number
|
|
42
|
+
maxMessages?: number
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export type SendGridTransportConfig = {
|
|
46
|
+
type: 'sendgrid'
|
|
47
|
+
apiKey: string
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export type MailgunTransportConfig = {
|
|
51
|
+
type: 'mailgun'
|
|
52
|
+
apiKey: string
|
|
53
|
+
domain: string
|
|
54
|
+
/** 'us' (default) or 'eu' */
|
|
55
|
+
region?: 'us' | 'eu'
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export type SesTransportConfig = {
|
|
59
|
+
type: 'ses'
|
|
60
|
+
region: string
|
|
61
|
+
credentials?: {
|
|
62
|
+
accessKeyId: string
|
|
63
|
+
secretAccessKey: string
|
|
64
|
+
sessionToken?: string
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export type ResendTransportConfig = {
|
|
69
|
+
type: 'resend'
|
|
70
|
+
apiKey: string
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** Development transport — logs emails to the console instead of sending */
|
|
74
|
+
export type ConsoleTransportConfig = {
|
|
75
|
+
type: 'console'
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export type EmailTransportConfig =
|
|
79
|
+
| SmtpTransportConfig
|
|
80
|
+
| SendGridTransportConfig
|
|
81
|
+
| MailgunTransportConfig
|
|
82
|
+
| SesTransportConfig
|
|
83
|
+
| ResendTransportConfig
|
|
84
|
+
| ConsoleTransportConfig
|
|
85
|
+
|
|
86
|
+
// ─────────────────────────────────────────────────────────────
|
|
87
|
+
// Template config
|
|
88
|
+
// ─────────────────────────────────────────────────────────────
|
|
89
|
+
|
|
90
|
+
export type EmailTemplateEngine = 'handlebars' | 'pug' | 'auto'
|
|
91
|
+
|
|
92
|
+
export type EmailTemplatesConfig = {
|
|
93
|
+
/** Absolute path to the templates directory */
|
|
94
|
+
dir: string
|
|
95
|
+
/**
|
|
96
|
+
* Default template engine. Use `'auto'` to detect the engine per-template from the file
|
|
97
|
+
* extension (.hbs/.handlebars vs .pug/.jade).
|
|
98
|
+
* @default 'handlebars'
|
|
99
|
+
*/
|
|
100
|
+
engine?: EmailTemplateEngine
|
|
101
|
+
/**
|
|
102
|
+
* Enable layout support.
|
|
103
|
+
* Place a `layouts/base.hbs` (or `.pug`) file and use `{{{body}}}` inside it.
|
|
104
|
+
* @default true
|
|
105
|
+
*/
|
|
106
|
+
layouts?: boolean
|
|
107
|
+
/**
|
|
108
|
+
* Auto-load Handlebars partials from `partials/` subdirectory.
|
|
109
|
+
* @default true
|
|
110
|
+
*/
|
|
111
|
+
partials?: boolean
|
|
112
|
+
/**
|
|
113
|
+
* Options passed directly to the underlying template engine.
|
|
114
|
+
* For Handlebars: `{ noEscape?: boolean, helpers?: Record<string, Function> }`
|
|
115
|
+
* For Pug: `{ pretty?: boolean, filters?: object }`
|
|
116
|
+
*/
|
|
117
|
+
engineOptions?: Record<string, unknown>
|
|
118
|
+
/**
|
|
119
|
+
* Reload templates from disk on every render (useful in development).
|
|
120
|
+
* @default false
|
|
121
|
+
*/
|
|
122
|
+
cache?: boolean
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// ─────────────────────────────────────────────────────────────
|
|
126
|
+
// Queue config
|
|
127
|
+
// ─────────────────────────────────────────────────────────────
|
|
128
|
+
|
|
129
|
+
export type EmailQueueConfig = {
|
|
130
|
+
/** Enabled unless explicitly set to false. @default true */
|
|
131
|
+
enabled?: boolean
|
|
132
|
+
/** Number of emails processed in parallel @default 5 */
|
|
133
|
+
concurrency?: number
|
|
134
|
+
/** How many times to retry a failed send @default 3 */
|
|
135
|
+
retries?: number
|
|
136
|
+
/**
|
|
137
|
+
* Base delay in ms between retries — doubled on each subsequent attempt.
|
|
138
|
+
* @default 5000
|
|
139
|
+
*/
|
|
140
|
+
retryDelay?: number
|
|
141
|
+
/** Discard jobs older than this (ms). 0 = never discard. @default 86400000 (24h) */
|
|
142
|
+
maxAge?: number
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// ─────────────────────────────────────────────────────────────
|
|
146
|
+
// Tracking config
|
|
147
|
+
// ─────────────────────────────────────────────────────────────
|
|
148
|
+
|
|
149
|
+
export type EmailTrackingConfig = {
|
|
150
|
+
/** Track email opens via a 1×1 pixel. @default false */
|
|
151
|
+
opens?: boolean
|
|
152
|
+
/** Rewrite links to track clicks. @default false */
|
|
153
|
+
clicks?: boolean
|
|
154
|
+
/**
|
|
155
|
+
* Base URL prepended to all tracking endpoints.
|
|
156
|
+
* Required when opens or clicks tracking is enabled.
|
|
157
|
+
*/
|
|
158
|
+
baseUrl: string
|
|
159
|
+
/** Path prefix for tracking routes. @default '/email-tracking' */
|
|
160
|
+
path?: string
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// ─────────────────────────────────────────────────────────────
|
|
164
|
+
// Development config
|
|
165
|
+
// ─────────────────────────────────────────────────────────────
|
|
166
|
+
|
|
167
|
+
export type EmailDevelopmentConfig = {
|
|
168
|
+
/**
|
|
169
|
+
* When true, emails are not sent — they are logged and optionally previewed.
|
|
170
|
+
* @default process.env.NODE_ENV !== 'production'
|
|
171
|
+
*/
|
|
172
|
+
enabled?: boolean
|
|
173
|
+
/**
|
|
174
|
+
* Write rendered HTML to this directory as .html files for browser preview.
|
|
175
|
+
* Filenames are `{template}-{timestamp}.html`.
|
|
176
|
+
*/
|
|
177
|
+
previewPath?: string
|
|
178
|
+
/**
|
|
179
|
+
* Override the "to" address for all emails in development mode.
|
|
180
|
+
* Useful for catching all outgoing mail in one inbox.
|
|
181
|
+
*/
|
|
182
|
+
toOverride?: string | string[]
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// ─────────────────────────────────────────────────────────────
|
|
186
|
+
// Callbacks
|
|
187
|
+
// ─────────────────────────────────────────────────────────────
|
|
188
|
+
|
|
189
|
+
export type EmailSentCallback = (info: NixxieEmailSentInfo, options: NixxieEmailSendOptions) => void | Promise<void>
|
|
190
|
+
export type EmailFailedCallback = (error: Error, options: NixxieEmailSendOptions) => void | Promise<void>
|
|
191
|
+
|
|
192
|
+
// ─────────────────────────────────────────────────────────────
|
|
193
|
+
// Top-level EmailConfig (accepted by createEmail())
|
|
194
|
+
// ─────────────────────────────────────────────────────────────
|
|
195
|
+
|
|
196
|
+
export type EmailConfig = {
|
|
197
|
+
/** Transport configuration — defines how emails are physically sent */
|
|
198
|
+
transport: EmailTransportConfig
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Default "from" address used when `SendEmailOptions.from` is not set.
|
|
202
|
+
* Can be a plain address string or an object with name + address.
|
|
203
|
+
*/
|
|
204
|
+
from: NixxieEmailRecipient
|
|
205
|
+
|
|
206
|
+
/** Default reply-to address */
|
|
207
|
+
replyTo?: NixxieEmailRecipient
|
|
208
|
+
|
|
209
|
+
/** Template engine configuration */
|
|
210
|
+
templates?: EmailTemplatesConfig
|
|
211
|
+
|
|
212
|
+
/** Outbound queue configuration */
|
|
213
|
+
queue?: EmailQueueConfig
|
|
214
|
+
|
|
215
|
+
/** Email open/click tracking */
|
|
216
|
+
tracking?: EmailTrackingConfig
|
|
217
|
+
|
|
218
|
+
/** Development-mode settings (preview, override recipients, etc.) */
|
|
219
|
+
development?: EmailDevelopmentConfig
|
|
220
|
+
|
|
221
|
+
/** Addresses that should never receive emails */
|
|
222
|
+
suppressionList?: string[]
|
|
223
|
+
|
|
224
|
+
/** Default headers appended to every message */
|
|
225
|
+
headers?: Record<string, string>
|
|
226
|
+
|
|
227
|
+
/** Called after every successfully sent email */
|
|
228
|
+
onSent?: EmailSentCallback
|
|
229
|
+
|
|
230
|
+
/** Called when an email fails to send */
|
|
231
|
+
onFailed?: EmailFailedCallback
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// ─────────────────────────────────────────────────────────────
|
|
235
|
+
// Internal transport abstraction
|
|
236
|
+
// ─────────────────────────────────────────────────────────────
|
|
237
|
+
|
|
238
|
+
export type TransportMessage = {
|
|
239
|
+
from: string
|
|
240
|
+
to: string[]
|
|
241
|
+
cc?: string[]
|
|
242
|
+
bcc?: string[]
|
|
243
|
+
replyTo?: string[]
|
|
244
|
+
subject: string
|
|
245
|
+
html?: string
|
|
246
|
+
text?: string
|
|
247
|
+
attachments?: NixxieEmailAttachment[]
|
|
248
|
+
headers?: Record<string, string>
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
export type TransportResult = {
|
|
252
|
+
messageId: string
|
|
253
|
+
accepted: string[]
|
|
254
|
+
rejected: string[]
|
|
255
|
+
response?: string
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
export interface EmailTransporter {
|
|
259
|
+
send(message: TransportMessage): Promise<TransportResult>
|
|
260
|
+
verify(): Promise<boolean>
|
|
261
|
+
close(): Promise<void>
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// ─────────────────────────────────────────────────────────────
|
|
265
|
+
// Queue internals
|
|
266
|
+
// ─────────────────────────────────────────────────────────────
|
|
267
|
+
|
|
268
|
+
export type QueueJobStatus = 'pending' | 'processing' | 'completed' | 'failed'
|
|
269
|
+
|
|
270
|
+
export type QueueJob = {
|
|
271
|
+
id: string
|
|
272
|
+
options: NixxieEmailQueueOptions
|
|
273
|
+
status: QueueJobStatus
|
|
274
|
+
attempts: number
|
|
275
|
+
createdAt: Date
|
|
276
|
+
sendAt?: Date
|
|
277
|
+
priority: 'high' | 'normal' | 'low'
|
|
278
|
+
lastError?: string
|
|
279
|
+
}
|