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/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