@mailhooks/sdk 1.0.3 → 1.0.4
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/README.md +41 -0
- package/dist/resources/emails.d.ts +27 -1
- package/dist/resources/emails.d.ts.map +1 -1
- package/dist/resources/emails.js +104 -0
- package/dist/types.d.ts +8 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -99,6 +99,47 @@ const attachment = await mailhooks.emails.downloadAttachment('email-id', 'attach
|
|
|
99
99
|
// attachment.contentType contains the MIME type
|
|
100
100
|
```
|
|
101
101
|
|
|
102
|
+
#### Wait for Email
|
|
103
|
+
|
|
104
|
+
Wait for an email that matches specific filters. Useful for testing and automation.
|
|
105
|
+
|
|
106
|
+
```typescript
|
|
107
|
+
// Basic usage - wait for email from specific sender
|
|
108
|
+
const email = await mailhooks.emails.waitFor({
|
|
109
|
+
filter: { from: 'noreply@example.com' },
|
|
110
|
+
timeout: 30000, // 30 seconds
|
|
111
|
+
pollInterval: 2000, // Check every 2 seconds
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
// Advanced usage with all options
|
|
115
|
+
const email = await mailhooks.emails.waitFor({
|
|
116
|
+
filter: {
|
|
117
|
+
from: 'noreply@example.com',
|
|
118
|
+
to: 'test@yourdomain.com',
|
|
119
|
+
subject: 'Order Confirmation',
|
|
120
|
+
},
|
|
121
|
+
lookbackWindow: 10000, // Only consider emails from last 10 seconds
|
|
122
|
+
initialDelay: 5000, // Wait 5 seconds before first check
|
|
123
|
+
timeout: 60000, // Total timeout of 60 seconds
|
|
124
|
+
pollInterval: 3000, // Check every 3 seconds
|
|
125
|
+
maxRetries: 20, // Stop after 20 attempts
|
|
126
|
+
});
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
**Options:**
|
|
130
|
+
- `filter`: Same filters as `list()` method (from, to, subject, startDate, endDate)
|
|
131
|
+
- `lookbackWindow`: How far back to look for emails on first check (default: 10000ms)
|
|
132
|
+
- `initialDelay`: Delay before starting to poll (default: 0ms)
|
|
133
|
+
- `timeout`: Maximum time to wait before throwing error (default: 30000ms)
|
|
134
|
+
- `pollInterval`: Time between checks (default: 1000ms)
|
|
135
|
+
- `maxRetries`: Maximum number of polling attempts (default: unlimited)
|
|
136
|
+
|
|
137
|
+
**Key Features:**
|
|
138
|
+
- Returns immediately if a matching email already exists (within lookback window)
|
|
139
|
+
- Prevents returning old emails by using the lookback window
|
|
140
|
+
- Efficiently tracks checked emails to avoid duplicates
|
|
141
|
+
- Throws error on timeout or max retries exceeded
|
|
142
|
+
|
|
102
143
|
## Types
|
|
103
144
|
|
|
104
145
|
The SDK includes comprehensive TypeScript types:
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { MailhooksClient } from '../client';
|
|
2
|
-
import { Email, EmailContent, EmailsResponse, EmailListParams, DownloadResponse } from '../types';
|
|
2
|
+
import { Email, EmailContent, EmailsResponse, EmailListParams, DownloadResponse, WaitForOptions } from '../types';
|
|
3
3
|
export declare class EmailsResource extends MailhooksClient {
|
|
4
4
|
/**
|
|
5
5
|
* Get a paginated list of emails
|
|
@@ -21,5 +21,31 @@ export declare class EmailsResource extends MailhooksClient {
|
|
|
21
21
|
* Download a specific attachment from an email
|
|
22
22
|
*/
|
|
23
23
|
downloadAttachment(emailId: string, attachmentId: string): Promise<DownloadResponse>;
|
|
24
|
+
/**
|
|
25
|
+
* Wait for an email that matches the given filters
|
|
26
|
+
*
|
|
27
|
+
* @param options - Options for waiting including filters, timeouts, and delays
|
|
28
|
+
* @returns The first email that matches the filters
|
|
29
|
+
* @throws Error if timeout is reached or max retries exceeded
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* // Wait for an email from a specific sender (only considers emails from last 10 seconds)
|
|
33
|
+
* const email = await mailhooks.emails.waitFor({
|
|
34
|
+
* filter: { from: 'test@example.com' },
|
|
35
|
+
* timeout: 30000, // 30 seconds
|
|
36
|
+
* pollInterval: 2000, // Check every 2 seconds
|
|
37
|
+
* lookbackWindow: 10000, // Only consider emails from last 10 seconds
|
|
38
|
+
* });
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* // Wait with initial delay (useful when you know email will take time)
|
|
42
|
+
* const email = await mailhooks.emails.waitFor({
|
|
43
|
+
* filter: { subject: 'Order Confirmation' },
|
|
44
|
+
* initialDelay: 5000, // Wait 5 seconds before first check
|
|
45
|
+
* timeout: 60000,
|
|
46
|
+
* lookbackWindow: 5000, // Only consider very recent emails
|
|
47
|
+
* });
|
|
48
|
+
*/
|
|
49
|
+
waitFor(options?: WaitForOptions): Promise<Email>;
|
|
24
50
|
}
|
|
25
51
|
//# sourceMappingURL=emails.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
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,
|
|
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;IAyB7D;;OAEG;IACG,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC;IAI/C;;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;;;;;;;;;;;;;;;;;;;;;;;;OAwBG;IACG,OAAO,CAAC,OAAO,GAAE,cAAmB,GAAG,OAAO,CAAC,KAAK,CAAC;CA8F5D"}
|
package/dist/resources/emails.js
CHANGED
|
@@ -75,5 +75,109 @@ class EmailsResource extends client_1.MailhooksClient {
|
|
|
75
75
|
contentType: response.headers['content-type'],
|
|
76
76
|
};
|
|
77
77
|
}
|
|
78
|
+
/**
|
|
79
|
+
* Wait for an email that matches the given filters
|
|
80
|
+
*
|
|
81
|
+
* @param options - Options for waiting including filters, timeouts, and delays
|
|
82
|
+
* @returns The first email that matches the filters
|
|
83
|
+
* @throws Error if timeout is reached or max retries exceeded
|
|
84
|
+
*
|
|
85
|
+
* @example
|
|
86
|
+
* // Wait for an email from a specific sender (only considers emails from last 10 seconds)
|
|
87
|
+
* const email = await mailhooks.emails.waitFor({
|
|
88
|
+
* filter: { from: 'test@example.com' },
|
|
89
|
+
* timeout: 30000, // 30 seconds
|
|
90
|
+
* pollInterval: 2000, // Check every 2 seconds
|
|
91
|
+
* lookbackWindow: 10000, // Only consider emails from last 10 seconds
|
|
92
|
+
* });
|
|
93
|
+
*
|
|
94
|
+
* @example
|
|
95
|
+
* // Wait with initial delay (useful when you know email will take time)
|
|
96
|
+
* const email = await mailhooks.emails.waitFor({
|
|
97
|
+
* filter: { subject: 'Order Confirmation' },
|
|
98
|
+
* initialDelay: 5000, // Wait 5 seconds before first check
|
|
99
|
+
* timeout: 60000,
|
|
100
|
+
* lookbackWindow: 5000, // Only consider very recent emails
|
|
101
|
+
* });
|
|
102
|
+
*/
|
|
103
|
+
async waitFor(options = {}) {
|
|
104
|
+
const { filter = {}, timeout = 30000, // Default 30 seconds
|
|
105
|
+
pollInterval = 1000, // Default poll every 1 second
|
|
106
|
+
maxRetries = null, initialDelay = 0, lookbackWindow = 10000, // Default 10 seconds lookback
|
|
107
|
+
} = options;
|
|
108
|
+
const startTime = Date.now();
|
|
109
|
+
let retries = 0;
|
|
110
|
+
let lastCheckedTime = null;
|
|
111
|
+
// Helper function to check for matching emails
|
|
112
|
+
const checkForEmail = async (isFirstCheck = false) => {
|
|
113
|
+
try {
|
|
114
|
+
// Calculate the time window for filtering
|
|
115
|
+
const now = new Date();
|
|
116
|
+
let startDateFilter;
|
|
117
|
+
if (isFirstCheck && lookbackWindow) {
|
|
118
|
+
// On first check, only look back the specified window
|
|
119
|
+
startDateFilter = new Date(now.getTime() - lookbackWindow).toISOString();
|
|
120
|
+
}
|
|
121
|
+
else if (lastCheckedTime) {
|
|
122
|
+
// On subsequent checks, look for emails since last check
|
|
123
|
+
startDateFilter = lastCheckedTime.toISOString();
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
// Fallback to lookback window
|
|
127
|
+
startDateFilter = new Date(now.getTime() - lookbackWindow).toISOString();
|
|
128
|
+
}
|
|
129
|
+
// Merge the time filter with user-provided filters
|
|
130
|
+
const searchFilter = {
|
|
131
|
+
...filter,
|
|
132
|
+
startDate: startDateFilter,
|
|
133
|
+
};
|
|
134
|
+
const response = await this.list({
|
|
135
|
+
filter: searchFilter,
|
|
136
|
+
perPage: 10,
|
|
137
|
+
sort: { field: 'createdAt', order: 'desc' },
|
|
138
|
+
});
|
|
139
|
+
// Update last checked time for next iteration
|
|
140
|
+
lastCheckedTime = now;
|
|
141
|
+
if (response.data.length > 0) {
|
|
142
|
+
// Return the most recent matching email
|
|
143
|
+
return response.data[0];
|
|
144
|
+
}
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
catch (error) {
|
|
148
|
+
// Log error but continue polling
|
|
149
|
+
console.warn('Error checking for email:', error);
|
|
150
|
+
return null;
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
// Check immediately for existing emails (before any delay)
|
|
154
|
+
const existingEmail = await checkForEmail(true);
|
|
155
|
+
if (existingEmail) {
|
|
156
|
+
return existingEmail;
|
|
157
|
+
}
|
|
158
|
+
// Apply initial delay if specified
|
|
159
|
+
if (initialDelay > 0) {
|
|
160
|
+
await new Promise(resolve => setTimeout(resolve, initialDelay));
|
|
161
|
+
}
|
|
162
|
+
// Start polling
|
|
163
|
+
while (true) {
|
|
164
|
+
// Check timeout
|
|
165
|
+
if (timeout && Date.now() - startTime > timeout) {
|
|
166
|
+
throw new Error(`Timeout waiting for email after ${timeout}ms`);
|
|
167
|
+
}
|
|
168
|
+
// Check max retries
|
|
169
|
+
if (maxRetries !== null && retries >= maxRetries) {
|
|
170
|
+
throw new Error(`Max retries (${maxRetries}) exceeded waiting for email`);
|
|
171
|
+
}
|
|
172
|
+
// Check for email
|
|
173
|
+
const email = await checkForEmail();
|
|
174
|
+
if (email) {
|
|
175
|
+
return email;
|
|
176
|
+
}
|
|
177
|
+
// Wait before next poll
|
|
178
|
+
await new Promise(resolve => setTimeout(resolve, pollInterval));
|
|
179
|
+
retries++;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
78
182
|
}
|
|
79
183
|
exports.EmailsResource = EmailsResource;
|
package/dist/types.d.ts
CHANGED
|
@@ -53,4 +53,12 @@ export interface DownloadResponse {
|
|
|
53
53
|
filename?: string;
|
|
54
54
|
contentType?: string;
|
|
55
55
|
}
|
|
56
|
+
export interface WaitForOptions {
|
|
57
|
+
filter?: EmailFilter;
|
|
58
|
+
timeout?: number;
|
|
59
|
+
pollInterval?: number;
|
|
60
|
+
maxRetries?: number;
|
|
61
|
+
initialDelay?: number;
|
|
62
|
+
lookbackWindow?: number;
|
|
63
|
+
}
|
|
56
64
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
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,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;CAClB;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"}
|
|
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,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;CAClB;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"}
|