@cb4kas17/medusa-notification-postmark 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/LICENSE +22 -0
- package/README.md +348 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +29 -0
- package/dist/service.d.ts +39 -0
- package/dist/service.d.ts.map +1 -0
- package/dist/service.js +170 -0
- package/dist/types.d.ts +74 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/utils.d.ts +25 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +72 -0
- package/package.json +44 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 const-code
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
package/README.md
ADDED
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
# @const-code/medusa-notification-postmark
|
|
2
|
+
|
|
3
|
+
A Postmark notification provider for Medusa v2. Supports sending emails using custom HTML (React Email, Handlebars, plain HTML) or Postmark's hosted templates.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Dual Mode** - Use custom HTML or Postmark's hosted templates
|
|
8
|
+
- **Auto CID Conversion** - Automatically converts `data:image` URLs to email attachments
|
|
9
|
+
- **Message Streams** - Supports transactional, broadcasts, and custom streams
|
|
10
|
+
- **Full Postmark API** - Tracking, tags, metadata, and custom headers
|
|
11
|
+
- **TypeScript** - Fully typed for type safety
|
|
12
|
+
- **Zero Templates** - Bring your own templates or use Postmark's
|
|
13
|
+
- **Framework Agnostic** - Works with any rendering engine
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install @const-code/medusa-notification-postmark postmark
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Or with yarn:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
yarn add @const-code/medusa-notification-postmark postmark
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Quick Start
|
|
28
|
+
|
|
29
|
+
### 1. Configure in `medusa-config.ts`
|
|
30
|
+
|
|
31
|
+
```typescript
|
|
32
|
+
import { defineConfig } from "@medusajs/framework/utils"
|
|
33
|
+
|
|
34
|
+
export default defineConfig({
|
|
35
|
+
modules: [
|
|
36
|
+
{
|
|
37
|
+
resolve: "@const-code/medusa-notification-postmark",
|
|
38
|
+
options: {
|
|
39
|
+
serverToken: process.env.POSTMARK_SERVER_TOKEN,
|
|
40
|
+
from: "orders@yourstore.com",
|
|
41
|
+
messageStream: "outbound", // Optional
|
|
42
|
+
trackOpens: true, // Optional
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
],
|
|
46
|
+
})
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### 2. Add Environment Variables
|
|
50
|
+
|
|
51
|
+
```env
|
|
52
|
+
POSTMARK_SERVER_TOKEN=your-server-token-here
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### 3. Use in Subscribers
|
|
56
|
+
|
|
57
|
+
**Option A: Custom HTML (React Email)**
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
import { render } from "@react-email/components"
|
|
61
|
+
import { OrderEmail } from "../emails/order"
|
|
62
|
+
|
|
63
|
+
export default async function({ event, container }) {
|
|
64
|
+
const notificationService = container.resolve("notification")
|
|
65
|
+
const order = event.data
|
|
66
|
+
|
|
67
|
+
// Render your template
|
|
68
|
+
const html = await render(<OrderEmail order={order} />)
|
|
69
|
+
|
|
70
|
+
// Send via Postmark
|
|
71
|
+
await notificationService.createNotifications({
|
|
72
|
+
to: order.email,
|
|
73
|
+
channel: "email",
|
|
74
|
+
template: "order-placed",
|
|
75
|
+
data: {
|
|
76
|
+
subject: `Order #${order.display_id} Confirmed`,
|
|
77
|
+
html: html,
|
|
78
|
+
},
|
|
79
|
+
})
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
**Option B: Postmark Templates**
|
|
84
|
+
|
|
85
|
+
```typescript
|
|
86
|
+
export default async function({ event, container }) {
|
|
87
|
+
const notificationService = container.resolve("notification")
|
|
88
|
+
const user = event.data
|
|
89
|
+
|
|
90
|
+
// Use Postmark template from dashboard
|
|
91
|
+
await notificationService.createNotifications({
|
|
92
|
+
to: user.email,
|
|
93
|
+
channel: "email",
|
|
94
|
+
template: "welcome",
|
|
95
|
+
data: {
|
|
96
|
+
templateAlias: "welcome-email", // or templateId: 12345
|
|
97
|
+
templateModel: {
|
|
98
|
+
user_name: user.name,
|
|
99
|
+
login_url: "https://yourstore.com/login",
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
})
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## Configuration Options
|
|
107
|
+
|
|
108
|
+
### Provider Options
|
|
109
|
+
|
|
110
|
+
```typescript
|
|
111
|
+
{
|
|
112
|
+
// Required
|
|
113
|
+
serverToken: string, // Postmark API token
|
|
114
|
+
from: string, // Default from address
|
|
115
|
+
|
|
116
|
+
// Optional
|
|
117
|
+
messageStream?: string, // Default: "outbound"
|
|
118
|
+
convertDataUrlsToCID?: boolean, // Auto-convert images (default: true)
|
|
119
|
+
trackOpens?: boolean, // Track email opens
|
|
120
|
+
trackLinks?: "None" | "HtmlAndText" | "HtmlOnly" | "TextOnly"
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Notification Data Options
|
|
125
|
+
|
|
126
|
+
**For Custom HTML:**
|
|
127
|
+
|
|
128
|
+
```typescript
|
|
129
|
+
{
|
|
130
|
+
html: string, // REQUIRED: Rendered HTML
|
|
131
|
+
subject: string, // REQUIRED: Email subject
|
|
132
|
+
from?: string, // Override default from
|
|
133
|
+
replyTo?: string, // Reply-to address
|
|
134
|
+
cc?: string, // CC recipients
|
|
135
|
+
bcc?: string, // BCC recipients
|
|
136
|
+
tag?: string, // Postmark tag
|
|
137
|
+
metadata?: object, // Custom metadata
|
|
138
|
+
attachments?: Attachment[], // Manual attachments
|
|
139
|
+
trackOpens?: boolean, // Override tracking
|
|
140
|
+
trackLinks?: string, // Override tracking
|
|
141
|
+
messageStream?: string, // Override stream
|
|
142
|
+
headers?: Array<{ // Custom headers
|
|
143
|
+
Name: string,
|
|
144
|
+
Value: string
|
|
145
|
+
}>,
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
**For Postmark Templates:**
|
|
150
|
+
|
|
151
|
+
```typescript
|
|
152
|
+
{
|
|
153
|
+
templateId?: number, // Template ID OR
|
|
154
|
+
templateAlias?: string, // Template alias (pick one)
|
|
155
|
+
templateModel: object, // Variables for template
|
|
156
|
+
|
|
157
|
+
// Optional (same as above)
|
|
158
|
+
from?: string,
|
|
159
|
+
replyTo?: string,
|
|
160
|
+
cc?: string,
|
|
161
|
+
bcc?: string,
|
|
162
|
+
tag?: string,
|
|
163
|
+
metadata?: object,
|
|
164
|
+
attachments?: Attachment[],
|
|
165
|
+
trackOpens?: boolean,
|
|
166
|
+
trackLinks?: string,
|
|
167
|
+
messageStream?: string,
|
|
168
|
+
headers?: Array<{ Name: string, Value: string }>,
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
## Examples
|
|
173
|
+
|
|
174
|
+
### Example 1: QR Codes (Auto-converted to CID)
|
|
175
|
+
|
|
176
|
+
```typescript
|
|
177
|
+
import QRCode from "qrcode"
|
|
178
|
+
|
|
179
|
+
const qrCode = await QRCode.toDataURL(ticketId)
|
|
180
|
+
const html = await render(<TicketEmail qrCode={qrCode} />)
|
|
181
|
+
|
|
182
|
+
// The provider automatically converts data:image to CID attachments
|
|
183
|
+
await notificationService.createNotifications({
|
|
184
|
+
to: email,
|
|
185
|
+
channel: "email",
|
|
186
|
+
template: "ticket",
|
|
187
|
+
data: {
|
|
188
|
+
subject: "Your Ticket",
|
|
189
|
+
html: html,
|
|
190
|
+
},
|
|
191
|
+
})
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### Example 2: Message Streams
|
|
195
|
+
|
|
196
|
+
```typescript
|
|
197
|
+
// Transactional email
|
|
198
|
+
await notificationService.createNotifications({
|
|
199
|
+
to: email,
|
|
200
|
+
channel: "email",
|
|
201
|
+
template: "receipt",
|
|
202
|
+
data: {
|
|
203
|
+
subject: "Your Receipt",
|
|
204
|
+
html: html,
|
|
205
|
+
messageStream: "outbound", // Default
|
|
206
|
+
},
|
|
207
|
+
})
|
|
208
|
+
|
|
209
|
+
// Marketing email
|
|
210
|
+
await notificationService.createNotifications({
|
|
211
|
+
to: email,
|
|
212
|
+
channel: "email",
|
|
213
|
+
template: "newsletter",
|
|
214
|
+
data: {
|
|
215
|
+
subject: "Monthly Newsletter",
|
|
216
|
+
html: html,
|
|
217
|
+
messageStream: "broadcasts",
|
|
218
|
+
},
|
|
219
|
+
})
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
### Example 3: Attachments
|
|
223
|
+
|
|
224
|
+
```typescript
|
|
225
|
+
await notificationService.createNotifications({
|
|
226
|
+
to: email,
|
|
227
|
+
channel: "email",
|
|
228
|
+
template: "invoice",
|
|
229
|
+
data: {
|
|
230
|
+
subject: "Your Invoice",
|
|
231
|
+
html: html,
|
|
232
|
+
attachments: [
|
|
233
|
+
{
|
|
234
|
+
Name: "invoice.pdf",
|
|
235
|
+
Content: "base64-content-here",
|
|
236
|
+
ContentType: "application/pdf",
|
|
237
|
+
},
|
|
238
|
+
],
|
|
239
|
+
},
|
|
240
|
+
})
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
### Example 4: Metadata & Tags
|
|
244
|
+
|
|
245
|
+
```typescript
|
|
246
|
+
await notificationService.createNotifications({
|
|
247
|
+
to: email,
|
|
248
|
+
channel: "email",
|
|
249
|
+
template: "order",
|
|
250
|
+
data: {
|
|
251
|
+
subject: "Order Confirmed",
|
|
252
|
+
html: html,
|
|
253
|
+
tag: "order-confirmation",
|
|
254
|
+
metadata: {
|
|
255
|
+
orderId: order.id,
|
|
256
|
+
customerId: customer.id,
|
|
257
|
+
},
|
|
258
|
+
},
|
|
259
|
+
})
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
## Message Streams
|
|
263
|
+
|
|
264
|
+
| Stream | Purpose | Example Use Cases |
|
|
265
|
+
|--------|---------|-------------------|
|
|
266
|
+
| `outbound` | Transactional emails (default) | Orders, receipts, password resets |
|
|
267
|
+
| `broadcasts` | Marketing emails | Newsletters, promotions |
|
|
268
|
+
| Custom | Any stream you create in Postmark | VIP customers, internal alerts |
|
|
269
|
+
|
|
270
|
+
## TypeScript Support
|
|
271
|
+
|
|
272
|
+
All TypeScript types are exported:
|
|
273
|
+
|
|
274
|
+
```typescript
|
|
275
|
+
import {
|
|
276
|
+
PostmarkOptions,
|
|
277
|
+
NotificationPayload,
|
|
278
|
+
Attachment,
|
|
279
|
+
convertDataUrlsToCID,
|
|
280
|
+
} from "@const-code/medusa-notification-postmark"
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
## Utility Functions
|
|
284
|
+
|
|
285
|
+
### convertDataUrlsToCID
|
|
286
|
+
|
|
287
|
+
Manually convert data URLs to CID attachments:
|
|
288
|
+
|
|
289
|
+
```typescript
|
|
290
|
+
import { convertDataUrlsToCID } from "@const-code/medusa-notification-postmark"
|
|
291
|
+
|
|
292
|
+
const html = `<img src="data:image/png;base64,..." />`
|
|
293
|
+
const { html: finalHtml, attachments } = convertDataUrlsToCID(html)
|
|
294
|
+
|
|
295
|
+
// Returns:
|
|
296
|
+
// finalHtml: <img src="cid:image-0" />
|
|
297
|
+
// attachments: [{ Name: "image-0.png", Content: "...", ContentID: "cid:image-0" }]
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
## When to Use Each Approach
|
|
301
|
+
|
|
302
|
+
### Custom HTML
|
|
303
|
+
|
|
304
|
+
- Full control over design
|
|
305
|
+
- Version controlled with your code
|
|
306
|
+
- TypeScript type safety
|
|
307
|
+
- Dynamic logic in templates
|
|
308
|
+
|
|
309
|
+
Best for: Order confirmations, receipts, password resets
|
|
310
|
+
|
|
311
|
+
### Postmark Templates
|
|
312
|
+
|
|
313
|
+
- Non-technical team can edit templates
|
|
314
|
+
- A/B testing in Postmark dashboard
|
|
315
|
+
- No deployment needed for updates
|
|
316
|
+
- Multi-language support
|
|
317
|
+
|
|
318
|
+
Best for: Marketing emails, newsletters, campaigns
|
|
319
|
+
|
|
320
|
+
## FAQ
|
|
321
|
+
|
|
322
|
+
**Q: Do I need to include templates in this package?**
|
|
323
|
+
A: No. This is a transport-only provider. Bring your own templates or use Postmark's hosted templates.
|
|
324
|
+
|
|
325
|
+
**Q: Can I use both approaches in the same project?**
|
|
326
|
+
A: Yes. You can use custom HTML for transactional emails and Postmark templates for marketing emails.
|
|
327
|
+
|
|
328
|
+
**Q: Are QR codes supported?**
|
|
329
|
+
A: Yes. Any `data:image` URL is automatically converted to CID attachments.
|
|
330
|
+
|
|
331
|
+
**Q: Can I disable CID conversion?**
|
|
332
|
+
A: Yes. Set `convertDataUrlsToCID: false` in the provider options.
|
|
333
|
+
|
|
334
|
+
## License
|
|
335
|
+
|
|
336
|
+
MIT
|
|
337
|
+
|
|
338
|
+
## Contributing
|
|
339
|
+
|
|
340
|
+
Contributions are welcome. Please open an issue or pull request on GitHub.
|
|
341
|
+
|
|
342
|
+
## Links
|
|
343
|
+
|
|
344
|
+
- [GitHub](https://github.com/const-code/medusa-notification-postmark)
|
|
345
|
+
- [npm](https://www.npmjs.com/package/@const-code/medusa-notification-postmark)
|
|
346
|
+
- [Postmark Documentation](https://postmarkapp.com/developer)
|
|
347
|
+
- [Medusa Documentation](https://docs.medusajs.com)
|
|
348
|
+
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import PostmarkNotificationService from "./service";
|
|
2
|
+
declare const _default: import("@medusajs/types").ModuleProviderExports;
|
|
3
|
+
export default _default;
|
|
4
|
+
export * from "./types";
|
|
5
|
+
export * from "./utils";
|
|
6
|
+
export { PostmarkNotificationService };
|
|
7
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,2BAA2B,MAAM,WAAW,CAAA;;AAEnD,wBAEE;AAGF,cAAc,SAAS,CAAA;AACvB,cAAc,SAAS,CAAA;AACvB,OAAO,EAAE,2BAA2B,EAAE,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
17
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
18
|
+
};
|
|
19
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
+
exports.PostmarkNotificationService = void 0;
|
|
21
|
+
const utils_1 = require("@medusajs/framework/utils");
|
|
22
|
+
const service_1 = __importDefault(require("./service"));
|
|
23
|
+
exports.PostmarkNotificationService = service_1.default;
|
|
24
|
+
exports.default = (0, utils_1.ModuleProvider)(utils_1.Modules.NOTIFICATION, {
|
|
25
|
+
services: [service_1.default],
|
|
26
|
+
});
|
|
27
|
+
// Export types for users
|
|
28
|
+
__exportStar(require("./types"), exports);
|
|
29
|
+
__exportStar(require("./utils"), exports);
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { AbstractNotificationProviderService } from "@medusajs/framework/utils";
|
|
2
|
+
import { Logger } from "@medusajs/framework/types";
|
|
3
|
+
import { ServerClient } from "postmark";
|
|
4
|
+
import { PostmarkOptions, NotificationPayload } from "./types";
|
|
5
|
+
export default class PostmarkNotificationService extends AbstractNotificationProviderService {
|
|
6
|
+
static identifier: string;
|
|
7
|
+
protected client: ServerClient;
|
|
8
|
+
protected options: PostmarkOptions;
|
|
9
|
+
protected logger: Logger;
|
|
10
|
+
constructor({ logger }: {
|
|
11
|
+
logger: Logger;
|
|
12
|
+
}, options: PostmarkOptions);
|
|
13
|
+
/**
|
|
14
|
+
* Send notification via Postmark
|
|
15
|
+
*
|
|
16
|
+
* Supports two modes:
|
|
17
|
+
* 1. Custom HTML - Pass `html` and `subject` in data
|
|
18
|
+
* 2. Postmark Template - Pass `templateId` or `templateAlias` in data
|
|
19
|
+
*
|
|
20
|
+
* @param notification - Notification payload from Medusa
|
|
21
|
+
* @returns Object with sent message ID
|
|
22
|
+
*/
|
|
23
|
+
send(notification: NotificationPayload): Promise<{
|
|
24
|
+
id: string;
|
|
25
|
+
}>;
|
|
26
|
+
/**
|
|
27
|
+
* Send email with custom HTML
|
|
28
|
+
*/
|
|
29
|
+
private sendWithHtml;
|
|
30
|
+
/**
|
|
31
|
+
* Send email using Postmark template
|
|
32
|
+
*/
|
|
33
|
+
private sendWithTemplate;
|
|
34
|
+
/**
|
|
35
|
+
* Add optional fields to email options
|
|
36
|
+
*/
|
|
37
|
+
private addOptionalFields;
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mCAAmC,EAAE,MAAM,2BAA2B,CAAA;AAC/E,OAAO,EAAE,MAAM,EAAE,MAAM,2BAA2B,CAAA;AAClD,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AACvC,OAAO,EACL,eAAe,EACf,mBAAmB,EAIpB,MAAM,SAAS,CAAA;AAGhB,MAAM,CAAC,OAAO,OAAO,2BAA4B,SAAQ,mCAAmC;IAC1F,MAAM,CAAC,UAAU,SAAa;IAE9B,SAAS,CAAC,MAAM,EAAE,YAAY,CAAA;IAC9B,SAAS,CAAC,OAAO,EAAE,eAAe,CAAA;IAClC,SAAS,CAAC,MAAM,EAAE,MAAM,CAAA;gBAGtB,EAAE,MAAM,EAAE,EAAE;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,EAC9B,OAAO,EAAE,eAAe;IAqB1B;;;;;;;;;OASG;IACG,IAAI,CAAC,YAAY,EAAE,mBAAmB,GAAG,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC;IActE;;OAEG;YACW,YAAY;IA4D1B;;OAEG;YACW,gBAAgB;IAkD9B;;OAEG;IACH,OAAO,CAAC,iBAAiB;CAgC1B"}
|
package/dist/service.js
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const utils_1 = require("@medusajs/framework/utils");
|
|
4
|
+
const postmark_1 = require("postmark");
|
|
5
|
+
const utils_2 = require("./utils");
|
|
6
|
+
class PostmarkNotificationService extends utils_1.AbstractNotificationProviderService {
|
|
7
|
+
constructor({ logger }, options) {
|
|
8
|
+
super();
|
|
9
|
+
if (!options.serverToken) {
|
|
10
|
+
throw new Error("Postmark server token is required");
|
|
11
|
+
}
|
|
12
|
+
if (!options.from) {
|
|
13
|
+
throw new Error("From email address is required");
|
|
14
|
+
}
|
|
15
|
+
this.logger = logger;
|
|
16
|
+
this.options = {
|
|
17
|
+
messageStream: "outbound",
|
|
18
|
+
convertDataUrlsToCID: true,
|
|
19
|
+
...options,
|
|
20
|
+
};
|
|
21
|
+
this.client = new postmark_1.ServerClient(options.serverToken);
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Send notification via Postmark
|
|
25
|
+
*
|
|
26
|
+
* Supports two modes:
|
|
27
|
+
* 1. Custom HTML - Pass `html` and `subject` in data
|
|
28
|
+
* 2. Postmark Template - Pass `templateId` or `templateAlias` in data
|
|
29
|
+
*
|
|
30
|
+
* @param notification - Notification payload from Medusa
|
|
31
|
+
* @returns Object with sent message ID
|
|
32
|
+
*/
|
|
33
|
+
async send(notification) {
|
|
34
|
+
const { to, data } = notification;
|
|
35
|
+
// Check if using Postmark templates or custom HTML
|
|
36
|
+
const templateId = data?.templateId;
|
|
37
|
+
const templateAlias = data?.templateAlias;
|
|
38
|
+
if (templateId || templateAlias) {
|
|
39
|
+
return this.sendWithTemplate(to, data);
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
return this.sendWithHtml(to, data);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Send email with custom HTML
|
|
47
|
+
*/
|
|
48
|
+
async sendWithHtml(to, data) {
|
|
49
|
+
const html = data?.html;
|
|
50
|
+
const subject = data?.subject;
|
|
51
|
+
const from = data?.from || this.options.from;
|
|
52
|
+
const replyTo = data?.replyTo;
|
|
53
|
+
const messageStream = data?.messageStream || this.options.messageStream;
|
|
54
|
+
if (!html) {
|
|
55
|
+
throw new Error("No HTML content provided. Pass 'html' in notification data or use 'templateId'/'templateAlias' for Postmark templates");
|
|
56
|
+
}
|
|
57
|
+
if (!subject) {
|
|
58
|
+
throw new Error("No subject provided. Pass 'subject' in notification data");
|
|
59
|
+
}
|
|
60
|
+
// Handle attachments (convert data URLs to CID if enabled)
|
|
61
|
+
let finalHtml = html;
|
|
62
|
+
let attachments = [];
|
|
63
|
+
if (this.options.convertDataUrlsToCID && html.includes('data:image')) {
|
|
64
|
+
const converted = (0, utils_2.convertDataUrlsToCID)(html, data?.attachments || []);
|
|
65
|
+
finalHtml = converted.html;
|
|
66
|
+
attachments = converted.attachments;
|
|
67
|
+
}
|
|
68
|
+
else if (data?.attachments) {
|
|
69
|
+
attachments = data.attachments;
|
|
70
|
+
}
|
|
71
|
+
// Build email options
|
|
72
|
+
const emailOptions = {
|
|
73
|
+
From: from,
|
|
74
|
+
To: to,
|
|
75
|
+
Subject: subject,
|
|
76
|
+
HtmlBody: finalHtml,
|
|
77
|
+
MessageStream: messageStream,
|
|
78
|
+
};
|
|
79
|
+
// Add optional fields
|
|
80
|
+
this.addOptionalFields(emailOptions, data, attachments);
|
|
81
|
+
try {
|
|
82
|
+
this.logger.debug(`Sending email via Postmark (custom HTML) to ${to}`);
|
|
83
|
+
const response = await this.client.sendEmail(emailOptions);
|
|
84
|
+
this.logger.info(`Email sent successfully via Postmark: ${response.MessageID}`);
|
|
85
|
+
return { id: response.MessageID };
|
|
86
|
+
}
|
|
87
|
+
catch (error) {
|
|
88
|
+
this.logger.error(`Failed to send email via Postmark: ${error?.message || "Unknown error"}`);
|
|
89
|
+
throw error;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Send email using Postmark template
|
|
94
|
+
*/
|
|
95
|
+
async sendWithTemplate(to, data) {
|
|
96
|
+
const templateId = data?.templateId;
|
|
97
|
+
const templateAlias = data?.templateAlias;
|
|
98
|
+
const templateModel = data?.templateModel || {};
|
|
99
|
+
const from = data?.from || this.options.from;
|
|
100
|
+
const replyTo = data?.replyTo;
|
|
101
|
+
const messageStream = data?.messageStream || this.options.messageStream;
|
|
102
|
+
if (!templateId && !templateAlias) {
|
|
103
|
+
throw new Error("Either 'templateId' (number) or 'templateAlias' (string) must be provided for Postmark templates");
|
|
104
|
+
}
|
|
105
|
+
// Build template options
|
|
106
|
+
const templateOptions = {
|
|
107
|
+
From: from,
|
|
108
|
+
To: to,
|
|
109
|
+
TemplateModel: templateModel,
|
|
110
|
+
MessageStream: messageStream,
|
|
111
|
+
};
|
|
112
|
+
// Use TemplateId or TemplateAlias
|
|
113
|
+
if (templateAlias) {
|
|
114
|
+
templateOptions.TemplateAlias = templateAlias;
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
templateOptions.TemplateId = templateId;
|
|
118
|
+
}
|
|
119
|
+
// Add optional fields
|
|
120
|
+
const attachments = data?.attachments || [];
|
|
121
|
+
this.addOptionalFields(templateOptions, data, attachments);
|
|
122
|
+
try {
|
|
123
|
+
this.logger.debug(`Sending email via Postmark (template ${templateId || templateAlias}) to ${to}`);
|
|
124
|
+
const response = await this.client.sendEmailWithTemplate(templateOptions);
|
|
125
|
+
this.logger.info(`Email sent successfully via Postmark template: ${response.MessageID}`);
|
|
126
|
+
return { id: response.MessageID };
|
|
127
|
+
}
|
|
128
|
+
catch (error) {
|
|
129
|
+
this.logger.error(`Failed to send email via Postmark template: ${error?.message || "Unknown error"}`);
|
|
130
|
+
throw error;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Add optional fields to email options
|
|
135
|
+
*/
|
|
136
|
+
addOptionalFields(emailOptions, data, attachments) {
|
|
137
|
+
const replyTo = data?.replyTo;
|
|
138
|
+
if (replyTo)
|
|
139
|
+
emailOptions.ReplyTo = replyTo;
|
|
140
|
+
if (data?.cc)
|
|
141
|
+
emailOptions.Cc = data.cc;
|
|
142
|
+
if (data?.bcc)
|
|
143
|
+
emailOptions.Bcc = data.bcc;
|
|
144
|
+
if (data?.tag)
|
|
145
|
+
emailOptions.Tag = data.tag;
|
|
146
|
+
if (data?.metadata)
|
|
147
|
+
emailOptions.Metadata = data.metadata;
|
|
148
|
+
if (attachments.length > 0)
|
|
149
|
+
emailOptions.Attachments = attachments;
|
|
150
|
+
// Tracking options
|
|
151
|
+
if (data?.trackOpens !== undefined) {
|
|
152
|
+
emailOptions.TrackOpens = data.trackOpens;
|
|
153
|
+
}
|
|
154
|
+
else if (this.options.trackOpens !== undefined) {
|
|
155
|
+
emailOptions.TrackOpens = this.options.trackOpens;
|
|
156
|
+
}
|
|
157
|
+
if (data?.trackLinks !== undefined) {
|
|
158
|
+
emailOptions.TrackLinks = data.trackLinks;
|
|
159
|
+
}
|
|
160
|
+
else if (this.options.trackLinks !== undefined) {
|
|
161
|
+
emailOptions.TrackLinks = this.options.trackLinks;
|
|
162
|
+
}
|
|
163
|
+
// Headers
|
|
164
|
+
if (data?.headers) {
|
|
165
|
+
emailOptions.Headers = data.headers;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
PostmarkNotificationService.identifier = "postmark";
|
|
170
|
+
exports.default = PostmarkNotificationService;
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
export interface PostmarkOptions {
|
|
2
|
+
/** Postmark server token */
|
|
3
|
+
serverToken: string;
|
|
4
|
+
/** Default from email address */
|
|
5
|
+
from: string;
|
|
6
|
+
/** Postmark message stream (default: "outbound") */
|
|
7
|
+
messageStream?: string;
|
|
8
|
+
/** Automatically convert data URLs to CID attachments (default: true) */
|
|
9
|
+
convertDataUrlsToCID?: boolean;
|
|
10
|
+
/** Track email opens (default: undefined - uses Postmark server default) */
|
|
11
|
+
trackOpens?: boolean;
|
|
12
|
+
/** Track link clicks */
|
|
13
|
+
trackLinks?: "None" | "HtmlAndText" | "HtmlOnly" | "TextOnly";
|
|
14
|
+
}
|
|
15
|
+
export interface NotificationPayload {
|
|
16
|
+
/** Recipient email address */
|
|
17
|
+
to: string;
|
|
18
|
+
/** Notification channel (e.g., "email") */
|
|
19
|
+
channel: string;
|
|
20
|
+
/** Template identifier (optional - for reference) */
|
|
21
|
+
template: string;
|
|
22
|
+
/** Notification data */
|
|
23
|
+
data: Record<string, unknown> | null;
|
|
24
|
+
}
|
|
25
|
+
export interface EmailOptions {
|
|
26
|
+
From: string;
|
|
27
|
+
To: string;
|
|
28
|
+
Subject: string;
|
|
29
|
+
HtmlBody: string;
|
|
30
|
+
MessageStream: string;
|
|
31
|
+
ReplyTo?: string;
|
|
32
|
+
Cc?: string;
|
|
33
|
+
Bcc?: string;
|
|
34
|
+
Tag?: string;
|
|
35
|
+
Metadata?: Record<string, string>;
|
|
36
|
+
Attachments?: Attachment[];
|
|
37
|
+
TrackOpens?: boolean;
|
|
38
|
+
TrackLinks?: "None" | "HtmlAndText" | "HtmlOnly" | "TextOnly";
|
|
39
|
+
Headers?: Array<{
|
|
40
|
+
Name: string;
|
|
41
|
+
Value: string;
|
|
42
|
+
}>;
|
|
43
|
+
}
|
|
44
|
+
export interface TemplateOptions {
|
|
45
|
+
From: string;
|
|
46
|
+
To: string;
|
|
47
|
+
TemplateId?: number;
|
|
48
|
+
TemplateAlias?: string;
|
|
49
|
+
TemplateModel: Record<string, any>;
|
|
50
|
+
MessageStream: string;
|
|
51
|
+
ReplyTo?: string;
|
|
52
|
+
Cc?: string;
|
|
53
|
+
Bcc?: string;
|
|
54
|
+
Tag?: string;
|
|
55
|
+
Metadata?: Record<string, string>;
|
|
56
|
+
Attachments?: Attachment[];
|
|
57
|
+
TrackOpens?: boolean;
|
|
58
|
+
TrackLinks?: "None" | "HtmlAndText" | "HtmlOnly" | "TextOnly";
|
|
59
|
+
Headers?: Array<{
|
|
60
|
+
Name: string;
|
|
61
|
+
Value: string;
|
|
62
|
+
}>;
|
|
63
|
+
}
|
|
64
|
+
export interface Attachment {
|
|
65
|
+
/** File name */
|
|
66
|
+
Name: string;
|
|
67
|
+
/** Base64-encoded content */
|
|
68
|
+
Content: string;
|
|
69
|
+
/** MIME type */
|
|
70
|
+
ContentType: string;
|
|
71
|
+
/** Content ID for inline images (e.g., "cid:logo") */
|
|
72
|
+
ContentID?: string;
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,eAAe;IAC9B,4BAA4B;IAC5B,WAAW,EAAE,MAAM,CAAA;IAEnB,iCAAiC;IACjC,IAAI,EAAE,MAAM,CAAA;IAEZ,oDAAoD;IACpD,aAAa,CAAC,EAAE,MAAM,CAAA;IAEtB,yEAAyE;IACzE,oBAAoB,CAAC,EAAE,OAAO,CAAA;IAE9B,4EAA4E;IAC5E,UAAU,CAAC,EAAE,OAAO,CAAA;IAEpB,wBAAwB;IACxB,UAAU,CAAC,EAAE,MAAM,GAAG,aAAa,GAAG,UAAU,GAAG,UAAU,CAAA;CAC9D;AAED,MAAM,WAAW,mBAAmB;IAClC,8BAA8B;IAC9B,EAAE,EAAE,MAAM,CAAA;IAEV,2CAA2C;IAC3C,OAAO,EAAE,MAAM,CAAA;IAEf,qDAAqD;IACrD,QAAQ,EAAE,MAAM,CAAA;IAEhB,wBAAwB;IACxB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAA;CACrC;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAA;IACZ,EAAE,EAAE,MAAM,CAAA;IACV,OAAO,EAAE,MAAM,CAAA;IACf,QAAQ,EAAE,MAAM,CAAA;IAChB,aAAa,EAAE,MAAM,CAAA;IACrB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACjC,WAAW,CAAC,EAAE,UAAU,EAAE,CAAA;IAC1B,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,UAAU,CAAC,EAAE,MAAM,GAAG,aAAa,GAAG,UAAU,GAAG,UAAU,CAAA;IAC7D,OAAO,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CACjD;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAA;IACZ,EAAE,EAAE,MAAM,CAAA;IACV,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IAClC,aAAa,EAAE,MAAM,CAAA;IACrB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACjC,WAAW,CAAC,EAAE,UAAU,EAAE,CAAA;IAC1B,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,UAAU,CAAC,EAAE,MAAM,GAAG,aAAa,GAAG,UAAU,GAAG,UAAU,CAAA;IAC7D,OAAO,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CACjD;AAED,MAAM,WAAW,UAAU;IACzB,gBAAgB;IAChB,IAAI,EAAE,MAAM,CAAA;IAEZ,6BAA6B;IAC7B,OAAO,EAAE,MAAM,CAAA;IAEf,gBAAgB;IAChB,WAAW,EAAE,MAAM,CAAA;IAEnB,sDAAsD;IACtD,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB"}
|
package/dist/types.js
ADDED
package/dist/utils.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Attachment } from "./types";
|
|
2
|
+
/**
|
|
3
|
+
* Convert data URLs in HTML to CID attachments
|
|
4
|
+
* Recursively finds all data:image URLs and converts them to CID references
|
|
5
|
+
*
|
|
6
|
+
* @param html - HTML content with potential data URLs
|
|
7
|
+
* @param existingAttachments - Any existing attachments to preserve
|
|
8
|
+
* @returns Object with modified HTML and all attachments
|
|
9
|
+
*/
|
|
10
|
+
export declare function convertDataUrlsToCID(html: string, existingAttachments?: any[]): {
|
|
11
|
+
html: string;
|
|
12
|
+
attachments: Attachment[];
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Convert any object with data URLs to CID attachments
|
|
16
|
+
* Useful for templates that pass images in data object
|
|
17
|
+
*
|
|
18
|
+
* @param data - Any data object that might contain data URLs
|
|
19
|
+
* @returns Object with modified data and attachments
|
|
20
|
+
*/
|
|
21
|
+
export declare function convertObjectDataUrlsToCID(data: any): {
|
|
22
|
+
data: any;
|
|
23
|
+
attachments: Attachment[];
|
|
24
|
+
};
|
|
25
|
+
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAEpC;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,MAAM,EACZ,mBAAmB,GAAE,GAAG,EAAO,GAC9B;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,UAAU,EAAE,CAAA;CAAE,CA0B7C;AAED;;;;;;GAMG;AACH,wBAAgB,0BAA0B,CACxC,IAAI,EAAE,GAAG,GACR;IAAE,IAAI,EAAE,GAAG,CAAC;IAAC,WAAW,EAAE,UAAU,EAAE,CAAA;CAAE,CAwC1C"}
|
package/dist/utils.js
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.convertDataUrlsToCID = convertDataUrlsToCID;
|
|
4
|
+
exports.convertObjectDataUrlsToCID = convertObjectDataUrlsToCID;
|
|
5
|
+
/**
|
|
6
|
+
* Convert data URLs in HTML to CID attachments
|
|
7
|
+
* Recursively finds all data:image URLs and converts them to CID references
|
|
8
|
+
*
|
|
9
|
+
* @param html - HTML content with potential data URLs
|
|
10
|
+
* @param existingAttachments - Any existing attachments to preserve
|
|
11
|
+
* @returns Object with modified HTML and all attachments
|
|
12
|
+
*/
|
|
13
|
+
function convertDataUrlsToCID(html, existingAttachments = []) {
|
|
14
|
+
const attachments = [...existingAttachments];
|
|
15
|
+
let finalHtml = html;
|
|
16
|
+
let imageCount = 0;
|
|
17
|
+
// Find all data URLs in the HTML
|
|
18
|
+
const dataUrlRegex = /data:image\/(png|jpeg|jpg|gif|webp);base64,([A-Za-z0-9+/=]+)/g;
|
|
19
|
+
let match;
|
|
20
|
+
while ((match = dataUrlRegex.exec(html)) !== null) {
|
|
21
|
+
const [fullMatch, imageType, base64Content] = match;
|
|
22
|
+
const cid = `image-${imageCount++}`;
|
|
23
|
+
// Create CID attachment
|
|
24
|
+
attachments.push({
|
|
25
|
+
Name: `${cid}.${imageType}`,
|
|
26
|
+
Content: base64Content,
|
|
27
|
+
ContentType: `image/${imageType}`,
|
|
28
|
+
ContentID: `cid:${cid}`,
|
|
29
|
+
});
|
|
30
|
+
// Replace data URL with CID reference
|
|
31
|
+
finalHtml = finalHtml.replace(fullMatch, `cid:${cid}`);
|
|
32
|
+
}
|
|
33
|
+
return { html: finalHtml, attachments };
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Convert any object with data URLs to CID attachments
|
|
37
|
+
* Useful for templates that pass images in data object
|
|
38
|
+
*
|
|
39
|
+
* @param data - Any data object that might contain data URLs
|
|
40
|
+
* @returns Object with modified data and attachments
|
|
41
|
+
*/
|
|
42
|
+
function convertObjectDataUrlsToCID(data) {
|
|
43
|
+
const attachments = [];
|
|
44
|
+
let imageCount = 0;
|
|
45
|
+
function processValue(value) {
|
|
46
|
+
if (typeof value === 'string' && value.startsWith('data:image')) {
|
|
47
|
+
const match = value.match(/data:image\/(png|jpeg|jpg|gif|webp);base64,([A-Za-z0-9+/=]+)/);
|
|
48
|
+
if (match) {
|
|
49
|
+
const [, imageType, base64Content] = match;
|
|
50
|
+
const cid = `image-${imageCount++}`;
|
|
51
|
+
attachments.push({
|
|
52
|
+
Name: `${cid}.${imageType}`,
|
|
53
|
+
Content: base64Content,
|
|
54
|
+
ContentType: `image/${imageType}`,
|
|
55
|
+
ContentID: `cid:${cid}`,
|
|
56
|
+
});
|
|
57
|
+
return `cid:${cid}`;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if (Array.isArray(value)) {
|
|
61
|
+
return value.map(processValue);
|
|
62
|
+
}
|
|
63
|
+
if (value && typeof value === 'object') {
|
|
64
|
+
return Object.fromEntries(Object.entries(value).map(([k, v]) => [k, processValue(v)]));
|
|
65
|
+
}
|
|
66
|
+
return value;
|
|
67
|
+
}
|
|
68
|
+
return {
|
|
69
|
+
data: processValue(data),
|
|
70
|
+
attachments,
|
|
71
|
+
};
|
|
72
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@cb4kas17/medusa-notification-postmark",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Generic Postmark notification provider for Medusa v2 - supports custom HTML and Postmark templates",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist",
|
|
9
|
+
"README.md",
|
|
10
|
+
"LICENSE"
|
|
11
|
+
],
|
|
12
|
+
"scripts": {
|
|
13
|
+
"build": "tsc",
|
|
14
|
+
"dev": "tsc --watch",
|
|
15
|
+
"prepublishOnly": "npm run build",
|
|
16
|
+
"test": "jest"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"medusa",
|
|
20
|
+
"medusa-plugin",
|
|
21
|
+
"medusa-v2",
|
|
22
|
+
"notification",
|
|
23
|
+
"postmark",
|
|
24
|
+
"email",
|
|
25
|
+
"notification-provider"
|
|
26
|
+
],
|
|
27
|
+
"author": "const-code",
|
|
28
|
+
"license": "MIT",
|
|
29
|
+
"peerDependencies": {
|
|
30
|
+
"@medusajs/framework": "^2.0.0",
|
|
31
|
+
"postmark": "^4.0.0"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@medusajs/framework": "^2.13.1",
|
|
35
|
+
"@types/jest": "^29.0.0",
|
|
36
|
+
"@types/node": "^20.19.33",
|
|
37
|
+
"jest": "^29.0.0",
|
|
38
|
+
"postmark": "^4.0.5",
|
|
39
|
+
"typescript": "^5.9.3"
|
|
40
|
+
},
|
|
41
|
+
"directories": {
|
|
42
|
+
"example": "examples"
|
|
43
|
+
}
|
|
44
|
+
}
|