@verifnow/sdk 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,250 @@
1
+ /**
2
+ * Validation rules exposed by the VerifNow API.
3
+ *
4
+ * Each maps to `POST /api/v1/validate/{rule}`.
5
+ */
6
+ type ValidationRule = 'email' | 'phone' | 'iban' | 'vat' | 'nas' | 'ssn' | 'nif';
7
+ declare const VALIDATION_RULES: readonly ValidationRule[];
8
+ /**
9
+ * Depth of checks applied to a request, decided by the plan attached to the API key.
10
+ *
11
+ * `STANDARD` runs on the FREE and STARTER plans, `ADVANCED` on GROWTH, `PREMIUM` on PRO.
12
+ * Branch on this rather than on the plan name: it is the only value that tells you which
13
+ * signals are actually present in the response.
14
+ */
15
+ type ValidationLevel = 'BASIC' | 'STANDARD' | 'ADVANCED' | 'PREMIUM';
16
+ /** Categorical risk assessment. Returned from `ADVANCED` depth upward. */
17
+ type RiskLevel = 'LOW' | 'MEDIUM' | 'HIGH';
18
+ type Deliverability = 'DELIVERABLE' | 'RISKY' | 'UNDELIVERABLE' | 'UNKNOWN';
19
+ /**
20
+ * Per-signal breakdown behind an email verdict.
21
+ *
22
+ * Fields are `undefined` when the applied level does not compute them — the last four require
23
+ * `ADVANCED` depth or higher. Check `ValidationResult.appliedLevel` before relying on one.
24
+ */
25
+ interface EmailSignals {
26
+ /** The address matches the syntax pattern for the applied level. */
27
+ syntaxValid?: boolean;
28
+ /** The domain resolves and publishes MX (or fallback A) records. */
29
+ mxValid?: boolean;
30
+ /** A likely typo was found in the domain, e.g. `gmail.con`. */
31
+ typoDetected?: boolean;
32
+ /** The correction proposed when `typoDetected` is true. */
33
+ suggestedDomain?: string;
34
+ /** The domain belongs to a throwaway mailbox provider. */
35
+ disposable?: boolean;
36
+ /** The local part is a shared mailbox: `info@`, `admin@`, `noreply@`. */
37
+ roleBased?: boolean;
38
+ /** The domain is a consumer mailbox provider. Requires ADVANCED. */
39
+ freeProvider?: boolean;
40
+ /** Estimated age of the domain in days. Requires ADVANCED. */
41
+ domainAgeDays?: number;
42
+ /** Identified mail provider, e.g. `google`. Requires ADVANCED. */
43
+ mxProvider?: string;
44
+ /** Mail server quality between 0 and 1. Requires ADVANCED. */
45
+ mxQualityScore?: number;
46
+ }
47
+ /** Email-specific diagnostics. Absent when the applied level is `BASIC`. */
48
+ interface EmailDetails {
49
+ signals?: EmailSignals;
50
+ /** Aggregated risk on a 0–100 scale, where 0 is the lowest risk. */
51
+ riskScore?: number;
52
+ /** Categorical risk. Requires ADVANCED depth or higher. */
53
+ riskLevel?: RiskLevel;
54
+ deliverability?: Deliverability;
55
+ /** The depth actually applied, echoed back by the API. */
56
+ appliedLevel?: ValidationLevel;
57
+ }
58
+ /** Outcome of a single validation call. */
59
+ interface ValidationResult {
60
+ /** Whether the value passed every check the applied level ran. */
61
+ valid: boolean;
62
+ /** Human-readable explanation of the verdict. */
63
+ message?: string;
64
+ /** Canonical form of the input — `null` when the value is invalid. */
65
+ normalizedValue: string | null;
66
+ /** The value exactly as submitted. */
67
+ originalValue?: string;
68
+ /** Depth applied to this request. */
69
+ validationLevel?: ValidationLevel;
70
+ /** Present for email validations from `STANDARD` depth upward. */
71
+ emailDetails?: EmailDetails;
72
+ /** Quota state reported by the response headers. */
73
+ quota?: QuotaInfo;
74
+ /** The unmodified JSON body, for fields this SDK version does not model yet. */
75
+ raw: Record<string, unknown>;
76
+ }
77
+ /** Quota counters read from the `X-RateLimit-*` response headers. */
78
+ interface QuotaInfo {
79
+ /** Validations included in the current billing period. */
80
+ limit?: number;
81
+ /** Validations left before overage or blocking. */
82
+ remaining?: number;
83
+ /** When the current period resets. */
84
+ resetAt?: Date;
85
+ /** True once you are past the included quota and into per-unit billing. */
86
+ overage?: boolean;
87
+ }
88
+ interface RetryOptions {
89
+ /**
90
+ * Retry attempts after the first failure. Defaults to 2, so up to 3 requests in total.
91
+ * Only connection failures, 429 and 5xx are retried — never a 400 or 401, which will not
92
+ * succeed on a second try.
93
+ */
94
+ attempts?: number;
95
+ /** Delay before the first retry, in ms. Doubles each attempt. Defaults to 200. */
96
+ backoffMs?: number;
97
+ /** Upper bound on a single backoff delay, in ms. Defaults to 2000. */
98
+ maxBackoffMs?: number;
99
+ }
100
+ interface VerifNowOptions {
101
+ /** API key created in the VerifNow dashboard. Sent as the `X-API-KEY` header. */
102
+ apiKey: string;
103
+ /** Override the API origin. Defaults to `https://api.verifnow.io`. */
104
+ baseUrl?: string;
105
+ /** Abort a single request after this many ms. Defaults to 5000. */
106
+ timeoutMs?: number;
107
+ /** Retry policy, or `false` to disable retries entirely. */
108
+ retry?: RetryOptions | false;
109
+ /** Extra headers merged into every request. */
110
+ headers?: Record<string, string>;
111
+ /**
112
+ * Replacement for the global `fetch`, for tests or a custom agent.
113
+ * Defaults to `globalThis.fetch`.
114
+ */
115
+ fetch?: typeof globalThis.fetch;
116
+ }
117
+
118
+ /** Per-call overrides. */
119
+ interface RequestOptions {
120
+ /** Override the client timeout for this call. */
121
+ timeoutMs?: number;
122
+ /** Cancel the call from your own controller. Combined with the timeout. */
123
+ signal?: AbortSignal;
124
+ }
125
+ /**
126
+ * Client for the VerifNow validation API.
127
+ *
128
+ * @example
129
+ * ```ts
130
+ * import { VerifNow } from '@verifnow/sdk';
131
+ *
132
+ * const client = new VerifNow({ apiKey: process.env.VERIFNOW_API_KEY! });
133
+ * const result = await client.validateEmail('user@example.com');
134
+ *
135
+ * if (!result.valid) console.log(result.message);
136
+ * if (result.emailDetails?.signals?.typoDetected) {
137
+ * console.log('Did you mean', result.emailDetails.signals.suggestedDomain);
138
+ * }
139
+ * ```
140
+ */
141
+ declare class VerifNow {
142
+ #private;
143
+ constructor(options: VerifNowOptions);
144
+ /** Validate an email address: syntax, DNS/MX, typo, disposable, role-based, quality score. */
145
+ validateEmail(value: string, options?: RequestOptions): Promise<ValidationResult>;
146
+ /** Validate a phone number in international format. */
147
+ validatePhone(value: string, options?: RequestOptions): Promise<ValidationResult>;
148
+ /** Validate an IBAN: country structure and check digits. */
149
+ validateIban(value: string, options?: RequestOptions): Promise<ValidationResult>;
150
+ /** Validate a VAT number. */
151
+ validateVat(value: string, options?: RequestOptions): Promise<ValidationResult>;
152
+ /** Validate a Canadian Social Insurance Number. */
153
+ validateNas(value: string, options?: RequestOptions): Promise<ValidationResult>;
154
+ /** Validate a US Social Security Number. */
155
+ validateSsn(value: string, options?: RequestOptions): Promise<ValidationResult>;
156
+ /** Validate a Spanish/Portuguese NIF. */
157
+ validateNif(value: string, options?: RequestOptions): Promise<ValidationResult>;
158
+ /**
159
+ * Validate a value against any rule.
160
+ *
161
+ * The typed helpers above call this. Use it directly when the rule is chosen at runtime.
162
+ */
163
+ validate(rule: ValidationRule, value: string, options?: RequestOptions): Promise<ValidationResult>;
164
+ }
165
+
166
+ /**
167
+ * Base class for every error this SDK throws.
168
+ *
169
+ * The SDK fails loudly on purpose. A validation client that swallows a network failure and
170
+ * reports `valid: true` turns an outage into silently accepted bad data, and the outage stays
171
+ * invisible until someone audits the database. Catch these and decide explicitly — accepting the
172
+ * input on failure is a reasonable choice, but it should be a choice.
173
+ *
174
+ * @example
175
+ * ```ts
176
+ * try {
177
+ * const result = await client.validateEmail(input);
178
+ * return result.valid;
179
+ * } catch (error) {
180
+ * if (error instanceof VerifNowRateLimitError) throw error; // back-pressure, do not swallow
181
+ * logger.warn({ error }, 'VerifNow unavailable, accepting input unverified');
182
+ * return true;
183
+ * }
184
+ * ```
185
+ */
186
+ declare class VerifNowError extends Error {
187
+ /** HTTP status, when the failure came back from the API rather than the network. */
188
+ readonly status?: number;
189
+ /** Correlation id from the `X-Request-Id` response header, useful in support requests. */
190
+ readonly requestId?: string;
191
+ constructor(message: string, options?: {
192
+ status?: number;
193
+ requestId?: string;
194
+ cause?: unknown;
195
+ });
196
+ }
197
+ /** The API key is missing, malformed, revoked, or not authorised for this endpoint (401/403). */
198
+ declare class VerifNowAuthError extends VerifNowError {
199
+ }
200
+ /**
201
+ * The request was rejected as malformed (400).
202
+ *
203
+ * Retrying is pointless — the payload itself needs to change.
204
+ */
205
+ declare class VerifNowRequestError extends VerifNowError {
206
+ }
207
+ /** The monthly quota or the concurrency limit was exceeded (429). */
208
+ declare class VerifNowRateLimitError extends VerifNowError {
209
+ /** Quota counters from the response headers, when present. */
210
+ readonly quota?: QuotaInfo;
211
+ /** Seconds to wait before retrying, derived from `Retry-After` or `X-RateLimit-Reset`. */
212
+ readonly retryAfterSeconds?: number;
213
+ constructor(message: string, options?: {
214
+ status?: number;
215
+ requestId?: string;
216
+ cause?: unknown;
217
+ quota?: QuotaInfo;
218
+ retryAfterSeconds?: number;
219
+ });
220
+ }
221
+ /** The API failed to process the request (5xx). Retried automatically before surfacing. */
222
+ declare class VerifNowServerError extends VerifNowError {
223
+ }
224
+ /**
225
+ * The API could not be reached at all: DNS failure, refused connection, TLS error, or the
226
+ * request exceeded `timeoutMs`.
227
+ *
228
+ * A wrong `baseUrl` surfaces here, which is why it names the URL it tried.
229
+ */
230
+ declare class VerifNowConnectionError extends VerifNowError {
231
+ /** True when the failure was the client-side timeout rather than a transport error. */
232
+ readonly timedOut: boolean;
233
+ constructor(message: string, options?: {
234
+ cause?: unknown;
235
+ timedOut?: boolean;
236
+ });
237
+ }
238
+ /** The API returned a success status with a body this SDK could not parse. */
239
+ declare class VerifNowResponseError extends VerifNowError {
240
+ }
241
+
242
+ /**
243
+ * SDK version, sent to the API as `X-VerifNow-SDK: node/<version>` so calls made through an
244
+ * official SDK can be told apart from hand-rolled integrations.
245
+ *
246
+ * Kept in sync with `package.json` by a test — bump both together.
247
+ */
248
+ declare const VERSION = "1.0.0";
249
+
250
+ export { type Deliverability, type EmailDetails, type EmailSignals, type QuotaInfo, type RequestOptions, type RetryOptions, type RiskLevel, VALIDATION_RULES, VERSION, type ValidationLevel, type ValidationResult, type ValidationRule, VerifNow, VerifNowAuthError, VerifNowConnectionError, VerifNowError, type VerifNowOptions, VerifNowRateLimitError, VerifNowRequestError, VerifNowResponseError, VerifNowServerError };
@@ -0,0 +1,250 @@
1
+ /**
2
+ * Validation rules exposed by the VerifNow API.
3
+ *
4
+ * Each maps to `POST /api/v1/validate/{rule}`.
5
+ */
6
+ type ValidationRule = 'email' | 'phone' | 'iban' | 'vat' | 'nas' | 'ssn' | 'nif';
7
+ declare const VALIDATION_RULES: readonly ValidationRule[];
8
+ /**
9
+ * Depth of checks applied to a request, decided by the plan attached to the API key.
10
+ *
11
+ * `STANDARD` runs on the FREE and STARTER plans, `ADVANCED` on GROWTH, `PREMIUM` on PRO.
12
+ * Branch on this rather than on the plan name: it is the only value that tells you which
13
+ * signals are actually present in the response.
14
+ */
15
+ type ValidationLevel = 'BASIC' | 'STANDARD' | 'ADVANCED' | 'PREMIUM';
16
+ /** Categorical risk assessment. Returned from `ADVANCED` depth upward. */
17
+ type RiskLevel = 'LOW' | 'MEDIUM' | 'HIGH';
18
+ type Deliverability = 'DELIVERABLE' | 'RISKY' | 'UNDELIVERABLE' | 'UNKNOWN';
19
+ /**
20
+ * Per-signal breakdown behind an email verdict.
21
+ *
22
+ * Fields are `undefined` when the applied level does not compute them — the last four require
23
+ * `ADVANCED` depth or higher. Check `ValidationResult.appliedLevel` before relying on one.
24
+ */
25
+ interface EmailSignals {
26
+ /** The address matches the syntax pattern for the applied level. */
27
+ syntaxValid?: boolean;
28
+ /** The domain resolves and publishes MX (or fallback A) records. */
29
+ mxValid?: boolean;
30
+ /** A likely typo was found in the domain, e.g. `gmail.con`. */
31
+ typoDetected?: boolean;
32
+ /** The correction proposed when `typoDetected` is true. */
33
+ suggestedDomain?: string;
34
+ /** The domain belongs to a throwaway mailbox provider. */
35
+ disposable?: boolean;
36
+ /** The local part is a shared mailbox: `info@`, `admin@`, `noreply@`. */
37
+ roleBased?: boolean;
38
+ /** The domain is a consumer mailbox provider. Requires ADVANCED. */
39
+ freeProvider?: boolean;
40
+ /** Estimated age of the domain in days. Requires ADVANCED. */
41
+ domainAgeDays?: number;
42
+ /** Identified mail provider, e.g. `google`. Requires ADVANCED. */
43
+ mxProvider?: string;
44
+ /** Mail server quality between 0 and 1. Requires ADVANCED. */
45
+ mxQualityScore?: number;
46
+ }
47
+ /** Email-specific diagnostics. Absent when the applied level is `BASIC`. */
48
+ interface EmailDetails {
49
+ signals?: EmailSignals;
50
+ /** Aggregated risk on a 0–100 scale, where 0 is the lowest risk. */
51
+ riskScore?: number;
52
+ /** Categorical risk. Requires ADVANCED depth or higher. */
53
+ riskLevel?: RiskLevel;
54
+ deliverability?: Deliverability;
55
+ /** The depth actually applied, echoed back by the API. */
56
+ appliedLevel?: ValidationLevel;
57
+ }
58
+ /** Outcome of a single validation call. */
59
+ interface ValidationResult {
60
+ /** Whether the value passed every check the applied level ran. */
61
+ valid: boolean;
62
+ /** Human-readable explanation of the verdict. */
63
+ message?: string;
64
+ /** Canonical form of the input — `null` when the value is invalid. */
65
+ normalizedValue: string | null;
66
+ /** The value exactly as submitted. */
67
+ originalValue?: string;
68
+ /** Depth applied to this request. */
69
+ validationLevel?: ValidationLevel;
70
+ /** Present for email validations from `STANDARD` depth upward. */
71
+ emailDetails?: EmailDetails;
72
+ /** Quota state reported by the response headers. */
73
+ quota?: QuotaInfo;
74
+ /** The unmodified JSON body, for fields this SDK version does not model yet. */
75
+ raw: Record<string, unknown>;
76
+ }
77
+ /** Quota counters read from the `X-RateLimit-*` response headers. */
78
+ interface QuotaInfo {
79
+ /** Validations included in the current billing period. */
80
+ limit?: number;
81
+ /** Validations left before overage or blocking. */
82
+ remaining?: number;
83
+ /** When the current period resets. */
84
+ resetAt?: Date;
85
+ /** True once you are past the included quota and into per-unit billing. */
86
+ overage?: boolean;
87
+ }
88
+ interface RetryOptions {
89
+ /**
90
+ * Retry attempts after the first failure. Defaults to 2, so up to 3 requests in total.
91
+ * Only connection failures, 429 and 5xx are retried — never a 400 or 401, which will not
92
+ * succeed on a second try.
93
+ */
94
+ attempts?: number;
95
+ /** Delay before the first retry, in ms. Doubles each attempt. Defaults to 200. */
96
+ backoffMs?: number;
97
+ /** Upper bound on a single backoff delay, in ms. Defaults to 2000. */
98
+ maxBackoffMs?: number;
99
+ }
100
+ interface VerifNowOptions {
101
+ /** API key created in the VerifNow dashboard. Sent as the `X-API-KEY` header. */
102
+ apiKey: string;
103
+ /** Override the API origin. Defaults to `https://api.verifnow.io`. */
104
+ baseUrl?: string;
105
+ /** Abort a single request after this many ms. Defaults to 5000. */
106
+ timeoutMs?: number;
107
+ /** Retry policy, or `false` to disable retries entirely. */
108
+ retry?: RetryOptions | false;
109
+ /** Extra headers merged into every request. */
110
+ headers?: Record<string, string>;
111
+ /**
112
+ * Replacement for the global `fetch`, for tests or a custom agent.
113
+ * Defaults to `globalThis.fetch`.
114
+ */
115
+ fetch?: typeof globalThis.fetch;
116
+ }
117
+
118
+ /** Per-call overrides. */
119
+ interface RequestOptions {
120
+ /** Override the client timeout for this call. */
121
+ timeoutMs?: number;
122
+ /** Cancel the call from your own controller. Combined with the timeout. */
123
+ signal?: AbortSignal;
124
+ }
125
+ /**
126
+ * Client for the VerifNow validation API.
127
+ *
128
+ * @example
129
+ * ```ts
130
+ * import { VerifNow } from '@verifnow/sdk';
131
+ *
132
+ * const client = new VerifNow({ apiKey: process.env.VERIFNOW_API_KEY! });
133
+ * const result = await client.validateEmail('user@example.com');
134
+ *
135
+ * if (!result.valid) console.log(result.message);
136
+ * if (result.emailDetails?.signals?.typoDetected) {
137
+ * console.log('Did you mean', result.emailDetails.signals.suggestedDomain);
138
+ * }
139
+ * ```
140
+ */
141
+ declare class VerifNow {
142
+ #private;
143
+ constructor(options: VerifNowOptions);
144
+ /** Validate an email address: syntax, DNS/MX, typo, disposable, role-based, quality score. */
145
+ validateEmail(value: string, options?: RequestOptions): Promise<ValidationResult>;
146
+ /** Validate a phone number in international format. */
147
+ validatePhone(value: string, options?: RequestOptions): Promise<ValidationResult>;
148
+ /** Validate an IBAN: country structure and check digits. */
149
+ validateIban(value: string, options?: RequestOptions): Promise<ValidationResult>;
150
+ /** Validate a VAT number. */
151
+ validateVat(value: string, options?: RequestOptions): Promise<ValidationResult>;
152
+ /** Validate a Canadian Social Insurance Number. */
153
+ validateNas(value: string, options?: RequestOptions): Promise<ValidationResult>;
154
+ /** Validate a US Social Security Number. */
155
+ validateSsn(value: string, options?: RequestOptions): Promise<ValidationResult>;
156
+ /** Validate a Spanish/Portuguese NIF. */
157
+ validateNif(value: string, options?: RequestOptions): Promise<ValidationResult>;
158
+ /**
159
+ * Validate a value against any rule.
160
+ *
161
+ * The typed helpers above call this. Use it directly when the rule is chosen at runtime.
162
+ */
163
+ validate(rule: ValidationRule, value: string, options?: RequestOptions): Promise<ValidationResult>;
164
+ }
165
+
166
+ /**
167
+ * Base class for every error this SDK throws.
168
+ *
169
+ * The SDK fails loudly on purpose. A validation client that swallows a network failure and
170
+ * reports `valid: true` turns an outage into silently accepted bad data, and the outage stays
171
+ * invisible until someone audits the database. Catch these and decide explicitly — accepting the
172
+ * input on failure is a reasonable choice, but it should be a choice.
173
+ *
174
+ * @example
175
+ * ```ts
176
+ * try {
177
+ * const result = await client.validateEmail(input);
178
+ * return result.valid;
179
+ * } catch (error) {
180
+ * if (error instanceof VerifNowRateLimitError) throw error; // back-pressure, do not swallow
181
+ * logger.warn({ error }, 'VerifNow unavailable, accepting input unverified');
182
+ * return true;
183
+ * }
184
+ * ```
185
+ */
186
+ declare class VerifNowError extends Error {
187
+ /** HTTP status, when the failure came back from the API rather than the network. */
188
+ readonly status?: number;
189
+ /** Correlation id from the `X-Request-Id` response header, useful in support requests. */
190
+ readonly requestId?: string;
191
+ constructor(message: string, options?: {
192
+ status?: number;
193
+ requestId?: string;
194
+ cause?: unknown;
195
+ });
196
+ }
197
+ /** The API key is missing, malformed, revoked, or not authorised for this endpoint (401/403). */
198
+ declare class VerifNowAuthError extends VerifNowError {
199
+ }
200
+ /**
201
+ * The request was rejected as malformed (400).
202
+ *
203
+ * Retrying is pointless — the payload itself needs to change.
204
+ */
205
+ declare class VerifNowRequestError extends VerifNowError {
206
+ }
207
+ /** The monthly quota or the concurrency limit was exceeded (429). */
208
+ declare class VerifNowRateLimitError extends VerifNowError {
209
+ /** Quota counters from the response headers, when present. */
210
+ readonly quota?: QuotaInfo;
211
+ /** Seconds to wait before retrying, derived from `Retry-After` or `X-RateLimit-Reset`. */
212
+ readonly retryAfterSeconds?: number;
213
+ constructor(message: string, options?: {
214
+ status?: number;
215
+ requestId?: string;
216
+ cause?: unknown;
217
+ quota?: QuotaInfo;
218
+ retryAfterSeconds?: number;
219
+ });
220
+ }
221
+ /** The API failed to process the request (5xx). Retried automatically before surfacing. */
222
+ declare class VerifNowServerError extends VerifNowError {
223
+ }
224
+ /**
225
+ * The API could not be reached at all: DNS failure, refused connection, TLS error, or the
226
+ * request exceeded `timeoutMs`.
227
+ *
228
+ * A wrong `baseUrl` surfaces here, which is why it names the URL it tried.
229
+ */
230
+ declare class VerifNowConnectionError extends VerifNowError {
231
+ /** True when the failure was the client-side timeout rather than a transport error. */
232
+ readonly timedOut: boolean;
233
+ constructor(message: string, options?: {
234
+ cause?: unknown;
235
+ timedOut?: boolean;
236
+ });
237
+ }
238
+ /** The API returned a success status with a body this SDK could not parse. */
239
+ declare class VerifNowResponseError extends VerifNowError {
240
+ }
241
+
242
+ /**
243
+ * SDK version, sent to the API as `X-VerifNow-SDK: node/<version>` so calls made through an
244
+ * official SDK can be told apart from hand-rolled integrations.
245
+ *
246
+ * Kept in sync with `package.json` by a test — bump both together.
247
+ */
248
+ declare const VERSION = "1.0.0";
249
+
250
+ export { type Deliverability, type EmailDetails, type EmailSignals, type QuotaInfo, type RequestOptions, type RetryOptions, type RiskLevel, VALIDATION_RULES, VERSION, type ValidationLevel, type ValidationResult, type ValidationRule, VerifNow, VerifNowAuthError, VerifNowConnectionError, VerifNowError, type VerifNowOptions, VerifNowRateLimitError, VerifNowRequestError, VerifNowResponseError, VerifNowServerError };