@mailhooks/sdk 1.1.3 → 2.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/dist/index.d.ts CHANGED
@@ -1,7 +1,273 @@
1
- export { Mailhooks } from './mailhooks';
2
- export { EmailsResource } from './resources/emails';
3
- export * from './types';
4
- export { verifyWebhookSignature, parseWebhookPayload, constructSignature, type WebhookPayload, } from './webhooks';
5
- import { Mailhooks } from './mailhooks';
6
- export default Mailhooks;
7
- //# sourceMappingURL=index.d.ts.map
1
+ import { AxiosInstance } from 'axios';
2
+
3
+ interface Attachment {
4
+ id: string;
5
+ filename: string;
6
+ contentType: string;
7
+ size: number;
8
+ }
9
+ interface Email {
10
+ id: string;
11
+ from: string;
12
+ to: string[];
13
+ subject: string;
14
+ read: boolean;
15
+ createdAt: Date;
16
+ attachments: Attachment[];
17
+ }
18
+ interface EmailContent {
19
+ html?: string;
20
+ text?: string;
21
+ }
22
+ interface PaginationResponse {
23
+ currentPage: number;
24
+ perPage: number;
25
+ totalItems: number;
26
+ totalPages: number;
27
+ hasNextPage: boolean;
28
+ nextCursor?: string;
29
+ }
30
+ interface EmailsResponse extends PaginationResponse {
31
+ data: Email[];
32
+ }
33
+ interface EmailFilter {
34
+ from?: string;
35
+ to?: string;
36
+ subject?: string;
37
+ startDate?: string;
38
+ endDate?: string;
39
+ read?: boolean;
40
+ }
41
+ interface EmailSort {
42
+ field?: 'createdAt' | 'from' | 'subject';
43
+ order?: 'asc' | 'desc';
44
+ }
45
+ interface EmailListParams {
46
+ page?: number;
47
+ perPage?: number;
48
+ filter?: EmailFilter;
49
+ sort?: EmailSort;
50
+ }
51
+ interface MailhooksConfig {
52
+ apiKey: string;
53
+ baseUrl?: string;
54
+ }
55
+ interface DownloadResponse {
56
+ data: ArrayBuffer;
57
+ filename?: string;
58
+ contentType?: string;
59
+ }
60
+ interface WaitForOptions {
61
+ filter?: EmailFilter;
62
+ timeout?: number;
63
+ pollInterval?: number;
64
+ maxRetries?: number;
65
+ initialDelay?: number;
66
+ lookbackWindow?: number;
67
+ }
68
+
69
+ declare class MailhooksClient {
70
+ private http;
71
+ constructor(config: MailhooksConfig);
72
+ private parseDates;
73
+ protected get<T>(path: string, params?: Record<string, any>): Promise<T>;
74
+ protected post<T>(path: string, data?: any): Promise<T>;
75
+ protected put<T>(path: string, data?: any): Promise<T>;
76
+ protected patch<T>(path: string, data?: any): Promise<T>;
77
+ protected delete<T>(path: string): Promise<T>;
78
+ protected downloadFile(path: string): Promise<ArrayBuffer>;
79
+ protected getAxiosInstance(): AxiosInstance;
80
+ }
81
+
82
+ declare class EmailsResource extends MailhooksClient {
83
+ /**
84
+ * Get a paginated list of emails
85
+ */
86
+ list(params?: EmailListParams): Promise<EmailsResponse>;
87
+ /**
88
+ * Get a specific email by ID
89
+ * @param emailId - The ID of the email to retrieve
90
+ * @param markAsRead - Optional: Mark the email as read when fetching (default: false)
91
+ */
92
+ getEmail(emailId: string, markAsRead?: boolean): Promise<Email>;
93
+ /**
94
+ * Get the HTML and text content of an email
95
+ */
96
+ getContent(emailId: string): Promise<EmailContent>;
97
+ /**
98
+ * Download email in EML format
99
+ */
100
+ downloadEml(emailId: string): Promise<DownloadResponse>;
101
+ /**
102
+ * Download a specific attachment from an email
103
+ */
104
+ downloadAttachment(emailId: string, attachmentId: string): Promise<DownloadResponse>;
105
+ /**
106
+ * Mark an email as read
107
+ */
108
+ markAsRead(emailId: string): Promise<Email>;
109
+ /**
110
+ * Mark an email as unread
111
+ */
112
+ markAsUnread(emailId: string): Promise<Email>;
113
+ /**
114
+ * Wait for an email that matches the given filters
115
+ *
116
+ * @param options - Options for waiting including filters, timeouts, and delays
117
+ * @returns The first email that matches the filters
118
+ * @throws Error if timeout is reached or max retries exceeded
119
+ *
120
+ * @example
121
+ * // Wait for an email from a specific sender (only considers emails from last 10 seconds)
122
+ * const email = await mailhooks.emails.waitFor({
123
+ * filter: { from: 'test@example.com' },
124
+ * timeout: 30000, // 30 seconds
125
+ * pollInterval: 2000, // Check every 2 seconds
126
+ * lookbackWindow: 10000, // Only consider emails from last 10 seconds
127
+ * });
128
+ *
129
+ * @example
130
+ * // Wait with initial delay (useful when you know email will take time)
131
+ * const email = await mailhooks.emails.waitFor({
132
+ * filter: { subject: 'Order Confirmation' },
133
+ * initialDelay: 5000, // Wait 5 seconds before first check
134
+ * timeout: 60000,
135
+ * lookbackWindow: 5000, // Only consider very recent emails
136
+ * });
137
+ */
138
+ waitFor(options?: WaitForOptions): Promise<Email>;
139
+ }
140
+
141
+ declare class Mailhooks {
142
+ emails: EmailsResource;
143
+ constructor(config: MailhooksConfig);
144
+ /**
145
+ * Create a new Mailhooks SDK instance
146
+ */
147
+ static create(config: MailhooksConfig): Mailhooks;
148
+ }
149
+
150
+ /**
151
+ * Webhook payload sent by Mailhooks when an email is received.
152
+ */
153
+ interface WebhookPayload {
154
+ /** Unique email ID */
155
+ id: string;
156
+ /** Sender email address */
157
+ from: string;
158
+ /** Array of recipient email addresses */
159
+ to: string[];
160
+ /** Email subject line */
161
+ subject: string;
162
+ /** Plain text body of the email */
163
+ body: string;
164
+ /** HTML body of the email (if available) */
165
+ html?: string;
166
+ /** Array of attachment metadata */
167
+ attachments: Array<{
168
+ filename: string;
169
+ contentType: string;
170
+ size: number;
171
+ }>;
172
+ /** ISO 8601 timestamp when the email was received */
173
+ receivedAt: string;
174
+ /** SPF authentication result */
175
+ spfResult?: 'pass' | 'fail' | 'softfail' | 'neutral' | 'none';
176
+ /** DKIM authentication result */
177
+ dkimResult?: 'pass' | 'fail' | 'none' | 'temperror' | 'permerror';
178
+ /** DMARC authentication result */
179
+ dmarcResult?: 'pass' | 'fail' | 'none' | 'temperror' | 'permerror';
180
+ /** Overall authentication summary */
181
+ authSummary?: 'pass' | 'fail' | 'partial';
182
+ /** Email headers as key-value pairs */
183
+ headers?: Record<string, string>;
184
+ /** Authentication diagnostic details */
185
+ authDiagnostics?: {
186
+ spf: {
187
+ clientIp: string;
188
+ domain: string;
189
+ record?: string;
190
+ helo?: string;
191
+ } | null;
192
+ dkim: Array<{
193
+ domain: string;
194
+ selector?: string;
195
+ algorithm?: string;
196
+ aligned?: boolean;
197
+ result: string;
198
+ }>;
199
+ dmarc: {
200
+ domain: string;
201
+ policy: string;
202
+ record?: string;
203
+ alignment: {
204
+ spf: {
205
+ result: string | false;
206
+ strict: boolean;
207
+ };
208
+ dkim: {
209
+ result: string | false;
210
+ strict: boolean;
211
+ };
212
+ };
213
+ } | null;
214
+ };
215
+ }
216
+ /**
217
+ * Verifies a webhook signature using HMAC-SHA256.
218
+ *
219
+ * Each webhook request from Mailhooks includes an `X-Webhook-Signature` header
220
+ * containing a hex-encoded HMAC-SHA256 signature of the request body.
221
+ *
222
+ * @param payload - The raw request body as a string or Buffer
223
+ * @param signature - The signature from the `X-Webhook-Signature` header
224
+ * @param secret - Your webhook secret (starts with `whsec_`)
225
+ * @returns `true` if the signature is valid, `false` otherwise
226
+ *
227
+ * @example
228
+ * ```typescript
229
+ * import { verifyWebhookSignature } from '@mailhooks/sdk';
230
+ *
231
+ * // Express.js with raw body parser
232
+ * app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
233
+ * const signature = req.headers['x-webhook-signature'] as string;
234
+ * const payload = req.body.toString();
235
+ *
236
+ * if (!verifyWebhookSignature(payload, signature, process.env.WEBHOOK_SECRET!)) {
237
+ * return res.status(401).send('Invalid signature');
238
+ * }
239
+ *
240
+ * const event = JSON.parse(payload);
241
+ * // Process the webhook...
242
+ * res.status(200).send('OK');
243
+ * });
244
+ * ```
245
+ */
246
+ declare function verifyWebhookSignature(payload: string | Buffer, signature: string, secret: string): boolean;
247
+ /**
248
+ * Parses a webhook payload from a JSON string.
249
+ *
250
+ * @param body - The raw request body as a string
251
+ * @returns The parsed webhook payload
252
+ * @throws {SyntaxError} If the body is not valid JSON
253
+ *
254
+ * @example
255
+ * ```typescript
256
+ * import { parseWebhookPayload } from '@mailhooks/sdk';
257
+ *
258
+ * const payload = parseWebhookPayload(req.body.toString());
259
+ * console.log(`Received email from ${payload.from}: ${payload.subject}`);
260
+ * ```
261
+ */
262
+ declare function parseWebhookPayload(body: string): WebhookPayload;
263
+ /**
264
+ * Constructs the expected signature for a webhook payload.
265
+ * Useful for debugging or manual verification.
266
+ *
267
+ * @param payload - The raw request body as a string or Buffer
268
+ * @param secret - Your webhook secret
269
+ * @returns The expected HMAC-SHA256 signature as a hex string
270
+ */
271
+ declare function constructSignature(payload: string | Buffer, secret: string): string;
272
+
273
+ export { type Attachment, type DownloadResponse, type Email, type EmailContent, type EmailFilter, type EmailListParams, type EmailSort, EmailsResource, type EmailsResponse, Mailhooks, type MailhooksConfig, type PaginationResponse, type WaitForOptions, type WebhookPayload, constructSignature, Mailhooks as default, parseWebhookPayload, verifyWebhookSignature };
package/dist/index.js CHANGED
@@ -1,30 +1,280 @@
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]; } };
1
+ import axios from 'axios';
2
+ import { createHmac, timingSafeEqual } from 'crypto';
3
+
4
+ // src/client.ts
5
+ var MailhooksClient = class {
6
+ constructor(config) {
7
+ this.http = axios.create({
8
+ baseURL: config.baseUrl ?? "https://mailhooks.dev/api",
9
+ headers: {
10
+ "x-api-key": config.apiKey,
11
+ "Content-Type": "application/json"
12
+ }
13
+ });
14
+ this.http.interceptors.response.use((response) => {
15
+ if (response.data) {
16
+ this.parseDates(response.data);
17
+ }
18
+ return response;
19
+ });
20
+ }
21
+ parseDates(obj) {
22
+ if (obj && typeof obj === "object") {
23
+ if (Array.isArray(obj)) {
24
+ obj.forEach((item) => this.parseDates(item));
25
+ } else {
26
+ Object.keys(obj).forEach((key) => {
27
+ if (key === "createdAt" || key === "updatedAt") {
28
+ if (typeof obj[key] === "string") {
29
+ obj[key] = new Date(obj[key]);
30
+ }
31
+ } else if (typeof obj[key] === "object") {
32
+ this.parseDates(obj[key]);
33
+ }
34
+ });
35
+ }
7
36
  }
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);
37
+ }
38
+ async get(path, params) {
39
+ const response = await this.http.get(path, { params });
40
+ return response.data;
41
+ }
42
+ async post(path, data) {
43
+ const response = await this.http.post(path, data);
44
+ return response.data;
45
+ }
46
+ async put(path, data) {
47
+ const response = await this.http.put(path, data);
48
+ return response.data;
49
+ }
50
+ async patch(path, data) {
51
+ const response = await this.http.patch(path, data);
52
+ return response.data;
53
+ }
54
+ async delete(path) {
55
+ const response = await this.http.delete(path);
56
+ return response.data;
57
+ }
58
+ async downloadFile(path) {
59
+ const response = await this.http.get(path, {
60
+ responseType: "arraybuffer"
61
+ });
62
+ return response.data;
63
+ }
64
+ getAxiosInstance() {
65
+ return this.http;
66
+ }
15
67
  };
16
- Object.defineProperty(exports, "__esModule", { value: true });
17
- exports.constructSignature = exports.parseWebhookPayload = exports.verifyWebhookSignature = exports.EmailsResource = exports.Mailhooks = void 0;
18
- var mailhooks_1 = require("./mailhooks");
19
- Object.defineProperty(exports, "Mailhooks", { enumerable: true, get: function () { return mailhooks_1.Mailhooks; } });
20
- var emails_1 = require("./resources/emails");
21
- Object.defineProperty(exports, "EmailsResource", { enumerable: true, get: function () { return emails_1.EmailsResource; } });
22
- __exportStar(require("./types"), exports);
23
- // Webhook verification utilities
24
- var webhooks_1 = require("./webhooks");
25
- Object.defineProperty(exports, "verifyWebhookSignature", { enumerable: true, get: function () { return webhooks_1.verifyWebhookSignature; } });
26
- Object.defineProperty(exports, "parseWebhookPayload", { enumerable: true, get: function () { return webhooks_1.parseWebhookPayload; } });
27
- Object.defineProperty(exports, "constructSignature", { enumerable: true, get: function () { return webhooks_1.constructSignature; } });
28
- // Default export for convenience
29
- const mailhooks_2 = require("./mailhooks");
30
- exports.default = mailhooks_2.Mailhooks;
68
+
69
+ // src/resources/emails.ts
70
+ var EmailsResource = class extends MailhooksClient {
71
+ /**
72
+ * Get a paginated list of emails
73
+ */
74
+ async list(params) {
75
+ const queryParams = {
76
+ page: params?.page || 1,
77
+ perPage: params?.perPage || 20
78
+ };
79
+ if (params?.filter) {
80
+ if (params.filter.from) queryParams["filter.from"] = params.filter.from;
81
+ if (params.filter.to) queryParams["filter.to"] = params.filter.to;
82
+ if (params.filter.subject) queryParams["filter.subject"] = params.filter.subject;
83
+ if (params.filter.startDate) queryParams["filter.createdAfter"] = params.filter.startDate;
84
+ if (params.filter.endDate) queryParams["filter.createdBefore"] = params.filter.endDate;
85
+ if (params.filter.read !== void 0) queryParams["filter.read"] = String(params.filter.read);
86
+ }
87
+ if (params?.sort) {
88
+ if (params.sort.field) queryParams["sort[field]"] = params.sort.field;
89
+ if (params.sort.order) queryParams["sort[order]"] = params.sort.order;
90
+ }
91
+ return super.get("/v1/emails", queryParams);
92
+ }
93
+ /**
94
+ * Get a specific email by ID
95
+ * @param emailId - The ID of the email to retrieve
96
+ * @param markAsRead - Optional: Mark the email as read when fetching (default: false)
97
+ */
98
+ async getEmail(emailId, markAsRead = false) {
99
+ const params = markAsRead ? { markAsRead: "true" } : void 0;
100
+ return super.get(`/v1/emails/${emailId}`, params);
101
+ }
102
+ /**
103
+ * Get the HTML and text content of an email
104
+ */
105
+ async getContent(emailId) {
106
+ return super.get(`/v1/emails/${emailId}/content`);
107
+ }
108
+ /**
109
+ * Download email in EML format
110
+ */
111
+ async downloadEml(emailId) {
112
+ const data = await this.downloadFile(`/v1/emails/${emailId}/eml`);
113
+ return {
114
+ data,
115
+ filename: `email-${emailId}.eml`,
116
+ contentType: "message/rfc822"
117
+ };
118
+ }
119
+ /**
120
+ * Download a specific attachment from an email
121
+ */
122
+ async downloadAttachment(emailId, attachmentId) {
123
+ const axios2 = this.getAxiosInstance();
124
+ const response = await axios2.get(
125
+ `/v1/emails/${emailId}/attachments/${attachmentId}`,
126
+ {
127
+ responseType: "arraybuffer"
128
+ }
129
+ );
130
+ const contentDisposition = response.headers["content-disposition"];
131
+ const filename = contentDisposition ? contentDisposition.split("filename=")[1]?.replace(/['"]/g, "") : `attachment-${attachmentId}`;
132
+ return {
133
+ data: response.data,
134
+ filename,
135
+ contentType: response.headers["content-type"]
136
+ };
137
+ }
138
+ /**
139
+ * Mark an email as read
140
+ */
141
+ async markAsRead(emailId) {
142
+ return super.patch(`/v1/emails/${emailId}/read`);
143
+ }
144
+ /**
145
+ * Mark an email as unread
146
+ */
147
+ async markAsUnread(emailId) {
148
+ return super.patch(`/v1/emails/${emailId}/unread`);
149
+ }
150
+ /**
151
+ * Wait for an email that matches the given filters
152
+ *
153
+ * @param options - Options for waiting including filters, timeouts, and delays
154
+ * @returns The first email that matches the filters
155
+ * @throws Error if timeout is reached or max retries exceeded
156
+ *
157
+ * @example
158
+ * // Wait for an email from a specific sender (only considers emails from last 10 seconds)
159
+ * const email = await mailhooks.emails.waitFor({
160
+ * filter: { from: 'test@example.com' },
161
+ * timeout: 30000, // 30 seconds
162
+ * pollInterval: 2000, // Check every 2 seconds
163
+ * lookbackWindow: 10000, // Only consider emails from last 10 seconds
164
+ * });
165
+ *
166
+ * @example
167
+ * // Wait with initial delay (useful when you know email will take time)
168
+ * const email = await mailhooks.emails.waitFor({
169
+ * filter: { subject: 'Order Confirmation' },
170
+ * initialDelay: 5000, // Wait 5 seconds before first check
171
+ * timeout: 60000,
172
+ * lookbackWindow: 5000, // Only consider very recent emails
173
+ * });
174
+ */
175
+ async waitFor(options = {}) {
176
+ const {
177
+ filter = {},
178
+ timeout = 3e4,
179
+ // Default 30 seconds
180
+ pollInterval = 1e3,
181
+ // Default poll every 1 second
182
+ maxRetries = null,
183
+ initialDelay = 0,
184
+ lookbackWindow = 1e4
185
+ // Default 10 seconds lookback
186
+ } = options;
187
+ const startTime = Date.now();
188
+ let retries = 0;
189
+ let lastCheckedTime = null;
190
+ const checkForEmail = async (isFirstCheck = false) => {
191
+ try {
192
+ const now = /* @__PURE__ */ new Date();
193
+ let startDateFilter;
194
+ if (isFirstCheck && lookbackWindow) {
195
+ startDateFilter = new Date(now.getTime() - lookbackWindow).toISOString();
196
+ } else if (lastCheckedTime) {
197
+ startDateFilter = lastCheckedTime.toISOString();
198
+ } else {
199
+ startDateFilter = new Date(now.getTime() - lookbackWindow).toISOString();
200
+ }
201
+ const searchFilter = {
202
+ ...filter,
203
+ startDate: startDateFilter
204
+ };
205
+ const response = await this.list({
206
+ filter: searchFilter,
207
+ perPage: 10,
208
+ sort: { field: "createdAt", order: "desc" }
209
+ });
210
+ lastCheckedTime = now;
211
+ if (response.data.length > 0) {
212
+ return response.data[0];
213
+ }
214
+ return null;
215
+ } catch (error) {
216
+ console.warn("Error checking for email:", error);
217
+ return null;
218
+ }
219
+ };
220
+ const existingEmail = await checkForEmail(true);
221
+ if (existingEmail) {
222
+ return existingEmail;
223
+ }
224
+ if (initialDelay > 0) {
225
+ await new Promise((resolve) => setTimeout(resolve, initialDelay));
226
+ }
227
+ while (true) {
228
+ if (timeout && Date.now() - startTime > timeout) {
229
+ throw new Error(`Timeout waiting for email after ${timeout}ms`);
230
+ }
231
+ if (maxRetries !== null && retries >= maxRetries) {
232
+ throw new Error(`Max retries (${maxRetries}) exceeded waiting for email`);
233
+ }
234
+ const email = await checkForEmail();
235
+ if (email) {
236
+ return email;
237
+ }
238
+ await new Promise((resolve) => setTimeout(resolve, pollInterval));
239
+ retries++;
240
+ }
241
+ }
242
+ };
243
+
244
+ // src/mailhooks.ts
245
+ var Mailhooks = class _Mailhooks {
246
+ constructor(config) {
247
+ this.emails = new EmailsResource(config);
248
+ }
249
+ /**
250
+ * Create a new Mailhooks SDK instance
251
+ */
252
+ static create(config) {
253
+ return new _Mailhooks(config);
254
+ }
255
+ };
256
+ function verifyWebhookSignature(payload, signature, secret) {
257
+ if (!payload || !signature || !secret) {
258
+ return false;
259
+ }
260
+ const normalizedSignature = signature.trim().toLowerCase();
261
+ if (!/^[a-f0-9]{64}$/.test(normalizedSignature)) {
262
+ return false;
263
+ }
264
+ const expectedSignature = createHmac("sha256", secret).update(payload).digest("hex");
265
+ return timingSafeEqual(
266
+ Buffer.from(normalizedSignature, "hex"),
267
+ Buffer.from(expectedSignature, "hex")
268
+ );
269
+ }
270
+ function parseWebhookPayload(body) {
271
+ return JSON.parse(body);
272
+ }
273
+ function constructSignature(payload, secret) {
274
+ return createHmac("sha256", secret).update(payload).digest("hex");
275
+ }
276
+
277
+ // src/index.ts
278
+ var index_default = Mailhooks;
279
+
280
+ export { EmailsResource, Mailhooks, constructSignature, index_default as default, parseWebhookPayload, verifyWebhookSignature };
package/package.json CHANGED
@@ -1,14 +1,15 @@
1
1
  {
2
2
  "name": "@mailhooks/sdk",
3
- "version": "1.1.3",
3
+ "version": "2.0.1",
4
4
  "description": "TypeScript SDK for Mailhooks API",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
8
+ "type": "module",
8
9
  "main": "dist/index.js",
9
10
  "types": "dist/index.d.ts",
10
11
  "scripts": {
11
- "build": "tsc -p .",
12
+ "build": "tsup",
12
13
  "dev": "tsc -p . --watch",
13
14
  "test": "echo \"No tests yet\"",
14
15
  "prepublishOnly": "pnpm build"
@@ -26,6 +27,7 @@
26
27
  "devDependencies": {
27
28
  "@types/node": "^20.19.0",
28
29
  "dotenv": "^17.2.1",
30
+ "tsup": "^8.5.1",
29
31
  "typescript": "^5.4.5"
30
32
  },
31
33
  "files": [
@@ -33,9 +35,9 @@
33
35
  ],
34
36
  "exports": {
35
37
  ".": {
38
+ "types": "./dist/index.d.ts",
36
39
  "import": "./dist/index.js",
37
- "require": "./dist/index.js",
38
- "types": "./dist/index.d.ts"
40
+ "default": "./dist/index.js"
39
41
  }
40
42
  }
41
43
  }
package/dist/client.d.ts DELETED
@@ -1,15 +0,0 @@
1
- import { AxiosInstance } from 'axios';
2
- import { MailhooksConfig } from './types';
3
- export declare class MailhooksClient {
4
- private http;
5
- constructor(config: MailhooksConfig);
6
- private parseDates;
7
- protected get<T>(path: string, params?: Record<string, any>): Promise<T>;
8
- protected post<T>(path: string, data?: any): Promise<T>;
9
- protected put<T>(path: string, data?: any): Promise<T>;
10
- protected patch<T>(path: string, data?: any): Promise<T>;
11
- protected delete<T>(path: string): Promise<T>;
12
- protected downloadFile(path: string): Promise<ArrayBuffer>;
13
- protected getAxiosInstance(): AxiosInstance;
14
- }
15
- //# sourceMappingURL=client.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAc,EAAE,aAAa,EAAiB,MAAM,OAAO,CAAC;AAC5D,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE1C,qBAAa,eAAe;IAC1B,OAAO,CAAC,IAAI,CAAgB;gBAEhB,MAAM,EAAE,eAAe;IAkBnC,OAAO,CAAC,UAAU;cAkBF,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;cAK9D,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,CAAC,CAAC;cAK7C,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,CAAC,CAAC;cAK5C,KAAK,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,CAAC,CAAC;cAK9C,MAAM,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC;cAKnC,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IAOhE,SAAS,CAAC,gBAAgB,IAAI,aAAa;CAG5C"}
package/dist/client.js DELETED
@@ -1,74 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.MailhooksClient = void 0;
7
- const axios_1 = __importDefault(require("axios"));
8
- class MailhooksClient {
9
- constructor(config) {
10
- this.http = axios_1.default.create({
11
- baseURL: config.baseUrl ?? 'https://mailhooks.dev/api',
12
- headers: {
13
- 'x-api-key': config.apiKey,
14
- 'Content-Type': 'application/json',
15
- },
16
- });
17
- // Response interceptor to handle date parsing
18
- this.http.interceptors.response.use((response) => {
19
- if (response.data) {
20
- this.parseDates(response.data);
21
- }
22
- return response;
23
- });
24
- }
25
- parseDates(obj) {
26
- if (obj && typeof obj === 'object') {
27
- if (Array.isArray(obj)) {
28
- obj.forEach(item => this.parseDates(item));
29
- }
30
- else {
31
- Object.keys(obj).forEach(key => {
32
- if (key === 'createdAt' || key === 'updatedAt') {
33
- if (typeof obj[key] === 'string') {
34
- obj[key] = new Date(obj[key]);
35
- }
36
- }
37
- else if (typeof obj[key] === 'object') {
38
- this.parseDates(obj[key]);
39
- }
40
- });
41
- }
42
- }
43
- }
44
- async get(path, params) {
45
- const response = await this.http.get(path, { params });
46
- return response.data;
47
- }
48
- async post(path, data) {
49
- const response = await this.http.post(path, data);
50
- return response.data;
51
- }
52
- async put(path, data) {
53
- const response = await this.http.put(path, data);
54
- return response.data;
55
- }
56
- async patch(path, data) {
57
- const response = await this.http.patch(path, data);
58
- return response.data;
59
- }
60
- async delete(path) {
61
- const response = await this.http.delete(path);
62
- return response.data;
63
- }
64
- async downloadFile(path) {
65
- const response = await this.http.get(path, {
66
- responseType: 'arraybuffer',
67
- });
68
- return response.data;
69
- }
70
- getAxiosInstance() {
71
- return this.http;
72
- }
73
- }
74
- exports.MailhooksClient = MailhooksClient;
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,cAAc,SAAS,CAAC;AAGxB,OAAO,EACL,sBAAsB,EACtB,mBAAmB,EACnB,kBAAkB,EAClB,KAAK,cAAc,GACpB,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,eAAe,SAAS,CAAC"}
@@ -1,11 +0,0 @@
1
- import { MailhooksConfig } from './types';
2
- import { EmailsResource } from './resources/emails';
3
- export declare class Mailhooks {
4
- emails: EmailsResource;
5
- constructor(config: MailhooksConfig);
6
- /**
7
- * Create a new Mailhooks SDK instance
8
- */
9
- static create(config: MailhooksConfig): Mailhooks;
10
- }
11
- //# sourceMappingURL=mailhooks.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"mailhooks.d.ts","sourceRoot":"","sources":["../src/mailhooks.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAC1C,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAEpD,qBAAa,SAAS;IACb,MAAM,EAAE,cAAc,CAAC;gBAElB,MAAM,EAAE,eAAe;IAInC;;OAEG;IACH,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,eAAe,GAAG,SAAS;CAGlD"}
package/dist/mailhooks.js DELETED
@@ -1,16 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Mailhooks = void 0;
4
- const emails_1 = require("./resources/emails");
5
- class Mailhooks {
6
- constructor(config) {
7
- this.emails = new emails_1.EmailsResource(config);
8
- }
9
- /**
10
- * Create a new Mailhooks SDK instance
11
- */
12
- static create(config) {
13
- return new Mailhooks(config);
14
- }
15
- }
16
- exports.Mailhooks = Mailhooks;
@@ -1,61 +0,0 @@
1
- import { MailhooksClient } from '../client';
2
- import { Email, EmailContent, EmailsResponse, EmailListParams, DownloadResponse, WaitForOptions } from '../types';
3
- export declare class EmailsResource extends MailhooksClient {
4
- /**
5
- * Get a paginated list of emails
6
- */
7
- list(params?: EmailListParams): Promise<EmailsResponse>;
8
- /**
9
- * Get a specific email by ID
10
- * @param emailId - The ID of the email to retrieve
11
- * @param markAsRead - Optional: Mark the email as read when fetching (default: false)
12
- */
13
- getEmail(emailId: string, markAsRead?: boolean): Promise<Email>;
14
- /**
15
- * Get the HTML and text content of an email
16
- */
17
- getContent(emailId: string): Promise<EmailContent>;
18
- /**
19
- * Download email in EML format
20
- */
21
- downloadEml(emailId: string): Promise<DownloadResponse>;
22
- /**
23
- * Download a specific attachment from an email
24
- */
25
- downloadAttachment(emailId: string, attachmentId: string): Promise<DownloadResponse>;
26
- /**
27
- * Mark an email as read
28
- */
29
- markAsRead(emailId: string): Promise<Email>;
30
- /**
31
- * Mark an email as unread
32
- */
33
- markAsUnread(emailId: string): Promise<Email>;
34
- /**
35
- * Wait for an email that matches the given filters
36
- *
37
- * @param options - Options for waiting including filters, timeouts, and delays
38
- * @returns The first email that matches the filters
39
- * @throws Error if timeout is reached or max retries exceeded
40
- *
41
- * @example
42
- * // Wait for an email from a specific sender (only considers emails from last 10 seconds)
43
- * const email = await mailhooks.emails.waitFor({
44
- * filter: { from: 'test@example.com' },
45
- * timeout: 30000, // 30 seconds
46
- * pollInterval: 2000, // Check every 2 seconds
47
- * lookbackWindow: 10000, // Only consider emails from last 10 seconds
48
- * });
49
- *
50
- * @example
51
- * // Wait with initial delay (useful when you know email will take time)
52
- * const email = await mailhooks.emails.waitFor({
53
- * filter: { subject: 'Order Confirmation' },
54
- * initialDelay: 5000, // Wait 5 seconds before first check
55
- * timeout: 60000,
56
- * lookbackWindow: 5000, // Only consider very recent emails
57
- * });
58
- */
59
- waitFor(options?: WaitForOptions): Promise<Email>;
60
- }
61
- //# sourceMappingURL=emails.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"emails.d.ts","sourceRoot":"","sources":["../../src/resources/emails.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAC5C,OAAO,EACL,KAAK,EACL,YAAY,EACZ,cAAc,EACd,eAAe,EACf,gBAAgB,EAChB,cAAc,EACf,MAAM,UAAU,CAAC;AAElB,qBAAa,cAAe,SAAQ,eAAe;IACjD;;OAEG;IACG,IAAI,CAAC,MAAM,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,cAAc,CAAC;IA0B7D;;;;OAIG;IACG,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,GAAE,OAAe,GAAG,OAAO,CAAC,KAAK,CAAC;IAK5E;;OAEG;IACG,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAIxD;;OAEG;IACG,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAS7D;;OAEG;IACG,kBAAkB,CACtB,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,gBAAgB,CAAC;IAqB5B;;OAEG;IACG,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC;IAIjD;;OAEG;IACG,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC;IAInD;;;;;;;;;;;;;;;;;;;;;;;;OAwBG;IACG,OAAO,CAAC,OAAO,GAAE,cAAmB,GAAG,OAAO,CAAC,KAAK,CAAC;CA8F5D"}
@@ -1,200 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.EmailsResource = void 0;
4
- const client_1 = require("../client");
5
- class EmailsResource extends client_1.MailhooksClient {
6
- /**
7
- * Get a paginated list of emails
8
- */
9
- async list(params) {
10
- // Set defaults
11
- const queryParams = {
12
- page: params?.page || 1,
13
- perPage: params?.perPage || 20,
14
- };
15
- // Handle filter params
16
- if (params?.filter) {
17
- if (params.filter.from)
18
- queryParams['filter.from'] = params.filter.from;
19
- if (params.filter.to)
20
- queryParams['filter.to'] = params.filter.to;
21
- if (params.filter.subject)
22
- queryParams['filter.subject'] = params.filter.subject;
23
- if (params.filter.startDate)
24
- queryParams['filter.createdAfter'] = params.filter.startDate;
25
- if (params.filter.endDate)
26
- queryParams['filter.createdBefore'] = params.filter.endDate;
27
- if (params.filter.read !== undefined)
28
- queryParams['filter.read'] = String(params.filter.read);
29
- }
30
- // Handle sort params
31
- if (params?.sort) {
32
- if (params.sort.field)
33
- queryParams['sort[field]'] = params.sort.field;
34
- if (params.sort.order)
35
- queryParams['sort[order]'] = params.sort.order;
36
- }
37
- return super.get('/v1/emails', queryParams);
38
- }
39
- /**
40
- * Get a specific email by ID
41
- * @param emailId - The ID of the email to retrieve
42
- * @param markAsRead - Optional: Mark the email as read when fetching (default: false)
43
- */
44
- async getEmail(emailId, markAsRead = false) {
45
- const params = markAsRead ? { markAsRead: 'true' } : undefined;
46
- return super.get(`/v1/emails/${emailId}`, params);
47
- }
48
- /**
49
- * Get the HTML and text content of an email
50
- */
51
- async getContent(emailId) {
52
- return super.get(`/v1/emails/${emailId}/content`);
53
- }
54
- /**
55
- * Download email in EML format
56
- */
57
- async downloadEml(emailId) {
58
- const data = await this.downloadFile(`/v1/emails/${emailId}/eml`);
59
- return {
60
- data,
61
- filename: `email-${emailId}.eml`,
62
- contentType: 'message/rfc822',
63
- };
64
- }
65
- /**
66
- * Download a specific attachment from an email
67
- */
68
- async downloadAttachment(emailId, attachmentId) {
69
- const axios = this.getAxiosInstance();
70
- const response = await axios.get(`/v1/emails/${emailId}/attachments/${attachmentId}`, {
71
- responseType: 'arraybuffer',
72
- });
73
- const contentDisposition = response.headers['content-disposition'];
74
- const filename = contentDisposition
75
- ? contentDisposition.split('filename=')[1]?.replace(/['"]/g, '')
76
- : `attachment-${attachmentId}`;
77
- return {
78
- data: response.data,
79
- filename,
80
- contentType: response.headers['content-type'],
81
- };
82
- }
83
- /**
84
- * Mark an email as read
85
- */
86
- async markAsRead(emailId) {
87
- return super.patch(`/v1/emails/${emailId}/read`);
88
- }
89
- /**
90
- * Mark an email as unread
91
- */
92
- async markAsUnread(emailId) {
93
- return super.patch(`/v1/emails/${emailId}/unread`);
94
- }
95
- /**
96
- * Wait for an email that matches the given filters
97
- *
98
- * @param options - Options for waiting including filters, timeouts, and delays
99
- * @returns The first email that matches the filters
100
- * @throws Error if timeout is reached or max retries exceeded
101
- *
102
- * @example
103
- * // Wait for an email from a specific sender (only considers emails from last 10 seconds)
104
- * const email = await mailhooks.emails.waitFor({
105
- * filter: { from: 'test@example.com' },
106
- * timeout: 30000, // 30 seconds
107
- * pollInterval: 2000, // Check every 2 seconds
108
- * lookbackWindow: 10000, // Only consider emails from last 10 seconds
109
- * });
110
- *
111
- * @example
112
- * // Wait with initial delay (useful when you know email will take time)
113
- * const email = await mailhooks.emails.waitFor({
114
- * filter: { subject: 'Order Confirmation' },
115
- * initialDelay: 5000, // Wait 5 seconds before first check
116
- * timeout: 60000,
117
- * lookbackWindow: 5000, // Only consider very recent emails
118
- * });
119
- */
120
- async waitFor(options = {}) {
121
- const { filter = {}, timeout = 30000, // Default 30 seconds
122
- pollInterval = 1000, // Default poll every 1 second
123
- maxRetries = null, initialDelay = 0, lookbackWindow = 10000, // Default 10 seconds lookback
124
- } = options;
125
- const startTime = Date.now();
126
- let retries = 0;
127
- let lastCheckedTime = null;
128
- // Helper function to check for matching emails
129
- const checkForEmail = async (isFirstCheck = false) => {
130
- try {
131
- // Calculate the time window for filtering
132
- const now = new Date();
133
- let startDateFilter;
134
- if (isFirstCheck && lookbackWindow) {
135
- // On first check, only look back the specified window
136
- startDateFilter = new Date(now.getTime() - lookbackWindow).toISOString();
137
- }
138
- else if (lastCheckedTime) {
139
- // On subsequent checks, look for emails since last check
140
- startDateFilter = lastCheckedTime.toISOString();
141
- }
142
- else {
143
- // Fallback to lookback window
144
- startDateFilter = new Date(now.getTime() - lookbackWindow).toISOString();
145
- }
146
- // Merge the time filter with user-provided filters
147
- const searchFilter = {
148
- ...filter,
149
- startDate: startDateFilter,
150
- };
151
- const response = await this.list({
152
- filter: searchFilter,
153
- perPage: 10,
154
- sort: { field: 'createdAt', order: 'desc' },
155
- });
156
- // Update last checked time for next iteration
157
- lastCheckedTime = now;
158
- if (response.data.length > 0) {
159
- // Return the most recent matching email
160
- return response.data[0];
161
- }
162
- return null;
163
- }
164
- catch (error) {
165
- // Log error but continue polling
166
- console.warn('Error checking for email:', error);
167
- return null;
168
- }
169
- };
170
- // Check immediately for existing emails (before any delay)
171
- const existingEmail = await checkForEmail(true);
172
- if (existingEmail) {
173
- return existingEmail;
174
- }
175
- // Apply initial delay if specified
176
- if (initialDelay > 0) {
177
- await new Promise(resolve => setTimeout(resolve, initialDelay));
178
- }
179
- // Start polling
180
- while (true) {
181
- // Check timeout
182
- if (timeout && Date.now() - startTime > timeout) {
183
- throw new Error(`Timeout waiting for email after ${timeout}ms`);
184
- }
185
- // Check max retries
186
- if (maxRetries !== null && retries >= maxRetries) {
187
- throw new Error(`Max retries (${maxRetries}) exceeded waiting for email`);
188
- }
189
- // Check for email
190
- const email = await checkForEmail();
191
- if (email) {
192
- return email;
193
- }
194
- // Wait before next poll
195
- await new Promise(resolve => setTimeout(resolve, pollInterval));
196
- retries++;
197
- }
198
- }
199
- }
200
- exports.EmailsResource = EmailsResource;
package/dist/types.d.ts DELETED
@@ -1,66 +0,0 @@
1
- export interface Attachment {
2
- id: string;
3
- filename: string;
4
- contentType: string;
5
- size: number;
6
- }
7
- export interface Email {
8
- id: string;
9
- from: string;
10
- to: string[];
11
- subject: string;
12
- read: boolean;
13
- createdAt: Date;
14
- attachments: Attachment[];
15
- }
16
- export interface EmailContent {
17
- html?: string;
18
- text?: string;
19
- }
20
- export interface PaginationResponse {
21
- currentPage: number;
22
- perPage: number;
23
- totalItems: number;
24
- totalPages: number;
25
- hasNextPage: boolean;
26
- nextCursor?: string;
27
- }
28
- export interface EmailsResponse extends PaginationResponse {
29
- data: Email[];
30
- }
31
- export interface EmailFilter {
32
- from?: string;
33
- to?: string;
34
- subject?: string;
35
- startDate?: string;
36
- endDate?: string;
37
- read?: boolean;
38
- }
39
- export interface EmailSort {
40
- field?: 'createdAt' | 'from' | 'subject';
41
- order?: 'asc' | 'desc';
42
- }
43
- export interface EmailListParams {
44
- page?: number;
45
- perPage?: number;
46
- filter?: EmailFilter;
47
- sort?: EmailSort;
48
- }
49
- export interface MailhooksConfig {
50
- apiKey: string;
51
- baseUrl?: string;
52
- }
53
- export interface DownloadResponse {
54
- data: ArrayBuffer;
55
- filename?: string;
56
- contentType?: string;
57
- }
58
- export interface WaitForOptions {
59
- filter?: EmailFilter;
60
- timeout?: number;
61
- pollInterval?: number;
62
- maxRetries?: number;
63
- initialDelay?: number;
64
- lookbackWindow?: number;
65
- }
66
- //# sourceMappingURL=types.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,KAAK;IACpB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,EAAE,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,OAAO,CAAC;IACd,SAAS,EAAE,IAAI,CAAC;IAChB,WAAW,EAAE,UAAU,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,kBAAkB;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,OAAO,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,cAAe,SAAQ,kBAAkB;IACxD,IAAI,EAAE,KAAK,EAAE,CAAC;CACf;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,SAAS;IACxB,KAAK,CAAC,EAAE,WAAW,GAAG,MAAM,GAAG,SAAS,CAAC;IACzC,KAAK,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,IAAI,CAAC,EAAE,SAAS,CAAC;CAClB;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,WAAW,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB"}
package/dist/types.js DELETED
@@ -1,2 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
@@ -1,123 +0,0 @@
1
- /**
2
- * Webhook payload sent by Mailhooks when an email is received.
3
- */
4
- export interface WebhookPayload {
5
- /** Unique email ID */
6
- id: string;
7
- /** Sender email address */
8
- from: string;
9
- /** Array of recipient email addresses */
10
- to: string[];
11
- /** Email subject line */
12
- subject: string;
13
- /** Plain text body of the email */
14
- body: string;
15
- /** HTML body of the email (if available) */
16
- html?: string;
17
- /** Array of attachment metadata */
18
- attachments: Array<{
19
- filename: string;
20
- contentType: string;
21
- size: number;
22
- }>;
23
- /** ISO 8601 timestamp when the email was received */
24
- receivedAt: string;
25
- /** SPF authentication result */
26
- spfResult?: 'pass' | 'fail' | 'softfail' | 'neutral' | 'none';
27
- /** DKIM authentication result */
28
- dkimResult?: 'pass' | 'fail' | 'none' | 'temperror' | 'permerror';
29
- /** DMARC authentication result */
30
- dmarcResult?: 'pass' | 'fail' | 'none' | 'temperror' | 'permerror';
31
- /** Overall authentication summary */
32
- authSummary?: 'pass' | 'fail' | 'partial';
33
- /** Email headers as key-value pairs */
34
- headers?: Record<string, string>;
35
- /** Authentication diagnostic details */
36
- authDiagnostics?: {
37
- spf: {
38
- clientIp: string;
39
- domain: string;
40
- record?: string;
41
- helo?: string;
42
- } | null;
43
- dkim: Array<{
44
- domain: string;
45
- selector?: string;
46
- algorithm?: string;
47
- aligned?: boolean;
48
- result: string;
49
- }>;
50
- dmarc: {
51
- domain: string;
52
- policy: string;
53
- record?: string;
54
- alignment: {
55
- spf: {
56
- result: string | false;
57
- strict: boolean;
58
- };
59
- dkim: {
60
- result: string | false;
61
- strict: boolean;
62
- };
63
- };
64
- } | null;
65
- };
66
- }
67
- /**
68
- * Verifies a webhook signature using HMAC-SHA256.
69
- *
70
- * Each webhook request from Mailhooks includes an `X-Webhook-Signature` header
71
- * containing a hex-encoded HMAC-SHA256 signature of the request body.
72
- *
73
- * @param payload - The raw request body as a string or Buffer
74
- * @param signature - The signature from the `X-Webhook-Signature` header
75
- * @param secret - Your webhook secret (starts with `whsec_`)
76
- * @returns `true` if the signature is valid, `false` otherwise
77
- *
78
- * @example
79
- * ```typescript
80
- * import { verifyWebhookSignature } from '@mailhooks/sdk';
81
- *
82
- * // Express.js with raw body parser
83
- * app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
84
- * const signature = req.headers['x-webhook-signature'] as string;
85
- * const payload = req.body.toString();
86
- *
87
- * if (!verifyWebhookSignature(payload, signature, process.env.WEBHOOK_SECRET!)) {
88
- * return res.status(401).send('Invalid signature');
89
- * }
90
- *
91
- * const event = JSON.parse(payload);
92
- * // Process the webhook...
93
- * res.status(200).send('OK');
94
- * });
95
- * ```
96
- */
97
- export declare function verifyWebhookSignature(payload: string | Buffer, signature: string, secret: string): boolean;
98
- /**
99
- * Parses a webhook payload from a JSON string.
100
- *
101
- * @param body - The raw request body as a string
102
- * @returns The parsed webhook payload
103
- * @throws {SyntaxError} If the body is not valid JSON
104
- *
105
- * @example
106
- * ```typescript
107
- * import { parseWebhookPayload } from '@mailhooks/sdk';
108
- *
109
- * const payload = parseWebhookPayload(req.body.toString());
110
- * console.log(`Received email from ${payload.from}: ${payload.subject}`);
111
- * ```
112
- */
113
- export declare function parseWebhookPayload(body: string): WebhookPayload;
114
- /**
115
- * Constructs the expected signature for a webhook payload.
116
- * Useful for debugging or manual verification.
117
- *
118
- * @param payload - The raw request body as a string or Buffer
119
- * @param secret - Your webhook secret
120
- * @returns The expected HMAC-SHA256 signature as a hex string
121
- */
122
- export declare function constructSignature(payload: string | Buffer, secret: string): string;
123
- //# sourceMappingURL=webhooks.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"webhooks.d.ts","sourceRoot":"","sources":["../src/webhooks.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,sBAAsB;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,2BAA2B;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,yCAAyC;IACzC,EAAE,EAAE,MAAM,EAAE,CAAC;IACb,yBAAyB;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,mCAAmC;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,4CAA4C;IAC5C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,mCAAmC;IACnC,WAAW,EAAE,KAAK,CAAC;QACjB,QAAQ,EAAE,MAAM,CAAC;QACjB,WAAW,EAAE,MAAM,CAAC;QACpB,IAAI,EAAE,MAAM,CAAC;KACd,CAAC,CAAC;IACH,qDAAqD;IACrD,UAAU,EAAE,MAAM,CAAC;IACnB,gCAAgC;IAChC,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,UAAU,GAAG,SAAS,GAAG,MAAM,CAAC;IAC9D,iCAAiC;IACjC,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,WAAW,GAAG,WAAW,CAAC;IAClE,kCAAkC;IAClC,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,WAAW,GAAG,WAAW,CAAC;IACnE,qCAAqC;IACrC,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;IAC1C,uCAAuC;IACvC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,wCAAwC;IACxC,eAAe,CAAC,EAAE;QAChB,GAAG,EAAE;YACH,QAAQ,EAAE,MAAM,CAAC;YACjB,MAAM,EAAE,MAAM,CAAC;YACf,MAAM,CAAC,EAAE,MAAM,CAAC;YAChB,IAAI,CAAC,EAAE,MAAM,CAAC;SACf,GAAG,IAAI,CAAC;QACT,IAAI,EAAE,KAAK,CAAC;YACV,MAAM,EAAE,MAAM,CAAC;YACf,QAAQ,CAAC,EAAE,MAAM,CAAC;YAClB,SAAS,CAAC,EAAE,MAAM,CAAC;YACnB,OAAO,CAAC,EAAE,OAAO,CAAC;YAClB,MAAM,EAAE,MAAM,CAAC;SAChB,CAAC,CAAC;QACH,KAAK,EAAE;YACL,MAAM,EAAE,MAAM,CAAC;YACf,MAAM,EAAE,MAAM,CAAC;YACf,MAAM,CAAC,EAAE,MAAM,CAAC;YAChB,SAAS,EAAE;gBACT,GAAG,EAAE;oBAAE,MAAM,EAAE,MAAM,GAAG,KAAK,CAAC;oBAAC,MAAM,EAAE,OAAO,CAAA;iBAAE,CAAC;gBACjD,IAAI,EAAE;oBAAE,MAAM,EAAE,MAAM,GAAG,KAAK,CAAC;oBAAC,MAAM,EAAE,OAAO,CAAA;iBAAE,CAAC;aACnD,CAAC;SACH,GAAG,IAAI,CAAC;KACV,CAAC;CACH;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,MAAM,GAAG,MAAM,EACxB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,GACb,OAAO,CAqBT;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,CAEhE;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,MAAM,GAAG,MAAM,EACxB,MAAM,EAAE,MAAM,GACb,MAAM,CAER"}
package/dist/webhooks.js DELETED
@@ -1,81 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.verifyWebhookSignature = verifyWebhookSignature;
4
- exports.parseWebhookPayload = parseWebhookPayload;
5
- exports.constructSignature = constructSignature;
6
- const crypto_1 = require("crypto");
7
- /**
8
- * Verifies a webhook signature using HMAC-SHA256.
9
- *
10
- * Each webhook request from Mailhooks includes an `X-Webhook-Signature` header
11
- * containing a hex-encoded HMAC-SHA256 signature of the request body.
12
- *
13
- * @param payload - The raw request body as a string or Buffer
14
- * @param signature - The signature from the `X-Webhook-Signature` header
15
- * @param secret - Your webhook secret (starts with `whsec_`)
16
- * @returns `true` if the signature is valid, `false` otherwise
17
- *
18
- * @example
19
- * ```typescript
20
- * import { verifyWebhookSignature } from '@mailhooks/sdk';
21
- *
22
- * // Express.js with raw body parser
23
- * app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
24
- * const signature = req.headers['x-webhook-signature'] as string;
25
- * const payload = req.body.toString();
26
- *
27
- * if (!verifyWebhookSignature(payload, signature, process.env.WEBHOOK_SECRET!)) {
28
- * return res.status(401).send('Invalid signature');
29
- * }
30
- *
31
- * const event = JSON.parse(payload);
32
- * // Process the webhook...
33
- * res.status(200).send('OK');
34
- * });
35
- * ```
36
- */
37
- function verifyWebhookSignature(payload, signature, secret) {
38
- if (!payload || !signature || !secret) {
39
- return false;
40
- }
41
- // Normalize and validate signature format (should be 64 hex chars for SHA256)
42
- const normalizedSignature = signature.trim().toLowerCase();
43
- if (!/^[a-f0-9]{64}$/.test(normalizedSignature)) {
44
- return false;
45
- }
46
- const expectedSignature = (0, crypto_1.createHmac)('sha256', secret)
47
- .update(payload)
48
- .digest('hex');
49
- // Use timing-safe comparison to prevent timing attacks
50
- // Both buffers are guaranteed to be 32 bytes (64 hex chars)
51
- return (0, crypto_1.timingSafeEqual)(Buffer.from(normalizedSignature, 'hex'), Buffer.from(expectedSignature, 'hex'));
52
- }
53
- /**
54
- * Parses a webhook payload from a JSON string.
55
- *
56
- * @param body - The raw request body as a string
57
- * @returns The parsed webhook payload
58
- * @throws {SyntaxError} If the body is not valid JSON
59
- *
60
- * @example
61
- * ```typescript
62
- * import { parseWebhookPayload } from '@mailhooks/sdk';
63
- *
64
- * const payload = parseWebhookPayload(req.body.toString());
65
- * console.log(`Received email from ${payload.from}: ${payload.subject}`);
66
- * ```
67
- */
68
- function parseWebhookPayload(body) {
69
- return JSON.parse(body);
70
- }
71
- /**
72
- * Constructs the expected signature for a webhook payload.
73
- * Useful for debugging or manual verification.
74
- *
75
- * @param payload - The raw request body as a string or Buffer
76
- * @param secret - Your webhook secret
77
- * @returns The expected HMAC-SHA256 signature as a hex string
78
- */
79
- function constructSignature(payload, secret) {
80
- return (0, crypto_1.createHmac)('sha256', secret).update(payload).digest('hex');
81
- }