@verifnow/sdk 1.4.0 → 1.5.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 CHANGED
@@ -90,7 +90,7 @@ await client.validateIban('FR7630006000011234567890189');
90
90
  await client.validateVat('FR12345678901');
91
91
  await client.validateNas('046454286'); // Canadian Social Insurance Number
92
92
  await client.validateSsn('123-45-6789'); // US Social Security Number
93
- await client.validateNif('12345678Z'); // Spanish / Portuguese NIF
93
+ await client.validateNif('B12345674'); // Spanish NIF — DNI, NIE or company
94
94
 
95
95
  // When the rule is only known at runtime
96
96
  await client.validate('iban', value);
@@ -110,6 +110,7 @@ interface ValidationResult {
110
110
  phoneDetails?: PhoneDetails; // phone only — country, lineType, formats
111
111
  ibanDetails?: IbanDetails; // IBAN only — structure and checksum, separately
112
112
  nasDetails?: NasDetails; // Canadian SIN only — temporary resident, series
113
+ nifDetails?: NifDetails; // Spanish NIF only — DNI, NIE or company, legal form
113
114
  quota?: QuotaInfo; // from the X-RateLimit-* headers
114
115
  raw: Record<string, unknown>; // untouched response body
115
116
  }
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.4.0";
79
+ var VERSION = "1.5.0";
80
80
 
81
81
  // src/client.ts
82
82
  var DEFAULT_BASE_URL = "https://api.verifnow.io";
@@ -151,7 +151,12 @@ var VerifNow = class {
151
151
  validateSsn(value, options) {
152
152
  return this.validate("ssn", value, options);
153
153
  }
154
- /** Validate a Spanish/Portuguese NIF. */
154
+ /**
155
+ * Validate a Spanish NIF: a DNI, a NIE (foreign nationals), the K/L/M series, or a company NIF.
156
+ *
157
+ * `nifDetails` says which, whether it belongs to a person, and for a company its legal form.
158
+ * Spanish only — a Portuguese NIF is a different scheme and is not accepted here.
159
+ */
155
160
  validateNif(value, options) {
156
161
  return this.validate("nif", value, options);
157
162
  }
@@ -429,6 +434,17 @@ function mapNasDetails(raw) {
429
434
  formatted: asString(d.formatted)
430
435
  };
431
436
  }
437
+ function mapNifDetails(raw) {
438
+ if (raw === null || typeof raw !== "object") return void 0;
439
+ const d = raw;
440
+ return {
441
+ type: asString(d.type),
442
+ naturalPerson: asBoolean(d.natural_person),
443
+ checksumValid: asBoolean(d.checksum_valid),
444
+ entityLetter: asString(d.entity_letter),
445
+ entityType: asString(d.entity_type)
446
+ };
447
+ }
432
448
  function mapResult(payload, quota) {
433
449
  return {
434
450
  valid: payload.valid === true,
@@ -441,6 +457,7 @@ function mapResult(payload, quota) {
441
457
  phoneDetails: mapPhoneDetails(payload.phoneDetails),
442
458
  ibanDetails: mapIbanDetails(payload.ibanDetails),
443
459
  nasDetails: mapNasDetails(payload.nasDetails),
460
+ nifDetails: mapNifDetails(payload.nifDetails),
444
461
  quota,
445
462
  raw: payload
446
463
  };
@@ -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 Deliverability,\n type EmailDetails,\n type EmailSignals,\n type IbanDetails,\n type NasDetails,\n type PhoneDetails,\n type PhoneLineType,\n type QuotaInfo,\n type RetryOptions,\n type RiskLevel,\n type ValidationLevel,\n type ValidationResult,\n type ValidationRule,\n type VatDetails,\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.4.0';\n","import {\n VerifNowAuthError,\n VerifNowConnectionError,\n VerifNowError,\n VerifNowRateLimitError,\n VerifNowRequestError,\n VerifNowResponseError,\n VerifNowServerError,\n} from './errors.js';\nimport type {\n EmailDetails,\n EmailSignals,\n IbanDetails,\n NasDetails,\n PhoneDetails,\n QuotaInfo,\n RetryOptions,\n ValidationResult,\n ValidationRule,\n VatDetails,\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 /** Validate a US Social Security Number. */\n validateSsn(value: string, options?: RequestOptions): Promise<ValidationResult> {\n return this.validate('ssn', value, options);\n }\n\n /** Validate a Spanish/Portuguese NIF. */\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 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 this.#requestOnce(url, body, options);\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(\n url: string,\n body: string,\n options: RequestOptions,\n ): Promise<ValidationResult> {\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: 'POST',\n headers: {\n ...this.#headers,\n '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);\n }\n\n async #handleResponse(response: Response): Promise<ValidationResult> {\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 mapResult(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 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\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 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 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 /** 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/** 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 /** 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/** 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 } 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 Deliverability,\n type EmailDetails,\n type EmailSignals,\n type IbanDetails,\n type NasDetails,\n type NifDetails,\n type NifType,\n type PhoneDetails,\n type PhoneLineType,\n type QuotaInfo,\n type RetryOptions,\n type RiskLevel,\n type ValidationLevel,\n type ValidationResult,\n type ValidationRule,\n type VatDetails,\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.5.0';\n","import {\n VerifNowAuthError,\n VerifNowConnectionError,\n VerifNowError,\n VerifNowRateLimitError,\n VerifNowRequestError,\n VerifNowResponseError,\n VerifNowServerError,\n} from './errors.js';\nimport type {\n EmailDetails,\n EmailSignals,\n IbanDetails,\n NasDetails,\n NifDetails,\n PhoneDetails,\n QuotaInfo,\n RetryOptions,\n ValidationResult,\n ValidationRule,\n VatDetails,\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 /** Validate a US Social Security Number. */\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 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 this.#requestOnce(url, body, options);\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(\n url: string,\n body: string,\n options: RequestOptions,\n ): Promise<ValidationResult> {\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: 'POST',\n headers: {\n ...this.#headers,\n '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);\n }\n\n async #handleResponse(response: Response): Promise<ValidationResult> {\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 mapResult(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 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\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 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 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 /** 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/** 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 /** 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/** 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
@@ -175,6 +175,32 @@ interface NasDetails {
175
175
  /** Printed form, e.g. `046 454 286`. */
176
176
  formatted?: string;
177
177
  }
178
+ /** The kinds of Spanish tax identification number. */
179
+ type NifType =
180
+ /** Spanish national with a DNI: 8 digits and a letter. */
181
+ 'DNI'
182
+ /** Foreign national: X, Y or Z, 7 digits and a letter. */
183
+ | 'NIE'
184
+ /** Spanish national under 14 without a DNI. */
185
+ | 'NIF_K'
186
+ /** Spanish national resident abroad, staying under six months. */
187
+ | 'NIF_L'
188
+ /** Foreign national without a NIE. */
189
+ | 'NIF_M'
190
+ /** A company or other entity: a letter for the legal form, 7 digits, a control character. */
191
+ | 'ENTITY';
192
+ /** Spanish NIF diagnostics. Present on `nif` validations. */
193
+ interface NifDetails {
194
+ type?: NifType;
195
+ /** The number belongs to a person rather than a company or other entity. */
196
+ naturalPerson?: boolean;
197
+ /** The control character is correct. */
198
+ checksumValid?: boolean;
199
+ /** For an entity, the letter that encodes its legal form, e.g. `B`. */
200
+ entityLetter?: string;
201
+ /** For an entity, its legal form, e.g. `Private limited company (Sociedad de responsabilidad limitada)`. */
202
+ entityType?: string;
203
+ }
178
204
  /** Outcome of a single validation call. */
179
205
  interface ValidationResult {
180
206
  /** Whether the value passed every check the applied level ran. */
@@ -197,6 +223,8 @@ interface ValidationResult {
197
223
  ibanDetails?: IbanDetails;
198
224
  /** Present for Canadian SIN (`nas`) validations. */
199
225
  nasDetails?: NasDetails;
226
+ /** Present for Spanish NIF validations. */
227
+ nifDetails?: NifDetails;
200
228
  /** Quota state reported by the response headers. */
201
229
  quota?: QuotaInfo;
202
230
  /** The unmodified JSON body, for fields this SDK version does not model yet. */
@@ -296,7 +324,12 @@ declare class VerifNow {
296
324
  validateNas(value: string, options?: RequestOptions): Promise<ValidationResult>;
297
325
  /** Validate a US Social Security Number. */
298
326
  validateSsn(value: string, options?: RequestOptions): Promise<ValidationResult>;
299
- /** Validate a Spanish/Portuguese NIF. */
327
+ /**
328
+ * Validate a Spanish NIF: a DNI, a NIE (foreign nationals), the K/L/M series, or a company NIF.
329
+ *
330
+ * `nifDetails` says which, whether it belongs to a person, and for a company its legal form.
331
+ * Spanish only — a Portuguese NIF is a different scheme and is not accepted here.
332
+ */
300
333
  validateNif(value: string, options?: RequestOptions): Promise<ValidationResult>;
301
334
  /**
302
335
  * Validate a value against any rule.
@@ -388,6 +421,6 @@ declare class VerifNowResponseError extends VerifNowError {
388
421
  *
389
422
  * Kept in sync with `package.json` by a test — bump both together.
390
423
  */
391
- declare const VERSION = "1.4.0";
424
+ declare const VERSION = "1.5.0";
392
425
 
393
- export { type Deliverability, type EmailDetails, type EmailSignals, type IbanDetails, type NasDetails, type PhoneDetails, type PhoneLineType, type QuotaInfo, type RequestOptions, type RetryOptions, type RiskLevel, VALIDATION_RULES, VERSION, type ValidationLevel, type ValidationResult, type ValidationRule, type VatDetails, type VatSource, VerifNow, VerifNowAuthError, VerifNowConnectionError, VerifNowError, type VerifNowOptions, VerifNowRateLimitError, VerifNowRequestError, VerifNowResponseError, VerifNowServerError };
426
+ export { type Deliverability, type EmailDetails, type EmailSignals, type IbanDetails, type NasDetails, type NifDetails, type NifType, type PhoneDetails, type PhoneLineType, type QuotaInfo, type RequestOptions, type RetryOptions, type RiskLevel, VALIDATION_RULES, VERSION, type ValidationLevel, type ValidationResult, type ValidationRule, type VatDetails, type VatSource, VerifNow, VerifNowAuthError, VerifNowConnectionError, VerifNowError, type VerifNowOptions, VerifNowRateLimitError, VerifNowRequestError, VerifNowResponseError, VerifNowServerError };
package/dist/index.d.ts CHANGED
@@ -175,6 +175,32 @@ interface NasDetails {
175
175
  /** Printed form, e.g. `046 454 286`. */
176
176
  formatted?: string;
177
177
  }
178
+ /** The kinds of Spanish tax identification number. */
179
+ type NifType =
180
+ /** Spanish national with a DNI: 8 digits and a letter. */
181
+ 'DNI'
182
+ /** Foreign national: X, Y or Z, 7 digits and a letter. */
183
+ | 'NIE'
184
+ /** Spanish national under 14 without a DNI. */
185
+ | 'NIF_K'
186
+ /** Spanish national resident abroad, staying under six months. */
187
+ | 'NIF_L'
188
+ /** Foreign national without a NIE. */
189
+ | 'NIF_M'
190
+ /** A company or other entity: a letter for the legal form, 7 digits, a control character. */
191
+ | 'ENTITY';
192
+ /** Spanish NIF diagnostics. Present on `nif` validations. */
193
+ interface NifDetails {
194
+ type?: NifType;
195
+ /** The number belongs to a person rather than a company or other entity. */
196
+ naturalPerson?: boolean;
197
+ /** The control character is correct. */
198
+ checksumValid?: boolean;
199
+ /** For an entity, the letter that encodes its legal form, e.g. `B`. */
200
+ entityLetter?: string;
201
+ /** For an entity, its legal form, e.g. `Private limited company (Sociedad de responsabilidad limitada)`. */
202
+ entityType?: string;
203
+ }
178
204
  /** Outcome of a single validation call. */
179
205
  interface ValidationResult {
180
206
  /** Whether the value passed every check the applied level ran. */
@@ -197,6 +223,8 @@ interface ValidationResult {
197
223
  ibanDetails?: IbanDetails;
198
224
  /** Present for Canadian SIN (`nas`) validations. */
199
225
  nasDetails?: NasDetails;
226
+ /** Present for Spanish NIF validations. */
227
+ nifDetails?: NifDetails;
200
228
  /** Quota state reported by the response headers. */
201
229
  quota?: QuotaInfo;
202
230
  /** The unmodified JSON body, for fields this SDK version does not model yet. */
@@ -296,7 +324,12 @@ declare class VerifNow {
296
324
  validateNas(value: string, options?: RequestOptions): Promise<ValidationResult>;
297
325
  /** Validate a US Social Security Number. */
298
326
  validateSsn(value: string, options?: RequestOptions): Promise<ValidationResult>;
299
- /** Validate a Spanish/Portuguese NIF. */
327
+ /**
328
+ * Validate a Spanish NIF: a DNI, a NIE (foreign nationals), the K/L/M series, or a company NIF.
329
+ *
330
+ * `nifDetails` says which, whether it belongs to a person, and for a company its legal form.
331
+ * Spanish only — a Portuguese NIF is a different scheme and is not accepted here.
332
+ */
300
333
  validateNif(value: string, options?: RequestOptions): Promise<ValidationResult>;
301
334
  /**
302
335
  * Validate a value against any rule.
@@ -388,6 +421,6 @@ declare class VerifNowResponseError extends VerifNowError {
388
421
  *
389
422
  * Kept in sync with `package.json` by a test — bump both together.
390
423
  */
391
- declare const VERSION = "1.4.0";
424
+ declare const VERSION = "1.5.0";
392
425
 
393
- export { type Deliverability, type EmailDetails, type EmailSignals, type IbanDetails, type NasDetails, type PhoneDetails, type PhoneLineType, type QuotaInfo, type RequestOptions, type RetryOptions, type RiskLevel, VALIDATION_RULES, VERSION, type ValidationLevel, type ValidationResult, type ValidationRule, type VatDetails, type VatSource, VerifNow, VerifNowAuthError, VerifNowConnectionError, VerifNowError, type VerifNowOptions, VerifNowRateLimitError, VerifNowRequestError, VerifNowResponseError, VerifNowServerError };
426
+ export { type Deliverability, type EmailDetails, type EmailSignals, type IbanDetails, type NasDetails, type NifDetails, type NifType, type PhoneDetails, type PhoneLineType, type QuotaInfo, type RequestOptions, type RetryOptions, type RiskLevel, VALIDATION_RULES, VERSION, type ValidationLevel, type ValidationResult, type ValidationRule, type VatDetails, type VatSource, 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.4.0";
44
+ var VERSION = "1.5.0";
45
45
 
46
46
  // src/client.ts
47
47
  var DEFAULT_BASE_URL = "https://api.verifnow.io";
@@ -116,7 +116,12 @@ var VerifNow = class {
116
116
  validateSsn(value, options) {
117
117
  return this.validate("ssn", value, options);
118
118
  }
119
- /** Validate a Spanish/Portuguese NIF. */
119
+ /**
120
+ * Validate a Spanish NIF: a DNI, a NIE (foreign nationals), the K/L/M series, or a company NIF.
121
+ *
122
+ * `nifDetails` says which, whether it belongs to a person, and for a company its legal form.
123
+ * Spanish only — a Portuguese NIF is a different scheme and is not accepted here.
124
+ */
120
125
  validateNif(value, options) {
121
126
  return this.validate("nif", value, options);
122
127
  }
@@ -394,6 +399,17 @@ function mapNasDetails(raw) {
394
399
  formatted: asString(d.formatted)
395
400
  };
396
401
  }
402
+ function mapNifDetails(raw) {
403
+ if (raw === null || typeof raw !== "object") return void 0;
404
+ const d = raw;
405
+ return {
406
+ type: asString(d.type),
407
+ naturalPerson: asBoolean(d.natural_person),
408
+ checksumValid: asBoolean(d.checksum_valid),
409
+ entityLetter: asString(d.entity_letter),
410
+ entityType: asString(d.entity_type)
411
+ };
412
+ }
397
413
  function mapResult(payload, quota) {
398
414
  return {
399
415
  valid: payload.valid === true,
@@ -406,6 +422,7 @@ function mapResult(payload, quota) {
406
422
  phoneDetails: mapPhoneDetails(payload.phoneDetails),
407
423
  ibanDetails: mapIbanDetails(payload.ibanDetails),
408
424
  nasDetails: mapNasDetails(payload.nasDetails),
425
+ nifDetails: mapNifDetails(payload.nifDetails),
409
426
  quota,
410
427
  raw: payload
411
428
  };
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.4.0';\n","import {\n VerifNowAuthError,\n VerifNowConnectionError,\n VerifNowError,\n VerifNowRateLimitError,\n VerifNowRequestError,\n VerifNowResponseError,\n VerifNowServerError,\n} from './errors.js';\nimport type {\n EmailDetails,\n EmailSignals,\n IbanDetails,\n NasDetails,\n PhoneDetails,\n QuotaInfo,\n RetryOptions,\n ValidationResult,\n ValidationRule,\n VatDetails,\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 /** Validate a US Social Security Number. */\n validateSsn(value: string, options?: RequestOptions): Promise<ValidationResult> {\n return this.validate('ssn', value, options);\n }\n\n /** Validate a Spanish/Portuguese NIF. */\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 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 this.#requestOnce(url, body, options);\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(\n url: string,\n body: string,\n options: RequestOptions,\n ): Promise<ValidationResult> {\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: 'POST',\n headers: {\n ...this.#headers,\n '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);\n }\n\n async #handleResponse(response: Response): Promise<ValidationResult> {\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 mapResult(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 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\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 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 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 /** 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/** 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 /** 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/** 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.5.0';\n","import {\n VerifNowAuthError,\n VerifNowConnectionError,\n VerifNowError,\n VerifNowRateLimitError,\n VerifNowRequestError,\n VerifNowResponseError,\n VerifNowServerError,\n} from './errors.js';\nimport type {\n EmailDetails,\n EmailSignals,\n IbanDetails,\n NasDetails,\n NifDetails,\n PhoneDetails,\n QuotaInfo,\n RetryOptions,\n ValidationResult,\n ValidationRule,\n VatDetails,\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 /** Validate a US Social Security Number. */\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 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 this.#requestOnce(url, body, options);\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(\n url: string,\n body: string,\n options: RequestOptions,\n ): Promise<ValidationResult> {\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: 'POST',\n headers: {\n ...this.#headers,\n '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);\n }\n\n async #handleResponse(response: Response): Promise<ValidationResult> {\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 mapResult(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 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\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 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 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 /** 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/** 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 /** 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/** 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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@verifnow/sdk",
3
- "version": "1.4.0",
3
+ "version": "1.5.0",
4
4
  "description": "Official Node.js SDK for the VerifNow validation API — email, phone, IBAN, VAT, SSN, SIN and NIF validation in one call.",
5
5
  "keywords": [
6
6
  "verifnow",