@mailhooks/sdk 2.4.0 → 2.6.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/README.md +109 -0
- package/dist/index.d.ts +17 -11
- package/dist/index.js +2 -18
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -161,6 +161,115 @@ const email = await mailhooks.emails.waitFor({
|
|
|
161
161
|
- Efficiently tracks checked emails to avoid duplicates
|
|
162
162
|
- Throws error on timeout or max retries exceeded
|
|
163
163
|
|
|
164
|
+
## Push Notifications (SSE)
|
|
165
|
+
|
|
166
|
+
Get instant push notifications when emails arrive using Server-Sent Events. This is ideal for serverless environments, client-side apps, firewalled networks, or local development where webhooks aren't practical.
|
|
167
|
+
|
|
168
|
+
### Basic Usage
|
|
169
|
+
|
|
170
|
+
```typescript
|
|
171
|
+
import { Mailhooks } from '@mailhooks/sdk';
|
|
172
|
+
|
|
173
|
+
const mailhooks = new Mailhooks({ apiKey: 'mh_xxx' });
|
|
174
|
+
|
|
175
|
+
const subscription = mailhooks.realtime.subscribe({
|
|
176
|
+
onEmailReceived: (email) => {
|
|
177
|
+
console.log('New email:', email.subject);
|
|
178
|
+
console.log('From:', email.from);
|
|
179
|
+
},
|
|
180
|
+
onConnected: (payload) => {
|
|
181
|
+
console.log('Connected!', payload.connectionId);
|
|
182
|
+
},
|
|
183
|
+
onError: (error) => {
|
|
184
|
+
console.error('Error:', error);
|
|
185
|
+
},
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
// Later, disconnect when done
|
|
189
|
+
subscription.disconnect();
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### Distributed Mode (Worker Load Balancing)
|
|
193
|
+
|
|
194
|
+
When running multiple workers, use distributed mode so only ONE worker receives each notification:
|
|
195
|
+
|
|
196
|
+
```typescript
|
|
197
|
+
// Each worker connects with the same API key
|
|
198
|
+
// Only ONE worker receives each notification (random selection)
|
|
199
|
+
const subscription = mailhooks.realtime.subscribe({
|
|
200
|
+
mode: 'distributed',
|
|
201
|
+
onEmailReceived: async (email) => {
|
|
202
|
+
// Process email - only one worker will receive this
|
|
203
|
+
await processEmail(email.id);
|
|
204
|
+
},
|
|
205
|
+
});
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
### Full Event Handling
|
|
209
|
+
|
|
210
|
+
```typescript
|
|
211
|
+
const subscription = mailhooks.realtime.subscribe({
|
|
212
|
+
mode: 'broadcast', // or 'distributed'
|
|
213
|
+
onConnected: (payload) => {
|
|
214
|
+
console.log(`Connected: ${payload.connectionId}`);
|
|
215
|
+
console.log(`Mode: ${payload.mode}`);
|
|
216
|
+
},
|
|
217
|
+
onEmailReceived: (email) => {
|
|
218
|
+
console.log(`From: ${email.from}`);
|
|
219
|
+
console.log(`Subject: ${email.subject}`);
|
|
220
|
+
console.log(`Has attachments: ${email.hasAttachments}`);
|
|
221
|
+
},
|
|
222
|
+
onEmailUpdated: (update) => {
|
|
223
|
+
console.log(`Email ${update.id} updated:`, update.changes);
|
|
224
|
+
},
|
|
225
|
+
onHeartbeat: () => {
|
|
226
|
+
// Sent every 30 seconds to keep connection alive
|
|
227
|
+
},
|
|
228
|
+
onError: (error) => {
|
|
229
|
+
console.error('Connection error:', error);
|
|
230
|
+
},
|
|
231
|
+
onDisconnect: () => {
|
|
232
|
+
console.log('Disconnected');
|
|
233
|
+
},
|
|
234
|
+
autoReconnect: true, // Automatically reconnect on disconnect (default: true)
|
|
235
|
+
reconnectDelay: 5000, // Delay between reconnect attempts in ms (default: 5000)
|
|
236
|
+
});
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
### Node.js Usage
|
|
240
|
+
|
|
241
|
+
In Node.js, you need to install the `eventsource` package:
|
|
242
|
+
|
|
243
|
+
```bash
|
|
244
|
+
npm install eventsource
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
Then make EventSource available globally before importing the SDK:
|
|
248
|
+
|
|
249
|
+
```typescript
|
|
250
|
+
import EventSource from 'eventsource';
|
|
251
|
+
(global as any).EventSource = EventSource;
|
|
252
|
+
|
|
253
|
+
import { Mailhooks } from '@mailhooks/sdk';
|
|
254
|
+
// Use as normal
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
### Event Types
|
|
258
|
+
|
|
259
|
+
| Event | Payload | Description |
|
|
260
|
+
|-------|---------|-------------|
|
|
261
|
+
| `connected` | `{ tenantId, connectedAt, mode, connectionId }` | Connection established |
|
|
262
|
+
| `email.received` | `{ id, from, to, subject, hasAttachments, ... }` | New email arrived |
|
|
263
|
+
| `email.updated` | `{ id, changes: { read?: boolean } }` | Email was updated |
|
|
264
|
+
| `heartbeat` | `{ timestamp }` | Keep-alive ping (every 30s) |
|
|
265
|
+
|
|
266
|
+
### Connection Modes
|
|
267
|
+
|
|
268
|
+
| Mode | Behavior | Use Case |
|
|
269
|
+
|------|----------|----------|
|
|
270
|
+
| `broadcast` | All connected clients receive every event | Dashboards, monitoring |
|
|
271
|
+
| `distributed` | One random client per API key receives each event | Worker pools, load balancing |
|
|
272
|
+
|
|
164
273
|
## Types
|
|
165
274
|
|
|
166
275
|
The SDK includes comprehensive TypeScript types:
|
package/dist/index.d.ts
CHANGED
|
@@ -153,7 +153,13 @@ declare class EmailsResource extends MailhooksClient {
|
|
|
153
153
|
}
|
|
154
154
|
|
|
155
155
|
/**
|
|
156
|
-
*
|
|
156
|
+
* Connection mode for SSE subscriptions
|
|
157
|
+
* - broadcast: All connected clients receive every event (default)
|
|
158
|
+
* - distributed: Only ONE client per API key group receives each event (load balancing)
|
|
159
|
+
*/
|
|
160
|
+
type ConnectionMode = 'broadcast' | 'distributed';
|
|
161
|
+
/**
|
|
162
|
+
* Event types for push notifications
|
|
157
163
|
*/
|
|
158
164
|
declare enum RealtimeEventType {
|
|
159
165
|
EMAIL_RECEIVED = "email.received",
|
|
@@ -190,6 +196,8 @@ interface EmailUpdatedPayload {
|
|
|
190
196
|
interface ConnectedPayload {
|
|
191
197
|
tenantId: string;
|
|
192
198
|
connectedAt: string;
|
|
199
|
+
mode?: ConnectionMode;
|
|
200
|
+
connectionId?: string;
|
|
193
201
|
}
|
|
194
202
|
/**
|
|
195
203
|
* Heartbeat event payload
|
|
@@ -220,6 +228,12 @@ interface RealtimeCallbacks {
|
|
|
220
228
|
* Subscription options
|
|
221
229
|
*/
|
|
222
230
|
interface SubscribeOptions extends RealtimeCallbacks {
|
|
231
|
+
/**
|
|
232
|
+
* Connection mode (default: 'broadcast')
|
|
233
|
+
* - broadcast: All connected clients receive every event
|
|
234
|
+
* - distributed: Only ONE client per API key group receives each event (load balancing for workers)
|
|
235
|
+
*/
|
|
236
|
+
mode?: ConnectionMode;
|
|
223
237
|
/** Auto-reconnect on disconnect (default: true) */
|
|
224
238
|
autoReconnect?: boolean;
|
|
225
239
|
/** Reconnect delay in milliseconds (default: 5000) */
|
|
@@ -260,16 +274,6 @@ declare class RealtimeResource {
|
|
|
260
274
|
private eventSource;
|
|
261
275
|
private reconnectTimeout;
|
|
262
276
|
constructor(config: MailhooksConfig);
|
|
263
|
-
/**
|
|
264
|
-
* Check if the plan allows real-time notifications and get connection quota
|
|
265
|
-
*
|
|
266
|
-
* @returns Promise resolving to access status and connection limits
|
|
267
|
-
*/
|
|
268
|
-
checkAccess(): Promise<{
|
|
269
|
-
hasAccess: boolean;
|
|
270
|
-
maxConnections: number;
|
|
271
|
-
currentConnections: number;
|
|
272
|
-
}>;
|
|
273
277
|
/**
|
|
274
278
|
* Subscribe to real-time email notifications via Server-Sent Events (SSE)
|
|
275
279
|
*
|
|
@@ -331,6 +335,8 @@ interface WebhookPayload {
|
|
|
331
335
|
html?: string;
|
|
332
336
|
/** Array of attachment metadata */
|
|
333
337
|
attachments: Array<{
|
|
338
|
+
/** Unique attachment ID */
|
|
339
|
+
id: string;
|
|
334
340
|
filename: string;
|
|
335
341
|
contentType: string;
|
|
336
342
|
size: number;
|
package/dist/index.js
CHANGED
|
@@ -256,23 +256,6 @@ var RealtimeResource = class {
|
|
|
256
256
|
this.reconnectTimeout = null;
|
|
257
257
|
this.config = config;
|
|
258
258
|
}
|
|
259
|
-
/**
|
|
260
|
-
* Check if the plan allows real-time notifications and get connection quota
|
|
261
|
-
*
|
|
262
|
-
* @returns Promise resolving to access status and connection limits
|
|
263
|
-
*/
|
|
264
|
-
async checkAccess() {
|
|
265
|
-
const baseUrl = this.config.baseUrl ?? "https://mailhooks.dev/api";
|
|
266
|
-
const response = await fetch(`${baseUrl}/v1/realtime/plan-access`, {
|
|
267
|
-
headers: {
|
|
268
|
-
"x-api-key": this.config.apiKey
|
|
269
|
-
}
|
|
270
|
-
});
|
|
271
|
-
if (!response.ok) {
|
|
272
|
-
throw new Error(`Failed to check plan access: ${response.statusText}`);
|
|
273
|
-
}
|
|
274
|
-
return response.json();
|
|
275
|
-
}
|
|
276
259
|
/**
|
|
277
260
|
* Subscribe to real-time email notifications via Server-Sent Events (SSE)
|
|
278
261
|
*
|
|
@@ -305,6 +288,7 @@ var RealtimeResource = class {
|
|
|
305
288
|
*/
|
|
306
289
|
subscribe(options = {}) {
|
|
307
290
|
const {
|
|
291
|
+
mode = "broadcast",
|
|
308
292
|
onEmailReceived,
|
|
309
293
|
onEmailUpdated,
|
|
310
294
|
onConnected,
|
|
@@ -328,7 +312,7 @@ var RealtimeResource = class {
|
|
|
328
312
|
);
|
|
329
313
|
}
|
|
330
314
|
const baseUrl = this.config.baseUrl ?? "https://mailhooks.dev/api";
|
|
331
|
-
const url = `${baseUrl}/v1/realtime/events?token=${encodeURIComponent(this.config.apiKey)}`;
|
|
315
|
+
const url = `${baseUrl}/v1/realtime/events?token=${encodeURIComponent(this.config.apiKey)}&mode=${mode}`;
|
|
332
316
|
const connect = () => {
|
|
333
317
|
this.eventSource = new EventSource(url);
|
|
334
318
|
this.eventSource.onmessage = (event) => {
|