retransmit.dev 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 ADDED
@@ -0,0 +1,80 @@
1
+ # retransmit.dev
2
+
3
+ Node.js SDK for the [Retransmit](https://retransmit.dev) email API. Zero dependencies, works on Node 18+ and edge runtimes with `fetch`.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install retransmit.dev
9
+ # or
10
+ pnpm add retransmit.dev
11
+ ```
12
+
13
+ ## Usage
14
+
15
+ Grab an API key from your Retransmit dashboard, then:
16
+
17
+ ```ts
18
+ import { Retransmit } from "retransmit.dev";
19
+
20
+ const retransmit = new Retransmit("rt_xxxxxxxxxxxx");
21
+ // or set RETRANSMIT_API_KEY and call `new Retransmit()`
22
+
23
+ const { data, error } = await retransmit.emails.send({
24
+ from: "Acme <hello@yourdomain.com>",
25
+ to: "user@example.com",
26
+ subject: "Hello from Retransmit",
27
+ html: "<p>It works!</p>",
28
+ });
29
+
30
+ if (error) {
31
+ console.error(error.code, error.message);
32
+ } else {
33
+ console.log(data.id); // em_xxxxxxxxxxxx
34
+ }
35
+ ```
36
+
37
+ Emails are queued and sent asynchronously. Check the outcome later:
38
+
39
+ ```ts
40
+ const { data } = await retransmit.emails.get("em_xxxxxxxxxxxx");
41
+ console.log(data?.status); // "delivered"
42
+ ```
43
+
44
+ ### Batches
45
+
46
+ Send up to 10,000 emails in one request:
47
+
48
+ ```ts
49
+ const { data: batch } = await retransmit.batch.send([
50
+ { from: "Acme <hello@yourdomain.com>", to: "a@example.com", subject: "Hi", text: "Hello A" },
51
+ { from: "Acme <hello@yourdomain.com>", to: "b@example.com", subject: "Hi", text: "Hello B" },
52
+ ]);
53
+
54
+ const { data: progress } = await retransmit.batch.get(batch!.id);
55
+ console.log(progress?.processed, "/", progress?.total, progress?.counts);
56
+ ```
57
+
58
+ ## Error handling
59
+
60
+ Methods never throw on API errors — they return `{ data, error }`:
61
+
62
+ ```ts
63
+ const { data, error } = await retransmit.emails.send(/* ... */);
64
+ if (error) {
65
+ // error.code: "validation_error" | "domain_not_verified" | "unauthorized" | ...
66
+ }
67
+ ```
68
+
69
+ Only constructing the client without an API key throws.
70
+
71
+ ## Configuration
72
+
73
+ | Option | Env var | Default |
74
+ | --- | --- | --- |
75
+ | `apiKey` (first argument) | `RETRANSMIT_API_KEY` | — (required) |
76
+ | `baseUrl` | `RETRANSMIT_BASE_URL` | `https://api.retransmit.dev` |
77
+
78
+ ## License
79
+
80
+ MIT
package/dist/index.cjs ADDED
@@ -0,0 +1,163 @@
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
+ Batch: () => Batch,
24
+ EMAIL_STATUSES: () => EMAIL_STATUSES,
25
+ Emails: () => Emails,
26
+ Retransmit: () => Retransmit
27
+ });
28
+ module.exports = __toCommonJS(index_exports);
29
+
30
+ // src/emails.ts
31
+ function toWirePayload(options) {
32
+ return {
33
+ from: options.from,
34
+ to: options.to,
35
+ cc: options.cc,
36
+ bcc: options.bcc,
37
+ reply_to: options.replyTo,
38
+ subject: options.subject,
39
+ html: options.html,
40
+ text: options.text
41
+ };
42
+ }
43
+ var Emails = class {
44
+ constructor(client) {
45
+ this.client = client;
46
+ }
47
+ client;
48
+ /** Queues a single email. Poll `get(id)` or subscribe to webhooks for the outcome. */
49
+ send(options) {
50
+ return this.client.request("POST", "/v1/emails", toWirePayload(options));
51
+ }
52
+ /** Retrieves an email with its current status and event history. */
53
+ get(id) {
54
+ return this.client.request("GET", `/v1/emails/${encodeURIComponent(id)}`);
55
+ }
56
+ };
57
+
58
+ // src/batch.ts
59
+ var Batch = class {
60
+ constructor(client) {
61
+ this.client = client;
62
+ }
63
+ client;
64
+ /** Queues up to 10,000 emails in one request. Track progress with `get(id)`. */
65
+ send(emails) {
66
+ return this.client.request("POST", "/v1/emails/batch", {
67
+ emails: emails.map(toWirePayload)
68
+ });
69
+ }
70
+ /** Batch progress: how many emails are in each status so far. */
71
+ get(id) {
72
+ return this.client.request("GET", `/v1/emails/batch/${encodeURIComponent(id)}`);
73
+ }
74
+ };
75
+
76
+ // src/retransmit.ts
77
+ var DEFAULT_BASE_URL = "https://api.retransmit.dev";
78
+ var USER_AGENT = "retransmit.dev-node/0.1.0";
79
+ function readEnv(name) {
80
+ return typeof process !== "undefined" ? process.env?.[name] : void 0;
81
+ }
82
+ var Retransmit = class {
83
+ emails = new Emails(this);
84
+ batch = new Batch(this);
85
+ apiKey;
86
+ baseUrl;
87
+ constructor(apiKey, options = {}) {
88
+ const key = apiKey ?? readEnv("RETRANSMIT_API_KEY");
89
+ if (!key) {
90
+ throw new Error(
91
+ 'Missing API key. Pass it to `new Retransmit("rt_...")` or set the RETRANSMIT_API_KEY environment variable.'
92
+ );
93
+ }
94
+ this.apiKey = key;
95
+ this.baseUrl = (options.baseUrl ?? readEnv("RETRANSMIT_BASE_URL") ?? DEFAULT_BASE_URL).replace(
96
+ /\/+$/,
97
+ ""
98
+ );
99
+ }
100
+ /** Internal transport shared by the resource classes. API failures are returned, never thrown. */
101
+ async request(method, path, body) {
102
+ let response;
103
+ try {
104
+ response = await fetch(`${this.baseUrl}${path}`, {
105
+ method,
106
+ headers: {
107
+ Authorization: `Bearer ${this.apiKey}`,
108
+ "Content-Type": "application/json",
109
+ "User-Agent": USER_AGENT
110
+ },
111
+ body: body === void 0 ? void 0 : JSON.stringify(body)
112
+ });
113
+ } catch (cause) {
114
+ return {
115
+ data: null,
116
+ error: {
117
+ code: "network_error",
118
+ message: cause instanceof Error ? cause.message : "Unable to reach the Retransmit API"
119
+ }
120
+ };
121
+ }
122
+ let json = null;
123
+ try {
124
+ json = await response.json();
125
+ } catch {
126
+ }
127
+ if (!response.ok) {
128
+ const error = json?.error;
129
+ return {
130
+ data: null,
131
+ error: error ?? {
132
+ code: "internal_error",
133
+ message: `Request failed with status ${response.status}`
134
+ }
135
+ };
136
+ }
137
+ return { data: json, error: null };
138
+ }
139
+ };
140
+
141
+ // src/types.ts
142
+ var EMAIL_STATUSES = [
143
+ "queued",
144
+ "scheduled",
145
+ "sent",
146
+ "delivery_delayed",
147
+ "delivered",
148
+ "opened",
149
+ "clicked",
150
+ "bounced",
151
+ "complained",
152
+ "suppressed",
153
+ "canceled",
154
+ "rejected",
155
+ "failed"
156
+ ];
157
+ // Annotate the CommonJS export names for ESM import in node:
158
+ 0 && (module.exports = {
159
+ Batch,
160
+ EMAIL_STATUSES,
161
+ Emails,
162
+ Retransmit
163
+ });
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Response and payload types mirror the public API wire format
3
+ * (snake_case fields, ISO 8601 timestamps). Kept standalone on purpose:
4
+ * this package is published to npm and cannot import workspace packages.
5
+ */
6
+ declare const EMAIL_STATUSES: readonly ["queued", "scheduled", "sent", "delivery_delayed", "delivered", "opened", "clicked", "bounced", "complained", "suppressed", "canceled", "rejected", "failed"];
7
+ type EmailStatus = (typeof EMAIL_STATUSES)[number];
8
+ interface RetransmitError {
9
+ code: string;
10
+ message: string;
11
+ }
12
+ type Result<T> = {
13
+ data: T;
14
+ error: null;
15
+ } | {
16
+ data: null;
17
+ error: RetransmitError;
18
+ };
19
+ interface RetransmitOptions {
20
+ /** Override the API origin. Defaults to `https://api.retransmit.dev` (or `RETRANSMIT_BASE_URL`). */
21
+ baseUrl?: string;
22
+ }
23
+ interface SendEmailOptions {
24
+ /** Sender, as `address@domain.com` or `Name <address@domain.com>`. The domain must be verified on your account. */
25
+ from: string;
26
+ /** One recipient or up to 50. */
27
+ to: string | string[];
28
+ cc?: string | string[];
29
+ bcc?: string | string[];
30
+ replyTo?: string | string[];
31
+ subject: string;
32
+ /** HTML body. At least one of `html` or `text` is required. */
33
+ html?: string;
34
+ /** Plain-text body. At least one of `html` or `text` is required. */
35
+ text?: string;
36
+ }
37
+ interface SendEmailResponse {
38
+ id: string;
39
+ status: "queued";
40
+ created_at: string;
41
+ }
42
+ interface EmailEvent {
43
+ type: string;
44
+ created_at: string;
45
+ }
46
+ interface GetEmailResponse {
47
+ id: string;
48
+ batch_id: string | null;
49
+ from: string;
50
+ to: string[];
51
+ cc: string[] | null;
52
+ bcc: string[] | null;
53
+ reply_to: string[] | null;
54
+ subject: string;
55
+ status: EmailStatus;
56
+ error: string | null;
57
+ created_at: string;
58
+ last_event_at: string | null;
59
+ events: EmailEvent[];
60
+ }
61
+ interface SendBatchResponse {
62
+ id: string;
63
+ total: number;
64
+ status: "queued";
65
+ created_at: string;
66
+ }
67
+ interface GetBatchResponse {
68
+ id: string;
69
+ total: number;
70
+ /** Emails that have left the queue (any status other than `queued`/`scheduled`). */
71
+ processed: number;
72
+ /** Email count per status, e.g. `{ queued: 4, delivered: 96 }`. */
73
+ counts: Partial<Record<EmailStatus, number>>;
74
+ created_at: string;
75
+ }
76
+
77
+ declare class Batch {
78
+ private readonly client;
79
+ constructor(client: Retransmit);
80
+ /** Queues up to 10,000 emails in one request. Track progress with `get(id)`. */
81
+ send(emails: SendEmailOptions[]): Promise<Result<SendBatchResponse>>;
82
+ /** Batch progress: how many emails are in each status so far. */
83
+ get(id: string): Promise<Result<GetBatchResponse>>;
84
+ }
85
+
86
+ declare class Emails {
87
+ private readonly client;
88
+ constructor(client: Retransmit);
89
+ /** Queues a single email. Poll `get(id)` or subscribe to webhooks for the outcome. */
90
+ send(options: SendEmailOptions): Promise<Result<SendEmailResponse>>;
91
+ /** Retrieves an email with its current status and event history. */
92
+ get(id: string): Promise<Result<GetEmailResponse>>;
93
+ }
94
+
95
+ declare class Retransmit {
96
+ readonly emails: Emails;
97
+ readonly batch: Batch;
98
+ private readonly apiKey;
99
+ private readonly baseUrl;
100
+ constructor(apiKey?: string, options?: RetransmitOptions);
101
+ /** Internal transport shared by the resource classes. API failures are returned, never thrown. */
102
+ request<T>(method: "GET" | "POST", path: string, body?: unknown): Promise<Result<T>>;
103
+ }
104
+
105
+ export { Batch, EMAIL_STATUSES, type EmailEvent, type EmailStatus, Emails, type GetBatchResponse, type GetEmailResponse, type Result, Retransmit, type RetransmitError, type RetransmitOptions, type SendBatchResponse, type SendEmailOptions, type SendEmailResponse };
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Response and payload types mirror the public API wire format
3
+ * (snake_case fields, ISO 8601 timestamps). Kept standalone on purpose:
4
+ * this package is published to npm and cannot import workspace packages.
5
+ */
6
+ declare const EMAIL_STATUSES: readonly ["queued", "scheduled", "sent", "delivery_delayed", "delivered", "opened", "clicked", "bounced", "complained", "suppressed", "canceled", "rejected", "failed"];
7
+ type EmailStatus = (typeof EMAIL_STATUSES)[number];
8
+ interface RetransmitError {
9
+ code: string;
10
+ message: string;
11
+ }
12
+ type Result<T> = {
13
+ data: T;
14
+ error: null;
15
+ } | {
16
+ data: null;
17
+ error: RetransmitError;
18
+ };
19
+ interface RetransmitOptions {
20
+ /** Override the API origin. Defaults to `https://api.retransmit.dev` (or `RETRANSMIT_BASE_URL`). */
21
+ baseUrl?: string;
22
+ }
23
+ interface SendEmailOptions {
24
+ /** Sender, as `address@domain.com` or `Name <address@domain.com>`. The domain must be verified on your account. */
25
+ from: string;
26
+ /** One recipient or up to 50. */
27
+ to: string | string[];
28
+ cc?: string | string[];
29
+ bcc?: string | string[];
30
+ replyTo?: string | string[];
31
+ subject: string;
32
+ /** HTML body. At least one of `html` or `text` is required. */
33
+ html?: string;
34
+ /** Plain-text body. At least one of `html` or `text` is required. */
35
+ text?: string;
36
+ }
37
+ interface SendEmailResponse {
38
+ id: string;
39
+ status: "queued";
40
+ created_at: string;
41
+ }
42
+ interface EmailEvent {
43
+ type: string;
44
+ created_at: string;
45
+ }
46
+ interface GetEmailResponse {
47
+ id: string;
48
+ batch_id: string | null;
49
+ from: string;
50
+ to: string[];
51
+ cc: string[] | null;
52
+ bcc: string[] | null;
53
+ reply_to: string[] | null;
54
+ subject: string;
55
+ status: EmailStatus;
56
+ error: string | null;
57
+ created_at: string;
58
+ last_event_at: string | null;
59
+ events: EmailEvent[];
60
+ }
61
+ interface SendBatchResponse {
62
+ id: string;
63
+ total: number;
64
+ status: "queued";
65
+ created_at: string;
66
+ }
67
+ interface GetBatchResponse {
68
+ id: string;
69
+ total: number;
70
+ /** Emails that have left the queue (any status other than `queued`/`scheduled`). */
71
+ processed: number;
72
+ /** Email count per status, e.g. `{ queued: 4, delivered: 96 }`. */
73
+ counts: Partial<Record<EmailStatus, number>>;
74
+ created_at: string;
75
+ }
76
+
77
+ declare class Batch {
78
+ private readonly client;
79
+ constructor(client: Retransmit);
80
+ /** Queues up to 10,000 emails in one request. Track progress with `get(id)`. */
81
+ send(emails: SendEmailOptions[]): Promise<Result<SendBatchResponse>>;
82
+ /** Batch progress: how many emails are in each status so far. */
83
+ get(id: string): Promise<Result<GetBatchResponse>>;
84
+ }
85
+
86
+ declare class Emails {
87
+ private readonly client;
88
+ constructor(client: Retransmit);
89
+ /** Queues a single email. Poll `get(id)` or subscribe to webhooks for the outcome. */
90
+ send(options: SendEmailOptions): Promise<Result<SendEmailResponse>>;
91
+ /** Retrieves an email with its current status and event history. */
92
+ get(id: string): Promise<Result<GetEmailResponse>>;
93
+ }
94
+
95
+ declare class Retransmit {
96
+ readonly emails: Emails;
97
+ readonly batch: Batch;
98
+ private readonly apiKey;
99
+ private readonly baseUrl;
100
+ constructor(apiKey?: string, options?: RetransmitOptions);
101
+ /** Internal transport shared by the resource classes. API failures are returned, never thrown. */
102
+ request<T>(method: "GET" | "POST", path: string, body?: unknown): Promise<Result<T>>;
103
+ }
104
+
105
+ export { Batch, EMAIL_STATUSES, type EmailEvent, type EmailStatus, Emails, type GetBatchResponse, type GetEmailResponse, type Result, Retransmit, type RetransmitError, type RetransmitOptions, type SendBatchResponse, type SendEmailOptions, type SendEmailResponse };
package/dist/index.js ADDED
@@ -0,0 +1,133 @@
1
+ // src/emails.ts
2
+ function toWirePayload(options) {
3
+ return {
4
+ from: options.from,
5
+ to: options.to,
6
+ cc: options.cc,
7
+ bcc: options.bcc,
8
+ reply_to: options.replyTo,
9
+ subject: options.subject,
10
+ html: options.html,
11
+ text: options.text
12
+ };
13
+ }
14
+ var Emails = class {
15
+ constructor(client) {
16
+ this.client = client;
17
+ }
18
+ client;
19
+ /** Queues a single email. Poll `get(id)` or subscribe to webhooks for the outcome. */
20
+ send(options) {
21
+ return this.client.request("POST", "/v1/emails", toWirePayload(options));
22
+ }
23
+ /** Retrieves an email with its current status and event history. */
24
+ get(id) {
25
+ return this.client.request("GET", `/v1/emails/${encodeURIComponent(id)}`);
26
+ }
27
+ };
28
+
29
+ // src/batch.ts
30
+ var Batch = class {
31
+ constructor(client) {
32
+ this.client = client;
33
+ }
34
+ client;
35
+ /** Queues up to 10,000 emails in one request. Track progress with `get(id)`. */
36
+ send(emails) {
37
+ return this.client.request("POST", "/v1/emails/batch", {
38
+ emails: emails.map(toWirePayload)
39
+ });
40
+ }
41
+ /** Batch progress: how many emails are in each status so far. */
42
+ get(id) {
43
+ return this.client.request("GET", `/v1/emails/batch/${encodeURIComponent(id)}`);
44
+ }
45
+ };
46
+
47
+ // src/retransmit.ts
48
+ var DEFAULT_BASE_URL = "https://api.retransmit.dev";
49
+ var USER_AGENT = "retransmit.dev-node/0.1.0";
50
+ function readEnv(name) {
51
+ return typeof process !== "undefined" ? process.env?.[name] : void 0;
52
+ }
53
+ var Retransmit = class {
54
+ emails = new Emails(this);
55
+ batch = new Batch(this);
56
+ apiKey;
57
+ baseUrl;
58
+ constructor(apiKey, options = {}) {
59
+ const key = apiKey ?? readEnv("RETRANSMIT_API_KEY");
60
+ if (!key) {
61
+ throw new Error(
62
+ 'Missing API key. Pass it to `new Retransmit("rt_...")` or set the RETRANSMIT_API_KEY environment variable.'
63
+ );
64
+ }
65
+ this.apiKey = key;
66
+ this.baseUrl = (options.baseUrl ?? readEnv("RETRANSMIT_BASE_URL") ?? DEFAULT_BASE_URL).replace(
67
+ /\/+$/,
68
+ ""
69
+ );
70
+ }
71
+ /** Internal transport shared by the resource classes. API failures are returned, never thrown. */
72
+ async request(method, path, body) {
73
+ let response;
74
+ try {
75
+ response = await fetch(`${this.baseUrl}${path}`, {
76
+ method,
77
+ headers: {
78
+ Authorization: `Bearer ${this.apiKey}`,
79
+ "Content-Type": "application/json",
80
+ "User-Agent": USER_AGENT
81
+ },
82
+ body: body === void 0 ? void 0 : JSON.stringify(body)
83
+ });
84
+ } catch (cause) {
85
+ return {
86
+ data: null,
87
+ error: {
88
+ code: "network_error",
89
+ message: cause instanceof Error ? cause.message : "Unable to reach the Retransmit API"
90
+ }
91
+ };
92
+ }
93
+ let json = null;
94
+ try {
95
+ json = await response.json();
96
+ } catch {
97
+ }
98
+ if (!response.ok) {
99
+ const error = json?.error;
100
+ return {
101
+ data: null,
102
+ error: error ?? {
103
+ code: "internal_error",
104
+ message: `Request failed with status ${response.status}`
105
+ }
106
+ };
107
+ }
108
+ return { data: json, error: null };
109
+ }
110
+ };
111
+
112
+ // src/types.ts
113
+ var EMAIL_STATUSES = [
114
+ "queued",
115
+ "scheduled",
116
+ "sent",
117
+ "delivery_delayed",
118
+ "delivered",
119
+ "opened",
120
+ "clicked",
121
+ "bounced",
122
+ "complained",
123
+ "suppressed",
124
+ "canceled",
125
+ "rejected",
126
+ "failed"
127
+ ];
128
+ export {
129
+ Batch,
130
+ EMAIL_STATUSES,
131
+ Emails,
132
+ Retransmit
133
+ };
package/package.json ADDED
@@ -0,0 +1,54 @@
1
+ {
2
+ "name": "retransmit.dev",
3
+ "version": "0.1.0",
4
+ "description": "Node.js SDK for the Retransmit email API",
5
+ "keywords": [
6
+ "email",
7
+ "retransmit",
8
+ "ses",
9
+ "transactional-email"
10
+ ],
11
+ "homepage": "https://retransmit.dev/docs",
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "git+https://github.com/jpainam/retransmit.git",
15
+ "directory": "packages/sdk"
16
+ },
17
+ "license": "MIT",
18
+ "type": "module",
19
+ "sideEffects": false,
20
+ "main": "./dist/index.cjs",
21
+ "module": "./dist/index.js",
22
+ "types": "./dist/index.d.ts",
23
+ "exports": {
24
+ ".": {
25
+ "import": {
26
+ "types": "./dist/index.d.ts",
27
+ "default": "./dist/index.js"
28
+ },
29
+ "require": {
30
+ "types": "./dist/index.d.cts",
31
+ "default": "./dist/index.cjs"
32
+ }
33
+ }
34
+ },
35
+ "files": [
36
+ "dist"
37
+ ],
38
+ "devDependencies": {
39
+ "@types/node": "^26.2.0",
40
+ "tsup": "^8.5.0",
41
+ "typescript": "^6.0.3",
42
+ "@retransmit/config": "0.0.0"
43
+ },
44
+ "publishConfig": {
45
+ "access": "public"
46
+ },
47
+ "engines": {
48
+ "node": ">=18"
49
+ },
50
+ "scripts": {
51
+ "build": "tsup src/index.ts --format esm,cjs --dts --clean",
52
+ "check-types": "tsc --noEmit"
53
+ }
54
+ }