@emailr/sdk 1.0.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.mjs ADDED
@@ -0,0 +1,666 @@
1
+ // src/errors.ts
2
+ var EmailrError = class _EmailrError extends Error {
3
+ constructor(message, statusCode, code, requestId) {
4
+ super(message);
5
+ this.name = "EmailrError";
6
+ this.statusCode = statusCode;
7
+ this.code = code;
8
+ this.requestId = requestId;
9
+ if (Error.captureStackTrace) {
10
+ Error.captureStackTrace(this, _EmailrError);
11
+ }
12
+ }
13
+ };
14
+ var NetworkError = class extends EmailrError {
15
+ constructor(message, cause) {
16
+ super(message, 0, "NETWORK_ERROR");
17
+ this.name = "NetworkError";
18
+ this.originalError = cause;
19
+ }
20
+ };
21
+ var AuthenticationError = class extends EmailrError {
22
+ constructor(message = "Invalid API key", requestId) {
23
+ super(message, 401, "AUTHENTICATION_ERROR", requestId);
24
+ this.name = "AuthenticationError";
25
+ }
26
+ };
27
+ var RateLimitError = class extends EmailrError {
28
+ constructor(message = "Rate limit exceeded", retryAfter, requestId) {
29
+ super(message, 429, "RATE_LIMIT_ERROR", requestId);
30
+ this.name = "RateLimitError";
31
+ this.retryAfter = retryAfter;
32
+ }
33
+ };
34
+ var NotFoundError = class extends EmailrError {
35
+ constructor(message = "Resource not found", requestId) {
36
+ super(message, 404, "NOT_FOUND", requestId);
37
+ this.name = "NotFoundError";
38
+ }
39
+ };
40
+ var ValidationError = class extends EmailrError {
41
+ constructor(message, details, requestId) {
42
+ super(message, 400, "VALIDATION_ERROR", requestId);
43
+ this.name = "ValidationError";
44
+ this.details = details;
45
+ }
46
+ };
47
+
48
+ // src/http.ts
49
+ var HttpClient = class {
50
+ constructor(config) {
51
+ this.apiKey = config.apiKey;
52
+ this.baseUrl = config.baseUrl;
53
+ this.timeout = config.timeout;
54
+ }
55
+ async get(path, options) {
56
+ return this.request("GET", path, void 0, options);
57
+ }
58
+ async post(path, body, options) {
59
+ return this.request("POST", path, body, options);
60
+ }
61
+ async put(path, body, options) {
62
+ return this.request("PUT", path, body, options);
63
+ }
64
+ async patch(path, body, options) {
65
+ return this.request("PATCH", path, body, options);
66
+ }
67
+ async delete(path, options) {
68
+ return this.request("DELETE", path, void 0, options);
69
+ }
70
+ async request(method, path, body, options) {
71
+ const url = this.buildUrl(path, options?.params);
72
+ const headers = {
73
+ "Authorization": `Bearer ${this.apiKey}`,
74
+ "Content-Type": "application/json",
75
+ ...options?.headers
76
+ };
77
+ const controller = new AbortController();
78
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
79
+ try {
80
+ const response = await fetch(url, {
81
+ method,
82
+ headers,
83
+ body: body ? JSON.stringify(body) : void 0,
84
+ signal: controller.signal
85
+ });
86
+ clearTimeout(timeoutId);
87
+ const requestId = response.headers.get("x-request-id") ?? void 0;
88
+ if (!response.ok) {
89
+ await this.handleErrorResponse(response, requestId);
90
+ }
91
+ if (response.status === 204) {
92
+ return {};
93
+ }
94
+ const data = await response.json();
95
+ return data;
96
+ } catch (error) {
97
+ clearTimeout(timeoutId);
98
+ if (error instanceof EmailrError) {
99
+ throw error;
100
+ }
101
+ if (error instanceof Error) {
102
+ if (error.name === "AbortError") {
103
+ throw new NetworkError(`Request timeout after ${this.timeout}ms`);
104
+ }
105
+ throw new NetworkError(error.message, error);
106
+ }
107
+ throw new NetworkError("An unknown error occurred");
108
+ }
109
+ }
110
+ buildUrl(path, params) {
111
+ const url = new URL(path, this.baseUrl);
112
+ if (params) {
113
+ Object.entries(params).forEach(([key, value]) => {
114
+ if (value !== void 0) {
115
+ url.searchParams.append(key, String(value));
116
+ }
117
+ });
118
+ }
119
+ return url.toString();
120
+ }
121
+ async handleErrorResponse(response, requestId) {
122
+ let errorData;
123
+ try {
124
+ errorData = await response.json();
125
+ } catch {
126
+ errorData = { error: response.statusText || "Unknown error" };
127
+ }
128
+ const message = errorData.error || "An error occurred";
129
+ switch (response.status) {
130
+ case 400:
131
+ throw new ValidationError(message, errorData.details, requestId);
132
+ case 401:
133
+ throw new AuthenticationError(message, requestId);
134
+ case 404:
135
+ throw new NotFoundError(message, requestId);
136
+ case 429:
137
+ const retryAfter = response.headers.get("retry-after");
138
+ throw new RateLimitError(
139
+ message,
140
+ retryAfter ? parseInt(retryAfter, 10) : void 0,
141
+ requestId
142
+ );
143
+ default:
144
+ throw new EmailrError(message, response.status, errorData.code, requestId);
145
+ }
146
+ }
147
+ };
148
+
149
+ // src/resources/emails.ts
150
+ var EmailsResource = class {
151
+ constructor(http) {
152
+ this.http = http;
153
+ }
154
+ /**
155
+ * Send an email to one or multiple recipients
156
+ */
157
+ async send(data) {
158
+ return this.http.post("/v1/emails/send", data);
159
+ }
160
+ /**
161
+ * Get email by ID
162
+ */
163
+ async get(id) {
164
+ return this.http.get(`/v1/emails/${id}`);
165
+ }
166
+ /**
167
+ * List emails with pagination
168
+ */
169
+ async list(params) {
170
+ return this.http.get("/v1/emails", {
171
+ params
172
+ });
173
+ }
174
+ /**
175
+ * Forward an email to other recipients
176
+ */
177
+ async forward(data) {
178
+ return this.http.post("/v1/emails/forward", data);
179
+ }
180
+ /**
181
+ * Create an email forwarding rule
182
+ */
183
+ async createForwardingRule(data) {
184
+ return this.http.post("/v1/emails/forwarding-rules", data);
185
+ }
186
+ /**
187
+ * List all forwarding rules
188
+ */
189
+ async listForwardingRules() {
190
+ return this.http.get("/v1/emails/forwarding-rules");
191
+ }
192
+ /**
193
+ * Delete a forwarding rule
194
+ */
195
+ async deleteForwardingRule(id) {
196
+ return this.http.delete(`/v1/emails/forwarding-rules/${id}`);
197
+ }
198
+ };
199
+
200
+ // src/resources/contacts.ts
201
+ var ContactsResource = class {
202
+ constructor(http) {
203
+ this.http = http;
204
+ }
205
+ /**
206
+ * Create a new contact
207
+ */
208
+ async create(data) {
209
+ return this.http.post("/v1/contacts", data);
210
+ }
211
+ /**
212
+ * Get contact by ID
213
+ */
214
+ async get(id) {
215
+ return this.http.get(`/v1/contacts/${id}`);
216
+ }
217
+ /**
218
+ * List contacts with optional filtering
219
+ */
220
+ async list(params) {
221
+ return this.http.get("/v1/contacts", {
222
+ params
223
+ });
224
+ }
225
+ /**
226
+ * Update a contact
227
+ */
228
+ async update(id, data) {
229
+ return this.http.put(`/v1/contacts/${id}`, data);
230
+ }
231
+ /**
232
+ * Delete a contact
233
+ */
234
+ async delete(id) {
235
+ return this.http.delete(`/v1/contacts/${id}`);
236
+ }
237
+ /**
238
+ * Bulk create contacts
239
+ */
240
+ async bulkCreate(data) {
241
+ return this.http.post("/v1/contacts/bulk", data);
242
+ }
243
+ /**
244
+ * Unsubscribe a contact
245
+ */
246
+ async unsubscribe(id) {
247
+ return this.http.post(`/v1/contacts/${id}/unsubscribe`);
248
+ }
249
+ /**
250
+ * Resubscribe a contact
251
+ */
252
+ async resubscribe(id) {
253
+ return this.http.post(`/v1/contacts/${id}/resubscribe`);
254
+ }
255
+ };
256
+
257
+ // src/resources/templates.ts
258
+ var TemplatesResource = class {
259
+ constructor(http) {
260
+ this.http = http;
261
+ }
262
+ /**
263
+ * Create a new template
264
+ */
265
+ async create(data) {
266
+ return this.http.post("/v1/templates", data);
267
+ }
268
+ /**
269
+ * Get template by ID
270
+ */
271
+ async get(id) {
272
+ return this.http.get(`/v1/templates/${id}`);
273
+ }
274
+ /**
275
+ * List templates with pagination
276
+ */
277
+ async list(params) {
278
+ return this.http.get("/v1/templates", {
279
+ params
280
+ });
281
+ }
282
+ /**
283
+ * Update a template
284
+ */
285
+ async update(id, data) {
286
+ return this.http.put(`/v1/templates/${id}`, data);
287
+ }
288
+ /**
289
+ * Delete a template
290
+ */
291
+ async delete(id) {
292
+ return this.http.delete(`/v1/templates/${id}`);
293
+ }
294
+ /**
295
+ * Duplicate a template
296
+ */
297
+ async duplicate(id) {
298
+ return this.http.post(`/v1/templates/${id}/duplicate`);
299
+ }
300
+ };
301
+
302
+ // src/resources/domains.ts
303
+ var DomainsResource = class {
304
+ constructor(http) {
305
+ this.http = http;
306
+ }
307
+ /**
308
+ * Add a new domain
309
+ */
310
+ async add(data) {
311
+ return this.http.post("/v1/domains", data);
312
+ }
313
+ /**
314
+ * Get domain by ID
315
+ */
316
+ async get(id) {
317
+ return this.http.get(`/v1/domains/${id}`);
318
+ }
319
+ /**
320
+ * List all domains
321
+ */
322
+ async list() {
323
+ return this.http.get("/v1/domains");
324
+ }
325
+ /**
326
+ * Update domain settings
327
+ */
328
+ async update(id, data) {
329
+ return this.http.patch(`/v1/domains/${id}`, data);
330
+ }
331
+ /**
332
+ * Delete a domain
333
+ */
334
+ async delete(id) {
335
+ return this.http.delete(`/v1/domains/${id}`);
336
+ }
337
+ /**
338
+ * Verify domain DNS records
339
+ */
340
+ async verify(id) {
341
+ return this.http.post(`/v1/domains/${id}/verify`);
342
+ }
343
+ /**
344
+ * Check DNS verification status
345
+ */
346
+ async checkDns(id) {
347
+ return this.http.get(`/v1/domains/${id}/dns-status`);
348
+ }
349
+ };
350
+
351
+ // src/resources/webhooks.ts
352
+ var WebhooksResource = class {
353
+ constructor(http) {
354
+ this.http = http;
355
+ }
356
+ /**
357
+ * Create a new webhook
358
+ */
359
+ async create(data) {
360
+ return this.http.post("/v1/webhooks", data);
361
+ }
362
+ /**
363
+ * Get webhook by ID
364
+ */
365
+ async get(id) {
366
+ return this.http.get(`/v1/webhooks/${id}`);
367
+ }
368
+ /**
369
+ * List webhooks with pagination
370
+ */
371
+ async list(params) {
372
+ return this.http.get("/v1/webhooks", {
373
+ params
374
+ });
375
+ }
376
+ /**
377
+ * Update a webhook
378
+ */
379
+ async update(id, data) {
380
+ return this.http.put(`/v1/webhooks/${id}`, data);
381
+ }
382
+ /**
383
+ * Delete a webhook
384
+ */
385
+ async delete(id) {
386
+ return this.http.delete(`/v1/webhooks/${id}`);
387
+ }
388
+ /**
389
+ * Enable a webhook
390
+ */
391
+ async enable(id) {
392
+ return this.http.post(`/v1/webhooks/${id}/enable`);
393
+ }
394
+ /**
395
+ * Disable a webhook
396
+ */
397
+ async disable(id) {
398
+ return this.http.post(`/v1/webhooks/${id}/disable`);
399
+ }
400
+ /**
401
+ * List webhook deliveries
402
+ */
403
+ async listDeliveries(id) {
404
+ return this.http.get(`/v1/webhooks/${id}/deliveries`);
405
+ }
406
+ /**
407
+ * Retry a failed webhook delivery
408
+ */
409
+ async retryDelivery(webhookId, deliveryId) {
410
+ return this.http.post(`/v1/webhooks/${webhookId}/deliveries/${deliveryId}/retry`);
411
+ }
412
+ };
413
+
414
+ // src/resources/broadcasts.ts
415
+ var BroadcastsResource = class {
416
+ constructor(http) {
417
+ this.http = http;
418
+ }
419
+ /**
420
+ * Create a new broadcast
421
+ */
422
+ async create(data) {
423
+ return this.http.post("/v1/broadcasts", data);
424
+ }
425
+ /**
426
+ * Get broadcast by ID
427
+ */
428
+ async get(id) {
429
+ return this.http.get(`/v1/broadcasts/${id}`);
430
+ }
431
+ /**
432
+ * List broadcasts
433
+ */
434
+ async list(params) {
435
+ const queryParams = {};
436
+ if (params?.page) queryParams.page = String(params.page);
437
+ if (params?.limit) queryParams.limit = String(params.limit);
438
+ if (params?.status) queryParams.status = params.status;
439
+ const data = await this.http.get("/v1/broadcasts", queryParams);
440
+ if (Array.isArray(data)) {
441
+ return data;
442
+ }
443
+ return data.data || [];
444
+ }
445
+ /**
446
+ * Update a broadcast
447
+ */
448
+ async update(id, data) {
449
+ return this.http.put(`/v1/broadcasts/${id}`, data);
450
+ }
451
+ /**
452
+ * Delete a broadcast
453
+ */
454
+ async delete(id) {
455
+ return this.http.delete(`/v1/broadcasts/${id}`);
456
+ }
457
+ /**
458
+ * Send a broadcast immediately
459
+ */
460
+ async send(id) {
461
+ return this.http.post(`/v1/broadcasts/${id}/send`);
462
+ }
463
+ /**
464
+ * Schedule a broadcast
465
+ */
466
+ async schedule(id, scheduledAt) {
467
+ return this.http.post(`/v1/broadcasts/${id}/schedule`, { scheduled_at: scheduledAt });
468
+ }
469
+ /**
470
+ * Cancel a scheduled broadcast
471
+ */
472
+ async cancel(id) {
473
+ return this.http.post(`/v1/broadcasts/${id}/cancel`);
474
+ }
475
+ /**
476
+ * Get broadcast statistics
477
+ */
478
+ async getStats(id) {
479
+ return this.http.get(`/v1/broadcasts/${id}/stats`);
480
+ }
481
+ };
482
+
483
+ // src/resources/segments.ts
484
+ var SegmentsResource = class {
485
+ constructor(http) {
486
+ this.http = http;
487
+ }
488
+ /**
489
+ * Create a new segment
490
+ */
491
+ async create(data) {
492
+ return this.http.post("/v1/segments", data);
493
+ }
494
+ /**
495
+ * Get segment by ID
496
+ */
497
+ async get(id) {
498
+ return this.http.get(`/v1/segments/${id}`);
499
+ }
500
+ /**
501
+ * List segments with pagination
502
+ */
503
+ async list(params) {
504
+ return this.http.get("/v1/segments", {
505
+ params
506
+ });
507
+ }
508
+ /**
509
+ * Update a segment
510
+ */
511
+ async update(id, data) {
512
+ return this.http.put(`/v1/segments/${id}`, data);
513
+ }
514
+ /**
515
+ * Delete a segment
516
+ */
517
+ async delete(id) {
518
+ return this.http.delete(`/v1/segments/${id}`);
519
+ }
520
+ /**
521
+ * Get contacts in a segment
522
+ */
523
+ async getContacts(id, params) {
524
+ return this.http.get(`/v1/segments/${id}/contacts`, {
525
+ params
526
+ });
527
+ }
528
+ /**
529
+ * Get segment contact count
530
+ */
531
+ async getCount(id) {
532
+ return this.http.get(`/v1/segments/${id}/count`);
533
+ }
534
+ };
535
+
536
+ // src/resources/api-keys.ts
537
+ var ApiKeysResource = class {
538
+ constructor(http) {
539
+ this.http = http;
540
+ }
541
+ /**
542
+ * Create a new API key.
543
+ * Note: The full key is only returned once upon creation.
544
+ */
545
+ async create(data) {
546
+ return this.http.post("/v1/api-keys", data);
547
+ }
548
+ /**
549
+ * List all active API keys.
550
+ * Note: Full keys are not returned for security.
551
+ */
552
+ async list() {
553
+ return this.http.get("/v1/api-keys");
554
+ }
555
+ /**
556
+ * Revoke an API key.
557
+ */
558
+ async revoke(id) {
559
+ return this.http.delete(`/v1/api-keys/${id}`);
560
+ }
561
+ };
562
+
563
+ // src/resources/smtp.ts
564
+ var SmtpResource = class {
565
+ constructor(http) {
566
+ this.http = http;
567
+ }
568
+ /**
569
+ * Get SMTP credentials for sending emails.
570
+ * The password is your API key.
571
+ */
572
+ async getCredentials() {
573
+ return this.http.get("/v1/smtp/credentials");
574
+ }
575
+ };
576
+
577
+ // src/resources/settings.ts
578
+ var SettingsResource = class {
579
+ constructor(http) {
580
+ this.http = http;
581
+ }
582
+ /**
583
+ * Get organization settings.
584
+ */
585
+ async getOrganization() {
586
+ return this.http.get("/v1/settings/organization");
587
+ }
588
+ /**
589
+ * Update organization settings.
590
+ */
591
+ async updateOrganization(data) {
592
+ return this.http.put("/v1/settings/organization", data);
593
+ }
594
+ /**
595
+ * Get team members.
596
+ */
597
+ async getTeam() {
598
+ return this.http.get("/v1/settings/team");
599
+ }
600
+ /**
601
+ * Get unsubscribe settings.
602
+ */
603
+ async getUnsubscribeSettings() {
604
+ return this.http.get("/v1/settings/unsubscribe");
605
+ }
606
+ /**
607
+ * Update unsubscribe settings.
608
+ */
609
+ async updateUnsubscribeSettings(data) {
610
+ return this.http.put("/v1/settings/unsubscribe", data);
611
+ }
612
+ };
613
+
614
+ // src/resources/metrics.ts
615
+ var MetricsResource = class {
616
+ constructor(http) {
617
+ this.http = http;
618
+ }
619
+ /**
620
+ * Get usage metrics for a specified number of days.
621
+ */
622
+ async getUsage(days = 30) {
623
+ return this.http.get("/v1/metrics/usage", { params: { days } });
624
+ }
625
+ /**
626
+ * Get aggregated email delivery metrics.
627
+ */
628
+ async getEmailMetrics() {
629
+ return this.http.get("/v1/metrics/emails");
630
+ }
631
+ };
632
+
633
+ // src/client.ts
634
+ var Emailr = class {
635
+ constructor(config) {
636
+ if (!config.apiKey) {
637
+ throw new Error("API key is required");
638
+ }
639
+ this.http = new HttpClient({
640
+ apiKey: config.apiKey,
641
+ baseUrl: config.baseUrl ?? "https://api.emailr.dev",
642
+ timeout: config.timeout ?? 3e4
643
+ });
644
+ this.emails = new EmailsResource(this.http);
645
+ this.contacts = new ContactsResource(this.http);
646
+ this.templates = new TemplatesResource(this.http);
647
+ this.domains = new DomainsResource(this.http);
648
+ this.webhooks = new WebhooksResource(this.http);
649
+ this.broadcasts = new BroadcastsResource(this.http);
650
+ this.segments = new SegmentsResource(this.http);
651
+ this.apiKeys = new ApiKeysResource(this.http);
652
+ this.smtp = new SmtpResource(this.http);
653
+ this.settings = new SettingsResource(this.http);
654
+ this.metrics = new MetricsResource(this.http);
655
+ }
656
+ };
657
+ export {
658
+ AuthenticationError,
659
+ Emailr,
660
+ Emailr as EmailrClient,
661
+ EmailrError,
662
+ NetworkError,
663
+ NotFoundError,
664
+ RateLimitError,
665
+ ValidationError
666
+ };
package/package.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "@emailr/sdk",
3
+ "version": "1.0.0",
4
+ "description": "Official Emailr API SDK for TypeScript/JavaScript",
5
+ "main": "./dist/index.js",
6
+ "module": "./dist/index.mjs",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/index.mjs",
11
+ "require": "./dist/index.js",
12
+ "types": "./dist/index.d.ts"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist"
17
+ ],
18
+ "keywords": [
19
+ "emailr",
20
+ "email",
21
+ "api",
22
+ "sdk",
23
+ "typescript"
24
+ ],
25
+ "author": "Emailr",
26
+ "license": "MIT",
27
+ "devDependencies": {
28
+ "tsup": "^8.0.0",
29
+ "typescript": "^5.3.0",
30
+ "vitest": "^1.2.0"
31
+ },
32
+ "engines": {
33
+ "node": ">=18.0.0"
34
+ },
35
+ "scripts": {
36
+ "build": "tsup src/index.ts --format cjs,esm --dts --clean",
37
+ "test": "vitest run",
38
+ "typecheck": "tsc --noEmit"
39
+ }
40
+ }