shipmail 0.1.5 → 0.1.20
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 +3 -7
- package/dist/index.cjs +226 -87
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +204 -54
- package/dist/index.d.ts +204 -54
- package/dist/index.js +220 -86
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# ShipMail TypeScript SDK
|
|
2
2
|
|
|
3
|
-
Official TypeScript SDK for the [ShipMail](https://shipmail.to) API. Zero runtime dependencies. Works with Node.js
|
|
3
|
+
Official TypeScript SDK for the [ShipMail](https://shipmail.to) API. Zero runtime dependencies. Works with Node.js 18+, Bun, and Deno.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -38,7 +38,7 @@ import { ShipMailClient } from "shipmail";
|
|
|
38
38
|
|
|
39
39
|
const client = new ShipMailClient({
|
|
40
40
|
apiKey: "sm_live_...",
|
|
41
|
-
baseUrl: "https://
|
|
41
|
+
baseUrl: "https://shipmail.to/api/v1", // default
|
|
42
42
|
maxRetries: 2, // default, retries on 5xx and 429
|
|
43
43
|
timeout: 30_000, // default, in milliseconds
|
|
44
44
|
});
|
|
@@ -69,10 +69,6 @@ const mailboxes = await client.mailboxes.list({ domain_id: "dom_..." });
|
|
|
69
69
|
const mailbox = await client.mailboxes.get("mbx_...");
|
|
70
70
|
const updated = await client.mailboxes.update("mbx_...", { display_name: "New Name" });
|
|
71
71
|
await client.mailboxes.delete("mbx_...");
|
|
72
|
-
|
|
73
|
-
// Avatar
|
|
74
|
-
const avatar = await client.mailboxes.uploadAvatar("mbx_...", imageBuffer, "image/png");
|
|
75
|
-
await client.mailboxes.deleteAvatar("mbx_...");
|
|
76
72
|
```
|
|
77
73
|
|
|
78
74
|
### Messages
|
|
@@ -161,7 +157,7 @@ Verify incoming webhook signatures without instantiating a client:
|
|
|
161
157
|
import { verifyWebhook, WebhookVerificationError } from "shipmail";
|
|
162
158
|
|
|
163
159
|
try {
|
|
164
|
-
const event = verifyWebhook(rawBody, request.headers, webhookSecret);
|
|
160
|
+
const event = await verifyWebhook(rawBody, request.headers, webhookSecret);
|
|
165
161
|
console.log(event.event_type); // e.g., "message.received"
|
|
166
162
|
console.log(event.data);
|
|
167
163
|
} catch (err) {
|
package/dist/index.cjs
CHANGED
|
@@ -2,8 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
|
-
var crypto = require('crypto');
|
|
6
|
-
|
|
7
5
|
// src/errors.ts
|
|
8
6
|
var ShipMailError = class extends Error {
|
|
9
7
|
constructor(message, options) {
|
|
@@ -39,6 +37,12 @@ var ValidationError = class extends ShipMailError {
|
|
|
39
37
|
this.details = options?.details;
|
|
40
38
|
}
|
|
41
39
|
};
|
|
40
|
+
var QuotaExceededError = class extends ShipMailError {
|
|
41
|
+
constructor(message, requestId) {
|
|
42
|
+
super(message, { status: 403, type: "quota_exceeded", requestId, retryable: false });
|
|
43
|
+
this.name = "QuotaExceededError";
|
|
44
|
+
}
|
|
45
|
+
};
|
|
42
46
|
var NotFoundError = class extends ShipMailError {
|
|
43
47
|
constructor(message, requestId) {
|
|
44
48
|
super(message, { status: 404, type: "not_found", requestId, retryable: false });
|
|
@@ -89,6 +93,8 @@ function mapErrorFromResponse(status, body, requestId) {
|
|
|
89
93
|
return new AuthenticationError(error.message, rid);
|
|
90
94
|
case "authorization_error":
|
|
91
95
|
return new AuthorizationError(error.message, rid);
|
|
96
|
+
case "quota_exceeded":
|
|
97
|
+
return new QuotaExceededError(error.message, rid);
|
|
92
98
|
case "validation_error":
|
|
93
99
|
return new ValidationError(error.message, { requestId: rid, details: error.details });
|
|
94
100
|
case "not_found":
|
|
@@ -142,21 +148,23 @@ var ApiResource = class {
|
|
|
142
148
|
|
|
143
149
|
// src/resources/domains.ts
|
|
144
150
|
var Domains = class extends ApiResource {
|
|
145
|
-
create(params) {
|
|
151
|
+
create(params, options) {
|
|
146
152
|
return this.client.request({
|
|
147
153
|
method: "POST",
|
|
148
154
|
path: "/domains",
|
|
149
|
-
body: params
|
|
155
|
+
body: params,
|
|
156
|
+
methodOptions: options
|
|
150
157
|
});
|
|
151
158
|
}
|
|
152
|
-
list(params) {
|
|
159
|
+
list(params, options) {
|
|
153
160
|
return this.client.request({
|
|
154
161
|
method: "GET",
|
|
155
162
|
path: "/domains",
|
|
156
163
|
query: {
|
|
157
164
|
cursor: params?.cursor,
|
|
158
165
|
limit: params?.limit
|
|
159
|
-
}
|
|
166
|
+
},
|
|
167
|
+
methodOptions: options
|
|
160
168
|
});
|
|
161
169
|
}
|
|
162
170
|
listAutoPaginating(params) {
|
|
@@ -164,43 +172,64 @@ var Domains = class extends ApiResource {
|
|
|
164
172
|
limit: params?.limit
|
|
165
173
|
});
|
|
166
174
|
}
|
|
167
|
-
get(id) {
|
|
175
|
+
get(id, options) {
|
|
168
176
|
return this.client.request({
|
|
169
177
|
method: "GET",
|
|
170
|
-
path: `/domains/${id}
|
|
178
|
+
path: `/domains/${encodeURIComponent(id)}`,
|
|
179
|
+
methodOptions: options
|
|
171
180
|
});
|
|
172
181
|
}
|
|
173
|
-
update(id, params) {
|
|
182
|
+
update(id, params, options) {
|
|
174
183
|
return this.client.request({
|
|
175
184
|
method: "PATCH",
|
|
176
|
-
path: `/domains/${id}`,
|
|
177
|
-
body: params
|
|
185
|
+
path: `/domains/${encodeURIComponent(id)}`,
|
|
186
|
+
body: params,
|
|
187
|
+
methodOptions: options
|
|
178
188
|
});
|
|
179
189
|
}
|
|
180
|
-
delete(id) {
|
|
190
|
+
delete(id, options) {
|
|
181
191
|
return this.client.request({
|
|
182
192
|
method: "DELETE",
|
|
183
|
-
path: `/domains/${id}
|
|
193
|
+
path: `/domains/${encodeURIComponent(id)}`,
|
|
194
|
+
methodOptions: options
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
verify(id, options) {
|
|
198
|
+
return this.client.request({
|
|
199
|
+
method: "POST",
|
|
200
|
+
path: `/domains/${encodeURIComponent(id)}/verification`,
|
|
201
|
+
methodOptions: options
|
|
184
202
|
});
|
|
185
203
|
}
|
|
186
|
-
|
|
204
|
+
search(params, options) {
|
|
187
205
|
return this.client.request({
|
|
188
206
|
method: "POST",
|
|
189
|
-
path:
|
|
207
|
+
path: "/domains/search",
|
|
208
|
+
body: params,
|
|
209
|
+
methodOptions: options
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
register(params, options) {
|
|
213
|
+
return this.client.request({
|
|
214
|
+
method: "POST",
|
|
215
|
+
path: "/domains/register",
|
|
216
|
+
body: params,
|
|
217
|
+
methodOptions: options
|
|
190
218
|
});
|
|
191
219
|
}
|
|
192
220
|
};
|
|
193
221
|
|
|
194
222
|
// src/resources/mailboxes.ts
|
|
195
223
|
var Mailboxes = class extends ApiResource {
|
|
196
|
-
create(params) {
|
|
224
|
+
create(params, options) {
|
|
197
225
|
return this.client.request({
|
|
198
226
|
method: "POST",
|
|
199
227
|
path: "/mailboxes",
|
|
200
|
-
body: params
|
|
228
|
+
body: params,
|
|
229
|
+
methodOptions: options
|
|
201
230
|
});
|
|
202
231
|
}
|
|
203
|
-
list(params) {
|
|
232
|
+
list(params, options) {
|
|
204
233
|
return this.client.request({
|
|
205
234
|
method: "GET",
|
|
206
235
|
path: "/mailboxes",
|
|
@@ -208,7 +237,8 @@ var Mailboxes = class extends ApiResource {
|
|
|
208
237
|
domain_id: params?.domain_id,
|
|
209
238
|
cursor: params?.cursor,
|
|
210
239
|
limit: params?.limit
|
|
211
|
-
}
|
|
240
|
+
},
|
|
241
|
+
methodOptions: options
|
|
212
242
|
});
|
|
213
243
|
}
|
|
214
244
|
listAutoPaginating(params) {
|
|
@@ -217,71 +247,124 @@ var Mailboxes = class extends ApiResource {
|
|
|
217
247
|
limit: params?.limit
|
|
218
248
|
});
|
|
219
249
|
}
|
|
220
|
-
get(id) {
|
|
250
|
+
get(id, options) {
|
|
221
251
|
return this.client.request({
|
|
222
252
|
method: "GET",
|
|
223
|
-
path: `/mailboxes/${id}
|
|
253
|
+
path: `/mailboxes/${encodeURIComponent(id)}`,
|
|
254
|
+
methodOptions: options
|
|
224
255
|
});
|
|
225
256
|
}
|
|
226
|
-
update(id, params) {
|
|
257
|
+
update(id, params, options) {
|
|
227
258
|
return this.client.request({
|
|
228
259
|
method: "PATCH",
|
|
229
|
-
path: `/mailboxes/${id}`,
|
|
230
|
-
body: params
|
|
260
|
+
path: `/mailboxes/${encodeURIComponent(id)}`,
|
|
261
|
+
body: params,
|
|
262
|
+
methodOptions: options
|
|
231
263
|
});
|
|
232
264
|
}
|
|
233
|
-
delete(id) {
|
|
265
|
+
delete(id, options) {
|
|
234
266
|
return this.client.request({
|
|
235
267
|
method: "DELETE",
|
|
236
|
-
path: `/mailboxes/${id}
|
|
237
|
-
|
|
238
|
-
}
|
|
239
|
-
uploadAvatar(id, data, contentType) {
|
|
240
|
-
return this.client.request({
|
|
241
|
-
method: "PUT",
|
|
242
|
-
path: `/mailboxes/${id}/avatar`,
|
|
243
|
-
rawBody: data,
|
|
244
|
-
contentType
|
|
268
|
+
path: `/mailboxes/${encodeURIComponent(id)}`,
|
|
269
|
+
methodOptions: options
|
|
245
270
|
});
|
|
246
271
|
}
|
|
247
|
-
|
|
272
|
+
updateAutoReply(id, params, options) {
|
|
248
273
|
return this.client.request({
|
|
249
|
-
method: "
|
|
250
|
-
path: `/mailboxes/${id}/
|
|
274
|
+
method: "PATCH",
|
|
275
|
+
path: `/mailboxes/${encodeURIComponent(id)}/auto-reply`,
|
|
276
|
+
body: params,
|
|
277
|
+
methodOptions: options
|
|
251
278
|
});
|
|
252
279
|
}
|
|
253
280
|
};
|
|
254
281
|
|
|
255
282
|
// src/resources/messages.ts
|
|
256
283
|
var Messages = class extends ApiResource {
|
|
257
|
-
send(params) {
|
|
284
|
+
send(params, options) {
|
|
258
285
|
return this.client.request({
|
|
259
286
|
method: "POST",
|
|
260
287
|
path: "/messages",
|
|
261
|
-
body: params
|
|
288
|
+
body: params,
|
|
289
|
+
methodOptions: options
|
|
262
290
|
});
|
|
263
291
|
}
|
|
264
|
-
|
|
292
|
+
list(params, options) {
|
|
265
293
|
return this.client.request({
|
|
266
294
|
method: "GET",
|
|
267
|
-
path:
|
|
295
|
+
path: "/messages",
|
|
296
|
+
query: {
|
|
297
|
+
mailbox_id: params.mailbox_id,
|
|
298
|
+
cursor: params.cursor,
|
|
299
|
+
limit: params.limit
|
|
300
|
+
},
|
|
301
|
+
methodOptions: options
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
listAutoPaginating(params) {
|
|
305
|
+
return new Page(this.client, "/messages", {
|
|
306
|
+
mailbox_id: params.mailbox_id,
|
|
307
|
+
limit: params.limit
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
get(id, options) {
|
|
311
|
+
return this.client.request({
|
|
312
|
+
method: "GET",
|
|
313
|
+
path: `/messages/${encodeURIComponent(id)}`,
|
|
314
|
+
methodOptions: options
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
reply(id, params, options) {
|
|
318
|
+
return this.client.request({
|
|
319
|
+
method: "POST",
|
|
320
|
+
path: `/messages/${encodeURIComponent(id)}/reply`,
|
|
321
|
+
body: params,
|
|
322
|
+
methodOptions: options
|
|
268
323
|
});
|
|
269
324
|
}
|
|
270
325
|
};
|
|
271
326
|
|
|
272
327
|
// src/resources/status.ts
|
|
273
328
|
var Status = class extends ApiResource {
|
|
274
|
-
get() {
|
|
329
|
+
get(options) {
|
|
330
|
+
return this.client.request({
|
|
331
|
+
method: "GET",
|
|
332
|
+
path: "/status",
|
|
333
|
+
methodOptions: options
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
};
|
|
337
|
+
|
|
338
|
+
// src/resources/suppressions.ts
|
|
339
|
+
var Suppressions = class extends ApiResource {
|
|
340
|
+
list(params, options) {
|
|
275
341
|
return this.client.request({
|
|
276
342
|
method: "GET",
|
|
277
|
-
path: "/
|
|
343
|
+
path: "/suppressions",
|
|
344
|
+
query: {
|
|
345
|
+
cursor: params?.cursor,
|
|
346
|
+
limit: params?.limit
|
|
347
|
+
},
|
|
348
|
+
methodOptions: options
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
listAutoPaginating(params) {
|
|
352
|
+
return new Page(this.client, "/suppressions", {
|
|
353
|
+
limit: params?.limit
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
remove(email, options) {
|
|
357
|
+
return this.client.request({
|
|
358
|
+
method: "DELETE",
|
|
359
|
+
path: `/suppressions/${encodeURIComponent(email)}`,
|
|
360
|
+
methodOptions: options
|
|
278
361
|
});
|
|
279
362
|
}
|
|
280
363
|
};
|
|
281
364
|
|
|
282
365
|
// src/resources/threads.ts
|
|
283
366
|
var Threads = class extends ApiResource {
|
|
284
|
-
list(params) {
|
|
367
|
+
list(params, options) {
|
|
285
368
|
return this.client.request({
|
|
286
369
|
method: "GET",
|
|
287
370
|
path: "/threads",
|
|
@@ -289,7 +372,8 @@ var Threads = class extends ApiResource {
|
|
|
289
372
|
mailbox_id: params.mailbox_id,
|
|
290
373
|
cursor: params.cursor,
|
|
291
374
|
limit: params.limit
|
|
292
|
-
}
|
|
375
|
+
},
|
|
376
|
+
methodOptions: options
|
|
293
377
|
});
|
|
294
378
|
}
|
|
295
379
|
listAutoPaginating(params) {
|
|
@@ -298,42 +382,46 @@ var Threads = class extends ApiResource {
|
|
|
298
382
|
limit: params.limit
|
|
299
383
|
});
|
|
300
384
|
}
|
|
301
|
-
get(id, params) {
|
|
385
|
+
get(id, params, options) {
|
|
302
386
|
return this.client.request({
|
|
303
387
|
method: "GET",
|
|
304
|
-
path: `/threads/${id}`,
|
|
388
|
+
path: `/threads/${encodeURIComponent(id)}`,
|
|
305
389
|
query: {
|
|
306
390
|
cursor: params?.cursor,
|
|
307
391
|
limit: params?.limit
|
|
308
|
-
}
|
|
392
|
+
},
|
|
393
|
+
methodOptions: options
|
|
309
394
|
});
|
|
310
395
|
}
|
|
311
|
-
reply(id, params) {
|
|
396
|
+
reply(id, params, options) {
|
|
312
397
|
return this.client.request({
|
|
313
398
|
method: "POST",
|
|
314
|
-
path: `/threads/${id}/reply`,
|
|
315
|
-
body: params
|
|
399
|
+
path: `/threads/${encodeURIComponent(id)}/reply`,
|
|
400
|
+
body: params,
|
|
401
|
+
methodOptions: options
|
|
316
402
|
});
|
|
317
403
|
}
|
|
318
404
|
};
|
|
319
405
|
|
|
320
406
|
// src/resources/webhooks.ts
|
|
321
407
|
var Webhooks = class extends ApiResource {
|
|
322
|
-
create(params) {
|
|
408
|
+
create(params, options) {
|
|
323
409
|
return this.client.request({
|
|
324
410
|
method: "POST",
|
|
325
411
|
path: "/webhooks",
|
|
326
|
-
body: params
|
|
412
|
+
body: params,
|
|
413
|
+
methodOptions: options
|
|
327
414
|
});
|
|
328
415
|
}
|
|
329
|
-
list(params) {
|
|
416
|
+
list(params, options) {
|
|
330
417
|
return this.client.request({
|
|
331
418
|
method: "GET",
|
|
332
419
|
path: "/webhooks",
|
|
333
420
|
query: {
|
|
334
421
|
cursor: params?.cursor,
|
|
335
422
|
limit: params?.limit
|
|
336
|
-
}
|
|
423
|
+
},
|
|
424
|
+
methodOptions: options
|
|
337
425
|
});
|
|
338
426
|
}
|
|
339
427
|
listAutoPaginating(params) {
|
|
@@ -341,69 +429,82 @@ var Webhooks = class extends ApiResource {
|
|
|
341
429
|
limit: params?.limit
|
|
342
430
|
});
|
|
343
431
|
}
|
|
344
|
-
get(id) {
|
|
432
|
+
get(id, options) {
|
|
345
433
|
return this.client.request({
|
|
346
434
|
method: "GET",
|
|
347
|
-
path: `/webhooks/${id}
|
|
435
|
+
path: `/webhooks/${encodeURIComponent(id)}`,
|
|
436
|
+
methodOptions: options
|
|
348
437
|
});
|
|
349
438
|
}
|
|
350
|
-
update(id, params) {
|
|
439
|
+
update(id, params, options) {
|
|
351
440
|
return this.client.request({
|
|
352
441
|
method: "PATCH",
|
|
353
|
-
path: `/webhooks/${id}`,
|
|
354
|
-
body: params
|
|
442
|
+
path: `/webhooks/${encodeURIComponent(id)}`,
|
|
443
|
+
body: params,
|
|
444
|
+
methodOptions: options
|
|
355
445
|
});
|
|
356
446
|
}
|
|
357
|
-
delete(id) {
|
|
447
|
+
delete(id, options) {
|
|
358
448
|
return this.client.request({
|
|
359
449
|
method: "DELETE",
|
|
360
|
-
path: `/webhooks/${id}
|
|
450
|
+
path: `/webhooks/${encodeURIComponent(id)}`,
|
|
451
|
+
methodOptions: options
|
|
361
452
|
});
|
|
362
453
|
}
|
|
363
|
-
rotateSecret(id) {
|
|
454
|
+
rotateSecret(id, options) {
|
|
364
455
|
return this.client.request({
|
|
365
456
|
method: "POST",
|
|
366
|
-
path: `/webhooks/${id}/rotate-secret
|
|
457
|
+
path: `/webhooks/${encodeURIComponent(id)}/rotate-secret`,
|
|
458
|
+
methodOptions: options
|
|
367
459
|
});
|
|
368
460
|
}
|
|
369
|
-
test(id) {
|
|
461
|
+
test(id, options) {
|
|
370
462
|
return this.client.request({
|
|
371
463
|
method: "POST",
|
|
372
|
-
path: `/webhooks/${id}/test
|
|
464
|
+
path: `/webhooks/${encodeURIComponent(id)}/test`,
|
|
465
|
+
methodOptions: options
|
|
373
466
|
});
|
|
374
467
|
}
|
|
375
|
-
listDeliveries(id, params) {
|
|
468
|
+
listDeliveries(id, params, options) {
|
|
376
469
|
return this.client.request({
|
|
377
470
|
method: "GET",
|
|
378
|
-
path: `/webhooks/${id}/deliveries`,
|
|
471
|
+
path: `/webhooks/${encodeURIComponent(id)}/deliveries`,
|
|
379
472
|
query: {
|
|
380
473
|
status: params?.status,
|
|
381
474
|
event_type: params?.event_type,
|
|
382
475
|
cursor: params?.cursor,
|
|
383
476
|
limit: params?.limit
|
|
384
|
-
}
|
|
477
|
+
},
|
|
478
|
+
methodOptions: options
|
|
385
479
|
});
|
|
386
480
|
}
|
|
387
481
|
listDeliveriesAutoPaginating(id, params) {
|
|
388
|
-
return new Page(
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
482
|
+
return new Page(
|
|
483
|
+
this.client,
|
|
484
|
+
`/webhooks/${encodeURIComponent(id)}/deliveries`,
|
|
485
|
+
{
|
|
486
|
+
status: params?.status,
|
|
487
|
+
event_type: params?.event_type,
|
|
488
|
+
limit: params?.limit
|
|
489
|
+
}
|
|
490
|
+
);
|
|
393
491
|
}
|
|
394
492
|
};
|
|
395
493
|
|
|
396
494
|
// src/version.ts
|
|
397
|
-
var VERSION = "0.1.
|
|
495
|
+
var VERSION = "0.1.20";
|
|
398
496
|
|
|
399
497
|
// src/client.ts
|
|
400
|
-
var DEFAULT_BASE_URL = "https://
|
|
498
|
+
var DEFAULT_BASE_URL = "https://shipmail.to/api/v1";
|
|
401
499
|
var DEFAULT_MAX_RETRIES = 2;
|
|
402
500
|
var DEFAULT_TIMEOUT = 3e4;
|
|
403
501
|
function isRetryableStatus(status) {
|
|
404
502
|
return status === 429 || status >= 500;
|
|
405
503
|
}
|
|
406
|
-
function calculateBackoff(attempt) {
|
|
504
|
+
function calculateBackoff(attempt, retryAfterMs) {
|
|
505
|
+
if (retryAfterMs !== void 0 && retryAfterMs > 0) {
|
|
506
|
+
return retryAfterMs;
|
|
507
|
+
}
|
|
407
508
|
const base = Math.min(2 ** attempt * 500, 8e3);
|
|
408
509
|
const jitter = Math.random() * base * 0.5;
|
|
409
510
|
return base + jitter;
|
|
@@ -416,6 +517,10 @@ function isApiErrorBody(value) {
|
|
|
416
517
|
return typeof error["type"] === "string" && typeof error["message"] === "string";
|
|
417
518
|
}
|
|
418
519
|
var ShipMailClient = class {
|
|
520
|
+
/**
|
|
521
|
+
* Create a new ShipMail client.
|
|
522
|
+
* @param config - API key string or full configuration object.
|
|
523
|
+
*/
|
|
419
524
|
constructor(config) {
|
|
420
525
|
if (typeof config === "string") {
|
|
421
526
|
this.apiKey = config;
|
|
@@ -423,16 +528,19 @@ var ShipMailClient = class {
|
|
|
423
528
|
this.maxRetries = DEFAULT_MAX_RETRIES;
|
|
424
529
|
this.timeout = DEFAULT_TIMEOUT;
|
|
425
530
|
this.fetchFn = globalThis.fetch;
|
|
531
|
+
this.defaultHeaders = {};
|
|
426
532
|
} else {
|
|
427
533
|
this.apiKey = config.apiKey;
|
|
428
534
|
this.baseUrl = config.baseUrl ?? DEFAULT_BASE_URL;
|
|
429
535
|
this.maxRetries = config.maxRetries ?? DEFAULT_MAX_RETRIES;
|
|
430
536
|
this.timeout = config.timeout ?? DEFAULT_TIMEOUT;
|
|
431
537
|
this.fetchFn = config.fetch ?? globalThis.fetch;
|
|
538
|
+
this.defaultHeaders = config.defaultHeaders ? { ...config.defaultHeaders } : {};
|
|
432
539
|
}
|
|
433
540
|
this.domains = new Domains(this);
|
|
434
541
|
this.mailboxes = new Mailboxes(this);
|
|
435
542
|
this.messages = new Messages(this);
|
|
543
|
+
this.suppressions = new Suppressions(this);
|
|
436
544
|
this.threads = new Threads(this);
|
|
437
545
|
this.webhooks = new Webhooks(this);
|
|
438
546
|
this.status = new Status(this);
|
|
@@ -440,18 +548,25 @@ var ShipMailClient = class {
|
|
|
440
548
|
async request(options) {
|
|
441
549
|
const url = this.buildUrl(options.path, options.query);
|
|
442
550
|
let lastError;
|
|
551
|
+
const methodOpts = options.methodOptions;
|
|
443
552
|
for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
|
|
444
553
|
if (attempt > 0) {
|
|
445
|
-
const
|
|
554
|
+
const retryAfterMs = lastError instanceof RateLimitError && lastError.retryAfter !== void 0 ? lastError.retryAfter * 1e3 : void 0;
|
|
555
|
+
const delay = calculateBackoff(attempt - 1, retryAfterMs);
|
|
446
556
|
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
447
557
|
}
|
|
448
558
|
let response;
|
|
449
559
|
try {
|
|
450
560
|
const headers = {
|
|
561
|
+
...this.defaultHeaders,
|
|
451
562
|
Authorization: `Bearer ${this.apiKey}`,
|
|
452
563
|
"User-Agent": `shipmail-node/${VERSION}`,
|
|
453
|
-
Accept: "application/json"
|
|
564
|
+
Accept: "application/json",
|
|
565
|
+
...methodOpts?.headers
|
|
454
566
|
};
|
|
567
|
+
if (methodOpts?.idempotencyKey) {
|
|
568
|
+
headers["Idempotency-Key"] = methodOpts.idempotencyKey;
|
|
569
|
+
}
|
|
455
570
|
let body = null;
|
|
456
571
|
if (options.rawBody !== void 0) {
|
|
457
572
|
headers["Content-Type"] = options.contentType ?? "application/octet-stream";
|
|
@@ -460,11 +575,12 @@ var ShipMailClient = class {
|
|
|
460
575
|
headers["Content-Type"] = "application/json";
|
|
461
576
|
body = JSON.stringify(options.body);
|
|
462
577
|
}
|
|
578
|
+
const effectiveTimeout = methodOpts?.timeout ?? this.timeout;
|
|
463
579
|
response = await this.fetchFn(url, {
|
|
464
580
|
method: options.method,
|
|
465
581
|
headers,
|
|
466
582
|
body,
|
|
467
|
-
signal: AbortSignal.timeout(
|
|
583
|
+
signal: methodOpts?.signal ?? AbortSignal.timeout(effectiveTimeout)
|
|
468
584
|
});
|
|
469
585
|
} catch (err) {
|
|
470
586
|
lastError = new ConnectionError(err instanceof Error ? err.message : "Request failed");
|
|
@@ -513,15 +629,31 @@ var ShipMailClient = class {
|
|
|
513
629
|
// src/types/common.ts
|
|
514
630
|
var WEBHOOK_EVENT_TYPES = [
|
|
515
631
|
"message.received",
|
|
516
|
-
"message.queued",
|
|
517
632
|
"message.sent",
|
|
518
633
|
"message.delivered",
|
|
519
634
|
"message.bounced",
|
|
520
635
|
"message.complained",
|
|
521
636
|
"domain.verified",
|
|
522
637
|
"domain.verification_failed",
|
|
523
|
-
"domain.degraded"
|
|
638
|
+
"domain.degraded",
|
|
639
|
+
"org.reputation_warning",
|
|
640
|
+
"org.sending_throttled",
|
|
641
|
+
"org.sending_suspended",
|
|
642
|
+
"org.reputation_recovered"
|
|
524
643
|
];
|
|
644
|
+
var DOMAIN_STATUSES = ["pending", "verifying", "verified", "degraded", "failed"];
|
|
645
|
+
var MESSAGE_STATUSES = [
|
|
646
|
+
"queued",
|
|
647
|
+
"sent",
|
|
648
|
+
"delivered",
|
|
649
|
+
"bounced",
|
|
650
|
+
"complained",
|
|
651
|
+
"failed"
|
|
652
|
+
];
|
|
653
|
+
var MESSAGE_SOURCES = ["api", "dashboard", "inbound", "smtp"];
|
|
654
|
+
var WEBHOOK_DELIVERY_STATUSES = ["pending", "delivered", "failed"];
|
|
655
|
+
|
|
656
|
+
// src/webhook-verification.ts
|
|
525
657
|
var DEFAULT_TOLERANCE_SECONDS = 300;
|
|
526
658
|
function getHeader(headers, name) {
|
|
527
659
|
if (headers instanceof Headers) {
|
|
@@ -529,12 +661,13 @@ function getHeader(headers, name) {
|
|
|
529
661
|
}
|
|
530
662
|
return headers[name] ?? headers[name.toLowerCase()];
|
|
531
663
|
}
|
|
532
|
-
function signPayload(secret, timestamp, body) {
|
|
664
|
+
async function signPayload(secret, timestamp, body) {
|
|
665
|
+
const { createHmac } = await import('crypto');
|
|
533
666
|
const payload = `v1=${timestamp}
|
|
534
667
|
${body}`;
|
|
535
|
-
return
|
|
668
|
+
return createHmac("sha256", secret).update(payload).digest("hex");
|
|
536
669
|
}
|
|
537
|
-
function verifyWebhook(body, headers, secret, options) {
|
|
670
|
+
async function verifyWebhook(body, headers, secret, options) {
|
|
538
671
|
const signature = getHeader(headers, "x-shipmail-signature");
|
|
539
672
|
const timestampStr = getHeader(headers, "x-shipmail-timestamp");
|
|
540
673
|
if (!signature) {
|
|
@@ -552,10 +685,11 @@ function verifyWebhook(body, headers, secret, options) {
|
|
|
552
685
|
if (Math.abs(now - timestamp) > tolerance) {
|
|
553
686
|
throw new WebhookVerificationError("Webhook timestamp is outside tolerance window");
|
|
554
687
|
}
|
|
555
|
-
const expected = signPayload(secret, timestamp, body);
|
|
688
|
+
const expected = await signPayload(secret, timestamp, body);
|
|
689
|
+
const { timingSafeEqual } = await import('crypto');
|
|
556
690
|
const sigBuffer = Buffer.from(signature, "hex");
|
|
557
691
|
const expectedBuffer = Buffer.from(expected, "hex");
|
|
558
|
-
if (sigBuffer.length !== expectedBuffer.length || !
|
|
692
|
+
if (sigBuffer.length !== expectedBuffer.length || !timingSafeEqual(sigBuffer, expectedBuffer)) {
|
|
559
693
|
throw new WebhookVerificationError("Webhook signature verification failed");
|
|
560
694
|
}
|
|
561
695
|
let parsed;
|
|
@@ -571,13 +705,18 @@ exports.AuthenticationError = AuthenticationError;
|
|
|
571
705
|
exports.AuthorizationError = AuthorizationError;
|
|
572
706
|
exports.ConflictError = ConflictError;
|
|
573
707
|
exports.ConnectionError = ConnectionError;
|
|
708
|
+
exports.DOMAIN_STATUSES = DOMAIN_STATUSES;
|
|
574
709
|
exports.InternalServerError = InternalServerError;
|
|
710
|
+
exports.MESSAGE_SOURCES = MESSAGE_SOURCES;
|
|
711
|
+
exports.MESSAGE_STATUSES = MESSAGE_STATUSES;
|
|
575
712
|
exports.NotFoundError = NotFoundError;
|
|
576
713
|
exports.Page = Page;
|
|
714
|
+
exports.QuotaExceededError = QuotaExceededError;
|
|
577
715
|
exports.RateLimitError = RateLimitError;
|
|
578
716
|
exports.ShipMailClient = ShipMailClient;
|
|
579
717
|
exports.ShipMailError = ShipMailError;
|
|
580
718
|
exports.ValidationError = ValidationError;
|
|
719
|
+
exports.WEBHOOK_DELIVERY_STATUSES = WEBHOOK_DELIVERY_STATUSES;
|
|
581
720
|
exports.WEBHOOK_EVENT_TYPES = WEBHOOK_EVENT_TYPES;
|
|
582
721
|
exports.WebhookVerificationError = WebhookVerificationError;
|
|
583
722
|
exports.default = ShipMailClient;
|