mailbreeze 0.1.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 +360 -0
- package/dist/index.cjs +1003 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +1280 -0
- package/dist/index.d.ts +1280 -0
- package/dist/index.js +995 -0
- package/dist/index.js.map +1 -0
- package/package.json +66 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,995 @@
|
|
|
1
|
+
// src/http/errors.ts
|
|
2
|
+
var MailBreezeError = class extends Error {
|
|
3
|
+
/** Error code for programmatic handling */
|
|
4
|
+
code;
|
|
5
|
+
/** HTTP status code (if applicable) */
|
|
6
|
+
statusCode;
|
|
7
|
+
/** Unique request ID for debugging with support */
|
|
8
|
+
requestId;
|
|
9
|
+
/** Additional error details (e.g., field-level validation errors) */
|
|
10
|
+
details;
|
|
11
|
+
constructor(message, code, statusCode, requestId, details) {
|
|
12
|
+
super(message);
|
|
13
|
+
this.name = "MailBreezeError";
|
|
14
|
+
this.code = code;
|
|
15
|
+
if (statusCode !== void 0) this.statusCode = statusCode;
|
|
16
|
+
if (requestId !== void 0) this.requestId = requestId;
|
|
17
|
+
if (details !== void 0) this.details = details;
|
|
18
|
+
if (Error.captureStackTrace) {
|
|
19
|
+
Error.captureStackTrace(this, this.constructor);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Serialize error to JSON-friendly object.
|
|
24
|
+
* Useful for logging and error reporting.
|
|
25
|
+
*/
|
|
26
|
+
toJSON() {
|
|
27
|
+
return {
|
|
28
|
+
name: this.name,
|
|
29
|
+
message: this.message,
|
|
30
|
+
code: this.code,
|
|
31
|
+
statusCode: this.statusCode,
|
|
32
|
+
requestId: this.requestId,
|
|
33
|
+
details: this.details
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
var AuthenticationError = class extends MailBreezeError {
|
|
38
|
+
constructor(message, code = "AUTHENTICATION_ERROR", requestId) {
|
|
39
|
+
super(message, code, 401, requestId);
|
|
40
|
+
this.name = "AuthenticationError";
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
var ValidationError = class extends MailBreezeError {
|
|
44
|
+
constructor(message, code = "VALIDATION_ERROR", requestId, details) {
|
|
45
|
+
super(message, code, 400, requestId, details);
|
|
46
|
+
this.name = "ValidationError";
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
var NotFoundError = class extends MailBreezeError {
|
|
50
|
+
constructor(message, code = "NOT_FOUND", requestId) {
|
|
51
|
+
super(message, code, 404, requestId);
|
|
52
|
+
this.name = "NotFoundError";
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
var RateLimitError = class extends MailBreezeError {
|
|
56
|
+
/** Seconds to wait before retrying */
|
|
57
|
+
retryAfter;
|
|
58
|
+
constructor(message, code = "RATE_LIMIT_EXCEEDED", requestId, retryAfter) {
|
|
59
|
+
super(message, code, 429, requestId);
|
|
60
|
+
this.name = "RateLimitError";
|
|
61
|
+
if (retryAfter !== void 0) this.retryAfter = retryAfter;
|
|
62
|
+
}
|
|
63
|
+
toJSON() {
|
|
64
|
+
return {
|
|
65
|
+
...super.toJSON(),
|
|
66
|
+
retryAfter: this.retryAfter
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
var ServerError = class extends MailBreezeError {
|
|
71
|
+
constructor(message, code = "SERVER_ERROR", statusCode = 500, requestId) {
|
|
72
|
+
super(message, code, statusCode, requestId);
|
|
73
|
+
this.name = "ServerError";
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
function createErrorFromResponse(statusCode, body, requestId, retryAfter) {
|
|
77
|
+
const message = body?.message ?? "Unknown error";
|
|
78
|
+
const code = body?.code ?? "UNKNOWN_ERROR";
|
|
79
|
+
const details = body?.details;
|
|
80
|
+
switch (statusCode) {
|
|
81
|
+
case 400:
|
|
82
|
+
return new ValidationError(message, code, requestId, details);
|
|
83
|
+
case 401:
|
|
84
|
+
return new AuthenticationError(message, code, requestId);
|
|
85
|
+
case 404:
|
|
86
|
+
return new NotFoundError(message, code, requestId);
|
|
87
|
+
case 429:
|
|
88
|
+
return new RateLimitError(message, code, requestId, retryAfter);
|
|
89
|
+
default:
|
|
90
|
+
if (statusCode >= 500) {
|
|
91
|
+
return new ServerError(message, code, statusCode, requestId);
|
|
92
|
+
}
|
|
93
|
+
return new MailBreezeError(message, code, statusCode, requestId, details);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// src/http/fetcher.ts
|
|
98
|
+
var SDK_VERSION = "0.1.0";
|
|
99
|
+
var Fetcher = class {
|
|
100
|
+
config;
|
|
101
|
+
constructor(config) {
|
|
102
|
+
this.config = {
|
|
103
|
+
...config,
|
|
104
|
+
baseUrl: config.baseUrl.replace(/\/$/, "")
|
|
105
|
+
// Remove trailing slash
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Make an HTTP request to the API.
|
|
110
|
+
*
|
|
111
|
+
* @param method - HTTP method
|
|
112
|
+
* @param path - API path (will be appended to baseUrl)
|
|
113
|
+
* @param body - Request body (for POST/PUT/PATCH)
|
|
114
|
+
* @param query - Query parameters
|
|
115
|
+
* @param options - Additional request options
|
|
116
|
+
* @returns Parsed response data
|
|
117
|
+
*/
|
|
118
|
+
async request(method, path, body, query, options) {
|
|
119
|
+
const url = this.buildUrl(path, query);
|
|
120
|
+
const headers = this.buildHeaders(options?.idempotencyKey);
|
|
121
|
+
const timeout = options?.timeout ?? this.config.timeout;
|
|
122
|
+
let lastError;
|
|
123
|
+
let attempt = 0;
|
|
124
|
+
const maxAttempts = this.config.maxRetries + 1;
|
|
125
|
+
while (attempt < maxAttempts) {
|
|
126
|
+
attempt++;
|
|
127
|
+
try {
|
|
128
|
+
const result = await this.executeRequest(method, url, headers, body, timeout);
|
|
129
|
+
return result;
|
|
130
|
+
} catch (error) {
|
|
131
|
+
lastError = error;
|
|
132
|
+
if (!this.isRetryable(lastError, error)) {
|
|
133
|
+
throw lastError;
|
|
134
|
+
}
|
|
135
|
+
if (attempt >= maxAttempts) {
|
|
136
|
+
throw lastError;
|
|
137
|
+
}
|
|
138
|
+
const delay = this.getRetryDelay(lastError, attempt);
|
|
139
|
+
await this.sleep(delay);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
throw lastError ?? new MailBreezeError("Unexpected error during request", "UNEXPECTED_ERROR");
|
|
143
|
+
}
|
|
144
|
+
async executeRequest(method, url, headers, body, timeout) {
|
|
145
|
+
const controller = new AbortController();
|
|
146
|
+
const timeoutId = timeout ? setTimeout(() => controller.abort(), timeout) : void 0;
|
|
147
|
+
try {
|
|
148
|
+
const fetchOptions = {
|
|
149
|
+
method,
|
|
150
|
+
headers,
|
|
151
|
+
signal: controller.signal
|
|
152
|
+
};
|
|
153
|
+
if (body && method !== "GET" && method !== "HEAD") {
|
|
154
|
+
fetchOptions.body = JSON.stringify(body);
|
|
155
|
+
}
|
|
156
|
+
const response = await fetch(url, fetchOptions);
|
|
157
|
+
return await this.handleResponse(response);
|
|
158
|
+
} catch (error) {
|
|
159
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
160
|
+
throw new MailBreezeError("Request timeout", "TIMEOUT_ERROR", 0);
|
|
161
|
+
}
|
|
162
|
+
if (error instanceof Error && !(error instanceof MailBreezeError)) {
|
|
163
|
+
throw new ServerError(`Network error: ${error.message}`, "NETWORK_ERROR", 0);
|
|
164
|
+
}
|
|
165
|
+
throw error;
|
|
166
|
+
} finally {
|
|
167
|
+
if (timeoutId !== void 0) {
|
|
168
|
+
clearTimeout(timeoutId);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
async handleResponse(response) {
|
|
173
|
+
const requestId = response.headers.get("X-Request-Id") ?? void 0;
|
|
174
|
+
const retryAfter = response.headers.get("Retry-After");
|
|
175
|
+
const retryAfterSeconds = retryAfter ? Number.parseInt(retryAfter, 10) : void 0;
|
|
176
|
+
if (response.status === 204) {
|
|
177
|
+
return void 0;
|
|
178
|
+
}
|
|
179
|
+
let body;
|
|
180
|
+
try {
|
|
181
|
+
body = await response.json();
|
|
182
|
+
} catch {
|
|
183
|
+
if (!response.ok) {
|
|
184
|
+
throw createErrorFromResponse(response.status, void 0, requestId);
|
|
185
|
+
}
|
|
186
|
+
return void 0;
|
|
187
|
+
}
|
|
188
|
+
if (!body.success) {
|
|
189
|
+
const errorBody = body.error;
|
|
190
|
+
const statusCode = response.ok ? 400 : response.status;
|
|
191
|
+
throw createErrorFromResponse(statusCode, errorBody, requestId, retryAfterSeconds);
|
|
192
|
+
}
|
|
193
|
+
if (!response.ok) {
|
|
194
|
+
throw createErrorFromResponse(response.status, void 0, requestId, retryAfterSeconds);
|
|
195
|
+
}
|
|
196
|
+
return body.data;
|
|
197
|
+
}
|
|
198
|
+
buildUrl(path, query) {
|
|
199
|
+
const normalizedPath = path.startsWith("/") ? path : `/${path}`;
|
|
200
|
+
const url = new URL(`${this.config.baseUrl}${normalizedPath}`);
|
|
201
|
+
if (query) {
|
|
202
|
+
for (const [key, value] of Object.entries(query)) {
|
|
203
|
+
if (value !== void 0 && value !== null) {
|
|
204
|
+
url.searchParams.append(key, String(value));
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
return url.toString();
|
|
209
|
+
}
|
|
210
|
+
buildHeaders(idempotencyKey) {
|
|
211
|
+
const headers = {
|
|
212
|
+
"Content-Type": "application/json",
|
|
213
|
+
"User-Agent": `mailbreeze-js/${SDK_VERSION}`
|
|
214
|
+
};
|
|
215
|
+
if (this.config.authStyle === "bearer") {
|
|
216
|
+
headers.Authorization = `Bearer ${this.config.apiKey}`;
|
|
217
|
+
} else {
|
|
218
|
+
headers["X-API-Key"] = this.config.apiKey;
|
|
219
|
+
}
|
|
220
|
+
if (idempotencyKey) {
|
|
221
|
+
if (/[\r\n]/.test(idempotencyKey)) {
|
|
222
|
+
throw new MailBreezeError("Invalid idempotency key: contains invalid characters", "INVALID_IDEMPOTENCY_KEY");
|
|
223
|
+
}
|
|
224
|
+
headers["X-Idempotency-Key"] = idempotencyKey;
|
|
225
|
+
}
|
|
226
|
+
return headers;
|
|
227
|
+
}
|
|
228
|
+
isRetryable(error, originalError) {
|
|
229
|
+
if (originalError instanceof Error && originalError.name === "AbortError") {
|
|
230
|
+
return false;
|
|
231
|
+
}
|
|
232
|
+
if (error.statusCode === 429 || error.statusCode !== void 0 && error.statusCode >= 500) {
|
|
233
|
+
return true;
|
|
234
|
+
}
|
|
235
|
+
if (error.statusCode === 0 && error.code === "NETWORK_ERROR") {
|
|
236
|
+
return true;
|
|
237
|
+
}
|
|
238
|
+
return false;
|
|
239
|
+
}
|
|
240
|
+
getRetryDelay(error, attempt) {
|
|
241
|
+
if (error instanceof RateLimitError && error.retryAfter) {
|
|
242
|
+
return error.retryAfter * 1e3;
|
|
243
|
+
}
|
|
244
|
+
return 2 ** (attempt - 1) * 1e3;
|
|
245
|
+
}
|
|
246
|
+
sleep(ms) {
|
|
247
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
248
|
+
}
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
// src/resources/base.ts
|
|
252
|
+
var BaseResource = class {
|
|
253
|
+
fetcher;
|
|
254
|
+
domainId;
|
|
255
|
+
constructor(fetcher, domainId) {
|
|
256
|
+
this.fetcher = fetcher;
|
|
257
|
+
if (domainId !== void 0) this.domainId = domainId;
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Make a GET request.
|
|
261
|
+
*/
|
|
262
|
+
_get(path, query, options) {
|
|
263
|
+
return this.fetcher.request("GET", this.buildPath(path), void 0, this.addDomainId(query), options);
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Make a POST request.
|
|
267
|
+
*/
|
|
268
|
+
_post(path, body, query, options) {
|
|
269
|
+
return this.fetcher.request(
|
|
270
|
+
"POST",
|
|
271
|
+
this.buildPath(path),
|
|
272
|
+
body,
|
|
273
|
+
this.addDomainId(query),
|
|
274
|
+
options
|
|
275
|
+
);
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* Make a PUT request.
|
|
279
|
+
*/
|
|
280
|
+
_put(path, body, query, options) {
|
|
281
|
+
return this.fetcher.request(
|
|
282
|
+
"PUT",
|
|
283
|
+
this.buildPath(path),
|
|
284
|
+
body,
|
|
285
|
+
this.addDomainId(query),
|
|
286
|
+
options
|
|
287
|
+
);
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* Make a PATCH request.
|
|
291
|
+
*/
|
|
292
|
+
_patch(path, body, query, options) {
|
|
293
|
+
return this.fetcher.request(
|
|
294
|
+
"PATCH",
|
|
295
|
+
this.buildPath(path),
|
|
296
|
+
body,
|
|
297
|
+
this.addDomainId(query),
|
|
298
|
+
options
|
|
299
|
+
);
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* Make a DELETE request.
|
|
303
|
+
*/
|
|
304
|
+
_delete(path, query, options) {
|
|
305
|
+
return this.fetcher.request("DELETE", this.buildPath(path), void 0, this.addDomainId(query), options);
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Build the full path including any base path for the resource.
|
|
309
|
+
*/
|
|
310
|
+
buildPath(path) {
|
|
311
|
+
return path;
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* Add domain ID to query params if configured.
|
|
315
|
+
*/
|
|
316
|
+
addDomainId(query) {
|
|
317
|
+
if (!this.domainId) {
|
|
318
|
+
return query;
|
|
319
|
+
}
|
|
320
|
+
return { ...query, domainId: this.domainId };
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* Helper to extract pagination from list response.
|
|
324
|
+
*/
|
|
325
|
+
extractPaginatedList(response) {
|
|
326
|
+
if (Array.isArray(response)) {
|
|
327
|
+
return {
|
|
328
|
+
data: response,
|
|
329
|
+
pagination: {
|
|
330
|
+
page: 1,
|
|
331
|
+
limit: response.length,
|
|
332
|
+
total: response.length,
|
|
333
|
+
totalPages: 1,
|
|
334
|
+
hasNext: false,
|
|
335
|
+
hasPrev: false
|
|
336
|
+
}
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
return {
|
|
340
|
+
data: response.data,
|
|
341
|
+
pagination: response.pagination
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* Convert list params to query object.
|
|
346
|
+
*/
|
|
347
|
+
listParamsToQuery(params) {
|
|
348
|
+
if (!params) return void 0;
|
|
349
|
+
const query = {};
|
|
350
|
+
if (params.page !== void 0) query.page = params.page;
|
|
351
|
+
if (params.limit !== void 0) query.limit = params.limit;
|
|
352
|
+
return Object.keys(query).length > 0 ? query : void 0;
|
|
353
|
+
}
|
|
354
|
+
};
|
|
355
|
+
|
|
356
|
+
// src/resources/attachments.ts
|
|
357
|
+
var Attachments = class extends BaseResource {
|
|
358
|
+
/**
|
|
359
|
+
* Create a presigned URL for uploading an attachment.
|
|
360
|
+
*
|
|
361
|
+
* @param params - Attachment metadata
|
|
362
|
+
* @returns Upload URL and attachment ID
|
|
363
|
+
*
|
|
364
|
+
* @example
|
|
365
|
+
* ```ts
|
|
366
|
+
* const { attachmentId, uploadUrl, uploadToken, expiresAt } =
|
|
367
|
+
* await mailbreeze.attachments.createUpload({
|
|
368
|
+
* fileName: "document.pdf",
|
|
369
|
+
* contentType: "application/pdf",
|
|
370
|
+
* fileSize: 2048000,
|
|
371
|
+
* });
|
|
372
|
+
* ```
|
|
373
|
+
*/
|
|
374
|
+
async createUpload(params) {
|
|
375
|
+
return this._post("/attachments/upload", params);
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* Confirm that an attachment has been uploaded.
|
|
379
|
+
*
|
|
380
|
+
* Must be called after uploading the file to the presigned URL.
|
|
381
|
+
* The attachment is only available for use after confirmation.
|
|
382
|
+
*
|
|
383
|
+
* @param params - Confirmation parameters including upload token
|
|
384
|
+
* @returns Confirmed attachment record
|
|
385
|
+
*
|
|
386
|
+
* @example
|
|
387
|
+
* ```ts
|
|
388
|
+
* const attachment = await mailbreeze.attachments.confirm({
|
|
389
|
+
* uploadToken: "token_xxx",
|
|
390
|
+
* });
|
|
391
|
+
* console.log(attachment.status); // "uploaded"
|
|
392
|
+
* ```
|
|
393
|
+
*/
|
|
394
|
+
async confirm(params) {
|
|
395
|
+
return this._post("/attachments/confirm", params);
|
|
396
|
+
}
|
|
397
|
+
};
|
|
398
|
+
|
|
399
|
+
// src/resources/automations.ts
|
|
400
|
+
var Automations = class extends BaseResource {
|
|
401
|
+
/**
|
|
402
|
+
* Enrollments sub-resource for listing and cancelling.
|
|
403
|
+
*/
|
|
404
|
+
enrollments;
|
|
405
|
+
constructor(fetcher, domainId) {
|
|
406
|
+
super(fetcher, domainId);
|
|
407
|
+
this.enrollments = new AutomationEnrollments(fetcher, domainId);
|
|
408
|
+
}
|
|
409
|
+
/**
|
|
410
|
+
* Enroll a contact in an automation.
|
|
411
|
+
*
|
|
412
|
+
* @param params - Enrollment parameters
|
|
413
|
+
* @returns Enrollment result
|
|
414
|
+
*
|
|
415
|
+
* @example
|
|
416
|
+
* ```ts
|
|
417
|
+
* const enrollment = await mailbreeze.automations.enroll({
|
|
418
|
+
* automationId: "auto_welcome",
|
|
419
|
+
* contactId: "contact_xxx",
|
|
420
|
+
* variables: { firstName: "John" },
|
|
421
|
+
* });
|
|
422
|
+
* console.log(enrollment.enrollmentId);
|
|
423
|
+
* ```
|
|
424
|
+
*/
|
|
425
|
+
async enroll(params) {
|
|
426
|
+
return this._post(`/automations/${params.automationId}/enroll`, {
|
|
427
|
+
contactId: params.contactId,
|
|
428
|
+
variables: params.variables
|
|
429
|
+
});
|
|
430
|
+
}
|
|
431
|
+
};
|
|
432
|
+
var AutomationEnrollments = class extends BaseResource {
|
|
433
|
+
/**
|
|
434
|
+
* List automation enrollments.
|
|
435
|
+
*
|
|
436
|
+
* @param params - Filter and pagination options
|
|
437
|
+
* @returns Paginated list of enrollments
|
|
438
|
+
*
|
|
439
|
+
* @example
|
|
440
|
+
* ```ts
|
|
441
|
+
* const { data, pagination } = await mailbreeze.automations.enrollments.list({
|
|
442
|
+
* automationId: "auto_xxx",
|
|
443
|
+
* status: "active",
|
|
444
|
+
* });
|
|
445
|
+
* ```
|
|
446
|
+
*/
|
|
447
|
+
async list(params) {
|
|
448
|
+
const query = {
|
|
449
|
+
...this.listParamsToQuery(params)
|
|
450
|
+
};
|
|
451
|
+
if (params?.automationId) query.automationId = params.automationId;
|
|
452
|
+
if (params?.status) query.status = params.status;
|
|
453
|
+
const response = await this._get("/automation-enrollments", Object.keys(query).length > 0 ? query : void 0);
|
|
454
|
+
return this.extractPaginatedList(response);
|
|
455
|
+
}
|
|
456
|
+
/**
|
|
457
|
+
* Cancel an enrollment.
|
|
458
|
+
*
|
|
459
|
+
* The contact will stop receiving emails from this automation.
|
|
460
|
+
*
|
|
461
|
+
* @param enrollmentId - Enrollment ID
|
|
462
|
+
* @returns Cancellation result
|
|
463
|
+
*
|
|
464
|
+
* @example
|
|
465
|
+
* ```ts
|
|
466
|
+
* const result = await mailbreeze.automations.enrollments.cancel("enroll_xxx");
|
|
467
|
+
* console.log(result.cancelled); // true
|
|
468
|
+
* ```
|
|
469
|
+
*/
|
|
470
|
+
async cancel(enrollmentId) {
|
|
471
|
+
return this._post(`/automation-enrollments/${enrollmentId}/cancel`, {});
|
|
472
|
+
}
|
|
473
|
+
};
|
|
474
|
+
|
|
475
|
+
// src/resources/contacts.ts
|
|
476
|
+
var Contacts = class extends BaseResource {
|
|
477
|
+
listId;
|
|
478
|
+
constructor(fetcher, listId, domainId) {
|
|
479
|
+
super(fetcher, domainId);
|
|
480
|
+
this.listId = listId;
|
|
481
|
+
}
|
|
482
|
+
buildPath(path) {
|
|
483
|
+
return `/contact-lists/${this.listId}/contacts${path}`;
|
|
484
|
+
}
|
|
485
|
+
/**
|
|
486
|
+
* Create a new contact in the list.
|
|
487
|
+
*
|
|
488
|
+
* @param params - Contact data
|
|
489
|
+
* @returns Created contact record
|
|
490
|
+
*
|
|
491
|
+
* @example
|
|
492
|
+
* ```ts
|
|
493
|
+
* const contact = await contacts.create({
|
|
494
|
+
* email: "user@example.com",
|
|
495
|
+
* firstName: "Jane",
|
|
496
|
+
* lastName: "Smith",
|
|
497
|
+
* customFields: { plan: "pro" },
|
|
498
|
+
* });
|
|
499
|
+
* ```
|
|
500
|
+
*/
|
|
501
|
+
async create(params) {
|
|
502
|
+
return this._post("", params);
|
|
503
|
+
}
|
|
504
|
+
/**
|
|
505
|
+
* List contacts in the list.
|
|
506
|
+
*
|
|
507
|
+
* @param params - Filter and pagination options
|
|
508
|
+
* @returns Paginated list of contacts
|
|
509
|
+
*
|
|
510
|
+
* @example
|
|
511
|
+
* ```ts
|
|
512
|
+
* const { data, pagination } = await contacts.list({
|
|
513
|
+
* status: "active",
|
|
514
|
+
* page: 1,
|
|
515
|
+
* limit: 50,
|
|
516
|
+
* });
|
|
517
|
+
* ```
|
|
518
|
+
*/
|
|
519
|
+
async list(params) {
|
|
520
|
+
const query = {
|
|
521
|
+
...this.listParamsToQuery(params)
|
|
522
|
+
};
|
|
523
|
+
if (params?.status) query.status = params.status;
|
|
524
|
+
const response = await this._get(
|
|
525
|
+
"",
|
|
526
|
+
Object.keys(query).length > 0 ? query : void 0
|
|
527
|
+
);
|
|
528
|
+
return this.extractPaginatedList(response);
|
|
529
|
+
}
|
|
530
|
+
/**
|
|
531
|
+
* Get a contact by ID.
|
|
532
|
+
*
|
|
533
|
+
* @param id - Contact ID
|
|
534
|
+
* @returns Contact record
|
|
535
|
+
*
|
|
536
|
+
* @example
|
|
537
|
+
* ```ts
|
|
538
|
+
* const contact = await contacts.get("contact_xxx");
|
|
539
|
+
* ```
|
|
540
|
+
*/
|
|
541
|
+
async get(id) {
|
|
542
|
+
return this._get(`/${id}`);
|
|
543
|
+
}
|
|
544
|
+
/**
|
|
545
|
+
* Update a contact.
|
|
546
|
+
*
|
|
547
|
+
* @param id - Contact ID
|
|
548
|
+
* @param params - Fields to update
|
|
549
|
+
* @returns Updated contact record
|
|
550
|
+
*
|
|
551
|
+
* @example
|
|
552
|
+
* ```ts
|
|
553
|
+
* const contact = await contacts.update("contact_xxx", {
|
|
554
|
+
* firstName: "Updated Name",
|
|
555
|
+
* customFields: { plan: "enterprise" },
|
|
556
|
+
* });
|
|
557
|
+
* ```
|
|
558
|
+
*/
|
|
559
|
+
async update(id, params) {
|
|
560
|
+
return this._put(`/${id}`, params);
|
|
561
|
+
}
|
|
562
|
+
/**
|
|
563
|
+
* Delete a contact.
|
|
564
|
+
*
|
|
565
|
+
* @param id - Contact ID
|
|
566
|
+
* @returns void
|
|
567
|
+
*
|
|
568
|
+
* @example
|
|
569
|
+
* ```ts
|
|
570
|
+
* await contacts.delete("contact_xxx");
|
|
571
|
+
* ```
|
|
572
|
+
*/
|
|
573
|
+
async delete(id) {
|
|
574
|
+
await this._delete(`/${id}`);
|
|
575
|
+
}
|
|
576
|
+
/**
|
|
577
|
+
* Suppress a contact.
|
|
578
|
+
*
|
|
579
|
+
* Suppressed contacts will not receive any emails.
|
|
580
|
+
* This is different from unsubscribing - suppression is
|
|
581
|
+
* typically used for bounces, complaints, or manual removal.
|
|
582
|
+
*
|
|
583
|
+
* @param id - Contact ID
|
|
584
|
+
* @param reason - Reason for suppression
|
|
585
|
+
* @returns void
|
|
586
|
+
*
|
|
587
|
+
* @example
|
|
588
|
+
* ```ts
|
|
589
|
+
* await contacts.suppress("contact_xxx", "manual");
|
|
590
|
+
* ```
|
|
591
|
+
*/
|
|
592
|
+
async suppress(id, reason) {
|
|
593
|
+
await this._post(`/${id}/suppress`, { reason });
|
|
594
|
+
}
|
|
595
|
+
};
|
|
596
|
+
|
|
597
|
+
// src/resources/emails.ts
|
|
598
|
+
var Emails = class extends BaseResource {
|
|
599
|
+
/**
|
|
600
|
+
* Send an email.
|
|
601
|
+
*
|
|
602
|
+
* @param params - Email parameters
|
|
603
|
+
* @returns Send result with email ID and status
|
|
604
|
+
*
|
|
605
|
+
* @example
|
|
606
|
+
* ```ts
|
|
607
|
+
* const result = await mailbreeze.emails.send({
|
|
608
|
+
* from: "hello@yourdomain.com",
|
|
609
|
+
* to: "user@example.com",
|
|
610
|
+
* subject: "Hello",
|
|
611
|
+
* html: "<p>Hello World!</p>",
|
|
612
|
+
* });
|
|
613
|
+
* console.log(result.id); // email_xxx
|
|
614
|
+
* ```
|
|
615
|
+
*/
|
|
616
|
+
async send(params) {
|
|
617
|
+
const { idempotencyKey, ...body } = params;
|
|
618
|
+
const normalizedBody = {
|
|
619
|
+
...body,
|
|
620
|
+
to: Array.isArray(body.to) ? body.to : [body.to],
|
|
621
|
+
cc: body.cc ? Array.isArray(body.cc) ? body.cc : [body.cc] : void 0,
|
|
622
|
+
bcc: body.bcc ? Array.isArray(body.bcc) ? body.bcc : [body.bcc] : void 0
|
|
623
|
+
};
|
|
624
|
+
return this._post(
|
|
625
|
+
"/emails",
|
|
626
|
+
normalizedBody,
|
|
627
|
+
void 0,
|
|
628
|
+
idempotencyKey ? { idempotencyKey } : void 0
|
|
629
|
+
);
|
|
630
|
+
}
|
|
631
|
+
/**
|
|
632
|
+
* List sent emails with optional filtering.
|
|
633
|
+
*
|
|
634
|
+
* @param params - Filter and pagination options
|
|
635
|
+
* @returns Paginated list of emails
|
|
636
|
+
*
|
|
637
|
+
* @example
|
|
638
|
+
* ```ts
|
|
639
|
+
* const { data, pagination } = await mailbreeze.emails.list({
|
|
640
|
+
* status: "delivered",
|
|
641
|
+
* page: 1,
|
|
642
|
+
* limit: 20,
|
|
643
|
+
* });
|
|
644
|
+
* ```
|
|
645
|
+
*/
|
|
646
|
+
async list(params) {
|
|
647
|
+
const query = {
|
|
648
|
+
...this.listParamsToQuery(params)
|
|
649
|
+
};
|
|
650
|
+
if (params?.status) query.status = params.status;
|
|
651
|
+
if (params?.to) query.to = params.to;
|
|
652
|
+
if (params?.from) query.from = params.from;
|
|
653
|
+
if (params?.startDate) query.startDate = params.startDate;
|
|
654
|
+
if (params?.endDate) query.endDate = params.endDate;
|
|
655
|
+
const response = await this._get(
|
|
656
|
+
"/emails",
|
|
657
|
+
Object.keys(query).length > 0 ? query : void 0
|
|
658
|
+
);
|
|
659
|
+
return this.extractPaginatedList(response);
|
|
660
|
+
}
|
|
661
|
+
/**
|
|
662
|
+
* Get a single email by ID.
|
|
663
|
+
*
|
|
664
|
+
* @param id - Email ID
|
|
665
|
+
* @returns Email record
|
|
666
|
+
*
|
|
667
|
+
* @example
|
|
668
|
+
* ```ts
|
|
669
|
+
* const email = await mailbreeze.emails.get("email_xxx");
|
|
670
|
+
* console.log(email.status); // "delivered"
|
|
671
|
+
* ```
|
|
672
|
+
*/
|
|
673
|
+
async get(id) {
|
|
674
|
+
return this._get(`/emails/${id}`);
|
|
675
|
+
}
|
|
676
|
+
/**
|
|
677
|
+
* Get email statistics for the domain.
|
|
678
|
+
*
|
|
679
|
+
* @returns Email statistics including delivery rates
|
|
680
|
+
*
|
|
681
|
+
* @example
|
|
682
|
+
* ```ts
|
|
683
|
+
* const stats = await mailbreeze.emails.stats();
|
|
684
|
+
* console.log(stats.deliveryRate); // 0.98
|
|
685
|
+
* ```
|
|
686
|
+
*/
|
|
687
|
+
async stats() {
|
|
688
|
+
return this._get("/emails/stats");
|
|
689
|
+
}
|
|
690
|
+
};
|
|
691
|
+
|
|
692
|
+
// src/resources/lists.ts
|
|
693
|
+
var Lists = class extends BaseResource {
|
|
694
|
+
/**
|
|
695
|
+
* Create a new contact list.
|
|
696
|
+
*
|
|
697
|
+
* @param params - List configuration
|
|
698
|
+
* @returns Created list record
|
|
699
|
+
*
|
|
700
|
+
* @example
|
|
701
|
+
* ```ts
|
|
702
|
+
* const list = await mailbreeze.lists.create({
|
|
703
|
+
* name: "VIP Customers",
|
|
704
|
+
* customFields: [
|
|
705
|
+
* { key: "tier", label: "Tier", type: "select", options: ["gold", "platinum"] },
|
|
706
|
+
* ],
|
|
707
|
+
* });
|
|
708
|
+
* ```
|
|
709
|
+
*/
|
|
710
|
+
async create(params) {
|
|
711
|
+
return this._post("/contact-lists", params);
|
|
712
|
+
}
|
|
713
|
+
/**
|
|
714
|
+
* List all contact lists.
|
|
715
|
+
*
|
|
716
|
+
* @param params - Pagination options
|
|
717
|
+
* @returns Paginated list of contact lists
|
|
718
|
+
*
|
|
719
|
+
* @example
|
|
720
|
+
* ```ts
|
|
721
|
+
* const { data, pagination } = await mailbreeze.lists.list({ page: 1, limit: 10 });
|
|
722
|
+
* ```
|
|
723
|
+
*/
|
|
724
|
+
async list(params) {
|
|
725
|
+
const response = await this._get(
|
|
726
|
+
"/contact-lists",
|
|
727
|
+
this.listParamsToQuery(params)
|
|
728
|
+
);
|
|
729
|
+
return this.extractPaginatedList(response);
|
|
730
|
+
}
|
|
731
|
+
/**
|
|
732
|
+
* Get a contact list by ID.
|
|
733
|
+
*
|
|
734
|
+
* @param id - List ID
|
|
735
|
+
* @returns Contact list record
|
|
736
|
+
*
|
|
737
|
+
* @example
|
|
738
|
+
* ```ts
|
|
739
|
+
* const list = await mailbreeze.lists.get("list_xxx");
|
|
740
|
+
* ```
|
|
741
|
+
*/
|
|
742
|
+
async get(id) {
|
|
743
|
+
return this._get(`/contact-lists/${id}`);
|
|
744
|
+
}
|
|
745
|
+
/**
|
|
746
|
+
* Update a contact list.
|
|
747
|
+
*
|
|
748
|
+
* @param id - List ID
|
|
749
|
+
* @param params - Fields to update
|
|
750
|
+
* @returns Updated list record
|
|
751
|
+
*
|
|
752
|
+
* @example
|
|
753
|
+
* ```ts
|
|
754
|
+
* const list = await mailbreeze.lists.update("list_xxx", {
|
|
755
|
+
* name: "Updated List Name",
|
|
756
|
+
* });
|
|
757
|
+
* ```
|
|
758
|
+
*/
|
|
759
|
+
async update(id, params) {
|
|
760
|
+
return this._patch(`/contact-lists/${id}`, params);
|
|
761
|
+
}
|
|
762
|
+
/**
|
|
763
|
+
* Delete a contact list.
|
|
764
|
+
*
|
|
765
|
+
* Warning: This will also delete all contacts in the list.
|
|
766
|
+
*
|
|
767
|
+
* @param id - List ID
|
|
768
|
+
* @returns void
|
|
769
|
+
*
|
|
770
|
+
* @example
|
|
771
|
+
* ```ts
|
|
772
|
+
* await mailbreeze.lists.delete("list_xxx");
|
|
773
|
+
* ```
|
|
774
|
+
*/
|
|
775
|
+
async delete(id) {
|
|
776
|
+
await this._delete(`/contact-lists/${id}`);
|
|
777
|
+
}
|
|
778
|
+
/**
|
|
779
|
+
* Get statistics for a contact list.
|
|
780
|
+
*
|
|
781
|
+
* @param id - List ID
|
|
782
|
+
* @returns List statistics
|
|
783
|
+
*
|
|
784
|
+
* @example
|
|
785
|
+
* ```ts
|
|
786
|
+
* const stats = await mailbreeze.lists.stats("list_xxx");
|
|
787
|
+
* console.log(`Active: ${stats.activeContacts}, Bounced: ${stats.bouncedContacts}`);
|
|
788
|
+
* ```
|
|
789
|
+
*/
|
|
790
|
+
async stats(id) {
|
|
791
|
+
return this._get(`/contact-lists/${id}/stats`);
|
|
792
|
+
}
|
|
793
|
+
};
|
|
794
|
+
|
|
795
|
+
// src/resources/verification.ts
|
|
796
|
+
var Verification = class extends BaseResource {
|
|
797
|
+
/**
|
|
798
|
+
* Verify a single email address.
|
|
799
|
+
*
|
|
800
|
+
* This is a synchronous operation - the result is returned immediately.
|
|
801
|
+
* Results are cached for 24 hours.
|
|
802
|
+
*
|
|
803
|
+
* @param email - Email address to verify
|
|
804
|
+
* @returns Verification result
|
|
805
|
+
*
|
|
806
|
+
* @example
|
|
807
|
+
* ```ts
|
|
808
|
+
* const result = await mailbreeze.verification.verify("test@example.com");
|
|
809
|
+
* console.log(result.isValid); // true
|
|
810
|
+
* console.log(result.result); // "valid"
|
|
811
|
+
* console.log(result.details?.isDisposable); // false
|
|
812
|
+
* ```
|
|
813
|
+
*/
|
|
814
|
+
async verify(email) {
|
|
815
|
+
return this._post("/email-verification/single", { email });
|
|
816
|
+
}
|
|
817
|
+
/**
|
|
818
|
+
* Start batch verification.
|
|
819
|
+
*
|
|
820
|
+
* Submits a batch of emails for verification. For large batches,
|
|
821
|
+
* results are processed asynchronously - poll with `get()` for status.
|
|
822
|
+
*
|
|
823
|
+
* If all emails are cached, results are returned immediately.
|
|
824
|
+
*
|
|
825
|
+
* @param params - Batch parameters including emails array
|
|
826
|
+
* @returns Batch verification result with ID for polling
|
|
827
|
+
*
|
|
828
|
+
* @example
|
|
829
|
+
* ```ts
|
|
830
|
+
* const batch = await mailbreeze.verification.batch({
|
|
831
|
+
* emails: ["user1@example.com", "user2@example.com", "user3@example.com"],
|
|
832
|
+
* });
|
|
833
|
+
*
|
|
834
|
+
* if (batch.results) {
|
|
835
|
+
* // All cached - results available immediately
|
|
836
|
+
* console.log(batch.results);
|
|
837
|
+
* } else {
|
|
838
|
+
* // Poll for results
|
|
839
|
+
* const status = await mailbreeze.verification.get(batch.verificationId);
|
|
840
|
+
* }
|
|
841
|
+
* ```
|
|
842
|
+
*/
|
|
843
|
+
async batch(params) {
|
|
844
|
+
return this._post("/email-verification/batch", params);
|
|
845
|
+
}
|
|
846
|
+
/**
|
|
847
|
+
* Get verification batch status and results.
|
|
848
|
+
*
|
|
849
|
+
* @param verificationId - Verification batch ID
|
|
850
|
+
* @returns Current status and results when complete
|
|
851
|
+
*
|
|
852
|
+
* @example
|
|
853
|
+
* ```ts
|
|
854
|
+
* const status = await mailbreeze.verification.get("ver_xxx");
|
|
855
|
+
* if (status.status === "completed") {
|
|
856
|
+
* console.log(status.results);
|
|
857
|
+
* console.log(status.analytics);
|
|
858
|
+
* }
|
|
859
|
+
* ```
|
|
860
|
+
*/
|
|
861
|
+
async get(verificationId) {
|
|
862
|
+
return this._get(`/email-verification/${verificationId}`, {
|
|
863
|
+
includeResults: true
|
|
864
|
+
});
|
|
865
|
+
}
|
|
866
|
+
/**
|
|
867
|
+
* List verification batches.
|
|
868
|
+
*
|
|
869
|
+
* @param params - Filter and pagination options
|
|
870
|
+
* @returns Paginated list of verification batches
|
|
871
|
+
*
|
|
872
|
+
* @example
|
|
873
|
+
* ```ts
|
|
874
|
+
* const { data, pagination } = await mailbreeze.verification.list({
|
|
875
|
+
* status: "completed",
|
|
876
|
+
* page: 1,
|
|
877
|
+
* });
|
|
878
|
+
* ```
|
|
879
|
+
*/
|
|
880
|
+
async list(params) {
|
|
881
|
+
const query = {
|
|
882
|
+
...this.listParamsToQuery(params)
|
|
883
|
+
};
|
|
884
|
+
if (params?.status) query.status = params.status;
|
|
885
|
+
const response = await this._get("/email-verification", Object.keys(query).length > 0 ? query : void 0);
|
|
886
|
+
return this.extractPaginatedList(response);
|
|
887
|
+
}
|
|
888
|
+
/**
|
|
889
|
+
* Get verification statistics.
|
|
890
|
+
*
|
|
891
|
+
* @returns Aggregate verification stats
|
|
892
|
+
*
|
|
893
|
+
* @example
|
|
894
|
+
* ```ts
|
|
895
|
+
* const stats = await mailbreeze.verification.stats();
|
|
896
|
+
* console.log(`Verified: ${stats.totalVerified}, Valid: ${stats.valid}`);
|
|
897
|
+
* ```
|
|
898
|
+
*/
|
|
899
|
+
async stats() {
|
|
900
|
+
return this._get("/email-verification/stats");
|
|
901
|
+
}
|
|
902
|
+
};
|
|
903
|
+
|
|
904
|
+
// src/client.ts
|
|
905
|
+
var DEFAULT_BASE_URL = "https://api.mailbreeze.com";
|
|
906
|
+
var DEFAULT_TIMEOUT = 3e4;
|
|
907
|
+
var DEFAULT_MAX_RETRIES = 3;
|
|
908
|
+
var MailBreeze = class {
|
|
909
|
+
/**
|
|
910
|
+
* Email sending and management.
|
|
911
|
+
*/
|
|
912
|
+
emails;
|
|
913
|
+
/**
|
|
914
|
+
* Email attachment handling.
|
|
915
|
+
*/
|
|
916
|
+
attachments;
|
|
917
|
+
/**
|
|
918
|
+
* Contact list management.
|
|
919
|
+
*/
|
|
920
|
+
lists;
|
|
921
|
+
/**
|
|
922
|
+
* Email verification services.
|
|
923
|
+
*/
|
|
924
|
+
verification;
|
|
925
|
+
/**
|
|
926
|
+
* Automation enrollment management.
|
|
927
|
+
*/
|
|
928
|
+
automations;
|
|
929
|
+
fetcher;
|
|
930
|
+
domainId;
|
|
931
|
+
/**
|
|
932
|
+
* Create a new MailBreeze client.
|
|
933
|
+
*
|
|
934
|
+
* @param config - Client configuration
|
|
935
|
+
*
|
|
936
|
+
* @example
|
|
937
|
+
* ```ts
|
|
938
|
+
* // Basic usage
|
|
939
|
+
* const mailbreeze = new MailBreeze({
|
|
940
|
+
* apiKey: "sk_live_xxx",
|
|
941
|
+
* });
|
|
942
|
+
*
|
|
943
|
+
* // With custom options
|
|
944
|
+
* const mailbreeze = new MailBreeze({
|
|
945
|
+
* apiKey: "sk_live_xxx",
|
|
946
|
+
* baseUrl: "https://api.eu.mailbreeze.com",
|
|
947
|
+
* timeout: 60000,
|
|
948
|
+
* maxRetries: 5,
|
|
949
|
+
* });
|
|
950
|
+
* ```
|
|
951
|
+
*/
|
|
952
|
+
constructor(config) {
|
|
953
|
+
if (!config.apiKey) {
|
|
954
|
+
throw new Error("API key is required");
|
|
955
|
+
}
|
|
956
|
+
this.fetcher = new Fetcher({
|
|
957
|
+
apiKey: config.apiKey,
|
|
958
|
+
baseUrl: config.baseUrl ?? DEFAULT_BASE_URL,
|
|
959
|
+
timeout: config.timeout ?? DEFAULT_TIMEOUT,
|
|
960
|
+
maxRetries: config.maxRetries ?? DEFAULT_MAX_RETRIES,
|
|
961
|
+
authStyle: config.authStyle ?? "header"
|
|
962
|
+
});
|
|
963
|
+
this.emails = new Emails(this.fetcher, this.domainId);
|
|
964
|
+
this.attachments = new Attachments(this.fetcher, this.domainId);
|
|
965
|
+
this.lists = new Lists(this.fetcher, this.domainId);
|
|
966
|
+
this.verification = new Verification(this.fetcher, this.domainId);
|
|
967
|
+
this.automations = new Automations(this.fetcher, this.domainId);
|
|
968
|
+
}
|
|
969
|
+
/**
|
|
970
|
+
* Get a contacts resource for a specific list.
|
|
971
|
+
*
|
|
972
|
+
* @param listId - Contact list ID
|
|
973
|
+
* @returns Contacts resource bound to the list
|
|
974
|
+
*
|
|
975
|
+
* @example
|
|
976
|
+
* ```ts
|
|
977
|
+
* const contacts = mailbreeze.contacts("list_xxx");
|
|
978
|
+
*
|
|
979
|
+
* // Create a contact in this list
|
|
980
|
+
* const contact = await contacts.create({
|
|
981
|
+
* email: "user@example.com",
|
|
982
|
+
* });
|
|
983
|
+
*
|
|
984
|
+
* // List contacts in this list
|
|
985
|
+
* const { data } = await contacts.list();
|
|
986
|
+
* ```
|
|
987
|
+
*/
|
|
988
|
+
contacts(listId) {
|
|
989
|
+
return new Contacts(this.fetcher, listId, this.domainId);
|
|
990
|
+
}
|
|
991
|
+
};
|
|
992
|
+
|
|
993
|
+
export { AuthenticationError, MailBreeze, MailBreezeError, NotFoundError, RateLimitError, ServerError, ValidationError };
|
|
994
|
+
//# sourceMappingURL=index.js.map
|
|
995
|
+
//# sourceMappingURL=index.js.map
|