verifymailapi 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 VerifyMail
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 ADDED
@@ -0,0 +1,160 @@
1
+ # verifymailapi
2
+
3
+ Official Node / TypeScript SDK for the [VerifyMail API](https://verifymailapi.com) — disposable, throwaway, and abusive email detection.
4
+
5
+ ```ts
6
+ import { VerifyMail } from "verifymailapi";
7
+
8
+ const vm = new VerifyMail({ apiKey: process.env.VERIFYMAIL_KEY! });
9
+
10
+ const result = await vm.check("user@example.com");
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
+ }
23
+ ```
24
+
25
+ ## Install
26
+
27
+ ```bash
28
+ npm install verifymailapi
29
+ # or
30
+ pnpm add verifymailapi
31
+ ```
32
+
33
+ Requires Node 18+ (uses native `fetch`). Works in any modern server runtime — Node, Bun, Deno, Vercel Functions, Cloudflare Workers.
34
+
35
+ ## API
36
+
37
+ ### `new VerifyMail(options)`
38
+
39
+ | Option | Default | Description |
40
+ |---|---|---|
41
+ | `apiKey` | (required) | Your `dc_…` API key from the dashboard. |
42
+ | `baseUrl` | `https://api.verifymailapi.com` | Override for staging. |
43
+ | `retries` | `2` | Retries on 429 / 5xx with backoff. Set to 0 to disable. |
44
+ | `timeoutMs` | `30000` | Per-request timeout. |
45
+ | `riskProfile` | (server default) | `"strict"` / `"balanced"` / `"permissive"`. Per-call override available. |
46
+ | `fetch` | `globalThis.fetch` | Inject a custom fetch (tests, edge runtimes). |
47
+
48
+ ### `vm.check(email, opts?)`
49
+
50
+ Checks a single email. Returns a `CheckResponse` (the five-block schema).
51
+
52
+ ```ts
53
+ const r = await vm.check("user@example.com", { idempotencyKey: true });
54
+ console.log(r.verdict.recommendation, r.score.value);
55
+ ```
56
+
57
+ ### `vm.checkDomain(domain, opts?)`
58
+
59
+ Domain-only check (skips syntax validation). Same 1-credit cost.
60
+
61
+ ### `vm.checkBulk(emails[], opts?)`
62
+
63
+ Submit 1–100 emails. Charges N credits up front, all-or-nothing.
64
+
65
+ ```ts
66
+ const { items, summary } = await vm.checkBulk(["a@x.com", "b@x.com"]);
67
+ // items[i] matches emails[i] (order preserved).
68
+ ```
69
+
70
+ ### `vm.checkBulkStream(emails[], onEvent, opts?)`
71
+
72
+ For large batches (5k–100k addresses). Calls `onEvent` once per finished row + once with a `{ event: "summary" }` final line. Results arrive in finish order; correlate via `index`.
73
+
74
+ ```ts
75
+ await vm.checkBulkStream(emails, (e) => {
76
+ if ("event" in e) {
77
+ console.log("done — credits remaining:", e.credits_remaining);
78
+ } else {
79
+ processRow(e.index, e.result);
80
+ }
81
+ });
82
+ ```
83
+
84
+ ### `vm.checkAsync({ email, webhookUrl, webhookSecret? })`
85
+
86
+ Two-phase verification. Returns a 202 immediately with a preliminary verdict; the final result is POSTed to your webhook URL after the deep SMTP probe completes.
87
+
88
+ ```ts
89
+ const { request_id, preliminary } = await vm.checkAsync({
90
+ email: "user@example.com",
91
+ webhookUrl: "https://your-app.example/webhooks/verifymail",
92
+ webhookSecret: process.env.VERIFYMAIL_WEBHOOK_SECRET,
93
+ });
94
+ ```
95
+
96
+ In your webhook handler, verify the signature with `verifyWebhook()`:
97
+
98
+ ```ts
99
+ import express from "express";
100
+ import { verifyWebhook } from "verifymailapi";
101
+
102
+ app.post(
103
+ "/webhooks/verifymail",
104
+ express.raw({ type: "application/json" }),
105
+ (req, res) => {
106
+ const sig = req.header("X-VerifyMail-Signature") ?? "";
107
+ if (!verifyWebhook(req.body, sig, process.env.VERIFYMAIL_WEBHOOK_SECRET!)) {
108
+ return res.status(401).send("bad signature");
109
+ }
110
+ const event = JSON.parse(req.body.toString("utf8"));
111
+ // event.result is the final CheckResponse.
112
+ res.status(200).end();
113
+ },
114
+ );
115
+ ```
116
+
117
+ ### `vm.report({ domain, outcome, notes? })`
118
+
119
+ Tell us when a domain turned out to be confirmed throwaway / legitimate / suspected.
120
+
121
+ ### `vm.usage()`
122
+
123
+ Returns the same shape the dashboard reads — current-period totals + remaining credit balance.
124
+
125
+ ### `vm.status()`
126
+
127
+ Per-component health (Redis, Postgres, DNS). Always 200; read individual fields.
128
+
129
+ ## Errors
130
+
131
+ ```ts
132
+ import {
133
+ VerifyMailError,
134
+ InvalidApiKeyError,
135
+ QuotaExceededError,
136
+ RateLimitError,
137
+ IdempotencyConflictError,
138
+ ValidationError,
139
+ ServiceDegradedError,
140
+ } from "verifymailapi";
141
+
142
+ try {
143
+ await vm.check(email);
144
+ } catch (e) {
145
+ if (e instanceof QuotaExceededError) return showBilling(e.upgradeUrl);
146
+ if (e instanceof RateLimitError) return retryLater(e.retryAfter);
147
+ if (e instanceof VerifyMailError) return logAndShowGeneric(e);
148
+ throw e;
149
+ }
150
+ ```
151
+
152
+ All errors carry `code`, `status`, `requestId`, `docsUrl`, and the original `body` payload when available.
153
+
154
+ ## Idempotency
155
+
156
+ Pass `idempotencyKey: true` to auto-generate a UUID v4, or pass a fixed string you choose. Within 24 hours the same key replays the cached response with no duplicate charge. Re-using a key with a different body throws `IdempotencyConflictError`.
157
+
158
+ ## License
159
+
160
+ MIT
package/dist/index.cjs ADDED
@@ -0,0 +1,402 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ IdempotencyConflictError: () => IdempotencyConflictError,
24
+ InvalidApiKeyError: () => InvalidApiKeyError,
25
+ QuotaExceededError: () => QuotaExceededError,
26
+ RateLimitError: () => RateLimitError,
27
+ ServiceDegradedError: () => ServiceDegradedError,
28
+ ValidationError: () => ValidationError,
29
+ VerifyMail: () => VerifyMail,
30
+ VerifyMailError: () => VerifyMailError,
31
+ default: () => index_default,
32
+ errorFromResponse: () => errorFromResponse,
33
+ verifyWebhook: () => verifyWebhook
34
+ });
35
+ module.exports = __toCommonJS(index_exports);
36
+
37
+ // src/errors.ts
38
+ var VerifyMailError = class extends Error {
39
+ code;
40
+ status;
41
+ requestId;
42
+ docsUrl;
43
+ body;
44
+ constructor(message, opts = {}) {
45
+ super(message);
46
+ this.name = "VerifyMailError";
47
+ this.code = opts.code ?? "verifymail_error";
48
+ this.status = opts.status ?? 0;
49
+ this.requestId = opts.requestId;
50
+ this.docsUrl = opts.docsUrl;
51
+ this.body = opts.body;
52
+ }
53
+ };
54
+ var InvalidApiKeyError = class extends VerifyMailError {
55
+ constructor(opts) {
56
+ super("API key is missing or invalid.", opts);
57
+ this.name = "InvalidApiKeyError";
58
+ }
59
+ };
60
+ var QuotaExceededError = class extends VerifyMailError {
61
+ upgradeUrl;
62
+ constructor(opts) {
63
+ super("Out of credits. Buy a bundle to keep going.", opts);
64
+ this.name = "QuotaExceededError";
65
+ this.upgradeUrl = opts.body?.upgrade_url;
66
+ }
67
+ };
68
+ var RateLimitError = class extends VerifyMailError {
69
+ retryAfter;
70
+ limit;
71
+ resetAt;
72
+ constructor(opts) {
73
+ super(`Rate limit hit; retry after ${opts.retryAfter}s.`, opts);
74
+ this.name = "RateLimitError";
75
+ this.retryAfter = opts.retryAfter;
76
+ this.limit = opts.body?.limit;
77
+ this.resetAt = opts.body?.reset_at;
78
+ }
79
+ };
80
+ var IdempotencyConflictError = class extends VerifyMailError {
81
+ constructor(opts) {
82
+ super(
83
+ "Idempotency-Key was reused with a different request body. Use a new key or resend the original payload.",
84
+ opts
85
+ );
86
+ this.name = "IdempotencyConflictError";
87
+ }
88
+ };
89
+ var ValidationError = class extends VerifyMailError {
90
+ constructor(message, opts) {
91
+ super(message, opts);
92
+ this.name = "ValidationError";
93
+ }
94
+ };
95
+ var ServiceDegradedError = class extends VerifyMailError {
96
+ constructor(opts) {
97
+ super("A component is degraded; retry shortly.", opts);
98
+ this.name = "ServiceDegradedError";
99
+ }
100
+ };
101
+ function errorFromResponse(status, body, retryAfterHeader) {
102
+ const envelope = body && typeof body === "object" && "error" in body ? body.error : void 0;
103
+ const code = envelope?.code ?? "verifymail_error";
104
+ const opts = {
105
+ code,
106
+ status,
107
+ requestId: envelope?.request_id,
108
+ docsUrl: envelope?.docs_url,
109
+ body: envelope
110
+ };
111
+ if (status === 401) return new InvalidApiKeyError(opts);
112
+ if (status === 402) return new QuotaExceededError(opts);
113
+ if (status === 409 && code === "invalid_idempotency_key")
114
+ return new IdempotencyConflictError(opts);
115
+ if (status === 422) return new ValidationError(envelope?.message ?? "Validation error.", opts);
116
+ if (status === 429) {
117
+ const retryAfter = Number(retryAfterHeader ?? envelope?.limit ?? 1);
118
+ return new RateLimitError({ ...opts, retryAfter: Number.isFinite(retryAfter) ? retryAfter : 1 });
119
+ }
120
+ if (status === 503 || status === 504) return new ServiceDegradedError(opts);
121
+ return new VerifyMailError(envelope?.message ?? `HTTP ${status}`, opts);
122
+ }
123
+
124
+ // src/client.ts
125
+ var RETRYABLE_STATUS = /* @__PURE__ */ new Set([408, 429, 500, 502, 503, 504]);
126
+ function uuidv4() {
127
+ const c = globalThis.crypto;
128
+ if (c?.randomUUID) return c.randomUUID();
129
+ const buf = new Uint8Array(16);
130
+ for (let i = 0; i < 16; i++) buf[i] = Math.floor(Math.random() * 256);
131
+ buf[6] = buf[6] & 15 | 64;
132
+ buf[8] = buf[8] & 63 | 128;
133
+ const hex = Array.from(buf, (b) => b.toString(16).padStart(2, "0")).join("");
134
+ return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
135
+ }
136
+ var HttpClient = class {
137
+ apiKey;
138
+ baseUrl;
139
+ retries;
140
+ timeoutMs;
141
+ fetchImpl;
142
+ defaultRiskProfile;
143
+ constructor(opts) {
144
+ if (!opts.apiKey) throw new VerifyMailError("apiKey is required.");
145
+ this.apiKey = opts.apiKey;
146
+ this.baseUrl = (opts.baseUrl ?? "https://api.verifymailapi.com").replace(/\/+$/, "");
147
+ this.retries = opts.retries ?? 2;
148
+ this.timeoutMs = opts.timeoutMs ?? 3e4;
149
+ this.fetchImpl = opts.fetch ?? globalThis.fetch;
150
+ this.defaultRiskProfile = opts.riskProfile;
151
+ if (!this.fetchImpl) {
152
+ throw new VerifyMailError(
153
+ "No fetch available. Pass `fetch` in the options, or use Node 18+."
154
+ );
155
+ }
156
+ }
157
+ /** Build the headers shared by every request. */
158
+ buildHeaders(opts) {
159
+ const h = new Headers(opts.headers);
160
+ h.set("X-API-Key", this.apiKey);
161
+ h.set("Accept", "application/json");
162
+ h.set("User-Agent", "verifymailapi-js/0.1.0");
163
+ if (opts.body !== void 0) h.set("Content-Type", "application/json");
164
+ const profile = opts.riskProfile ?? this.defaultRiskProfile;
165
+ if (profile) h.set("X-Risk-Profile", profile);
166
+ if (opts.idempotencyKey) {
167
+ h.set(
168
+ "Idempotency-Key",
169
+ typeof opts.idempotencyKey === "string" ? opts.idempotencyKey : uuidv4()
170
+ );
171
+ }
172
+ return h;
173
+ }
174
+ buildUrl(path, query) {
175
+ const url = new URL(`${this.baseUrl}${path}`);
176
+ if (query) {
177
+ for (const [k, v] of Object.entries(query)) {
178
+ if (v !== void 0) url.searchParams.set(k, String(v));
179
+ }
180
+ }
181
+ return url.toString();
182
+ }
183
+ /** Send and decode a JSON request. Retries on 429 / 5xx up to `retries` times. */
184
+ async json(opts) {
185
+ const raw = await this.raw(opts);
186
+ if (raw.status === 204) return void 0;
187
+ const text = await raw.text();
188
+ try {
189
+ return JSON.parse(text);
190
+ } catch {
191
+ throw new VerifyMailError(`Non-JSON response from ${opts.path}.`, {
192
+ status: raw.status
193
+ });
194
+ }
195
+ }
196
+ /**
197
+ * Send a request and return the raw Response. Handles retries + error mapping.
198
+ * Used internally; exposed for the streaming bulk method to consume the body.
199
+ */
200
+ async raw(opts) {
201
+ const url = this.buildUrl(opts.path, opts.query);
202
+ const headers = this.buildHeaders(opts);
203
+ const body = opts.body !== void 0 ? JSON.stringify(opts.body) : void 0;
204
+ const method = opts.method ?? (body ? "POST" : "GET");
205
+ const maxTries = opts.noRetry ? 1 : this.retries + 1;
206
+ const timeoutMs = opts.timeoutMs ?? this.timeoutMs;
207
+ let lastError;
208
+ for (let attempt = 0; attempt < maxTries; attempt++) {
209
+ const controller = new AbortController();
210
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
211
+ let res;
212
+ try {
213
+ res = await this.fetchImpl(url, {
214
+ method,
215
+ headers,
216
+ body,
217
+ signal: controller.signal
218
+ });
219
+ } catch (err2) {
220
+ clearTimeout(timer);
221
+ lastError = new VerifyMailError(
222
+ err2 instanceof Error ? err2.message : "Network error",
223
+ { code: "network_error" }
224
+ );
225
+ if (attempt < maxTries - 1) {
226
+ await sleep(backoffMs(attempt));
227
+ continue;
228
+ }
229
+ throw lastError;
230
+ }
231
+ clearTimeout(timer);
232
+ if (res.ok) return res;
233
+ let parsed = void 0;
234
+ try {
235
+ parsed = await res.clone().json();
236
+ } catch {
237
+ }
238
+ const err = errorFromResponse(res.status, parsed, res.headers.get("Retry-After"));
239
+ const isRetryable = !opts.noRetry && RETRYABLE_STATUS.has(res.status) && attempt < maxTries - 1;
240
+ if (isRetryable) {
241
+ const wait = err instanceof RateLimitError ? err.retryAfter * 1e3 : err instanceof ServiceDegradedError ? backoffMs(attempt) : backoffMs(attempt);
242
+ await sleep(wait);
243
+ lastError = err;
244
+ continue;
245
+ }
246
+ throw err;
247
+ }
248
+ throw lastError ?? new VerifyMailError("Request failed after retries.");
249
+ }
250
+ };
251
+ function sleep(ms) {
252
+ return new Promise((r) => setTimeout(r, ms));
253
+ }
254
+ function backoffMs(attempt) {
255
+ return Math.min(250 * Math.pow(3, attempt), 1e4);
256
+ }
257
+
258
+ // src/webhooks.ts
259
+ var import_node_crypto = require("crypto");
260
+ function verifyWebhook(rawBody, signatureHeader, secret) {
261
+ if (!signatureHeader) return false;
262
+ const body = typeof rawBody === "string" ? Buffer.from(rawBody, "utf8") : Buffer.from(rawBody);
263
+ const expected = "sha256=" + (0, import_node_crypto.createHmac)("sha256", secret).update(body).digest("hex");
264
+ const a = Buffer.from(signatureHeader);
265
+ const b = Buffer.from(expected);
266
+ return a.length === b.length && (0, import_node_crypto.timingSafeEqual)(a, b);
267
+ }
268
+
269
+ // src/index.ts
270
+ var VerifyMail = class {
271
+ http;
272
+ constructor(opts) {
273
+ this.http = new HttpClient(opts);
274
+ }
275
+ /** Check a single email. Charges 1 credit. */
276
+ check(email, opts = {}) {
277
+ return this.http.json({
278
+ path: "/v1/check",
279
+ body: { email },
280
+ idempotencyKey: opts.idempotencyKey,
281
+ riskProfile: opts.riskProfile
282
+ });
283
+ }
284
+ /** Check a domain only (no local part). Charges 1 credit. */
285
+ checkDomain(domain, opts = {}) {
286
+ return this.http.json({
287
+ path: "/v1/check/domain",
288
+ body: { domain },
289
+ idempotencyKey: opts.idempotencyKey,
290
+ riskProfile: opts.riskProfile
291
+ });
292
+ }
293
+ /** Bulk check 1–100 emails. Charges N credits up front (all-or-nothing). */
294
+ checkBulk(emails, opts = {}) {
295
+ if (emails.length === 0 || emails.length > 100) {
296
+ throw new RangeError("checkBulk requires 1\u2013100 emails.");
297
+ }
298
+ return this.http.json({
299
+ path: "/v1/check/bulk",
300
+ body: { emails },
301
+ idempotencyKey: opts.idempotencyKey,
302
+ riskProfile: opts.riskProfile
303
+ });
304
+ }
305
+ /**
306
+ * Stream bulk-check results as each row completes. Calls `onEvent` once
307
+ * per result (in finish-order) plus once with a final `{event: "summary"}`.
308
+ *
309
+ * await vm.checkBulkStream(emails, (e) => {
310
+ * if ("event" in e) console.log("done", e);
311
+ * else processRow(e.index, e.result);
312
+ * });
313
+ */
314
+ async checkBulkStream(emails, onEvent, opts = {}) {
315
+ if (emails.length === 0) throw new RangeError("checkBulkStream needs at least one email.");
316
+ const req = {
317
+ path: "/v1/check/bulk/stream",
318
+ body: { emails },
319
+ riskProfile: opts.riskProfile,
320
+ // Streaming requests aren't safe to auto-retry — would re-charge credits
321
+ // and re-stream rows. Customer must retry explicitly.
322
+ noRetry: true
323
+ };
324
+ const res = await this.http.raw(req);
325
+ if (!res.body) throw new Error("Streaming response had no body.");
326
+ const reader = res.body.getReader();
327
+ const decoder = new TextDecoder();
328
+ let buf = "";
329
+ while (true) {
330
+ const { value, done } = await reader.read();
331
+ if (done) break;
332
+ buf += decoder.decode(value, { stream: true });
333
+ let nl;
334
+ while ((nl = buf.indexOf("\n")) !== -1) {
335
+ const line = buf.slice(0, nl).trim();
336
+ buf = buf.slice(nl + 1);
337
+ if (!line) continue;
338
+ try {
339
+ await onEvent(JSON.parse(line));
340
+ } catch (err) {
341
+ throw new Error(`Failed to parse stream line: ${line}`);
342
+ }
343
+ }
344
+ }
345
+ const tail = buf.trim();
346
+ if (tail) await onEvent(JSON.parse(tail));
347
+ }
348
+ /**
349
+ * Async deep check. Returns 202 immediately with a preliminary verdict;
350
+ * VerifyMail POSTs the final result to your webhook URL once the deep
351
+ * SMTP probe completes. Verify the signature with `verifyWebhook()` in
352
+ * your handler before trusting the payload.
353
+ */
354
+ checkAsync(args, opts = {}) {
355
+ return this.http.json({
356
+ path: "/v1/check/async",
357
+ body: {
358
+ email: args.email,
359
+ webhook_url: args.webhookUrl,
360
+ webhook_secret: args.webhookSecret
361
+ },
362
+ idempotencyKey: opts.idempotencyKey,
363
+ riskProfile: opts.riskProfile
364
+ });
365
+ }
366
+ /** File a /v1/report for a domain outcome (feedback loop). */
367
+ report(req) {
368
+ return this.http.json({
369
+ path: "/v1/report",
370
+ body: req
371
+ });
372
+ }
373
+ /** Programmatic equivalent of the dashboard's Usage summary. */
374
+ usage() {
375
+ return this.http.json({
376
+ path: "/v1/usage/me",
377
+ method: "GET"
378
+ });
379
+ }
380
+ /** Component health (Redis / Postgres / DNS). Always returns 200. */
381
+ status() {
382
+ return this.http.json({
383
+ path: "/v1/status",
384
+ method: "GET"
385
+ });
386
+ }
387
+ };
388
+ var index_default = VerifyMail;
389
+ // Annotate the CommonJS export names for ESM import in node:
390
+ 0 && (module.exports = {
391
+ IdempotencyConflictError,
392
+ InvalidApiKeyError,
393
+ QuotaExceededError,
394
+ RateLimitError,
395
+ ServiceDegradedError,
396
+ ValidationError,
397
+ VerifyMail,
398
+ VerifyMailError,
399
+ errorFromResponse,
400
+ verifyWebhook
401
+ });
402
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +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"]}