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