@flink-app/email-plugin 2.0.0-alpha.92 → 2.0.0-alpha.94

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 CHANGED
@@ -1,5 +1,23 @@
1
1
  # @flink-app/email-plugin
2
2
 
3
+ ## 2.0.0-alpha.94
4
+
5
+ ### Patch Changes
6
+
7
+ - @flink-app/flink@2.0.0-alpha.94
8
+
9
+ ## 2.0.0-alpha.93
10
+
11
+ ### Minor Changes
12
+
13
+ - 64ae56f: feat(email-plugin): add Postmark provider
14
+
15
+ Add `postmarkClient` alongside the existing SMTP, SendGrid, AWS SES, and Flowmailer clients. Construct it with `{ serverToken, defaultMessageStream? }` and pass it as the `client` option to `emailPlugin`. Supports html/text bodies, cc/bcc, reply-to, attachments, message streams, tags, and metadata.
16
+
17
+ ### Patch Changes
18
+
19
+ - @flink-app/flink@2.0.0-alpha.93
20
+
3
21
  ## 2.0.0-alpha.92
4
22
 
5
23
  ### Patch Changes
package/dist/index.d.ts CHANGED
@@ -4,8 +4,10 @@ export { sendgridClient } from "./sendgridClient";
4
4
  export { smtpClient } from "./smtpClient";
5
5
  export { flowMailerClient } from "./flowmailerClient";
6
6
  export { sesClient } from "./sesClient";
7
+ export { postmarkClient } from "./postmarkClient";
7
8
  export type { email } from "./schemas/email";
8
9
  export type { emailSes } from "./schemas/emailSes";
10
+ export type { emailPostmark, PostmarkAttachment } from "./schemas/emailPostmark";
9
11
  export * from "./ses-types";
10
12
  export type emailPluginOptions = {
11
13
  /**
package/dist/index.js CHANGED
@@ -14,7 +14,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
- exports.emailPlugin = exports.sesClient = exports.flowMailerClient = exports.smtpClient = exports.sendgridClient = void 0;
17
+ exports.emailPlugin = exports.postmarkClient = exports.sesClient = exports.flowMailerClient = exports.smtpClient = exports.sendgridClient = void 0;
18
18
  var sendgridClient_1 = require("./sendgridClient");
19
19
  Object.defineProperty(exports, "sendgridClient", { enumerable: true, get: function () { return sendgridClient_1.sendgridClient; } });
20
20
  var smtpClient_1 = require("./smtpClient");
@@ -23,6 +23,8 @@ var flowmailerClient_1 = require("./flowmailerClient");
23
23
  Object.defineProperty(exports, "flowMailerClient", { enumerable: true, get: function () { return flowmailerClient_1.flowMailerClient; } });
24
24
  var sesClient_1 = require("./sesClient");
25
25
  Object.defineProperty(exports, "sesClient", { enumerable: true, get: function () { return sesClient_1.sesClient; } });
26
+ var postmarkClient_1 = require("./postmarkClient");
27
+ Object.defineProperty(exports, "postmarkClient", { enumerable: true, get: function () { return postmarkClient_1.postmarkClient; } });
26
28
  __exportStar(require("./ses-types"), exports);
27
29
  var emailPlugin = function (options) {
28
30
  return {
@@ -0,0 +1,19 @@
1
+ import { ServerClient } from "postmark";
2
+ import { emailPostmark as email } from "./schemas/emailPostmark";
3
+ import { client } from "./schemas/client";
4
+ export interface postmarkClientOptions {
5
+ /**
6
+ * Postmark server token
7
+ */
8
+ serverToken: string;
9
+ /**
10
+ * Default Postmark message stream, defaults to "outbound"
11
+ */
12
+ defaultMessageStream?: string;
13
+ }
14
+ export declare class postmarkClient implements client {
15
+ postmark: ServerClient;
16
+ defaultMessageStream: string;
17
+ constructor(options: postmarkClientOptions);
18
+ send(email: email): Promise<boolean>;
19
+ }
@@ -0,0 +1,91 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __generator = (this && this.__generator) || function (thisArg, body) {
12
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
13
+ return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
14
+ function verb(n) { return function (v) { return step([n, v]); }; }
15
+ function step(op) {
16
+ if (f) throw new TypeError("Generator is already executing.");
17
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
18
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
19
+ if (y = 0, t) op = [op[0] & 2, t.value];
20
+ switch (op[0]) {
21
+ case 0: case 1: t = op; break;
22
+ case 4: _.label++; return { value: op[1], done: false };
23
+ case 5: _.label++; y = op[1]; op = [0]; continue;
24
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
25
+ default:
26
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
27
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
28
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
29
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
30
+ if (t[2]) _.ops.pop();
31
+ _.trys.pop(); continue;
32
+ }
33
+ op = body.call(thisArg, _);
34
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
35
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
36
+ }
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.postmarkClient = void 0;
40
+ var postmark_1 = require("postmark");
41
+ var postmarkClient = /** @class */ (function () {
42
+ function postmarkClient(options) {
43
+ var _a;
44
+ this.postmark = new postmark_1.ServerClient(options.serverToken);
45
+ this.defaultMessageStream = (_a = options.defaultMessageStream) !== null && _a !== void 0 ? _a : "outbound";
46
+ }
47
+ postmarkClient.prototype.send = function (email) {
48
+ return __awaiter(this, void 0, void 0, function () {
49
+ var ex_1;
50
+ var _a, _b, _c, _d;
51
+ return __generator(this, function (_e) {
52
+ switch (_e.label) {
53
+ case 0:
54
+ _e.trys.push([0, 2, , 3]);
55
+ return [4 /*yield*/, this.postmark.sendEmail({
56
+ From: email.from,
57
+ To: email.to.join(","),
58
+ Cc: (_a = email.cc) === null || _a === void 0 ? void 0 : _a.join(","),
59
+ Bcc: (_b = email.bcc) === null || _b === void 0 ? void 0 : _b.join(","),
60
+ ReplyTo: email.replyTo,
61
+ Subject: email.subject,
62
+ HtmlBody: "html" in email ? email.html : undefined,
63
+ TextBody: "text" in email ? email.text : undefined,
64
+ MessageStream: (_c = email.messageStream) !== null && _c !== void 0 ? _c : this.defaultMessageStream,
65
+ Tag: email.tag,
66
+ Metadata: email.metadata,
67
+ Attachments: (_d = email.attachments) === null || _d === void 0 ? void 0 : _d.map(function (a) {
68
+ var _a, _b;
69
+ return ({
70
+ Name: a.filename,
71
+ Content: a.content,
72
+ ContentType: (_a = a.contentType) !== null && _a !== void 0 ? _a : "application/octet-stream",
73
+ ContentID: (_b = a.contentId) !== null && _b !== void 0 ? _b : "",
74
+ });
75
+ }),
76
+ })];
77
+ case 1:
78
+ _e.sent();
79
+ return [3 /*break*/, 3];
80
+ case 2:
81
+ ex_1 = _e.sent();
82
+ console.log(JSON.stringify(ex_1));
83
+ return [2 /*return*/, false];
84
+ case 3: return [2 /*return*/, true];
85
+ }
86
+ });
87
+ });
88
+ };
89
+ return postmarkClient;
90
+ }());
91
+ exports.postmarkClient = postmarkClient;
@@ -1,7 +1,8 @@
1
1
  import { email } from "./email";
2
2
  import { emailFlowmailer } from "./emailFlowmailer";
3
+ import { emailPostmark } from "./emailPostmark";
3
4
  import { emailSendgrid } from "./emailSendgrid";
4
5
  import { emailSes } from "./emailSes";
5
6
  export interface client {
6
- send(email: email | emailSendgrid | emailFlowmailer | emailSes): Promise<boolean>;
7
+ send(email: email | emailSendgrid | emailFlowmailer | emailSes | emailPostmark): Promise<boolean>;
7
8
  }
@@ -0,0 +1,67 @@
1
+ export type emailPostmark = {
2
+ /**
3
+ * From address used to send the email
4
+ */
5
+ from: string;
6
+ /**
7
+ * Email addresses to send to
8
+ */
9
+ to: string[];
10
+ /**
11
+ * Reply-to address
12
+ */
13
+ replyTo?: string;
14
+ /**
15
+ * CC addresses
16
+ */
17
+ cc?: string[];
18
+ /**
19
+ * BCC addresses
20
+ */
21
+ bcc?: string[];
22
+ /**
23
+ * Subject of email
24
+ */
25
+ subject: string;
26
+ /**
27
+ * Postmark message stream id, defaults to "outbound"
28
+ */
29
+ messageStream?: string;
30
+ /**
31
+ * Postmark tag for categorization
32
+ */
33
+ tag?: string;
34
+ /**
35
+ * Custom metadata sent along with the email
36
+ */
37
+ metadata?: Record<string, string>;
38
+ /**
39
+ * File attachments
40
+ */
41
+ attachments?: PostmarkAttachment[];
42
+ } & ({
43
+ text: string;
44
+ } | {
45
+ html: string;
46
+ } | {
47
+ text: string;
48
+ html: string;
49
+ });
50
+ export interface PostmarkAttachment {
51
+ /**
52
+ * Base64 encoded content
53
+ */
54
+ content: string;
55
+ /**
56
+ * Filename shown to recipient
57
+ */
58
+ filename: string;
59
+ /**
60
+ * MIME content type (e.g. "application/pdf"). Defaults to "application/octet-stream"
61
+ */
62
+ contentType?: string;
63
+ /**
64
+ * Content-ID for inline attachments, e.g. "cid:image1"
65
+ */
66
+ contentId?: string;
67
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@flink-app/email-plugin",
3
- "version": "2.0.0-alpha.92",
4
- "description": "Flink plugin that provides multi-provider email sending capabilities with support for SendGrid, SMTP, Flowmailer, and AWS SES",
3
+ "version": "2.0.0-alpha.94",
4
+ "description": "Flink plugin that provides multi-provider email sending capabilities with support for SendGrid, SMTP, Flowmailer, AWS SES, and Postmark",
5
5
  "author": "johan@frost.se",
6
6
  "publishConfig": {
7
7
  "access": "public"
@@ -15,16 +15,17 @@
15
15
  "@types/nodemailer": "^6.4.4",
16
16
  "axios": "^0.27.2",
17
17
  "nodemailer": "^6.6.2",
18
+ "postmark": "^4.0.7",
18
19
  "querystring": "^0.2.1"
19
20
  },
20
21
  "devDependencies": {
21
22
  "@types/jasmine": "^3.7.1",
22
23
  "@types/node": "22.13.10",
23
- "@flink-app/flink": "2.0.0-alpha.92"
24
+ "@flink-app/flink": "2.0.0-alpha.94"
24
25
  },
25
26
  "gitHead": "4243e3b3cd6d4e1ca001a61baa8436bf2bbe4113",
26
27
  "peerDependencies": {
27
- "@flink-app/flink": ">=2.0.0-alpha.92"
28
+ "@flink-app/flink": ">=2.0.0-alpha.94"
28
29
  },
29
30
  "scripts": {
30
31
  "test": "jasmine-ts --config=./spec/support/jasmine.json",
package/readme.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Email Plugin
2
2
 
3
- A Flink plugin that provides multi-provider email sending capabilities with support for SendGrid, SMTP, Flowmailer, and AWS SES.
3
+ A Flink plugin that provides multi-provider email sending capabilities with support for SendGrid, SMTP, Flowmailer, AWS SES, and Postmark.
4
4
 
5
5
  ## Installation
6
6
 
@@ -12,7 +12,7 @@ npm install @flink-app/email-plugin
12
12
 
13
13
  ## Configuration
14
14
 
15
- The email plugin supports four different email providers through a unified client interface:
15
+ The email plugin supports five different email providers through a unified client interface:
16
16
 
17
17
  ### Option 1: SendGrid
18
18
 
@@ -162,6 +162,61 @@ Credentials are resolved in this order:
162
162
  3. IAM instance profile / ECS task role
163
163
  4. AWS SSO / credential file
164
164
 
165
+ ### Option 5: Postmark
166
+
167
+ Configure the plugin with [Postmark](https://postmarkapp.com/):
168
+
169
+ ```typescript
170
+ import { emailPlugin, postmarkClient } from "@flink-app/email-plugin";
171
+
172
+ function start() {
173
+ new FlinkApp<AppContext>({
174
+ name: "My app",
175
+ plugins: [
176
+ emailPlugin({
177
+ client: new postmarkClient({
178
+ serverToken: process.env.POSTMARK_SERVER_TOKEN!,
179
+ defaultMessageStream: "outbound", // optional
180
+ })
181
+ })
182
+ ],
183
+ }).start();
184
+ }
185
+ ```
186
+
187
+ **Postmark Client Options:**
188
+
189
+ ```typescript
190
+ interface postmarkClientOptions {
191
+ serverToken: string; // Postmark server token
192
+ defaultMessageStream?: string; // Defaults to "outbound"
193
+ }
194
+ ```
195
+
196
+ **Postmark Email Type:**
197
+
198
+ ```typescript
199
+ type emailPostmark = {
200
+ from: string;
201
+ to: string[];
202
+ replyTo?: string;
203
+ cc?: string[];
204
+ bcc?: string[];
205
+ subject: string;
206
+ messageStream?: string; // Overrides defaultMessageStream
207
+ tag?: string; // Postmark tag for categorization
208
+ metadata?: Record<string, string>; // Custom metadata
209
+ attachments?: PostmarkAttachment[];
210
+ } & ({ text: string } | { html: string } | { text: string; html: string });
211
+
212
+ interface PostmarkAttachment {
213
+ content: string; // Base64 encoded
214
+ filename: string;
215
+ contentType?: string; // Defaults to "application/octet-stream"
216
+ contentId?: string; // For inline images, e.g. "cid:image1"
217
+ }
218
+ ```
219
+
165
220
  ## TypeScript Setup
166
221
 
167
222
  Add the plugin context to your app's context type (usually in `Ctx.ts`):
@@ -350,7 +405,7 @@ All email clients implement this unified interface:
350
405
 
351
406
  ```typescript
352
407
  interface client {
353
- send(email: email | emailSendgrid | emailFlowmailer | emailSes): Promise<boolean>;
408
+ send(email: email | emailSendgrid | emailFlowmailer | emailSes | emailPostmark): Promise<boolean>;
354
409
  }
355
410
  ```
356
411
 
@@ -602,4 +657,5 @@ aws sesv2 create-configuration-set --configuration-set-name my-tracking-set --re
602
657
  - Flowmailer automatically handles OAuth token refresh
603
658
  - SendGrid attachments must be base64 encoded
604
659
  - SMTP client uses nodemailer internally
660
+ - Postmark attachments must be base64 encoded; supports `messageStream`, `tag`, and `metadata` Postmark-specific fields
605
661
  - Multiple recipients are supported by all clients
package/src/index.ts CHANGED
@@ -4,8 +4,10 @@ export { sendgridClient } from "./sendgridClient";
4
4
  export { smtpClient } from "./smtpClient";
5
5
  export { flowMailerClient } from "./flowmailerClient";
6
6
  export { sesClient } from "./sesClient";
7
+ export { postmarkClient } from "./postmarkClient";
7
8
  export type { email } from "./schemas/email";
8
9
  export type { emailSes } from "./schemas/emailSes";
10
+ export type { emailPostmark, PostmarkAttachment } from "./schemas/emailPostmark";
9
11
  export * from "./ses-types";
10
12
 
11
13
  export type emailPluginOptions = {
@@ -0,0 +1,53 @@
1
+ import { ServerClient } from "postmark";
2
+ import { emailPostmark as email } from "./schemas/emailPostmark";
3
+ import { client } from "./schemas/client";
4
+
5
+ export interface postmarkClientOptions {
6
+ /**
7
+ * Postmark server token
8
+ */
9
+ serverToken: string;
10
+
11
+ /**
12
+ * Default Postmark message stream, defaults to "outbound"
13
+ */
14
+ defaultMessageStream?: string;
15
+ }
16
+
17
+ export class postmarkClient implements client {
18
+ postmark: ServerClient;
19
+ defaultMessageStream: string;
20
+
21
+ constructor(options: postmarkClientOptions) {
22
+ this.postmark = new ServerClient(options.serverToken);
23
+ this.defaultMessageStream = options.defaultMessageStream ?? "outbound";
24
+ }
25
+
26
+ async send(email: email) {
27
+ try {
28
+ await this.postmark.sendEmail({
29
+ From: email.from,
30
+ To: email.to.join(","),
31
+ Cc: email.cc?.join(","),
32
+ Bcc: email.bcc?.join(","),
33
+ ReplyTo: email.replyTo,
34
+ Subject: email.subject,
35
+ HtmlBody: "html" in email ? email.html : undefined,
36
+ TextBody: "text" in email ? email.text : undefined,
37
+ MessageStream: email.messageStream ?? this.defaultMessageStream,
38
+ Tag: email.tag,
39
+ Metadata: email.metadata,
40
+ Attachments: email.attachments?.map(a => ({
41
+ Name: a.filename,
42
+ Content: a.content,
43
+ ContentType: a.contentType ?? "application/octet-stream",
44
+ ContentID: a.contentId ?? "",
45
+ })),
46
+ });
47
+ } catch (ex) {
48
+ console.log(JSON.stringify(ex));
49
+ return false;
50
+ }
51
+ return true;
52
+ }
53
+ }
@@ -1,8 +1,9 @@
1
1
  import { email } from "./email";
2
2
  import { emailFlowmailer } from "./emailFlowmailer";
3
+ import { emailPostmark } from "./emailPostmark";
3
4
  import { emailSendgrid } from "./emailSendgrid";
4
5
  import { emailSes } from "./emailSes";
5
6
 
6
7
  export interface client {
7
- send(email: email | emailSendgrid | emailFlowmailer | emailSes): Promise<boolean>;
8
+ send(email: email | emailSendgrid | emailFlowmailer | emailSes | emailPostmark): Promise<boolean>;
8
9
  }
@@ -0,0 +1,73 @@
1
+ export type emailPostmark = {
2
+ /**
3
+ * From address used to send the email
4
+ */
5
+ from: string;
6
+
7
+ /**
8
+ * Email addresses to send to
9
+ */
10
+ to: string[];
11
+
12
+ /**
13
+ * Reply-to address
14
+ */
15
+ replyTo?: string;
16
+
17
+ /**
18
+ * CC addresses
19
+ */
20
+ cc?: string[];
21
+
22
+ /**
23
+ * BCC addresses
24
+ */
25
+ bcc?: string[];
26
+
27
+ /**
28
+ * Subject of email
29
+ */
30
+ subject: string;
31
+
32
+ /**
33
+ * Postmark message stream id, defaults to "outbound"
34
+ */
35
+ messageStream?: string;
36
+
37
+ /**
38
+ * Postmark tag for categorization
39
+ */
40
+ tag?: string;
41
+
42
+ /**
43
+ * Custom metadata sent along with the email
44
+ */
45
+ metadata?: Record<string, string>;
46
+
47
+ /**
48
+ * File attachments
49
+ */
50
+ attachments?: PostmarkAttachment[];
51
+ } & ({ text: string } | { html: string } | { text: string; html: string });
52
+
53
+ export interface PostmarkAttachment {
54
+ /**
55
+ * Base64 encoded content
56
+ */
57
+ content: string;
58
+
59
+ /**
60
+ * Filename shown to recipient
61
+ */
62
+ filename: string;
63
+
64
+ /**
65
+ * MIME content type (e.g. "application/pdf"). Defaults to "application/octet-stream"
66
+ */
67
+ contentType?: string;
68
+
69
+ /**
70
+ * Content-ID for inline attachments, e.g. "cid:image1"
71
+ */
72
+ contentId?: string;
73
+ }