@xenterprises/fastify-xemail 1.1.1 → 1.3.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/CHANGELOG.md +61 -0
- package/README.md +103 -127
- package/index.d.ts +18 -4
- package/package.json +14 -5
- package/src/providers/postmark.js +223 -0
- package/src/providers/sendgrid.js +389 -0
- package/src/xEmail.js +41 -380
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `@xenterprises/fastify-xemail` are documented here.
|
|
4
|
+
|
|
5
|
+
## Unreleased
|
|
6
|
+
|
|
7
|
+
## [1.3.0] - 2026-09-07
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- **Postmark provider** (`provider: 'postmark'`): `send`, `sendTemplate`,
|
|
12
|
+
`sendWithAttachments`, `sendBulk`, and `sendPersonalizedBulk` are backed by the
|
|
13
|
+
Postmark API (via the `postmark` package). Postmark is the migration target;
|
|
14
|
+
SendGrid is now legacy. The default remains `'sendgrid'`, so existing
|
|
15
|
+
registrations are unchanged.
|
|
16
|
+
- New `provider` registration option (`'sendgrid' | 'postmark'`), validated
|
|
17
|
+
fail-fast at registration.
|
|
18
|
+
- `messageStream` extra option for Postmark message streams.
|
|
19
|
+
- `test/xEmail.postmark.test.js` — Postmark tests mocking
|
|
20
|
+
`postmark.ServerClient.prototype` with `mock.method()`, fully offline.
|
|
21
|
+
|
|
22
|
+
### Changed
|
|
23
|
+
|
|
24
|
+
- The plugin internals are split into provider modules:
|
|
25
|
+
`src/providers/sendgrid.js` (existing implementation, unchanged behavior) and
|
|
26
|
+
`src/providers/postmark.js` (new). `src/xEmail.js` now only validates options
|
|
27
|
+
and selects the provider.
|
|
28
|
+
- Under `provider: 'postmark'`, the SendGrid-only methods (`validate`,
|
|
29
|
+
`addContact`, `searchContact`, `deleteContact`, `createList`, `getLists`,
|
|
30
|
+
`deleteList`) are still present on the decorator but throw
|
|
31
|
+
`[xEmail] '<method>()' is not supported by provider 'postmark'.`
|
|
32
|
+
|
|
33
|
+
## [1.2.0] - 2026-07-27
|
|
34
|
+
|
|
35
|
+
### Breaking changes
|
|
36
|
+
|
|
37
|
+
- **Registration error messages changed format.** Missing/invalid `apiKey` and
|
|
38
|
+
`fromEmail` now throw `xemail: option \`<name>\` must be a string, e.g.
|
|
39
|
+
\`app.register(xEmail, { ... })\`` instead of `[xEmail] '<name>' (string) is required.`
|
|
40
|
+
(Suite-wide error-format standard; code matching on the old message text must be updated.)
|
|
41
|
+
- **`fp()` metadata changed**: plugin name is now `xemail` (was `xEmail`) and the
|
|
42
|
+
Fastify constraint is `5.x` (was `>=5.0.0`). Affects duplicate-registration
|
|
43
|
+
detection and `fastify-plugin` metadata only.
|
|
44
|
+
|
|
45
|
+
### Added
|
|
46
|
+
|
|
47
|
+
- TypeScript declarations are now reachable by consumers: `package.json` gains a
|
|
48
|
+
top-level `"types": "./index.d.ts"` and a `types` condition in `exports["."]`.
|
|
49
|
+
Declaration content is unchanged.
|
|
50
|
+
- New fail-fast validation: `fromName` must be a string when provided and `active`
|
|
51
|
+
must be a boolean when provided.
|
|
52
|
+
- Tests asserting the plugin is env-independent (no `process.env` reads; options
|
|
53
|
+
are the only configuration source).
|
|
54
|
+
|
|
55
|
+
### Changed
|
|
56
|
+
|
|
57
|
+
- Tooling migrated to Biome 2.5.5 (`lint`/`format` scripts); test script now uses
|
|
58
|
+
the `test/**/*.test.js` glob.
|
|
59
|
+
- README rewritten to the suite README contract; `docs/INTEGRATION.md` examples
|
|
60
|
+
corrected to use the `fastify.xEmail` decorator.
|
|
61
|
+
- Dependency updates via `npm audit fix` — 0 known vulnerabilities.
|
package/README.md
CHANGED
|
@@ -1,14 +1,22 @@
|
|
|
1
1
|
# @xenterprises/fastify-xemail
|
|
2
2
|
|
|
3
|
-
Fastify plugin for SendGrid
|
|
3
|
+
Fastify 5 plugin for email via **Postmark** or **SendGrid** (legacy) — send transactional, template, attachment, and bulk emails through one `fastify.xEmail` decorator. SendGrid additionally supports address validation and Marketing contacts/lists. For Fastify apps that need email with the least possible wiring.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
> **Migration note:** SendGrid support is legacy. New integrations should use
|
|
6
|
+
> `provider: 'postmark'`; existing SendGrid registrations keep working unchanged.
|
|
7
|
+
|
|
8
|
+
## Install
|
|
6
9
|
|
|
7
10
|
```bash
|
|
8
|
-
npm install @xenterprises/fastify-xemail
|
|
11
|
+
npm install @xenterprises/fastify-xemail fastify@5
|
|
9
12
|
```
|
|
10
13
|
|
|
11
|
-
|
|
14
|
+
`fastify@^5.0.0` is a peer dependency.
|
|
15
|
+
|
|
16
|
+
TypeScript declarations ship with the package — importing the plugin types the
|
|
17
|
+
`fastify.xEmail` decorator automatically (`index.d.ts`).
|
|
18
|
+
|
|
19
|
+
## Minimal example
|
|
12
20
|
|
|
13
21
|
```javascript
|
|
14
22
|
import Fastify from 'fastify';
|
|
@@ -17,12 +25,11 @@ import xEmail from '@xenterprises/fastify-xemail';
|
|
|
17
25
|
const fastify = Fastify();
|
|
18
26
|
|
|
19
27
|
await fastify.register(xEmail, {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
28
|
+
provider: 'postmark', // or 'sendgrid' (legacy default)
|
|
29
|
+
apiKey: 'your-postmark-server-token',
|
|
30
|
+
fromEmail: 'noreply@example.com',
|
|
23
31
|
});
|
|
24
32
|
|
|
25
|
-
// Send a simple email
|
|
26
33
|
await fastify.xEmail.send(
|
|
27
34
|
'user@example.com',
|
|
28
35
|
'Welcome!',
|
|
@@ -30,168 +37,137 @@ await fastify.xEmail.send(
|
|
|
30
37
|
);
|
|
31
38
|
```
|
|
32
39
|
|
|
40
|
+
The plugin reads **no environment variables**. All configuration arrives via the
|
|
41
|
+
register options object; reading `process.env.POSTMARK_SERVER_TOKEN` and passing
|
|
42
|
+
it in is the consumer's job.
|
|
43
|
+
|
|
44
|
+
## Providers
|
|
45
|
+
|
|
46
|
+
| Provider | Status | Sending methods | `validate`, contacts, lists |
|
|
47
|
+
|----------|--------|-----------------|------------------------------|
|
|
48
|
+
| `postmark` | Migration target | `send`, `sendTemplate`, `sendWithAttachments`, `sendBulk`, `sendPersonalizedBulk` | Throw "not supported by provider 'postmark'" |
|
|
49
|
+
| `sendgrid` | Legacy (default) | All sending methods | Fully supported |
|
|
50
|
+
|
|
51
|
+
Postmark's API has no equivalents for email validation or contact/list
|
|
52
|
+
management, so those methods exist in the decorator under Postmark but throw
|
|
53
|
+
`[xEmail] '<method>()' is not supported by provider 'postmark'.`
|
|
54
|
+
|
|
33
55
|
## Options
|
|
34
56
|
|
|
35
57
|
| Name | Type | Default | Required | Description |
|
|
36
58
|
|------|------|---------|----------|-------------|
|
|
37
|
-
| `
|
|
59
|
+
| `provider` | `'sendgrid' \| 'postmark'` | `'sendgrid'` | No | Email provider. `sendgrid` is legacy; `postmark` is the migration target |
|
|
60
|
+
| `apiKey` | `string` | — | Yes | SendGrid API key (needs Mail Send; Marketing APIs for contact/list methods) or Postmark server API token |
|
|
38
61
|
| `fromEmail` | `string` | — | Yes | Verified sender email address |
|
|
39
62
|
| `fromName` | `string` | — | No | Sender display name |
|
|
40
|
-
| `active` | `boolean` | `true` | No | Set `false` to disable the plugin entirely |
|
|
63
|
+
| `active` | `boolean` | `true` | No | Set `false` to disable the plugin entirely (no decorator is added) |
|
|
41
64
|
|
|
42
|
-
|
|
65
|
+
Invalid options fail fast at registration with errors like:
|
|
43
66
|
|
|
44
|
-
|
|
67
|
+
```
|
|
68
|
+
xemail: option `apiKey` must be a string, e.g. `app.register(xEmail, { apiKey: 'SG.your-api-key' })`
|
|
69
|
+
```
|
|
45
70
|
|
|
46
|
-
|
|
71
|
+
## Decorators
|
|
47
72
|
|
|
48
|
-
|
|
73
|
+
The plugin adds one decorator: `fastify.xEmail`. No request decorators.
|
|
49
74
|
|
|
50
|
-
|
|
51
|
-
const result = await fastify.xEmail.send('user@example.com', 'Hello', '<p>Hi</p>');
|
|
52
|
-
// { success: true, statusCode: 202, messageId: 'xxx' }
|
|
53
|
-
```
|
|
75
|
+
### `send(to, subject, html, text?, extraOptions?)`
|
|
54
76
|
|
|
55
|
-
|
|
77
|
+
Send an email. Plain text is auto-generated from the HTML if `text` is omitted.
|
|
78
|
+
Under SendGrid, `extraOptions` is merged into the message (e.g. `replyTo`, `cc`,
|
|
79
|
+
`categories`). Under Postmark, `extraOptions` accepts `replyTo`, `cc`, `bcc`,
|
|
80
|
+
`headers`, and `messageStream` (mapped to Postmark's PascalCase fields).
|
|
81
|
+
Returns `{ success, statusCode, messageId }` (Postmark reports a fixed
|
|
82
|
+
`statusCode: 200` on success and throws on failure).
|
|
56
83
|
|
|
57
|
-
|
|
84
|
+
### `sendTemplate(to, subject, templateId, dynamicData?, extraOptions?)`
|
|
58
85
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
{ firstName: 'Tim', actionUrl: 'https://example.com/verify' }
|
|
65
|
-
);
|
|
66
|
-
```
|
|
86
|
+
Send using a provider template. SendGrid: `templateId` is the dynamic template
|
|
87
|
+
ID (`d-xxx`) and `dynamicData` is sent as `dynamicTemplateData`. Postmark:
|
|
88
|
+
`templateId` is a template alias (string) or numeric template ID and
|
|
89
|
+
`dynamicData` becomes the `TemplateModel`. The subject is always included in the
|
|
90
|
+
template data. Returns `{ success, statusCode, messageId }`.
|
|
67
91
|
|
|
68
92
|
### `sendWithAttachments(to, subject, html, attachments)`
|
|
69
93
|
|
|
70
|
-
Send an email with
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
await fastify.xEmail.sendWithAttachments(
|
|
74
|
-
'user@example.com',
|
|
75
|
-
'Your Invoice',
|
|
76
|
-
'<p>See attached.</p>',
|
|
77
|
-
[{ content: base64String, filename: 'invoice.pdf', type: 'application/pdf' }]
|
|
78
|
-
);
|
|
79
|
-
```
|
|
94
|
+
Send an email with attachments. Each attachment needs `content` (base64), `filename`,
|
|
95
|
+
and `type` (MIME type); `disposition` defaults to `'attachment'`. Returns
|
|
96
|
+
`{ success, statusCode, messageId }`.
|
|
80
97
|
|
|
81
98
|
### `sendBulk(to, subject, html)`
|
|
82
99
|
|
|
83
|
-
Send the same email to
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
await fastify.xEmail.sendBulk(
|
|
87
|
-
['a@example.com', 'b@example.com'],
|
|
88
|
-
'Announcement',
|
|
89
|
-
'<p>Big news!</p>'
|
|
90
|
-
);
|
|
91
|
-
// { success: true, count: 2, statusCode: 202 }
|
|
92
|
-
```
|
|
100
|
+
Send the same email to an array of recipients. SendGrid uses one
|
|
101
|
+
`sgMail.sendMultiple` call; Postmark sends one email per recipient in chunks of
|
|
102
|
+
500 concurrent requests. Returns `{ success, count, statusCode }`.
|
|
93
103
|
|
|
94
104
|
### `sendPersonalizedBulk(messages)`
|
|
95
105
|
|
|
96
|
-
Send different content
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
]);
|
|
103
|
-
// [{ success: true, to: 'a@example.com', statusCode: 202 }, ...]
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
### `validate(email)`
|
|
106
|
+
Send different content per recipient. `messages` is an array of
|
|
107
|
+
`{ to, subject, html, text? }`. Never throws on individual failures — returns
|
|
108
|
+
one result per recipient: `{ success: true, to, statusCode }` or
|
|
109
|
+
`{ success: false, to, error }`. Postmark uses the batch endpoint (chunked at
|
|
110
|
+
500 messages); a failure of the whole batch call marks that chunk's recipients
|
|
111
|
+
as failed instead of throwing.
|
|
107
112
|
|
|
108
|
-
|
|
113
|
+
### `validate(email)` — SendGrid only
|
|
109
114
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
115
|
+
Validate an address via the SendGrid Email Validation API. Returns
|
|
116
|
+
`{ email, valid, verdict, score, result }`. On API failure it returns a soft result
|
|
117
|
+
`{ email, valid: false, verdict: 'Unknown', error }` instead of throwing.
|
|
118
|
+
Throws under `provider: 'postmark'`.
|
|
114
119
|
|
|
115
|
-
### `addContact(email, data?, listIds?)`
|
|
120
|
+
### `addContact(email, data?, listIds?)` — SendGrid only
|
|
116
121
|
|
|
117
|
-
Add or update a
|
|
122
|
+
Add or update a SendGrid Marketing contact. `data` accepts `firstName`/`lastName`
|
|
123
|
+
(or `first_name`/`last_name`) and `customFields` (spread into the contact);
|
|
124
|
+
`listIds` is an array of list IDs. Returns `{ success, jobId, email }`.
|
|
118
125
|
|
|
119
|
-
|
|
120
|
-
await fastify.xEmail.addContact('user@example.com', {
|
|
121
|
-
firstName: 'Tim',
|
|
122
|
-
lastName: 'Smith',
|
|
123
|
-
customFields: { w1_T: 'premium' }
|
|
124
|
-
}, ['list-id-1']);
|
|
125
|
-
// { success: true, jobId: '...', email: '...' }
|
|
126
|
-
```
|
|
126
|
+
### `searchContact(email)` — SendGrid only
|
|
127
127
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
Search for a contact by email.
|
|
131
|
-
|
|
132
|
-
```javascript
|
|
133
|
-
const result = await fastify.xEmail.searchContact('user@example.com');
|
|
134
|
-
// { found: true, contact: { id: '...', email: '...', ... } }
|
|
135
|
-
```
|
|
136
|
-
|
|
137
|
-
### `deleteContact(contactId)`
|
|
138
|
-
|
|
139
|
-
Delete a contact by ID. Returns `true` on success.
|
|
140
|
-
|
|
141
|
-
### `createList(name)`
|
|
142
|
-
|
|
143
|
-
Create a new marketing contact list.
|
|
144
|
-
|
|
145
|
-
```javascript
|
|
146
|
-
const result = await fastify.xEmail.createList('Newsletter');
|
|
147
|
-
// { success: true, list: { id: '...', name: 'Newsletter' } }
|
|
148
|
-
```
|
|
128
|
+
Search for a contact by email. Returns `{ found: true, contact }` or `{ found: false }`.
|
|
149
129
|
|
|
150
|
-
### `
|
|
130
|
+
### `deleteContact(contactId)` — SendGrid only
|
|
151
131
|
|
|
152
|
-
|
|
132
|
+
Delete a contact by ID. Returns `true` on success (HTTP 200/202), `false` otherwise.
|
|
153
133
|
|
|
154
|
-
### `
|
|
134
|
+
### `createList(name)` — SendGrid only
|
|
155
135
|
|
|
156
|
-
|
|
136
|
+
Create a marketing contact list. Returns `{ success, list }`.
|
|
157
137
|
|
|
158
|
-
|
|
138
|
+
### `getLists()` — SendGrid only
|
|
159
139
|
|
|
160
|
-
|
|
161
|
-
|------|----------|-------------|
|
|
162
|
-
| `SENDGRID_API_KEY` | Yes | SendGrid API key with Mail Send permissions |
|
|
163
|
-
| `SENDGRID_FROM_EMAIL` | Yes | Verified sender email address |
|
|
164
|
-
| `SENDGRID_FROM_NAME` | No | Sender display name |
|
|
140
|
+
Get all contact lists. Returns an array (empty when none exist).
|
|
165
141
|
|
|
166
|
-
|
|
142
|
+
### `deleteList(listId)` — SendGrid only
|
|
167
143
|
|
|
168
|
-
|
|
144
|
+
Delete a list by ID. Returns `true` on success (HTTP 200/202/204), `false` otherwise.
|
|
169
145
|
|
|
170
|
-
|
|
171
|
-
|-------|------|
|
|
172
|
-
| `[xEmail] 'apiKey' (string) is required.` | Missing or non-string `apiKey` at registration |
|
|
173
|
-
| `[xEmail] 'fromEmail' (string) is required.` | Missing or non-string `fromEmail` at registration |
|
|
174
|
-
| `[xEmail] 'to' is required for send().` | Calling `send()` without a recipient |
|
|
175
|
-
| `[xEmail] 'subject' is required for send().` | Calling `send()` without a subject |
|
|
176
|
-
| `[xEmail] 'html' is required for send().` | Calling `send()` without HTML content |
|
|
177
|
-
| `[xEmail] 'templateId' is required for sendTemplate().` | Missing template ID |
|
|
178
|
-
| `[xEmail] 'attachments' must be a non-empty array.` | Empty or non-array attachments |
|
|
179
|
-
| `[xEmail] 'to' must be a non-empty array for sendBulk().` | Non-array or empty array for bulk |
|
|
180
|
-
| `[xEmail] 'messages' must be a non-empty array for sendPersonalizedBulk().` | Empty messages array |
|
|
181
|
-
| `[xEmail] 'email' (string) is required for validate().` | Missing email for validation |
|
|
182
|
-
| `[xEmail] Failed to send email: <reason>` | SendGrid API rejection on send |
|
|
183
|
-
| `[xEmail] Failed to add contact: <reason>` | SendGrid API rejection on contact add |
|
|
146
|
+
## Routes
|
|
184
147
|
|
|
185
|
-
|
|
148
|
+
None. This plugin adds no routes.
|
|
186
149
|
|
|
187
|
-
|
|
150
|
+
## Error behavior
|
|
188
151
|
|
|
189
|
-
|
|
152
|
+
- **Registration** throws fail-fast `Error`s naming the plugin and the option, with a
|
|
153
|
+
registration example (see Options above).
|
|
154
|
+
- **Method argument validation** throws synchronously-rejecting `Error`s prefixed with
|
|
155
|
+
`[xEmail]`, e.g. `[xEmail] 'to' is required for send().`
|
|
156
|
+
- **SendGrid API failures** are logged via `fastify.log.error` (structured error, never
|
|
157
|
+
the API key) and re-thrown wrapped: `[xEmail] Failed to send email: <reason>`.
|
|
158
|
+
`validate()` is the exception — it returns a soft result instead of throwing, since
|
|
159
|
+
validation failures are expected in normal operation. Postmark failures are wrapped
|
|
160
|
+
the same way.
|
|
161
|
+
- **SendGrid-only methods under Postmark** throw
|
|
162
|
+
`[xEmail] '<method>()' is not supported by provider 'postmark'.`
|
|
163
|
+
- With `active: false` the plugin returns before registering anything, so
|
|
164
|
+
`fastify.xEmail` is `undefined`.
|
|
190
165
|
|
|
191
|
-
|
|
166
|
+
## Requirements
|
|
192
167
|
|
|
193
|
-
|
|
168
|
+
- Node.js >= 20
|
|
169
|
+
- Fastify ^5.0.0 (peer dependency)
|
|
194
170
|
|
|
195
171
|
## License
|
|
196
172
|
|
|
197
|
-
|
|
173
|
+
Proprietary — All Rights Reserved, X Enterprises. See `LICENSE`.
|
package/index.d.ts
CHANGED
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* xEmail - Fastify Plugin for SendGrid
|
|
2
|
+
* xEmail - Fastify Plugin for Email (Postmark + SendGrid legacy)
|
|
3
3
|
* @module @xenterprises/fastify-xemail
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { FastifyPluginAsync } from 'fastify';
|
|
7
7
|
|
|
8
|
-
/** Additional
|
|
8
|
+
/** Additional mail options passed through to the provider API. */
|
|
9
9
|
export interface EmailExtraOptions {
|
|
10
10
|
replyTo?: string;
|
|
11
11
|
bcc?: string | string[];
|
|
12
12
|
cc?: string | string[];
|
|
13
13
|
categories?: string[];
|
|
14
14
|
headers?: Record<string, string>;
|
|
15
|
+
/** Postmark message stream (e.g. 'outbound'); Postmark provider only */
|
|
16
|
+
messageStream?: string;
|
|
15
17
|
[key: string]: unknown;
|
|
16
18
|
}
|
|
17
19
|
|
|
@@ -98,22 +100,34 @@ export interface PersonalizedMessage {
|
|
|
98
100
|
/** All methods decorated onto fastify.xEmail. */
|
|
99
101
|
export interface XEmailService {
|
|
100
102
|
send(to: string | string[], subject: string, html: string, text?: string | null, extraOptions?: EmailExtraOptions): Promise<EmailSendResult>;
|
|
101
|
-
sendTemplate(to: string | string[], subject: string, templateId: string, dynamicData?: Record<string, unknown>, extraOptions?: EmailExtraOptions): Promise<EmailSendResult>;
|
|
103
|
+
sendTemplate(to: string | string[], subject: string, templateId: string | number, dynamicData?: Record<string, unknown>, extraOptions?: EmailExtraOptions): Promise<EmailSendResult>;
|
|
102
104
|
sendWithAttachments(to: string | string[], subject: string, html: string, attachments: EmailAttachment[]): Promise<EmailSendResult>;
|
|
103
105
|
sendBulk(to: string[], subject: string, html: string): Promise<EmailBulkResult>;
|
|
104
106
|
sendPersonalizedBulk(messages: PersonalizedMessage[]): Promise<EmailPersonalizedResult[]>;
|
|
107
|
+
/** SendGrid only — throws under provider 'postmark'. */
|
|
105
108
|
validate(email: string): Promise<EmailValidationResult>;
|
|
109
|
+
/** SendGrid only — throws under provider 'postmark'. */
|
|
106
110
|
addContact(email: string, data?: ContactData, listIds?: string[]): Promise<EmailAddContactResult>;
|
|
111
|
+
/** SendGrid only — throws under provider 'postmark'. */
|
|
107
112
|
searchContact(email: string): Promise<EmailSearchContactResult>;
|
|
113
|
+
/** SendGrid only — throws under provider 'postmark'. */
|
|
108
114
|
deleteContact(contactId: string): Promise<boolean>;
|
|
115
|
+
/** SendGrid only — throws under provider 'postmark'. */
|
|
109
116
|
createList(name: string): Promise<EmailCreateListResult>;
|
|
117
|
+
/** SendGrid only — throws under provider 'postmark'. */
|
|
110
118
|
getLists(): Promise<Record<string, unknown>[]>;
|
|
119
|
+
/** SendGrid only — throws under provider 'postmark'. */
|
|
111
120
|
deleteList(listId: string): Promise<boolean>;
|
|
112
121
|
}
|
|
113
122
|
|
|
114
123
|
/** Plugin configuration options. */
|
|
115
124
|
export interface XEmailPluginOptions {
|
|
116
|
-
/**
|
|
125
|
+
/**
|
|
126
|
+
* Email provider: 'sendgrid' (legacy, default) or 'postmark' (migration target).
|
|
127
|
+
* @default 'sendgrid'
|
|
128
|
+
*/
|
|
129
|
+
provider?: 'sendgrid' | 'postmark';
|
|
130
|
+
/** Provider API key: SendGrid API key or Postmark server token (required) */
|
|
117
131
|
apiKey: string;
|
|
118
132
|
/** Verified sender email address (required) */
|
|
119
133
|
fromEmail: string;
|
package/package.json
CHANGED
|
@@ -1,14 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xenterprises/fastify-xemail",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.
|
|
5
|
-
"description": "Fastify plugin for SendGrid
|
|
4
|
+
"version": "1.3.0",
|
|
5
|
+
"description": "Fastify plugin for email via Postmark or SendGrid (legacy) — transactional emails, templates, bulk sending, validation, and contact management.",
|
|
6
6
|
"main": "src/xEmail.js",
|
|
7
|
+
"types": "./index.d.ts",
|
|
7
8
|
"exports": {
|
|
8
|
-
".":
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./index.d.ts",
|
|
11
|
+
"default": "./src/xEmail.js"
|
|
12
|
+
}
|
|
9
13
|
},
|
|
10
14
|
"scripts": {
|
|
11
|
-
"test": "node --test test
|
|
15
|
+
"test": "node --test 'test/**/*.test.js'",
|
|
16
|
+
"lint": "biome check src/ test/",
|
|
17
|
+
"format": "biome format --write src/ test/"
|
|
12
18
|
},
|
|
13
19
|
"engines": {
|
|
14
20
|
"node": ">=20.0.0",
|
|
@@ -16,6 +22,7 @@
|
|
|
16
22
|
},
|
|
17
23
|
"keywords": [
|
|
18
24
|
"fastify",
|
|
25
|
+
"postmark",
|
|
19
26
|
"sendgrid",
|
|
20
27
|
"email",
|
|
21
28
|
"plugin"
|
|
@@ -23,13 +30,15 @@
|
|
|
23
30
|
"author": "Tim Mushen",
|
|
24
31
|
"license": "SEE LICENSE IN LICENSE",
|
|
25
32
|
"devDependencies": {
|
|
33
|
+
"@biomejs/biome": "2.5.5",
|
|
26
34
|
"@types/node": "^22.7.4",
|
|
27
35
|
"fastify": "^5.1.0"
|
|
28
36
|
},
|
|
29
37
|
"dependencies": {
|
|
30
38
|
"@sendgrid/client": "^8.1.3",
|
|
31
39
|
"@sendgrid/mail": "^8.1.3",
|
|
32
|
-
"fastify-plugin": "^5.0.0"
|
|
40
|
+
"fastify-plugin": "^5.0.0",
|
|
41
|
+
"postmark": "^4.0.7"
|
|
33
42
|
},
|
|
34
43
|
"peerDependencies": {
|
|
35
44
|
"fastify": "^5.0.0"
|