@verifnow/sdk 1.8.0 → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +22 -1
- package/dist/index.cjs +26 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +33 -4
- package/dist/index.d.ts +33 -4
- package/dist/index.js +26 -8
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -78,6 +78,23 @@ if (vat.registered === null) {
|
|
|
78
78
|
return accept({ verified: true, stale: vat.source === 'STALE' });
|
|
79
79
|
```
|
|
80
80
|
|
|
81
|
+
### Does the number belong to this company?
|
|
82
|
+
|
|
83
|
+
Pass the name you expect — from a supplier form, for instance — and the response says whether it
|
|
84
|
+
matches the registered holder:
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
const result = await client.validateVat('ESA28015865', { traderName: 'Telefonica' });
|
|
88
|
+
|
|
89
|
+
result.vatDetails?.traderNameMatch; // 'MATCH' | 'MISMATCH' | 'NOT_AVAILABLE'
|
|
90
|
+
result.vatDetails?.traderNameMatchSource; // 'VERIFNOW' | 'VIES'
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Who compares depends on the member state. Where VIES publishes the holder's name (most of them),
|
|
94
|
+
VerifNow compares, ignoring case, accents, punctuation and legal forms. Spain publishes no name but
|
|
95
|
+
has VIES check one. Germany does neither, and the answer is `NOT_AVAILABLE` rather than a guess. A
|
|
96
|
+
`MISMATCH` is a question for a human, not proof of fraud.
|
|
97
|
+
|
|
81
98
|
Per-country VIES availability is public and needs no API key:
|
|
82
99
|
[`GET /api/v1/status/vies`](https://www.verifnow.io/en/status).
|
|
83
100
|
|
|
@@ -92,13 +109,17 @@ const france = await client.vatRate('FR'); // GR is accepted for Greece (EL)
|
|
|
92
109
|
|
|
93
110
|
france.standardRate; // 20
|
|
94
111
|
france.reducedRates; // [2.1, 5.5, 10] — which one applies depends on the product
|
|
95
|
-
france.regionalRates; // [{ rate: 8.5, note: 'The standard VAT rate in Martinique, …' }, …]
|
|
112
|
+
france.regionalRates; // [{ rate: 8.5, note: 'The standard VAT rate in Martinique, …', euVatArea: false }, …]
|
|
96
113
|
france.situationOn; // '2026-07-01' — the date TEDB says these rates apply from
|
|
97
114
|
france.fetchedAt; // Date — when VerifNow last retrieved them
|
|
98
115
|
|
|
99
116
|
const all = await client.vatRates(); // all.rates: one entry per member state
|
|
100
117
|
```
|
|
101
118
|
|
|
119
|
+
`euVatArea: false` marks the Canary Islands and the French overseas territories, which the VAT
|
|
120
|
+
Directive excludes: goods shipped there from another member state are an export, not a distance
|
|
121
|
+
sale at that rate.
|
|
122
|
+
|
|
102
123
|
**These are the rates a member state has, not the rate an invoice carries.** In B2B trade between
|
|
103
124
|
member states the invoice is usually zero-rated under the reverse charge, whatever the buyer's
|
|
104
125
|
country rate is. Multiplying an amount by the buyer's standard rate is wrong in exactly the case a
|
package/dist/index.cjs
CHANGED
|
@@ -76,7 +76,7 @@ var VerifNowResponseError = class extends VerifNowError {
|
|
|
76
76
|
};
|
|
77
77
|
|
|
78
78
|
// src/version.ts
|
|
79
|
-
var VERSION = "1.
|
|
79
|
+
var VERSION = "1.9.0";
|
|
80
80
|
|
|
81
81
|
// src/client.ts
|
|
82
82
|
var DEFAULT_BASE_URL = "https://api.verifnow.io";
|
|
@@ -134,9 +134,18 @@ var VerifNow = class {
|
|
|
134
134
|
validateIban(value, options) {
|
|
135
135
|
return this.validate("iban", value, options);
|
|
136
136
|
}
|
|
137
|
-
/**
|
|
138
|
-
|
|
139
|
-
|
|
137
|
+
/**
|
|
138
|
+
* Validate a VAT number.
|
|
139
|
+
*
|
|
140
|
+
* Pass `traderName` to ask whether the number belongs to that company:
|
|
141
|
+
* `vatDetails.traderNameMatch` answers `MATCH`, `MISMATCH` or `NOT_AVAILABLE`, and
|
|
142
|
+
* `traderNameMatchSource` says who compared — VerifNow against the name VIES publishes, or VIES
|
|
143
|
+
* itself where it withholds the name but checks one (Spain). Germany does neither.
|
|
144
|
+
*/
|
|
145
|
+
validateVat(value, options = {}) {
|
|
146
|
+
const { traderName, ...requestOptions } = options;
|
|
147
|
+
const extra = traderName && traderName.trim() !== "" ? { traderName } : void 0;
|
|
148
|
+
return this.#validate("vat", value, requestOptions, extra);
|
|
140
149
|
}
|
|
141
150
|
/**
|
|
142
151
|
* Validate a Canadian Social Insurance Number: format and Luhn check digit.
|
|
@@ -170,14 +179,17 @@ var VerifNow = class {
|
|
|
170
179
|
*
|
|
171
180
|
* The typed helpers above call this. Use it directly when the rule is chosen at runtime.
|
|
172
181
|
*/
|
|
173
|
-
|
|
182
|
+
validate(rule, value, options = {}) {
|
|
183
|
+
return this.#validate(rule, value, options);
|
|
184
|
+
}
|
|
185
|
+
async #validate(rule, value, options, extra) {
|
|
174
186
|
if (typeof value !== "string" || value.trim() === "") {
|
|
175
187
|
throw new VerifNowRequestError(
|
|
176
188
|
`Cannot validate an empty value for rule "${rule}".`
|
|
177
189
|
);
|
|
178
190
|
}
|
|
179
191
|
const url = `${this.#baseUrl}/api/v1/validate/${rule}`;
|
|
180
|
-
const body = JSON.stringify({ value });
|
|
192
|
+
const body = JSON.stringify({ value, ...extra });
|
|
181
193
|
return this.#withRetry(
|
|
182
194
|
() => this.#requestOnce("POST", url, body, options, (payload, quota) => mapResult(payload, quota))
|
|
183
195
|
);
|
|
@@ -436,7 +448,9 @@ function mapVatDetails(raw) {
|
|
|
436
448
|
traderName: asString(d.trader_name),
|
|
437
449
|
traderAddress: asString(d.trader_address),
|
|
438
450
|
viesAvailable: asBoolean(d.vies_available),
|
|
439
|
-
consultationNumber: asString(d.consultation_number)
|
|
451
|
+
consultationNumber: asString(d.consultation_number),
|
|
452
|
+
traderNameMatch: asString(d.trader_name_match),
|
|
453
|
+
traderNameMatchSource: asString(d.trader_name_match_source)
|
|
440
454
|
};
|
|
441
455
|
}
|
|
442
456
|
function mapPhoneDetails(raw) {
|
|
@@ -495,7 +509,11 @@ function mapCountryVatRates(raw) {
|
|
|
495
509
|
countryCode: asString(raw.countryCode) ?? "",
|
|
496
510
|
standardRate: asNumber(raw.standardRate) ?? Number.NaN,
|
|
497
511
|
reducedRates: numbers(raw.reducedRates),
|
|
498
|
-
regionalRates: Array.isArray(raw.regionalRates) ? raw.regionalRates.filter((r) => r !== null && typeof r === "object").map((r) => ({
|
|
512
|
+
regionalRates: Array.isArray(raw.regionalRates) ? raw.regionalRates.filter((r) => r !== null && typeof r === "object").map((r) => ({
|
|
513
|
+
rate: asNumber(r.rate) ?? Number.NaN,
|
|
514
|
+
note: asString(r.note),
|
|
515
|
+
euVatArea: asBoolean(r.euVatArea)
|
|
516
|
+
})) : [],
|
|
499
517
|
situationOn: asString(raw.situationOn),
|
|
500
518
|
fetchedAt: asDate(raw.fetchedAt)
|
|
501
519
|
};
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/errors.ts","../src/version.ts","../src/client.ts","../src/types.ts"],"sourcesContent":["export { VerifNow, type RequestOptions } from './client.js';\n\nexport {\n VerifNowError,\n VerifNowAuthError,\n VerifNowRequestError,\n VerifNowRateLimitError,\n VerifNowServerError,\n VerifNowConnectionError,\n VerifNowResponseError,\n} from './errors.js';\n\nexport {\n VALIDATION_RULES,\n type CountryVatRates,\n type Deliverability,\n type EmailDetails,\n type EmailSignals,\n type IbanDetails,\n type NasDetails,\n type NifDetails,\n type NifType,\n type SsnDetails,\n type PhoneDetails,\n type PhoneLineType,\n type QuotaInfo,\n type RegionalVatRate,\n type RetryOptions,\n type RiskLevel,\n type ValidationLevel,\n type ValidationResult,\n type ValidationRule,\n type VatDetails,\n type VatRates,\n type VatSource,\n type VerifNowOptions,\n} from './types.js';\n\nexport { VERSION } from './version.js';\n","import type { QuotaInfo } from './types.js';\n\n/**\n * Base class for every error this SDK throws.\n *\n * The SDK fails loudly on purpose. A validation client that swallows a network failure and\n * reports `valid: true` turns an outage into silently accepted bad data, and the outage stays\n * invisible until someone audits the database. Catch these and decide explicitly — accepting the\n * input on failure is a reasonable choice, but it should be a choice.\n *\n * @example\n * ```ts\n * try {\n * const result = await client.validateEmail(input);\n * return result.valid;\n * } catch (error) {\n * if (error instanceof VerifNowRateLimitError) throw error; // back-pressure, do not swallow\n * logger.warn({ error }, 'VerifNow unavailable, accepting input unverified');\n * return true;\n * }\n * ```\n */\nexport class VerifNowError extends Error {\n /** HTTP status, when the failure came back from the API rather than the network. */\n readonly status?: number;\n /** Correlation id from the `X-Request-Id` response header, useful in support requests. */\n readonly requestId?: string;\n\n constructor(\n message: string,\n options: { status?: number; requestId?: string; cause?: unknown } = {},\n ) {\n super(message, { cause: options.cause });\n this.name = new.target.name;\n this.status = options.status;\n this.requestId = options.requestId;\n Error.captureStackTrace?.(this, new.target);\n }\n}\n\n/** The API key is missing, malformed, revoked, or not authorised for this endpoint (401/403). */\nexport class VerifNowAuthError extends VerifNowError {}\n\n/**\n * The request was rejected as malformed (400).\n *\n * Retrying is pointless — the payload itself needs to change.\n */\nexport class VerifNowRequestError extends VerifNowError {}\n\n/** The monthly quota or the concurrency limit was exceeded (429). */\nexport class VerifNowRateLimitError extends VerifNowError {\n /** Quota counters from the response headers, when present. */\n readonly quota?: QuotaInfo;\n /** Seconds to wait before retrying, derived from `Retry-After` or `X-RateLimit-Reset`. */\n readonly retryAfterSeconds?: number;\n\n constructor(\n message: string,\n options: {\n status?: number;\n requestId?: string;\n cause?: unknown;\n quota?: QuotaInfo;\n retryAfterSeconds?: number;\n } = {},\n ) {\n super(message, options);\n this.quota = options.quota;\n this.retryAfterSeconds = options.retryAfterSeconds;\n }\n}\n\n/** The API failed to process the request (5xx). Retried automatically before surfacing. */\nexport class VerifNowServerError extends VerifNowError {}\n\n/**\n * The API could not be reached at all: DNS failure, refused connection, TLS error, or the\n * request exceeded `timeoutMs`.\n *\n * A wrong `baseUrl` surfaces here, which is why it names the URL it tried.\n */\nexport class VerifNowConnectionError extends VerifNowError {\n /** True when the failure was the client-side timeout rather than a transport error. */\n readonly timedOut: boolean;\n\n constructor(\n message: string,\n options: { cause?: unknown; timedOut?: boolean } = {},\n ) {\n super(message, { cause: options.cause });\n this.timedOut = options.timedOut ?? false;\n }\n}\n\n/** The API returned a success status with a body this SDK could not parse. */\nexport class VerifNowResponseError extends VerifNowError {}\n","/**\n * SDK version, sent to the API as `X-VerifNow-SDK: node/<version>` so calls made through an\n * official SDK can be told apart from hand-rolled integrations.\n *\n * Kept in sync with `package.json` by a test — bump both together.\n */\nexport const VERSION = '1.8.0';\n","import {\n VerifNowAuthError,\n VerifNowConnectionError,\n VerifNowError,\n VerifNowRateLimitError,\n VerifNowRequestError,\n VerifNowResponseError,\n VerifNowServerError,\n} from './errors.js';\nimport type {\n CountryVatRates,\n EmailDetails,\n EmailSignals,\n IbanDetails,\n NasDetails,\n NifDetails,\n SsnDetails,\n PhoneDetails,\n QuotaInfo,\n RetryOptions,\n ValidationResult,\n ValidationRule,\n VatDetails,\n VatRates,\n VerifNowOptions,\n} from './types.js';\nimport { VERSION } from './version.js';\n\nconst DEFAULT_BASE_URL = 'https://api.verifnow.io';\nconst DEFAULT_TIMEOUT_MS = 5_000;\nconst DEFAULT_RETRY: Required<RetryOptions> = {\n attempts: 2,\n backoffMs: 200,\n maxBackoffMs: 2_000,\n};\n\n/** Per-call overrides. */\nexport interface RequestOptions {\n /** Override the client timeout for this call. */\n timeoutMs?: number;\n /** Cancel the call from your own controller. Combined with the timeout. */\n signal?: AbortSignal;\n}\n\n/**\n * Client for the VerifNow validation API.\n *\n * @example\n * ```ts\n * import { VerifNow } from '@verifnow/sdk';\n *\n * const client = new VerifNow({ apiKey: process.env.VERIFNOW_API_KEY! });\n * const result = await client.validateEmail('user@example.com');\n *\n * if (!result.valid) console.log(result.message);\n * if (result.emailDetails?.signals?.typoDetected) {\n * console.log('Did you mean', result.emailDetails.signals.suggestedDomain);\n * }\n * ```\n */\nexport class VerifNow {\n readonly #apiKey: string;\n readonly #baseUrl: string;\n readonly #timeoutMs: number;\n readonly #retry: Required<RetryOptions> | null;\n readonly #headers: Record<string, string>;\n readonly #fetch: typeof globalThis.fetch;\n\n constructor(options: VerifNowOptions) {\n if (!options?.apiKey || options.apiKey.trim() === '') {\n throw new VerifNowError(\n 'A VerifNow API key is required. Create one in the dashboard and pass it as `apiKey`.',\n );\n }\n\n const fetchImpl = options.fetch ?? globalThis.fetch;\n if (typeof fetchImpl !== 'function') {\n throw new VerifNowError(\n 'No global fetch available. Use Node 18 or later, or pass a `fetch` implementation.',\n );\n }\n\n this.#apiKey = options.apiKey.trim();\n // Trailing slashes would produce `//api/v1/...`, which some proxies reject.\n this.#baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/, '');\n this.#timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n this.#retry =\n options.retry === false ? null : { ...DEFAULT_RETRY, ...(options.retry ?? {}) };\n this.#headers = options.headers ?? {};\n this.#fetch = fetchImpl.bind(globalThis);\n }\n\n /** Validate an email address: syntax, DNS/MX, typo, disposable, role-based, quality score. */\n validateEmail(value: string, options?: RequestOptions): Promise<ValidationResult> {\n return this.validate('email', value, options);\n }\n\n /**\n * Validate a phone number against its country's numbering plan.\n *\n * The number must include its country code (`+33…` or `0033…`). Valid numbers come back in\n * E.164 as `normalizedValue`, with country and line type in `phoneDetails`.\n */\n validatePhone(value: string, options?: RequestOptions): Promise<ValidationResult> {\n return this.validate('phone', value, options);\n }\n\n /**\n * Validate an IBAN against the SWIFT registry entry for its country, then its check digits.\n *\n * `ibanDetails` reports the two separately: check digits catch a typo, the registry catches an\n * account number that could never exist in that country.\n */\n validateIban(value: string, options?: RequestOptions): Promise<ValidationResult> {\n return this.validate('iban', value, options);\n }\n\n /** Validate a VAT number. */\n validateVat(value: string, options?: RequestOptions): Promise<ValidationResult> {\n return this.validate('vat', value, options);\n }\n\n /**\n * Validate a Canadian Social Insurance Number: format and Luhn check digit.\n *\n * `nasDetails` flags a temporary resident's number (it expires with their permit) and numbers\n * from series not issued to individuals. Only collect a SIN where the law requires it.\n */\n validateNas(value: string, options?: RequestOptions): Promise<ValidationResult> {\n return this.validate('nas', value, options);\n }\n\n /**\n * Validate a US Social Security Number against the numbers the SSA never issues.\n *\n * An SSN has no check digit: a typo that lands on another possible number cannot be caught, and\n * only the SSA can confirm a number was issued. `ssnDetails.itin` flags an IRS ITIN.\n */\n validateSsn(value: string, options?: RequestOptions): Promise<ValidationResult> {\n return this.validate('ssn', value, options);\n }\n\n /**\n * Validate a Spanish NIF: a DNI, a NIE (foreign nationals), the K/L/M series, or a company NIF.\n *\n * `nifDetails` says which, whether it belongs to a person, and for a company its legal form.\n * Spanish only — a Portuguese NIF is a different scheme and is not accepted here.\n */\n validateNif(value: string, options?: RequestOptions): Promise<ValidationResult> {\n return this.validate('nif', value, options);\n }\n\n /**\n * Validate a value against any rule.\n *\n * The typed helpers above call this. Use it directly when the rule is chosen at runtime.\n */\n async validate(\n rule: ValidationRule,\n value: string,\n options: RequestOptions = {},\n ): Promise<ValidationResult> {\n if (typeof value !== 'string' || value.trim() === '') {\n // Caught here rather than server-side: an empty value consumes quota and can only fail.\n throw new VerifNowRequestError(\n `Cannot validate an empty value for rule \"${rule}\".`,\n );\n }\n\n const url = `${this.#baseUrl}/api/v1/validate/${rule}`;\n const body = JSON.stringify({ value });\n return this.#withRetry(() =>\n this.#requestOnce('POST', url, body, options, (payload, quota) => mapResult(payload, quota)),\n );\n }\n\n /**\n * EU VAT rates of every member state, from the European Commission's TEDB.\n *\n * Public reference data: the call spends no quota. These are the rates a member state has, not\n * the rate a sale is charged — in B2B trade between member states the invoice is usually\n * zero-rated under the reverse charge whatever the buyer's country rate is.\n */\n async vatRates(options: RequestOptions = {}): Promise<VatRates> {\n const url = `${this.#baseUrl}/api/v1/vat/rates`;\n return this.#withRetry(() =>\n this.#requestOnce('GET', url, undefined, options, (payload) => mapVatRates(payload)),\n );\n }\n\n /**\n * One EU member state's VAT rates. Accepts `GR` for Greece as well as `EL`.\n *\n * A code outside the 27 member states throws {@link VerifNowRequestError} (HTTP 404).\n */\n async vatRate(countryCode: string, options: RequestOptions = {}): Promise<CountryVatRates> {\n if (typeof countryCode !== 'string' || countryCode.trim() === '') {\n throw new VerifNowRequestError('A member state code is required, e.g. \"FR\".');\n }\n const url = `${this.#baseUrl}/api/v1/vat/rates/${encodeURIComponent(countryCode.trim())}`;\n return this.#withRetry(() =>\n this.#requestOnce('GET', url, undefined, options, (payload) => mapCountryVatRates(payload)),\n );\n }\n\n /** Runs one request under the retry policy. */\n async #withRetry<T>(attemptOnce: () => Promise<T>): Promise<T> {\n const maxAttempts = this.#retry ? this.#retry.attempts + 1 : 1;\n\n let lastError: VerifNowError | undefined;\n\n for (let attempt = 0; attempt < maxAttempts; attempt++) {\n try {\n return await attemptOnce();\n } catch (error) {\n if (!(error instanceof VerifNowError)) throw error;\n lastError = error;\n\n const isLastAttempt = attempt === maxAttempts - 1;\n if (isLastAttempt || !this.#retry) throw error;\n\n const delay = this.#retryDelay(error, attempt);\n if (delay === null) throw error;\n\n await sleep(delay);\n }\n }\n\n /* c8 ignore next -- the loop either returns or throws */\n throw lastError ?? new VerifNowError('Request failed');\n }\n\n /**\n * How long to wait before retrying, or `null` when the error should surface immediately.\n *\n * A 429 is retried only when the reset is close: the concurrency limit clears in\n * milliseconds, but a spent monthly quota does not, and sleeping on it helps nobody.\n */\n #retryDelay(error: VerifNowError, attempt: number): number | null {\n const retry = this.#retry!;\n const backoff = Math.min(retry.backoffMs * 2 ** attempt, retry.maxBackoffMs);\n\n if (error instanceof VerifNowRateLimitError) {\n const waitMs = (error.retryAfterSeconds ?? 0) * 1000;\n if (waitMs > retry.maxBackoffMs) return null;\n return Math.max(waitMs, backoff);\n }\n\n if (error instanceof VerifNowServerError) return backoff;\n // A timeout is retried: the deadline is ours, and the next attempt gets a fresh one.\n if (error instanceof VerifNowConnectionError) return backoff;\n\n // 400, 401 and unparseable bodies will fail identically on a second attempt.\n return null;\n }\n\n async #requestOnce<T>(\n method: 'GET' | 'POST',\n url: string,\n body: string | undefined,\n options: RequestOptions,\n map: (payload: Record<string, unknown>, quota: QuotaInfo | undefined) => T,\n ): Promise<T> {\n const timeoutMs = options.timeoutMs ?? this.#timeoutMs;\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n const abortFromCaller = () => controller.abort();\n options.signal?.addEventListener('abort', abortFromCaller, { once: true });\n\n let response: Response;\n try {\n response = await this.#fetch(url, {\n method,\n headers: {\n ...this.#headers,\n ...(body === undefined ? {} : { 'Content-Type': 'application/json' }),\n Accept: 'application/json',\n 'X-API-KEY': this.#apiKey,\n 'X-VerifNow-SDK': `node/${VERSION}`,\n },\n body,\n signal: controller.signal,\n });\n } catch (cause) {\n // The caller's own cancellation is theirs to handle, not a transport failure.\n if (options.signal?.aborted) throw cause;\n\n const timedOut = controller.signal.aborted;\n throw new VerifNowConnectionError(\n timedOut\n ? `VerifNow request to ${url} timed out after ${timeoutMs}ms.`\n : `Could not reach the VerifNow API at ${url}. Check \\`baseUrl\\` and network access.`,\n { cause, timedOut },\n );\n } finally {\n clearTimeout(timer);\n options.signal?.removeEventListener('abort', abortFromCaller);\n }\n\n return this.#handleResponse(response, map);\n }\n\n async #handleResponse<T>(\n response: Response,\n map: (payload: Record<string, unknown>, quota: QuotaInfo | undefined) => T,\n ): Promise<T> {\n const requestId = response.headers.get('X-Request-Id') ?? undefined;\n const quota = parseQuota(response.headers);\n\n if (response.ok) {\n let payload: unknown;\n try {\n payload = await response.json();\n } catch (cause) {\n throw new VerifNowResponseError(\n 'VerifNow returned a success status with a body that is not valid JSON.',\n { status: response.status, requestId, cause },\n );\n }\n\n if (payload === null || typeof payload !== 'object') {\n throw new VerifNowResponseError(\n 'VerifNow returned an unexpected response shape.',\n { status: response.status, requestId },\n );\n }\n\n return map(payload as Record<string, unknown>, quota);\n }\n\n const message = await readErrorMessage(response);\n const context = { status: response.status, requestId };\n\n if (response.status === 401 || response.status === 403) {\n throw new VerifNowAuthError(\n `VerifNow rejected the API key (${response.status}): ${message}`,\n context,\n );\n }\n\n if (response.status === 429) {\n throw new VerifNowRateLimitError(`VerifNow rate limit reached: ${message}`, {\n ...context,\n quota,\n retryAfterSeconds: parseRetryAfter(response.headers, quota),\n });\n }\n\n if (response.status >= 500) {\n throw new VerifNowServerError(\n `VerifNow returned ${response.status}: ${message}`,\n context,\n );\n }\n\n throw new VerifNowRequestError(\n `VerifNow rejected the request (${response.status}): ${message}`,\n context,\n );\n }\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nfunction toNumber(value: string | null): number | undefined {\n if (value === null) return undefined;\n const parsed = Number(value);\n return Number.isFinite(parsed) ? parsed : undefined;\n}\n\nfunction parseQuota(headers: Headers): QuotaInfo | undefined {\n const limit = toNumber(headers.get('X-RateLimit-Limit'));\n const remaining = toNumber(headers.get('X-RateLimit-Remaining'));\n const resetSeconds = toNumber(headers.get('X-RateLimit-Reset'));\n const overage = headers.get('X-Quota-Overage') === 'true';\n\n if (\n limit === undefined &&\n remaining === undefined &&\n resetSeconds === undefined &&\n !overage\n ) {\n return undefined;\n }\n\n return {\n limit,\n remaining,\n resetAt: resetSeconds === undefined ? undefined : new Date(resetSeconds * 1000),\n overage,\n };\n}\n\nfunction parseRetryAfter(headers: Headers, quota?: QuotaInfo): number | undefined {\n const retryAfter = headers.get('Retry-After');\n if (retryAfter !== null) {\n const seconds = Number(retryAfter);\n if (Number.isFinite(seconds)) return seconds;\n\n // RFC 7231 also allows an HTTP-date.\n const asDate = Date.parse(retryAfter);\n if (!Number.isNaN(asDate)) {\n return Math.max(0, Math.ceil((asDate - Date.now()) / 1000));\n }\n }\n\n if (quota?.resetAt) {\n return Math.max(0, Math.ceil((quota.resetAt.getTime() - Date.now()) / 1000));\n }\n\n return undefined;\n}\n\nasync function readErrorMessage(response: Response): Promise<string> {\n try {\n const text = await response.text();\n if (!text) return response.statusText || 'no details';\n\n try {\n const parsed = JSON.parse(text) as Record<string, unknown>;\n const message = parsed.message ?? parsed.error;\n if (typeof message === 'string' && message !== '') return message;\n } catch {\n // Not JSON — a proxy or the servlet container's default error page.\n }\n\n return text.slice(0, 500);\n } catch {\n return response.statusText || 'no details';\n }\n}\n\nfunction asString(value: unknown): string | undefined {\n return typeof value === 'string' ? value : undefined;\n}\n\nfunction asBoolean(value: unknown): boolean | undefined {\n return typeof value === 'boolean' ? value : undefined;\n}\n\nfunction asNumber(value: unknown): number | undefined {\n return typeof value === 'number' && Number.isFinite(value) ? value : undefined;\n}\n\nfunction mapSignals(raw: unknown): EmailSignals | undefined {\n if (raw === null || typeof raw !== 'object') return undefined;\n const s = raw as Record<string, unknown>;\n\n return {\n syntaxValid: asBoolean(s.syntax_valid),\n mxValid: asBoolean(s.mx_valid),\n typoDetected: asBoolean(s.typo_detected),\n suggestedDomain: asString(s.suggested_domain),\n disposable: asBoolean(s.disposable),\n roleBased: asBoolean(s.role_based),\n freeProvider: asBoolean(s.free_provider),\n domainAgeDays: asNumber(s.domain_age_days),\n mxProvider: asString(s.mx_provider),\n mxQualityScore: asNumber(s.mx_quality_score),\n };\n}\n\nfunction mapEmailDetails(raw: unknown): EmailDetails | undefined {\n if (raw === null || typeof raw !== 'object') return undefined;\n const d = raw as Record<string, unknown>;\n\n return {\n signals: mapSignals(d.signals),\n riskScore: asNumber(d.risk_score),\n riskLevel: asString(d.risk_level) as EmailDetails['riskLevel'],\n deliverability: asString(d.deliverability) as EmailDetails['deliverability'],\n appliedLevel: asString(d.applied_level) as EmailDetails['appliedLevel'],\n };\n}\n\n/**\n * Reads `registered`, preserving the difference between `false` and `null`.\n *\n * `asBoolean` cannot be used here: it maps `null` to `undefined`, which would erase the one\n * distinction the whole VAT design exists to carry. `false` means the registry answered and the\n * number is not there; `null` means nobody could ask. A caller that cannot tell them apart will\n * reject real businesses whenever VIES is down.\n *\n * A missing key is read as `null` for the same reason — unknown, not absent.\n */\nfunction asRegistered(value: unknown): boolean | null {\n return typeof value === 'boolean' ? value : null;\n}\n\nfunction asDate(value: unknown): Date | undefined {\n if (typeof value !== 'string') return undefined;\n const parsed = new Date(value);\n return Number.isNaN(parsed.getTime()) ? undefined : parsed;\n}\n\nfunction mapVatDetails(raw: unknown): VatDetails | undefined {\n if (raw === null || typeof raw !== 'object') return undefined;\n const d = raw as Record<string, unknown>;\n\n return {\n formatValid: asBoolean(d.format_valid),\n registered: asRegistered(d.registered),\n countryCode: asString(d.country_code),\n source: asString(d.source) as VatDetails['source'],\n checkedAt: asDate(d.checked_at),\n traderName: asString(d.trader_name),\n traderAddress: asString(d.trader_address),\n viesAvailable: asBoolean(d.vies_available),\n consultationNumber: asString(d.consultation_number),\n };\n}\n\nfunction mapPhoneDetails(raw: unknown): PhoneDetails | undefined {\n if (raw === null || typeof raw !== 'object') return undefined;\n const d = raw as Record<string, unknown>;\n\n return {\n countryCode: asString(d.country_code),\n callingCode: asNumber(d.calling_code),\n lineType: asString(d.line_type) as PhoneDetails['lineType'],\n internationalFormat: asString(d.international_format),\n nationalFormat: asString(d.national_format),\n };\n}\n\nfunction mapIbanDetails(raw: unknown): IbanDetails | undefined {\n if (raw === null || typeof raw !== 'object') return undefined;\n const d = raw as Record<string, unknown>;\n\n return {\n countryCode: asString(d.country_code),\n sepa: asBoolean(d.sepa),\n structureValid: asBoolean(d.structure_valid),\n checksumValid: asBoolean(d.checksum_valid),\n length: asNumber(d.length),\n expectedLength: asNumber(d.expected_length),\n formatted: asString(d.formatted),\n };\n}\n\nfunction mapNasDetails(raw: unknown): NasDetails | undefined {\n if (raw === null || typeof raw !== 'object') return undefined;\n const d = raw as Record<string, unknown>;\n\n return {\n checksumValid: asBoolean(d.checksum_valid),\n temporaryResident: asBoolean(d.temporary_resident),\n individualSeries: asBoolean(d.individual_series),\n formatted: asString(d.formatted),\n };\n}\n\nfunction mapNifDetails(raw: unknown): NifDetails | undefined {\n if (raw === null || typeof raw !== 'object') return undefined;\n const d = raw as Record<string, unknown>;\n\n return {\n type: asString(d.type) as NifDetails['type'],\n naturalPerson: asBoolean(d.natural_person),\n checksumValid: asBoolean(d.checksum_valid),\n entityLetter: asString(d.entity_letter),\n entityType: asString(d.entity_type),\n };\n}\n\nfunction mapSsnDetails(raw: unknown): SsnDetails | undefined {\n if (raw === null || typeof raw !== 'object') return undefined;\n const d = raw as Record<string, unknown>;\n\n return { itin: asBoolean(d.itin) };\n}\n\n/**\n * Maps the API's snake_case diagnostics onto camelCase, so a TypeScript caller is not switching\n * naming conventions mid-expression. The untouched body stays available on `raw`.\n */\nfunction mapCountryVatRates(raw: Record<string, unknown>): CountryVatRates {\n const numbers = (value: unknown): number[] =>\n Array.isArray(value) ? value.filter((v): v is number => asNumber(v) !== undefined) : [];\n\n return {\n countryCode: asString(raw.countryCode) ?? '',\n standardRate: asNumber(raw.standardRate) ?? Number.NaN,\n reducedRates: numbers(raw.reducedRates),\n regionalRates: Array.isArray(raw.regionalRates)\n ? raw.regionalRates\n .filter((r): r is Record<string, unknown> => r !== null && typeof r === 'object')\n .map((r) => ({ rate: asNumber(r.rate) ?? Number.NaN, note: asString(r.note) }))\n : [],\n situationOn: asString(raw.situationOn),\n fetchedAt: asDate(raw.fetchedAt),\n };\n}\n\nfunction mapVatRates(raw: Record<string, unknown>): VatRates {\n const rates = Array.isArray(raw.rates)\n ? raw.rates\n .filter((r): r is Record<string, unknown> => r !== null && typeof r === 'object')\n .map(mapCountryVatRates)\n : [];\n return {\n source: asString(raw.source) ?? 'TEDB',\n sourceUrl: asString(raw.sourceUrl),\n rates,\n };\n}\n\nfunction mapResult(\n payload: Record<string, unknown>,\n quota: QuotaInfo | undefined,\n): ValidationResult {\n return {\n valid: payload.valid === true,\n message: asString(payload.message),\n normalizedValue: asString(payload.normalizedValue) ?? null,\n originalValue: asString(payload.originalValue),\n validationLevel: asString(payload.validationLevel) as ValidationResult['validationLevel'],\n emailDetails: mapEmailDetails(payload.emailDetails),\n vatDetails: mapVatDetails(payload.vatDetails),\n phoneDetails: mapPhoneDetails(payload.phoneDetails),\n ibanDetails: mapIbanDetails(payload.ibanDetails),\n nasDetails: mapNasDetails(payload.nasDetails),\n nifDetails: mapNifDetails(payload.nifDetails),\n ssnDetails: mapSsnDetails(payload.ssnDetails),\n quota,\n raw: payload,\n };\n}\n","/**\n * Validation rules exposed by the VerifNow API.\n *\n * Each maps to `POST /api/v1/validate/{rule}`.\n */\nexport type ValidationRule =\n | 'email'\n | 'phone'\n | 'iban'\n | 'vat'\n | 'nas'\n | 'ssn'\n | 'nif';\n\nexport const VALIDATION_RULES: readonly ValidationRule[] = [\n 'email',\n 'phone',\n 'iban',\n 'vat',\n 'nas',\n 'ssn',\n 'nif',\n] as const;\n\n/**\n * Depth of checks applied to a request, decided by the plan attached to the API key.\n *\n * `STANDARD` runs on the FREE and STARTER plans, `ADVANCED` on GROWTH, `PREMIUM` on PRO.\n * Branch on this rather than on the plan name: it is the only value that tells you which\n * signals are actually present in the response.\n */\nexport type ValidationLevel = 'BASIC' | 'STANDARD' | 'ADVANCED' | 'PREMIUM';\n\n/** Categorical risk assessment. Returned from `ADVANCED` depth upward. */\nexport type RiskLevel = 'LOW' | 'MEDIUM' | 'HIGH';\n\nexport type Deliverability =\n | 'DELIVERABLE'\n | 'RISKY'\n | 'UNDELIVERABLE'\n | 'UNKNOWN';\n\n/**\n * Per-signal breakdown behind an email verdict.\n *\n * Fields are `undefined` when the applied level does not compute them — the last four require\n * `ADVANCED` depth or higher. Check `ValidationResult.appliedLevel` before relying on one.\n */\nexport interface EmailSignals {\n /** The address matches the syntax pattern for the applied level. */\n syntaxValid?: boolean;\n /** The domain resolves and publishes MX (or fallback A) records. */\n mxValid?: boolean;\n /** A likely typo was found in the domain, e.g. `gmail.con`. */\n typoDetected?: boolean;\n /** The correction proposed when `typoDetected` is true. */\n suggestedDomain?: string;\n /** The domain belongs to a throwaway mailbox provider. */\n disposable?: boolean;\n /** The local part is a shared mailbox: `info@`, `admin@`, `noreply@`. */\n roleBased?: boolean;\n /** The domain is a consumer mailbox provider. Requires ADVANCED. */\n freeProvider?: boolean;\n /** Estimated age of the domain in days. Requires ADVANCED. */\n domainAgeDays?: number;\n /** Identified mail provider, e.g. `google`. Requires ADVANCED. */\n mxProvider?: string;\n /** Mail server quality between 0 and 1. Requires ADVANCED. */\n mxQualityScore?: number;\n}\n\n/** Email-specific diagnostics. Absent when the applied level is `BASIC`. */\nexport interface EmailDetails {\n signals?: EmailSignals;\n /** Aggregated risk on a 0–100 scale, where 0 is the lowest risk. */\n riskScore?: number;\n /** Categorical risk. Requires ADVANCED depth or higher. */\n riskLevel?: RiskLevel;\n deliverability?: Deliverability;\n /** The depth actually applied, echoed back by the API. */\n appliedLevel?: ValidationLevel;\n}\n\n/**\n * Where a VAT registration verdict came from.\n *\n * VIES publishes no SLA and drops member states several times a month, so a VAT answer is not\n * always a live one. Branch on this rather than on `ValidationResult.valid` whenever the\n * difference matters for your own compliance.\n */\nexport type VatSource =\n /** Confirmed against VIES during this request. */\n | 'LIVE'\n /** Served from a VIES answer less than 24 hours old. */\n | 'CACHE'\n /** VIES was unreachable, so an older cached answer was used. */\n | 'STALE'\n /** VIES was unreachable and nothing was cached. Registration is unknown. */\n | 'UNVERIFIED'\n /** The country is outside VIES, so no registry lookup is possible. */\n | 'NOT_APPLICABLE';\n\n/** VAT-specific diagnostics. Present on `vat` validations. */\nexport interface VatDetails {\n /** The number matches its member state's structure. Decided locally, never depends on VIES. */\n formatValid?: boolean;\n /**\n * Present in the member state's registry.\n *\n * **`null` means unknown, never \"not registered.\"** It is returned when VIES could not be\n * consulted. Treating `null` as `false` rejects legitimate customers during someone else's\n * outage — the single most expensive mistake available in VAT validation.\n */\n registered: boolean | null;\n /** Member state the number belongs to, e.g. `IE`. Greece is `EL`, Northern Ireland `XI`. */\n countryCode?: string;\n source?: VatSource;\n /** When the registration was last confirmed against VIES. */\n checkedAt?: Date;\n /** Registered trading name, when the member state discloses it. Germany does not. */\n traderName?: string;\n /** Registered address, when the member state discloses it. */\n traderAddress?: string;\n /** Whether VIES could answer for this country during the request. */\n viesAvailable?: boolean;\n /**\n * The consultation number VIES issued for this lookup — the receipt a tax authority accepts as\n * evidence that you checked. Present only when your account has its own VAT number configured,\n * because VIES issues one only to an identified requester.\n */\n consultationNumber?: string;\n}\n\n/**\n * Kind of line, according to the country's numbering plan.\n *\n * A `PREMIUM_RATE` or `VOIP` number is still `valid` — it exists. This is how you decide to\n * exclude one, rather than the API deciding for you.\n */\nexport type PhoneLineType =\n | 'MOBILE'\n | 'FIXED_LINE'\n /** The plan does not distinguish the two — the case for the US and Canada. */\n | 'FIXED_LINE_OR_MOBILE'\n | 'TOLL_FREE'\n | 'PREMIUM_RATE'\n | 'SHARED_COST'\n | 'VOIP'\n | 'PERSONAL_NUMBER'\n | 'PAGER'\n | 'UAN'\n | 'VOICEMAIL'\n | 'UNKNOWN';\n\n/**\n * Phone-specific diagnostics. Present whenever the input parsed as an international number —\n * including when it is invalid for its country, so you can tell the user which country it was\n * read as.\n */\nexport interface PhoneDetails {\n /** ISO 3166-1 alpha-2 country, e.g. `FR`. Absent when the calling code is shared by several. */\n countryCode?: string;\n /** International calling code without the plus sign, e.g. `33`. */\n callingCode?: number;\n /** Absent when the number is invalid. */\n lineType?: PhoneLineType;\n /** e.g. `+33 6 12 34 56 78`. Absent when the number is invalid. */\n internationalFormat?: string;\n /** e.g. `06 12 34 56 78`. Absent when the number is invalid. */\n nationalFormat?: string;\n}\n\n/**\n * IBAN-specific diagnostics. Present on `iban` validations.\n *\n * Structure and checksum are reported separately because they fail for different reasons:\n * `structureValid` answers \"could this be an account number in that country\" (the SWIFT\n * registry's length and layout), `checksumValid` answers \"was it typed correctly\" (mod-97).\n * There is no bank name or BIC — that needs a registry the API does not hold.\n */\nexport interface IbanDetails {\n /** The IBAN's country, from its first two characters. */\n countryCode?: string;\n /**\n * The country is inside the SEPA schemes' geographical scope, so a bank there may collect a\n * SEPA direct debit. Whether this particular bank does is published per bank, not per country,\n * and cannot be read from an IBAN.\n */\n sepa?: boolean;\n /** Length and character layout match the registry entry for that country. */\n structureValid?: boolean;\n /** The mod-97 check digits are correct. */\n checksumValid?: boolean;\n /** Length of the value as submitted, spaces removed. */\n length?: number;\n /** Length the registry requires for that country; absent for an unknown country. */\n expectedLength?: number;\n /** Print format, in groups of four. Present only for a valid IBAN. */\n formatted?: string;\n}\n\n/**\n * Canadian Social Insurance Number diagnostics. Present on `nas` validations.\n *\n * There is no province and no expiry date: the first digit no longer reliably identifies a\n * province, and a temporary resident's SIN expires with their permit, which only the document\n * shows.\n */\nexport interface NasDetails {\n /** The Luhn check digit is correct. */\n checksumValid?: boolean;\n /**\n * A 9-series number, issued to temporary residents. It expires with the holder's permit, and the\n * number itself does not say when — check the document.\n */\n temporaryResident?: boolean;\n /**\n * The first digit belongs to a series issued to individuals. `false` for numbers starting with\n * 0 or 8 — including 046 454 286, the government's sample number, which is why it is safe to\n * use in tests.\n */\n individualSeries?: boolean;\n /** Printed form, e.g. `046 454 286`. */\n formatted?: string;\n}\n\n/** The kinds of Spanish tax identification number. */\nexport type NifType =\n /** Spanish national with a DNI: 8 digits and a letter. */\n | 'DNI'\n /** Foreign national: X, Y or Z, 7 digits and a letter. */\n | 'NIE'\n /** Spanish national under 14 without a DNI. */\n | 'NIF_K'\n /** Spanish national resident abroad, staying under six months. */\n | 'NIF_L'\n /** Foreign national without a NIE. */\n | 'NIF_M'\n /** A company or other entity: a letter for the legal form, 7 digits, a control character. */\n | 'ENTITY';\n\n/** Spanish NIF diagnostics. Present on `nif` validations. */\nexport interface NifDetails {\n type?: NifType;\n /** The number belongs to a person rather than a company or other entity. */\n naturalPerson?: boolean;\n /** The control character is correct. */\n checksumValid?: boolean;\n /** For an entity, the letter that encodes its legal form, e.g. `B`. */\n entityLetter?: string;\n /** For an entity, its legal form, e.g. `Private limited company (Sociedad de responsabilidad limitada)`. */\n entityType?: string;\n}\n\n/**\n * US SSN diagnostics. Present on `ssn` validations.\n *\n * An SSN has no check digit, and since 2011 its first digits say nothing about a state, so there is\n * little a number can reveal about itself. The one thing worth knowing is whether it is an ITIN.\n */\nexport interface SsnDetails {\n /**\n * The number is an IRS ITIN, not an SSN: it starts with 9 and its fourth and fifth digits are in\n * 50-65, 70-88, 90-92 or 94-99. `valid` is `false` for the SSN, but an ITIN is an acceptable\n * taxpayer number where one is accepted (a W-9, for instance).\n */\n itin?: boolean;\n}\n\n/** Outcome of a single validation call. */\nexport interface ValidationResult {\n /** Whether the value passed every check the applied level ran. */\n valid: boolean;\n /** Human-readable explanation of the verdict. */\n message?: string;\n /** Canonical form of the input — `null` when the value is invalid. */\n normalizedValue: string | null;\n /** The value exactly as submitted. */\n originalValue?: string;\n /** Depth applied to this request. */\n validationLevel?: ValidationLevel;\n /** Present for email validations from `STANDARD` depth upward. */\n emailDetails?: EmailDetails;\n /** Present for VAT validations. */\n vatDetails?: VatDetails;\n /** Present for phone validations. The E.164 form is `normalizedValue`. */\n phoneDetails?: PhoneDetails;\n /** Present for IBAN validations. */\n ibanDetails?: IbanDetails;\n /** Present for Canadian SIN (`nas`) validations. */\n nasDetails?: NasDetails;\n /** Present for Spanish NIF validations. */\n nifDetails?: NifDetails;\n /** Present for US SSN validations. */\n ssnDetails?: SsnDetails;\n /** Quota state reported by the response headers. */\n quota?: QuotaInfo;\n /** The unmodified JSON body, for fields this SDK version does not model yet. */\n raw: Record<string, unknown>;\n}\n\n/** A VAT rate applying to part of a member state only — an overseas department, an island. */\nexport interface RegionalVatRate {\n rate: number;\n /** Where it applies, in the words of the Commission's TEDB. */\n note?: string;\n}\n\n/**\n * One EU member state's VAT rates, from the European Commission's TEDB.\n *\n * These are the rates the member state has, not the rate a sale is charged: which one applies\n * depends on who sells to whom and what. In B2B trade between member states the invoice is usually\n * zero-rated under the reverse charge, whatever the buyer's country rate is.\n */\nexport interface CountryVatRates {\n /** Member state as TEDB and VIES name it: Greece is `EL`. */\n countryCode: string;\n /** The national standard rate, e.g. `20` for France. */\n standardRate: number;\n /**\n * Every reduced, super-reduced and parking rate on the mainland territory, ascending.\n * TEDB's own sub-labels are inconsistent between member states, so they are not reproduced.\n */\n reducedRates: number[];\n /** Rates for part of the territory only, e.g. 8.5 % in Martinique, Guadeloupe and Réunion. */\n regionalRates: RegionalVatRate[];\n /**\n * The date TEDB says these rates apply from, as `YYYY-MM-DD`. Kept as a string: a date without\n * a time zone turned into a `Date` can land on the previous day.\n */\n situationOn?: string;\n /** When VerifNow last retrieved them from TEDB. */\n fetchedAt?: Date;\n}\n\n/** VAT rates of every EU member state. */\nexport interface VatRates {\n /** Always `TEDB`, the Commission's Taxes in Europe Database. */\n source: string;\n sourceUrl?: string;\n /** One entry per member state retrieved so far — normally all 27. */\n rates: CountryVatRates[];\n}\n\n/** Quota counters read from the `X-RateLimit-*` response headers. */\nexport interface QuotaInfo {\n /** Validations included in the current billing period. */\n limit?: number;\n /** Validations left before overage or blocking. */\n remaining?: number;\n /** When the current period resets. */\n resetAt?: Date;\n /** True once you are past the included quota and into per-unit billing. */\n overage?: boolean;\n}\n\nexport interface RetryOptions {\n /**\n * Retry attempts after the first failure. Defaults to 2, so up to 3 requests in total.\n * Only connection failures, 429 and 5xx are retried — never a 400 or 401, which will not\n * succeed on a second try.\n */\n attempts?: number;\n /** Delay before the first retry, in ms. Doubles each attempt. Defaults to 200. */\n backoffMs?: number;\n /** Upper bound on a single backoff delay, in ms. Defaults to 2000. */\n maxBackoffMs?: number;\n}\n\nexport interface VerifNowOptions {\n /** API key created in the VerifNow dashboard. Sent as the `X-API-KEY` header. */\n apiKey: string;\n /** Override the API origin. Defaults to `https://api.verifnow.io`. */\n baseUrl?: string;\n /** Abort a single request after this many ms. Defaults to 5000. */\n timeoutMs?: number;\n /** Retry policy, or `false` to disable retries entirely. */\n retry?: RetryOptions | false;\n /** Extra headers merged into every request. */\n headers?: Record<string, string>;\n /**\n * Replacement for the global `fetch`, for tests or a custom agent.\n * Defaults to `globalThis.fetch`.\n */\n fetch?: typeof globalThis.fetch;\n}\n"],"mappings":"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","names":["asDate"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/errors.ts","../src/version.ts","../src/client.ts","../src/types.ts"],"sourcesContent":["export { VerifNow, type RequestOptions, type VatValidationOptions } from './client.js';\n\nexport {\n VerifNowError,\n VerifNowAuthError,\n VerifNowRequestError,\n VerifNowRateLimitError,\n VerifNowServerError,\n VerifNowConnectionError,\n VerifNowResponseError,\n} from './errors.js';\n\nexport {\n VALIDATION_RULES,\n type CountryVatRates,\n type Deliverability,\n type EmailDetails,\n type EmailSignals,\n type IbanDetails,\n type NasDetails,\n type NifDetails,\n type NifType,\n type SsnDetails,\n type PhoneDetails,\n type PhoneLineType,\n type QuotaInfo,\n type RegionalVatRate,\n type TraderNameMatch,\n type TraderNameMatchSource,\n type RetryOptions,\n type RiskLevel,\n type ValidationLevel,\n type ValidationResult,\n type ValidationRule,\n type VatDetails,\n type VatRates,\n type VatSource,\n type VerifNowOptions,\n} from './types.js';\n\nexport { VERSION } from './version.js';\n","import type { QuotaInfo } from './types.js';\n\n/**\n * Base class for every error this SDK throws.\n *\n * The SDK fails loudly on purpose. A validation client that swallows a network failure and\n * reports `valid: true` turns an outage into silently accepted bad data, and the outage stays\n * invisible until someone audits the database. Catch these and decide explicitly — accepting the\n * input on failure is a reasonable choice, but it should be a choice.\n *\n * @example\n * ```ts\n * try {\n * const result = await client.validateEmail(input);\n * return result.valid;\n * } catch (error) {\n * if (error instanceof VerifNowRateLimitError) throw error; // back-pressure, do not swallow\n * logger.warn({ error }, 'VerifNow unavailable, accepting input unverified');\n * return true;\n * }\n * ```\n */\nexport class VerifNowError extends Error {\n /** HTTP status, when the failure came back from the API rather than the network. */\n readonly status?: number;\n /** Correlation id from the `X-Request-Id` response header, useful in support requests. */\n readonly requestId?: string;\n\n constructor(\n message: string,\n options: { status?: number; requestId?: string; cause?: unknown } = {},\n ) {\n super(message, { cause: options.cause });\n this.name = new.target.name;\n this.status = options.status;\n this.requestId = options.requestId;\n Error.captureStackTrace?.(this, new.target);\n }\n}\n\n/** The API key is missing, malformed, revoked, or not authorised for this endpoint (401/403). */\nexport class VerifNowAuthError extends VerifNowError {}\n\n/**\n * The request was rejected as malformed (400).\n *\n * Retrying is pointless — the payload itself needs to change.\n */\nexport class VerifNowRequestError extends VerifNowError {}\n\n/** The monthly quota or the concurrency limit was exceeded (429). */\nexport class VerifNowRateLimitError extends VerifNowError {\n /** Quota counters from the response headers, when present. */\n readonly quota?: QuotaInfo;\n /** Seconds to wait before retrying, derived from `Retry-After` or `X-RateLimit-Reset`. */\n readonly retryAfterSeconds?: number;\n\n constructor(\n message: string,\n options: {\n status?: number;\n requestId?: string;\n cause?: unknown;\n quota?: QuotaInfo;\n retryAfterSeconds?: number;\n } = {},\n ) {\n super(message, options);\n this.quota = options.quota;\n this.retryAfterSeconds = options.retryAfterSeconds;\n }\n}\n\n/** The API failed to process the request (5xx). Retried automatically before surfacing. */\nexport class VerifNowServerError extends VerifNowError {}\n\n/**\n * The API could not be reached at all: DNS failure, refused connection, TLS error, or the\n * request exceeded `timeoutMs`.\n *\n * A wrong `baseUrl` surfaces here, which is why it names the URL it tried.\n */\nexport class VerifNowConnectionError extends VerifNowError {\n /** True when the failure was the client-side timeout rather than a transport error. */\n readonly timedOut: boolean;\n\n constructor(\n message: string,\n options: { cause?: unknown; timedOut?: boolean } = {},\n ) {\n super(message, { cause: options.cause });\n this.timedOut = options.timedOut ?? false;\n }\n}\n\n/** The API returned a success status with a body this SDK could not parse. */\nexport class VerifNowResponseError extends VerifNowError {}\n","/**\n * SDK version, sent to the API as `X-VerifNow-SDK: node/<version>` so calls made through an\n * official SDK can be told apart from hand-rolled integrations.\n *\n * Kept in sync with `package.json` by a test — bump both together.\n */\nexport const VERSION = '1.9.0';\n","import {\n VerifNowAuthError,\n VerifNowConnectionError,\n VerifNowError,\n VerifNowRateLimitError,\n VerifNowRequestError,\n VerifNowResponseError,\n VerifNowServerError,\n} from './errors.js';\nimport type {\n CountryVatRates,\n EmailDetails,\n EmailSignals,\n IbanDetails,\n NasDetails,\n NifDetails,\n SsnDetails,\n PhoneDetails,\n QuotaInfo,\n RetryOptions,\n ValidationResult,\n ValidationRule,\n VatDetails,\n VatRates,\n VerifNowOptions,\n} from './types.js';\nimport { VERSION } from './version.js';\n\nconst DEFAULT_BASE_URL = 'https://api.verifnow.io';\nconst DEFAULT_TIMEOUT_MS = 5_000;\nconst DEFAULT_RETRY: Required<RetryOptions> = {\n attempts: 2,\n backoffMs: 200,\n maxBackoffMs: 2_000,\n};\n\n/** Per-call overrides. */\nexport interface RequestOptions {\n /** Override the client timeout for this call. */\n timeoutMs?: number;\n /** Cancel the call from your own controller. Combined with the timeout. */\n signal?: AbortSignal;\n}\n\n/** Options for `validateVat`: the per-call options, plus the company expected to hold the number. */\nexport interface VatValidationOptions extends RequestOptions {\n /** The company you expect to hold this VAT number, e.g. from a supplier form. At most 200 characters. */\n traderName?: string;\n}\n\n/**\n * Client for the VerifNow validation API.\n *\n * @example\n * ```ts\n * import { VerifNow } from '@verifnow/sdk';\n *\n * const client = new VerifNow({ apiKey: process.env.VERIFNOW_API_KEY! });\n * const result = await client.validateEmail('user@example.com');\n *\n * if (!result.valid) console.log(result.message);\n * if (result.emailDetails?.signals?.typoDetected) {\n * console.log('Did you mean', result.emailDetails.signals.suggestedDomain);\n * }\n * ```\n */\nexport class VerifNow {\n readonly #apiKey: string;\n readonly #baseUrl: string;\n readonly #timeoutMs: number;\n readonly #retry: Required<RetryOptions> | null;\n readonly #headers: Record<string, string>;\n readonly #fetch: typeof globalThis.fetch;\n\n constructor(options: VerifNowOptions) {\n if (!options?.apiKey || options.apiKey.trim() === '') {\n throw new VerifNowError(\n 'A VerifNow API key is required. Create one in the dashboard and pass it as `apiKey`.',\n );\n }\n\n const fetchImpl = options.fetch ?? globalThis.fetch;\n if (typeof fetchImpl !== 'function') {\n throw new VerifNowError(\n 'No global fetch available. Use Node 18 or later, or pass a `fetch` implementation.',\n );\n }\n\n this.#apiKey = options.apiKey.trim();\n // Trailing slashes would produce `//api/v1/...`, which some proxies reject.\n this.#baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/, '');\n this.#timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n this.#retry =\n options.retry === false ? null : { ...DEFAULT_RETRY, ...(options.retry ?? {}) };\n this.#headers = options.headers ?? {};\n this.#fetch = fetchImpl.bind(globalThis);\n }\n\n /** Validate an email address: syntax, DNS/MX, typo, disposable, role-based, quality score. */\n validateEmail(value: string, options?: RequestOptions): Promise<ValidationResult> {\n return this.validate('email', value, options);\n }\n\n /**\n * Validate a phone number against its country's numbering plan.\n *\n * The number must include its country code (`+33…` or `0033…`). Valid numbers come back in\n * E.164 as `normalizedValue`, with country and line type in `phoneDetails`.\n */\n validatePhone(value: string, options?: RequestOptions): Promise<ValidationResult> {\n return this.validate('phone', value, options);\n }\n\n /**\n * Validate an IBAN against the SWIFT registry entry for its country, then its check digits.\n *\n * `ibanDetails` reports the two separately: check digits catch a typo, the registry catches an\n * account number that could never exist in that country.\n */\n validateIban(value: string, options?: RequestOptions): Promise<ValidationResult> {\n return this.validate('iban', value, options);\n }\n\n /**\n * Validate a VAT number.\n *\n * Pass `traderName` to ask whether the number belongs to that company:\n * `vatDetails.traderNameMatch` answers `MATCH`, `MISMATCH` or `NOT_AVAILABLE`, and\n * `traderNameMatchSource` says who compared — VerifNow against the name VIES publishes, or VIES\n * itself where it withholds the name but checks one (Spain). Germany does neither.\n */\n validateVat(value: string, options: VatValidationOptions = {}): Promise<ValidationResult> {\n const { traderName, ...requestOptions } = options;\n const extra = traderName && traderName.trim() !== '' ? { traderName } : undefined;\n return this.#validate('vat', value, requestOptions, extra);\n }\n\n /**\n * Validate a Canadian Social Insurance Number: format and Luhn check digit.\n *\n * `nasDetails` flags a temporary resident's number (it expires with their permit) and numbers\n * from series not issued to individuals. Only collect a SIN where the law requires it.\n */\n validateNas(value: string, options?: RequestOptions): Promise<ValidationResult> {\n return this.validate('nas', value, options);\n }\n\n /**\n * Validate a US Social Security Number against the numbers the SSA never issues.\n *\n * An SSN has no check digit: a typo that lands on another possible number cannot be caught, and\n * only the SSA can confirm a number was issued. `ssnDetails.itin` flags an IRS ITIN.\n */\n validateSsn(value: string, options?: RequestOptions): Promise<ValidationResult> {\n return this.validate('ssn', value, options);\n }\n\n /**\n * Validate a Spanish NIF: a DNI, a NIE (foreign nationals), the K/L/M series, or a company NIF.\n *\n * `nifDetails` says which, whether it belongs to a person, and for a company its legal form.\n * Spanish only — a Portuguese NIF is a different scheme and is not accepted here.\n */\n validateNif(value: string, options?: RequestOptions): Promise<ValidationResult> {\n return this.validate('nif', value, options);\n }\n\n /**\n * Validate a value against any rule.\n *\n * The typed helpers above call this. Use it directly when the rule is chosen at runtime.\n */\n validate(\n rule: ValidationRule,\n value: string,\n options: RequestOptions = {},\n ): Promise<ValidationResult> {\n return this.#validate(rule, value, options);\n }\n\n async #validate(\n rule: ValidationRule,\n value: string,\n options: RequestOptions,\n extra?: Record<string, string>,\n ): Promise<ValidationResult> {\n if (typeof value !== 'string' || value.trim() === '') {\n // Caught here rather than server-side: an empty value consumes quota and can only fail.\n throw new VerifNowRequestError(\n `Cannot validate an empty value for rule \"${rule}\".`,\n );\n }\n\n const url = `${this.#baseUrl}/api/v1/validate/${rule}`;\n const body = JSON.stringify({ value, ...extra });\n return this.#withRetry(() =>\n this.#requestOnce('POST', url, body, options, (payload, quota) => mapResult(payload, quota)),\n );\n }\n\n /**\n * EU VAT rates of every member state, from the European Commission's TEDB.\n *\n * Public reference data: the call spends no quota. These are the rates a member state has, not\n * the rate a sale is charged — in B2B trade between member states the invoice is usually\n * zero-rated under the reverse charge whatever the buyer's country rate is.\n */\n async vatRates(options: RequestOptions = {}): Promise<VatRates> {\n const url = `${this.#baseUrl}/api/v1/vat/rates`;\n return this.#withRetry(() =>\n this.#requestOnce('GET', url, undefined, options, (payload) => mapVatRates(payload)),\n );\n }\n\n /**\n * One EU member state's VAT rates. Accepts `GR` for Greece as well as `EL`.\n *\n * A code outside the 27 member states throws {@link VerifNowRequestError} (HTTP 404).\n */\n async vatRate(countryCode: string, options: RequestOptions = {}): Promise<CountryVatRates> {\n if (typeof countryCode !== 'string' || countryCode.trim() === '') {\n throw new VerifNowRequestError('A member state code is required, e.g. \"FR\".');\n }\n const url = `${this.#baseUrl}/api/v1/vat/rates/${encodeURIComponent(countryCode.trim())}`;\n return this.#withRetry(() =>\n this.#requestOnce('GET', url, undefined, options, (payload) => mapCountryVatRates(payload)),\n );\n }\n\n /** Runs one request under the retry policy. */\n async #withRetry<T>(attemptOnce: () => Promise<T>): Promise<T> {\n const maxAttempts = this.#retry ? this.#retry.attempts + 1 : 1;\n\n let lastError: VerifNowError | undefined;\n\n for (let attempt = 0; attempt < maxAttempts; attempt++) {\n try {\n return await attemptOnce();\n } catch (error) {\n if (!(error instanceof VerifNowError)) throw error;\n lastError = error;\n\n const isLastAttempt = attempt === maxAttempts - 1;\n if (isLastAttempt || !this.#retry) throw error;\n\n const delay = this.#retryDelay(error, attempt);\n if (delay === null) throw error;\n\n await sleep(delay);\n }\n }\n\n /* c8 ignore next -- the loop either returns or throws */\n throw lastError ?? new VerifNowError('Request failed');\n }\n\n /**\n * How long to wait before retrying, or `null` when the error should surface immediately.\n *\n * A 429 is retried only when the reset is close: the concurrency limit clears in\n * milliseconds, but a spent monthly quota does not, and sleeping on it helps nobody.\n */\n #retryDelay(error: VerifNowError, attempt: number): number | null {\n const retry = this.#retry!;\n const backoff = Math.min(retry.backoffMs * 2 ** attempt, retry.maxBackoffMs);\n\n if (error instanceof VerifNowRateLimitError) {\n const waitMs = (error.retryAfterSeconds ?? 0) * 1000;\n if (waitMs > retry.maxBackoffMs) return null;\n return Math.max(waitMs, backoff);\n }\n\n if (error instanceof VerifNowServerError) return backoff;\n // A timeout is retried: the deadline is ours, and the next attempt gets a fresh one.\n if (error instanceof VerifNowConnectionError) return backoff;\n\n // 400, 401 and unparseable bodies will fail identically on a second attempt.\n return null;\n }\n\n async #requestOnce<T>(\n method: 'GET' | 'POST',\n url: string,\n body: string | undefined,\n options: RequestOptions,\n map: (payload: Record<string, unknown>, quota: QuotaInfo | undefined) => T,\n ): Promise<T> {\n const timeoutMs = options.timeoutMs ?? this.#timeoutMs;\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n const abortFromCaller = () => controller.abort();\n options.signal?.addEventListener('abort', abortFromCaller, { once: true });\n\n let response: Response;\n try {\n response = await this.#fetch(url, {\n method,\n headers: {\n ...this.#headers,\n ...(body === undefined ? {} : { 'Content-Type': 'application/json' }),\n Accept: 'application/json',\n 'X-API-KEY': this.#apiKey,\n 'X-VerifNow-SDK': `node/${VERSION}`,\n },\n body,\n signal: controller.signal,\n });\n } catch (cause) {\n // The caller's own cancellation is theirs to handle, not a transport failure.\n if (options.signal?.aborted) throw cause;\n\n const timedOut = controller.signal.aborted;\n throw new VerifNowConnectionError(\n timedOut\n ? `VerifNow request to ${url} timed out after ${timeoutMs}ms.`\n : `Could not reach the VerifNow API at ${url}. Check \\`baseUrl\\` and network access.`,\n { cause, timedOut },\n );\n } finally {\n clearTimeout(timer);\n options.signal?.removeEventListener('abort', abortFromCaller);\n }\n\n return this.#handleResponse(response, map);\n }\n\n async #handleResponse<T>(\n response: Response,\n map: (payload: Record<string, unknown>, quota: QuotaInfo | undefined) => T,\n ): Promise<T> {\n const requestId = response.headers.get('X-Request-Id') ?? undefined;\n const quota = parseQuota(response.headers);\n\n if (response.ok) {\n let payload: unknown;\n try {\n payload = await response.json();\n } catch (cause) {\n throw new VerifNowResponseError(\n 'VerifNow returned a success status with a body that is not valid JSON.',\n { status: response.status, requestId, cause },\n );\n }\n\n if (payload === null || typeof payload !== 'object') {\n throw new VerifNowResponseError(\n 'VerifNow returned an unexpected response shape.',\n { status: response.status, requestId },\n );\n }\n\n return map(payload as Record<string, unknown>, quota);\n }\n\n const message = await readErrorMessage(response);\n const context = { status: response.status, requestId };\n\n if (response.status === 401 || response.status === 403) {\n throw new VerifNowAuthError(\n `VerifNow rejected the API key (${response.status}): ${message}`,\n context,\n );\n }\n\n if (response.status === 429) {\n throw new VerifNowRateLimitError(`VerifNow rate limit reached: ${message}`, {\n ...context,\n quota,\n retryAfterSeconds: parseRetryAfter(response.headers, quota),\n });\n }\n\n if (response.status >= 500) {\n throw new VerifNowServerError(\n `VerifNow returned ${response.status}: ${message}`,\n context,\n );\n }\n\n throw new VerifNowRequestError(\n `VerifNow rejected the request (${response.status}): ${message}`,\n context,\n );\n }\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nfunction toNumber(value: string | null): number | undefined {\n if (value === null) return undefined;\n const parsed = Number(value);\n return Number.isFinite(parsed) ? parsed : undefined;\n}\n\nfunction parseQuota(headers: Headers): QuotaInfo | undefined {\n const limit = toNumber(headers.get('X-RateLimit-Limit'));\n const remaining = toNumber(headers.get('X-RateLimit-Remaining'));\n const resetSeconds = toNumber(headers.get('X-RateLimit-Reset'));\n const overage = headers.get('X-Quota-Overage') === 'true';\n\n if (\n limit === undefined &&\n remaining === undefined &&\n resetSeconds === undefined &&\n !overage\n ) {\n return undefined;\n }\n\n return {\n limit,\n remaining,\n resetAt: resetSeconds === undefined ? undefined : new Date(resetSeconds * 1000),\n overage,\n };\n}\n\nfunction parseRetryAfter(headers: Headers, quota?: QuotaInfo): number | undefined {\n const retryAfter = headers.get('Retry-After');\n if (retryAfter !== null) {\n const seconds = Number(retryAfter);\n if (Number.isFinite(seconds)) return seconds;\n\n // RFC 7231 also allows an HTTP-date.\n const asDate = Date.parse(retryAfter);\n if (!Number.isNaN(asDate)) {\n return Math.max(0, Math.ceil((asDate - Date.now()) / 1000));\n }\n }\n\n if (quota?.resetAt) {\n return Math.max(0, Math.ceil((quota.resetAt.getTime() - Date.now()) / 1000));\n }\n\n return undefined;\n}\n\nasync function readErrorMessage(response: Response): Promise<string> {\n try {\n const text = await response.text();\n if (!text) return response.statusText || 'no details';\n\n try {\n const parsed = JSON.parse(text) as Record<string, unknown>;\n const message = parsed.message ?? parsed.error;\n if (typeof message === 'string' && message !== '') return message;\n } catch {\n // Not JSON — a proxy or the servlet container's default error page.\n }\n\n return text.slice(0, 500);\n } catch {\n return response.statusText || 'no details';\n }\n}\n\nfunction asString(value: unknown): string | undefined {\n return typeof value === 'string' ? value : undefined;\n}\n\nfunction asBoolean(value: unknown): boolean | undefined {\n return typeof value === 'boolean' ? value : undefined;\n}\n\nfunction asNumber(value: unknown): number | undefined {\n return typeof value === 'number' && Number.isFinite(value) ? value : undefined;\n}\n\nfunction mapSignals(raw: unknown): EmailSignals | undefined {\n if (raw === null || typeof raw !== 'object') return undefined;\n const s = raw as Record<string, unknown>;\n\n return {\n syntaxValid: asBoolean(s.syntax_valid),\n mxValid: asBoolean(s.mx_valid),\n typoDetected: asBoolean(s.typo_detected),\n suggestedDomain: asString(s.suggested_domain),\n disposable: asBoolean(s.disposable),\n roleBased: asBoolean(s.role_based),\n freeProvider: asBoolean(s.free_provider),\n domainAgeDays: asNumber(s.domain_age_days),\n mxProvider: asString(s.mx_provider),\n mxQualityScore: asNumber(s.mx_quality_score),\n };\n}\n\nfunction mapEmailDetails(raw: unknown): EmailDetails | undefined {\n if (raw === null || typeof raw !== 'object') return undefined;\n const d = raw as Record<string, unknown>;\n\n return {\n signals: mapSignals(d.signals),\n riskScore: asNumber(d.risk_score),\n riskLevel: asString(d.risk_level) as EmailDetails['riskLevel'],\n deliverability: asString(d.deliverability) as EmailDetails['deliverability'],\n appliedLevel: asString(d.applied_level) as EmailDetails['appliedLevel'],\n };\n}\n\n/**\n * Reads `registered`, preserving the difference between `false` and `null`.\n *\n * `asBoolean` cannot be used here: it maps `null` to `undefined`, which would erase the one\n * distinction the whole VAT design exists to carry. `false` means the registry answered and the\n * number is not there; `null` means nobody could ask. A caller that cannot tell them apart will\n * reject real businesses whenever VIES is down.\n *\n * A missing key is read as `null` for the same reason — unknown, not absent.\n */\nfunction asRegistered(value: unknown): boolean | null {\n return typeof value === 'boolean' ? value : null;\n}\n\nfunction asDate(value: unknown): Date | undefined {\n if (typeof value !== 'string') return undefined;\n const parsed = new Date(value);\n return Number.isNaN(parsed.getTime()) ? undefined : parsed;\n}\n\nfunction mapVatDetails(raw: unknown): VatDetails | undefined {\n if (raw === null || typeof raw !== 'object') return undefined;\n const d = raw as Record<string, unknown>;\n\n return {\n formatValid: asBoolean(d.format_valid),\n registered: asRegistered(d.registered),\n countryCode: asString(d.country_code),\n source: asString(d.source) as VatDetails['source'],\n checkedAt: asDate(d.checked_at),\n traderName: asString(d.trader_name),\n traderAddress: asString(d.trader_address),\n viesAvailable: asBoolean(d.vies_available),\n consultationNumber: asString(d.consultation_number),\n traderNameMatch: asString(d.trader_name_match) as VatDetails['traderNameMatch'],\n traderNameMatchSource: asString(d.trader_name_match_source) as VatDetails['traderNameMatchSource'],\n };\n}\n\nfunction mapPhoneDetails(raw: unknown): PhoneDetails | undefined {\n if (raw === null || typeof raw !== 'object') return undefined;\n const d = raw as Record<string, unknown>;\n\n return {\n countryCode: asString(d.country_code),\n callingCode: asNumber(d.calling_code),\n lineType: asString(d.line_type) as PhoneDetails['lineType'],\n internationalFormat: asString(d.international_format),\n nationalFormat: asString(d.national_format),\n };\n}\n\nfunction mapIbanDetails(raw: unknown): IbanDetails | undefined {\n if (raw === null || typeof raw !== 'object') return undefined;\n const d = raw as Record<string, unknown>;\n\n return {\n countryCode: asString(d.country_code),\n sepa: asBoolean(d.sepa),\n structureValid: asBoolean(d.structure_valid),\n checksumValid: asBoolean(d.checksum_valid),\n length: asNumber(d.length),\n expectedLength: asNumber(d.expected_length),\n formatted: asString(d.formatted),\n };\n}\n\nfunction mapNasDetails(raw: unknown): NasDetails | undefined {\n if (raw === null || typeof raw !== 'object') return undefined;\n const d = raw as Record<string, unknown>;\n\n return {\n checksumValid: asBoolean(d.checksum_valid),\n temporaryResident: asBoolean(d.temporary_resident),\n individualSeries: asBoolean(d.individual_series),\n formatted: asString(d.formatted),\n };\n}\n\nfunction mapNifDetails(raw: unknown): NifDetails | undefined {\n if (raw === null || typeof raw !== 'object') return undefined;\n const d = raw as Record<string, unknown>;\n\n return {\n type: asString(d.type) as NifDetails['type'],\n naturalPerson: asBoolean(d.natural_person),\n checksumValid: asBoolean(d.checksum_valid),\n entityLetter: asString(d.entity_letter),\n entityType: asString(d.entity_type),\n };\n}\n\nfunction mapSsnDetails(raw: unknown): SsnDetails | undefined {\n if (raw === null || typeof raw !== 'object') return undefined;\n const d = raw as Record<string, unknown>;\n\n return { itin: asBoolean(d.itin) };\n}\n\n/**\n * Maps the API's snake_case diagnostics onto camelCase, so a TypeScript caller is not switching\n * naming conventions mid-expression. The untouched body stays available on `raw`.\n */\nfunction mapCountryVatRates(raw: Record<string, unknown>): CountryVatRates {\n const numbers = (value: unknown): number[] =>\n Array.isArray(value) ? value.filter((v): v is number => asNumber(v) !== undefined) : [];\n\n return {\n countryCode: asString(raw.countryCode) ?? '',\n standardRate: asNumber(raw.standardRate) ?? Number.NaN,\n reducedRates: numbers(raw.reducedRates),\n regionalRates: Array.isArray(raw.regionalRates)\n ? raw.regionalRates\n .filter((r): r is Record<string, unknown> => r !== null && typeof r === 'object')\n .map((r) => ({\n rate: asNumber(r.rate) ?? Number.NaN,\n note: asString(r.note),\n euVatArea: asBoolean(r.euVatArea),\n }))\n : [],\n situationOn: asString(raw.situationOn),\n fetchedAt: asDate(raw.fetchedAt),\n };\n}\n\nfunction mapVatRates(raw: Record<string, unknown>): VatRates {\n const rates = Array.isArray(raw.rates)\n ? raw.rates\n .filter((r): r is Record<string, unknown> => r !== null && typeof r === 'object')\n .map(mapCountryVatRates)\n : [];\n return {\n source: asString(raw.source) ?? 'TEDB',\n sourceUrl: asString(raw.sourceUrl),\n rates,\n };\n}\n\nfunction mapResult(\n payload: Record<string, unknown>,\n quota: QuotaInfo | undefined,\n): ValidationResult {\n return {\n valid: payload.valid === true,\n message: asString(payload.message),\n normalizedValue: asString(payload.normalizedValue) ?? null,\n originalValue: asString(payload.originalValue),\n validationLevel: asString(payload.validationLevel) as ValidationResult['validationLevel'],\n emailDetails: mapEmailDetails(payload.emailDetails),\n vatDetails: mapVatDetails(payload.vatDetails),\n phoneDetails: mapPhoneDetails(payload.phoneDetails),\n ibanDetails: mapIbanDetails(payload.ibanDetails),\n nasDetails: mapNasDetails(payload.nasDetails),\n nifDetails: mapNifDetails(payload.nifDetails),\n ssnDetails: mapSsnDetails(payload.ssnDetails),\n quota,\n raw: payload,\n };\n}\n","/**\n * Validation rules exposed by the VerifNow API.\n *\n * Each maps to `POST /api/v1/validate/{rule}`.\n */\nexport type ValidationRule =\n | 'email'\n | 'phone'\n | 'iban'\n | 'vat'\n | 'nas'\n | 'ssn'\n | 'nif';\n\nexport const VALIDATION_RULES: readonly ValidationRule[] = [\n 'email',\n 'phone',\n 'iban',\n 'vat',\n 'nas',\n 'ssn',\n 'nif',\n] as const;\n\n/**\n * Depth of checks applied to a request, decided by the plan attached to the API key.\n *\n * `STANDARD` runs on the FREE and STARTER plans, `ADVANCED` on GROWTH, `PREMIUM` on PRO.\n * Branch on this rather than on the plan name: it is the only value that tells you which\n * signals are actually present in the response.\n */\nexport type ValidationLevel = 'BASIC' | 'STANDARD' | 'ADVANCED' | 'PREMIUM';\n\n/** Categorical risk assessment. Returned from `ADVANCED` depth upward. */\nexport type RiskLevel = 'LOW' | 'MEDIUM' | 'HIGH';\n\nexport type Deliverability =\n | 'DELIVERABLE'\n | 'RISKY'\n | 'UNDELIVERABLE'\n | 'UNKNOWN';\n\n/**\n * Per-signal breakdown behind an email verdict.\n *\n * Fields are `undefined` when the applied level does not compute them — the last four require\n * `ADVANCED` depth or higher. Check `ValidationResult.appliedLevel` before relying on one.\n */\nexport interface EmailSignals {\n /** The address matches the syntax pattern for the applied level. */\n syntaxValid?: boolean;\n /** The domain resolves and publishes MX (or fallback A) records. */\n mxValid?: boolean;\n /** A likely typo was found in the domain, e.g. `gmail.con`. */\n typoDetected?: boolean;\n /** The correction proposed when `typoDetected` is true. */\n suggestedDomain?: string;\n /** The domain belongs to a throwaway mailbox provider. */\n disposable?: boolean;\n /** The local part is a shared mailbox: `info@`, `admin@`, `noreply@`. */\n roleBased?: boolean;\n /** The domain is a consumer mailbox provider. Requires ADVANCED. */\n freeProvider?: boolean;\n /** Estimated age of the domain in days. Requires ADVANCED. */\n domainAgeDays?: number;\n /** Identified mail provider, e.g. `google`. Requires ADVANCED. */\n mxProvider?: string;\n /** Mail server quality between 0 and 1. Requires ADVANCED. */\n mxQualityScore?: number;\n}\n\n/** Email-specific diagnostics. Absent when the applied level is `BASIC`. */\nexport interface EmailDetails {\n signals?: EmailSignals;\n /** Aggregated risk on a 0–100 scale, where 0 is the lowest risk. */\n riskScore?: number;\n /** Categorical risk. Requires ADVANCED depth or higher. */\n riskLevel?: RiskLevel;\n deliverability?: Deliverability;\n /** The depth actually applied, echoed back by the API. */\n appliedLevel?: ValidationLevel;\n}\n\n/**\n * Where a VAT registration verdict came from.\n *\n * VIES publishes no SLA and drops member states several times a month, so a VAT answer is not\n * always a live one. Branch on this rather than on `ValidationResult.valid` whenever the\n * difference matters for your own compliance.\n */\n/** Whether a supplied company name belongs to a VAT number's registered holder. */\nexport type TraderNameMatch = 'MATCH' | 'MISMATCH' | 'NOT_AVAILABLE';\n\n/** Who compared the names. */\nexport type TraderNameMatchSource = 'VIES' | 'VERIFNOW';\n\nexport type VatSource =\n /** Confirmed against VIES during this request. */\n | 'LIVE'\n /** Served from a VIES answer less than 24 hours old. */\n | 'CACHE'\n /** VIES was unreachable, so an older cached answer was used. */\n | 'STALE'\n /** VIES was unreachable and nothing was cached. Registration is unknown. */\n | 'UNVERIFIED'\n /** The country is outside VIES, so no registry lookup is possible. */\n | 'NOT_APPLICABLE';\n\n/** VAT-specific diagnostics. Present on `vat` validations. */\nexport interface VatDetails {\n /** The number matches its member state's structure. Decided locally, never depends on VIES. */\n formatValid?: boolean;\n /**\n * Present in the member state's registry.\n *\n * **`null` means unknown, never \"not registered.\"** It is returned when VIES could not be\n * consulted. Treating `null` as `false` rejects legitimate customers during someone else's\n * outage — the single most expensive mistake available in VAT validation.\n */\n registered: boolean | null;\n /** Member state the number belongs to, e.g. `IE`. Greece is `EL`, Northern Ireland `XI`. */\n countryCode?: string;\n source?: VatSource;\n /** When the registration was last confirmed against VIES. */\n checkedAt?: Date;\n /** Registered trading name, when the member state discloses it. Germany does not. */\n traderName?: string;\n /** Registered address, when the member state discloses it. */\n traderAddress?: string;\n /** Whether VIES could answer for this country during the request. */\n viesAvailable?: boolean;\n /**\n * The consultation number VIES issued for this lookup — the receipt a tax authority accepts as\n * evidence that you checked. Present only when your account has its own VAT number configured,\n * because VIES issues one only to an identified requester.\n */\n consultationNumber?: string;\n /**\n * Present when a `traderName` was sent: whether it belongs to the registered holder. `MISMATCH`\n * is a question for a human, not proof of fraud — trading names and group companies differ.\n */\n traderNameMatch?: TraderNameMatch;\n /** Who compared: `VERIFNOW`, against the name VIES published, or `VIES` itself (Spain). */\n traderNameMatchSource?: TraderNameMatchSource;\n}\n\n/**\n * Kind of line, according to the country's numbering plan.\n *\n * A `PREMIUM_RATE` or `VOIP` number is still `valid` — it exists. This is how you decide to\n * exclude one, rather than the API deciding for you.\n */\nexport type PhoneLineType =\n | 'MOBILE'\n | 'FIXED_LINE'\n /** The plan does not distinguish the two — the case for the US and Canada. */\n | 'FIXED_LINE_OR_MOBILE'\n | 'TOLL_FREE'\n | 'PREMIUM_RATE'\n | 'SHARED_COST'\n | 'VOIP'\n | 'PERSONAL_NUMBER'\n | 'PAGER'\n | 'UAN'\n | 'VOICEMAIL'\n | 'UNKNOWN';\n\n/**\n * Phone-specific diagnostics. Present whenever the input parsed as an international number —\n * including when it is invalid for its country, so you can tell the user which country it was\n * read as.\n */\nexport interface PhoneDetails {\n /** ISO 3166-1 alpha-2 country, e.g. `FR`. Absent when the calling code is shared by several. */\n countryCode?: string;\n /** International calling code without the plus sign, e.g. `33`. */\n callingCode?: number;\n /** Absent when the number is invalid. */\n lineType?: PhoneLineType;\n /** e.g. `+33 6 12 34 56 78`. Absent when the number is invalid. */\n internationalFormat?: string;\n /** e.g. `06 12 34 56 78`. Absent when the number is invalid. */\n nationalFormat?: string;\n}\n\n/**\n * IBAN-specific diagnostics. Present on `iban` validations.\n *\n * Structure and checksum are reported separately because they fail for different reasons:\n * `structureValid` answers \"could this be an account number in that country\" (the SWIFT\n * registry's length and layout), `checksumValid` answers \"was it typed correctly\" (mod-97).\n * There is no bank name or BIC — that needs a registry the API does not hold.\n */\nexport interface IbanDetails {\n /** The IBAN's country, from its first two characters. */\n countryCode?: string;\n /**\n * The country is inside the SEPA schemes' geographical scope, so a bank there may collect a\n * SEPA direct debit. Whether this particular bank does is published per bank, not per country,\n * and cannot be read from an IBAN.\n */\n sepa?: boolean;\n /** Length and character layout match the registry entry for that country. */\n structureValid?: boolean;\n /** The mod-97 check digits are correct. */\n checksumValid?: boolean;\n /** Length of the value as submitted, spaces removed. */\n length?: number;\n /** Length the registry requires for that country; absent for an unknown country. */\n expectedLength?: number;\n /** Print format, in groups of four. Present only for a valid IBAN. */\n formatted?: string;\n}\n\n/**\n * Canadian Social Insurance Number diagnostics. Present on `nas` validations.\n *\n * There is no province and no expiry date: the first digit no longer reliably identifies a\n * province, and a temporary resident's SIN expires with their permit, which only the document\n * shows.\n */\nexport interface NasDetails {\n /** The Luhn check digit is correct. */\n checksumValid?: boolean;\n /**\n * A 9-series number, issued to temporary residents. It expires with the holder's permit, and the\n * number itself does not say when — check the document.\n */\n temporaryResident?: boolean;\n /**\n * The first digit belongs to a series issued to individuals. `false` for numbers starting with\n * 0 or 8 — including 046 454 286, the government's sample number, which is why it is safe to\n * use in tests.\n */\n individualSeries?: boolean;\n /** Printed form, e.g. `046 454 286`. */\n formatted?: string;\n}\n\n/** The kinds of Spanish tax identification number. */\nexport type NifType =\n /** Spanish national with a DNI: 8 digits and a letter. */\n | 'DNI'\n /** Foreign national: X, Y or Z, 7 digits and a letter. */\n | 'NIE'\n /** Spanish national under 14 without a DNI. */\n | 'NIF_K'\n /** Spanish national resident abroad, staying under six months. */\n | 'NIF_L'\n /** Foreign national without a NIE. */\n | 'NIF_M'\n /** A company or other entity: a letter for the legal form, 7 digits, a control character. */\n | 'ENTITY';\n\n/** Spanish NIF diagnostics. Present on `nif` validations. */\nexport interface NifDetails {\n type?: NifType;\n /** The number belongs to a person rather than a company or other entity. */\n naturalPerson?: boolean;\n /** The control character is correct. */\n checksumValid?: boolean;\n /** For an entity, the letter that encodes its legal form, e.g. `B`. */\n entityLetter?: string;\n /** For an entity, its legal form, e.g. `Private limited company (Sociedad de responsabilidad limitada)`. */\n entityType?: string;\n}\n\n/**\n * US SSN diagnostics. Present on `ssn` validations.\n *\n * An SSN has no check digit, and since 2011 its first digits say nothing about a state, so there is\n * little a number can reveal about itself. The one thing worth knowing is whether it is an ITIN.\n */\nexport interface SsnDetails {\n /**\n * The number is an IRS ITIN, not an SSN: it starts with 9 and its fourth and fifth digits are in\n * 50-65, 70-88, 90-92 or 94-99. `valid` is `false` for the SSN, but an ITIN is an acceptable\n * taxpayer number where one is accepted (a W-9, for instance).\n */\n itin?: boolean;\n}\n\n/** Outcome of a single validation call. */\nexport interface ValidationResult {\n /** Whether the value passed every check the applied level ran. */\n valid: boolean;\n /** Human-readable explanation of the verdict. */\n message?: string;\n /** Canonical form of the input — `null` when the value is invalid. */\n normalizedValue: string | null;\n /** The value exactly as submitted. */\n originalValue?: string;\n /** Depth applied to this request. */\n validationLevel?: ValidationLevel;\n /** Present for email validations from `STANDARD` depth upward. */\n emailDetails?: EmailDetails;\n /** Present for VAT validations. */\n vatDetails?: VatDetails;\n /** Present for phone validations. The E.164 form is `normalizedValue`. */\n phoneDetails?: PhoneDetails;\n /** Present for IBAN validations. */\n ibanDetails?: IbanDetails;\n /** Present for Canadian SIN (`nas`) validations. */\n nasDetails?: NasDetails;\n /** Present for Spanish NIF validations. */\n nifDetails?: NifDetails;\n /** Present for US SSN validations. */\n ssnDetails?: SsnDetails;\n /** Quota state reported by the response headers. */\n quota?: QuotaInfo;\n /** The unmodified JSON body, for fields this SDK version does not model yet. */\n raw: Record<string, unknown>;\n}\n\n/** A VAT rate applying to part of a member state only — an overseas department, an island. */\nexport interface RegionalVatRate {\n rate: number;\n /** Where it applies, in the words of the Commission's TEDB. */\n note?: string;\n /**\n * `false` for the Canary Islands and the French overseas territories, which the VAT Directive\n * excludes (Article 6(1)): goods shipped there from another member state are an export, not a\n * distance sale at this rate.\n */\n euVatArea?: boolean;\n}\n\n/**\n * One EU member state's VAT rates, from the European Commission's TEDB.\n *\n * These are the rates the member state has, not the rate a sale is charged: which one applies\n * depends on who sells to whom and what. In B2B trade between member states the invoice is usually\n * zero-rated under the reverse charge, whatever the buyer's country rate is.\n */\nexport interface CountryVatRates {\n /** Member state as TEDB and VIES name it: Greece is `EL`. */\n countryCode: string;\n /** The national standard rate, e.g. `20` for France. */\n standardRate: number;\n /**\n * Every reduced, super-reduced and parking rate on the mainland territory, ascending.\n * TEDB's own sub-labels are inconsistent between member states, so they are not reproduced.\n */\n reducedRates: number[];\n /** Rates for part of the territory only, e.g. 8.5 % in Martinique, Guadeloupe and Réunion. */\n regionalRates: RegionalVatRate[];\n /**\n * The date TEDB says these rates apply from, as `YYYY-MM-DD`. Kept as a string: a date without\n * a time zone turned into a `Date` can land on the previous day.\n */\n situationOn?: string;\n /** When VerifNow last retrieved them from TEDB. */\n fetchedAt?: Date;\n}\n\n/** VAT rates of every EU member state. */\nexport interface VatRates {\n /** Always `TEDB`, the Commission's Taxes in Europe Database. */\n source: string;\n sourceUrl?: string;\n /** One entry per member state retrieved so far — normally all 27. */\n rates: CountryVatRates[];\n}\n\n/** Quota counters read from the `X-RateLimit-*` response headers. */\nexport interface QuotaInfo {\n /** Validations included in the current billing period. */\n limit?: number;\n /** Validations left before overage or blocking. */\n remaining?: number;\n /** When the current period resets. */\n resetAt?: Date;\n /** True once you are past the included quota and into per-unit billing. */\n overage?: boolean;\n}\n\nexport interface RetryOptions {\n /**\n * Retry attempts after the first failure. Defaults to 2, so up to 3 requests in total.\n * Only connection failures, 429 and 5xx are retried — never a 400 or 401, which will not\n * succeed on a second try.\n */\n attempts?: number;\n /** Delay before the first retry, in ms. Doubles each attempt. Defaults to 200. */\n backoffMs?: number;\n /** Upper bound on a single backoff delay, in ms. Defaults to 2000. */\n maxBackoffMs?: number;\n}\n\nexport interface VerifNowOptions {\n /** API key created in the VerifNow dashboard. Sent as the `X-API-KEY` header. */\n apiKey: string;\n /** Override the API origin. Defaults to `https://api.verifnow.io`. */\n baseUrl?: string;\n /** Abort a single request after this many ms. Defaults to 5000. */\n timeoutMs?: number;\n /** Retry policy, or `false` to disable retries entirely. */\n retry?: RetryOptions | false;\n /** Extra headers merged into every request. */\n headers?: Record<string, string>;\n /**\n * Replacement for the global `fetch`, for tests or a custom agent.\n * Defaults to `globalThis.fetch`.\n */\n fetch?: typeof globalThis.fetch;\n}\n"],"mappings":"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","names":["asDate"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -62,6 +62,10 @@ interface EmailDetails {
|
|
|
62
62
|
* always a live one. Branch on this rather than on `ValidationResult.valid` whenever the
|
|
63
63
|
* difference matters for your own compliance.
|
|
64
64
|
*/
|
|
65
|
+
/** Whether a supplied company name belongs to a VAT number's registered holder. */
|
|
66
|
+
type TraderNameMatch = 'MATCH' | 'MISMATCH' | 'NOT_AVAILABLE';
|
|
67
|
+
/** Who compared the names. */
|
|
68
|
+
type TraderNameMatchSource = 'VIES' | 'VERIFNOW';
|
|
65
69
|
type VatSource =
|
|
66
70
|
/** Confirmed against VIES during this request. */
|
|
67
71
|
'LIVE'
|
|
@@ -102,6 +106,13 @@ interface VatDetails {
|
|
|
102
106
|
* because VIES issues one only to an identified requester.
|
|
103
107
|
*/
|
|
104
108
|
consultationNumber?: string;
|
|
109
|
+
/**
|
|
110
|
+
* Present when a `traderName` was sent: whether it belongs to the registered holder. `MISMATCH`
|
|
111
|
+
* is a question for a human, not proof of fraud — trading names and group companies differ.
|
|
112
|
+
*/
|
|
113
|
+
traderNameMatch?: TraderNameMatch;
|
|
114
|
+
/** Who compared: `VERIFNOW`, against the name VIES published, or `VIES` itself (Spain). */
|
|
115
|
+
traderNameMatchSource?: TraderNameMatchSource;
|
|
105
116
|
}
|
|
106
117
|
/**
|
|
107
118
|
* Kind of line, according to the country's numbering plan.
|
|
@@ -257,6 +268,12 @@ interface RegionalVatRate {
|
|
|
257
268
|
rate: number;
|
|
258
269
|
/** Where it applies, in the words of the Commission's TEDB. */
|
|
259
270
|
note?: string;
|
|
271
|
+
/**
|
|
272
|
+
* `false` for the Canary Islands and the French overseas territories, which the VAT Directive
|
|
273
|
+
* excludes (Article 6(1)): goods shipped there from another member state are an export, not a
|
|
274
|
+
* distance sale at this rate.
|
|
275
|
+
*/
|
|
276
|
+
euVatArea?: boolean;
|
|
260
277
|
}
|
|
261
278
|
/**
|
|
262
279
|
* One EU member state's VAT rates, from the European Commission's TEDB.
|
|
@@ -341,6 +358,11 @@ interface RequestOptions {
|
|
|
341
358
|
/** Cancel the call from your own controller. Combined with the timeout. */
|
|
342
359
|
signal?: AbortSignal;
|
|
343
360
|
}
|
|
361
|
+
/** Options for `validateVat`: the per-call options, plus the company expected to hold the number. */
|
|
362
|
+
interface VatValidationOptions extends RequestOptions {
|
|
363
|
+
/** The company you expect to hold this VAT number, e.g. from a supplier form. At most 200 characters. */
|
|
364
|
+
traderName?: string;
|
|
365
|
+
}
|
|
344
366
|
/**
|
|
345
367
|
* Client for the VerifNow validation API.
|
|
346
368
|
*
|
|
@@ -376,8 +398,15 @@ declare class VerifNow {
|
|
|
376
398
|
* account number that could never exist in that country.
|
|
377
399
|
*/
|
|
378
400
|
validateIban(value: string, options?: RequestOptions): Promise<ValidationResult>;
|
|
379
|
-
/**
|
|
380
|
-
|
|
401
|
+
/**
|
|
402
|
+
* Validate a VAT number.
|
|
403
|
+
*
|
|
404
|
+
* Pass `traderName` to ask whether the number belongs to that company:
|
|
405
|
+
* `vatDetails.traderNameMatch` answers `MATCH`, `MISMATCH` or `NOT_AVAILABLE`, and
|
|
406
|
+
* `traderNameMatchSource` says who compared — VerifNow against the name VIES publishes, or VIES
|
|
407
|
+
* itself where it withholds the name but checks one (Spain). Germany does neither.
|
|
408
|
+
*/
|
|
409
|
+
validateVat(value: string, options?: VatValidationOptions): Promise<ValidationResult>;
|
|
381
410
|
/**
|
|
382
411
|
* Validate a Canadian Social Insurance Number: format and Luhn check digit.
|
|
383
412
|
*
|
|
@@ -503,6 +532,6 @@ declare class VerifNowResponseError extends VerifNowError {
|
|
|
503
532
|
*
|
|
504
533
|
* Kept in sync with `package.json` by a test — bump both together.
|
|
505
534
|
*/
|
|
506
|
-
declare const VERSION = "1.
|
|
535
|
+
declare const VERSION = "1.9.0";
|
|
507
536
|
|
|
508
|
-
export { type CountryVatRates, type Deliverability, type EmailDetails, type EmailSignals, type IbanDetails, type NasDetails, type NifDetails, type NifType, type PhoneDetails, type PhoneLineType, type QuotaInfo, type RegionalVatRate, type RequestOptions, type RetryOptions, type RiskLevel, type SsnDetails, VALIDATION_RULES, VERSION, type ValidationLevel, type ValidationResult, type ValidationRule, type VatDetails, type VatRates, type VatSource, VerifNow, VerifNowAuthError, VerifNowConnectionError, VerifNowError, type VerifNowOptions, VerifNowRateLimitError, VerifNowRequestError, VerifNowResponseError, VerifNowServerError };
|
|
537
|
+
export { type CountryVatRates, type Deliverability, type EmailDetails, type EmailSignals, type IbanDetails, type NasDetails, type NifDetails, type NifType, type PhoneDetails, type PhoneLineType, type QuotaInfo, type RegionalVatRate, type RequestOptions, type RetryOptions, type RiskLevel, type SsnDetails, type TraderNameMatch, type TraderNameMatchSource, VALIDATION_RULES, VERSION, type ValidationLevel, type ValidationResult, type ValidationRule, type VatDetails, type VatRates, type VatSource, type VatValidationOptions, VerifNow, VerifNowAuthError, VerifNowConnectionError, VerifNowError, type VerifNowOptions, VerifNowRateLimitError, VerifNowRequestError, VerifNowResponseError, VerifNowServerError };
|
package/dist/index.d.ts
CHANGED
|
@@ -62,6 +62,10 @@ interface EmailDetails {
|
|
|
62
62
|
* always a live one. Branch on this rather than on `ValidationResult.valid` whenever the
|
|
63
63
|
* difference matters for your own compliance.
|
|
64
64
|
*/
|
|
65
|
+
/** Whether a supplied company name belongs to a VAT number's registered holder. */
|
|
66
|
+
type TraderNameMatch = 'MATCH' | 'MISMATCH' | 'NOT_AVAILABLE';
|
|
67
|
+
/** Who compared the names. */
|
|
68
|
+
type TraderNameMatchSource = 'VIES' | 'VERIFNOW';
|
|
65
69
|
type VatSource =
|
|
66
70
|
/** Confirmed against VIES during this request. */
|
|
67
71
|
'LIVE'
|
|
@@ -102,6 +106,13 @@ interface VatDetails {
|
|
|
102
106
|
* because VIES issues one only to an identified requester.
|
|
103
107
|
*/
|
|
104
108
|
consultationNumber?: string;
|
|
109
|
+
/**
|
|
110
|
+
* Present when a `traderName` was sent: whether it belongs to the registered holder. `MISMATCH`
|
|
111
|
+
* is a question for a human, not proof of fraud — trading names and group companies differ.
|
|
112
|
+
*/
|
|
113
|
+
traderNameMatch?: TraderNameMatch;
|
|
114
|
+
/** Who compared: `VERIFNOW`, against the name VIES published, or `VIES` itself (Spain). */
|
|
115
|
+
traderNameMatchSource?: TraderNameMatchSource;
|
|
105
116
|
}
|
|
106
117
|
/**
|
|
107
118
|
* Kind of line, according to the country's numbering plan.
|
|
@@ -257,6 +268,12 @@ interface RegionalVatRate {
|
|
|
257
268
|
rate: number;
|
|
258
269
|
/** Where it applies, in the words of the Commission's TEDB. */
|
|
259
270
|
note?: string;
|
|
271
|
+
/**
|
|
272
|
+
* `false` for the Canary Islands and the French overseas territories, which the VAT Directive
|
|
273
|
+
* excludes (Article 6(1)): goods shipped there from another member state are an export, not a
|
|
274
|
+
* distance sale at this rate.
|
|
275
|
+
*/
|
|
276
|
+
euVatArea?: boolean;
|
|
260
277
|
}
|
|
261
278
|
/**
|
|
262
279
|
* One EU member state's VAT rates, from the European Commission's TEDB.
|
|
@@ -341,6 +358,11 @@ interface RequestOptions {
|
|
|
341
358
|
/** Cancel the call from your own controller. Combined with the timeout. */
|
|
342
359
|
signal?: AbortSignal;
|
|
343
360
|
}
|
|
361
|
+
/** Options for `validateVat`: the per-call options, plus the company expected to hold the number. */
|
|
362
|
+
interface VatValidationOptions extends RequestOptions {
|
|
363
|
+
/** The company you expect to hold this VAT number, e.g. from a supplier form. At most 200 characters. */
|
|
364
|
+
traderName?: string;
|
|
365
|
+
}
|
|
344
366
|
/**
|
|
345
367
|
* Client for the VerifNow validation API.
|
|
346
368
|
*
|
|
@@ -376,8 +398,15 @@ declare class VerifNow {
|
|
|
376
398
|
* account number that could never exist in that country.
|
|
377
399
|
*/
|
|
378
400
|
validateIban(value: string, options?: RequestOptions): Promise<ValidationResult>;
|
|
379
|
-
/**
|
|
380
|
-
|
|
401
|
+
/**
|
|
402
|
+
* Validate a VAT number.
|
|
403
|
+
*
|
|
404
|
+
* Pass `traderName` to ask whether the number belongs to that company:
|
|
405
|
+
* `vatDetails.traderNameMatch` answers `MATCH`, `MISMATCH` or `NOT_AVAILABLE`, and
|
|
406
|
+
* `traderNameMatchSource` says who compared — VerifNow against the name VIES publishes, or VIES
|
|
407
|
+
* itself where it withholds the name but checks one (Spain). Germany does neither.
|
|
408
|
+
*/
|
|
409
|
+
validateVat(value: string, options?: VatValidationOptions): Promise<ValidationResult>;
|
|
381
410
|
/**
|
|
382
411
|
* Validate a Canadian Social Insurance Number: format and Luhn check digit.
|
|
383
412
|
*
|
|
@@ -503,6 +532,6 @@ declare class VerifNowResponseError extends VerifNowError {
|
|
|
503
532
|
*
|
|
504
533
|
* Kept in sync with `package.json` by a test — bump both together.
|
|
505
534
|
*/
|
|
506
|
-
declare const VERSION = "1.
|
|
535
|
+
declare const VERSION = "1.9.0";
|
|
507
536
|
|
|
508
|
-
export { type CountryVatRates, type Deliverability, type EmailDetails, type EmailSignals, type IbanDetails, type NasDetails, type NifDetails, type NifType, type PhoneDetails, type PhoneLineType, type QuotaInfo, type RegionalVatRate, type RequestOptions, type RetryOptions, type RiskLevel, type SsnDetails, VALIDATION_RULES, VERSION, type ValidationLevel, type ValidationResult, type ValidationRule, type VatDetails, type VatRates, type VatSource, VerifNow, VerifNowAuthError, VerifNowConnectionError, VerifNowError, type VerifNowOptions, VerifNowRateLimitError, VerifNowRequestError, VerifNowResponseError, VerifNowServerError };
|
|
537
|
+
export { type CountryVatRates, type Deliverability, type EmailDetails, type EmailSignals, type IbanDetails, type NasDetails, type NifDetails, type NifType, type PhoneDetails, type PhoneLineType, type QuotaInfo, type RegionalVatRate, type RequestOptions, type RetryOptions, type RiskLevel, type SsnDetails, type TraderNameMatch, type TraderNameMatchSource, VALIDATION_RULES, VERSION, type ValidationLevel, type ValidationResult, type ValidationRule, type VatDetails, type VatRates, type VatSource, type VatValidationOptions, VerifNow, VerifNowAuthError, VerifNowConnectionError, VerifNowError, type VerifNowOptions, VerifNowRateLimitError, VerifNowRequestError, VerifNowResponseError, VerifNowServerError };
|
package/dist/index.js
CHANGED
|
@@ -41,7 +41,7 @@ var VerifNowResponseError = class extends VerifNowError {
|
|
|
41
41
|
};
|
|
42
42
|
|
|
43
43
|
// src/version.ts
|
|
44
|
-
var VERSION = "1.
|
|
44
|
+
var VERSION = "1.9.0";
|
|
45
45
|
|
|
46
46
|
// src/client.ts
|
|
47
47
|
var DEFAULT_BASE_URL = "https://api.verifnow.io";
|
|
@@ -99,9 +99,18 @@ var VerifNow = class {
|
|
|
99
99
|
validateIban(value, options) {
|
|
100
100
|
return this.validate("iban", value, options);
|
|
101
101
|
}
|
|
102
|
-
/**
|
|
103
|
-
|
|
104
|
-
|
|
102
|
+
/**
|
|
103
|
+
* Validate a VAT number.
|
|
104
|
+
*
|
|
105
|
+
* Pass `traderName` to ask whether the number belongs to that company:
|
|
106
|
+
* `vatDetails.traderNameMatch` answers `MATCH`, `MISMATCH` or `NOT_AVAILABLE`, and
|
|
107
|
+
* `traderNameMatchSource` says who compared — VerifNow against the name VIES publishes, or VIES
|
|
108
|
+
* itself where it withholds the name but checks one (Spain). Germany does neither.
|
|
109
|
+
*/
|
|
110
|
+
validateVat(value, options = {}) {
|
|
111
|
+
const { traderName, ...requestOptions } = options;
|
|
112
|
+
const extra = traderName && traderName.trim() !== "" ? { traderName } : void 0;
|
|
113
|
+
return this.#validate("vat", value, requestOptions, extra);
|
|
105
114
|
}
|
|
106
115
|
/**
|
|
107
116
|
* Validate a Canadian Social Insurance Number: format and Luhn check digit.
|
|
@@ -135,14 +144,17 @@ var VerifNow = class {
|
|
|
135
144
|
*
|
|
136
145
|
* The typed helpers above call this. Use it directly when the rule is chosen at runtime.
|
|
137
146
|
*/
|
|
138
|
-
|
|
147
|
+
validate(rule, value, options = {}) {
|
|
148
|
+
return this.#validate(rule, value, options);
|
|
149
|
+
}
|
|
150
|
+
async #validate(rule, value, options, extra) {
|
|
139
151
|
if (typeof value !== "string" || value.trim() === "") {
|
|
140
152
|
throw new VerifNowRequestError(
|
|
141
153
|
`Cannot validate an empty value for rule "${rule}".`
|
|
142
154
|
);
|
|
143
155
|
}
|
|
144
156
|
const url = `${this.#baseUrl}/api/v1/validate/${rule}`;
|
|
145
|
-
const body = JSON.stringify({ value });
|
|
157
|
+
const body = JSON.stringify({ value, ...extra });
|
|
146
158
|
return this.#withRetry(
|
|
147
159
|
() => this.#requestOnce("POST", url, body, options, (payload, quota) => mapResult(payload, quota))
|
|
148
160
|
);
|
|
@@ -401,7 +413,9 @@ function mapVatDetails(raw) {
|
|
|
401
413
|
traderName: asString(d.trader_name),
|
|
402
414
|
traderAddress: asString(d.trader_address),
|
|
403
415
|
viesAvailable: asBoolean(d.vies_available),
|
|
404
|
-
consultationNumber: asString(d.consultation_number)
|
|
416
|
+
consultationNumber: asString(d.consultation_number),
|
|
417
|
+
traderNameMatch: asString(d.trader_name_match),
|
|
418
|
+
traderNameMatchSource: asString(d.trader_name_match_source)
|
|
405
419
|
};
|
|
406
420
|
}
|
|
407
421
|
function mapPhoneDetails(raw) {
|
|
@@ -460,7 +474,11 @@ function mapCountryVatRates(raw) {
|
|
|
460
474
|
countryCode: asString(raw.countryCode) ?? "",
|
|
461
475
|
standardRate: asNumber(raw.standardRate) ?? Number.NaN,
|
|
462
476
|
reducedRates: numbers(raw.reducedRates),
|
|
463
|
-
regionalRates: Array.isArray(raw.regionalRates) ? raw.regionalRates.filter((r) => r !== null && typeof r === "object").map((r) => ({
|
|
477
|
+
regionalRates: Array.isArray(raw.regionalRates) ? raw.regionalRates.filter((r) => r !== null && typeof r === "object").map((r) => ({
|
|
478
|
+
rate: asNumber(r.rate) ?? Number.NaN,
|
|
479
|
+
note: asString(r.note),
|
|
480
|
+
euVatArea: asBoolean(r.euVatArea)
|
|
481
|
+
})) : [],
|
|
464
482
|
situationOn: asString(raw.situationOn),
|
|
465
483
|
fetchedAt: asDate(raw.fetchedAt)
|
|
466
484
|
};
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/errors.ts","../src/version.ts","../src/client.ts","../src/types.ts"],"sourcesContent":["import type { QuotaInfo } from './types.js';\n\n/**\n * Base class for every error this SDK throws.\n *\n * The SDK fails loudly on purpose. A validation client that swallows a network failure and\n * reports `valid: true` turns an outage into silently accepted bad data, and the outage stays\n * invisible until someone audits the database. Catch these and decide explicitly — accepting the\n * input on failure is a reasonable choice, but it should be a choice.\n *\n * @example\n * ```ts\n * try {\n * const result = await client.validateEmail(input);\n * return result.valid;\n * } catch (error) {\n * if (error instanceof VerifNowRateLimitError) throw error; // back-pressure, do not swallow\n * logger.warn({ error }, 'VerifNow unavailable, accepting input unverified');\n * return true;\n * }\n * ```\n */\nexport class VerifNowError extends Error {\n /** HTTP status, when the failure came back from the API rather than the network. */\n readonly status?: number;\n /** Correlation id from the `X-Request-Id` response header, useful in support requests. */\n readonly requestId?: string;\n\n constructor(\n message: string,\n options: { status?: number; requestId?: string; cause?: unknown } = {},\n ) {\n super(message, { cause: options.cause });\n this.name = new.target.name;\n this.status = options.status;\n this.requestId = options.requestId;\n Error.captureStackTrace?.(this, new.target);\n }\n}\n\n/** The API key is missing, malformed, revoked, or not authorised for this endpoint (401/403). */\nexport class VerifNowAuthError extends VerifNowError {}\n\n/**\n * The request was rejected as malformed (400).\n *\n * Retrying is pointless — the payload itself needs to change.\n */\nexport class VerifNowRequestError extends VerifNowError {}\n\n/** The monthly quota or the concurrency limit was exceeded (429). */\nexport class VerifNowRateLimitError extends VerifNowError {\n /** Quota counters from the response headers, when present. */\n readonly quota?: QuotaInfo;\n /** Seconds to wait before retrying, derived from `Retry-After` or `X-RateLimit-Reset`. */\n readonly retryAfterSeconds?: number;\n\n constructor(\n message: string,\n options: {\n status?: number;\n requestId?: string;\n cause?: unknown;\n quota?: QuotaInfo;\n retryAfterSeconds?: number;\n } = {},\n ) {\n super(message, options);\n this.quota = options.quota;\n this.retryAfterSeconds = options.retryAfterSeconds;\n }\n}\n\n/** The API failed to process the request (5xx). Retried automatically before surfacing. */\nexport class VerifNowServerError extends VerifNowError {}\n\n/**\n * The API could not be reached at all: DNS failure, refused connection, TLS error, or the\n * request exceeded `timeoutMs`.\n *\n * A wrong `baseUrl` surfaces here, which is why it names the URL it tried.\n */\nexport class VerifNowConnectionError extends VerifNowError {\n /** True when the failure was the client-side timeout rather than a transport error. */\n readonly timedOut: boolean;\n\n constructor(\n message: string,\n options: { cause?: unknown; timedOut?: boolean } = {},\n ) {\n super(message, { cause: options.cause });\n this.timedOut = options.timedOut ?? false;\n }\n}\n\n/** The API returned a success status with a body this SDK could not parse. */\nexport class VerifNowResponseError extends VerifNowError {}\n","/**\n * SDK version, sent to the API as `X-VerifNow-SDK: node/<version>` so calls made through an\n * official SDK can be told apart from hand-rolled integrations.\n *\n * Kept in sync with `package.json` by a test — bump both together.\n */\nexport const VERSION = '1.8.0';\n","import {\n VerifNowAuthError,\n VerifNowConnectionError,\n VerifNowError,\n VerifNowRateLimitError,\n VerifNowRequestError,\n VerifNowResponseError,\n VerifNowServerError,\n} from './errors.js';\nimport type {\n CountryVatRates,\n EmailDetails,\n EmailSignals,\n IbanDetails,\n NasDetails,\n NifDetails,\n SsnDetails,\n PhoneDetails,\n QuotaInfo,\n RetryOptions,\n ValidationResult,\n ValidationRule,\n VatDetails,\n VatRates,\n VerifNowOptions,\n} from './types.js';\nimport { VERSION } from './version.js';\n\nconst DEFAULT_BASE_URL = 'https://api.verifnow.io';\nconst DEFAULT_TIMEOUT_MS = 5_000;\nconst DEFAULT_RETRY: Required<RetryOptions> = {\n attempts: 2,\n backoffMs: 200,\n maxBackoffMs: 2_000,\n};\n\n/** Per-call overrides. */\nexport interface RequestOptions {\n /** Override the client timeout for this call. */\n timeoutMs?: number;\n /** Cancel the call from your own controller. Combined with the timeout. */\n signal?: AbortSignal;\n}\n\n/**\n * Client for the VerifNow validation API.\n *\n * @example\n * ```ts\n * import { VerifNow } from '@verifnow/sdk';\n *\n * const client = new VerifNow({ apiKey: process.env.VERIFNOW_API_KEY! });\n * const result = await client.validateEmail('user@example.com');\n *\n * if (!result.valid) console.log(result.message);\n * if (result.emailDetails?.signals?.typoDetected) {\n * console.log('Did you mean', result.emailDetails.signals.suggestedDomain);\n * }\n * ```\n */\nexport class VerifNow {\n readonly #apiKey: string;\n readonly #baseUrl: string;\n readonly #timeoutMs: number;\n readonly #retry: Required<RetryOptions> | null;\n readonly #headers: Record<string, string>;\n readonly #fetch: typeof globalThis.fetch;\n\n constructor(options: VerifNowOptions) {\n if (!options?.apiKey || options.apiKey.trim() === '') {\n throw new VerifNowError(\n 'A VerifNow API key is required. Create one in the dashboard and pass it as `apiKey`.',\n );\n }\n\n const fetchImpl = options.fetch ?? globalThis.fetch;\n if (typeof fetchImpl !== 'function') {\n throw new VerifNowError(\n 'No global fetch available. Use Node 18 or later, or pass a `fetch` implementation.',\n );\n }\n\n this.#apiKey = options.apiKey.trim();\n // Trailing slashes would produce `//api/v1/...`, which some proxies reject.\n this.#baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/, '');\n this.#timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n this.#retry =\n options.retry === false ? null : { ...DEFAULT_RETRY, ...(options.retry ?? {}) };\n this.#headers = options.headers ?? {};\n this.#fetch = fetchImpl.bind(globalThis);\n }\n\n /** Validate an email address: syntax, DNS/MX, typo, disposable, role-based, quality score. */\n validateEmail(value: string, options?: RequestOptions): Promise<ValidationResult> {\n return this.validate('email', value, options);\n }\n\n /**\n * Validate a phone number against its country's numbering plan.\n *\n * The number must include its country code (`+33…` or `0033…`). Valid numbers come back in\n * E.164 as `normalizedValue`, with country and line type in `phoneDetails`.\n */\n validatePhone(value: string, options?: RequestOptions): Promise<ValidationResult> {\n return this.validate('phone', value, options);\n }\n\n /**\n * Validate an IBAN against the SWIFT registry entry for its country, then its check digits.\n *\n * `ibanDetails` reports the two separately: check digits catch a typo, the registry catches an\n * account number that could never exist in that country.\n */\n validateIban(value: string, options?: RequestOptions): Promise<ValidationResult> {\n return this.validate('iban', value, options);\n }\n\n /** Validate a VAT number. */\n validateVat(value: string, options?: RequestOptions): Promise<ValidationResult> {\n return this.validate('vat', value, options);\n }\n\n /**\n * Validate a Canadian Social Insurance Number: format and Luhn check digit.\n *\n * `nasDetails` flags a temporary resident's number (it expires with their permit) and numbers\n * from series not issued to individuals. Only collect a SIN where the law requires it.\n */\n validateNas(value: string, options?: RequestOptions): Promise<ValidationResult> {\n return this.validate('nas', value, options);\n }\n\n /**\n * Validate a US Social Security Number against the numbers the SSA never issues.\n *\n * An SSN has no check digit: a typo that lands on another possible number cannot be caught, and\n * only the SSA can confirm a number was issued. `ssnDetails.itin` flags an IRS ITIN.\n */\n validateSsn(value: string, options?: RequestOptions): Promise<ValidationResult> {\n return this.validate('ssn', value, options);\n }\n\n /**\n * Validate a Spanish NIF: a DNI, a NIE (foreign nationals), the K/L/M series, or a company NIF.\n *\n * `nifDetails` says which, whether it belongs to a person, and for a company its legal form.\n * Spanish only — a Portuguese NIF is a different scheme and is not accepted here.\n */\n validateNif(value: string, options?: RequestOptions): Promise<ValidationResult> {\n return this.validate('nif', value, options);\n }\n\n /**\n * Validate a value against any rule.\n *\n * The typed helpers above call this. Use it directly when the rule is chosen at runtime.\n */\n async validate(\n rule: ValidationRule,\n value: string,\n options: RequestOptions = {},\n ): Promise<ValidationResult> {\n if (typeof value !== 'string' || value.trim() === '') {\n // Caught here rather than server-side: an empty value consumes quota and can only fail.\n throw new VerifNowRequestError(\n `Cannot validate an empty value for rule \"${rule}\".`,\n );\n }\n\n const url = `${this.#baseUrl}/api/v1/validate/${rule}`;\n const body = JSON.stringify({ value });\n return this.#withRetry(() =>\n this.#requestOnce('POST', url, body, options, (payload, quota) => mapResult(payload, quota)),\n );\n }\n\n /**\n * EU VAT rates of every member state, from the European Commission's TEDB.\n *\n * Public reference data: the call spends no quota. These are the rates a member state has, not\n * the rate a sale is charged — in B2B trade between member states the invoice is usually\n * zero-rated under the reverse charge whatever the buyer's country rate is.\n */\n async vatRates(options: RequestOptions = {}): Promise<VatRates> {\n const url = `${this.#baseUrl}/api/v1/vat/rates`;\n return this.#withRetry(() =>\n this.#requestOnce('GET', url, undefined, options, (payload) => mapVatRates(payload)),\n );\n }\n\n /**\n * One EU member state's VAT rates. Accepts `GR` for Greece as well as `EL`.\n *\n * A code outside the 27 member states throws {@link VerifNowRequestError} (HTTP 404).\n */\n async vatRate(countryCode: string, options: RequestOptions = {}): Promise<CountryVatRates> {\n if (typeof countryCode !== 'string' || countryCode.trim() === '') {\n throw new VerifNowRequestError('A member state code is required, e.g. \"FR\".');\n }\n const url = `${this.#baseUrl}/api/v1/vat/rates/${encodeURIComponent(countryCode.trim())}`;\n return this.#withRetry(() =>\n this.#requestOnce('GET', url, undefined, options, (payload) => mapCountryVatRates(payload)),\n );\n }\n\n /** Runs one request under the retry policy. */\n async #withRetry<T>(attemptOnce: () => Promise<T>): Promise<T> {\n const maxAttempts = this.#retry ? this.#retry.attempts + 1 : 1;\n\n let lastError: VerifNowError | undefined;\n\n for (let attempt = 0; attempt < maxAttempts; attempt++) {\n try {\n return await attemptOnce();\n } catch (error) {\n if (!(error instanceof VerifNowError)) throw error;\n lastError = error;\n\n const isLastAttempt = attempt === maxAttempts - 1;\n if (isLastAttempt || !this.#retry) throw error;\n\n const delay = this.#retryDelay(error, attempt);\n if (delay === null) throw error;\n\n await sleep(delay);\n }\n }\n\n /* c8 ignore next -- the loop either returns or throws */\n throw lastError ?? new VerifNowError('Request failed');\n }\n\n /**\n * How long to wait before retrying, or `null` when the error should surface immediately.\n *\n * A 429 is retried only when the reset is close: the concurrency limit clears in\n * milliseconds, but a spent monthly quota does not, and sleeping on it helps nobody.\n */\n #retryDelay(error: VerifNowError, attempt: number): number | null {\n const retry = this.#retry!;\n const backoff = Math.min(retry.backoffMs * 2 ** attempt, retry.maxBackoffMs);\n\n if (error instanceof VerifNowRateLimitError) {\n const waitMs = (error.retryAfterSeconds ?? 0) * 1000;\n if (waitMs > retry.maxBackoffMs) return null;\n return Math.max(waitMs, backoff);\n }\n\n if (error instanceof VerifNowServerError) return backoff;\n // A timeout is retried: the deadline is ours, and the next attempt gets a fresh one.\n if (error instanceof VerifNowConnectionError) return backoff;\n\n // 400, 401 and unparseable bodies will fail identically on a second attempt.\n return null;\n }\n\n async #requestOnce<T>(\n method: 'GET' | 'POST',\n url: string,\n body: string | undefined,\n options: RequestOptions,\n map: (payload: Record<string, unknown>, quota: QuotaInfo | undefined) => T,\n ): Promise<T> {\n const timeoutMs = options.timeoutMs ?? this.#timeoutMs;\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n const abortFromCaller = () => controller.abort();\n options.signal?.addEventListener('abort', abortFromCaller, { once: true });\n\n let response: Response;\n try {\n response = await this.#fetch(url, {\n method,\n headers: {\n ...this.#headers,\n ...(body === undefined ? {} : { 'Content-Type': 'application/json' }),\n Accept: 'application/json',\n 'X-API-KEY': this.#apiKey,\n 'X-VerifNow-SDK': `node/${VERSION}`,\n },\n body,\n signal: controller.signal,\n });\n } catch (cause) {\n // The caller's own cancellation is theirs to handle, not a transport failure.\n if (options.signal?.aborted) throw cause;\n\n const timedOut = controller.signal.aborted;\n throw new VerifNowConnectionError(\n timedOut\n ? `VerifNow request to ${url} timed out after ${timeoutMs}ms.`\n : `Could not reach the VerifNow API at ${url}. Check \\`baseUrl\\` and network access.`,\n { cause, timedOut },\n );\n } finally {\n clearTimeout(timer);\n options.signal?.removeEventListener('abort', abortFromCaller);\n }\n\n return this.#handleResponse(response, map);\n }\n\n async #handleResponse<T>(\n response: Response,\n map: (payload: Record<string, unknown>, quota: QuotaInfo | undefined) => T,\n ): Promise<T> {\n const requestId = response.headers.get('X-Request-Id') ?? undefined;\n const quota = parseQuota(response.headers);\n\n if (response.ok) {\n let payload: unknown;\n try {\n payload = await response.json();\n } catch (cause) {\n throw new VerifNowResponseError(\n 'VerifNow returned a success status with a body that is not valid JSON.',\n { status: response.status, requestId, cause },\n );\n }\n\n if (payload === null || typeof payload !== 'object') {\n throw new VerifNowResponseError(\n 'VerifNow returned an unexpected response shape.',\n { status: response.status, requestId },\n );\n }\n\n return map(payload as Record<string, unknown>, quota);\n }\n\n const message = await readErrorMessage(response);\n const context = { status: response.status, requestId };\n\n if (response.status === 401 || response.status === 403) {\n throw new VerifNowAuthError(\n `VerifNow rejected the API key (${response.status}): ${message}`,\n context,\n );\n }\n\n if (response.status === 429) {\n throw new VerifNowRateLimitError(`VerifNow rate limit reached: ${message}`, {\n ...context,\n quota,\n retryAfterSeconds: parseRetryAfter(response.headers, quota),\n });\n }\n\n if (response.status >= 500) {\n throw new VerifNowServerError(\n `VerifNow returned ${response.status}: ${message}`,\n context,\n );\n }\n\n throw new VerifNowRequestError(\n `VerifNow rejected the request (${response.status}): ${message}`,\n context,\n );\n }\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nfunction toNumber(value: string | null): number | undefined {\n if (value === null) return undefined;\n const parsed = Number(value);\n return Number.isFinite(parsed) ? parsed : undefined;\n}\n\nfunction parseQuota(headers: Headers): QuotaInfo | undefined {\n const limit = toNumber(headers.get('X-RateLimit-Limit'));\n const remaining = toNumber(headers.get('X-RateLimit-Remaining'));\n const resetSeconds = toNumber(headers.get('X-RateLimit-Reset'));\n const overage = headers.get('X-Quota-Overage') === 'true';\n\n if (\n limit === undefined &&\n remaining === undefined &&\n resetSeconds === undefined &&\n !overage\n ) {\n return undefined;\n }\n\n return {\n limit,\n remaining,\n resetAt: resetSeconds === undefined ? undefined : new Date(resetSeconds * 1000),\n overage,\n };\n}\n\nfunction parseRetryAfter(headers: Headers, quota?: QuotaInfo): number | undefined {\n const retryAfter = headers.get('Retry-After');\n if (retryAfter !== null) {\n const seconds = Number(retryAfter);\n if (Number.isFinite(seconds)) return seconds;\n\n // RFC 7231 also allows an HTTP-date.\n const asDate = Date.parse(retryAfter);\n if (!Number.isNaN(asDate)) {\n return Math.max(0, Math.ceil((asDate - Date.now()) / 1000));\n }\n }\n\n if (quota?.resetAt) {\n return Math.max(0, Math.ceil((quota.resetAt.getTime() - Date.now()) / 1000));\n }\n\n return undefined;\n}\n\nasync function readErrorMessage(response: Response): Promise<string> {\n try {\n const text = await response.text();\n if (!text) return response.statusText || 'no details';\n\n try {\n const parsed = JSON.parse(text) as Record<string, unknown>;\n const message = parsed.message ?? parsed.error;\n if (typeof message === 'string' && message !== '') return message;\n } catch {\n // Not JSON — a proxy or the servlet container's default error page.\n }\n\n return text.slice(0, 500);\n } catch {\n return response.statusText || 'no details';\n }\n}\n\nfunction asString(value: unknown): string | undefined {\n return typeof value === 'string' ? value : undefined;\n}\n\nfunction asBoolean(value: unknown): boolean | undefined {\n return typeof value === 'boolean' ? value : undefined;\n}\n\nfunction asNumber(value: unknown): number | undefined {\n return typeof value === 'number' && Number.isFinite(value) ? value : undefined;\n}\n\nfunction mapSignals(raw: unknown): EmailSignals | undefined {\n if (raw === null || typeof raw !== 'object') return undefined;\n const s = raw as Record<string, unknown>;\n\n return {\n syntaxValid: asBoolean(s.syntax_valid),\n mxValid: asBoolean(s.mx_valid),\n typoDetected: asBoolean(s.typo_detected),\n suggestedDomain: asString(s.suggested_domain),\n disposable: asBoolean(s.disposable),\n roleBased: asBoolean(s.role_based),\n freeProvider: asBoolean(s.free_provider),\n domainAgeDays: asNumber(s.domain_age_days),\n mxProvider: asString(s.mx_provider),\n mxQualityScore: asNumber(s.mx_quality_score),\n };\n}\n\nfunction mapEmailDetails(raw: unknown): EmailDetails | undefined {\n if (raw === null || typeof raw !== 'object') return undefined;\n const d = raw as Record<string, unknown>;\n\n return {\n signals: mapSignals(d.signals),\n riskScore: asNumber(d.risk_score),\n riskLevel: asString(d.risk_level) as EmailDetails['riskLevel'],\n deliverability: asString(d.deliverability) as EmailDetails['deliverability'],\n appliedLevel: asString(d.applied_level) as EmailDetails['appliedLevel'],\n };\n}\n\n/**\n * Reads `registered`, preserving the difference between `false` and `null`.\n *\n * `asBoolean` cannot be used here: it maps `null` to `undefined`, which would erase the one\n * distinction the whole VAT design exists to carry. `false` means the registry answered and the\n * number is not there; `null` means nobody could ask. A caller that cannot tell them apart will\n * reject real businesses whenever VIES is down.\n *\n * A missing key is read as `null` for the same reason — unknown, not absent.\n */\nfunction asRegistered(value: unknown): boolean | null {\n return typeof value === 'boolean' ? value : null;\n}\n\nfunction asDate(value: unknown): Date | undefined {\n if (typeof value !== 'string') return undefined;\n const parsed = new Date(value);\n return Number.isNaN(parsed.getTime()) ? undefined : parsed;\n}\n\nfunction mapVatDetails(raw: unknown): VatDetails | undefined {\n if (raw === null || typeof raw !== 'object') return undefined;\n const d = raw as Record<string, unknown>;\n\n return {\n formatValid: asBoolean(d.format_valid),\n registered: asRegistered(d.registered),\n countryCode: asString(d.country_code),\n source: asString(d.source) as VatDetails['source'],\n checkedAt: asDate(d.checked_at),\n traderName: asString(d.trader_name),\n traderAddress: asString(d.trader_address),\n viesAvailable: asBoolean(d.vies_available),\n consultationNumber: asString(d.consultation_number),\n };\n}\n\nfunction mapPhoneDetails(raw: unknown): PhoneDetails | undefined {\n if (raw === null || typeof raw !== 'object') return undefined;\n const d = raw as Record<string, unknown>;\n\n return {\n countryCode: asString(d.country_code),\n callingCode: asNumber(d.calling_code),\n lineType: asString(d.line_type) as PhoneDetails['lineType'],\n internationalFormat: asString(d.international_format),\n nationalFormat: asString(d.national_format),\n };\n}\n\nfunction mapIbanDetails(raw: unknown): IbanDetails | undefined {\n if (raw === null || typeof raw !== 'object') return undefined;\n const d = raw as Record<string, unknown>;\n\n return {\n countryCode: asString(d.country_code),\n sepa: asBoolean(d.sepa),\n structureValid: asBoolean(d.structure_valid),\n checksumValid: asBoolean(d.checksum_valid),\n length: asNumber(d.length),\n expectedLength: asNumber(d.expected_length),\n formatted: asString(d.formatted),\n };\n}\n\nfunction mapNasDetails(raw: unknown): NasDetails | undefined {\n if (raw === null || typeof raw !== 'object') return undefined;\n const d = raw as Record<string, unknown>;\n\n return {\n checksumValid: asBoolean(d.checksum_valid),\n temporaryResident: asBoolean(d.temporary_resident),\n individualSeries: asBoolean(d.individual_series),\n formatted: asString(d.formatted),\n };\n}\n\nfunction mapNifDetails(raw: unknown): NifDetails | undefined {\n if (raw === null || typeof raw !== 'object') return undefined;\n const d = raw as Record<string, unknown>;\n\n return {\n type: asString(d.type) as NifDetails['type'],\n naturalPerson: asBoolean(d.natural_person),\n checksumValid: asBoolean(d.checksum_valid),\n entityLetter: asString(d.entity_letter),\n entityType: asString(d.entity_type),\n };\n}\n\nfunction mapSsnDetails(raw: unknown): SsnDetails | undefined {\n if (raw === null || typeof raw !== 'object') return undefined;\n const d = raw as Record<string, unknown>;\n\n return { itin: asBoolean(d.itin) };\n}\n\n/**\n * Maps the API's snake_case diagnostics onto camelCase, so a TypeScript caller is not switching\n * naming conventions mid-expression. The untouched body stays available on `raw`.\n */\nfunction mapCountryVatRates(raw: Record<string, unknown>): CountryVatRates {\n const numbers = (value: unknown): number[] =>\n Array.isArray(value) ? value.filter((v): v is number => asNumber(v) !== undefined) : [];\n\n return {\n countryCode: asString(raw.countryCode) ?? '',\n standardRate: asNumber(raw.standardRate) ?? Number.NaN,\n reducedRates: numbers(raw.reducedRates),\n regionalRates: Array.isArray(raw.regionalRates)\n ? raw.regionalRates\n .filter((r): r is Record<string, unknown> => r !== null && typeof r === 'object')\n .map((r) => ({ rate: asNumber(r.rate) ?? Number.NaN, note: asString(r.note) }))\n : [],\n situationOn: asString(raw.situationOn),\n fetchedAt: asDate(raw.fetchedAt),\n };\n}\n\nfunction mapVatRates(raw: Record<string, unknown>): VatRates {\n const rates = Array.isArray(raw.rates)\n ? raw.rates\n .filter((r): r is Record<string, unknown> => r !== null && typeof r === 'object')\n .map(mapCountryVatRates)\n : [];\n return {\n source: asString(raw.source) ?? 'TEDB',\n sourceUrl: asString(raw.sourceUrl),\n rates,\n };\n}\n\nfunction mapResult(\n payload: Record<string, unknown>,\n quota: QuotaInfo | undefined,\n): ValidationResult {\n return {\n valid: payload.valid === true,\n message: asString(payload.message),\n normalizedValue: asString(payload.normalizedValue) ?? null,\n originalValue: asString(payload.originalValue),\n validationLevel: asString(payload.validationLevel) as ValidationResult['validationLevel'],\n emailDetails: mapEmailDetails(payload.emailDetails),\n vatDetails: mapVatDetails(payload.vatDetails),\n phoneDetails: mapPhoneDetails(payload.phoneDetails),\n ibanDetails: mapIbanDetails(payload.ibanDetails),\n nasDetails: mapNasDetails(payload.nasDetails),\n nifDetails: mapNifDetails(payload.nifDetails),\n ssnDetails: mapSsnDetails(payload.ssnDetails),\n quota,\n raw: payload,\n };\n}\n","/**\n * Validation rules exposed by the VerifNow API.\n *\n * Each maps to `POST /api/v1/validate/{rule}`.\n */\nexport type ValidationRule =\n | 'email'\n | 'phone'\n | 'iban'\n | 'vat'\n | 'nas'\n | 'ssn'\n | 'nif';\n\nexport const VALIDATION_RULES: readonly ValidationRule[] = [\n 'email',\n 'phone',\n 'iban',\n 'vat',\n 'nas',\n 'ssn',\n 'nif',\n] as const;\n\n/**\n * Depth of checks applied to a request, decided by the plan attached to the API key.\n *\n * `STANDARD` runs on the FREE and STARTER plans, `ADVANCED` on GROWTH, `PREMIUM` on PRO.\n * Branch on this rather than on the plan name: it is the only value that tells you which\n * signals are actually present in the response.\n */\nexport type ValidationLevel = 'BASIC' | 'STANDARD' | 'ADVANCED' | 'PREMIUM';\n\n/** Categorical risk assessment. Returned from `ADVANCED` depth upward. */\nexport type RiskLevel = 'LOW' | 'MEDIUM' | 'HIGH';\n\nexport type Deliverability =\n | 'DELIVERABLE'\n | 'RISKY'\n | 'UNDELIVERABLE'\n | 'UNKNOWN';\n\n/**\n * Per-signal breakdown behind an email verdict.\n *\n * Fields are `undefined` when the applied level does not compute them — the last four require\n * `ADVANCED` depth or higher. Check `ValidationResult.appliedLevel` before relying on one.\n */\nexport interface EmailSignals {\n /** The address matches the syntax pattern for the applied level. */\n syntaxValid?: boolean;\n /** The domain resolves and publishes MX (or fallback A) records. */\n mxValid?: boolean;\n /** A likely typo was found in the domain, e.g. `gmail.con`. */\n typoDetected?: boolean;\n /** The correction proposed when `typoDetected` is true. */\n suggestedDomain?: string;\n /** The domain belongs to a throwaway mailbox provider. */\n disposable?: boolean;\n /** The local part is a shared mailbox: `info@`, `admin@`, `noreply@`. */\n roleBased?: boolean;\n /** The domain is a consumer mailbox provider. Requires ADVANCED. */\n freeProvider?: boolean;\n /** Estimated age of the domain in days. Requires ADVANCED. */\n domainAgeDays?: number;\n /** Identified mail provider, e.g. `google`. Requires ADVANCED. */\n mxProvider?: string;\n /** Mail server quality between 0 and 1. Requires ADVANCED. */\n mxQualityScore?: number;\n}\n\n/** Email-specific diagnostics. Absent when the applied level is `BASIC`. */\nexport interface EmailDetails {\n signals?: EmailSignals;\n /** Aggregated risk on a 0–100 scale, where 0 is the lowest risk. */\n riskScore?: number;\n /** Categorical risk. Requires ADVANCED depth or higher. */\n riskLevel?: RiskLevel;\n deliverability?: Deliverability;\n /** The depth actually applied, echoed back by the API. */\n appliedLevel?: ValidationLevel;\n}\n\n/**\n * Where a VAT registration verdict came from.\n *\n * VIES publishes no SLA and drops member states several times a month, so a VAT answer is not\n * always a live one. Branch on this rather than on `ValidationResult.valid` whenever the\n * difference matters for your own compliance.\n */\nexport type VatSource =\n /** Confirmed against VIES during this request. */\n | 'LIVE'\n /** Served from a VIES answer less than 24 hours old. */\n | 'CACHE'\n /** VIES was unreachable, so an older cached answer was used. */\n | 'STALE'\n /** VIES was unreachable and nothing was cached. Registration is unknown. */\n | 'UNVERIFIED'\n /** The country is outside VIES, so no registry lookup is possible. */\n | 'NOT_APPLICABLE';\n\n/** VAT-specific diagnostics. Present on `vat` validations. */\nexport interface VatDetails {\n /** The number matches its member state's structure. Decided locally, never depends on VIES. */\n formatValid?: boolean;\n /**\n * Present in the member state's registry.\n *\n * **`null` means unknown, never \"not registered.\"** It is returned when VIES could not be\n * consulted. Treating `null` as `false` rejects legitimate customers during someone else's\n * outage — the single most expensive mistake available in VAT validation.\n */\n registered: boolean | null;\n /** Member state the number belongs to, e.g. `IE`. Greece is `EL`, Northern Ireland `XI`. */\n countryCode?: string;\n source?: VatSource;\n /** When the registration was last confirmed against VIES. */\n checkedAt?: Date;\n /** Registered trading name, when the member state discloses it. Germany does not. */\n traderName?: string;\n /** Registered address, when the member state discloses it. */\n traderAddress?: string;\n /** Whether VIES could answer for this country during the request. */\n viesAvailable?: boolean;\n /**\n * The consultation number VIES issued for this lookup — the receipt a tax authority accepts as\n * evidence that you checked. Present only when your account has its own VAT number configured,\n * because VIES issues one only to an identified requester.\n */\n consultationNumber?: string;\n}\n\n/**\n * Kind of line, according to the country's numbering plan.\n *\n * A `PREMIUM_RATE` or `VOIP` number is still `valid` — it exists. This is how you decide to\n * exclude one, rather than the API deciding for you.\n */\nexport type PhoneLineType =\n | 'MOBILE'\n | 'FIXED_LINE'\n /** The plan does not distinguish the two — the case for the US and Canada. */\n | 'FIXED_LINE_OR_MOBILE'\n | 'TOLL_FREE'\n | 'PREMIUM_RATE'\n | 'SHARED_COST'\n | 'VOIP'\n | 'PERSONAL_NUMBER'\n | 'PAGER'\n | 'UAN'\n | 'VOICEMAIL'\n | 'UNKNOWN';\n\n/**\n * Phone-specific diagnostics. Present whenever the input parsed as an international number —\n * including when it is invalid for its country, so you can tell the user which country it was\n * read as.\n */\nexport interface PhoneDetails {\n /** ISO 3166-1 alpha-2 country, e.g. `FR`. Absent when the calling code is shared by several. */\n countryCode?: string;\n /** International calling code without the plus sign, e.g. `33`. */\n callingCode?: number;\n /** Absent when the number is invalid. */\n lineType?: PhoneLineType;\n /** e.g. `+33 6 12 34 56 78`. Absent when the number is invalid. */\n internationalFormat?: string;\n /** e.g. `06 12 34 56 78`. Absent when the number is invalid. */\n nationalFormat?: string;\n}\n\n/**\n * IBAN-specific diagnostics. Present on `iban` validations.\n *\n * Structure and checksum are reported separately because they fail for different reasons:\n * `structureValid` answers \"could this be an account number in that country\" (the SWIFT\n * registry's length and layout), `checksumValid` answers \"was it typed correctly\" (mod-97).\n * There is no bank name or BIC — that needs a registry the API does not hold.\n */\nexport interface IbanDetails {\n /** The IBAN's country, from its first two characters. */\n countryCode?: string;\n /**\n * The country is inside the SEPA schemes' geographical scope, so a bank there may collect a\n * SEPA direct debit. Whether this particular bank does is published per bank, not per country,\n * and cannot be read from an IBAN.\n */\n sepa?: boolean;\n /** Length and character layout match the registry entry for that country. */\n structureValid?: boolean;\n /** The mod-97 check digits are correct. */\n checksumValid?: boolean;\n /** Length of the value as submitted, spaces removed. */\n length?: number;\n /** Length the registry requires for that country; absent for an unknown country. */\n expectedLength?: number;\n /** Print format, in groups of four. Present only for a valid IBAN. */\n formatted?: string;\n}\n\n/**\n * Canadian Social Insurance Number diagnostics. Present on `nas` validations.\n *\n * There is no province and no expiry date: the first digit no longer reliably identifies a\n * province, and a temporary resident's SIN expires with their permit, which only the document\n * shows.\n */\nexport interface NasDetails {\n /** The Luhn check digit is correct. */\n checksumValid?: boolean;\n /**\n * A 9-series number, issued to temporary residents. It expires with the holder's permit, and the\n * number itself does not say when — check the document.\n */\n temporaryResident?: boolean;\n /**\n * The first digit belongs to a series issued to individuals. `false` for numbers starting with\n * 0 or 8 — including 046 454 286, the government's sample number, which is why it is safe to\n * use in tests.\n */\n individualSeries?: boolean;\n /** Printed form, e.g. `046 454 286`. */\n formatted?: string;\n}\n\n/** The kinds of Spanish tax identification number. */\nexport type NifType =\n /** Spanish national with a DNI: 8 digits and a letter. */\n | 'DNI'\n /** Foreign national: X, Y or Z, 7 digits and a letter. */\n | 'NIE'\n /** Spanish national under 14 without a DNI. */\n | 'NIF_K'\n /** Spanish national resident abroad, staying under six months. */\n | 'NIF_L'\n /** Foreign national without a NIE. */\n | 'NIF_M'\n /** A company or other entity: a letter for the legal form, 7 digits, a control character. */\n | 'ENTITY';\n\n/** Spanish NIF diagnostics. Present on `nif` validations. */\nexport interface NifDetails {\n type?: NifType;\n /** The number belongs to a person rather than a company or other entity. */\n naturalPerson?: boolean;\n /** The control character is correct. */\n checksumValid?: boolean;\n /** For an entity, the letter that encodes its legal form, e.g. `B`. */\n entityLetter?: string;\n /** For an entity, its legal form, e.g. `Private limited company (Sociedad de responsabilidad limitada)`. */\n entityType?: string;\n}\n\n/**\n * US SSN diagnostics. Present on `ssn` validations.\n *\n * An SSN has no check digit, and since 2011 its first digits say nothing about a state, so there is\n * little a number can reveal about itself. The one thing worth knowing is whether it is an ITIN.\n */\nexport interface SsnDetails {\n /**\n * The number is an IRS ITIN, not an SSN: it starts with 9 and its fourth and fifth digits are in\n * 50-65, 70-88, 90-92 or 94-99. `valid` is `false` for the SSN, but an ITIN is an acceptable\n * taxpayer number where one is accepted (a W-9, for instance).\n */\n itin?: boolean;\n}\n\n/** Outcome of a single validation call. */\nexport interface ValidationResult {\n /** Whether the value passed every check the applied level ran. */\n valid: boolean;\n /** Human-readable explanation of the verdict. */\n message?: string;\n /** Canonical form of the input — `null` when the value is invalid. */\n normalizedValue: string | null;\n /** The value exactly as submitted. */\n originalValue?: string;\n /** Depth applied to this request. */\n validationLevel?: ValidationLevel;\n /** Present for email validations from `STANDARD` depth upward. */\n emailDetails?: EmailDetails;\n /** Present for VAT validations. */\n vatDetails?: VatDetails;\n /** Present for phone validations. The E.164 form is `normalizedValue`. */\n phoneDetails?: PhoneDetails;\n /** Present for IBAN validations. */\n ibanDetails?: IbanDetails;\n /** Present for Canadian SIN (`nas`) validations. */\n nasDetails?: NasDetails;\n /** Present for Spanish NIF validations. */\n nifDetails?: NifDetails;\n /** Present for US SSN validations. */\n ssnDetails?: SsnDetails;\n /** Quota state reported by the response headers. */\n quota?: QuotaInfo;\n /** The unmodified JSON body, for fields this SDK version does not model yet. */\n raw: Record<string, unknown>;\n}\n\n/** A VAT rate applying to part of a member state only — an overseas department, an island. */\nexport interface RegionalVatRate {\n rate: number;\n /** Where it applies, in the words of the Commission's TEDB. */\n note?: string;\n}\n\n/**\n * One EU member state's VAT rates, from the European Commission's TEDB.\n *\n * These are the rates the member state has, not the rate a sale is charged: which one applies\n * depends on who sells to whom and what. In B2B trade between member states the invoice is usually\n * zero-rated under the reverse charge, whatever the buyer's country rate is.\n */\nexport interface CountryVatRates {\n /** Member state as TEDB and VIES name it: Greece is `EL`. */\n countryCode: string;\n /** The national standard rate, e.g. `20` for France. */\n standardRate: number;\n /**\n * Every reduced, super-reduced and parking rate on the mainland territory, ascending.\n * TEDB's own sub-labels are inconsistent between member states, so they are not reproduced.\n */\n reducedRates: number[];\n /** Rates for part of the territory only, e.g. 8.5 % in Martinique, Guadeloupe and Réunion. */\n regionalRates: RegionalVatRate[];\n /**\n * The date TEDB says these rates apply from, as `YYYY-MM-DD`. Kept as a string: a date without\n * a time zone turned into a `Date` can land on the previous day.\n */\n situationOn?: string;\n /** When VerifNow last retrieved them from TEDB. */\n fetchedAt?: Date;\n}\n\n/** VAT rates of every EU member state. */\nexport interface VatRates {\n /** Always `TEDB`, the Commission's Taxes in Europe Database. */\n source: string;\n sourceUrl?: string;\n /** One entry per member state retrieved so far — normally all 27. */\n rates: CountryVatRates[];\n}\n\n/** Quota counters read from the `X-RateLimit-*` response headers. */\nexport interface QuotaInfo {\n /** Validations included in the current billing period. */\n limit?: number;\n /** Validations left before overage or blocking. */\n remaining?: number;\n /** When the current period resets. */\n resetAt?: Date;\n /** True once you are past the included quota and into per-unit billing. */\n overage?: boolean;\n}\n\nexport interface RetryOptions {\n /**\n * Retry attempts after the first failure. Defaults to 2, so up to 3 requests in total.\n * Only connection failures, 429 and 5xx are retried — never a 400 or 401, which will not\n * succeed on a second try.\n */\n attempts?: number;\n /** Delay before the first retry, in ms. Doubles each attempt. Defaults to 200. */\n backoffMs?: number;\n /** Upper bound on a single backoff delay, in ms. Defaults to 2000. */\n maxBackoffMs?: number;\n}\n\nexport interface VerifNowOptions {\n /** API key created in the VerifNow dashboard. Sent as the `X-API-KEY` header. */\n apiKey: string;\n /** Override the API origin. Defaults to `https://api.verifnow.io`. */\n baseUrl?: string;\n /** Abort a single request after this many ms. Defaults to 5000. */\n timeoutMs?: number;\n /** Retry policy, or `false` to disable retries entirely. */\n retry?: RetryOptions | false;\n /** Extra headers merged into every request. */\n headers?: Record<string, string>;\n /**\n * Replacement for the global `fetch`, for tests or a custom agent.\n * Defaults to `globalThis.fetch`.\n */\n fetch?: typeof globalThis.fetch;\n}\n"],"mappings":"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","names":["asDate"]}
|
|
1
|
+
{"version":3,"sources":["../src/errors.ts","../src/version.ts","../src/client.ts","../src/types.ts"],"sourcesContent":["import type { QuotaInfo } from './types.js';\n\n/**\n * Base class for every error this SDK throws.\n *\n * The SDK fails loudly on purpose. A validation client that swallows a network failure and\n * reports `valid: true` turns an outage into silently accepted bad data, and the outage stays\n * invisible until someone audits the database. Catch these and decide explicitly — accepting the\n * input on failure is a reasonable choice, but it should be a choice.\n *\n * @example\n * ```ts\n * try {\n * const result = await client.validateEmail(input);\n * return result.valid;\n * } catch (error) {\n * if (error instanceof VerifNowRateLimitError) throw error; // back-pressure, do not swallow\n * logger.warn({ error }, 'VerifNow unavailable, accepting input unverified');\n * return true;\n * }\n * ```\n */\nexport class VerifNowError extends Error {\n /** HTTP status, when the failure came back from the API rather than the network. */\n readonly status?: number;\n /** Correlation id from the `X-Request-Id` response header, useful in support requests. */\n readonly requestId?: string;\n\n constructor(\n message: string,\n options: { status?: number; requestId?: string; cause?: unknown } = {},\n ) {\n super(message, { cause: options.cause });\n this.name = new.target.name;\n this.status = options.status;\n this.requestId = options.requestId;\n Error.captureStackTrace?.(this, new.target);\n }\n}\n\n/** The API key is missing, malformed, revoked, or not authorised for this endpoint (401/403). */\nexport class VerifNowAuthError extends VerifNowError {}\n\n/**\n * The request was rejected as malformed (400).\n *\n * Retrying is pointless — the payload itself needs to change.\n */\nexport class VerifNowRequestError extends VerifNowError {}\n\n/** The monthly quota or the concurrency limit was exceeded (429). */\nexport class VerifNowRateLimitError extends VerifNowError {\n /** Quota counters from the response headers, when present. */\n readonly quota?: QuotaInfo;\n /** Seconds to wait before retrying, derived from `Retry-After` or `X-RateLimit-Reset`. */\n readonly retryAfterSeconds?: number;\n\n constructor(\n message: string,\n options: {\n status?: number;\n requestId?: string;\n cause?: unknown;\n quota?: QuotaInfo;\n retryAfterSeconds?: number;\n } = {},\n ) {\n super(message, options);\n this.quota = options.quota;\n this.retryAfterSeconds = options.retryAfterSeconds;\n }\n}\n\n/** The API failed to process the request (5xx). Retried automatically before surfacing. */\nexport class VerifNowServerError extends VerifNowError {}\n\n/**\n * The API could not be reached at all: DNS failure, refused connection, TLS error, or the\n * request exceeded `timeoutMs`.\n *\n * A wrong `baseUrl` surfaces here, which is why it names the URL it tried.\n */\nexport class VerifNowConnectionError extends VerifNowError {\n /** True when the failure was the client-side timeout rather than a transport error. */\n readonly timedOut: boolean;\n\n constructor(\n message: string,\n options: { cause?: unknown; timedOut?: boolean } = {},\n ) {\n super(message, { cause: options.cause });\n this.timedOut = options.timedOut ?? false;\n }\n}\n\n/** The API returned a success status with a body this SDK could not parse. */\nexport class VerifNowResponseError extends VerifNowError {}\n","/**\n * SDK version, sent to the API as `X-VerifNow-SDK: node/<version>` so calls made through an\n * official SDK can be told apart from hand-rolled integrations.\n *\n * Kept in sync with `package.json` by a test — bump both together.\n */\nexport const VERSION = '1.9.0';\n","import {\n VerifNowAuthError,\n VerifNowConnectionError,\n VerifNowError,\n VerifNowRateLimitError,\n VerifNowRequestError,\n VerifNowResponseError,\n VerifNowServerError,\n} from './errors.js';\nimport type {\n CountryVatRates,\n EmailDetails,\n EmailSignals,\n IbanDetails,\n NasDetails,\n NifDetails,\n SsnDetails,\n PhoneDetails,\n QuotaInfo,\n RetryOptions,\n ValidationResult,\n ValidationRule,\n VatDetails,\n VatRates,\n VerifNowOptions,\n} from './types.js';\nimport { VERSION } from './version.js';\n\nconst DEFAULT_BASE_URL = 'https://api.verifnow.io';\nconst DEFAULT_TIMEOUT_MS = 5_000;\nconst DEFAULT_RETRY: Required<RetryOptions> = {\n attempts: 2,\n backoffMs: 200,\n maxBackoffMs: 2_000,\n};\n\n/** Per-call overrides. */\nexport interface RequestOptions {\n /** Override the client timeout for this call. */\n timeoutMs?: number;\n /** Cancel the call from your own controller. Combined with the timeout. */\n signal?: AbortSignal;\n}\n\n/** Options for `validateVat`: the per-call options, plus the company expected to hold the number. */\nexport interface VatValidationOptions extends RequestOptions {\n /** The company you expect to hold this VAT number, e.g. from a supplier form. At most 200 characters. */\n traderName?: string;\n}\n\n/**\n * Client for the VerifNow validation API.\n *\n * @example\n * ```ts\n * import { VerifNow } from '@verifnow/sdk';\n *\n * const client = new VerifNow({ apiKey: process.env.VERIFNOW_API_KEY! });\n * const result = await client.validateEmail('user@example.com');\n *\n * if (!result.valid) console.log(result.message);\n * if (result.emailDetails?.signals?.typoDetected) {\n * console.log('Did you mean', result.emailDetails.signals.suggestedDomain);\n * }\n * ```\n */\nexport class VerifNow {\n readonly #apiKey: string;\n readonly #baseUrl: string;\n readonly #timeoutMs: number;\n readonly #retry: Required<RetryOptions> | null;\n readonly #headers: Record<string, string>;\n readonly #fetch: typeof globalThis.fetch;\n\n constructor(options: VerifNowOptions) {\n if (!options?.apiKey || options.apiKey.trim() === '') {\n throw new VerifNowError(\n 'A VerifNow API key is required. Create one in the dashboard and pass it as `apiKey`.',\n );\n }\n\n const fetchImpl = options.fetch ?? globalThis.fetch;\n if (typeof fetchImpl !== 'function') {\n throw new VerifNowError(\n 'No global fetch available. Use Node 18 or later, or pass a `fetch` implementation.',\n );\n }\n\n this.#apiKey = options.apiKey.trim();\n // Trailing slashes would produce `//api/v1/...`, which some proxies reject.\n this.#baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/, '');\n this.#timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n this.#retry =\n options.retry === false ? null : { ...DEFAULT_RETRY, ...(options.retry ?? {}) };\n this.#headers = options.headers ?? {};\n this.#fetch = fetchImpl.bind(globalThis);\n }\n\n /** Validate an email address: syntax, DNS/MX, typo, disposable, role-based, quality score. */\n validateEmail(value: string, options?: RequestOptions): Promise<ValidationResult> {\n return this.validate('email', value, options);\n }\n\n /**\n * Validate a phone number against its country's numbering plan.\n *\n * The number must include its country code (`+33…` or `0033…`). Valid numbers come back in\n * E.164 as `normalizedValue`, with country and line type in `phoneDetails`.\n */\n validatePhone(value: string, options?: RequestOptions): Promise<ValidationResult> {\n return this.validate('phone', value, options);\n }\n\n /**\n * Validate an IBAN against the SWIFT registry entry for its country, then its check digits.\n *\n * `ibanDetails` reports the two separately: check digits catch a typo, the registry catches an\n * account number that could never exist in that country.\n */\n validateIban(value: string, options?: RequestOptions): Promise<ValidationResult> {\n return this.validate('iban', value, options);\n }\n\n /**\n * Validate a VAT number.\n *\n * Pass `traderName` to ask whether the number belongs to that company:\n * `vatDetails.traderNameMatch` answers `MATCH`, `MISMATCH` or `NOT_AVAILABLE`, and\n * `traderNameMatchSource` says who compared — VerifNow against the name VIES publishes, or VIES\n * itself where it withholds the name but checks one (Spain). Germany does neither.\n */\n validateVat(value: string, options: VatValidationOptions = {}): Promise<ValidationResult> {\n const { traderName, ...requestOptions } = options;\n const extra = traderName && traderName.trim() !== '' ? { traderName } : undefined;\n return this.#validate('vat', value, requestOptions, extra);\n }\n\n /**\n * Validate a Canadian Social Insurance Number: format and Luhn check digit.\n *\n * `nasDetails` flags a temporary resident's number (it expires with their permit) and numbers\n * from series not issued to individuals. Only collect a SIN where the law requires it.\n */\n validateNas(value: string, options?: RequestOptions): Promise<ValidationResult> {\n return this.validate('nas', value, options);\n }\n\n /**\n * Validate a US Social Security Number against the numbers the SSA never issues.\n *\n * An SSN has no check digit: a typo that lands on another possible number cannot be caught, and\n * only the SSA can confirm a number was issued. `ssnDetails.itin` flags an IRS ITIN.\n */\n validateSsn(value: string, options?: RequestOptions): Promise<ValidationResult> {\n return this.validate('ssn', value, options);\n }\n\n /**\n * Validate a Spanish NIF: a DNI, a NIE (foreign nationals), the K/L/M series, or a company NIF.\n *\n * `nifDetails` says which, whether it belongs to a person, and for a company its legal form.\n * Spanish only — a Portuguese NIF is a different scheme and is not accepted here.\n */\n validateNif(value: string, options?: RequestOptions): Promise<ValidationResult> {\n return this.validate('nif', value, options);\n }\n\n /**\n * Validate a value against any rule.\n *\n * The typed helpers above call this. Use it directly when the rule is chosen at runtime.\n */\n validate(\n rule: ValidationRule,\n value: string,\n options: RequestOptions = {},\n ): Promise<ValidationResult> {\n return this.#validate(rule, value, options);\n }\n\n async #validate(\n rule: ValidationRule,\n value: string,\n options: RequestOptions,\n extra?: Record<string, string>,\n ): Promise<ValidationResult> {\n if (typeof value !== 'string' || value.trim() === '') {\n // Caught here rather than server-side: an empty value consumes quota and can only fail.\n throw new VerifNowRequestError(\n `Cannot validate an empty value for rule \"${rule}\".`,\n );\n }\n\n const url = `${this.#baseUrl}/api/v1/validate/${rule}`;\n const body = JSON.stringify({ value, ...extra });\n return this.#withRetry(() =>\n this.#requestOnce('POST', url, body, options, (payload, quota) => mapResult(payload, quota)),\n );\n }\n\n /**\n * EU VAT rates of every member state, from the European Commission's TEDB.\n *\n * Public reference data: the call spends no quota. These are the rates a member state has, not\n * the rate a sale is charged — in B2B trade between member states the invoice is usually\n * zero-rated under the reverse charge whatever the buyer's country rate is.\n */\n async vatRates(options: RequestOptions = {}): Promise<VatRates> {\n const url = `${this.#baseUrl}/api/v1/vat/rates`;\n return this.#withRetry(() =>\n this.#requestOnce('GET', url, undefined, options, (payload) => mapVatRates(payload)),\n );\n }\n\n /**\n * One EU member state's VAT rates. Accepts `GR` for Greece as well as `EL`.\n *\n * A code outside the 27 member states throws {@link VerifNowRequestError} (HTTP 404).\n */\n async vatRate(countryCode: string, options: RequestOptions = {}): Promise<CountryVatRates> {\n if (typeof countryCode !== 'string' || countryCode.trim() === '') {\n throw new VerifNowRequestError('A member state code is required, e.g. \"FR\".');\n }\n const url = `${this.#baseUrl}/api/v1/vat/rates/${encodeURIComponent(countryCode.trim())}`;\n return this.#withRetry(() =>\n this.#requestOnce('GET', url, undefined, options, (payload) => mapCountryVatRates(payload)),\n );\n }\n\n /** Runs one request under the retry policy. */\n async #withRetry<T>(attemptOnce: () => Promise<T>): Promise<T> {\n const maxAttempts = this.#retry ? this.#retry.attempts + 1 : 1;\n\n let lastError: VerifNowError | undefined;\n\n for (let attempt = 0; attempt < maxAttempts; attempt++) {\n try {\n return await attemptOnce();\n } catch (error) {\n if (!(error instanceof VerifNowError)) throw error;\n lastError = error;\n\n const isLastAttempt = attempt === maxAttempts - 1;\n if (isLastAttempt || !this.#retry) throw error;\n\n const delay = this.#retryDelay(error, attempt);\n if (delay === null) throw error;\n\n await sleep(delay);\n }\n }\n\n /* c8 ignore next -- the loop either returns or throws */\n throw lastError ?? new VerifNowError('Request failed');\n }\n\n /**\n * How long to wait before retrying, or `null` when the error should surface immediately.\n *\n * A 429 is retried only when the reset is close: the concurrency limit clears in\n * milliseconds, but a spent monthly quota does not, and sleeping on it helps nobody.\n */\n #retryDelay(error: VerifNowError, attempt: number): number | null {\n const retry = this.#retry!;\n const backoff = Math.min(retry.backoffMs * 2 ** attempt, retry.maxBackoffMs);\n\n if (error instanceof VerifNowRateLimitError) {\n const waitMs = (error.retryAfterSeconds ?? 0) * 1000;\n if (waitMs > retry.maxBackoffMs) return null;\n return Math.max(waitMs, backoff);\n }\n\n if (error instanceof VerifNowServerError) return backoff;\n // A timeout is retried: the deadline is ours, and the next attempt gets a fresh one.\n if (error instanceof VerifNowConnectionError) return backoff;\n\n // 400, 401 and unparseable bodies will fail identically on a second attempt.\n return null;\n }\n\n async #requestOnce<T>(\n method: 'GET' | 'POST',\n url: string,\n body: string | undefined,\n options: RequestOptions,\n map: (payload: Record<string, unknown>, quota: QuotaInfo | undefined) => T,\n ): Promise<T> {\n const timeoutMs = options.timeoutMs ?? this.#timeoutMs;\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n const abortFromCaller = () => controller.abort();\n options.signal?.addEventListener('abort', abortFromCaller, { once: true });\n\n let response: Response;\n try {\n response = await this.#fetch(url, {\n method,\n headers: {\n ...this.#headers,\n ...(body === undefined ? {} : { 'Content-Type': 'application/json' }),\n Accept: 'application/json',\n 'X-API-KEY': this.#apiKey,\n 'X-VerifNow-SDK': `node/${VERSION}`,\n },\n body,\n signal: controller.signal,\n });\n } catch (cause) {\n // The caller's own cancellation is theirs to handle, not a transport failure.\n if (options.signal?.aborted) throw cause;\n\n const timedOut = controller.signal.aborted;\n throw new VerifNowConnectionError(\n timedOut\n ? `VerifNow request to ${url} timed out after ${timeoutMs}ms.`\n : `Could not reach the VerifNow API at ${url}. Check \\`baseUrl\\` and network access.`,\n { cause, timedOut },\n );\n } finally {\n clearTimeout(timer);\n options.signal?.removeEventListener('abort', abortFromCaller);\n }\n\n return this.#handleResponse(response, map);\n }\n\n async #handleResponse<T>(\n response: Response,\n map: (payload: Record<string, unknown>, quota: QuotaInfo | undefined) => T,\n ): Promise<T> {\n const requestId = response.headers.get('X-Request-Id') ?? undefined;\n const quota = parseQuota(response.headers);\n\n if (response.ok) {\n let payload: unknown;\n try {\n payload = await response.json();\n } catch (cause) {\n throw new VerifNowResponseError(\n 'VerifNow returned a success status with a body that is not valid JSON.',\n { status: response.status, requestId, cause },\n );\n }\n\n if (payload === null || typeof payload !== 'object') {\n throw new VerifNowResponseError(\n 'VerifNow returned an unexpected response shape.',\n { status: response.status, requestId },\n );\n }\n\n return map(payload as Record<string, unknown>, quota);\n }\n\n const message = await readErrorMessage(response);\n const context = { status: response.status, requestId };\n\n if (response.status === 401 || response.status === 403) {\n throw new VerifNowAuthError(\n `VerifNow rejected the API key (${response.status}): ${message}`,\n context,\n );\n }\n\n if (response.status === 429) {\n throw new VerifNowRateLimitError(`VerifNow rate limit reached: ${message}`, {\n ...context,\n quota,\n retryAfterSeconds: parseRetryAfter(response.headers, quota),\n });\n }\n\n if (response.status >= 500) {\n throw new VerifNowServerError(\n `VerifNow returned ${response.status}: ${message}`,\n context,\n );\n }\n\n throw new VerifNowRequestError(\n `VerifNow rejected the request (${response.status}): ${message}`,\n context,\n );\n }\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nfunction toNumber(value: string | null): number | undefined {\n if (value === null) return undefined;\n const parsed = Number(value);\n return Number.isFinite(parsed) ? parsed : undefined;\n}\n\nfunction parseQuota(headers: Headers): QuotaInfo | undefined {\n const limit = toNumber(headers.get('X-RateLimit-Limit'));\n const remaining = toNumber(headers.get('X-RateLimit-Remaining'));\n const resetSeconds = toNumber(headers.get('X-RateLimit-Reset'));\n const overage = headers.get('X-Quota-Overage') === 'true';\n\n if (\n limit === undefined &&\n remaining === undefined &&\n resetSeconds === undefined &&\n !overage\n ) {\n return undefined;\n }\n\n return {\n limit,\n remaining,\n resetAt: resetSeconds === undefined ? undefined : new Date(resetSeconds * 1000),\n overage,\n };\n}\n\nfunction parseRetryAfter(headers: Headers, quota?: QuotaInfo): number | undefined {\n const retryAfter = headers.get('Retry-After');\n if (retryAfter !== null) {\n const seconds = Number(retryAfter);\n if (Number.isFinite(seconds)) return seconds;\n\n // RFC 7231 also allows an HTTP-date.\n const asDate = Date.parse(retryAfter);\n if (!Number.isNaN(asDate)) {\n return Math.max(0, Math.ceil((asDate - Date.now()) / 1000));\n }\n }\n\n if (quota?.resetAt) {\n return Math.max(0, Math.ceil((quota.resetAt.getTime() - Date.now()) / 1000));\n }\n\n return undefined;\n}\n\nasync function readErrorMessage(response: Response): Promise<string> {\n try {\n const text = await response.text();\n if (!text) return response.statusText || 'no details';\n\n try {\n const parsed = JSON.parse(text) as Record<string, unknown>;\n const message = parsed.message ?? parsed.error;\n if (typeof message === 'string' && message !== '') return message;\n } catch {\n // Not JSON — a proxy or the servlet container's default error page.\n }\n\n return text.slice(0, 500);\n } catch {\n return response.statusText || 'no details';\n }\n}\n\nfunction asString(value: unknown): string | undefined {\n return typeof value === 'string' ? value : undefined;\n}\n\nfunction asBoolean(value: unknown): boolean | undefined {\n return typeof value === 'boolean' ? value : undefined;\n}\n\nfunction asNumber(value: unknown): number | undefined {\n return typeof value === 'number' && Number.isFinite(value) ? value : undefined;\n}\n\nfunction mapSignals(raw: unknown): EmailSignals | undefined {\n if (raw === null || typeof raw !== 'object') return undefined;\n const s = raw as Record<string, unknown>;\n\n return {\n syntaxValid: asBoolean(s.syntax_valid),\n mxValid: asBoolean(s.mx_valid),\n typoDetected: asBoolean(s.typo_detected),\n suggestedDomain: asString(s.suggested_domain),\n disposable: asBoolean(s.disposable),\n roleBased: asBoolean(s.role_based),\n freeProvider: asBoolean(s.free_provider),\n domainAgeDays: asNumber(s.domain_age_days),\n mxProvider: asString(s.mx_provider),\n mxQualityScore: asNumber(s.mx_quality_score),\n };\n}\n\nfunction mapEmailDetails(raw: unknown): EmailDetails | undefined {\n if (raw === null || typeof raw !== 'object') return undefined;\n const d = raw as Record<string, unknown>;\n\n return {\n signals: mapSignals(d.signals),\n riskScore: asNumber(d.risk_score),\n riskLevel: asString(d.risk_level) as EmailDetails['riskLevel'],\n deliverability: asString(d.deliverability) as EmailDetails['deliverability'],\n appliedLevel: asString(d.applied_level) as EmailDetails['appliedLevel'],\n };\n}\n\n/**\n * Reads `registered`, preserving the difference between `false` and `null`.\n *\n * `asBoolean` cannot be used here: it maps `null` to `undefined`, which would erase the one\n * distinction the whole VAT design exists to carry. `false` means the registry answered and the\n * number is not there; `null` means nobody could ask. A caller that cannot tell them apart will\n * reject real businesses whenever VIES is down.\n *\n * A missing key is read as `null` for the same reason — unknown, not absent.\n */\nfunction asRegistered(value: unknown): boolean | null {\n return typeof value === 'boolean' ? value : null;\n}\n\nfunction asDate(value: unknown): Date | undefined {\n if (typeof value !== 'string') return undefined;\n const parsed = new Date(value);\n return Number.isNaN(parsed.getTime()) ? undefined : parsed;\n}\n\nfunction mapVatDetails(raw: unknown): VatDetails | undefined {\n if (raw === null || typeof raw !== 'object') return undefined;\n const d = raw as Record<string, unknown>;\n\n return {\n formatValid: asBoolean(d.format_valid),\n registered: asRegistered(d.registered),\n countryCode: asString(d.country_code),\n source: asString(d.source) as VatDetails['source'],\n checkedAt: asDate(d.checked_at),\n traderName: asString(d.trader_name),\n traderAddress: asString(d.trader_address),\n viesAvailable: asBoolean(d.vies_available),\n consultationNumber: asString(d.consultation_number),\n traderNameMatch: asString(d.trader_name_match) as VatDetails['traderNameMatch'],\n traderNameMatchSource: asString(d.trader_name_match_source) as VatDetails['traderNameMatchSource'],\n };\n}\n\nfunction mapPhoneDetails(raw: unknown): PhoneDetails | undefined {\n if (raw === null || typeof raw !== 'object') return undefined;\n const d = raw as Record<string, unknown>;\n\n return {\n countryCode: asString(d.country_code),\n callingCode: asNumber(d.calling_code),\n lineType: asString(d.line_type) as PhoneDetails['lineType'],\n internationalFormat: asString(d.international_format),\n nationalFormat: asString(d.national_format),\n };\n}\n\nfunction mapIbanDetails(raw: unknown): IbanDetails | undefined {\n if (raw === null || typeof raw !== 'object') return undefined;\n const d = raw as Record<string, unknown>;\n\n return {\n countryCode: asString(d.country_code),\n sepa: asBoolean(d.sepa),\n structureValid: asBoolean(d.structure_valid),\n checksumValid: asBoolean(d.checksum_valid),\n length: asNumber(d.length),\n expectedLength: asNumber(d.expected_length),\n formatted: asString(d.formatted),\n };\n}\n\nfunction mapNasDetails(raw: unknown): NasDetails | undefined {\n if (raw === null || typeof raw !== 'object') return undefined;\n const d = raw as Record<string, unknown>;\n\n return {\n checksumValid: asBoolean(d.checksum_valid),\n temporaryResident: asBoolean(d.temporary_resident),\n individualSeries: asBoolean(d.individual_series),\n formatted: asString(d.formatted),\n };\n}\n\nfunction mapNifDetails(raw: unknown): NifDetails | undefined {\n if (raw === null || typeof raw !== 'object') return undefined;\n const d = raw as Record<string, unknown>;\n\n return {\n type: asString(d.type) as NifDetails['type'],\n naturalPerson: asBoolean(d.natural_person),\n checksumValid: asBoolean(d.checksum_valid),\n entityLetter: asString(d.entity_letter),\n entityType: asString(d.entity_type),\n };\n}\n\nfunction mapSsnDetails(raw: unknown): SsnDetails | undefined {\n if (raw === null || typeof raw !== 'object') return undefined;\n const d = raw as Record<string, unknown>;\n\n return { itin: asBoolean(d.itin) };\n}\n\n/**\n * Maps the API's snake_case diagnostics onto camelCase, so a TypeScript caller is not switching\n * naming conventions mid-expression. The untouched body stays available on `raw`.\n */\nfunction mapCountryVatRates(raw: Record<string, unknown>): CountryVatRates {\n const numbers = (value: unknown): number[] =>\n Array.isArray(value) ? value.filter((v): v is number => asNumber(v) !== undefined) : [];\n\n return {\n countryCode: asString(raw.countryCode) ?? '',\n standardRate: asNumber(raw.standardRate) ?? Number.NaN,\n reducedRates: numbers(raw.reducedRates),\n regionalRates: Array.isArray(raw.regionalRates)\n ? raw.regionalRates\n .filter((r): r is Record<string, unknown> => r !== null && typeof r === 'object')\n .map((r) => ({\n rate: asNumber(r.rate) ?? Number.NaN,\n note: asString(r.note),\n euVatArea: asBoolean(r.euVatArea),\n }))\n : [],\n situationOn: asString(raw.situationOn),\n fetchedAt: asDate(raw.fetchedAt),\n };\n}\n\nfunction mapVatRates(raw: Record<string, unknown>): VatRates {\n const rates = Array.isArray(raw.rates)\n ? raw.rates\n .filter((r): r is Record<string, unknown> => r !== null && typeof r === 'object')\n .map(mapCountryVatRates)\n : [];\n return {\n source: asString(raw.source) ?? 'TEDB',\n sourceUrl: asString(raw.sourceUrl),\n rates,\n };\n}\n\nfunction mapResult(\n payload: Record<string, unknown>,\n quota: QuotaInfo | undefined,\n): ValidationResult {\n return {\n valid: payload.valid === true,\n message: asString(payload.message),\n normalizedValue: asString(payload.normalizedValue) ?? null,\n originalValue: asString(payload.originalValue),\n validationLevel: asString(payload.validationLevel) as ValidationResult['validationLevel'],\n emailDetails: mapEmailDetails(payload.emailDetails),\n vatDetails: mapVatDetails(payload.vatDetails),\n phoneDetails: mapPhoneDetails(payload.phoneDetails),\n ibanDetails: mapIbanDetails(payload.ibanDetails),\n nasDetails: mapNasDetails(payload.nasDetails),\n nifDetails: mapNifDetails(payload.nifDetails),\n ssnDetails: mapSsnDetails(payload.ssnDetails),\n quota,\n raw: payload,\n };\n}\n","/**\n * Validation rules exposed by the VerifNow API.\n *\n * Each maps to `POST /api/v1/validate/{rule}`.\n */\nexport type ValidationRule =\n | 'email'\n | 'phone'\n | 'iban'\n | 'vat'\n | 'nas'\n | 'ssn'\n | 'nif';\n\nexport const VALIDATION_RULES: readonly ValidationRule[] = [\n 'email',\n 'phone',\n 'iban',\n 'vat',\n 'nas',\n 'ssn',\n 'nif',\n] as const;\n\n/**\n * Depth of checks applied to a request, decided by the plan attached to the API key.\n *\n * `STANDARD` runs on the FREE and STARTER plans, `ADVANCED` on GROWTH, `PREMIUM` on PRO.\n * Branch on this rather than on the plan name: it is the only value that tells you which\n * signals are actually present in the response.\n */\nexport type ValidationLevel = 'BASIC' | 'STANDARD' | 'ADVANCED' | 'PREMIUM';\n\n/** Categorical risk assessment. Returned from `ADVANCED` depth upward. */\nexport type RiskLevel = 'LOW' | 'MEDIUM' | 'HIGH';\n\nexport type Deliverability =\n | 'DELIVERABLE'\n | 'RISKY'\n | 'UNDELIVERABLE'\n | 'UNKNOWN';\n\n/**\n * Per-signal breakdown behind an email verdict.\n *\n * Fields are `undefined` when the applied level does not compute them — the last four require\n * `ADVANCED` depth or higher. Check `ValidationResult.appliedLevel` before relying on one.\n */\nexport interface EmailSignals {\n /** The address matches the syntax pattern for the applied level. */\n syntaxValid?: boolean;\n /** The domain resolves and publishes MX (or fallback A) records. */\n mxValid?: boolean;\n /** A likely typo was found in the domain, e.g. `gmail.con`. */\n typoDetected?: boolean;\n /** The correction proposed when `typoDetected` is true. */\n suggestedDomain?: string;\n /** The domain belongs to a throwaway mailbox provider. */\n disposable?: boolean;\n /** The local part is a shared mailbox: `info@`, `admin@`, `noreply@`. */\n roleBased?: boolean;\n /** The domain is a consumer mailbox provider. Requires ADVANCED. */\n freeProvider?: boolean;\n /** Estimated age of the domain in days. Requires ADVANCED. */\n domainAgeDays?: number;\n /** Identified mail provider, e.g. `google`. Requires ADVANCED. */\n mxProvider?: string;\n /** Mail server quality between 0 and 1. Requires ADVANCED. */\n mxQualityScore?: number;\n}\n\n/** Email-specific diagnostics. Absent when the applied level is `BASIC`. */\nexport interface EmailDetails {\n signals?: EmailSignals;\n /** Aggregated risk on a 0–100 scale, where 0 is the lowest risk. */\n riskScore?: number;\n /** Categorical risk. Requires ADVANCED depth or higher. */\n riskLevel?: RiskLevel;\n deliverability?: Deliverability;\n /** The depth actually applied, echoed back by the API. */\n appliedLevel?: ValidationLevel;\n}\n\n/**\n * Where a VAT registration verdict came from.\n *\n * VIES publishes no SLA and drops member states several times a month, so a VAT answer is not\n * always a live one. Branch on this rather than on `ValidationResult.valid` whenever the\n * difference matters for your own compliance.\n */\n/** Whether a supplied company name belongs to a VAT number's registered holder. */\nexport type TraderNameMatch = 'MATCH' | 'MISMATCH' | 'NOT_AVAILABLE';\n\n/** Who compared the names. */\nexport type TraderNameMatchSource = 'VIES' | 'VERIFNOW';\n\nexport type VatSource =\n /** Confirmed against VIES during this request. */\n | 'LIVE'\n /** Served from a VIES answer less than 24 hours old. */\n | 'CACHE'\n /** VIES was unreachable, so an older cached answer was used. */\n | 'STALE'\n /** VIES was unreachable and nothing was cached. Registration is unknown. */\n | 'UNVERIFIED'\n /** The country is outside VIES, so no registry lookup is possible. */\n | 'NOT_APPLICABLE';\n\n/** VAT-specific diagnostics. Present on `vat` validations. */\nexport interface VatDetails {\n /** The number matches its member state's structure. Decided locally, never depends on VIES. */\n formatValid?: boolean;\n /**\n * Present in the member state's registry.\n *\n * **`null` means unknown, never \"not registered.\"** It is returned when VIES could not be\n * consulted. Treating `null` as `false` rejects legitimate customers during someone else's\n * outage — the single most expensive mistake available in VAT validation.\n */\n registered: boolean | null;\n /** Member state the number belongs to, e.g. `IE`. Greece is `EL`, Northern Ireland `XI`. */\n countryCode?: string;\n source?: VatSource;\n /** When the registration was last confirmed against VIES. */\n checkedAt?: Date;\n /** Registered trading name, when the member state discloses it. Germany does not. */\n traderName?: string;\n /** Registered address, when the member state discloses it. */\n traderAddress?: string;\n /** Whether VIES could answer for this country during the request. */\n viesAvailable?: boolean;\n /**\n * The consultation number VIES issued for this lookup — the receipt a tax authority accepts as\n * evidence that you checked. Present only when your account has its own VAT number configured,\n * because VIES issues one only to an identified requester.\n */\n consultationNumber?: string;\n /**\n * Present when a `traderName` was sent: whether it belongs to the registered holder. `MISMATCH`\n * is a question for a human, not proof of fraud — trading names and group companies differ.\n */\n traderNameMatch?: TraderNameMatch;\n /** Who compared: `VERIFNOW`, against the name VIES published, or `VIES` itself (Spain). */\n traderNameMatchSource?: TraderNameMatchSource;\n}\n\n/**\n * Kind of line, according to the country's numbering plan.\n *\n * A `PREMIUM_RATE` or `VOIP` number is still `valid` — it exists. This is how you decide to\n * exclude one, rather than the API deciding for you.\n */\nexport type PhoneLineType =\n | 'MOBILE'\n | 'FIXED_LINE'\n /** The plan does not distinguish the two — the case for the US and Canada. */\n | 'FIXED_LINE_OR_MOBILE'\n | 'TOLL_FREE'\n | 'PREMIUM_RATE'\n | 'SHARED_COST'\n | 'VOIP'\n | 'PERSONAL_NUMBER'\n | 'PAGER'\n | 'UAN'\n | 'VOICEMAIL'\n | 'UNKNOWN';\n\n/**\n * Phone-specific diagnostics. Present whenever the input parsed as an international number —\n * including when it is invalid for its country, so you can tell the user which country it was\n * read as.\n */\nexport interface PhoneDetails {\n /** ISO 3166-1 alpha-2 country, e.g. `FR`. Absent when the calling code is shared by several. */\n countryCode?: string;\n /** International calling code without the plus sign, e.g. `33`. */\n callingCode?: number;\n /** Absent when the number is invalid. */\n lineType?: PhoneLineType;\n /** e.g. `+33 6 12 34 56 78`. Absent when the number is invalid. */\n internationalFormat?: string;\n /** e.g. `06 12 34 56 78`. Absent when the number is invalid. */\n nationalFormat?: string;\n}\n\n/**\n * IBAN-specific diagnostics. Present on `iban` validations.\n *\n * Structure and checksum are reported separately because they fail for different reasons:\n * `structureValid` answers \"could this be an account number in that country\" (the SWIFT\n * registry's length and layout), `checksumValid` answers \"was it typed correctly\" (mod-97).\n * There is no bank name or BIC — that needs a registry the API does not hold.\n */\nexport interface IbanDetails {\n /** The IBAN's country, from its first two characters. */\n countryCode?: string;\n /**\n * The country is inside the SEPA schemes' geographical scope, so a bank there may collect a\n * SEPA direct debit. Whether this particular bank does is published per bank, not per country,\n * and cannot be read from an IBAN.\n */\n sepa?: boolean;\n /** Length and character layout match the registry entry for that country. */\n structureValid?: boolean;\n /** The mod-97 check digits are correct. */\n checksumValid?: boolean;\n /** Length of the value as submitted, spaces removed. */\n length?: number;\n /** Length the registry requires for that country; absent for an unknown country. */\n expectedLength?: number;\n /** Print format, in groups of four. Present only for a valid IBAN. */\n formatted?: string;\n}\n\n/**\n * Canadian Social Insurance Number diagnostics. Present on `nas` validations.\n *\n * There is no province and no expiry date: the first digit no longer reliably identifies a\n * province, and a temporary resident's SIN expires with their permit, which only the document\n * shows.\n */\nexport interface NasDetails {\n /** The Luhn check digit is correct. */\n checksumValid?: boolean;\n /**\n * A 9-series number, issued to temporary residents. It expires with the holder's permit, and the\n * number itself does not say when — check the document.\n */\n temporaryResident?: boolean;\n /**\n * The first digit belongs to a series issued to individuals. `false` for numbers starting with\n * 0 or 8 — including 046 454 286, the government's sample number, which is why it is safe to\n * use in tests.\n */\n individualSeries?: boolean;\n /** Printed form, e.g. `046 454 286`. */\n formatted?: string;\n}\n\n/** The kinds of Spanish tax identification number. */\nexport type NifType =\n /** Spanish national with a DNI: 8 digits and a letter. */\n | 'DNI'\n /** Foreign national: X, Y or Z, 7 digits and a letter. */\n | 'NIE'\n /** Spanish national under 14 without a DNI. */\n | 'NIF_K'\n /** Spanish national resident abroad, staying under six months. */\n | 'NIF_L'\n /** Foreign national without a NIE. */\n | 'NIF_M'\n /** A company or other entity: a letter for the legal form, 7 digits, a control character. */\n | 'ENTITY';\n\n/** Spanish NIF diagnostics. Present on `nif` validations. */\nexport interface NifDetails {\n type?: NifType;\n /** The number belongs to a person rather than a company or other entity. */\n naturalPerson?: boolean;\n /** The control character is correct. */\n checksumValid?: boolean;\n /** For an entity, the letter that encodes its legal form, e.g. `B`. */\n entityLetter?: string;\n /** For an entity, its legal form, e.g. `Private limited company (Sociedad de responsabilidad limitada)`. */\n entityType?: string;\n}\n\n/**\n * US SSN diagnostics. Present on `ssn` validations.\n *\n * An SSN has no check digit, and since 2011 its first digits say nothing about a state, so there is\n * little a number can reveal about itself. The one thing worth knowing is whether it is an ITIN.\n */\nexport interface SsnDetails {\n /**\n * The number is an IRS ITIN, not an SSN: it starts with 9 and its fourth and fifth digits are in\n * 50-65, 70-88, 90-92 or 94-99. `valid` is `false` for the SSN, but an ITIN is an acceptable\n * taxpayer number where one is accepted (a W-9, for instance).\n */\n itin?: boolean;\n}\n\n/** Outcome of a single validation call. */\nexport interface ValidationResult {\n /** Whether the value passed every check the applied level ran. */\n valid: boolean;\n /** Human-readable explanation of the verdict. */\n message?: string;\n /** Canonical form of the input — `null` when the value is invalid. */\n normalizedValue: string | null;\n /** The value exactly as submitted. */\n originalValue?: string;\n /** Depth applied to this request. */\n validationLevel?: ValidationLevel;\n /** Present for email validations from `STANDARD` depth upward. */\n emailDetails?: EmailDetails;\n /** Present for VAT validations. */\n vatDetails?: VatDetails;\n /** Present for phone validations. The E.164 form is `normalizedValue`. */\n phoneDetails?: PhoneDetails;\n /** Present for IBAN validations. */\n ibanDetails?: IbanDetails;\n /** Present for Canadian SIN (`nas`) validations. */\n nasDetails?: NasDetails;\n /** Present for Spanish NIF validations. */\n nifDetails?: NifDetails;\n /** Present for US SSN validations. */\n ssnDetails?: SsnDetails;\n /** Quota state reported by the response headers. */\n quota?: QuotaInfo;\n /** The unmodified JSON body, for fields this SDK version does not model yet. */\n raw: Record<string, unknown>;\n}\n\n/** A VAT rate applying to part of a member state only — an overseas department, an island. */\nexport interface RegionalVatRate {\n rate: number;\n /** Where it applies, in the words of the Commission's TEDB. */\n note?: string;\n /**\n * `false` for the Canary Islands and the French overseas territories, which the VAT Directive\n * excludes (Article 6(1)): goods shipped there from another member state are an export, not a\n * distance sale at this rate.\n */\n euVatArea?: boolean;\n}\n\n/**\n * One EU member state's VAT rates, from the European Commission's TEDB.\n *\n * These are the rates the member state has, not the rate a sale is charged: which one applies\n * depends on who sells to whom and what. In B2B trade between member states the invoice is usually\n * zero-rated under the reverse charge, whatever the buyer's country rate is.\n */\nexport interface CountryVatRates {\n /** Member state as TEDB and VIES name it: Greece is `EL`. */\n countryCode: string;\n /** The national standard rate, e.g. `20` for France. */\n standardRate: number;\n /**\n * Every reduced, super-reduced and parking rate on the mainland territory, ascending.\n * TEDB's own sub-labels are inconsistent between member states, so they are not reproduced.\n */\n reducedRates: number[];\n /** Rates for part of the territory only, e.g. 8.5 % in Martinique, Guadeloupe and Réunion. */\n regionalRates: RegionalVatRate[];\n /**\n * The date TEDB says these rates apply from, as `YYYY-MM-DD`. Kept as a string: a date without\n * a time zone turned into a `Date` can land on the previous day.\n */\n situationOn?: string;\n /** When VerifNow last retrieved them from TEDB. */\n fetchedAt?: Date;\n}\n\n/** VAT rates of every EU member state. */\nexport interface VatRates {\n /** Always `TEDB`, the Commission's Taxes in Europe Database. */\n source: string;\n sourceUrl?: string;\n /** One entry per member state retrieved so far — normally all 27. */\n rates: CountryVatRates[];\n}\n\n/** Quota counters read from the `X-RateLimit-*` response headers. */\nexport interface QuotaInfo {\n /** Validations included in the current billing period. */\n limit?: number;\n /** Validations left before overage or blocking. */\n remaining?: number;\n /** When the current period resets. */\n resetAt?: Date;\n /** True once you are past the included quota and into per-unit billing. */\n overage?: boolean;\n}\n\nexport interface RetryOptions {\n /**\n * Retry attempts after the first failure. Defaults to 2, so up to 3 requests in total.\n * Only connection failures, 429 and 5xx are retried — never a 400 or 401, which will not\n * succeed on a second try.\n */\n attempts?: number;\n /** Delay before the first retry, in ms. Doubles each attempt. Defaults to 200. */\n backoffMs?: number;\n /** Upper bound on a single backoff delay, in ms. Defaults to 2000. */\n maxBackoffMs?: number;\n}\n\nexport interface VerifNowOptions {\n /** API key created in the VerifNow dashboard. Sent as the `X-API-KEY` header. */\n apiKey: string;\n /** Override the API origin. Defaults to `https://api.verifnow.io`. */\n baseUrl?: string;\n /** Abort a single request after this many ms. Defaults to 5000. */\n timeoutMs?: number;\n /** Retry policy, or `false` to disable retries entirely. */\n retry?: RetryOptions | false;\n /** Extra headers merged into every request. */\n headers?: Record<string, string>;\n /**\n * Replacement for the global `fetch`, for tests or a custom agent.\n * Defaults to `globalThis.fetch`.\n */\n fetch?: typeof globalThis.fetch;\n}\n"],"mappings":"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","names":["asDate"]}
|
package/package.json
CHANGED