@sendclipper/node 0.0.0-stage → 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.
Files changed (125) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/LICENSE +21 -0
  3. package/README.md +208 -2
  4. package/dist/cjs/core.d.ts +95 -0
  5. package/dist/cjs/core.d.ts.map +1 -0
  6. package/dist/cjs/core.js +231 -0
  7. package/dist/cjs/core.js.map +1 -0
  8. package/dist/cjs/errors.d.ts +34 -0
  9. package/dist/cjs/errors.d.ts.map +1 -0
  10. package/dist/cjs/errors.js +110 -0
  11. package/dist/cjs/errors.js.map +1 -0
  12. package/dist/cjs/index.d.ts +52 -0
  13. package/dist/cjs/index.d.ts.map +1 -0
  14. package/dist/cjs/index.js +80 -0
  15. package/dist/cjs/index.js.map +1 -0
  16. package/dist/cjs/package.json +3 -0
  17. package/dist/cjs/render.d.ts +14 -0
  18. package/dist/cjs/render.d.ts.map +1 -0
  19. package/dist/cjs/render.js +64 -0
  20. package/dist/cjs/render.js.map +1 -0
  21. package/dist/cjs/resources/audience.d.ts +51 -0
  22. package/dist/cjs/resources/audience.d.ts.map +1 -0
  23. package/dist/cjs/resources/audience.js +105 -0
  24. package/dist/cjs/resources/audience.js.map +1 -0
  25. package/dist/cjs/resources/domains.d.ts +22 -0
  26. package/dist/cjs/resources/domains.d.ts.map +1 -0
  27. package/dist/cjs/resources/domains.js +45 -0
  28. package/dist/cjs/resources/domains.js.map +1 -0
  29. package/dist/cjs/resources/emails.d.ts +34 -0
  30. package/dist/cjs/resources/emails.d.ts.map +1 -0
  31. package/dist/cjs/resources/emails.js +67 -0
  32. package/dist/cjs/resources/emails.js.map +1 -0
  33. package/dist/cjs/resources/mailboxes.d.ts +27 -0
  34. package/dist/cjs/resources/mailboxes.d.ts.map +1 -0
  35. package/dist/cjs/resources/mailboxes.js +65 -0
  36. package/dist/cjs/resources/mailboxes.js.map +1 -0
  37. package/dist/cjs/resources/sandboxes.d.ts +54 -0
  38. package/dist/cjs/resources/sandboxes.d.ts.map +1 -0
  39. package/dist/cjs/resources/sandboxes.js +107 -0
  40. package/dist/cjs/resources/sandboxes.js.map +1 -0
  41. package/dist/cjs/resources/templates.d.ts +58 -0
  42. package/dist/cjs/resources/templates.d.ts.map +1 -0
  43. package/dist/cjs/resources/templates.js +97 -0
  44. package/dist/cjs/resources/templates.js.map +1 -0
  45. package/dist/cjs/resources/webhooks.d.ts +39 -0
  46. package/dist/cjs/resources/webhooks.d.ts.map +1 -0
  47. package/dist/cjs/resources/webhooks.js +75 -0
  48. package/dist/cjs/resources/webhooks.js.map +1 -0
  49. package/dist/cjs/resources/workspace.d.ts +21 -0
  50. package/dist/cjs/resources/workspace.d.ts.map +1 -0
  51. package/dist/cjs/resources/workspace.js +38 -0
  52. package/dist/cjs/resources/workspace.js.map +1 -0
  53. package/dist/cjs/types.d.ts +662 -0
  54. package/dist/cjs/types.d.ts.map +1 -0
  55. package/dist/cjs/types.js +9 -0
  56. package/dist/cjs/types.js.map +1 -0
  57. package/dist/cjs/version.d.ts +3 -0
  58. package/dist/cjs/version.d.ts.map +1 -0
  59. package/dist/cjs/version.js +6 -0
  60. package/dist/cjs/version.js.map +1 -0
  61. package/dist/cjs/webhook-signature.d.ts +38 -0
  62. package/dist/cjs/webhook-signature.d.ts.map +1 -0
  63. package/dist/cjs/webhook-signature.js +102 -0
  64. package/dist/cjs/webhook-signature.js.map +1 -0
  65. package/dist/esm/core.d.ts +95 -0
  66. package/dist/esm/core.d.ts.map +1 -0
  67. package/dist/esm/core.js +225 -0
  68. package/dist/esm/core.js.map +1 -0
  69. package/dist/esm/errors.d.ts +34 -0
  70. package/dist/esm/errors.d.ts.map +1 -0
  71. package/dist/esm/errors.js +102 -0
  72. package/dist/esm/errors.js.map +1 -0
  73. package/dist/esm/index.d.ts +52 -0
  74. package/dist/esm/index.d.ts.map +1 -0
  75. package/dist/esm/index.js +65 -0
  76. package/dist/esm/index.js.map +1 -0
  77. package/dist/esm/render.d.ts +14 -0
  78. package/dist/esm/render.d.ts.map +1 -0
  79. package/dist/esm/render.js +28 -0
  80. package/dist/esm/render.js.map +1 -0
  81. package/dist/esm/resources/audience.d.ts +51 -0
  82. package/dist/esm/resources/audience.d.ts.map +1 -0
  83. package/dist/esm/resources/audience.js +99 -0
  84. package/dist/esm/resources/audience.js.map +1 -0
  85. package/dist/esm/resources/domains.d.ts +22 -0
  86. package/dist/esm/resources/domains.d.ts.map +1 -0
  87. package/dist/esm/resources/domains.js +41 -0
  88. package/dist/esm/resources/domains.js.map +1 -0
  89. package/dist/esm/resources/emails.d.ts +34 -0
  90. package/dist/esm/resources/emails.d.ts.map +1 -0
  91. package/dist/esm/resources/emails.js +62 -0
  92. package/dist/esm/resources/emails.js.map +1 -0
  93. package/dist/esm/resources/mailboxes.d.ts +27 -0
  94. package/dist/esm/resources/mailboxes.d.ts.map +1 -0
  95. package/dist/esm/resources/mailboxes.js +60 -0
  96. package/dist/esm/resources/mailboxes.js.map +1 -0
  97. package/dist/esm/resources/sandboxes.d.ts +54 -0
  98. package/dist/esm/resources/sandboxes.d.ts.map +1 -0
  99. package/dist/esm/resources/sandboxes.js +102 -0
  100. package/dist/esm/resources/sandboxes.js.map +1 -0
  101. package/dist/esm/resources/templates.d.ts +58 -0
  102. package/dist/esm/resources/templates.d.ts.map +1 -0
  103. package/dist/esm/resources/templates.js +91 -0
  104. package/dist/esm/resources/templates.js.map +1 -0
  105. package/dist/esm/resources/webhooks.d.ts +39 -0
  106. package/dist/esm/resources/webhooks.d.ts.map +1 -0
  107. package/dist/esm/resources/webhooks.js +70 -0
  108. package/dist/esm/resources/webhooks.js.map +1 -0
  109. package/dist/esm/resources/workspace.d.ts +21 -0
  110. package/dist/esm/resources/workspace.d.ts.map +1 -0
  111. package/dist/esm/resources/workspace.js +32 -0
  112. package/dist/esm/resources/workspace.js.map +1 -0
  113. package/dist/esm/types.d.ts +662 -0
  114. package/dist/esm/types.d.ts.map +1 -0
  115. package/dist/esm/types.js +8 -0
  116. package/dist/esm/types.js.map +1 -0
  117. package/dist/esm/version.d.ts +3 -0
  118. package/dist/esm/version.d.ts.map +1 -0
  119. package/dist/esm/version.js +3 -0
  120. package/dist/esm/version.js.map +1 -0
  121. package/dist/esm/webhook-signature.d.ts +38 -0
  122. package/dist/esm/webhook-signature.d.ts.map +1 -0
  123. package/dist/esm/webhook-signature.js +95 -0
  124. package/dist/esm/webhook-signature.js.map +1 -0
  125. package/package.json +72 -4
package/CHANGELOG.md ADDED
@@ -0,0 +1,12 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0 — 2026-10-16
4
+
5
+ First release.
6
+
7
+ - `emails.send`, `batch.send`, `emails.get`, `emails.list`, `emails.iterate`, `emails.stats`.
8
+ - Templates (with tags and image assets), domains, sandboxes (including the JSON door and `waitFor`), webhooks and their dispatches, contacts, audiences, suppressions, mailboxes, automations, workspace and API keys.
9
+ - Every method resolves to `{ data, error, headers }`.
10
+ - Automatic `Idempotency-Key` on every JSON write; retries on network errors, 429 and 5xx.
11
+ - `react` support through the optional `@react-email/render`.
12
+ - `verifyWebhook()` for `X-SendClipper-Signature-V2`, rotation-aware.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 SendClipper
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,209 @@
1
- # Temporary Holding Version
1
+ # SendClipper Node.js SDK
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ The official Node.js library for the [SendClipper](https://sendclipper.com) email API.
4
+
5
+ ```bash
6
+ npm install @sendclipper/node
7
+ ```
8
+
9
+ Node.js 20 or newer. No runtime dependencies. ESM and CommonJS.
10
+
11
+ ## Setup
12
+
13
+ Create an API key in the dashboard under **API keys**.
14
+
15
+ ```js
16
+ import { SendClipper } from '@sendclipper/node';
17
+
18
+ const sc = new SendClipper('sc_live_xxxxxxxxxxxxxxxx');
19
+ // or: new SendClipper() — reads SENDCLIPPER_API_KEY
20
+ ```
21
+
22
+ ## Send an email
23
+
24
+ ```js
25
+ const { data, error } = await sc.emails.send({
26
+ from: 'hello@send.acme.io',
27
+ to: 'ada@lumen.io',
28
+ subject: 'Your receipt',
29
+ html: '<p>Thanks for your order.</p>',
30
+ text: 'Thanks for your order.',
31
+ });
32
+
33
+ if (error) {
34
+ console.error(error.name, error.message); // e.g. domain_not_verified
35
+ } else {
36
+ console.log(`Email ${data.id} accepted`);
37
+ }
38
+ ```
39
+
40
+ `from` must be a bare address on a domain you verified in SendClipper. Each message has one recipient in `to`; for several, use `batch.send`.
41
+
42
+ ## Send with a template
43
+
44
+ Design and publish a template in the dashboard, then send it by id or by its send name:
45
+
46
+ ```js
47
+ await sc.emails.send({
48
+ from: 'hello@send.acme.io',
49
+ to: 'ada@lumen.io',
50
+ templateId: 'tpl_9f2b7c10ad42', // or template: 'welcome'
51
+ variables: { first_name: 'Ada' },
52
+ });
53
+ ```
54
+
55
+ A tag with no value and no `{{tag|fallback}}` is refused by name (`template_variables_missing`), never sent blank.
56
+
57
+ ## Send with React
58
+
59
+ Install `@react-email/render` and pass a component:
60
+
61
+ ```jsx
62
+ import { WelcomeEmail } from './emails/welcome';
63
+
64
+ await sc.emails.send({
65
+ from: 'hello@send.acme.io',
66
+ to: 'ada@lumen.io',
67
+ subject: 'Welcome',
68
+ react: <WelcomeEmail firstName="Ada" />,
69
+ });
70
+ ```
71
+
72
+ The SDK renders it to `html` and a plain-text `text` before sending.
73
+
74
+ ## Send a batch
75
+
76
+ Up to 500 messages in one request. Each item is accepted or refused on its own:
77
+
78
+ ```js
79
+ const { data } = await sc.batch.send([
80
+ { from: 'hello@send.acme.io', to: 'ada@lumen.io', subject: 'Hi Ada', html: '<p>Hi</p>' },
81
+ { from: 'hello@send.acme.io', to: 'alan@lumen.io', templateId: 'tpl_9f2b7c10ad42', variables: { first_name: 'Alan' } },
82
+ ]);
83
+
84
+ for (const item of data.data) {
85
+ if (item.status === 'failed') console.warn(item.index, item.error.code);
86
+ }
87
+ ```
88
+
89
+ ## Errors
90
+
91
+ API methods never throw for an API refusal or a network failure. They resolve to `{ data, error, headers }`, and exactly one of `data` and `error` is set:
92
+
93
+ ```ts
94
+ type ErrorResponse = {
95
+ name: string; // SendClipper's code: 'validation_failed', 'rate_limited', 'network_error', …
96
+ message: string; // a sentence for a person
97
+ statusCode: number | null; // null when no answer arrived (network, timeout, abort)
98
+ detail?: unknown; // code-specific facts
99
+ };
100
+ ```
101
+
102
+ The async iterators (`emails.iterate()` and friends) throw a `SendClipperApiError` instead, since a loop has no `error` to hand back.
103
+
104
+ ## Retries and idempotency
105
+
106
+ Network errors, `429` and `5xx` answers are retried twice with backoff, honouring `Retry-After`. Every JSON write carries an `Idempotency-Key`, and the API answers a repeated key with the first result for 24 hours. A retried send therefore never goes out twice.
107
+
108
+ To make your own retries safe as well (a job that runs again after a crash), pass a key that names the logical operation:
109
+
110
+ ```js
111
+ await sc.emails.send(params, { idempotencyKey: `order-${order.id}-receipt` });
112
+ ```
113
+
114
+ ## Request options
115
+
116
+ Every method takes request options as its last argument:
117
+
118
+ ```js
119
+ await sc.emails.list(
120
+ { status: 'bounced' },
121
+ { signal: AbortSignal.timeout(5000), headers: { 'X-Trace-Id': traceId } },
122
+ );
123
+ ```
124
+
125
+ | Option | Meaning |
126
+ |---|---|
127
+ | `idempotencyKey` | Your own key for a write |
128
+ | `signal` | Abort the call |
129
+ | `timeoutMs` | Per-attempt timeout (default 30 000) |
130
+ | `maxRetries` | Retries for this call (default 2) |
131
+ | `headers` | Extra request headers |
132
+
133
+ Client options: `new SendClipper(key, { baseUrl, timeoutMs, maxRetries, fetch, defaultHeaders })`. `baseUrl` also reads `SENDCLIPPER_BASE_URL`.
134
+
135
+ ## Verify webhooks
136
+
137
+ SendClipper signs each delivery. Verify it against the **raw** body, before parsing:
138
+
139
+ ```js
140
+ import express from 'express';
141
+ import { verifyWebhook, WebhookVerificationError } from '@sendclipper/node';
142
+
143
+ app.post('/hooks/sendclipper', express.raw({ type: 'application/json' }), (req, res) => {
144
+ try {
145
+ const event = verifyWebhook({
146
+ payload: req.body, // a Buffer, not parsed JSON
147
+ headers: req.headers,
148
+ secret: process.env.SENDCLIPPER_WEBHOOK_SECRET,
149
+ });
150
+ // event.id is stable across retries: deduplicate on it
151
+ console.log(event.event, event.data.to);
152
+ res.sendStatus(200);
153
+ } catch (err) {
154
+ if (err instanceof WebhookVerificationError) return res.status(400).send(err.reason);
155
+ throw err;
156
+ }
157
+ });
158
+ ```
159
+
160
+ During a secret rotation a delivery carries two signatures and either one is accepted. A delivery signed more than five minutes away from now is refused. `sc.webhooks.verify(...)` is the same function.
161
+
162
+ To test your handler locally, `signatureHeader(secret, rawBody)` builds a valid header.
163
+
164
+ ## Sandboxes
165
+
166
+ A sandbox traps mail while you develop: nothing is delivered.
167
+
168
+ ```js
169
+ const { data: box } = await sc.sandboxes.create({ name: 'my-app-dev' });
170
+ const { data: creds } = await sc.sandboxes.credentials(box.id); // SMTP host, port, user, password
171
+
172
+ // Or send through the sandbox's JSON door with its own token, using the same fields as emails.send():
173
+ const { data: token } = await sc.sandboxes.resetCredentials(box.id);
174
+ await sc.sandboxes.send(token.apiToken, { from: 'app@example.com', to: 'test@example.com', subject: 'Hi', html: '<p>Hi</p>' });
175
+
176
+ // In a test: wait for the message to arrive
177
+ const { data: msg } = await sc.sandboxes.messages.waitFor(box.id, { match: (m) => m.subject === 'Hi' });
178
+ ```
179
+
180
+ ## Everything else
181
+
182
+ | Resource | Methods |
183
+ |---|---|
184
+ | `emails` | `send`, `get`, `list`, `iterate`, `stats` |
185
+ | `batch` | `send` |
186
+ | `domains` | `list`, `create`, `records`, `verify`, `enableReceiving`, `disableReceiving`, `setRegion`, `deletionPreview`, `remove` |
187
+ | `templates` | `list`, `get`, `html`, `create`, `update`, `remove`, `duplicate`, `publish`, `unpublish`, `preview`, `setTags`, `projects` |
188
+ | `templates.tags` | `list`, `create`, `update`, `remove` |
189
+ | `templates.assets` | `list`, `upload` |
190
+ | `sandboxes` | `list`, `get`, `create`, `update`, `remove`, `credentials`, `resetCredentials`, `send` |
191
+ | `sandboxes.messages` | `list`, `get`, `raw`, `remove`, `clear`, `markAllRead`, `waitFor` |
192
+ | `webhooks` | `list`, `create`, `update`, `remove`, `rotateSecret`, `deliveries`, `verify` |
193
+ | `webhooks.dispatches` | `list`, `iterate`, `get`, `replay`, `replayAll` |
194
+ | `contacts` | `list`, `iterate`, `create`, `import`, `importPreview`, `export` |
195
+ | `audiences` | `list` |
196
+ | `suppressions` | `list`, `iterate`, `create`, `remove`, `export` |
197
+ | `mailboxes` | `list`, `get`, `create`, `update`, `archive` |
198
+ | `mailboxes.messages` | `list`, `iterate`, `get`, `attachment` |
199
+ | `automations` | `enroll` |
200
+ | `workspace` | `get` |
201
+ | `apiKeys` | `list` |
202
+
203
+ Management routes need a key with **Full access**. A sending-only key can call `emails.send` and `batch.send`.
204
+
205
+ Full API reference: <https://sendclipper.com/docs>.
206
+
207
+ ## License
208
+
209
+ MIT
@@ -0,0 +1,95 @@
1
+ export declare const DEFAULT_BASE_URL = "https://sendclipper.com";
2
+ export interface ClientOptions {
3
+ /** Defaults to `process.env.SENDCLIPPER_API_KEY`. A `sc_live_…` key. */
4
+ apiKey?: string;
5
+ /** Defaults to `process.env.SENDCLIPPER_BASE_URL`, then `https://sendclipper.com`. No `/v1`. */
6
+ baseUrl?: string;
7
+ /** Per attempt, in milliseconds. Default 30 000. */
8
+ timeoutMs?: number;
9
+ /**
10
+ * Retries after a network error, a 429 or a 5xx. Default 2. A mutation is
11
+ * only retried when it carries an `Idempotency-Key`, which the SDK adds to
12
+ * every JSON mutation, so a retried send can never go out twice.
13
+ */
14
+ maxRetries?: number;
15
+ /** Your own `fetch` (a proxy agent, a test double). Defaults to the global one. */
16
+ fetch?: typeof fetch;
17
+ /** Sent on every request. */
18
+ defaultHeaders?: Record<string, string>;
19
+ }
20
+ /** What an API refusal or a network failure looks like. */
21
+ export interface ErrorResponse {
22
+ /** SendClipper's machine-readable code: `domain_not_verified`, `validation_failed`, `rate_limited`, `network_error`, … */
23
+ name: string;
24
+ /** A sentence meant for a person. */
25
+ message: string;
26
+ /** The HTTP status, or `null` when no answer arrived (network, timeout, abort). */
27
+ statusCode: number | null;
28
+ /** Extra, code-specific facts (e.g. the missing template tags). */
29
+ detail?: unknown;
30
+ }
31
+ /** Every API method resolves to this. Exactly one of `data` and `error` is set. */
32
+ export type Result<T> = ({
33
+ data: T;
34
+ error: null;
35
+ } | {
36
+ data: null;
37
+ error: ErrorResponse;
38
+ }) & {
39
+ headers: Record<string, string> | null;
40
+ };
41
+ export type Query = Record<string, string | number | boolean | undefined | null>;
42
+ export interface RequestOptions {
43
+ /** Your own key, reused when YOU retry the same logical call later. */
44
+ idempotencyKey?: string;
45
+ /** Override the client's timeout for this call. */
46
+ timeoutMs?: number;
47
+ /** Override the client's retry count for this call. */
48
+ maxRetries?: number;
49
+ /** Abort the call from outside. */
50
+ signal?: AbortSignal;
51
+ /** Extra headers for this call. */
52
+ headers?: Record<string, string>;
53
+ }
54
+ /** What a download resolves to. */
55
+ export interface DownloadedFile {
56
+ bytes: Uint8Array;
57
+ contentType: string | null;
58
+ filename: string | null;
59
+ }
60
+ interface InternalRequest extends RequestOptions {
61
+ method: string;
62
+ path: string;
63
+ query?: Query;
64
+ body?: unknown;
65
+ form?: FormData;
66
+ as?: 'json' | 'text' | 'bytes';
67
+ /** Replaces the API key (the sandbox JSON door takes an `sc_sbx_` token). */
68
+ bearer?: string;
69
+ }
70
+ export declare class HttpClient {
71
+ readonly baseUrl: string;
72
+ private readonly apiKey;
73
+ private readonly timeoutMs;
74
+ private readonly maxRetries;
75
+ private readonly fetchImpl;
76
+ private readonly defaultHeaders;
77
+ constructor(opts?: ClientOptions);
78
+ /**
79
+ * The public face of every API method: never throws for an API refusal or
80
+ * a network failure, it answers `{ data, error, headers }`.
81
+ */
82
+ call<T>(req: InternalRequest): Promise<Result<T>>;
83
+ /** Like `call()`, but throws `SendClipperApiError` / `SendClipperConnectionError`. */
84
+ request<T>(req: InternalRequest): Promise<{
85
+ data: T;
86
+ headers: Record<string, string>;
87
+ }>;
88
+ private fetchOnce;
89
+ }
90
+ /** `encodeURIComponent` for one path segment. */
91
+ export declare function seg(value: string): string;
92
+ /** Turn a `Result` back into a value, throwing its error. Used by the iterators. */
93
+ export declare function unwrap<T>(res: Result<T>): T;
94
+ export {};
95
+ //# sourceMappingURL=core.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"core.d.ts","sourceRoot":"","sources":["../../src/core.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,231 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.HttpClient = exports.DEFAULT_BASE_URL = void 0;
4
+ exports.seg = seg;
5
+ exports.unwrap = unwrap;
6
+ const node_crypto_1 = require("node:crypto");
7
+ const errors_js_1 = require("./errors.js");
8
+ const version_js_1 = require("./version.js");
9
+ exports.DEFAULT_BASE_URL = 'https://sendclipper.com';
10
+ const RETRYABLE_STATUS = new Set([408, 429, 500, 502, 503, 504]);
11
+ const MAX_RETRY_AFTER_MS = 60_000;
12
+ class HttpClient {
13
+ baseUrl;
14
+ apiKey;
15
+ timeoutMs;
16
+ maxRetries;
17
+ fetchImpl;
18
+ defaultHeaders;
19
+ constructor(opts = {}) {
20
+ const env = typeof process !== 'undefined' ? process.env : {};
21
+ this.apiKey = opts.apiKey ?? env.SENDCLIPPER_API_KEY;
22
+ this.baseUrl = (opts.baseUrl ?? env.SENDCLIPPER_BASE_URL ?? exports.DEFAULT_BASE_URL).replace(/\/+$/, '').replace(/\/v1$/, '');
23
+ this.timeoutMs = opts.timeoutMs ?? 30_000;
24
+ this.maxRetries = Math.max(0, opts.maxRetries ?? 2);
25
+ const f = opts.fetch ?? globalThis.fetch;
26
+ if (typeof f !== 'function') {
27
+ throw new Error('@sendclipper/node needs a global fetch (Node.js 18 or newer) or a `fetch` option.');
28
+ }
29
+ this.fetchImpl = f;
30
+ this.defaultHeaders = opts.defaultHeaders ?? {};
31
+ }
32
+ /**
33
+ * The public face of every API method: never throws for an API refusal or
34
+ * a network failure, it answers `{ data, error, headers }`.
35
+ */
36
+ async call(req) {
37
+ try {
38
+ const { data, headers } = await this.request(req);
39
+ return { data, error: null, headers };
40
+ }
41
+ catch (err) {
42
+ if (err instanceof errors_js_1.SendClipperApiError) {
43
+ return { data: null, error: errorResponseOf(err), headers: err.headers ? headersOf(err.headers) : null };
44
+ }
45
+ if (err instanceof errors_js_1.SendClipperConnectionError) {
46
+ return { data: null, error: { name: 'network_error', message: err.message, statusCode: null }, headers: null };
47
+ }
48
+ if (req.signal?.aborted) {
49
+ return {
50
+ data: null,
51
+ error: { name: 'network_error', message: 'The request was aborted.', statusCode: null },
52
+ headers: null,
53
+ };
54
+ }
55
+ throw err;
56
+ }
57
+ }
58
+ /** Like `call()`, but throws `SendClipperApiError` / `SendClipperConnectionError`. */
59
+ async request(req) {
60
+ const bearer = req.bearer ?? this.apiKey;
61
+ if (!bearer) {
62
+ throw new errors_js_1.SendClipperApiError(401, 'api_key_missing', 'No SendClipper API key. Pass one to `new SendClipper("sc_live_…")` or set SENDCLIPPER_API_KEY.');
63
+ }
64
+ const url = new URL(`${this.baseUrl}/v1${req.path.startsWith('/') ? req.path : `/${req.path}`}`);
65
+ for (const [k, v] of Object.entries(req.query ?? {})) {
66
+ if (v !== undefined && v !== null && v !== '')
67
+ url.searchParams.set(k, String(v));
68
+ }
69
+ const headers = {
70
+ Authorization: `Bearer ${bearer}`,
71
+ Accept: req.as === 'json' || !req.as ? 'application/json' : '*/*',
72
+ 'User-Agent': `sendclipper-node/${version_js_1.VERSION}`,
73
+ ...this.defaultHeaders,
74
+ ...(req.headers ?? {}),
75
+ };
76
+ let body;
77
+ if (req.form) {
78
+ body = req.form;
79
+ }
80
+ else if (req.body !== undefined) {
81
+ headers['Content-Type'] = 'application/json';
82
+ body = JSON.stringify(req.body);
83
+ }
84
+ const isRead = req.method === 'GET' || req.method === 'HEAD';
85
+ // The API de-duplicates every JSON mutation on this header for 24 hours.
86
+ // A multipart body is not fingerprinted server-side, so it gets no key and
87
+ // is never retried.
88
+ if (!isRead && !req.form) {
89
+ headers['Idempotency-Key'] = req.idempotencyKey ?? `sc-node-${(0, node_crypto_1.randomUUID)()}`;
90
+ }
91
+ else if (req.idempotencyKey) {
92
+ headers['Idempotency-Key'] = req.idempotencyKey;
93
+ }
94
+ const canRetry = isRead || Boolean(headers['Idempotency-Key']);
95
+ const maxRetries = canRetry ? Math.max(0, req.maxRetries ?? this.maxRetries) : 0;
96
+ for (let attempt = 0;; attempt++) {
97
+ let res;
98
+ try {
99
+ res = await this.fetchOnce(url, req.method, headers, body, req.timeoutMs ?? this.timeoutMs, req.signal);
100
+ }
101
+ catch (err) {
102
+ if (req.signal?.aborted)
103
+ throw err;
104
+ if (attempt < maxRetries) {
105
+ await sleep(backoffMs(attempt));
106
+ continue;
107
+ }
108
+ throw err;
109
+ }
110
+ if (!res.ok && RETRYABLE_STATUS.has(res.status) && attempt < maxRetries) {
111
+ await res.body?.cancel().catch(() => undefined);
112
+ await sleep(retryAfterMs(res.headers) ?? backoffMs(attempt));
113
+ continue;
114
+ }
115
+ if (!res.ok) {
116
+ const err = (0, errors_js_1.toApiError)(res.status, await res.text(), res.headers);
117
+ // Our own earlier attempt is still running server-side (a timeout
118
+ // that was not a failure). Its result is replayed once it finishes.
119
+ if (err.code === 'idempotency_key_in_flight' && attempt < maxRetries) {
120
+ await sleep(retryAfterMs(res.headers) ?? backoffMs(attempt));
121
+ continue;
122
+ }
123
+ throw err;
124
+ }
125
+ const responseHeaders = headersOf(res.headers);
126
+ if (req.as === 'bytes') {
127
+ const file = {
128
+ bytes: new Uint8Array(await res.arrayBuffer()),
129
+ contentType: res.headers.get('content-type'),
130
+ filename: filenameOf(res.headers.get('content-disposition')),
131
+ };
132
+ return { data: file, headers: responseHeaders };
133
+ }
134
+ const text = await res.text();
135
+ if (req.as === 'text' || !text)
136
+ return { data: (req.as === 'text' ? text : null), headers: responseHeaders };
137
+ try {
138
+ return { data: JSON.parse(text), headers: responseHeaders };
139
+ }
140
+ catch {
141
+ return { data: text, headers: responseHeaders };
142
+ }
143
+ }
144
+ }
145
+ async fetchOnce(url, method, headers, body, timeoutMs, outer) {
146
+ const ctrl = new AbortController();
147
+ const onAbort = () => ctrl.abort(outer?.reason);
148
+ if (outer) {
149
+ if (outer.aborted)
150
+ ctrl.abort(outer.reason);
151
+ else
152
+ outer.addEventListener('abort', onAbort, { once: true });
153
+ }
154
+ const timer = setTimeout(() => ctrl.abort(new Error('timeout')), timeoutMs);
155
+ try {
156
+ return await this.fetchImpl(url, { method, headers, body, signal: ctrl.signal });
157
+ }
158
+ catch (err) {
159
+ if (outer?.aborted)
160
+ throw err;
161
+ const timedOut = ctrl.signal.aborted;
162
+ throw new errors_js_1.SendClipperConnectionError(timedOut
163
+ ? `SendClipper did not answer within ${timeoutMs} ms (${method} ${url.pathname}).`
164
+ : `Could not reach SendClipper at ${this.baseUrl}: ${err.message}`, err);
165
+ }
166
+ finally {
167
+ clearTimeout(timer);
168
+ outer?.removeEventListener('abort', onAbort);
169
+ }
170
+ }
171
+ }
172
+ exports.HttpClient = HttpClient;
173
+ function backoffMs(attempt) {
174
+ const base = Math.min(8_000, 500 * 2 ** attempt);
175
+ return base / 2 + Math.random() * (base / 2);
176
+ }
177
+ function retryAfterMs(headers) {
178
+ const raw = headers.get('retry-after');
179
+ if (!raw)
180
+ return undefined;
181
+ const seconds = Number(raw);
182
+ if (Number.isFinite(seconds) && seconds >= 0)
183
+ return Math.min(MAX_RETRY_AFTER_MS, seconds * 1000);
184
+ const at = Date.parse(raw);
185
+ if (Number.isNaN(at))
186
+ return undefined;
187
+ return Math.min(MAX_RETRY_AFTER_MS, Math.max(0, at - Date.now()));
188
+ }
189
+ function filenameOf(disposition) {
190
+ if (!disposition)
191
+ return null;
192
+ const star = /filename\*\s*=\s*(?:UTF-8'')?([^;]+)/i.exec(disposition);
193
+ if (star?.[1]) {
194
+ try {
195
+ return decodeURIComponent(star[1].trim().replace(/^"|"$/g, ''));
196
+ }
197
+ catch {
198
+ /* fall through to the plain form */
199
+ }
200
+ }
201
+ const plain = /filename\s*=\s*"?([^";]+)"?/i.exec(disposition);
202
+ return plain?.[1]?.trim() ?? null;
203
+ }
204
+ function sleep(ms) {
205
+ return new Promise((resolve) => setTimeout(resolve, ms));
206
+ }
207
+ /** `encodeURIComponent` for one path segment. */
208
+ function seg(value) {
209
+ return encodeURIComponent(value);
210
+ }
211
+ function headersOf(h) {
212
+ const out = {};
213
+ h.forEach((value, key) => {
214
+ out[key] = value;
215
+ });
216
+ return out;
217
+ }
218
+ function errorResponseOf(err) {
219
+ const e = { name: err.code, message: err.message, statusCode: err.status };
220
+ if (err.detail !== undefined)
221
+ e.detail = err.detail;
222
+ return e;
223
+ }
224
+ /** Turn a `Result` back into a value, throwing its error. Used by the iterators. */
225
+ function unwrap(res) {
226
+ if (res.error) {
227
+ throw new errors_js_1.SendClipperApiError(res.error.statusCode ?? 0, res.error.name, res.error.message, res.error.detail);
228
+ }
229
+ return res.data;
230
+ }
231
+ //# sourceMappingURL=core.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"core.js","sourceRoot":"","sources":["../../src/core.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ /** Base class for everything this SDK throws on purpose. */
2
+ export declare class SendClipperError extends Error {
3
+ constructor(message: string);
4
+ }
5
+ /**
6
+ * The API answered with a non-2xx status. `code` is the API's own
7
+ * machine-readable reason (`domain_not_verified`, `template_variables_missing`,
8
+ * `rate_limited`, …) and is the field to branch on; `message` is a sentence
9
+ * meant for a person.
10
+ */
11
+ export declare class SendClipperApiError extends SendClipperError {
12
+ readonly status: number;
13
+ readonly code: string;
14
+ readonly detail?: unknown | undefined;
15
+ readonly headers?: Headers | undefined;
16
+ constructor(status: number, code: string, message: string, detail?: unknown | undefined, headers?: Headers | undefined);
17
+ }
18
+ /** The request never got an answer: DNS, TLS, a reset socket or the timeout. */
19
+ export declare class SendClipperConnectionError extends SendClipperError {
20
+ readonly cause?: unknown | undefined;
21
+ constructor(message: string, cause?: unknown | undefined);
22
+ }
23
+ /** `webhooks.verify()` refused a delivery. `reason` says which check failed. */
24
+ export declare class WebhookVerificationError extends SendClipperError {
25
+ readonly reason: 'missing_header' | 'malformed_header' | 'timestamp_out_of_tolerance' | 'signature_mismatch' | 'invalid_payload';
26
+ constructor(reason: 'missing_header' | 'malformed_header' | 'timestamp_out_of_tolerance' | 'signature_mismatch' | 'invalid_payload', message: string);
27
+ }
28
+ /**
29
+ * The API answers refusals in two shapes: the house envelope
30
+ * `{ error: { code, message, detail? } }` and Nest's default
31
+ * `{ statusCode, message: string | string[], error }` (request validation).
32
+ */
33
+ export declare function toApiError(status: number, text: string, headers?: Headers): SendClipperApiError;
34
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/errors.ts"],"names":[],"mappings":"AAAA,4DAA4D;AAC5D,qBAAa,gBAAiB,SAAQ,KAAK;gBAC7B,OAAO,EAAE,MAAM;CAI5B;AAED;;;;;GAKG;AACH,qBAAa,mBAAoB,SAAQ,gBAAgB;IAErD,QAAQ,CAAC,MAAM,EAAE,MAAM;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM;IAErB,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO;IACzB,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO;gBAJjB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACrB,OAAO,EAAE,MAAM,EACN,MAAM,CAAC,EAAE,OAAO,YAAA,EAChB,OAAO,CAAC,EAAE,OAAO,YAAA;CAK7B;AAED,gFAAgF;AAChF,qBAAa,0BAA2B,SAAQ,gBAAgB;IAG5D,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO;gBADxB,OAAO,EAAE,MAAM,EACN,KAAK,CAAC,EAAE,OAAO,YAAA;CAK3B;AAED,gFAAgF;AAChF,qBAAa,wBAAyB,SAAQ,gBAAgB;IAE1D,QAAQ,CAAC,MAAM,EACX,gBAAgB,GAChB,kBAAkB,GAClB,4BAA4B,GAC5B,oBAAoB,GACpB,iBAAiB;gBALZ,MAAM,EACX,gBAAgB,GAChB,kBAAkB,GAClB,4BAA4B,GAC5B,oBAAoB,GACpB,iBAAiB,EACrB,OAAO,EAAE,MAAM;CAKlB;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,mBAAmB,CAyC/F"}