verifymailapi 0.1.0 → 0.1.1
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 +298 -69
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,89 +1,233 @@
|
|
|
1
1
|
# verifymailapi
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/verifymailapi) [](https://www.npmjs.com/package/verifymailapi) [](./LICENSE)
|
|
4
|
+
|
|
5
|
+
**Official Node / TypeScript SDK for the [VerifyMail API](https://verifymailapi.com)** — detect disposable, throwaway, and abusive emails before they reach your database.
|
|
4
6
|
|
|
5
7
|
```ts
|
|
6
8
|
import { VerifyMail } from "verifymailapi";
|
|
7
9
|
|
|
8
10
|
const vm = new VerifyMail({ apiKey: process.env.VERIFYMAIL_KEY! });
|
|
9
11
|
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
switch (result.verdict.recommendation) {
|
|
13
|
-
case "block":
|
|
14
|
-
throw new Error("This email cannot be used.");
|
|
15
|
-
case "allow_with_flag":
|
|
16
|
-
user.requires_email_verification = true;
|
|
17
|
-
await sendVerificationEmail(user);
|
|
18
|
-
break;
|
|
19
|
-
case "allow":
|
|
20
|
-
// Clean. Proceed.
|
|
21
|
-
break;
|
|
22
|
-
}
|
|
12
|
+
const { verdict } = await vm.check("user@example.com");
|
|
13
|
+
console.log(verdict.recommendation); // "allow" | "allow_with_flag" | "block"
|
|
23
14
|
```
|
|
24
15
|
|
|
16
|
+
---
|
|
17
|
+
|
|
25
18
|
## Install
|
|
26
19
|
|
|
27
20
|
```bash
|
|
28
21
|
npm install verifymailapi
|
|
29
22
|
# or
|
|
30
23
|
pnpm add verifymailapi
|
|
24
|
+
# or
|
|
25
|
+
yarn add verifymailapi
|
|
31
26
|
```
|
|
32
27
|
|
|
33
|
-
|
|
28
|
+
Works in any modern server runtime: **Node 18+, Bun, Deno, Vercel Edge / Functions, Cloudflare Workers, Railway, Fly.io**. Uses native `fetch` — zero dependencies.
|
|
29
|
+
|
|
30
|
+
> **Server-side only.** Never call this from a browser bundle — your API key would leak. Use it inside a server action, API route, or backend service.
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## Production-ready example
|
|
35
|
+
|
|
36
|
+
This is what a real signup handler looks like. Copy-paste it into a Next.js server action, an Express route, or a Hono handler — the shape is the same everywhere.
|
|
37
|
+
|
|
38
|
+
```ts
|
|
39
|
+
import {
|
|
40
|
+
VerifyMail,
|
|
41
|
+
QuotaExceededError,
|
|
42
|
+
RateLimitError,
|
|
43
|
+
VerifyMailError,
|
|
44
|
+
} from "verifymailapi";
|
|
45
|
+
|
|
46
|
+
// Instantiate once per process, reuse per request.
|
|
47
|
+
const vm = new VerifyMail({
|
|
48
|
+
apiKey: process.env.VERIFYMAIL_KEY!,
|
|
49
|
+
riskProfile: "balanced", // strict | balanced | permissive
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
export async function handleSignup(email: string, requestId: string) {
|
|
53
|
+
let result;
|
|
54
|
+
try {
|
|
55
|
+
result = await vm.check(email, {
|
|
56
|
+
// Idempotent — safe to retry a flaky network without double-charging.
|
|
57
|
+
idempotencyKey: `signup:${requestId}`,
|
|
58
|
+
});
|
|
59
|
+
} catch (err) {
|
|
60
|
+
if (err instanceof QuotaExceededError) {
|
|
61
|
+
// Out of credits — don't bounce real customers. Allow with a flag.
|
|
62
|
+
return { ok: true, action: "allow_with_flag", reason: "verifymail_unavailable" };
|
|
63
|
+
}
|
|
64
|
+
if (err instanceof RateLimitError) {
|
|
65
|
+
return { ok: false, error: "Please retry in a moment.", retryAfter: err.retryAfter };
|
|
66
|
+
}
|
|
67
|
+
if (err instanceof VerifyMailError) {
|
|
68
|
+
// Log the error and fail open — losing one signup hurts more than
|
|
69
|
+
// briefly skipping fraud detection.
|
|
70
|
+
console.error("VerifyMail:", err.code, err.message, err.requestId);
|
|
71
|
+
return { ok: true, action: "allow", reason: "verifymail_error" };
|
|
72
|
+
}
|
|
73
|
+
throw err;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
switch (result.verdict.recommendation) {
|
|
77
|
+
case "block":
|
|
78
|
+
// Reject the signup. result.verdict.summary explains *why*.
|
|
79
|
+
return { ok: false, error: result.verdict.summary };
|
|
80
|
+
case "allow_with_flag":
|
|
81
|
+
// Pass through, but route this user through your verification step
|
|
82
|
+
// (email confirmation link, captcha, slower onboarding — whatever
|
|
83
|
+
// your app already has).
|
|
84
|
+
return { ok: true, action: "allow_with_flag" };
|
|
85
|
+
case "allow":
|
|
86
|
+
return { ok: true, action: "allow" };
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
That's the canonical pattern. Everything below is reference detail.
|
|
92
|
+
|
|
93
|
+
---
|
|
34
94
|
|
|
35
95
|
## API
|
|
36
96
|
|
|
37
97
|
### `new VerifyMail(options)`
|
|
38
98
|
|
|
39
|
-
| Option | Default |
|
|
99
|
+
| Option | Type | Default | Notes |
|
|
100
|
+
|---|---|---|---|
|
|
101
|
+
| `apiKey` | `string` | **required** | Your `dc_…` key from the [dashboard](https://verifymailapi.com/dashboard/keys). |
|
|
102
|
+
| `baseUrl` | `string` | `https://api.verifymailapi.com` | Override for staging or self-hosted. |
|
|
103
|
+
| `retries` | `number` | `2` | Retries on `429` / `5xx`. Set `0` to disable. |
|
|
104
|
+
| `timeoutMs` | `number` | `30000` | Per-request timeout. |
|
|
105
|
+
| `riskProfile` | `"strict" \| "balanced" \| "permissive"` | server default | Per-call `riskProfile` overrides this. |
|
|
106
|
+
| `fetch` | `typeof fetch` | `globalThis.fetch` | Inject for tests or non-Node runtimes. |
|
|
107
|
+
|
|
108
|
+
### Methods
|
|
109
|
+
|
|
110
|
+
| Method | What it does |
|
|
111
|
+
|---|---|
|
|
112
|
+
| **`vm.check(email, opts?)`** | Check one email. Charges 1 credit. Returns the full 5-block response. |
|
|
113
|
+
| **`vm.checkDomain(domain, opts?)`** | Check a domain only (no local part). Charges 1 credit. |
|
|
114
|
+
| **`vm.checkBulk(emails[], opts?)`** | 1–100 emails per call. Charges N up front, all-or-nothing. Order preserved. |
|
|
115
|
+
| **`vm.checkBulkStream(emails[], onEvent, opts?)`** | NDJSON streaming version of `checkBulk`. Use for 5k+ batches. |
|
|
116
|
+
| **`vm.checkAsync({ email, webhookUrl, webhookSecret? }, opts?)`** | Returns 202 immediately with a preliminary verdict. The final result is POSTed to your webhook. |
|
|
117
|
+
| **`vm.report({ domain, outcome, notes? })`** | Tell us when a domain turned out to be confirmed throwaway / legitimate / suspected. |
|
|
118
|
+
| **`vm.usage()`** | Current-period totals + credit balance — same shape your dashboard reads. |
|
|
119
|
+
| **`vm.status()`** | Component health (Redis / Postgres / DNS). Always returns 200. |
|
|
120
|
+
|
|
121
|
+
Every method that costs credits accepts `{ idempotencyKey: true }` (auto-generated UUID) or `{ idempotencyKey: "your-string" }`.
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
## Verdicts — what to do with each
|
|
126
|
+
|
|
127
|
+
```ts
|
|
128
|
+
switch (result.verdict.recommendation) {
|
|
129
|
+
case "block": // High confidence: abuse, dead address, or a disposable provider.
|
|
130
|
+
case "allow_with_flag": // Suspicious. Route through your verification step.
|
|
131
|
+
case "allow": // Clean. Proceed normally.
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
**The most important rule:** map `allow_with_flag` to `user.requires_email_verification = true` (or whatever your friction-step is called). Most B2B apps already have email verification — that one line costs zero new code and catches the vast majority of bot signups.
|
|
136
|
+
|
|
137
|
+
If your app doesn't have email verification, add one. It's the cheapest fraud defense in existence and the API is designed around the assumption that you have it.
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
## Errors
|
|
142
|
+
|
|
143
|
+
All HTTP failures become typed errors so you can branch cleanly:
|
|
144
|
+
|
|
145
|
+
```ts
|
|
146
|
+
import {
|
|
147
|
+
VerifyMailError,
|
|
148
|
+
InvalidApiKeyError,
|
|
149
|
+
QuotaExceededError,
|
|
150
|
+
RateLimitError,
|
|
151
|
+
IdempotencyConflictError,
|
|
152
|
+
ValidationError,
|
|
153
|
+
ServiceDegradedError,
|
|
154
|
+
} from "verifymailapi";
|
|
155
|
+
|
|
156
|
+
try {
|
|
157
|
+
await vm.check(email);
|
|
158
|
+
} catch (err) {
|
|
159
|
+
if (err instanceof QuotaExceededError) return showBilling(err.upgradeUrl);
|
|
160
|
+
if (err instanceof RateLimitError) return retryAfter(err.retryAfter); // seconds
|
|
161
|
+
if (err instanceof InvalidApiKeyError) return alertOps("VerifyMail key rotated?");
|
|
162
|
+
if (err instanceof VerifyMailError) return logAndFallback(err);
|
|
163
|
+
throw err;
|
|
164
|
+
}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
Every error carries:
|
|
168
|
+
|
|
169
|
+
| Property | Type | Example |
|
|
40
170
|
|---|---|---|
|
|
41
|
-
| `
|
|
42
|
-
| `
|
|
43
|
-
| `
|
|
44
|
-
| `
|
|
45
|
-
| `
|
|
46
|
-
|
|
171
|
+
| `code` | `string` | `"too_many_requests"`, `"quota_exceeded"`, … |
|
|
172
|
+
| `status` | `number` | HTTP status (`429`, `402`, …) |
|
|
173
|
+
| `requestId` | `string \| undefined` | Pass this when filing support tickets. |
|
|
174
|
+
| `docsUrl` | `string \| undefined` | Direct link to the relevant docs section. |
|
|
175
|
+
| `message` | `string` | Human-readable. |
|
|
176
|
+
|
|
177
|
+
Specific subclasses add specific fields — `RateLimitError.retryAfter`, `QuotaExceededError.upgradeUrl`, etc.
|
|
178
|
+
|
|
179
|
+
---
|
|
47
180
|
|
|
48
|
-
|
|
181
|
+
## Idempotency
|
|
49
182
|
|
|
50
|
-
|
|
183
|
+
`POST` endpoints that charge credits all accept an `Idempotency-Key` header. Replay the same key within **24 hours** and you get the cached response back — no duplicate work, no duplicate charge.
|
|
51
184
|
|
|
52
185
|
```ts
|
|
53
|
-
|
|
54
|
-
|
|
186
|
+
// Auto-generate a UUID v4
|
|
187
|
+
await vm.check(email, { idempotencyKey: true });
|
|
188
|
+
|
|
189
|
+
// Or pass your own (correlate with your own request ID)
|
|
190
|
+
await vm.check(email, { idempotencyKey: `signup:${requestId}` });
|
|
55
191
|
```
|
|
56
192
|
|
|
57
|
-
|
|
193
|
+
Re-using the same key with a different request body throws `IdempotencyConflictError` (HTTP 409). Pick a new key or send the original payload.
|
|
58
194
|
|
|
59
|
-
|
|
195
|
+
---
|
|
60
196
|
|
|
61
|
-
|
|
197
|
+
## Bulk processing
|
|
62
198
|
|
|
63
|
-
|
|
199
|
+
### Small batches (≤ 100 emails)
|
|
64
200
|
|
|
65
201
|
```ts
|
|
66
|
-
const { items, summary } = await vm.checkBulk(["a@x.com", "b@x.com"]);
|
|
202
|
+
const { items, summary } = await vm.checkBulk(["a@x.com", "b@x.com", "c@x.com"]);
|
|
203
|
+
|
|
67
204
|
// items[i] matches emails[i] (order preserved).
|
|
205
|
+
items.forEach((r, i) => console.log(r.meta.domain, "→", r.verdict.recommendation));
|
|
206
|
+
console.log(`charged ${summary.credits_charged} credits in ${summary.elapsed_ms}ms`);
|
|
68
207
|
```
|
|
69
208
|
|
|
70
|
-
###
|
|
209
|
+
### Large batches (5k–100k addresses)
|
|
71
210
|
|
|
72
|
-
|
|
211
|
+
Stream results as each check completes — process rows immediately instead of waiting for the whole batch:
|
|
73
212
|
|
|
74
213
|
```ts
|
|
75
|
-
await vm.checkBulkStream(emails, (
|
|
76
|
-
if ("event" in
|
|
77
|
-
console.log("done — credits remaining:",
|
|
78
|
-
|
|
79
|
-
processRow(e.index, e.result);
|
|
214
|
+
await vm.checkBulkStream(emails, (event) => {
|
|
215
|
+
if ("event" in event) {
|
|
216
|
+
console.log("done — credits remaining:", event.credits_remaining);
|
|
217
|
+
return;
|
|
80
218
|
}
|
|
219
|
+
// event = { index: number, result: CheckResponse }
|
|
220
|
+
await processRow(event.index, event.result);
|
|
81
221
|
});
|
|
82
222
|
```
|
|
83
223
|
|
|
84
|
-
|
|
224
|
+
Results arrive in **finish order**, not input order — correlate via `event.index`.
|
|
225
|
+
|
|
226
|
+
---
|
|
85
227
|
|
|
86
|
-
|
|
228
|
+
## Async deep checks (webhooks)
|
|
229
|
+
|
|
230
|
+
For workflows where the user can wait for an email but not a synchronous SMTP probe:
|
|
87
231
|
|
|
88
232
|
```ts
|
|
89
233
|
const { request_id, preliminary } = await vm.checkAsync({
|
|
@@ -91,9 +235,12 @@ const { request_id, preliminary } = await vm.checkAsync({
|
|
|
91
235
|
webhookUrl: "https://your-app.example/webhooks/verifymail",
|
|
92
236
|
webhookSecret: process.env.VERIFYMAIL_WEBHOOK_SECRET,
|
|
93
237
|
});
|
|
238
|
+
|
|
239
|
+
// `preliminary` is the fast-path verdict you can act on immediately.
|
|
240
|
+
// The deep SMTP probe runs in the background; the final result lands at your webhook.
|
|
94
241
|
```
|
|
95
242
|
|
|
96
|
-
|
|
243
|
+
### Verifying the webhook signature
|
|
97
244
|
|
|
98
245
|
```ts
|
|
99
246
|
import express from "express";
|
|
@@ -101,6 +248,7 @@ import { verifyWebhook } from "verifymailapi";
|
|
|
101
248
|
|
|
102
249
|
app.post(
|
|
103
250
|
"/webhooks/verifymail",
|
|
251
|
+
// IMPORTANT: raw body, not json — verifyWebhook needs the original bytes.
|
|
104
252
|
express.raw({ type: "application/json" }),
|
|
105
253
|
(req, res) => {
|
|
106
254
|
const sig = req.header("X-VerifyMail-Signature") ?? "";
|
|
@@ -108,52 +256,133 @@ app.post(
|
|
|
108
256
|
return res.status(401).send("bad signature");
|
|
109
257
|
}
|
|
110
258
|
const event = JSON.parse(req.body.toString("utf8"));
|
|
111
|
-
// event.result is the final CheckResponse.
|
|
259
|
+
// event.result is the final CheckResponse with the deep verdict.
|
|
260
|
+
await handleFinalVerdict(event);
|
|
112
261
|
res.status(200).end();
|
|
113
262
|
},
|
|
114
263
|
);
|
|
115
264
|
```
|
|
116
265
|
|
|
117
|
-
|
|
266
|
+
Same idea in Hono / Fastify / Next.js Route Handlers — just keep the body bytes raw until after `verifyWebhook` returns true.
|
|
118
267
|
|
|
119
|
-
|
|
268
|
+
---
|
|
120
269
|
|
|
121
|
-
|
|
270
|
+
## Framework recipes
|
|
122
271
|
|
|
123
|
-
|
|
272
|
+
### Next.js — Server Action
|
|
124
273
|
|
|
125
|
-
|
|
274
|
+
```ts
|
|
275
|
+
// app/actions.ts
|
|
276
|
+
"use server";
|
|
126
277
|
|
|
127
|
-
|
|
278
|
+
import { VerifyMail, QuotaExceededError } from "verifymailapi";
|
|
128
279
|
|
|
129
|
-
|
|
280
|
+
const vm = new VerifyMail({ apiKey: process.env.VERIFYMAIL_KEY! });
|
|
281
|
+
|
|
282
|
+
export async function signupAction(_: unknown, formData: FormData) {
|
|
283
|
+
const email = String(formData.get("email"));
|
|
284
|
+
try {
|
|
285
|
+
const r = await vm.check(email);
|
|
286
|
+
if (r.verdict.recommendation === "block") {
|
|
287
|
+
return { ok: false, error: r.verdict.summary };
|
|
288
|
+
}
|
|
289
|
+
await db.user.create({
|
|
290
|
+
data: { email, requires_verification: r.verdict.recommendation === "allow_with_flag" },
|
|
291
|
+
});
|
|
292
|
+
return { ok: true };
|
|
293
|
+
} catch (err) {
|
|
294
|
+
if (err instanceof QuotaExceededError) return { ok: true }; // fail open
|
|
295
|
+
throw err;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
### Express — middleware
|
|
130
301
|
|
|
131
302
|
```ts
|
|
132
|
-
import {
|
|
133
|
-
VerifyMailError,
|
|
134
|
-
InvalidApiKeyError,
|
|
135
|
-
QuotaExceededError,
|
|
136
|
-
RateLimitError,
|
|
137
|
-
IdempotencyConflictError,
|
|
138
|
-
ValidationError,
|
|
139
|
-
ServiceDegradedError,
|
|
140
|
-
} from "verifymailapi";
|
|
303
|
+
import { VerifyMail } from "verifymailapi";
|
|
141
304
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
if (
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
305
|
+
const vm = new VerifyMail({ apiKey: process.env.VERIFYMAIL_KEY! });
|
|
306
|
+
|
|
307
|
+
app.post("/signup", async (req, res) => {
|
|
308
|
+
const r = await vm.check(req.body.email);
|
|
309
|
+
if (r.verdict.recommendation === "block") {
|
|
310
|
+
return res.status(422).json({ error: r.verdict.summary });
|
|
311
|
+
}
|
|
312
|
+
const user = await createUser({
|
|
313
|
+
...req.body,
|
|
314
|
+
requires_verification: r.verdict.recommendation === "allow_with_flag",
|
|
315
|
+
});
|
|
316
|
+
res.json(user);
|
|
317
|
+
});
|
|
150
318
|
```
|
|
151
319
|
|
|
152
|
-
|
|
320
|
+
### Cloudflare Workers / Edge
|
|
153
321
|
|
|
154
|
-
|
|
322
|
+
```ts
|
|
323
|
+
import { VerifyMail } from "verifymailapi";
|
|
324
|
+
|
|
325
|
+
export default {
|
|
326
|
+
async fetch(req: Request, env: Env) {
|
|
327
|
+
const vm = new VerifyMail({ apiKey: env.VERIFYMAIL_KEY });
|
|
328
|
+
const { email } = await req.json();
|
|
329
|
+
const r = await vm.check(email);
|
|
330
|
+
return Response.json(r);
|
|
331
|
+
},
|
|
332
|
+
};
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
---
|
|
336
|
+
|
|
337
|
+
## Rate limits
|
|
338
|
+
|
|
339
|
+
The API enforces **600 requests / minute per key** by default (configurable for paying customers). The SDK automatically:
|
|
340
|
+
|
|
341
|
+
- Reads `Retry-After` on `429` responses.
|
|
342
|
+
- Backs off and retries up to `retries` times (default `2`).
|
|
343
|
+
- Surfaces the limit info on the thrown `RateLimitError` if all retries fail.
|
|
344
|
+
|
|
345
|
+
Look at the response headers `X-RateLimit-Remaining` / `X-RateLimit-Reset` if you want to throttle preemptively in your own queue.
|
|
346
|
+
|
|
347
|
+
---
|
|
348
|
+
|
|
349
|
+
## TypeScript
|
|
350
|
+
|
|
351
|
+
The package ships full `.d.ts` types. Every response field is typed; every verdict / risk level / signal direction is a literal union you can `switch` over exhaustively.
|
|
352
|
+
|
|
353
|
+
```ts
|
|
354
|
+
import type {
|
|
355
|
+
CheckResponse,
|
|
356
|
+
Recommendation,
|
|
357
|
+
RiskLevel,
|
|
358
|
+
Signal,
|
|
359
|
+
BulkCheckResponse,
|
|
360
|
+
CheckCompletedEvent,
|
|
361
|
+
} from "verifymailapi";
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
Strict mode + `noUncheckedIndexedAccess` clean. Works with `"moduleResolution": "Bundler"`, `"node16"`, and classic `"node"`.
|
|
365
|
+
|
|
366
|
+
---
|
|
367
|
+
|
|
368
|
+
## Configuration via environment
|
|
369
|
+
|
|
370
|
+
This SDK doesn't read env vars itself — you pass them. Recommended names:
|
|
371
|
+
|
|
372
|
+
| Var | Purpose |
|
|
373
|
+
|---|---|
|
|
374
|
+
| `VERIFYMAIL_KEY` | Your `dc_…` API key |
|
|
375
|
+
| `VERIFYMAIL_WEBHOOK_SECRET` | Optional shared secret for `vm.checkAsync(...)` |
|
|
376
|
+
| `VERIFYMAIL_API_URL` | Optional override of `baseUrl` for staging |
|
|
377
|
+
|
|
378
|
+
---
|
|
379
|
+
|
|
380
|
+
## Links
|
|
155
381
|
|
|
156
|
-
|
|
382
|
+
- **Full API docs:** https://verifymailapi.com/docs
|
|
383
|
+
- **Dashboard / API keys:** https://verifymailapi.com/dashboard/keys
|
|
384
|
+
- **Issues / discussions:** https://github.com/jt1402/verifymail-js
|
|
385
|
+
- **Pricing:** https://verifymailapi.com/pricing
|
|
157
386
|
|
|
158
387
|
## License
|
|
159
388
|
|
package/dist/index.cjs
CHANGED
|
@@ -159,7 +159,7 @@ var HttpClient = class {
|
|
|
159
159
|
const h = new Headers(opts.headers);
|
|
160
160
|
h.set("X-API-Key", this.apiKey);
|
|
161
161
|
h.set("Accept", "application/json");
|
|
162
|
-
h.set("User-Agent", "verifymailapi-js/0.1.
|
|
162
|
+
h.set("User-Agent", "verifymailapi-js/0.1.1");
|
|
163
163
|
if (opts.body !== void 0) h.set("Content-Type", "application/json");
|
|
164
164
|
const profile = opts.riskProfile ?? this.defaultRiskProfile;
|
|
165
165
|
if (profile) h.set("X-Risk-Profile", profile);
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/errors.ts","../src/client.ts","../src/webhooks.ts"],"sourcesContent":["/**\n * Official SDK for the VerifyMail API.\n *\n * import { VerifyMail } from \"verifymailapi\";\n * const vm = new VerifyMail({ apiKey: process.env.VERIFYMAIL_KEY! });\n * const r = await vm.check(\"user@example.com\");\n * if (r.verdict.recommendation === \"block\") { ... }\n *\n * Docs: https://verifymailapi.com/docs\n */\n\nimport { HttpClient, type ClientOptions, type RequestOptions } from \"./client.js\";\nimport type {\n AsyncCheckResponse,\n BulkCheckResponse,\n BulkStreamEvent,\n CheckResponse,\n ReportRequest,\n ReportResponse,\n StatusResponse,\n UsageMeResponse,\n} from \"./types.js\";\n\nexport * from \"./types.js\";\nexport * from \"./errors.js\";\nexport { verifyWebhook } from \"./webhooks.js\";\n\nexport interface CheckOptions {\n /** Override the SDK-wide risk profile for this call. */\n riskProfile?: \"strict\" | \"balanced\" | \"permissive\";\n /** Make the call idempotent. Pass `true` to auto-generate a UUID, or a fixed string. */\n idempotencyKey?: string | boolean;\n}\n\nexport interface AsyncCheckArgs {\n email: string;\n webhookUrl: string;\n /** Optional HMAC-SHA256 key used to sign the final webhook payload. */\n webhookSecret?: string;\n}\n\nexport class VerifyMail {\n private readonly http: HttpClient;\n\n constructor(opts: ClientOptions) {\n this.http = new HttpClient(opts);\n }\n\n /** Check a single email. Charges 1 credit. */\n check(email: string, opts: CheckOptions = {}): Promise<CheckResponse> {\n return this.http.json<CheckResponse>({\n path: \"/v1/check\",\n body: { email },\n idempotencyKey: opts.idempotencyKey,\n riskProfile: opts.riskProfile,\n });\n }\n\n /** Check a domain only (no local part). Charges 1 credit. */\n checkDomain(domain: string, opts: CheckOptions = {}): Promise<CheckResponse> {\n return this.http.json<CheckResponse>({\n path: \"/v1/check/domain\",\n body: { domain },\n idempotencyKey: opts.idempotencyKey,\n riskProfile: opts.riskProfile,\n });\n }\n\n /** Bulk check 1–100 emails. Charges N credits up front (all-or-nothing). */\n checkBulk(emails: string[], opts: CheckOptions = {}): Promise<BulkCheckResponse> {\n if (emails.length === 0 || emails.length > 100) {\n throw new RangeError(\"checkBulk requires 1–100 emails.\");\n }\n return this.http.json<BulkCheckResponse>({\n path: \"/v1/check/bulk\",\n body: { emails },\n idempotencyKey: opts.idempotencyKey,\n riskProfile: opts.riskProfile,\n });\n }\n\n /**\n * Stream bulk-check results as each row completes. Calls `onEvent` once\n * per result (in finish-order) plus once with a final `{event: \"summary\"}`.\n *\n * await vm.checkBulkStream(emails, (e) => {\n * if (\"event\" in e) console.log(\"done\", e);\n * else processRow(e.index, e.result);\n * });\n */\n async checkBulkStream(\n emails: string[],\n onEvent: (e: BulkStreamEvent) => void | Promise<void>,\n opts: Omit<CheckOptions, \"idempotencyKey\"> = {},\n ): Promise<void> {\n if (emails.length === 0) throw new RangeError(\"checkBulkStream needs at least one email.\");\n\n const req: RequestOptions = {\n path: \"/v1/check/bulk/stream\",\n body: { emails },\n riskProfile: opts.riskProfile,\n // Streaming requests aren't safe to auto-retry — would re-charge credits\n // and re-stream rows. Customer must retry explicitly.\n noRetry: true,\n };\n const res = await this.http.raw(req);\n if (!res.body) throw new Error(\"Streaming response had no body.\");\n\n const reader = res.body.getReader();\n const decoder = new TextDecoder();\n let buf = \"\";\n while (true) {\n const { value, done } = await reader.read();\n if (done) break;\n buf += decoder.decode(value, { stream: true });\n let nl: number;\n // Drain complete lines; keep the partial tail in buf for the next chunk.\n while ((nl = buf.indexOf(\"\\n\")) !== -1) {\n const line = buf.slice(0, nl).trim();\n buf = buf.slice(nl + 1);\n if (!line) continue;\n try {\n await onEvent(JSON.parse(line) as BulkStreamEvent);\n } catch (err) {\n throw new Error(`Failed to parse stream line: ${line}`);\n }\n }\n }\n // Final flush in case the server didn't terminate with newline.\n const tail = buf.trim();\n if (tail) await onEvent(JSON.parse(tail) as BulkStreamEvent);\n }\n\n /**\n * Async deep check. Returns 202 immediately with a preliminary verdict;\n * VerifyMail POSTs the final result to your webhook URL once the deep\n * SMTP probe completes. Verify the signature with `verifyWebhook()` in\n * your handler before trusting the payload.\n */\n checkAsync(args: AsyncCheckArgs, opts: CheckOptions = {}): Promise<AsyncCheckResponse> {\n return this.http.json<AsyncCheckResponse>({\n path: \"/v1/check/async\",\n body: {\n email: args.email,\n webhook_url: args.webhookUrl,\n webhook_secret: args.webhookSecret,\n },\n idempotencyKey: opts.idempotencyKey,\n riskProfile: opts.riskProfile,\n });\n }\n\n /** File a /v1/report for a domain outcome (feedback loop). */\n report(req: ReportRequest): Promise<ReportResponse> {\n return this.http.json<ReportResponse>({\n path: \"/v1/report\",\n body: req,\n });\n }\n\n /** Programmatic equivalent of the dashboard's Usage summary. */\n usage(): Promise<UsageMeResponse> {\n return this.http.json<UsageMeResponse>({\n path: \"/v1/usage/me\",\n method: \"GET\",\n });\n }\n\n /** Component health (Redis / Postgres / DNS). Always returns 200. */\n status(): Promise<StatusResponse> {\n return this.http.json<StatusResponse>({\n path: \"/v1/status\",\n method: \"GET\",\n });\n }\n}\n\nexport default VerifyMail;\n","/**\n * Error class hierarchy. All HTTP errors from the API are translated into one\n * of these — customers can `instanceof` them to branch cleanly:\n *\n * try { await vm.check(email) }\n * catch (e) {\n * if (e instanceof QuotaExceededError) return showBilling();\n * if (e instanceof RateLimitError) return retryLater(e.retryAfter);\n * if (e instanceof VerifyMailError) return logAndShowGeneric(e);\n * throw e;\n * }\n */\n\nexport interface ErrorBody {\n code: string;\n http_status: number;\n message: string;\n request_id?: string;\n docs_url?: string;\n // Rate-limit-specific\n limit?: number;\n reset_at?: string;\n // Quota-specific\n upgrade_url?: string;\n}\n\nexport class VerifyMailError extends Error {\n readonly code: string;\n readonly status: number;\n readonly requestId: string | undefined;\n readonly docsUrl: string | undefined;\n readonly body: ErrorBody | undefined;\n\n constructor(message: string, opts: {\n code?: string;\n status?: number;\n requestId?: string;\n docsUrl?: string;\n body?: ErrorBody;\n } = {}) {\n super(message);\n this.name = \"VerifyMailError\";\n this.code = opts.code ?? \"verifymail_error\";\n this.status = opts.status ?? 0;\n this.requestId = opts.requestId;\n this.docsUrl = opts.docsUrl;\n this.body = opts.body;\n }\n}\n\nexport class InvalidApiKeyError extends VerifyMailError {\n constructor(opts: NonNullable<ConstructorParameters<typeof VerifyMailError>[1]>) {\n super(\"API key is missing or invalid.\", opts);\n this.name = \"InvalidApiKeyError\";\n }\n}\n\nexport class QuotaExceededError extends VerifyMailError {\n readonly upgradeUrl: string | undefined;\n constructor(opts: NonNullable<ConstructorParameters<typeof VerifyMailError>[1]>) {\n super(\"Out of credits. Buy a bundle to keep going.\", opts);\n this.name = \"QuotaExceededError\";\n this.upgradeUrl = opts.body?.upgrade_url;\n }\n}\n\nexport class RateLimitError extends VerifyMailError {\n readonly retryAfter: number;\n readonly limit: number | undefined;\n readonly resetAt: string | undefined;\n\n constructor(opts: NonNullable<ConstructorParameters<typeof VerifyMailError>[1]> & {\n retryAfter: number;\n }) {\n super(`Rate limit hit; retry after ${opts.retryAfter}s.`, opts);\n this.name = \"RateLimitError\";\n this.retryAfter = opts.retryAfter;\n this.limit = opts.body?.limit;\n this.resetAt = opts.body?.reset_at;\n }\n}\n\nexport class IdempotencyConflictError extends VerifyMailError {\n constructor(opts: NonNullable<ConstructorParameters<typeof VerifyMailError>[1]>) {\n super(\n \"Idempotency-Key was reused with a different request body. Use a new key or resend the original payload.\",\n opts,\n );\n this.name = \"IdempotencyConflictError\";\n }\n}\n\nexport class ValidationError extends VerifyMailError {\n constructor(message: string, opts: NonNullable<ConstructorParameters<typeof VerifyMailError>[1]>) {\n super(message, opts);\n this.name = \"ValidationError\";\n }\n}\n\nexport class ServiceDegradedError extends VerifyMailError {\n constructor(opts: NonNullable<ConstructorParameters<typeof VerifyMailError>[1]>) {\n super(\"A component is degraded; retry shortly.\", opts);\n this.name = \"ServiceDegradedError\";\n }\n}\n\n/**\n * Map an HTTP response to the right error class. The backend's error envelope\n * is `{ error: { code, http_status, message, ... } }`.\n */\nexport function errorFromResponse(\n status: number,\n body: { error?: ErrorBody } | unknown,\n retryAfterHeader?: string | null,\n): VerifyMailError {\n const envelope =\n body && typeof body === \"object\" && \"error\" in body\n ? (body as { error?: ErrorBody }).error\n : undefined;\n const code = envelope?.code ?? \"verifymail_error\";\n const opts = {\n code,\n status,\n requestId: envelope?.request_id,\n docsUrl: envelope?.docs_url,\n body: envelope,\n };\n\n if (status === 401) return new InvalidApiKeyError(opts);\n if (status === 402) return new QuotaExceededError(opts);\n if (status === 409 && code === \"invalid_idempotency_key\")\n return new IdempotencyConflictError(opts);\n if (status === 422) return new ValidationError(envelope?.message ?? \"Validation error.\", opts);\n if (status === 429) {\n const retryAfter = Number(retryAfterHeader ?? envelope?.limit ?? 1);\n return new RateLimitError({ ...opts, retryAfter: Number.isFinite(retryAfter) ? retryAfter : 1 });\n }\n if (status === 503 || status === 504) return new ServiceDegradedError(opts);\n return new VerifyMailError(envelope?.message ?? `HTTP ${status}`, opts);\n}\n","import { errorFromResponse, RateLimitError, ServiceDegradedError, VerifyMailError } from \"./errors.js\";\n\nexport interface ClientOptions {\n apiKey: string;\n /** Defaults to https://api.verifymailapi.com */\n baseUrl?: string;\n /** Max retry attempts on retryable failures (429, 5xx). Default 2 (so up to 3 total tries). */\n retries?: number;\n /** Per-request timeout in ms. Default 30000. */\n timeoutMs?: number;\n /** Override fetch — useful for tests or non-Node runtimes. */\n fetch?: typeof fetch;\n /** Default risk profile sent as X-Risk-Profile on every call (overridable per request). */\n riskProfile?: \"strict\" | \"balanced\" | \"permissive\";\n}\n\nexport interface RequestOptions {\n method?: \"GET\" | \"POST\";\n path: string;\n query?: Record<string, string | number | undefined>;\n body?: unknown;\n headers?: Record<string, string>;\n /** Provide an Idempotency-Key for POSTs. Auto-generated UUID if `true`. */\n idempotencyKey?: string | boolean;\n /** Override the default risk profile for this call. */\n riskProfile?: \"strict\" | \"balanced\" | \"permissive\";\n /** Skip retry on transient errors for this call. */\n noRetry?: boolean;\n /** Override timeout for this call. */\n timeoutMs?: number;\n}\n\nconst RETRYABLE_STATUS = new Set([408, 429, 500, 502, 503, 504]);\n\nfunction uuidv4(): string {\n // Node 19+ has globalThis.crypto.randomUUID. Polyfill for older Node.\n const c = globalThis.crypto;\n if (c?.randomUUID) return c.randomUUID();\n const buf = new Uint8Array(16);\n for (let i = 0; i < 16; i++) buf[i] = Math.floor(Math.random() * 256);\n buf[6] = (buf[6]! & 0x0f) | 0x40;\n buf[8] = (buf[8]! & 0x3f) | 0x80;\n const hex = Array.from(buf, (b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;\n}\n\nexport class HttpClient {\n private readonly apiKey: string;\n private readonly baseUrl: string;\n private readonly retries: number;\n private readonly timeoutMs: number;\n private readonly fetchImpl: typeof fetch;\n private readonly defaultRiskProfile: string | undefined;\n\n constructor(opts: ClientOptions) {\n if (!opts.apiKey) throw new VerifyMailError(\"apiKey is required.\");\n this.apiKey = opts.apiKey;\n this.baseUrl = (opts.baseUrl ?? \"https://api.verifymailapi.com\").replace(/\\/+$/, \"\");\n this.retries = opts.retries ?? 2;\n this.timeoutMs = opts.timeoutMs ?? 30_000;\n this.fetchImpl = opts.fetch ?? globalThis.fetch;\n this.defaultRiskProfile = opts.riskProfile;\n if (!this.fetchImpl) {\n throw new VerifyMailError(\n \"No fetch available. Pass `fetch` in the options, or use Node 18+.\",\n );\n }\n }\n\n /** Build the headers shared by every request. */\n private buildHeaders(opts: RequestOptions): Headers {\n const h = new Headers(opts.headers);\n h.set(\"X-API-Key\", this.apiKey);\n h.set(\"Accept\", \"application/json\");\n h.set(\"User-Agent\", \"verifymailapi-js/0.1.0\");\n if (opts.body !== undefined) h.set(\"Content-Type\", \"application/json\");\n\n const profile = opts.riskProfile ?? this.defaultRiskProfile;\n if (profile) h.set(\"X-Risk-Profile\", profile);\n\n if (opts.idempotencyKey) {\n h.set(\n \"Idempotency-Key\",\n typeof opts.idempotencyKey === \"string\" ? opts.idempotencyKey : uuidv4(),\n );\n }\n return h;\n }\n\n private buildUrl(path: string, query?: RequestOptions[\"query\"]): string {\n const url = new URL(`${this.baseUrl}${path}`);\n if (query) {\n for (const [k, v] of Object.entries(query)) {\n if (v !== undefined) url.searchParams.set(k, String(v));\n }\n }\n return url.toString();\n }\n\n /** Send and decode a JSON request. Retries on 429 / 5xx up to `retries` times. */\n async json<T>(opts: RequestOptions): Promise<T> {\n const raw = await this.raw(opts);\n if (raw.status === 204) return undefined as T;\n const text = await raw.text();\n try {\n return JSON.parse(text) as T;\n } catch {\n throw new VerifyMailError(`Non-JSON response from ${opts.path}.`, {\n status: raw.status,\n });\n }\n }\n\n /**\n * Send a request and return the raw Response. Handles retries + error mapping.\n * Used internally; exposed for the streaming bulk method to consume the body.\n */\n async raw(opts: RequestOptions): Promise<Response> {\n const url = this.buildUrl(opts.path, opts.query);\n const headers = this.buildHeaders(opts);\n const body = opts.body !== undefined ? JSON.stringify(opts.body) : undefined;\n const method = opts.method ?? (body ? \"POST\" : \"GET\");\n const maxTries = opts.noRetry ? 1 : this.retries + 1;\n const timeoutMs = opts.timeoutMs ?? this.timeoutMs;\n\n let lastError: VerifyMailError | undefined;\n for (let attempt = 0; attempt < maxTries; attempt++) {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n let res: Response;\n try {\n res = await this.fetchImpl(url, {\n method,\n headers,\n body,\n signal: controller.signal,\n });\n } catch (err) {\n clearTimeout(timer);\n // Network-level failure — retry if attempts remain.\n lastError = new VerifyMailError(\n err instanceof Error ? err.message : \"Network error\",\n { code: \"network_error\" },\n );\n if (attempt < maxTries - 1) {\n await sleep(backoffMs(attempt));\n continue;\n }\n throw lastError;\n }\n clearTimeout(timer);\n\n if (res.ok) return res;\n\n // Parse error body (best-effort) so we can build a typed error.\n let parsed: unknown = undefined;\n try {\n parsed = await res.clone().json();\n } catch {\n /* ignore non-JSON error bodies */\n }\n const err = errorFromResponse(res.status, parsed, res.headers.get(\"Retry-After\"));\n\n const isRetryable =\n !opts.noRetry &&\n RETRYABLE_STATUS.has(res.status) &&\n attempt < maxTries - 1;\n\n if (isRetryable) {\n const wait =\n err instanceof RateLimitError\n ? err.retryAfter * 1000\n : err instanceof ServiceDegradedError\n ? backoffMs(attempt)\n : backoffMs(attempt);\n await sleep(wait);\n lastError = err;\n continue;\n }\n\n throw err;\n }\n\n throw lastError ?? new VerifyMailError(\"Request failed after retries.\");\n }\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((r) => setTimeout(r, ms));\n}\n\nfunction backoffMs(attempt: number): number {\n // 250ms, 750ms, 2.25s — typical exponential with a small jitter floor.\n return Math.min(250 * Math.pow(3, attempt), 10_000);\n}\n","import { createHmac, timingSafeEqual } from \"node:crypto\";\n\n/**\n * Verify an incoming webhook signature from a `/v1/check/async` completion.\n *\n * const ok = verifyWebhook(rawBody, req.header(\"X-VerifyMail-Signature\"), secret);\n * if (!ok) return res.status(401).end();\n *\n * Pass the *raw* request body bytes — not the parsed JSON. In Express:\n * app.post(\"/webhook\", express.raw({ type: \"application/json\" }), handler)\n */\nexport function verifyWebhook(\n rawBody: Buffer | Uint8Array | string,\n signatureHeader: string | null | undefined,\n secret: string,\n): boolean {\n if (!signatureHeader) return false;\n const body =\n typeof rawBody === \"string\" ? Buffer.from(rawBody, \"utf8\") : Buffer.from(rawBody);\n const expected =\n \"sha256=\" + createHmac(\"sha256\", secret).update(body).digest(\"hex\");\n const a = Buffer.from(signatureHeader);\n const b = Buffer.from(expected);\n return a.length === b.length && timingSafeEqual(a, b);\n}\n"],"mappings":"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","names":["err"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/errors.ts","../src/client.ts","../src/webhooks.ts"],"sourcesContent":["/**\n * Official SDK for the VerifyMail API.\n *\n * import { VerifyMail } from \"verifymailapi\";\n * const vm = new VerifyMail({ apiKey: process.env.VERIFYMAIL_KEY! });\n * const r = await vm.check(\"user@example.com\");\n * if (r.verdict.recommendation === \"block\") { ... }\n *\n * Docs: https://verifymailapi.com/docs\n */\n\nimport { HttpClient, type ClientOptions, type RequestOptions } from \"./client.js\";\nimport type {\n AsyncCheckResponse,\n BulkCheckResponse,\n BulkStreamEvent,\n CheckResponse,\n ReportRequest,\n ReportResponse,\n StatusResponse,\n UsageMeResponse,\n} from \"./types.js\";\n\nexport * from \"./types.js\";\nexport * from \"./errors.js\";\nexport { verifyWebhook } from \"./webhooks.js\";\n\nexport interface CheckOptions {\n /** Override the SDK-wide risk profile for this call. */\n riskProfile?: \"strict\" | \"balanced\" | \"permissive\";\n /** Make the call idempotent. Pass `true` to auto-generate a UUID, or a fixed string. */\n idempotencyKey?: string | boolean;\n}\n\nexport interface AsyncCheckArgs {\n email: string;\n webhookUrl: string;\n /** Optional HMAC-SHA256 key used to sign the final webhook payload. */\n webhookSecret?: string;\n}\n\nexport class VerifyMail {\n private readonly http: HttpClient;\n\n constructor(opts: ClientOptions) {\n this.http = new HttpClient(opts);\n }\n\n /** Check a single email. Charges 1 credit. */\n check(email: string, opts: CheckOptions = {}): Promise<CheckResponse> {\n return this.http.json<CheckResponse>({\n path: \"/v1/check\",\n body: { email },\n idempotencyKey: opts.idempotencyKey,\n riskProfile: opts.riskProfile,\n });\n }\n\n /** Check a domain only (no local part). Charges 1 credit. */\n checkDomain(domain: string, opts: CheckOptions = {}): Promise<CheckResponse> {\n return this.http.json<CheckResponse>({\n path: \"/v1/check/domain\",\n body: { domain },\n idempotencyKey: opts.idempotencyKey,\n riskProfile: opts.riskProfile,\n });\n }\n\n /** Bulk check 1–100 emails. Charges N credits up front (all-or-nothing). */\n checkBulk(emails: string[], opts: CheckOptions = {}): Promise<BulkCheckResponse> {\n if (emails.length === 0 || emails.length > 100) {\n throw new RangeError(\"checkBulk requires 1–100 emails.\");\n }\n return this.http.json<BulkCheckResponse>({\n path: \"/v1/check/bulk\",\n body: { emails },\n idempotencyKey: opts.idempotencyKey,\n riskProfile: opts.riskProfile,\n });\n }\n\n /**\n * Stream bulk-check results as each row completes. Calls `onEvent` once\n * per result (in finish-order) plus once with a final `{event: \"summary\"}`.\n *\n * await vm.checkBulkStream(emails, (e) => {\n * if (\"event\" in e) console.log(\"done\", e);\n * else processRow(e.index, e.result);\n * });\n */\n async checkBulkStream(\n emails: string[],\n onEvent: (e: BulkStreamEvent) => void | Promise<void>,\n opts: Omit<CheckOptions, \"idempotencyKey\"> = {},\n ): Promise<void> {\n if (emails.length === 0) throw new RangeError(\"checkBulkStream needs at least one email.\");\n\n const req: RequestOptions = {\n path: \"/v1/check/bulk/stream\",\n body: { emails },\n riskProfile: opts.riskProfile,\n // Streaming requests aren't safe to auto-retry — would re-charge credits\n // and re-stream rows. Customer must retry explicitly.\n noRetry: true,\n };\n const res = await this.http.raw(req);\n if (!res.body) throw new Error(\"Streaming response had no body.\");\n\n const reader = res.body.getReader();\n const decoder = new TextDecoder();\n let buf = \"\";\n while (true) {\n const { value, done } = await reader.read();\n if (done) break;\n buf += decoder.decode(value, { stream: true });\n let nl: number;\n // Drain complete lines; keep the partial tail in buf for the next chunk.\n while ((nl = buf.indexOf(\"\\n\")) !== -1) {\n const line = buf.slice(0, nl).trim();\n buf = buf.slice(nl + 1);\n if (!line) continue;\n try {\n await onEvent(JSON.parse(line) as BulkStreamEvent);\n } catch (err) {\n throw new Error(`Failed to parse stream line: ${line}`);\n }\n }\n }\n // Final flush in case the server didn't terminate with newline.\n const tail = buf.trim();\n if (tail) await onEvent(JSON.parse(tail) as BulkStreamEvent);\n }\n\n /**\n * Async deep check. Returns 202 immediately with a preliminary verdict;\n * VerifyMail POSTs the final result to your webhook URL once the deep\n * SMTP probe completes. Verify the signature with `verifyWebhook()` in\n * your handler before trusting the payload.\n */\n checkAsync(args: AsyncCheckArgs, opts: CheckOptions = {}): Promise<AsyncCheckResponse> {\n return this.http.json<AsyncCheckResponse>({\n path: \"/v1/check/async\",\n body: {\n email: args.email,\n webhook_url: args.webhookUrl,\n webhook_secret: args.webhookSecret,\n },\n idempotencyKey: opts.idempotencyKey,\n riskProfile: opts.riskProfile,\n });\n }\n\n /** File a /v1/report for a domain outcome (feedback loop). */\n report(req: ReportRequest): Promise<ReportResponse> {\n return this.http.json<ReportResponse>({\n path: \"/v1/report\",\n body: req,\n });\n }\n\n /** Programmatic equivalent of the dashboard's Usage summary. */\n usage(): Promise<UsageMeResponse> {\n return this.http.json<UsageMeResponse>({\n path: \"/v1/usage/me\",\n method: \"GET\",\n });\n }\n\n /** Component health (Redis / Postgres / DNS). Always returns 200. */\n status(): Promise<StatusResponse> {\n return this.http.json<StatusResponse>({\n path: \"/v1/status\",\n method: \"GET\",\n });\n }\n}\n\nexport default VerifyMail;\n","/**\n * Error class hierarchy. All HTTP errors from the API are translated into one\n * of these — customers can `instanceof` them to branch cleanly:\n *\n * try { await vm.check(email) }\n * catch (e) {\n * if (e instanceof QuotaExceededError) return showBilling();\n * if (e instanceof RateLimitError) return retryLater(e.retryAfter);\n * if (e instanceof VerifyMailError) return logAndShowGeneric(e);\n * throw e;\n * }\n */\n\nexport interface ErrorBody {\n code: string;\n http_status: number;\n message: string;\n request_id?: string;\n docs_url?: string;\n // Rate-limit-specific\n limit?: number;\n reset_at?: string;\n // Quota-specific\n upgrade_url?: string;\n}\n\nexport class VerifyMailError extends Error {\n readonly code: string;\n readonly status: number;\n readonly requestId: string | undefined;\n readonly docsUrl: string | undefined;\n readonly body: ErrorBody | undefined;\n\n constructor(message: string, opts: {\n code?: string;\n status?: number;\n requestId?: string;\n docsUrl?: string;\n body?: ErrorBody;\n } = {}) {\n super(message);\n this.name = \"VerifyMailError\";\n this.code = opts.code ?? \"verifymail_error\";\n this.status = opts.status ?? 0;\n this.requestId = opts.requestId;\n this.docsUrl = opts.docsUrl;\n this.body = opts.body;\n }\n}\n\nexport class InvalidApiKeyError extends VerifyMailError {\n constructor(opts: NonNullable<ConstructorParameters<typeof VerifyMailError>[1]>) {\n super(\"API key is missing or invalid.\", opts);\n this.name = \"InvalidApiKeyError\";\n }\n}\n\nexport class QuotaExceededError extends VerifyMailError {\n readonly upgradeUrl: string | undefined;\n constructor(opts: NonNullable<ConstructorParameters<typeof VerifyMailError>[1]>) {\n super(\"Out of credits. Buy a bundle to keep going.\", opts);\n this.name = \"QuotaExceededError\";\n this.upgradeUrl = opts.body?.upgrade_url;\n }\n}\n\nexport class RateLimitError extends VerifyMailError {\n readonly retryAfter: number;\n readonly limit: number | undefined;\n readonly resetAt: string | undefined;\n\n constructor(opts: NonNullable<ConstructorParameters<typeof VerifyMailError>[1]> & {\n retryAfter: number;\n }) {\n super(`Rate limit hit; retry after ${opts.retryAfter}s.`, opts);\n this.name = \"RateLimitError\";\n this.retryAfter = opts.retryAfter;\n this.limit = opts.body?.limit;\n this.resetAt = opts.body?.reset_at;\n }\n}\n\nexport class IdempotencyConflictError extends VerifyMailError {\n constructor(opts: NonNullable<ConstructorParameters<typeof VerifyMailError>[1]>) {\n super(\n \"Idempotency-Key was reused with a different request body. Use a new key or resend the original payload.\",\n opts,\n );\n this.name = \"IdempotencyConflictError\";\n }\n}\n\nexport class ValidationError extends VerifyMailError {\n constructor(message: string, opts: NonNullable<ConstructorParameters<typeof VerifyMailError>[1]>) {\n super(message, opts);\n this.name = \"ValidationError\";\n }\n}\n\nexport class ServiceDegradedError extends VerifyMailError {\n constructor(opts: NonNullable<ConstructorParameters<typeof VerifyMailError>[1]>) {\n super(\"A component is degraded; retry shortly.\", opts);\n this.name = \"ServiceDegradedError\";\n }\n}\n\n/**\n * Map an HTTP response to the right error class. The backend's error envelope\n * is `{ error: { code, http_status, message, ... } }`.\n */\nexport function errorFromResponse(\n status: number,\n body: { error?: ErrorBody } | unknown,\n retryAfterHeader?: string | null,\n): VerifyMailError {\n const envelope =\n body && typeof body === \"object\" && \"error\" in body\n ? (body as { error?: ErrorBody }).error\n : undefined;\n const code = envelope?.code ?? \"verifymail_error\";\n const opts = {\n code,\n status,\n requestId: envelope?.request_id,\n docsUrl: envelope?.docs_url,\n body: envelope,\n };\n\n if (status === 401) return new InvalidApiKeyError(opts);\n if (status === 402) return new QuotaExceededError(opts);\n if (status === 409 && code === \"invalid_idempotency_key\")\n return new IdempotencyConflictError(opts);\n if (status === 422) return new ValidationError(envelope?.message ?? \"Validation error.\", opts);\n if (status === 429) {\n const retryAfter = Number(retryAfterHeader ?? envelope?.limit ?? 1);\n return new RateLimitError({ ...opts, retryAfter: Number.isFinite(retryAfter) ? retryAfter : 1 });\n }\n if (status === 503 || status === 504) return new ServiceDegradedError(opts);\n return new VerifyMailError(envelope?.message ?? `HTTP ${status}`, opts);\n}\n","import { errorFromResponse, RateLimitError, ServiceDegradedError, VerifyMailError } from \"./errors.js\";\n\nexport interface ClientOptions {\n apiKey: string;\n /** Defaults to https://api.verifymailapi.com */\n baseUrl?: string;\n /** Max retry attempts on retryable failures (429, 5xx). Default 2 (so up to 3 total tries). */\n retries?: number;\n /** Per-request timeout in ms. Default 30000. */\n timeoutMs?: number;\n /** Override fetch — useful for tests or non-Node runtimes. */\n fetch?: typeof fetch;\n /** Default risk profile sent as X-Risk-Profile on every call (overridable per request). */\n riskProfile?: \"strict\" | \"balanced\" | \"permissive\";\n}\n\nexport interface RequestOptions {\n method?: \"GET\" | \"POST\";\n path: string;\n query?: Record<string, string | number | undefined>;\n body?: unknown;\n headers?: Record<string, string>;\n /** Provide an Idempotency-Key for POSTs. Auto-generated UUID if `true`. */\n idempotencyKey?: string | boolean;\n /** Override the default risk profile for this call. */\n riskProfile?: \"strict\" | \"balanced\" | \"permissive\";\n /** Skip retry on transient errors for this call. */\n noRetry?: boolean;\n /** Override timeout for this call. */\n timeoutMs?: number;\n}\n\nconst RETRYABLE_STATUS = new Set([408, 429, 500, 502, 503, 504]);\n\nfunction uuidv4(): string {\n // Node 19+ has globalThis.crypto.randomUUID. Polyfill for older Node.\n const c = globalThis.crypto;\n if (c?.randomUUID) return c.randomUUID();\n const buf = new Uint8Array(16);\n for (let i = 0; i < 16; i++) buf[i] = Math.floor(Math.random() * 256);\n buf[6] = (buf[6]! & 0x0f) | 0x40;\n buf[8] = (buf[8]! & 0x3f) | 0x80;\n const hex = Array.from(buf, (b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;\n}\n\nexport class HttpClient {\n private readonly apiKey: string;\n private readonly baseUrl: string;\n private readonly retries: number;\n private readonly timeoutMs: number;\n private readonly fetchImpl: typeof fetch;\n private readonly defaultRiskProfile: string | undefined;\n\n constructor(opts: ClientOptions) {\n if (!opts.apiKey) throw new VerifyMailError(\"apiKey is required.\");\n this.apiKey = opts.apiKey;\n this.baseUrl = (opts.baseUrl ?? \"https://api.verifymailapi.com\").replace(/\\/+$/, \"\");\n this.retries = opts.retries ?? 2;\n this.timeoutMs = opts.timeoutMs ?? 30_000;\n this.fetchImpl = opts.fetch ?? globalThis.fetch;\n this.defaultRiskProfile = opts.riskProfile;\n if (!this.fetchImpl) {\n throw new VerifyMailError(\n \"No fetch available. Pass `fetch` in the options, or use Node 18+.\",\n );\n }\n }\n\n /** Build the headers shared by every request. */\n private buildHeaders(opts: RequestOptions): Headers {\n const h = new Headers(opts.headers);\n h.set(\"X-API-Key\", this.apiKey);\n h.set(\"Accept\", \"application/json\");\n h.set(\"User-Agent\", \"verifymailapi-js/0.1.1\");\n if (opts.body !== undefined) h.set(\"Content-Type\", \"application/json\");\n\n const profile = opts.riskProfile ?? this.defaultRiskProfile;\n if (profile) h.set(\"X-Risk-Profile\", profile);\n\n if (opts.idempotencyKey) {\n h.set(\n \"Idempotency-Key\",\n typeof opts.idempotencyKey === \"string\" ? opts.idempotencyKey : uuidv4(),\n );\n }\n return h;\n }\n\n private buildUrl(path: string, query?: RequestOptions[\"query\"]): string {\n const url = new URL(`${this.baseUrl}${path}`);\n if (query) {\n for (const [k, v] of Object.entries(query)) {\n if (v !== undefined) url.searchParams.set(k, String(v));\n }\n }\n return url.toString();\n }\n\n /** Send and decode a JSON request. Retries on 429 / 5xx up to `retries` times. */\n async json<T>(opts: RequestOptions): Promise<T> {\n const raw = await this.raw(opts);\n if (raw.status === 204) return undefined as T;\n const text = await raw.text();\n try {\n return JSON.parse(text) as T;\n } catch {\n throw new VerifyMailError(`Non-JSON response from ${opts.path}.`, {\n status: raw.status,\n });\n }\n }\n\n /**\n * Send a request and return the raw Response. Handles retries + error mapping.\n * Used internally; exposed for the streaming bulk method to consume the body.\n */\n async raw(opts: RequestOptions): Promise<Response> {\n const url = this.buildUrl(opts.path, opts.query);\n const headers = this.buildHeaders(opts);\n const body = opts.body !== undefined ? JSON.stringify(opts.body) : undefined;\n const method = opts.method ?? (body ? \"POST\" : \"GET\");\n const maxTries = opts.noRetry ? 1 : this.retries + 1;\n const timeoutMs = opts.timeoutMs ?? this.timeoutMs;\n\n let lastError: VerifyMailError | undefined;\n for (let attempt = 0; attempt < maxTries; attempt++) {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n let res: Response;\n try {\n res = await this.fetchImpl(url, {\n method,\n headers,\n body,\n signal: controller.signal,\n });\n } catch (err) {\n clearTimeout(timer);\n // Network-level failure — retry if attempts remain.\n lastError = new VerifyMailError(\n err instanceof Error ? err.message : \"Network error\",\n { code: \"network_error\" },\n );\n if (attempt < maxTries - 1) {\n await sleep(backoffMs(attempt));\n continue;\n }\n throw lastError;\n }\n clearTimeout(timer);\n\n if (res.ok) return res;\n\n // Parse error body (best-effort) so we can build a typed error.\n let parsed: unknown = undefined;\n try {\n parsed = await res.clone().json();\n } catch {\n /* ignore non-JSON error bodies */\n }\n const err = errorFromResponse(res.status, parsed, res.headers.get(\"Retry-After\"));\n\n const isRetryable =\n !opts.noRetry &&\n RETRYABLE_STATUS.has(res.status) &&\n attempt < maxTries - 1;\n\n if (isRetryable) {\n const wait =\n err instanceof RateLimitError\n ? err.retryAfter * 1000\n : err instanceof ServiceDegradedError\n ? backoffMs(attempt)\n : backoffMs(attempt);\n await sleep(wait);\n lastError = err;\n continue;\n }\n\n throw err;\n }\n\n throw lastError ?? new VerifyMailError(\"Request failed after retries.\");\n }\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((r) => setTimeout(r, ms));\n}\n\nfunction backoffMs(attempt: number): number {\n // 250ms, 750ms, 2.25s — typical exponential with a small jitter floor.\n return Math.min(250 * Math.pow(3, attempt), 10_000);\n}\n","import { createHmac, timingSafeEqual } from \"node:crypto\";\n\n/**\n * Verify an incoming webhook signature from a `/v1/check/async` completion.\n *\n * const ok = verifyWebhook(rawBody, req.header(\"X-VerifyMail-Signature\"), secret);\n * if (!ok) return res.status(401).end();\n *\n * Pass the *raw* request body bytes — not the parsed JSON. In Express:\n * app.post(\"/webhook\", express.raw({ type: \"application/json\" }), handler)\n */\nexport function verifyWebhook(\n rawBody: Buffer | Uint8Array | string,\n signatureHeader: string | null | undefined,\n secret: string,\n): boolean {\n if (!signatureHeader) return false;\n const body =\n typeof rawBody === \"string\" ? Buffer.from(rawBody, \"utf8\") : Buffer.from(rawBody);\n const expected =\n \"sha256=\" + createHmac(\"sha256\", secret).update(body).digest(\"hex\");\n const a = Buffer.from(signatureHeader);\n const b = Buffer.from(expected);\n return a.length === b.length && timingSafeEqual(a, b);\n}\n"],"mappings":"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","names":["err"]}
|
package/dist/index.js
CHANGED
|
@@ -123,7 +123,7 @@ var HttpClient = class {
|
|
|
123
123
|
const h = new Headers(opts.headers);
|
|
124
124
|
h.set("X-API-Key", this.apiKey);
|
|
125
125
|
h.set("Accept", "application/json");
|
|
126
|
-
h.set("User-Agent", "verifymailapi-js/0.1.
|
|
126
|
+
h.set("User-Agent", "verifymailapi-js/0.1.1");
|
|
127
127
|
if (opts.body !== void 0) h.set("Content-Type", "application/json");
|
|
128
128
|
const profile = opts.riskProfile ?? this.defaultRiskProfile;
|
|
129
129
|
if (profile) h.set("X-Risk-Profile", profile);
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/errors.ts","../src/client.ts","../src/webhooks.ts","../src/index.ts"],"sourcesContent":["/**\n * Error class hierarchy. All HTTP errors from the API are translated into one\n * of these — customers can `instanceof` them to branch cleanly:\n *\n * try { await vm.check(email) }\n * catch (e) {\n * if (e instanceof QuotaExceededError) return showBilling();\n * if (e instanceof RateLimitError) return retryLater(e.retryAfter);\n * if (e instanceof VerifyMailError) return logAndShowGeneric(e);\n * throw e;\n * }\n */\n\nexport interface ErrorBody {\n code: string;\n http_status: number;\n message: string;\n request_id?: string;\n docs_url?: string;\n // Rate-limit-specific\n limit?: number;\n reset_at?: string;\n // Quota-specific\n upgrade_url?: string;\n}\n\nexport class VerifyMailError extends Error {\n readonly code: string;\n readonly status: number;\n readonly requestId: string | undefined;\n readonly docsUrl: string | undefined;\n readonly body: ErrorBody | undefined;\n\n constructor(message: string, opts: {\n code?: string;\n status?: number;\n requestId?: string;\n docsUrl?: string;\n body?: ErrorBody;\n } = {}) {\n super(message);\n this.name = \"VerifyMailError\";\n this.code = opts.code ?? \"verifymail_error\";\n this.status = opts.status ?? 0;\n this.requestId = opts.requestId;\n this.docsUrl = opts.docsUrl;\n this.body = opts.body;\n }\n}\n\nexport class InvalidApiKeyError extends VerifyMailError {\n constructor(opts: NonNullable<ConstructorParameters<typeof VerifyMailError>[1]>) {\n super(\"API key is missing or invalid.\", opts);\n this.name = \"InvalidApiKeyError\";\n }\n}\n\nexport class QuotaExceededError extends VerifyMailError {\n readonly upgradeUrl: string | undefined;\n constructor(opts: NonNullable<ConstructorParameters<typeof VerifyMailError>[1]>) {\n super(\"Out of credits. Buy a bundle to keep going.\", opts);\n this.name = \"QuotaExceededError\";\n this.upgradeUrl = opts.body?.upgrade_url;\n }\n}\n\nexport class RateLimitError extends VerifyMailError {\n readonly retryAfter: number;\n readonly limit: number | undefined;\n readonly resetAt: string | undefined;\n\n constructor(opts: NonNullable<ConstructorParameters<typeof VerifyMailError>[1]> & {\n retryAfter: number;\n }) {\n super(`Rate limit hit; retry after ${opts.retryAfter}s.`, opts);\n this.name = \"RateLimitError\";\n this.retryAfter = opts.retryAfter;\n this.limit = opts.body?.limit;\n this.resetAt = opts.body?.reset_at;\n }\n}\n\nexport class IdempotencyConflictError extends VerifyMailError {\n constructor(opts: NonNullable<ConstructorParameters<typeof VerifyMailError>[1]>) {\n super(\n \"Idempotency-Key was reused with a different request body. Use a new key or resend the original payload.\",\n opts,\n );\n this.name = \"IdempotencyConflictError\";\n }\n}\n\nexport class ValidationError extends VerifyMailError {\n constructor(message: string, opts: NonNullable<ConstructorParameters<typeof VerifyMailError>[1]>) {\n super(message, opts);\n this.name = \"ValidationError\";\n }\n}\n\nexport class ServiceDegradedError extends VerifyMailError {\n constructor(opts: NonNullable<ConstructorParameters<typeof VerifyMailError>[1]>) {\n super(\"A component is degraded; retry shortly.\", opts);\n this.name = \"ServiceDegradedError\";\n }\n}\n\n/**\n * Map an HTTP response to the right error class. The backend's error envelope\n * is `{ error: { code, http_status, message, ... } }`.\n */\nexport function errorFromResponse(\n status: number,\n body: { error?: ErrorBody } | unknown,\n retryAfterHeader?: string | null,\n): VerifyMailError {\n const envelope =\n body && typeof body === \"object\" && \"error\" in body\n ? (body as { error?: ErrorBody }).error\n : undefined;\n const code = envelope?.code ?? \"verifymail_error\";\n const opts = {\n code,\n status,\n requestId: envelope?.request_id,\n docsUrl: envelope?.docs_url,\n body: envelope,\n };\n\n if (status === 401) return new InvalidApiKeyError(opts);\n if (status === 402) return new QuotaExceededError(opts);\n if (status === 409 && code === \"invalid_idempotency_key\")\n return new IdempotencyConflictError(opts);\n if (status === 422) return new ValidationError(envelope?.message ?? \"Validation error.\", opts);\n if (status === 429) {\n const retryAfter = Number(retryAfterHeader ?? envelope?.limit ?? 1);\n return new RateLimitError({ ...opts, retryAfter: Number.isFinite(retryAfter) ? retryAfter : 1 });\n }\n if (status === 503 || status === 504) return new ServiceDegradedError(opts);\n return new VerifyMailError(envelope?.message ?? `HTTP ${status}`, opts);\n}\n","import { errorFromResponse, RateLimitError, ServiceDegradedError, VerifyMailError } from \"./errors.js\";\n\nexport interface ClientOptions {\n apiKey: string;\n /** Defaults to https://api.verifymailapi.com */\n baseUrl?: string;\n /** Max retry attempts on retryable failures (429, 5xx). Default 2 (so up to 3 total tries). */\n retries?: number;\n /** Per-request timeout in ms. Default 30000. */\n timeoutMs?: number;\n /** Override fetch — useful for tests or non-Node runtimes. */\n fetch?: typeof fetch;\n /** Default risk profile sent as X-Risk-Profile on every call (overridable per request). */\n riskProfile?: \"strict\" | \"balanced\" | \"permissive\";\n}\n\nexport interface RequestOptions {\n method?: \"GET\" | \"POST\";\n path: string;\n query?: Record<string, string | number | undefined>;\n body?: unknown;\n headers?: Record<string, string>;\n /** Provide an Idempotency-Key for POSTs. Auto-generated UUID if `true`. */\n idempotencyKey?: string | boolean;\n /** Override the default risk profile for this call. */\n riskProfile?: \"strict\" | \"balanced\" | \"permissive\";\n /** Skip retry on transient errors for this call. */\n noRetry?: boolean;\n /** Override timeout for this call. */\n timeoutMs?: number;\n}\n\nconst RETRYABLE_STATUS = new Set([408, 429, 500, 502, 503, 504]);\n\nfunction uuidv4(): string {\n // Node 19+ has globalThis.crypto.randomUUID. Polyfill for older Node.\n const c = globalThis.crypto;\n if (c?.randomUUID) return c.randomUUID();\n const buf = new Uint8Array(16);\n for (let i = 0; i < 16; i++) buf[i] = Math.floor(Math.random() * 256);\n buf[6] = (buf[6]! & 0x0f) | 0x40;\n buf[8] = (buf[8]! & 0x3f) | 0x80;\n const hex = Array.from(buf, (b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;\n}\n\nexport class HttpClient {\n private readonly apiKey: string;\n private readonly baseUrl: string;\n private readonly retries: number;\n private readonly timeoutMs: number;\n private readonly fetchImpl: typeof fetch;\n private readonly defaultRiskProfile: string | undefined;\n\n constructor(opts: ClientOptions) {\n if (!opts.apiKey) throw new VerifyMailError(\"apiKey is required.\");\n this.apiKey = opts.apiKey;\n this.baseUrl = (opts.baseUrl ?? \"https://api.verifymailapi.com\").replace(/\\/+$/, \"\");\n this.retries = opts.retries ?? 2;\n this.timeoutMs = opts.timeoutMs ?? 30_000;\n this.fetchImpl = opts.fetch ?? globalThis.fetch;\n this.defaultRiskProfile = opts.riskProfile;\n if (!this.fetchImpl) {\n throw new VerifyMailError(\n \"No fetch available. Pass `fetch` in the options, or use Node 18+.\",\n );\n }\n }\n\n /** Build the headers shared by every request. */\n private buildHeaders(opts: RequestOptions): Headers {\n const h = new Headers(opts.headers);\n h.set(\"X-API-Key\", this.apiKey);\n h.set(\"Accept\", \"application/json\");\n h.set(\"User-Agent\", \"verifymailapi-js/0.1.0\");\n if (opts.body !== undefined) h.set(\"Content-Type\", \"application/json\");\n\n const profile = opts.riskProfile ?? this.defaultRiskProfile;\n if (profile) h.set(\"X-Risk-Profile\", profile);\n\n if (opts.idempotencyKey) {\n h.set(\n \"Idempotency-Key\",\n typeof opts.idempotencyKey === \"string\" ? opts.idempotencyKey : uuidv4(),\n );\n }\n return h;\n }\n\n private buildUrl(path: string, query?: RequestOptions[\"query\"]): string {\n const url = new URL(`${this.baseUrl}${path}`);\n if (query) {\n for (const [k, v] of Object.entries(query)) {\n if (v !== undefined) url.searchParams.set(k, String(v));\n }\n }\n return url.toString();\n }\n\n /** Send and decode a JSON request. Retries on 429 / 5xx up to `retries` times. */\n async json<T>(opts: RequestOptions): Promise<T> {\n const raw = await this.raw(opts);\n if (raw.status === 204) return undefined as T;\n const text = await raw.text();\n try {\n return JSON.parse(text) as T;\n } catch {\n throw new VerifyMailError(`Non-JSON response from ${opts.path}.`, {\n status: raw.status,\n });\n }\n }\n\n /**\n * Send a request and return the raw Response. Handles retries + error mapping.\n * Used internally; exposed for the streaming bulk method to consume the body.\n */\n async raw(opts: RequestOptions): Promise<Response> {\n const url = this.buildUrl(opts.path, opts.query);\n const headers = this.buildHeaders(opts);\n const body = opts.body !== undefined ? JSON.stringify(opts.body) : undefined;\n const method = opts.method ?? (body ? \"POST\" : \"GET\");\n const maxTries = opts.noRetry ? 1 : this.retries + 1;\n const timeoutMs = opts.timeoutMs ?? this.timeoutMs;\n\n let lastError: VerifyMailError | undefined;\n for (let attempt = 0; attempt < maxTries; attempt++) {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n let res: Response;\n try {\n res = await this.fetchImpl(url, {\n method,\n headers,\n body,\n signal: controller.signal,\n });\n } catch (err) {\n clearTimeout(timer);\n // Network-level failure — retry if attempts remain.\n lastError = new VerifyMailError(\n err instanceof Error ? err.message : \"Network error\",\n { code: \"network_error\" },\n );\n if (attempt < maxTries - 1) {\n await sleep(backoffMs(attempt));\n continue;\n }\n throw lastError;\n }\n clearTimeout(timer);\n\n if (res.ok) return res;\n\n // Parse error body (best-effort) so we can build a typed error.\n let parsed: unknown = undefined;\n try {\n parsed = await res.clone().json();\n } catch {\n /* ignore non-JSON error bodies */\n }\n const err = errorFromResponse(res.status, parsed, res.headers.get(\"Retry-After\"));\n\n const isRetryable =\n !opts.noRetry &&\n RETRYABLE_STATUS.has(res.status) &&\n attempt < maxTries - 1;\n\n if (isRetryable) {\n const wait =\n err instanceof RateLimitError\n ? err.retryAfter * 1000\n : err instanceof ServiceDegradedError\n ? backoffMs(attempt)\n : backoffMs(attempt);\n await sleep(wait);\n lastError = err;\n continue;\n }\n\n throw err;\n }\n\n throw lastError ?? new VerifyMailError(\"Request failed after retries.\");\n }\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((r) => setTimeout(r, ms));\n}\n\nfunction backoffMs(attempt: number): number {\n // 250ms, 750ms, 2.25s — typical exponential with a small jitter floor.\n return Math.min(250 * Math.pow(3, attempt), 10_000);\n}\n","import { createHmac, timingSafeEqual } from \"node:crypto\";\n\n/**\n * Verify an incoming webhook signature from a `/v1/check/async` completion.\n *\n * const ok = verifyWebhook(rawBody, req.header(\"X-VerifyMail-Signature\"), secret);\n * if (!ok) return res.status(401).end();\n *\n * Pass the *raw* request body bytes — not the parsed JSON. In Express:\n * app.post(\"/webhook\", express.raw({ type: \"application/json\" }), handler)\n */\nexport function verifyWebhook(\n rawBody: Buffer | Uint8Array | string,\n signatureHeader: string | null | undefined,\n secret: string,\n): boolean {\n if (!signatureHeader) return false;\n const body =\n typeof rawBody === \"string\" ? Buffer.from(rawBody, \"utf8\") : Buffer.from(rawBody);\n const expected =\n \"sha256=\" + createHmac(\"sha256\", secret).update(body).digest(\"hex\");\n const a = Buffer.from(signatureHeader);\n const b = Buffer.from(expected);\n return a.length === b.length && timingSafeEqual(a, b);\n}\n","/**\n * Official SDK for the VerifyMail API.\n *\n * import { VerifyMail } from \"verifymailapi\";\n * const vm = new VerifyMail({ apiKey: process.env.VERIFYMAIL_KEY! });\n * const r = await vm.check(\"user@example.com\");\n * if (r.verdict.recommendation === \"block\") { ... }\n *\n * Docs: https://verifymailapi.com/docs\n */\n\nimport { HttpClient, type ClientOptions, type RequestOptions } from \"./client.js\";\nimport type {\n AsyncCheckResponse,\n BulkCheckResponse,\n BulkStreamEvent,\n CheckResponse,\n ReportRequest,\n ReportResponse,\n StatusResponse,\n UsageMeResponse,\n} from \"./types.js\";\n\nexport * from \"./types.js\";\nexport * from \"./errors.js\";\nexport { verifyWebhook } from \"./webhooks.js\";\n\nexport interface CheckOptions {\n /** Override the SDK-wide risk profile for this call. */\n riskProfile?: \"strict\" | \"balanced\" | \"permissive\";\n /** Make the call idempotent. Pass `true` to auto-generate a UUID, or a fixed string. */\n idempotencyKey?: string | boolean;\n}\n\nexport interface AsyncCheckArgs {\n email: string;\n webhookUrl: string;\n /** Optional HMAC-SHA256 key used to sign the final webhook payload. */\n webhookSecret?: string;\n}\n\nexport class VerifyMail {\n private readonly http: HttpClient;\n\n constructor(opts: ClientOptions) {\n this.http = new HttpClient(opts);\n }\n\n /** Check a single email. Charges 1 credit. */\n check(email: string, opts: CheckOptions = {}): Promise<CheckResponse> {\n return this.http.json<CheckResponse>({\n path: \"/v1/check\",\n body: { email },\n idempotencyKey: opts.idempotencyKey,\n riskProfile: opts.riskProfile,\n });\n }\n\n /** Check a domain only (no local part). Charges 1 credit. */\n checkDomain(domain: string, opts: CheckOptions = {}): Promise<CheckResponse> {\n return this.http.json<CheckResponse>({\n path: \"/v1/check/domain\",\n body: { domain },\n idempotencyKey: opts.idempotencyKey,\n riskProfile: opts.riskProfile,\n });\n }\n\n /** Bulk check 1–100 emails. Charges N credits up front (all-or-nothing). */\n checkBulk(emails: string[], opts: CheckOptions = {}): Promise<BulkCheckResponse> {\n if (emails.length === 0 || emails.length > 100) {\n throw new RangeError(\"checkBulk requires 1–100 emails.\");\n }\n return this.http.json<BulkCheckResponse>({\n path: \"/v1/check/bulk\",\n body: { emails },\n idempotencyKey: opts.idempotencyKey,\n riskProfile: opts.riskProfile,\n });\n }\n\n /**\n * Stream bulk-check results as each row completes. Calls `onEvent` once\n * per result (in finish-order) plus once with a final `{event: \"summary\"}`.\n *\n * await vm.checkBulkStream(emails, (e) => {\n * if (\"event\" in e) console.log(\"done\", e);\n * else processRow(e.index, e.result);\n * });\n */\n async checkBulkStream(\n emails: string[],\n onEvent: (e: BulkStreamEvent) => void | Promise<void>,\n opts: Omit<CheckOptions, \"idempotencyKey\"> = {},\n ): Promise<void> {\n if (emails.length === 0) throw new RangeError(\"checkBulkStream needs at least one email.\");\n\n const req: RequestOptions = {\n path: \"/v1/check/bulk/stream\",\n body: { emails },\n riskProfile: opts.riskProfile,\n // Streaming requests aren't safe to auto-retry — would re-charge credits\n // and re-stream rows. Customer must retry explicitly.\n noRetry: true,\n };\n const res = await this.http.raw(req);\n if (!res.body) throw new Error(\"Streaming response had no body.\");\n\n const reader = res.body.getReader();\n const decoder = new TextDecoder();\n let buf = \"\";\n while (true) {\n const { value, done } = await reader.read();\n if (done) break;\n buf += decoder.decode(value, { stream: true });\n let nl: number;\n // Drain complete lines; keep the partial tail in buf for the next chunk.\n while ((nl = buf.indexOf(\"\\n\")) !== -1) {\n const line = buf.slice(0, nl).trim();\n buf = buf.slice(nl + 1);\n if (!line) continue;\n try {\n await onEvent(JSON.parse(line) as BulkStreamEvent);\n } catch (err) {\n throw new Error(`Failed to parse stream line: ${line}`);\n }\n }\n }\n // Final flush in case the server didn't terminate with newline.\n const tail = buf.trim();\n if (tail) await onEvent(JSON.parse(tail) as BulkStreamEvent);\n }\n\n /**\n * Async deep check. Returns 202 immediately with a preliminary verdict;\n * VerifyMail POSTs the final result to your webhook URL once the deep\n * SMTP probe completes. Verify the signature with `verifyWebhook()` in\n * your handler before trusting the payload.\n */\n checkAsync(args: AsyncCheckArgs, opts: CheckOptions = {}): Promise<AsyncCheckResponse> {\n return this.http.json<AsyncCheckResponse>({\n path: \"/v1/check/async\",\n body: {\n email: args.email,\n webhook_url: args.webhookUrl,\n webhook_secret: args.webhookSecret,\n },\n idempotencyKey: opts.idempotencyKey,\n riskProfile: opts.riskProfile,\n });\n }\n\n /** File a /v1/report for a domain outcome (feedback loop). */\n report(req: ReportRequest): Promise<ReportResponse> {\n return this.http.json<ReportResponse>({\n path: \"/v1/report\",\n body: req,\n });\n }\n\n /** Programmatic equivalent of the dashboard's Usage summary. */\n usage(): Promise<UsageMeResponse> {\n return this.http.json<UsageMeResponse>({\n path: \"/v1/usage/me\",\n method: \"GET\",\n });\n }\n\n /** Component health (Redis / Postgres / DNS). Always returns 200. */\n status(): Promise<StatusResponse> {\n return this.http.json<StatusResponse>({\n path: \"/v1/status\",\n method: \"GET\",\n });\n }\n}\n\nexport default VerifyMail;\n"],"mappings":"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","names":["err"]}
|
|
1
|
+
{"version":3,"sources":["../src/errors.ts","../src/client.ts","../src/webhooks.ts","../src/index.ts"],"sourcesContent":["/**\n * Error class hierarchy. All HTTP errors from the API are translated into one\n * of these — customers can `instanceof` them to branch cleanly:\n *\n * try { await vm.check(email) }\n * catch (e) {\n * if (e instanceof QuotaExceededError) return showBilling();\n * if (e instanceof RateLimitError) return retryLater(e.retryAfter);\n * if (e instanceof VerifyMailError) return logAndShowGeneric(e);\n * throw e;\n * }\n */\n\nexport interface ErrorBody {\n code: string;\n http_status: number;\n message: string;\n request_id?: string;\n docs_url?: string;\n // Rate-limit-specific\n limit?: number;\n reset_at?: string;\n // Quota-specific\n upgrade_url?: string;\n}\n\nexport class VerifyMailError extends Error {\n readonly code: string;\n readonly status: number;\n readonly requestId: string | undefined;\n readonly docsUrl: string | undefined;\n readonly body: ErrorBody | undefined;\n\n constructor(message: string, opts: {\n code?: string;\n status?: number;\n requestId?: string;\n docsUrl?: string;\n body?: ErrorBody;\n } = {}) {\n super(message);\n this.name = \"VerifyMailError\";\n this.code = opts.code ?? \"verifymail_error\";\n this.status = opts.status ?? 0;\n this.requestId = opts.requestId;\n this.docsUrl = opts.docsUrl;\n this.body = opts.body;\n }\n}\n\nexport class InvalidApiKeyError extends VerifyMailError {\n constructor(opts: NonNullable<ConstructorParameters<typeof VerifyMailError>[1]>) {\n super(\"API key is missing or invalid.\", opts);\n this.name = \"InvalidApiKeyError\";\n }\n}\n\nexport class QuotaExceededError extends VerifyMailError {\n readonly upgradeUrl: string | undefined;\n constructor(opts: NonNullable<ConstructorParameters<typeof VerifyMailError>[1]>) {\n super(\"Out of credits. Buy a bundle to keep going.\", opts);\n this.name = \"QuotaExceededError\";\n this.upgradeUrl = opts.body?.upgrade_url;\n }\n}\n\nexport class RateLimitError extends VerifyMailError {\n readonly retryAfter: number;\n readonly limit: number | undefined;\n readonly resetAt: string | undefined;\n\n constructor(opts: NonNullable<ConstructorParameters<typeof VerifyMailError>[1]> & {\n retryAfter: number;\n }) {\n super(`Rate limit hit; retry after ${opts.retryAfter}s.`, opts);\n this.name = \"RateLimitError\";\n this.retryAfter = opts.retryAfter;\n this.limit = opts.body?.limit;\n this.resetAt = opts.body?.reset_at;\n }\n}\n\nexport class IdempotencyConflictError extends VerifyMailError {\n constructor(opts: NonNullable<ConstructorParameters<typeof VerifyMailError>[1]>) {\n super(\n \"Idempotency-Key was reused with a different request body. Use a new key or resend the original payload.\",\n opts,\n );\n this.name = \"IdempotencyConflictError\";\n }\n}\n\nexport class ValidationError extends VerifyMailError {\n constructor(message: string, opts: NonNullable<ConstructorParameters<typeof VerifyMailError>[1]>) {\n super(message, opts);\n this.name = \"ValidationError\";\n }\n}\n\nexport class ServiceDegradedError extends VerifyMailError {\n constructor(opts: NonNullable<ConstructorParameters<typeof VerifyMailError>[1]>) {\n super(\"A component is degraded; retry shortly.\", opts);\n this.name = \"ServiceDegradedError\";\n }\n}\n\n/**\n * Map an HTTP response to the right error class. The backend's error envelope\n * is `{ error: { code, http_status, message, ... } }`.\n */\nexport function errorFromResponse(\n status: number,\n body: { error?: ErrorBody } | unknown,\n retryAfterHeader?: string | null,\n): VerifyMailError {\n const envelope =\n body && typeof body === \"object\" && \"error\" in body\n ? (body as { error?: ErrorBody }).error\n : undefined;\n const code = envelope?.code ?? \"verifymail_error\";\n const opts = {\n code,\n status,\n requestId: envelope?.request_id,\n docsUrl: envelope?.docs_url,\n body: envelope,\n };\n\n if (status === 401) return new InvalidApiKeyError(opts);\n if (status === 402) return new QuotaExceededError(opts);\n if (status === 409 && code === \"invalid_idempotency_key\")\n return new IdempotencyConflictError(opts);\n if (status === 422) return new ValidationError(envelope?.message ?? \"Validation error.\", opts);\n if (status === 429) {\n const retryAfter = Number(retryAfterHeader ?? envelope?.limit ?? 1);\n return new RateLimitError({ ...opts, retryAfter: Number.isFinite(retryAfter) ? retryAfter : 1 });\n }\n if (status === 503 || status === 504) return new ServiceDegradedError(opts);\n return new VerifyMailError(envelope?.message ?? `HTTP ${status}`, opts);\n}\n","import { errorFromResponse, RateLimitError, ServiceDegradedError, VerifyMailError } from \"./errors.js\";\n\nexport interface ClientOptions {\n apiKey: string;\n /** Defaults to https://api.verifymailapi.com */\n baseUrl?: string;\n /** Max retry attempts on retryable failures (429, 5xx). Default 2 (so up to 3 total tries). */\n retries?: number;\n /** Per-request timeout in ms. Default 30000. */\n timeoutMs?: number;\n /** Override fetch — useful for tests or non-Node runtimes. */\n fetch?: typeof fetch;\n /** Default risk profile sent as X-Risk-Profile on every call (overridable per request). */\n riskProfile?: \"strict\" | \"balanced\" | \"permissive\";\n}\n\nexport interface RequestOptions {\n method?: \"GET\" | \"POST\";\n path: string;\n query?: Record<string, string | number | undefined>;\n body?: unknown;\n headers?: Record<string, string>;\n /** Provide an Idempotency-Key for POSTs. Auto-generated UUID if `true`. */\n idempotencyKey?: string | boolean;\n /** Override the default risk profile for this call. */\n riskProfile?: \"strict\" | \"balanced\" | \"permissive\";\n /** Skip retry on transient errors for this call. */\n noRetry?: boolean;\n /** Override timeout for this call. */\n timeoutMs?: number;\n}\n\nconst RETRYABLE_STATUS = new Set([408, 429, 500, 502, 503, 504]);\n\nfunction uuidv4(): string {\n // Node 19+ has globalThis.crypto.randomUUID. Polyfill for older Node.\n const c = globalThis.crypto;\n if (c?.randomUUID) return c.randomUUID();\n const buf = new Uint8Array(16);\n for (let i = 0; i < 16; i++) buf[i] = Math.floor(Math.random() * 256);\n buf[6] = (buf[6]! & 0x0f) | 0x40;\n buf[8] = (buf[8]! & 0x3f) | 0x80;\n const hex = Array.from(buf, (b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;\n}\n\nexport class HttpClient {\n private readonly apiKey: string;\n private readonly baseUrl: string;\n private readonly retries: number;\n private readonly timeoutMs: number;\n private readonly fetchImpl: typeof fetch;\n private readonly defaultRiskProfile: string | undefined;\n\n constructor(opts: ClientOptions) {\n if (!opts.apiKey) throw new VerifyMailError(\"apiKey is required.\");\n this.apiKey = opts.apiKey;\n this.baseUrl = (opts.baseUrl ?? \"https://api.verifymailapi.com\").replace(/\\/+$/, \"\");\n this.retries = opts.retries ?? 2;\n this.timeoutMs = opts.timeoutMs ?? 30_000;\n this.fetchImpl = opts.fetch ?? globalThis.fetch;\n this.defaultRiskProfile = opts.riskProfile;\n if (!this.fetchImpl) {\n throw new VerifyMailError(\n \"No fetch available. Pass `fetch` in the options, or use Node 18+.\",\n );\n }\n }\n\n /** Build the headers shared by every request. */\n private buildHeaders(opts: RequestOptions): Headers {\n const h = new Headers(opts.headers);\n h.set(\"X-API-Key\", this.apiKey);\n h.set(\"Accept\", \"application/json\");\n h.set(\"User-Agent\", \"verifymailapi-js/0.1.1\");\n if (opts.body !== undefined) h.set(\"Content-Type\", \"application/json\");\n\n const profile = opts.riskProfile ?? this.defaultRiskProfile;\n if (profile) h.set(\"X-Risk-Profile\", profile);\n\n if (opts.idempotencyKey) {\n h.set(\n \"Idempotency-Key\",\n typeof opts.idempotencyKey === \"string\" ? opts.idempotencyKey : uuidv4(),\n );\n }\n return h;\n }\n\n private buildUrl(path: string, query?: RequestOptions[\"query\"]): string {\n const url = new URL(`${this.baseUrl}${path}`);\n if (query) {\n for (const [k, v] of Object.entries(query)) {\n if (v !== undefined) url.searchParams.set(k, String(v));\n }\n }\n return url.toString();\n }\n\n /** Send and decode a JSON request. Retries on 429 / 5xx up to `retries` times. */\n async json<T>(opts: RequestOptions): Promise<T> {\n const raw = await this.raw(opts);\n if (raw.status === 204) return undefined as T;\n const text = await raw.text();\n try {\n return JSON.parse(text) as T;\n } catch {\n throw new VerifyMailError(`Non-JSON response from ${opts.path}.`, {\n status: raw.status,\n });\n }\n }\n\n /**\n * Send a request and return the raw Response. Handles retries + error mapping.\n * Used internally; exposed for the streaming bulk method to consume the body.\n */\n async raw(opts: RequestOptions): Promise<Response> {\n const url = this.buildUrl(opts.path, opts.query);\n const headers = this.buildHeaders(opts);\n const body = opts.body !== undefined ? JSON.stringify(opts.body) : undefined;\n const method = opts.method ?? (body ? \"POST\" : \"GET\");\n const maxTries = opts.noRetry ? 1 : this.retries + 1;\n const timeoutMs = opts.timeoutMs ?? this.timeoutMs;\n\n let lastError: VerifyMailError | undefined;\n for (let attempt = 0; attempt < maxTries; attempt++) {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n let res: Response;\n try {\n res = await this.fetchImpl(url, {\n method,\n headers,\n body,\n signal: controller.signal,\n });\n } catch (err) {\n clearTimeout(timer);\n // Network-level failure — retry if attempts remain.\n lastError = new VerifyMailError(\n err instanceof Error ? err.message : \"Network error\",\n { code: \"network_error\" },\n );\n if (attempt < maxTries - 1) {\n await sleep(backoffMs(attempt));\n continue;\n }\n throw lastError;\n }\n clearTimeout(timer);\n\n if (res.ok) return res;\n\n // Parse error body (best-effort) so we can build a typed error.\n let parsed: unknown = undefined;\n try {\n parsed = await res.clone().json();\n } catch {\n /* ignore non-JSON error bodies */\n }\n const err = errorFromResponse(res.status, parsed, res.headers.get(\"Retry-After\"));\n\n const isRetryable =\n !opts.noRetry &&\n RETRYABLE_STATUS.has(res.status) &&\n attempt < maxTries - 1;\n\n if (isRetryable) {\n const wait =\n err instanceof RateLimitError\n ? err.retryAfter * 1000\n : err instanceof ServiceDegradedError\n ? backoffMs(attempt)\n : backoffMs(attempt);\n await sleep(wait);\n lastError = err;\n continue;\n }\n\n throw err;\n }\n\n throw lastError ?? new VerifyMailError(\"Request failed after retries.\");\n }\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((r) => setTimeout(r, ms));\n}\n\nfunction backoffMs(attempt: number): number {\n // 250ms, 750ms, 2.25s — typical exponential with a small jitter floor.\n return Math.min(250 * Math.pow(3, attempt), 10_000);\n}\n","import { createHmac, timingSafeEqual } from \"node:crypto\";\n\n/**\n * Verify an incoming webhook signature from a `/v1/check/async` completion.\n *\n * const ok = verifyWebhook(rawBody, req.header(\"X-VerifyMail-Signature\"), secret);\n * if (!ok) return res.status(401).end();\n *\n * Pass the *raw* request body bytes — not the parsed JSON. In Express:\n * app.post(\"/webhook\", express.raw({ type: \"application/json\" }), handler)\n */\nexport function verifyWebhook(\n rawBody: Buffer | Uint8Array | string,\n signatureHeader: string | null | undefined,\n secret: string,\n): boolean {\n if (!signatureHeader) return false;\n const body =\n typeof rawBody === \"string\" ? Buffer.from(rawBody, \"utf8\") : Buffer.from(rawBody);\n const expected =\n \"sha256=\" + createHmac(\"sha256\", secret).update(body).digest(\"hex\");\n const a = Buffer.from(signatureHeader);\n const b = Buffer.from(expected);\n return a.length === b.length && timingSafeEqual(a, b);\n}\n","/**\n * Official SDK for the VerifyMail API.\n *\n * import { VerifyMail } from \"verifymailapi\";\n * const vm = new VerifyMail({ apiKey: process.env.VERIFYMAIL_KEY! });\n * const r = await vm.check(\"user@example.com\");\n * if (r.verdict.recommendation === \"block\") { ... }\n *\n * Docs: https://verifymailapi.com/docs\n */\n\nimport { HttpClient, type ClientOptions, type RequestOptions } from \"./client.js\";\nimport type {\n AsyncCheckResponse,\n BulkCheckResponse,\n BulkStreamEvent,\n CheckResponse,\n ReportRequest,\n ReportResponse,\n StatusResponse,\n UsageMeResponse,\n} from \"./types.js\";\n\nexport * from \"./types.js\";\nexport * from \"./errors.js\";\nexport { verifyWebhook } from \"./webhooks.js\";\n\nexport interface CheckOptions {\n /** Override the SDK-wide risk profile for this call. */\n riskProfile?: \"strict\" | \"balanced\" | \"permissive\";\n /** Make the call idempotent. Pass `true` to auto-generate a UUID, or a fixed string. */\n idempotencyKey?: string | boolean;\n}\n\nexport interface AsyncCheckArgs {\n email: string;\n webhookUrl: string;\n /** Optional HMAC-SHA256 key used to sign the final webhook payload. */\n webhookSecret?: string;\n}\n\nexport class VerifyMail {\n private readonly http: HttpClient;\n\n constructor(opts: ClientOptions) {\n this.http = new HttpClient(opts);\n }\n\n /** Check a single email. Charges 1 credit. */\n check(email: string, opts: CheckOptions = {}): Promise<CheckResponse> {\n return this.http.json<CheckResponse>({\n path: \"/v1/check\",\n body: { email },\n idempotencyKey: opts.idempotencyKey,\n riskProfile: opts.riskProfile,\n });\n }\n\n /** Check a domain only (no local part). Charges 1 credit. */\n checkDomain(domain: string, opts: CheckOptions = {}): Promise<CheckResponse> {\n return this.http.json<CheckResponse>({\n path: \"/v1/check/domain\",\n body: { domain },\n idempotencyKey: opts.idempotencyKey,\n riskProfile: opts.riskProfile,\n });\n }\n\n /** Bulk check 1–100 emails. Charges N credits up front (all-or-nothing). */\n checkBulk(emails: string[], opts: CheckOptions = {}): Promise<BulkCheckResponse> {\n if (emails.length === 0 || emails.length > 100) {\n throw new RangeError(\"checkBulk requires 1–100 emails.\");\n }\n return this.http.json<BulkCheckResponse>({\n path: \"/v1/check/bulk\",\n body: { emails },\n idempotencyKey: opts.idempotencyKey,\n riskProfile: opts.riskProfile,\n });\n }\n\n /**\n * Stream bulk-check results as each row completes. Calls `onEvent` once\n * per result (in finish-order) plus once with a final `{event: \"summary\"}`.\n *\n * await vm.checkBulkStream(emails, (e) => {\n * if (\"event\" in e) console.log(\"done\", e);\n * else processRow(e.index, e.result);\n * });\n */\n async checkBulkStream(\n emails: string[],\n onEvent: (e: BulkStreamEvent) => void | Promise<void>,\n opts: Omit<CheckOptions, \"idempotencyKey\"> = {},\n ): Promise<void> {\n if (emails.length === 0) throw new RangeError(\"checkBulkStream needs at least one email.\");\n\n const req: RequestOptions = {\n path: \"/v1/check/bulk/stream\",\n body: { emails },\n riskProfile: opts.riskProfile,\n // Streaming requests aren't safe to auto-retry — would re-charge credits\n // and re-stream rows. Customer must retry explicitly.\n noRetry: true,\n };\n const res = await this.http.raw(req);\n if (!res.body) throw new Error(\"Streaming response had no body.\");\n\n const reader = res.body.getReader();\n const decoder = new TextDecoder();\n let buf = \"\";\n while (true) {\n const { value, done } = await reader.read();\n if (done) break;\n buf += decoder.decode(value, { stream: true });\n let nl: number;\n // Drain complete lines; keep the partial tail in buf for the next chunk.\n while ((nl = buf.indexOf(\"\\n\")) !== -1) {\n const line = buf.slice(0, nl).trim();\n buf = buf.slice(nl + 1);\n if (!line) continue;\n try {\n await onEvent(JSON.parse(line) as BulkStreamEvent);\n } catch (err) {\n throw new Error(`Failed to parse stream line: ${line}`);\n }\n }\n }\n // Final flush in case the server didn't terminate with newline.\n const tail = buf.trim();\n if (tail) await onEvent(JSON.parse(tail) as BulkStreamEvent);\n }\n\n /**\n * Async deep check. Returns 202 immediately with a preliminary verdict;\n * VerifyMail POSTs the final result to your webhook URL once the deep\n * SMTP probe completes. Verify the signature with `verifyWebhook()` in\n * your handler before trusting the payload.\n */\n checkAsync(args: AsyncCheckArgs, opts: CheckOptions = {}): Promise<AsyncCheckResponse> {\n return this.http.json<AsyncCheckResponse>({\n path: \"/v1/check/async\",\n body: {\n email: args.email,\n webhook_url: args.webhookUrl,\n webhook_secret: args.webhookSecret,\n },\n idempotencyKey: opts.idempotencyKey,\n riskProfile: opts.riskProfile,\n });\n }\n\n /** File a /v1/report for a domain outcome (feedback loop). */\n report(req: ReportRequest): Promise<ReportResponse> {\n return this.http.json<ReportResponse>({\n path: \"/v1/report\",\n body: req,\n });\n }\n\n /** Programmatic equivalent of the dashboard's Usage summary. */\n usage(): Promise<UsageMeResponse> {\n return this.http.json<UsageMeResponse>({\n path: \"/v1/usage/me\",\n method: \"GET\",\n });\n }\n\n /** Component health (Redis / Postgres / DNS). Always returns 200. */\n status(): Promise<StatusResponse> {\n return this.http.json<StatusResponse>({\n path: \"/v1/status\",\n method: \"GET\",\n });\n }\n}\n\nexport default VerifyMail;\n"],"mappings":"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","names":["err"]}
|
package/package.json
CHANGED