sendcore 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/README.md ADDED
@@ -0,0 +1,195 @@
1
+ # SendCore Node.js SDK
2
+
3
+ The official Node.js SDK for [SendCore](https://sendcore.elasto.ng) — reliable email infrastructure for developers and businesses.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install sendcore
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```ts
14
+ import { SendCore } from 'sendcore';
15
+
16
+ const sendcore = new SendCore('sc_live_xxxxxxxxxx');
17
+
18
+ // Send an email
19
+ await sendcore.emails.send({
20
+ from: 'hello@yourdomain.com',
21
+ to: 'user@example.com',
22
+ subject: 'Welcome!',
23
+ html: '<h1>Hello World</h1>',
24
+ });
25
+ ```
26
+
27
+ ## Features
28
+
29
+ - **Zero dependencies** — uses native `fetch` (Node 18+)
30
+ - **Full TypeScript support** — complete type definitions included
31
+ - **Automatic retries** — exponential backoff on 5xx errors
32
+ - **Configurable timeouts** — prevent hanging requests
33
+ - **Secure by default** — API key via `x-api-key` header
34
+
35
+ ---
36
+
37
+ ## Configuration
38
+
39
+ ```ts
40
+ import { SendCore } from 'sendcore';
41
+
42
+ // Simple — just pass your API key
43
+ const sendcore = new SendCore('sc_live_xxxxxxxxxx');
44
+
45
+ // Advanced — full configuration
46
+ const sendcore = new SendCore({
47
+ apiKey: 'sc_live_xxxxxxxxxx',
48
+ baseUrl: 'https://api.sendcore.elasto.ng', // optional
49
+ timeout: 30000, // optional, ms
50
+ retries: 2, // optional
51
+ });
52
+ ```
53
+
54
+ Get your API key from the [SendCore Dashboard](https://sendcore.elasto.ng/dashboard/api-keys).
55
+
56
+ ---
57
+
58
+ ## Sending Emails
59
+
60
+ ### Basic Email
61
+
62
+ ```ts
63
+ await sendcore.emails.send({
64
+ from: 'hello@yourdomain.com',
65
+ to: 'user@example.com',
66
+ subject: 'Hello!',
67
+ html: '<h1>Welcome to our platform</h1>',
68
+ });
69
+ ```
70
+
71
+ ### With Multiple Recipients
72
+
73
+ ```ts
74
+ await sendcore.emails.send({
75
+ from: 'Team <team@yourdomain.com>',
76
+ to: ['alice@example.com', 'bob@example.com'],
77
+ cc: 'manager@example.com',
78
+ bcc: ['audit@yourdomain.com'],
79
+ subject: 'Team Update',
80
+ html: '<p>Here is your weekly update.</p>',
81
+ replyTo: 'support@yourdomain.com',
82
+ });
83
+ ```
84
+
85
+ ### Using a Template
86
+
87
+ ```ts
88
+ await sendcore.emails.sendTemplate({
89
+ from: 'hello@yourdomain.com',
90
+ to: 'user@example.com',
91
+ templateId: 'welcome-email',
92
+ templateData: {
93
+ name: 'John',
94
+ plan: 'Pro',
95
+ loginUrl: 'https://app.example.com/login',
96
+ },
97
+ });
98
+ ```
99
+
100
+ ### With Attachments
101
+
102
+ ```ts
103
+ import { readFileSync } from 'fs';
104
+
105
+ await sendcore.emails.send({
106
+ from: 'billing@yourdomain.com',
107
+ to: 'user@example.com',
108
+ subject: 'Your Invoice',
109
+ html: '<p>Please find your invoice attached.</p>',
110
+ attachments: [
111
+ {
112
+ filename: 'invoice.pdf',
113
+ content: readFileSync('./invoice.pdf').toString('base64'),
114
+ contentType: 'application/pdf',
115
+ },
116
+ ],
117
+ });
118
+ ```
119
+
120
+ ### With Tags
121
+
122
+ ```ts
123
+ await sendcore.emails.send({
124
+ from: 'hello@yourdomain.com',
125
+ to: 'user@example.com',
126
+ subject: 'Order Confirmation',
127
+ html: '<p>Your order is confirmed!</p>',
128
+ tags: {
129
+ category: 'transactional',
130
+ orderId: 'ORD-12345',
131
+ },
132
+ });
133
+ ```
134
+
135
+ ---
136
+
137
+ ## Managing Contacts
138
+
139
+ ### Subscribe a Contact
140
+
141
+ ```ts
142
+ await sendcore.contacts.subscribe({
143
+ email: 'user@example.com',
144
+ firstName: 'John',
145
+ lastName: 'Doe',
146
+ listId: 'lst_abc123', // optional
147
+ customData: { plan: 'pro' }, // optional
148
+ });
149
+ ```
150
+
151
+ ### Unsubscribe a Contact
152
+
153
+ ```ts
154
+ await sendcore.contacts.unsubscribe({
155
+ email: 'user@example.com',
156
+ });
157
+ ```
158
+
159
+ ---
160
+
161
+ ## Error Handling
162
+
163
+ ```ts
164
+ import { SendCore, SendCoreError } from 'sendcore';
165
+
166
+ const sendcore = new SendCore('sc_live_xxxxxxxxxx');
167
+
168
+ try {
169
+ await sendcore.emails.send({
170
+ from: 'hello@yourdomain.com',
171
+ to: 'user@example.com',
172
+ subject: 'Test',
173
+ html: '<p>Hello</p>',
174
+ });
175
+ } catch (error) {
176
+ if (error instanceof SendCoreError) {
177
+ console.error('Status:', error.statusCode); // e.g. 400, 401, 422
178
+ console.error('Message:', error.message);
179
+ console.error('Detail:', error.detail);
180
+ } else {
181
+ console.error('Network error:', error);
182
+ }
183
+ }
184
+ ```
185
+
186
+ ---
187
+
188
+ ## Requirements
189
+
190
+ - **Node.js 18+** (uses native `fetch`)
191
+ - A SendCore API key
192
+
193
+ ## License
194
+
195
+ MIT
@@ -0,0 +1,176 @@
1
+ /** Configuration for the SendCore client */
2
+ interface SendCoreConfig {
3
+ /** Your API key from the SendCore dashboard */
4
+ apiKey: string;
5
+ /** Base URL of the SendCore API (default: https://api.sendcore.elasto.ng) */
6
+ baseUrl?: string;
7
+ /** Request timeout in milliseconds (default: 30000) */
8
+ timeout?: number;
9
+ /** Number of automatic retries on 5xx errors (default: 2) */
10
+ retries?: number;
11
+ }
12
+ interface EmailAttachment {
13
+ /** Filename of the attachment */
14
+ filename: string;
15
+ /** Base64-encoded content of the file */
16
+ content: string;
17
+ /** MIME content type (e.g. 'application/pdf') */
18
+ contentType?: string;
19
+ }
20
+ interface SendEmailParams {
21
+ /** Sender address (e.g. 'John <john@example.com>') */
22
+ from: string;
23
+ /** One or more recipient email addresses */
24
+ to: string | string[];
25
+ /** Email subject line */
26
+ subject?: string;
27
+ /** HTML body of the email */
28
+ html?: string;
29
+ /** Plain text body of the email */
30
+ text?: string;
31
+ /** CC recipients */
32
+ cc?: string | string[];
33
+ /** BCC recipients */
34
+ bcc?: string | string[];
35
+ /** Reply-to addresses */
36
+ replyTo?: string | string[];
37
+ /** Use a pre-built template by its ID */
38
+ templateId?: string;
39
+ /** Variables to inject into the template */
40
+ templateData?: Record<string, any>;
41
+ /** File attachments */
42
+ attachments?: EmailAttachment[];
43
+ /** Custom tags for tracking and analytics */
44
+ tags?: Record<string, string>;
45
+ }
46
+ interface SendEmailResponse {
47
+ id: string;
48
+ message: string;
49
+ [key: string]: any;
50
+ }
51
+ interface SubscribeParams {
52
+ /** Email address of the contact */
53
+ email: string;
54
+ /** First name */
55
+ firstName?: string;
56
+ /** Last name */
57
+ lastName?: string;
58
+ /** ID of the audience list to add the contact to */
59
+ listId?: string;
60
+ /** Any additional custom data */
61
+ customData?: Record<string, any>;
62
+ }
63
+ interface UnsubscribeParams {
64
+ /** Email address to unsubscribe */
65
+ email: string;
66
+ }
67
+ interface SendCoreErrorDetail {
68
+ statusCode: number;
69
+ message: string;
70
+ error?: string;
71
+ [key: string]: any;
72
+ }
73
+
74
+ /**
75
+ * SendCore — Official Node.js SDK
76
+ *
77
+ * @example
78
+ * ```ts
79
+ * import { SendCore } from 'sendcore';
80
+ *
81
+ * const sendcore = new SendCore('sc_live_xxxxxxxxxx');
82
+ *
83
+ * await sendcore.emails.send({
84
+ * from: 'hello@yourdomain.com',
85
+ * to: 'user@example.com',
86
+ * subject: 'Welcome!',
87
+ * html: '<h1>Hello World</h1>',
88
+ * });
89
+ * ```
90
+ */
91
+ declare class SendCore {
92
+ private readonly apiKey;
93
+ private readonly baseUrl;
94
+ private readonly timeout;
95
+ private readonly retries;
96
+ /** Email operations */
97
+ readonly emails: EmailsResource;
98
+ /** Audience / contact operations */
99
+ readonly contacts: ContactsResource;
100
+ constructor(apiKeyOrConfig: string | SendCoreConfig);
101
+ /** @internal */
102
+ _request<T = any>(method: 'GET' | 'POST' | 'PUT' | 'DELETE', path: string, body?: Record<string, any>): Promise<T>;
103
+ }
104
+ declare class EmailsResource {
105
+ private readonly client;
106
+ constructor(client: SendCore);
107
+ /**
108
+ * Send an email.
109
+ *
110
+ * @example
111
+ * ```ts
112
+ * const result = await sendcore.emails.send({
113
+ * from: 'hello@yourdomain.com',
114
+ * to: 'user@example.com',
115
+ * subject: 'Hello!',
116
+ * html: '<h1>Welcome</h1>',
117
+ * });
118
+ * ```
119
+ */
120
+ send(params: SendEmailParams): Promise<SendEmailResponse>;
121
+ /**
122
+ * Send an email using a pre-built template.
123
+ *
124
+ * @example
125
+ * ```ts
126
+ * await sendcore.emails.sendTemplate({
127
+ * from: 'hello@yourdomain.com',
128
+ * to: 'user@example.com',
129
+ * templateId: 'welcome-email',
130
+ * templateData: { name: 'John', plan: 'Pro' },
131
+ * });
132
+ * ```
133
+ */
134
+ sendTemplate(params: Omit<SendEmailParams, 'html' | 'text'> & {
135
+ templateId: string;
136
+ templateData?: Record<string, any>;
137
+ }): Promise<SendEmailResponse>;
138
+ }
139
+ declare class ContactsResource {
140
+ private readonly client;
141
+ constructor(client: SendCore);
142
+ /**
143
+ * Subscribe a contact (add to your audience).
144
+ *
145
+ * @example
146
+ * ```ts
147
+ * await sendcore.contacts.subscribe({
148
+ * email: 'user@example.com',
149
+ * firstName: 'John',
150
+ * listId: 'lst_abc123',
151
+ * });
152
+ * ```
153
+ */
154
+ subscribe(params: SubscribeParams): Promise<any>;
155
+ /**
156
+ * Unsubscribe a contact from your audience.
157
+ *
158
+ * @example
159
+ * ```ts
160
+ * await sendcore.contacts.unsubscribe({ email: 'user@example.com' });
161
+ * ```
162
+ */
163
+ unsubscribe(params: UnsubscribeParams): Promise<any>;
164
+ }
165
+
166
+ /**
167
+ * Custom error class for SendCore API errors.
168
+ * Contains the HTTP status code and structured error detail from the server.
169
+ */
170
+ declare class SendCoreError extends Error {
171
+ readonly statusCode: number;
172
+ readonly detail: SendCoreErrorDetail;
173
+ constructor(statusCode: number, detail: SendCoreErrorDetail);
174
+ }
175
+
176
+ export { type EmailAttachment, SendCore, type SendCoreConfig, SendCoreError, type SendCoreErrorDetail, type SendEmailParams, type SendEmailResponse, type SubscribeParams, type UnsubscribeParams };
@@ -0,0 +1,176 @@
1
+ /** Configuration for the SendCore client */
2
+ interface SendCoreConfig {
3
+ /** Your API key from the SendCore dashboard */
4
+ apiKey: string;
5
+ /** Base URL of the SendCore API (default: https://api.sendcore.elasto.ng) */
6
+ baseUrl?: string;
7
+ /** Request timeout in milliseconds (default: 30000) */
8
+ timeout?: number;
9
+ /** Number of automatic retries on 5xx errors (default: 2) */
10
+ retries?: number;
11
+ }
12
+ interface EmailAttachment {
13
+ /** Filename of the attachment */
14
+ filename: string;
15
+ /** Base64-encoded content of the file */
16
+ content: string;
17
+ /** MIME content type (e.g. 'application/pdf') */
18
+ contentType?: string;
19
+ }
20
+ interface SendEmailParams {
21
+ /** Sender address (e.g. 'John <john@example.com>') */
22
+ from: string;
23
+ /** One or more recipient email addresses */
24
+ to: string | string[];
25
+ /** Email subject line */
26
+ subject?: string;
27
+ /** HTML body of the email */
28
+ html?: string;
29
+ /** Plain text body of the email */
30
+ text?: string;
31
+ /** CC recipients */
32
+ cc?: string | string[];
33
+ /** BCC recipients */
34
+ bcc?: string | string[];
35
+ /** Reply-to addresses */
36
+ replyTo?: string | string[];
37
+ /** Use a pre-built template by its ID */
38
+ templateId?: string;
39
+ /** Variables to inject into the template */
40
+ templateData?: Record<string, any>;
41
+ /** File attachments */
42
+ attachments?: EmailAttachment[];
43
+ /** Custom tags for tracking and analytics */
44
+ tags?: Record<string, string>;
45
+ }
46
+ interface SendEmailResponse {
47
+ id: string;
48
+ message: string;
49
+ [key: string]: any;
50
+ }
51
+ interface SubscribeParams {
52
+ /** Email address of the contact */
53
+ email: string;
54
+ /** First name */
55
+ firstName?: string;
56
+ /** Last name */
57
+ lastName?: string;
58
+ /** ID of the audience list to add the contact to */
59
+ listId?: string;
60
+ /** Any additional custom data */
61
+ customData?: Record<string, any>;
62
+ }
63
+ interface UnsubscribeParams {
64
+ /** Email address to unsubscribe */
65
+ email: string;
66
+ }
67
+ interface SendCoreErrorDetail {
68
+ statusCode: number;
69
+ message: string;
70
+ error?: string;
71
+ [key: string]: any;
72
+ }
73
+
74
+ /**
75
+ * SendCore — Official Node.js SDK
76
+ *
77
+ * @example
78
+ * ```ts
79
+ * import { SendCore } from 'sendcore';
80
+ *
81
+ * const sendcore = new SendCore('sc_live_xxxxxxxxxx');
82
+ *
83
+ * await sendcore.emails.send({
84
+ * from: 'hello@yourdomain.com',
85
+ * to: 'user@example.com',
86
+ * subject: 'Welcome!',
87
+ * html: '<h1>Hello World</h1>',
88
+ * });
89
+ * ```
90
+ */
91
+ declare class SendCore {
92
+ private readonly apiKey;
93
+ private readonly baseUrl;
94
+ private readonly timeout;
95
+ private readonly retries;
96
+ /** Email operations */
97
+ readonly emails: EmailsResource;
98
+ /** Audience / contact operations */
99
+ readonly contacts: ContactsResource;
100
+ constructor(apiKeyOrConfig: string | SendCoreConfig);
101
+ /** @internal */
102
+ _request<T = any>(method: 'GET' | 'POST' | 'PUT' | 'DELETE', path: string, body?: Record<string, any>): Promise<T>;
103
+ }
104
+ declare class EmailsResource {
105
+ private readonly client;
106
+ constructor(client: SendCore);
107
+ /**
108
+ * Send an email.
109
+ *
110
+ * @example
111
+ * ```ts
112
+ * const result = await sendcore.emails.send({
113
+ * from: 'hello@yourdomain.com',
114
+ * to: 'user@example.com',
115
+ * subject: 'Hello!',
116
+ * html: '<h1>Welcome</h1>',
117
+ * });
118
+ * ```
119
+ */
120
+ send(params: SendEmailParams): Promise<SendEmailResponse>;
121
+ /**
122
+ * Send an email using a pre-built template.
123
+ *
124
+ * @example
125
+ * ```ts
126
+ * await sendcore.emails.sendTemplate({
127
+ * from: 'hello@yourdomain.com',
128
+ * to: 'user@example.com',
129
+ * templateId: 'welcome-email',
130
+ * templateData: { name: 'John', plan: 'Pro' },
131
+ * });
132
+ * ```
133
+ */
134
+ sendTemplate(params: Omit<SendEmailParams, 'html' | 'text'> & {
135
+ templateId: string;
136
+ templateData?: Record<string, any>;
137
+ }): Promise<SendEmailResponse>;
138
+ }
139
+ declare class ContactsResource {
140
+ private readonly client;
141
+ constructor(client: SendCore);
142
+ /**
143
+ * Subscribe a contact (add to your audience).
144
+ *
145
+ * @example
146
+ * ```ts
147
+ * await sendcore.contacts.subscribe({
148
+ * email: 'user@example.com',
149
+ * firstName: 'John',
150
+ * listId: 'lst_abc123',
151
+ * });
152
+ * ```
153
+ */
154
+ subscribe(params: SubscribeParams): Promise<any>;
155
+ /**
156
+ * Unsubscribe a contact from your audience.
157
+ *
158
+ * @example
159
+ * ```ts
160
+ * await sendcore.contacts.unsubscribe({ email: 'user@example.com' });
161
+ * ```
162
+ */
163
+ unsubscribe(params: UnsubscribeParams): Promise<any>;
164
+ }
165
+
166
+ /**
167
+ * Custom error class for SendCore API errors.
168
+ * Contains the HTTP status code and structured error detail from the server.
169
+ */
170
+ declare class SendCoreError extends Error {
171
+ readonly statusCode: number;
172
+ readonly detail: SendCoreErrorDetail;
173
+ constructor(statusCode: number, detail: SendCoreErrorDetail);
174
+ }
175
+
176
+ export { type EmailAttachment, SendCore, type SendCoreConfig, SendCoreError, type SendCoreErrorDetail, type SendEmailParams, type SendEmailResponse, type SubscribeParams, type UnsubscribeParams };
package/dist/index.js ADDED
@@ -0,0 +1,213 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ SendCore: () => SendCore,
24
+ SendCoreError: () => SendCoreError
25
+ });
26
+ module.exports = __toCommonJS(index_exports);
27
+
28
+ // src/errors.ts
29
+ var SendCoreError = class _SendCoreError extends Error {
30
+ statusCode;
31
+ detail;
32
+ constructor(statusCode, detail) {
33
+ const msg = detail.message || `SendCore API error (${statusCode})`;
34
+ super(msg);
35
+ this.name = "SendCoreError";
36
+ this.statusCode = statusCode;
37
+ this.detail = detail;
38
+ Object.setPrototypeOf(this, _SendCoreError.prototype);
39
+ }
40
+ };
41
+
42
+ // src/client.ts
43
+ var DEFAULT_BASE_URL = "https://api.sendcore.elasto.ng";
44
+ var DEFAULT_TIMEOUT = 3e4;
45
+ var DEFAULT_RETRIES = 2;
46
+ var RETRYABLE_STATUS_CODES = /* @__PURE__ */ new Set([408, 429, 500, 502, 503, 504]);
47
+ var SDK_VERSION = "1.0.0";
48
+ var SendCore = class {
49
+ apiKey;
50
+ baseUrl;
51
+ timeout;
52
+ retries;
53
+ /** Email operations */
54
+ emails;
55
+ /** Audience / contact operations */
56
+ contacts;
57
+ constructor(apiKeyOrConfig) {
58
+ const config = typeof apiKeyOrConfig === "string" ? { apiKey: apiKeyOrConfig } : apiKeyOrConfig;
59
+ if (!config.apiKey) {
60
+ throw new Error(
61
+ "SendCore: An API key is required. Get one at https://sendcore.elasto.ng/dashboard/api-keys"
62
+ );
63
+ }
64
+ this.apiKey = config.apiKey;
65
+ this.baseUrl = (config.baseUrl || DEFAULT_BASE_URL).replace(/\/+$/, "");
66
+ this.timeout = config.timeout ?? DEFAULT_TIMEOUT;
67
+ this.retries = config.retries ?? DEFAULT_RETRIES;
68
+ this.emails = new EmailsResource(this);
69
+ this.contacts = new ContactsResource(this);
70
+ }
71
+ // ─── Internal HTTP layer ────────────────────
72
+ /** @internal */
73
+ async _request(method, path, body) {
74
+ const url = `${this.baseUrl}${path}`;
75
+ const headers = {
76
+ "x-api-key": this.apiKey,
77
+ "Content-Type": "application/json",
78
+ "User-Agent": `sendcore-node/${SDK_VERSION}`
79
+ };
80
+ const fetchOptions = {
81
+ method,
82
+ headers,
83
+ signal: AbortSignal.timeout(this.timeout),
84
+ ...body ? { body: JSON.stringify(body) } : {}
85
+ };
86
+ let lastError = null;
87
+ for (let attempt = 0; attempt <= this.retries; attempt++) {
88
+ try {
89
+ const response = await fetch(url, fetchOptions);
90
+ const responseBody = await response.json().catch(() => ({}));
91
+ if (response.ok) {
92
+ return responseBody;
93
+ }
94
+ if (!RETRYABLE_STATUS_CODES.has(response.status)) {
95
+ throw new SendCoreError(response.status, {
96
+ statusCode: response.status,
97
+ message: responseBody.message || response.statusText,
98
+ error: responseBody.error,
99
+ ...responseBody
100
+ });
101
+ }
102
+ lastError = new SendCoreError(response.status, {
103
+ statusCode: response.status,
104
+ message: responseBody.message || response.statusText,
105
+ error: responseBody.error,
106
+ ...responseBody
107
+ });
108
+ } catch (err) {
109
+ if (err instanceof SendCoreError) throw err;
110
+ lastError = err;
111
+ }
112
+ if (attempt < this.retries) {
113
+ const delay = Math.min(1e3 * 2 ** attempt, 1e4);
114
+ await new Promise((r) => setTimeout(r, delay));
115
+ }
116
+ }
117
+ throw lastError ?? new Error("SendCore: Request failed after retries");
118
+ }
119
+ };
120
+ var EmailsResource = class {
121
+ constructor(client) {
122
+ this.client = client;
123
+ }
124
+ client;
125
+ /**
126
+ * Send an email.
127
+ *
128
+ * @example
129
+ * ```ts
130
+ * const result = await sendcore.emails.send({
131
+ * from: 'hello@yourdomain.com',
132
+ * to: 'user@example.com',
133
+ * subject: 'Hello!',
134
+ * html: '<h1>Welcome</h1>',
135
+ * });
136
+ * ```
137
+ */
138
+ async send(params) {
139
+ if (!params.from) {
140
+ throw new Error("SendCore: The 'from' field is required to send an email.");
141
+ }
142
+ if (!params.to) {
143
+ throw new Error("SendCore: The 'to' field is required to send an email.");
144
+ }
145
+ const payload = {
146
+ ...params,
147
+ to: Array.isArray(params.to) ? params.to : [params.to],
148
+ cc: params.cc ? Array.isArray(params.cc) ? params.cc : [params.cc] : void 0,
149
+ bcc: params.bcc ? Array.isArray(params.bcc) ? params.bcc : [params.bcc] : void 0,
150
+ replyTo: params.replyTo ? Array.isArray(params.replyTo) ? params.replyTo : [params.replyTo] : void 0
151
+ };
152
+ return this.client._request("POST", "/emails/send", payload);
153
+ }
154
+ /**
155
+ * Send an email using a pre-built template.
156
+ *
157
+ * @example
158
+ * ```ts
159
+ * await sendcore.emails.sendTemplate({
160
+ * from: 'hello@yourdomain.com',
161
+ * to: 'user@example.com',
162
+ * templateId: 'welcome-email',
163
+ * templateData: { name: 'John', plan: 'Pro' },
164
+ * });
165
+ * ```
166
+ */
167
+ async sendTemplate(params) {
168
+ return this.send(params);
169
+ }
170
+ };
171
+ var ContactsResource = class {
172
+ constructor(client) {
173
+ this.client = client;
174
+ }
175
+ client;
176
+ /**
177
+ * Subscribe a contact (add to your audience).
178
+ *
179
+ * @example
180
+ * ```ts
181
+ * await sendcore.contacts.subscribe({
182
+ * email: 'user@example.com',
183
+ * firstName: 'John',
184
+ * listId: 'lst_abc123',
185
+ * });
186
+ * ```
187
+ */
188
+ async subscribe(params) {
189
+ if (!params.email) {
190
+ throw new Error("SendCore: The 'email' field is required.");
191
+ }
192
+ return this.client._request("POST", "/organizations/audience/subscribe", params);
193
+ }
194
+ /**
195
+ * Unsubscribe a contact from your audience.
196
+ *
197
+ * @example
198
+ * ```ts
199
+ * await sendcore.contacts.unsubscribe({ email: 'user@example.com' });
200
+ * ```
201
+ */
202
+ async unsubscribe(params) {
203
+ if (!params.email) {
204
+ throw new Error("SendCore: The 'email' field is required.");
205
+ }
206
+ return this.client._request("POST", "/organizations/audience/unsubscribe", params);
207
+ }
208
+ };
209
+ // Annotate the CommonJS export names for ESM import in node:
210
+ 0 && (module.exports = {
211
+ SendCore,
212
+ SendCoreError
213
+ });
package/dist/index.mjs ADDED
@@ -0,0 +1,185 @@
1
+ // src/errors.ts
2
+ var SendCoreError = class _SendCoreError extends Error {
3
+ statusCode;
4
+ detail;
5
+ constructor(statusCode, detail) {
6
+ const msg = detail.message || `SendCore API error (${statusCode})`;
7
+ super(msg);
8
+ this.name = "SendCoreError";
9
+ this.statusCode = statusCode;
10
+ this.detail = detail;
11
+ Object.setPrototypeOf(this, _SendCoreError.prototype);
12
+ }
13
+ };
14
+
15
+ // src/client.ts
16
+ var DEFAULT_BASE_URL = "https://api.sendcore.elasto.ng";
17
+ var DEFAULT_TIMEOUT = 3e4;
18
+ var DEFAULT_RETRIES = 2;
19
+ var RETRYABLE_STATUS_CODES = /* @__PURE__ */ new Set([408, 429, 500, 502, 503, 504]);
20
+ var SDK_VERSION = "1.0.0";
21
+ var SendCore = class {
22
+ apiKey;
23
+ baseUrl;
24
+ timeout;
25
+ retries;
26
+ /** Email operations */
27
+ emails;
28
+ /** Audience / contact operations */
29
+ contacts;
30
+ constructor(apiKeyOrConfig) {
31
+ const config = typeof apiKeyOrConfig === "string" ? { apiKey: apiKeyOrConfig } : apiKeyOrConfig;
32
+ if (!config.apiKey) {
33
+ throw new Error(
34
+ "SendCore: An API key is required. Get one at https://sendcore.elasto.ng/dashboard/api-keys"
35
+ );
36
+ }
37
+ this.apiKey = config.apiKey;
38
+ this.baseUrl = (config.baseUrl || DEFAULT_BASE_URL).replace(/\/+$/, "");
39
+ this.timeout = config.timeout ?? DEFAULT_TIMEOUT;
40
+ this.retries = config.retries ?? DEFAULT_RETRIES;
41
+ this.emails = new EmailsResource(this);
42
+ this.contacts = new ContactsResource(this);
43
+ }
44
+ // ─── Internal HTTP layer ────────────────────
45
+ /** @internal */
46
+ async _request(method, path, body) {
47
+ const url = `${this.baseUrl}${path}`;
48
+ const headers = {
49
+ "x-api-key": this.apiKey,
50
+ "Content-Type": "application/json",
51
+ "User-Agent": `sendcore-node/${SDK_VERSION}`
52
+ };
53
+ const fetchOptions = {
54
+ method,
55
+ headers,
56
+ signal: AbortSignal.timeout(this.timeout),
57
+ ...body ? { body: JSON.stringify(body) } : {}
58
+ };
59
+ let lastError = null;
60
+ for (let attempt = 0; attempt <= this.retries; attempt++) {
61
+ try {
62
+ const response = await fetch(url, fetchOptions);
63
+ const responseBody = await response.json().catch(() => ({}));
64
+ if (response.ok) {
65
+ return responseBody;
66
+ }
67
+ if (!RETRYABLE_STATUS_CODES.has(response.status)) {
68
+ throw new SendCoreError(response.status, {
69
+ statusCode: response.status,
70
+ message: responseBody.message || response.statusText,
71
+ error: responseBody.error,
72
+ ...responseBody
73
+ });
74
+ }
75
+ lastError = new SendCoreError(response.status, {
76
+ statusCode: response.status,
77
+ message: responseBody.message || response.statusText,
78
+ error: responseBody.error,
79
+ ...responseBody
80
+ });
81
+ } catch (err) {
82
+ if (err instanceof SendCoreError) throw err;
83
+ lastError = err;
84
+ }
85
+ if (attempt < this.retries) {
86
+ const delay = Math.min(1e3 * 2 ** attempt, 1e4);
87
+ await new Promise((r) => setTimeout(r, delay));
88
+ }
89
+ }
90
+ throw lastError ?? new Error("SendCore: Request failed after retries");
91
+ }
92
+ };
93
+ var EmailsResource = class {
94
+ constructor(client) {
95
+ this.client = client;
96
+ }
97
+ client;
98
+ /**
99
+ * Send an email.
100
+ *
101
+ * @example
102
+ * ```ts
103
+ * const result = await sendcore.emails.send({
104
+ * from: 'hello@yourdomain.com',
105
+ * to: 'user@example.com',
106
+ * subject: 'Hello!',
107
+ * html: '<h1>Welcome</h1>',
108
+ * });
109
+ * ```
110
+ */
111
+ async send(params) {
112
+ if (!params.from) {
113
+ throw new Error("SendCore: The 'from' field is required to send an email.");
114
+ }
115
+ if (!params.to) {
116
+ throw new Error("SendCore: The 'to' field is required to send an email.");
117
+ }
118
+ const payload = {
119
+ ...params,
120
+ to: Array.isArray(params.to) ? params.to : [params.to],
121
+ cc: params.cc ? Array.isArray(params.cc) ? params.cc : [params.cc] : void 0,
122
+ bcc: params.bcc ? Array.isArray(params.bcc) ? params.bcc : [params.bcc] : void 0,
123
+ replyTo: params.replyTo ? Array.isArray(params.replyTo) ? params.replyTo : [params.replyTo] : void 0
124
+ };
125
+ return this.client._request("POST", "/emails/send", payload);
126
+ }
127
+ /**
128
+ * Send an email using a pre-built template.
129
+ *
130
+ * @example
131
+ * ```ts
132
+ * await sendcore.emails.sendTemplate({
133
+ * from: 'hello@yourdomain.com',
134
+ * to: 'user@example.com',
135
+ * templateId: 'welcome-email',
136
+ * templateData: { name: 'John', plan: 'Pro' },
137
+ * });
138
+ * ```
139
+ */
140
+ async sendTemplate(params) {
141
+ return this.send(params);
142
+ }
143
+ };
144
+ var ContactsResource = class {
145
+ constructor(client) {
146
+ this.client = client;
147
+ }
148
+ client;
149
+ /**
150
+ * Subscribe a contact (add to your audience).
151
+ *
152
+ * @example
153
+ * ```ts
154
+ * await sendcore.contacts.subscribe({
155
+ * email: 'user@example.com',
156
+ * firstName: 'John',
157
+ * listId: 'lst_abc123',
158
+ * });
159
+ * ```
160
+ */
161
+ async subscribe(params) {
162
+ if (!params.email) {
163
+ throw new Error("SendCore: The 'email' field is required.");
164
+ }
165
+ return this.client._request("POST", "/organizations/audience/subscribe", params);
166
+ }
167
+ /**
168
+ * Unsubscribe a contact from your audience.
169
+ *
170
+ * @example
171
+ * ```ts
172
+ * await sendcore.contacts.unsubscribe({ email: 'user@example.com' });
173
+ * ```
174
+ */
175
+ async unsubscribe(params) {
176
+ if (!params.email) {
177
+ throw new Error("SendCore: The 'email' field is required.");
178
+ }
179
+ return this.client._request("POST", "/organizations/audience/unsubscribe", params);
180
+ }
181
+ };
182
+ export {
183
+ SendCore,
184
+ SendCoreError
185
+ };
package/package.json ADDED
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "sendcore",
3
+ "version": "1.0.0",
4
+ "description": "Official Node.js SDK for the SendCore email delivery platform",
5
+ "main": "dist/index.js",
6
+ "module": "dist/index.mjs",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "require": "./dist/index.js",
12
+ "import": "./dist/index.mjs"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist",
17
+ "README.md",
18
+ "LICENSE"
19
+ ],
20
+ "scripts": {
21
+ "build": "tsup src/index.ts --format cjs,esm --dts --clean",
22
+ "dev": "tsup src/index.ts --format cjs,esm --dts --watch",
23
+ "test": "vitest run",
24
+ "test:watch": "vitest",
25
+ "lint": "tsc --noEmit",
26
+ "prepublishOnly": "npm run build"
27
+ },
28
+ "keywords": [
29
+ "sendcore",
30
+ "email",
31
+ "smtp",
32
+ "transactional-email",
33
+ "email-api",
34
+ "broadcast",
35
+ "newsletter",
36
+ "elasto"
37
+ ],
38
+ "author": "Elasto Team",
39
+ "license": "MIT",
40
+ "repository": {
41
+ "type": "git",
42
+ "url": "https://github.com/elastodev/sendcore-node"
43
+ },
44
+ "engines": {
45
+ "node": ">=18.0.0"
46
+ },
47
+ "devDependencies": {
48
+ "tsup": "^8.0.0",
49
+ "typescript": "^5.4.0",
50
+ "vitest": "^2.0.0"
51
+ }
52
+ }