@fraudnova/sdk 1.0.0 → 1.0.2

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
@@ -298,6 +298,8 @@ const result: IpResult = await client.checkIp("8.8.8.8", options);
298
298
 
299
299
  The package also exports `VERSION` (the SDK version, sent in the `User-Agent` header), `BATCH_MAX`, `DEFAULT_BASE_URL`, and three small helpers: `maskApiKey()` (a shortened key such as `fn_…abcd` that is safe to log), `parseRetryAfter()` and `parseDateInfo()`.
300
300
 
301
+ If both the ESM and CommonJS builds of the package are loaded in one process (for example a CommonJS dependency and an ESM application each importing `@fraudnova/sdk`), each build has its own copies of the error classes, so an error thrown by one build is not `instanceof` the other build's class. In that situation, compare `error.name` (for example `"RateLimitError"`) instead of using `instanceof` across module systems.
302
+
301
303
  ## Credits and limits
302
304
 
303
305
  One check costs one credit, whichever type it is. Failed checks are refunded. You get free trial credits when you verify your email address, and you buy more as credit packs, with no subscription. A batch costs one credit per successful item.
package/dist/index.cjs CHANGED
@@ -48,7 +48,7 @@ __export(index_exports, {
48
48
  module.exports = __toCommonJS(index_exports);
49
49
 
50
50
  // src/version.ts
51
- var VERSION = "1.0.0";
51
+ var VERSION = "1.0.2";
52
52
 
53
53
  // src/errors.ts
54
54
  function maskApiKey(apiKey) {
@@ -201,6 +201,9 @@ function coerceInt(v) {
201
201
  function isPlainObject(v) {
202
202
  return typeof v === "object" && v !== null && !Array.isArray(v);
203
203
  }
204
+ function asRaw(data2) {
205
+ return isPlainObject(data2) ? data2 : {};
206
+ }
204
207
  function parseDateInfo(value) {
205
208
  if (typeof value === "string") {
206
209
  return { human: value, timestamp: null, iso: null };
@@ -283,7 +286,7 @@ var IpResult = class _IpResult extends FlaggableResult {
283
286
  this.deviceModel = coerceString(raw.device_model);
284
287
  }
285
288
  static fromData(data2, creditsRemaining = null) {
286
- return new _IpResult(data2, creditsRemaining);
289
+ return new _IpResult(asRaw(data2), creditsRemaining);
287
290
  }
288
291
  defaultFlagOn() {
289
292
  return _IpResult.DEFAULT_FLAG_ON;
@@ -341,7 +344,7 @@ var EmailResult = class _EmailResult extends FlaggableResult {
341
344
  this.domainAge = parseDateInfo(raw.domain_age);
342
345
  }
343
346
  static fromData(data2, creditsRemaining = null) {
344
- return new _EmailResult(data2, creditsRemaining);
347
+ return new _EmailResult(asRaw(data2), creditsRemaining);
345
348
  }
346
349
  defaultFlagOn() {
347
350
  return _EmailResult.DEFAULT_FLAG_ON;
@@ -395,7 +398,7 @@ var PhoneResult = class _PhoneResult extends FlaggableResult {
395
398
  this.localFormat = coerceString(raw.local_format);
396
399
  }
397
400
  static fromData(data2, creditsRemaining = null) {
398
- return new _PhoneResult(data2, creditsRemaining);
401
+ return new _PhoneResult(asRaw(data2), creditsRemaining);
399
402
  }
400
403
  defaultFlagOn() {
401
404
  return _PhoneResult.DEFAULT_FLAG_ON;
@@ -468,12 +471,22 @@ var BatchResult = class _BatchResult {
468
471
  };
469
472
 
470
473
  // src/retry.ts
474
+ var MONTH = "(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)";
475
+ var SHORT_DAY = "(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun)";
476
+ var LONG_DAY = "(?:Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday)";
477
+ var TIME = "[0-9]{2}:[0-9]{2}:[0-9]{2}";
478
+ var IMF_FIXDATE = new RegExp(`^${SHORT_DAY}, [0-9]{2} ${MONTH} [0-9]{4} ${TIME} GMT$`, "i");
479
+ var RFC_850 = new RegExp(`^${LONG_DAY}, [0-9]{2}-${MONTH}-[0-9]{2} ${TIME} GMT$`, "i");
480
+ var ASCTIME = new RegExp(`^${SHORT_DAY} ${MONTH} (?: [0-9]|[0-9]{2}) ${TIME} [0-9]{4}$`, "i");
471
481
  function parseRetryAfter(value, now = /* @__PURE__ */ new Date()) {
472
482
  if (value === null) return null;
473
483
  const text = value.trim();
474
484
  if (text === "") return null;
475
485
  if (/^[0-9]+$/.test(text)) return Number.parseInt(text, 10);
476
- const when = Date.parse(text);
486
+ let when;
487
+ if (IMF_FIXDATE.test(text) || RFC_850.test(text)) when = Date.parse(text);
488
+ else if (ASCTIME.test(text)) when = Date.parse(`${text} GMT`);
489
+ else return null;
477
490
  if (Number.isNaN(when)) return null;
478
491
  return Math.max(0, Math.ceil((when - now.getTime()) / 1e3));
479
492
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/version.ts","../src/errors.ts","../src/results.ts","../src/retry.ts","../src/transport.ts","../src/client.ts"],"sourcesContent":["export { VERSION } from \"./version.js\";\nexport {\n BATCH_MAX,\n DEFAULT_BASE_URL,\n FraudNova,\n type FraudNovaOptions,\n type IpCheckOptions,\n type PhoneCheckOptions,\n} from \"./client.js\";\nexport type { FetchLike } from \"./transport.js\";\nexport {\n AccountSuspendedError,\n ApiError,\n AuthenticationError,\n FraudNovaError,\n InsufficientCreditsError,\n InvalidArgumentError,\n RateLimitError,\n TransportError,\n UnexpectedResponseError,\n UpstreamError,\n ValidationError,\n maskApiKey,\n} from \"./errors.js\";\nexport { RetryPolicy, parseRetryAfter, type RetryPolicyOptions } from \"./retry.js\";\nexport {\n BatchItem,\n BatchResult,\n EmailResult,\n IpResult,\n PhoneResult,\n parseDateInfo,\n type CheckKind,\n type DateInfo,\n type FlagOptions,\n type Result,\n} from \"./results.js\";\n","export const VERSION = \"1.0.0\";\n","/** Return a safe, shortened form of an API key for display. */\nexport function maskApiKey(apiKey: string): string {\n return apiKey.length > 12 ? `fn_…${apiKey.slice(-4)}` : \"fn_…\";\n}\n\n/** Base class for every error thrown by this SDK. */\nexport class FraudNovaError extends Error {\n constructor(message?: string, options?: ErrorOptions) {\n super(message, options);\n this.name = \"FraudNovaError\";\n }\n}\n\n/** A client-side input problem; no request was sent. */\nexport class InvalidArgumentError extends FraudNovaError {\n constructor(message?: string) {\n super(message);\n this.name = \"InvalidArgumentError\";\n }\n}\n\n/** The HTTP request itself failed (timeout, DNS, TLS). */\nexport class TransportError extends FraudNovaError {\n constructor(message: string, options?: { cause?: unknown }) {\n super(message, options);\n this.name = \"TransportError\";\n }\n}\n\n/** The API answered with an error status. */\nexport class ApiError extends FraudNovaError {\n readonly statusCode: number;\n readonly apiMessage: string;\n readonly body: Record<string, unknown>;\n\n constructor(\n statusCode: number,\n apiMessage: string,\n body: Record<string, unknown> = {},\n message?: string,\n ) {\n super(message ?? `FraudNova API error (HTTP ${statusCode}): ${apiMessage}`);\n this.name = \"ApiError\";\n this.statusCode = statusCode;\n this.apiMessage = apiMessage;\n this.body = body;\n }\n\n static fromResponse(\n statusCode: number,\n text: string,\n retryAfter: number | null = null,\n ): ApiError {\n let decoded: unknown = null;\n try {\n decoded = JSON.parse(text);\n } catch {\n decoded = null;\n }\n const isJson = typeof decoded === \"object\" && decoded !== null && !Array.isArray(decoded);\n const body: Record<string, unknown> = isJson ? (decoded as Record<string, unknown>) : {};\n const envelope = isJson && (\"success\" in body || \"message\" in body);\n\n let apiMessage = \"\";\n for (const key of [\"message\", \"error\"]) {\n const value = body[key];\n if (typeof value === \"string\" && value !== \"\") {\n apiMessage = value;\n break;\n }\n }\n if (apiMessage === \"\") {\n const stripped = text.trim();\n apiMessage = stripped !== \"\" ? stripped.slice(0, 200) : `HTTP ${statusCode}`;\n }\n\n const full = isJson ? undefined : `FraudNova API error (HTTP ${statusCode}): non-JSON response`;\n\n if (envelope && statusCode === 401)\n return new AuthenticationError(statusCode, apiMessage, body, full);\n if (envelope && statusCode === 402) {\n return new InsufficientCreditsError(statusCode, apiMessage, body, full);\n }\n if (envelope && statusCode === 403)\n return new AccountSuspendedError(statusCode, apiMessage, body, full);\n if (envelope && statusCode === 422)\n return new ValidationError(statusCode, apiMessage, body, full);\n if (statusCode === 429) {\n return new RateLimitError(statusCode, apiMessage, body, retryAfter, full);\n }\n if (statusCode === 502 || statusCode === 503 || statusCode === 504) {\n return new UpstreamError(statusCode, apiMessage, body, statusCode === 503 && envelope, full);\n }\n return new ApiError(statusCode, apiMessage, body, full);\n }\n}\n\n/** 401: missing or invalid API key. */\nexport class AuthenticationError extends ApiError {\n constructor(\n statusCode: number,\n apiMessage: string,\n body: Record<string, unknown> = {},\n message?: string,\n ) {\n super(statusCode, apiMessage, body, message);\n this.name = \"AuthenticationError\";\n }\n}\n\n/** 402: the account has no credits left. */\nexport class InsufficientCreditsError extends ApiError {\n constructor(\n statusCode: number,\n apiMessage: string,\n body: Record<string, unknown> = {},\n message?: string,\n ) {\n super(statusCode, apiMessage, body, message);\n this.name = \"InsufficientCreditsError\";\n }\n}\n\n/** 403: the account is suspended. */\nexport class AccountSuspendedError extends ApiError {\n constructor(\n statusCode: number,\n apiMessage: string,\n body: Record<string, unknown> = {},\n message?: string,\n ) {\n super(statusCode, apiMessage, body, message);\n this.name = \"AccountSuspendedError\";\n }\n}\n\n/** 422: the request failed validation. */\nexport class ValidationError extends ApiError {\n constructor(\n statusCode: number,\n apiMessage: string,\n body: Record<string, unknown> = {},\n message?: string,\n ) {\n super(statusCode, apiMessage, body, message);\n this.name = \"ValidationError\";\n }\n}\n\n/** 429: rate or concurrency limit hit. */\nexport class RateLimitError extends ApiError {\n readonly retryAfter: number | null;\n\n constructor(\n statusCode: number,\n apiMessage: string,\n body: Record<string, unknown> = {},\n retryAfter: number | null = null,\n message?: string,\n ) {\n super(statusCode, apiMessage, body, message);\n this.name = \"RateLimitError\";\n this.retryAfter = retryAfter;\n }\n\n get isConcurrencyLimit(): boolean {\n return this.apiMessage.includes(\"Too many concurrent requests\");\n }\n}\n\n/** 502/503/504: the lookup could not be completed. */\nexport class UpstreamError extends ApiError {\n readonly creditRefunded: boolean;\n\n constructor(\n statusCode: number,\n apiMessage: string,\n body: Record<string, unknown> = {},\n creditRefunded = false,\n message?: string,\n ) {\n let text = message ?? `FraudNova API error (HTTP ${statusCode}): ${apiMessage}`;\n if (creditRefunded) text += \" The credit for this check was refunded.\";\n super(statusCode, apiMessage, body, text);\n this.name = \"UpstreamError\";\n this.creditRefunded = creditRefunded;\n }\n}\n\n/** A 2xx response whose body is not the success envelope. */\nexport class UnexpectedResponseError extends ApiError {\n constructor(\n statusCode: number,\n apiMessage: string,\n body: Record<string, unknown> = {},\n message?: string,\n ) {\n super(statusCode, apiMessage, body, message);\n this.name = \"UnexpectedResponseError\";\n }\n}\n","/** Typed, leniently parsed results for IP, email and phone checks. */\n\ntype Raw = Record<string, unknown>;\n\nexport function coerceBool(v: unknown): boolean | null {\n return typeof v === \"boolean\" ? v : null;\n}\n\nexport function coerceString(v: unknown): string | null {\n return typeof v === \"string\" ? v : null;\n}\n\nfunction roundHalfAway(n: number): number {\n const r = Math.sign(n) * Math.floor(Math.abs(n) + 0.5);\n return r === 0 ? 0 : r;\n}\n\nexport function coerceInt(v: unknown): number | null {\n let n: number;\n if (typeof v === \"number\") {\n n = v;\n } else if (typeof v === \"string\" && (/^-?\\d+$/.test(v) || /^-?\\d+\\.\\d+$/.test(v))) {\n n = Number(v);\n } else {\n return null;\n }\n return Number.isFinite(n) ? roundHalfAway(n) : null;\n}\n\nfunction isPlainObject(v: unknown): v is Raw {\n return typeof v === \"object\" && v !== null && !Array.isArray(v);\n}\n\nexport interface DateInfo {\n human: string | null;\n timestamp: number | null;\n iso: string | null;\n}\n\nexport function parseDateInfo(value: unknown): DateInfo | null {\n if (typeof value === \"string\") {\n return { human: value, timestamp: null, iso: null };\n }\n if (isPlainObject(value)) {\n return {\n human: coerceString(value.human),\n timestamp: coerceInt(value.timestamp),\n iso: coerceString(value.iso),\n };\n }\n return null;\n}\n\nexport interface FlagOptions {\n riskThreshold?: number;\n flagOn?: readonly string[];\n}\n\nexport type CheckKind = \"ip\" | \"email\" | \"phone\";\n\nabstract class FlaggableResult {\n readonly creditsRemaining: number | null;\n readonly raw: Raw;\n\n protected constructor(raw: Raw, creditsRemaining: number | null) {\n this.raw = raw;\n this.creditsRemaining = creditsRemaining;\n }\n\n protected abstract defaultFlagOn(): readonly string[];\n\n toJSON(): Raw {\n return this.raw;\n }\n\n flagReasons(options: FlagOptions = {}): string[] {\n const threshold = options.riskThreshold ?? 75;\n const reasons: string[] = [];\n if ((coerceInt(this.raw.fraud_score) ?? 0) >= threshold) {\n reasons.push(\"fraud_score\");\n }\n for (const name of options.flagOn ?? this.defaultFlagOn()) {\n if (this.raw[name] === true) {\n reasons.push(name);\n }\n }\n return reasons;\n }\n\n flagged(options: FlagOptions = {}): boolean {\n return this.flagReasons(options).length > 0;\n }\n}\n\nexport class IpResult extends FlaggableResult {\n static readonly DEFAULT_FLAG_ON: readonly string[] = [\"proxy\", \"vpn\", \"tor\"];\n\n readonly fraudScore: number;\n readonly proxy: boolean | null;\n readonly vpn: boolean | null;\n readonly tor: boolean | null;\n readonly activeVpn: boolean | null;\n readonly activeTor: boolean | null;\n readonly recentAbuse: boolean | null;\n readonly botStatus: boolean | null;\n readonly countryCode: string | null;\n readonly region: string | null;\n readonly city: string | null;\n readonly isp: string | null;\n readonly organization: string | null;\n readonly asn: number | null;\n readonly operatingSystem: string | null;\n readonly browser: string | null;\n readonly deviceBrand: string | null;\n readonly deviceModel: string | null;\n\n private constructor(raw: Raw, creditsRemaining: number | null) {\n super(raw, creditsRemaining);\n this.fraudScore = coerceInt(raw.fraud_score) ?? 0;\n this.proxy = coerceBool(raw.proxy);\n this.vpn = coerceBool(raw.vpn);\n this.tor = coerceBool(raw.tor);\n this.activeVpn = coerceBool(raw.active_vpn);\n this.activeTor = coerceBool(raw.active_tor);\n this.recentAbuse = coerceBool(raw.recent_abuse);\n this.botStatus = coerceBool(raw.bot_status);\n this.countryCode = coerceString(raw.country_code);\n this.region = coerceString(raw.region);\n this.city = coerceString(raw.city);\n this.isp = coerceString(raw.ISP);\n this.organization = coerceString(raw.organization);\n this.asn = coerceInt(raw.ASN);\n this.operatingSystem = coerceString(raw.operating_system);\n this.browser = coerceString(raw.browser);\n this.deviceBrand = coerceString(raw.device_brand);\n this.deviceModel = coerceString(raw.device_model);\n }\n\n static fromData(data: Raw, creditsRemaining: number | null = null): IpResult {\n return new IpResult(data, creditsRemaining);\n }\n\n protected defaultFlagOn(): readonly string[] {\n return IpResult.DEFAULT_FLAG_ON;\n }\n}\n\nexport class EmailResult extends FlaggableResult {\n static readonly DEFAULT_FLAG_ON: readonly string[] = [\"disposable\"];\n\n readonly fraudScore: number;\n readonly valid: boolean | null;\n readonly disposable: boolean | null;\n readonly catchAll: boolean | null;\n readonly generic: boolean | null;\n readonly common: boolean | null;\n readonly dnsValid: boolean | null;\n readonly honeypot: boolean | null;\n readonly frequentComplainer: boolean | null;\n readonly suspect: boolean | null;\n readonly recentAbuse: boolean | null;\n readonly leaked: boolean | null;\n readonly riskyTld: boolean | null;\n readonly spfRecord: boolean | null;\n readonly dmarcRecord: boolean | null;\n readonly smtpScore: number | null;\n readonly overallScore: number | null;\n readonly deliverability: string | null;\n readonly spamTrapScore: string | null;\n readonly resultType: string | null;\n readonly sanitizedEmail: string | null;\n readonly firstSeen: DateInfo | null;\n readonly domainAge: DateInfo | null;\n\n private constructor(raw: Raw, creditsRemaining: number | null) {\n super(raw, creditsRemaining);\n this.fraudScore = coerceInt(raw.fraud_score) ?? 0;\n this.valid = coerceBool(raw.valid);\n this.disposable = coerceBool(raw.disposable);\n this.catchAll = coerceBool(raw.catch_all);\n this.generic = coerceBool(raw.generic);\n this.common = coerceBool(raw.common);\n this.dnsValid = coerceBool(raw.dns_valid);\n this.honeypot = coerceBool(raw.honeypot);\n this.frequentComplainer = coerceBool(raw.frequent_complainer);\n this.suspect = coerceBool(raw.suspect);\n this.recentAbuse = coerceBool(raw.recent_abuse);\n this.leaked = coerceBool(raw.leaked);\n this.riskyTld = coerceBool(raw.risky_tld);\n this.spfRecord = coerceBool(raw.spf_record);\n this.dmarcRecord = coerceBool(raw.dmarc_record);\n this.smtpScore = coerceInt(raw.smtp_score);\n this.overallScore = coerceInt(raw.overall_score);\n this.deliverability = coerceString(raw.deliverability);\n this.spamTrapScore = coerceString(raw.spam_trap_score);\n this.resultType = coerceString(raw.result_type);\n this.sanitizedEmail = coerceString(raw.sanitized_email);\n this.firstSeen = parseDateInfo(raw.first_seen);\n this.domainAge = parseDateInfo(raw.domain_age);\n }\n\n static fromData(data: Raw, creditsRemaining: number | null = null): EmailResult {\n return new EmailResult(data, creditsRemaining);\n }\n\n protected defaultFlagOn(): readonly string[] {\n return EmailResult.DEFAULT_FLAG_ON;\n }\n}\n\nexport class PhoneResult extends FlaggableResult {\n static readonly DEFAULT_FLAG_ON: readonly string[] = [\"VOIP\", \"risky\"];\n\n readonly fraudScore: number;\n readonly valid: boolean | null;\n readonly recentAbuse: boolean | null;\n readonly voip: boolean | null;\n readonly prepaid: boolean | null;\n readonly risky: boolean | null;\n readonly active: boolean | null;\n readonly doNotCall: boolean | null;\n readonly leaked: boolean | null;\n readonly spammer: boolean | null;\n readonly dialingCode: number | null;\n readonly carrier: string | null;\n readonly lineType: string | null;\n readonly country: string | null;\n readonly region: string | null;\n readonly city: string | null;\n readonly timezone: string | null;\n readonly zipCode: string | null;\n readonly activeStatus: string | null;\n readonly formatted: string | null;\n readonly localFormat: string | null;\n\n private constructor(raw: Raw, creditsRemaining: number | null) {\n super(raw, creditsRemaining);\n this.fraudScore = coerceInt(raw.fraud_score) ?? 0;\n this.valid = coerceBool(raw.valid);\n this.recentAbuse = coerceBool(raw.recent_abuse);\n this.voip = coerceBool(raw.VOIP);\n this.prepaid = coerceBool(raw.prepaid);\n this.risky = coerceBool(raw.risky);\n this.active = coerceBool(raw.active);\n this.doNotCall = coerceBool(raw.do_not_call);\n this.leaked = coerceBool(raw.leaked);\n this.spammer = coerceBool(raw.spammer);\n this.dialingCode = coerceInt(raw.dialing_code);\n this.carrier = coerceString(raw.carrier);\n this.lineType = coerceString(raw.line_type);\n this.country = coerceString(raw.country);\n this.region = coerceString(raw.region);\n this.city = coerceString(raw.city);\n this.timezone = coerceString(raw.timezone);\n this.zipCode = coerceString(raw.zip_code);\n this.activeStatus = coerceString(raw.active_status);\n this.formatted = coerceString(raw.formatted);\n this.localFormat = coerceString(raw.local_format);\n }\n\n static fromData(data: Raw, creditsRemaining: number | null = null): PhoneResult {\n return new PhoneResult(data, creditsRemaining);\n }\n\n protected defaultFlagOn(): readonly string[] {\n return PhoneResult.DEFAULT_FLAG_ON;\n }\n}\n\nexport type Result = IpResult | EmailResult | PhoneResult;\n\nexport class BatchItem {\n constructor(\n readonly subject: string,\n readonly success: boolean,\n readonly result: Result | null,\n readonly error: string | null,\n ) {}\n\n isInvalid(): boolean {\n return (\n this.error === \"invalid_ip\" ||\n this.error === \"invalid_email\" ||\n this.error === \"invalid_phone\"\n );\n }\n\n isInsufficientCredits(): boolean {\n return this.error === \"insufficient_credits\";\n }\n\n isUpstreamFailed(): boolean {\n return this.error === \"upstream_failed\";\n }\n}\n\nexport class BatchResult {\n constructor(\n readonly items: BatchItem[],\n readonly creditsRemaining: number,\n ) {}\n\n static fromEnvelope(envelope: Raw, kind: CheckKind): BatchResult {\n const items: BatchItem[] = [];\n const rawItems = envelope.results;\n if (Array.isArray(rawItems)) {\n for (const item of rawItems) {\n if (!isPlainObject(item)) {\n continue;\n }\n const success = coerceBool(item.success) ?? false;\n const data = item.data;\n let result: Result | null = null;\n if (success && isPlainObject(data)) {\n result =\n kind === \"ip\"\n ? IpResult.fromData(data)\n : kind === \"email\"\n ? EmailResult.fromData(data)\n : PhoneResult.fromData(data);\n }\n items.push(\n new BatchItem(coerceString(item[kind]) ?? \"\", success, result, coerceString(item.error)),\n );\n }\n }\n return new BatchResult(items, coerceInt(envelope.credits_remaining) ?? 0);\n }\n\n successful(): BatchItem[] {\n return this.items.filter((i) => i.success);\n }\n\n failed(): BatchItem[] {\n return this.items.filter((i) => !i.success);\n }\n\n hasFailures(): boolean {\n return this.items.some((i) => !i.success);\n }\n\n get length(): number {\n return this.items.length;\n }\n\n *[Symbol.iterator](): Iterator<BatchItem> {\n yield* this.items;\n }\n}\n","import { InvalidArgumentError, type RateLimitError } from \"./errors.js\";\n\n/** Parse a Retry-After header (delta-seconds or HTTP-date) into whole seconds. */\nexport function parseRetryAfter(value: string | null, now: Date = new Date()): number | null {\n if (value === null) return null;\n const text = value.trim();\n if (text === \"\") return null;\n if (/^[0-9]+$/.test(text)) return Number.parseInt(text, 10);\n const when = Date.parse(text);\n if (Number.isNaN(when)) return null;\n return Math.max(0, Math.ceil((when - now.getTime()) / 1000));\n}\n\nexport interface RetryPolicyOptions {\n maxAttempts?: number;\n concurrencyDelays?: readonly number[];\n rateLimitDelays?: readonly number[];\n honourRetryAfter?: boolean;\n maxRetryAfter?: number;\n sleep?: (seconds: number) => Promise<void>;\n}\n\nconst defaultSleep = (seconds: number): Promise<void> =>\n new Promise((resolve) => setTimeout(resolve, seconds * 1000));\n\n/** Opt-in retry policy for rate-limited (429) requests. All delays are in seconds. */\nexport class RetryPolicy {\n readonly maxAttempts: number;\n readonly concurrencyDelays: readonly number[];\n readonly rateLimitDelays: readonly number[];\n readonly honourRetryAfter: boolean;\n readonly maxRetryAfter: number;\n readonly #sleeper: (seconds: number) => Promise<void>;\n\n constructor(options: RetryPolicyOptions = {}) {\n this.maxAttempts = options.maxAttempts ?? 3;\n this.concurrencyDelays = [...(options.concurrencyDelays ?? [1, 2, 4])];\n this.rateLimitDelays = [...(options.rateLimitDelays ?? [5, 15, 40])];\n this.honourRetryAfter = options.honourRetryAfter ?? true;\n this.maxRetryAfter = options.maxRetryAfter ?? 60;\n this.#sleeper = options.sleep ?? defaultSleep;\n if (!Number.isInteger(this.maxAttempts) || this.maxAttempts < 1) {\n throw new InvalidArgumentError(\"maxAttempts must be an integer of at least 1.\");\n }\n if (!Number.isFinite(this.maxRetryAfter) || this.maxRetryAfter < 0) {\n throw new InvalidArgumentError(\"maxRetryAfter must be a finite number that is not negative.\");\n }\n for (const [name, delays] of [\n [\"concurrencyDelays\", this.concurrencyDelays],\n [\"rateLimitDelays\", this.rateLimitDelays],\n ] as const) {\n if (\n !delays.every((delay) => typeof delay === \"number\" && Number.isFinite(delay) && delay >= 0)\n ) {\n throw new InvalidArgumentError(\n `${name} must contain only finite numbers that are not negative.`,\n );\n }\n }\n }\n\n /** Seconds to wait before retrying after `attempt` (1-based) failed, or null to stop. */\n delayFor(error: RateLimitError, attempt: number): number | null {\n if (attempt >= this.maxAttempts) return null;\n if (this.honourRetryAfter && error.retryAfter !== null) {\n return error.retryAfter > this.maxRetryAfter ? null : error.retryAfter;\n }\n const schedule = error.isConcurrencyLimit ? this.concurrencyDelays : this.rateLimitDelays;\n const delay = schedule[attempt - 1];\n return delay === undefined ? null : delay;\n }\n\n sleep(seconds: number): Promise<void> {\n return this.#sleeper(seconds);\n }\n}\n","import {\n ApiError,\n InvalidArgumentError,\n RateLimitError,\n TransportError,\n UnexpectedResponseError,\n maskApiKey,\n} from \"./errors.js\";\nimport { type RetryPolicy, parseRetryAfter } from \"./retry.js\";\nimport { VERSION } from \"./version.js\";\n\n/** A `fetch`-compatible function. */\nexport type FetchLike = (url: string, init: RequestInit) => Promise<Response>;\n\nexport interface TransportOptions {\n apiKey: string;\n baseUrl: string;\n timeout: number;\n retry: RetryPolicy | null;\n fetch: FetchLike | null;\n}\n\ntype QueryParams = Record<string, string | number | boolean | null | undefined>;\n\nfunction userAgent(): string {\n let agent = `fraudnova-node/${VERSION}`;\n if (typeof process !== \"undefined\" && typeof process.versions?.node === \"string\") {\n agent += ` node/${process.versions.node}`;\n }\n return agent;\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n/** Internal HTTP layer: builds requests, decodes the envelope, maps errors, retries 429s. */\nexport class Transport {\n readonly baseUrl: string;\n readonly #apiKey: string;\n readonly #timeout: number;\n readonly #retry: RetryPolicy | null;\n readonly #fetch: FetchLike;\n readonly #userAgent: string;\n\n constructor(options: TransportOptions) {\n if (!/^[\\x21-\\x7e]+$/.test(options.apiKey)) {\n throw new InvalidArgumentError(\n \"apiKey must contain only printable ASCII characters, without whitespace.\",\n );\n }\n let fetchImpl: FetchLike | null = options.fetch ?? null;\n if (fetchImpl === null) {\n const globalFetch = (globalThis as { fetch?: typeof globalThis.fetch }).fetch;\n if (typeof globalFetch === \"function\") {\n fetchImpl = globalFetch.bind(globalThis);\n }\n }\n if (fetchImpl === null) {\n throw new InvalidArgumentError(\n \"No fetch implementation available; pass one in the fetch option\",\n );\n }\n this.#apiKey = options.apiKey;\n this.baseUrl = options.baseUrl.replace(/\\/+$/, \"\");\n this.#timeout = options.timeout;\n this.#retry = options.retry;\n this.#fetch = fetchImpl;\n this.#userAgent = userAgent();\n }\n\n get(\n path: string,\n params: QueryParams | null = null,\n authenticated = true,\n ): Promise<Record<string, unknown>> {\n const query = new URLSearchParams();\n for (const [name, value] of Object.entries(params ?? {})) {\n if (value === undefined || value === null) continue;\n query.append(name, String(value));\n }\n const search = query.toString();\n const url = this.baseUrl + path + (search !== \"\" ? `?${search}` : \"\");\n return this.#send(\"GET\", url, authenticated);\n }\n\n post(path: string, json: Record<string, unknown>): Promise<Record<string, unknown>> {\n return this.#send(\"POST\", this.baseUrl + path, true, JSON.stringify(json));\n }\n\n #headers(authenticated: boolean, hasBody: boolean): Record<string, string> {\n const headers: Record<string, string> = {\n Accept: \"application/json\",\n \"User-Agent\": this.#userAgent,\n };\n if (authenticated) headers.Authorization = `Bearer ${this.#apiKey}`;\n if (hasBody) headers[\"Content-Type\"] = \"application/json\";\n return headers;\n }\n\n async #send(\n method: string,\n url: string,\n authenticated: boolean,\n body?: string,\n ): Promise<Record<string, unknown>> {\n let attempt = 1;\n for (;;) {\n const init: RequestInit = {\n method,\n headers: this.#headers(authenticated, body !== undefined),\n };\n if (body !== undefined) init.body = body;\n\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.#timeout);\n let response: Response;\n let text: string;\n try {\n response = await this.#fetch(url, { ...init, signal: controller.signal });\n text = await response.text();\n } catch (error) {\n if (controller.signal.aborted) {\n throw new TransportError(`FraudNova request timed out after ${this.#timeout} ms`, {\n cause: error,\n });\n }\n const original = error instanceof Error ? error.message : String(error);\n const masked = original.split(this.#apiKey).join(maskApiKey(this.#apiKey));\n throw new TransportError(`FraudNova request failed: ${masked}`, { cause: error });\n } finally {\n clearTimeout(timer);\n }\n\n try {\n return Transport.#decode(response, text);\n } catch (error) {\n if (!(error instanceof RateLimitError)) throw error;\n const delay = this.#retry?.delayFor(error, attempt) ?? null;\n if (this.#retry === null || delay === null) throw error;\n await this.#retry.sleep(delay);\n attempt++;\n }\n }\n }\n\n static #decode(response: Response, text: string): Record<string, unknown> {\n const status = response.status;\n if (status < 200 || status >= 300) {\n throw ApiError.fromResponse(\n status,\n text,\n parseRetryAfter(response.headers.get(\"retry-after\")),\n );\n }\n let decoded: unknown = null;\n try {\n decoded = JSON.parse(text);\n } catch {\n decoded = null;\n }\n if (!isPlainObject(decoded) || decoded.success !== true) {\n throw new UnexpectedResponseError(\n status,\n \"Unexpected response body\",\n isPlainObject(decoded) ? decoded : {},\n );\n }\n return decoded;\n }\n}\n","import { InvalidArgumentError, UnexpectedResponseError, maskApiKey } from \"./errors.js\";\nimport {\n BatchItem,\n BatchResult,\n EmailResult,\n IpResult,\n PhoneResult,\n coerceInt,\n type CheckKind,\n} from \"./results.js\";\nimport type { RetryPolicy } from \"./retry.js\";\nimport { Transport, type FetchLike } from \"./transport.js\";\n\n/** The production API base URL. */\nexport const DEFAULT_BASE_URL = \"https://fraudnova.com/api/v1\";\n\n/** The most subjects the API accepts in one batch request; larger arrays are chunked. */\nexport const BATCH_MAX = 100;\n\nconst DEFAULT_TIMEOUT = 30_000;\n\nconst inspectSymbol = Symbol.for(\"nodejs.util.inspect.custom\");\n\nexport interface FraudNovaOptions {\n /** API base URL; defaults to {@link DEFAULT_BASE_URL}. */\n baseUrl?: string;\n /** Per-request timeout in milliseconds; defaults to 30 000. */\n timeout?: number;\n /** Opt-in retry policy for 429 responses. */\n retry?: RetryPolicy;\n /** A fetch-compatible function; defaults to the global `fetch`. */\n fetch?: FetchLike;\n}\n\nexport interface IpCheckOptions {\n strictness?: 0 | 1 | 2 | 3;\n mobile?: boolean;\n userAgent?: string;\n userLanguage?: string;\n}\n\nexport interface PhoneCheckOptions {\n country?: string;\n}\n\ntype Envelope = Record<string, unknown>;\n\nfunction subject(value: unknown, name: string): string {\n if (typeof value !== \"string\" || value.trim() === \"\") {\n throw new InvalidArgumentError(`${name} must be a non-empty string`);\n }\n return value.trim();\n}\n\nfunction strictness(value: unknown): number | undefined {\n if (value === undefined) return undefined;\n if (typeof value !== \"number\" || !Number.isInteger(value) || value < 0 || value > 3) {\n throw new InvalidArgumentError(\"strictness must be an integer from 0 to 3\");\n }\n return value;\n}\n\nfunction country(value: unknown): string | undefined {\n if (typeof value !== \"string\") return undefined;\n const cleaned = value.trim().toUpperCase();\n return cleaned !== \"\" ? cleaned : undefined;\n}\n\nfunction data(envelope: Envelope): Record<string, unknown> {\n const value = envelope.data;\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n throw new UnexpectedResponseError(200, \"Missing data\", envelope);\n }\n return value as Record<string, unknown>;\n}\n\n/** Build the IP options shared by the single and batch calls, keeping only defined values. */\nfunction ipOptions(options: IpCheckOptions): Record<string, number | boolean | string> {\n const out: Record<string, number | boolean | string> = {};\n const level = strictness(options.strictness);\n if (level !== undefined) out.strictness = level;\n if (options.mobile === true || options.mobile === false) out.mobile = options.mobile;\n if (options.userAgent !== undefined) out.user_agent = options.userAgent;\n if (options.userLanguage !== undefined) out.user_language = options.userLanguage;\n return out;\n}\n\n/** The FraudNova API client. */\nexport class FraudNova {\n readonly baseUrl: string;\n readonly #apiKey: string;\n readonly #transport: Transport;\n\n constructor(apiKey: string, options: FraudNovaOptions = {}) {\n if (typeof apiKey !== \"string\" || apiKey.trim() === \"\") {\n throw new InvalidArgumentError(\"apiKey must be a non-empty string\");\n }\n const baseUrl = options.baseUrl ?? DEFAULT_BASE_URL;\n if (typeof baseUrl !== \"string\" || !/^https?:\\/\\//i.test(baseUrl)) {\n throw new InvalidArgumentError(\"baseUrl must start with http:// or https://\");\n }\n const timeout = options.timeout ?? DEFAULT_TIMEOUT;\n if (\n typeof timeout !== \"number\" ||\n !Number.isFinite(timeout) ||\n timeout <= 0 ||\n timeout > 2_147_483_647\n ) {\n throw new InvalidArgumentError(\n \"timeout must be a positive number of milliseconds (at most 2147483647)\",\n );\n }\n this.#apiKey = apiKey.trim();\n this.#transport = new Transport({\n apiKey: this.#apiKey,\n baseUrl,\n timeout,\n retry: options.retry ?? null,\n fetch: options.fetch ?? null,\n });\n this.baseUrl = this.#transport.baseUrl;\n }\n\n /** Score one IP address. */\n async checkIp(ip: string, options: IpCheckOptions = {}): Promise<IpResult> {\n const params = { ip: subject(ip, \"ip\"), ...ipOptions(options) };\n const envelope = await this.#transport.get(\"/check\", params);\n return IpResult.fromData(data(envelope), coerceInt(envelope.credits_remaining));\n }\n\n /** Score one email address. */\n async checkEmail(email: string): Promise<EmailResult> {\n const params = { email: subject(email, \"email\") };\n const envelope = await this.#transport.get(\"/check/email\", params);\n return EmailResult.fromData(data(envelope), coerceInt(envelope.credits_remaining));\n }\n\n /** Score one phone number. */\n async checkPhone(phone: string, options: PhoneCheckOptions = {}): Promise<PhoneResult> {\n const params = { phone: subject(phone, \"phone\"), country: country(options.country) };\n const envelope = await this.#transport.get(\"/check/phone\", params);\n return PhoneResult.fromData(data(envelope), coerceInt(envelope.credits_remaining));\n }\n\n /** Score many IP addresses, chunked by {@link BATCH_MAX}. */\n async checkIps(\n ips: ReadonlyArray<string | null>,\n options: IpCheckOptions = {},\n ): Promise<BatchResult> {\n return this.#batch(\"/check/batch\", \"ips\", \"ip\", ips, ipOptions(options));\n }\n\n /** Score many email addresses, chunked by {@link BATCH_MAX}. */\n async checkEmails(emails: ReadonlyArray<string | null>): Promise<BatchResult> {\n return this.#batch(\"/check/email/batch\", \"emails\", \"email\", emails, {});\n }\n\n /** Score many phone numbers, chunked by {@link BATCH_MAX}. */\n async checkPhones(\n phones: ReadonlyArray<string | null>,\n options: PhoneCheckOptions = {},\n ): Promise<BatchResult> {\n const code = country(options.country);\n const extra: Record<string, string> = code !== undefined ? { country: code } : {};\n return this.#batch(\"/check/phone/batch\", \"phones\", \"phone\", phones, extra);\n }\n\n /** The account's remaining credits. */\n async balance(): Promise<number> {\n const envelope = await this.#transport.get(\"/balance\");\n const credits = coerceInt(envelope.credits_remaining);\n if (credits === null) {\n throw new UnexpectedResponseError(200, \"Missing credits_remaining\", envelope);\n }\n return credits;\n }\n\n /** The caller's public IP address as the API sees it (free, unauthenticated). */\n async myIp(): Promise<string> {\n const envelope = await this.#transport.get(\"/my-ip\", null, false);\n const ip = envelope.ip;\n if (typeof ip !== \"string\" || ip === \"\") {\n throw new UnexpectedResponseError(200, \"Missing ip\", envelope);\n }\n return ip;\n }\n\n async #batch(\n path: string,\n field: string,\n kind: CheckKind,\n subjects: ReadonlyArray<string | null>,\n extra: Record<string, unknown>,\n ): Promise<BatchResult> {\n if (!Array.isArray(subjects)) {\n throw new InvalidArgumentError(`${field} must be an array of values`);\n }\n if (subjects.length === 0) {\n throw new InvalidArgumentError(`${field} must not be empty`);\n }\n const cleaned: Array<string | null> = subjects.map((item: unknown, index) => {\n if (item !== null && typeof item !== \"string\") {\n throw new InvalidArgumentError(`${field}[${index}] must be a string or null`);\n }\n return item === null ? null : item.trim();\n });\n\n const items: BatchItem[] = [];\n let credits = 0;\n for (let start = 0; start < cleaned.length; start += BATCH_MAX) {\n const chunk = cleaned.slice(start, start + BATCH_MAX);\n const envelope = await this.#transport.post(path, { [field]: chunk, ...extra });\n if (!Array.isArray(envelope.results)) {\n throw new UnexpectedResponseError(200, \"Missing results\", envelope);\n }\n const part = BatchResult.fromEnvelope(envelope, kind);\n items.push(...part.items);\n credits = part.creditsRemaining;\n const rest = cleaned.slice(start + BATCH_MAX);\n if (rest.length > 0 && coerceInt(envelope.credits_remaining) === 0) {\n // The API answers 402 to a request sent with a zero balance, which would discard\n // the results above; mark the unsent items the way the API does.\n for (const item of rest) {\n items.push(new BatchItem(item ?? \"\", false, null, \"insufficient_credits\"));\n }\n break;\n }\n }\n return new BatchResult(items, credits);\n }\n\n toString(): string {\n return `FraudNova { apiKey: \"${maskApiKey(this.#apiKey)}\", baseUrl: \"${this.baseUrl}\" }`;\n }\n\n [inspectSymbol](): string {\n return this.toString();\n }\n}\n"],"mappings":"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","names":["data","subject","isPlainObject"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/version.ts","../src/errors.ts","../src/results.ts","../src/retry.ts","../src/transport.ts","../src/client.ts"],"sourcesContent":["export { VERSION } from \"./version.js\";\nexport {\n BATCH_MAX,\n DEFAULT_BASE_URL,\n FraudNova,\n type FraudNovaOptions,\n type IpCheckOptions,\n type PhoneCheckOptions,\n} from \"./client.js\";\nexport type { FetchLike } from \"./transport.js\";\nexport {\n AccountSuspendedError,\n ApiError,\n AuthenticationError,\n FraudNovaError,\n InsufficientCreditsError,\n InvalidArgumentError,\n RateLimitError,\n TransportError,\n UnexpectedResponseError,\n UpstreamError,\n ValidationError,\n maskApiKey,\n} from \"./errors.js\";\nexport { RetryPolicy, parseRetryAfter, type RetryPolicyOptions } from \"./retry.js\";\nexport {\n BatchItem,\n BatchResult,\n EmailResult,\n IpResult,\n PhoneResult,\n parseDateInfo,\n type CheckKind,\n type DateInfo,\n type FlagOptions,\n type Result,\n} from \"./results.js\";\n","export const VERSION = \"1.0.2\";\n","/** Return a safe, shortened form of an API key for display. */\nexport function maskApiKey(apiKey: string): string {\n return apiKey.length > 12 ? `fn_…${apiKey.slice(-4)}` : \"fn_…\";\n}\n\n/** Base class for every error thrown by this SDK. */\nexport class FraudNovaError extends Error {\n constructor(message?: string, options?: ErrorOptions) {\n super(message, options);\n this.name = \"FraudNovaError\";\n }\n}\n\n/** A client-side input problem; no request was sent. */\nexport class InvalidArgumentError extends FraudNovaError {\n constructor(message?: string) {\n super(message);\n this.name = \"InvalidArgumentError\";\n }\n}\n\n/** The HTTP request itself failed (timeout, DNS, TLS). */\nexport class TransportError extends FraudNovaError {\n constructor(message: string, options?: { cause?: unknown }) {\n super(message, options);\n this.name = \"TransportError\";\n }\n}\n\n/** The API answered with an error status. */\nexport class ApiError extends FraudNovaError {\n readonly statusCode: number;\n readonly apiMessage: string;\n readonly body: Record<string, unknown>;\n\n constructor(\n statusCode: number,\n apiMessage: string,\n body: Record<string, unknown> = {},\n message?: string,\n ) {\n super(message ?? `FraudNova API error (HTTP ${statusCode}): ${apiMessage}`);\n this.name = \"ApiError\";\n this.statusCode = statusCode;\n this.apiMessage = apiMessage;\n this.body = body;\n }\n\n static fromResponse(\n statusCode: number,\n text: string,\n retryAfter: number | null = null,\n ): ApiError {\n let decoded: unknown = null;\n try {\n decoded = JSON.parse(text);\n } catch {\n decoded = null;\n }\n const isJson = typeof decoded === \"object\" && decoded !== null && !Array.isArray(decoded);\n const body: Record<string, unknown> = isJson ? (decoded as Record<string, unknown>) : {};\n const envelope = isJson && (\"success\" in body || \"message\" in body);\n\n let apiMessage = \"\";\n for (const key of [\"message\", \"error\"]) {\n const value = body[key];\n if (typeof value === \"string\" && value !== \"\") {\n apiMessage = value;\n break;\n }\n }\n if (apiMessage === \"\") {\n const stripped = text.trim();\n apiMessage = stripped !== \"\" ? stripped.slice(0, 200) : `HTTP ${statusCode}`;\n }\n\n const full = isJson ? undefined : `FraudNova API error (HTTP ${statusCode}): non-JSON response`;\n\n if (envelope && statusCode === 401)\n return new AuthenticationError(statusCode, apiMessage, body, full);\n if (envelope && statusCode === 402) {\n return new InsufficientCreditsError(statusCode, apiMessage, body, full);\n }\n if (envelope && statusCode === 403)\n return new AccountSuspendedError(statusCode, apiMessage, body, full);\n if (envelope && statusCode === 422)\n return new ValidationError(statusCode, apiMessage, body, full);\n if (statusCode === 429) {\n return new RateLimitError(statusCode, apiMessage, body, retryAfter, full);\n }\n if (statusCode === 502 || statusCode === 503 || statusCode === 504) {\n return new UpstreamError(statusCode, apiMessage, body, statusCode === 503 && envelope, full);\n }\n return new ApiError(statusCode, apiMessage, body, full);\n }\n}\n\n/** 401: missing or invalid API key. */\nexport class AuthenticationError extends ApiError {\n constructor(\n statusCode: number,\n apiMessage: string,\n body: Record<string, unknown> = {},\n message?: string,\n ) {\n super(statusCode, apiMessage, body, message);\n this.name = \"AuthenticationError\";\n }\n}\n\n/** 402: the account has no credits left. */\nexport class InsufficientCreditsError extends ApiError {\n constructor(\n statusCode: number,\n apiMessage: string,\n body: Record<string, unknown> = {},\n message?: string,\n ) {\n super(statusCode, apiMessage, body, message);\n this.name = \"InsufficientCreditsError\";\n }\n}\n\n/** 403: the account is suspended. */\nexport class AccountSuspendedError extends ApiError {\n constructor(\n statusCode: number,\n apiMessage: string,\n body: Record<string, unknown> = {},\n message?: string,\n ) {\n super(statusCode, apiMessage, body, message);\n this.name = \"AccountSuspendedError\";\n }\n}\n\n/** 422: the request failed validation. */\nexport class ValidationError extends ApiError {\n constructor(\n statusCode: number,\n apiMessage: string,\n body: Record<string, unknown> = {},\n message?: string,\n ) {\n super(statusCode, apiMessage, body, message);\n this.name = \"ValidationError\";\n }\n}\n\n/** 429: rate or concurrency limit hit. */\nexport class RateLimitError extends ApiError {\n readonly retryAfter: number | null;\n\n constructor(\n statusCode: number,\n apiMessage: string,\n body: Record<string, unknown> = {},\n retryAfter: number | null = null,\n message?: string,\n ) {\n super(statusCode, apiMessage, body, message);\n this.name = \"RateLimitError\";\n this.retryAfter = retryAfter;\n }\n\n get isConcurrencyLimit(): boolean {\n return this.apiMessage.includes(\"Too many concurrent requests\");\n }\n}\n\n/** 502/503/504: the lookup could not be completed. */\nexport class UpstreamError extends ApiError {\n readonly creditRefunded: boolean;\n\n constructor(\n statusCode: number,\n apiMessage: string,\n body: Record<string, unknown> = {},\n creditRefunded = false,\n message?: string,\n ) {\n let text = message ?? `FraudNova API error (HTTP ${statusCode}): ${apiMessage}`;\n if (creditRefunded) text += \" The credit for this check was refunded.\";\n super(statusCode, apiMessage, body, text);\n this.name = \"UpstreamError\";\n this.creditRefunded = creditRefunded;\n }\n}\n\n/** A 2xx response whose body is not the success envelope. */\nexport class UnexpectedResponseError extends ApiError {\n constructor(\n statusCode: number,\n apiMessage: string,\n body: Record<string, unknown> = {},\n message?: string,\n ) {\n super(statusCode, apiMessage, body, message);\n this.name = \"UnexpectedResponseError\";\n }\n}\n","/** Typed, leniently parsed results for IP, email and phone checks. */\n\ntype Raw = Record<string, unknown>;\n\nexport function coerceBool(v: unknown): boolean | null {\n return typeof v === \"boolean\" ? v : null;\n}\n\nexport function coerceString(v: unknown): string | null {\n return typeof v === \"string\" ? v : null;\n}\n\nfunction roundHalfAway(n: number): number {\n const r = Math.sign(n) * Math.floor(Math.abs(n) + 0.5);\n return r === 0 ? 0 : r;\n}\n\nexport function coerceInt(v: unknown): number | null {\n let n: number;\n if (typeof v === \"number\") {\n n = v;\n } else if (typeof v === \"string\" && (/^-?\\d+$/.test(v) || /^-?\\d+\\.\\d+$/.test(v))) {\n n = Number(v);\n } else {\n return null;\n }\n return Number.isFinite(n) ? roundHalfAway(n) : null;\n}\n\nfunction isPlainObject(v: unknown): v is Raw {\n return typeof v === \"object\" && v !== null && !Array.isArray(v);\n}\n\n/** Treat anything that is not a plain object as `{}`, so a malformed `data` yields defaults. */\nfunction asRaw(data: unknown): Raw {\n return isPlainObject(data) ? data : {};\n}\n\nexport interface DateInfo {\n human: string | null;\n timestamp: number | null;\n iso: string | null;\n}\n\nexport function parseDateInfo(value: unknown): DateInfo | null {\n if (typeof value === \"string\") {\n return { human: value, timestamp: null, iso: null };\n }\n if (isPlainObject(value)) {\n return {\n human: coerceString(value.human),\n timestamp: coerceInt(value.timestamp),\n iso: coerceString(value.iso),\n };\n }\n return null;\n}\n\nexport interface FlagOptions {\n riskThreshold?: number;\n flagOn?: readonly string[];\n}\n\nexport type CheckKind = \"ip\" | \"email\" | \"phone\";\n\nabstract class FlaggableResult {\n readonly creditsRemaining: number | null;\n readonly raw: Raw;\n\n protected constructor(raw: Raw, creditsRemaining: number | null) {\n this.raw = raw;\n this.creditsRemaining = creditsRemaining;\n }\n\n protected abstract defaultFlagOn(): readonly string[];\n\n toJSON(): Raw {\n return this.raw;\n }\n\n flagReasons(options: FlagOptions = {}): string[] {\n const threshold = options.riskThreshold ?? 75;\n const reasons: string[] = [];\n if ((coerceInt(this.raw.fraud_score) ?? 0) >= threshold) {\n reasons.push(\"fraud_score\");\n }\n for (const name of options.flagOn ?? this.defaultFlagOn()) {\n if (this.raw[name] === true) {\n reasons.push(name);\n }\n }\n return reasons;\n }\n\n flagged(options: FlagOptions = {}): boolean {\n return this.flagReasons(options).length > 0;\n }\n}\n\nexport class IpResult extends FlaggableResult {\n static readonly DEFAULT_FLAG_ON: readonly string[] = [\"proxy\", \"vpn\", \"tor\"];\n\n readonly fraudScore: number;\n readonly proxy: boolean | null;\n readonly vpn: boolean | null;\n readonly tor: boolean | null;\n readonly activeVpn: boolean | null;\n readonly activeTor: boolean | null;\n readonly recentAbuse: boolean | null;\n readonly botStatus: boolean | null;\n readonly countryCode: string | null;\n readonly region: string | null;\n readonly city: string | null;\n readonly isp: string | null;\n readonly organization: string | null;\n readonly asn: number | null;\n readonly operatingSystem: string | null;\n readonly browser: string | null;\n readonly deviceBrand: string | null;\n readonly deviceModel: string | null;\n\n private constructor(raw: Raw, creditsRemaining: number | null) {\n super(raw, creditsRemaining);\n this.fraudScore = coerceInt(raw.fraud_score) ?? 0;\n this.proxy = coerceBool(raw.proxy);\n this.vpn = coerceBool(raw.vpn);\n this.tor = coerceBool(raw.tor);\n this.activeVpn = coerceBool(raw.active_vpn);\n this.activeTor = coerceBool(raw.active_tor);\n this.recentAbuse = coerceBool(raw.recent_abuse);\n this.botStatus = coerceBool(raw.bot_status);\n this.countryCode = coerceString(raw.country_code);\n this.region = coerceString(raw.region);\n this.city = coerceString(raw.city);\n this.isp = coerceString(raw.ISP);\n this.organization = coerceString(raw.organization);\n this.asn = coerceInt(raw.ASN);\n this.operatingSystem = coerceString(raw.operating_system);\n this.browser = coerceString(raw.browser);\n this.deviceBrand = coerceString(raw.device_brand);\n this.deviceModel = coerceString(raw.device_model);\n }\n\n static fromData(data: Raw, creditsRemaining: number | null = null): IpResult {\n return new IpResult(asRaw(data), creditsRemaining);\n }\n\n protected defaultFlagOn(): readonly string[] {\n return IpResult.DEFAULT_FLAG_ON;\n }\n}\n\nexport class EmailResult extends FlaggableResult {\n static readonly DEFAULT_FLAG_ON: readonly string[] = [\"disposable\"];\n\n readonly fraudScore: number;\n readonly valid: boolean | null;\n readonly disposable: boolean | null;\n readonly catchAll: boolean | null;\n readonly generic: boolean | null;\n readonly common: boolean | null;\n readonly dnsValid: boolean | null;\n readonly honeypot: boolean | null;\n readonly frequentComplainer: boolean | null;\n readonly suspect: boolean | null;\n readonly recentAbuse: boolean | null;\n readonly leaked: boolean | null;\n readonly riskyTld: boolean | null;\n readonly spfRecord: boolean | null;\n readonly dmarcRecord: boolean | null;\n readonly smtpScore: number | null;\n readonly overallScore: number | null;\n readonly deliverability: string | null;\n readonly spamTrapScore: string | null;\n readonly resultType: string | null;\n readonly sanitizedEmail: string | null;\n readonly firstSeen: DateInfo | null;\n readonly domainAge: DateInfo | null;\n\n private constructor(raw: Raw, creditsRemaining: number | null) {\n super(raw, creditsRemaining);\n this.fraudScore = coerceInt(raw.fraud_score) ?? 0;\n this.valid = coerceBool(raw.valid);\n this.disposable = coerceBool(raw.disposable);\n this.catchAll = coerceBool(raw.catch_all);\n this.generic = coerceBool(raw.generic);\n this.common = coerceBool(raw.common);\n this.dnsValid = coerceBool(raw.dns_valid);\n this.honeypot = coerceBool(raw.honeypot);\n this.frequentComplainer = coerceBool(raw.frequent_complainer);\n this.suspect = coerceBool(raw.suspect);\n this.recentAbuse = coerceBool(raw.recent_abuse);\n this.leaked = coerceBool(raw.leaked);\n this.riskyTld = coerceBool(raw.risky_tld);\n this.spfRecord = coerceBool(raw.spf_record);\n this.dmarcRecord = coerceBool(raw.dmarc_record);\n this.smtpScore = coerceInt(raw.smtp_score);\n this.overallScore = coerceInt(raw.overall_score);\n this.deliverability = coerceString(raw.deliverability);\n this.spamTrapScore = coerceString(raw.spam_trap_score);\n this.resultType = coerceString(raw.result_type);\n this.sanitizedEmail = coerceString(raw.sanitized_email);\n this.firstSeen = parseDateInfo(raw.first_seen);\n this.domainAge = parseDateInfo(raw.domain_age);\n }\n\n static fromData(data: Raw, creditsRemaining: number | null = null): EmailResult {\n return new EmailResult(asRaw(data), creditsRemaining);\n }\n\n protected defaultFlagOn(): readonly string[] {\n return EmailResult.DEFAULT_FLAG_ON;\n }\n}\n\nexport class PhoneResult extends FlaggableResult {\n static readonly DEFAULT_FLAG_ON: readonly string[] = [\"VOIP\", \"risky\"];\n\n readonly fraudScore: number;\n readonly valid: boolean | null;\n readonly recentAbuse: boolean | null;\n readonly voip: boolean | null;\n readonly prepaid: boolean | null;\n readonly risky: boolean | null;\n readonly active: boolean | null;\n readonly doNotCall: boolean | null;\n readonly leaked: boolean | null;\n readonly spammer: boolean | null;\n readonly dialingCode: number | null;\n readonly carrier: string | null;\n readonly lineType: string | null;\n readonly country: string | null;\n readonly region: string | null;\n readonly city: string | null;\n readonly timezone: string | null;\n readonly zipCode: string | null;\n readonly activeStatus: string | null;\n readonly formatted: string | null;\n readonly localFormat: string | null;\n\n private constructor(raw: Raw, creditsRemaining: number | null) {\n super(raw, creditsRemaining);\n this.fraudScore = coerceInt(raw.fraud_score) ?? 0;\n this.valid = coerceBool(raw.valid);\n this.recentAbuse = coerceBool(raw.recent_abuse);\n this.voip = coerceBool(raw.VOIP);\n this.prepaid = coerceBool(raw.prepaid);\n this.risky = coerceBool(raw.risky);\n this.active = coerceBool(raw.active);\n this.doNotCall = coerceBool(raw.do_not_call);\n this.leaked = coerceBool(raw.leaked);\n this.spammer = coerceBool(raw.spammer);\n this.dialingCode = coerceInt(raw.dialing_code);\n this.carrier = coerceString(raw.carrier);\n this.lineType = coerceString(raw.line_type);\n this.country = coerceString(raw.country);\n this.region = coerceString(raw.region);\n this.city = coerceString(raw.city);\n this.timezone = coerceString(raw.timezone);\n this.zipCode = coerceString(raw.zip_code);\n this.activeStatus = coerceString(raw.active_status);\n this.formatted = coerceString(raw.formatted);\n this.localFormat = coerceString(raw.local_format);\n }\n\n static fromData(data: Raw, creditsRemaining: number | null = null): PhoneResult {\n return new PhoneResult(asRaw(data), creditsRemaining);\n }\n\n protected defaultFlagOn(): readonly string[] {\n return PhoneResult.DEFAULT_FLAG_ON;\n }\n}\n\nexport type Result = IpResult | EmailResult | PhoneResult;\n\nexport class BatchItem {\n constructor(\n readonly subject: string,\n readonly success: boolean,\n readonly result: Result | null,\n readonly error: string | null,\n ) {}\n\n isInvalid(): boolean {\n return (\n this.error === \"invalid_ip\" ||\n this.error === \"invalid_email\" ||\n this.error === \"invalid_phone\"\n );\n }\n\n isInsufficientCredits(): boolean {\n return this.error === \"insufficient_credits\";\n }\n\n isUpstreamFailed(): boolean {\n return this.error === \"upstream_failed\";\n }\n}\n\nexport class BatchResult {\n constructor(\n readonly items: BatchItem[],\n readonly creditsRemaining: number,\n ) {}\n\n static fromEnvelope(envelope: Raw, kind: CheckKind): BatchResult {\n const items: BatchItem[] = [];\n const rawItems = envelope.results;\n if (Array.isArray(rawItems)) {\n for (const item of rawItems) {\n if (!isPlainObject(item)) {\n continue;\n }\n const success = coerceBool(item.success) ?? false;\n const data = item.data;\n let result: Result | null = null;\n if (success && isPlainObject(data)) {\n result =\n kind === \"ip\"\n ? IpResult.fromData(data)\n : kind === \"email\"\n ? EmailResult.fromData(data)\n : PhoneResult.fromData(data);\n }\n items.push(\n new BatchItem(coerceString(item[kind]) ?? \"\", success, result, coerceString(item.error)),\n );\n }\n }\n return new BatchResult(items, coerceInt(envelope.credits_remaining) ?? 0);\n }\n\n successful(): BatchItem[] {\n return this.items.filter((i) => i.success);\n }\n\n failed(): BatchItem[] {\n return this.items.filter((i) => !i.success);\n }\n\n hasFailures(): boolean {\n return this.items.some((i) => !i.success);\n }\n\n get length(): number {\n return this.items.length;\n }\n\n *[Symbol.iterator](): Iterator<BatchItem> {\n yield* this.items;\n }\n}\n","import { InvalidArgumentError, type RateLimitError } from \"./errors.js\";\n\nconst MONTH = \"(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\";\nconst SHORT_DAY = \"(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun)\";\nconst LONG_DAY = \"(?:Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday)\";\nconst TIME = \"[0-9]{2}:[0-9]{2}:[0-9]{2}\";\n\n// RFC 7231 HTTP-date: IMF-fixdate, then the obsolete RFC 850 and asctime forms.\nconst IMF_FIXDATE = new RegExp(`^${SHORT_DAY}, [0-9]{2} ${MONTH} [0-9]{4} ${TIME} GMT$`, \"i\");\nconst RFC_850 = new RegExp(`^${LONG_DAY}, [0-9]{2}-${MONTH}-[0-9]{2} ${TIME} GMT$`, \"i\");\nconst ASCTIME = new RegExp(`^${SHORT_DAY} ${MONTH} (?: [0-9]|[0-9]{2}) ${TIME} [0-9]{4}$`, \"i\");\n\n/**\n * Parse a Retry-After header into whole seconds.\n *\n * Accepts delta-seconds (digits only) or an RFC 7231 HTTP-date in one of its three\n * forms: IMF-fixdate (`Sun, 06 Nov 1994 08:49:37 GMT`), RFC 850\n * (`Sunday, 06-Nov-94 08:49:37 GMT`) or asctime (`Sun Nov 6 08:49:37 1994`, read as GMT).\n * Anything else, or a day or time that `Date.parse` rejects (such as day 99), returns null.\n */\nexport function parseRetryAfter(value: string | null, now: Date = new Date()): number | null {\n if (value === null) return null;\n const text = value.trim();\n if (text === \"\") return null;\n if (/^[0-9]+$/.test(text)) return Number.parseInt(text, 10);\n let when: number;\n if (IMF_FIXDATE.test(text) || RFC_850.test(text)) when = Date.parse(text);\n else if (ASCTIME.test(text)) when = Date.parse(`${text} GMT`);\n else return null;\n if (Number.isNaN(when)) return null;\n return Math.max(0, Math.ceil((when - now.getTime()) / 1000));\n}\n\nexport interface RetryPolicyOptions {\n maxAttempts?: number;\n concurrencyDelays?: readonly number[];\n rateLimitDelays?: readonly number[];\n honourRetryAfter?: boolean;\n maxRetryAfter?: number;\n sleep?: (seconds: number) => Promise<void>;\n}\n\nconst defaultSleep = (seconds: number): Promise<void> =>\n new Promise((resolve) => setTimeout(resolve, seconds * 1000));\n\n/** Opt-in retry policy for rate-limited (429) requests. All delays are in seconds. */\nexport class RetryPolicy {\n readonly maxAttempts: number;\n readonly concurrencyDelays: readonly number[];\n readonly rateLimitDelays: readonly number[];\n readonly honourRetryAfter: boolean;\n readonly maxRetryAfter: number;\n readonly #sleeper: (seconds: number) => Promise<void>;\n\n constructor(options: RetryPolicyOptions = {}) {\n this.maxAttempts = options.maxAttempts ?? 3;\n this.concurrencyDelays = [...(options.concurrencyDelays ?? [1, 2, 4])];\n this.rateLimitDelays = [...(options.rateLimitDelays ?? [5, 15, 40])];\n this.honourRetryAfter = options.honourRetryAfter ?? true;\n this.maxRetryAfter = options.maxRetryAfter ?? 60;\n this.#sleeper = options.sleep ?? defaultSleep;\n if (!Number.isInteger(this.maxAttempts) || this.maxAttempts < 1) {\n throw new InvalidArgumentError(\"maxAttempts must be an integer of at least 1.\");\n }\n if (!Number.isFinite(this.maxRetryAfter) || this.maxRetryAfter < 0) {\n throw new InvalidArgumentError(\"maxRetryAfter must be a finite number that is not negative.\");\n }\n for (const [name, delays] of [\n [\"concurrencyDelays\", this.concurrencyDelays],\n [\"rateLimitDelays\", this.rateLimitDelays],\n ] as const) {\n if (\n !delays.every((delay) => typeof delay === \"number\" && Number.isFinite(delay) && delay >= 0)\n ) {\n throw new InvalidArgumentError(\n `${name} must contain only finite numbers that are not negative.`,\n );\n }\n }\n }\n\n /** Seconds to wait before retrying after `attempt` (1-based) failed, or null to stop. */\n delayFor(error: RateLimitError, attempt: number): number | null {\n if (attempt >= this.maxAttempts) return null;\n if (this.honourRetryAfter && error.retryAfter !== null) {\n return error.retryAfter > this.maxRetryAfter ? null : error.retryAfter;\n }\n const schedule = error.isConcurrencyLimit ? this.concurrencyDelays : this.rateLimitDelays;\n const delay = schedule[attempt - 1];\n return delay === undefined ? null : delay;\n }\n\n sleep(seconds: number): Promise<void> {\n return this.#sleeper(seconds);\n }\n}\n","import {\n ApiError,\n InvalidArgumentError,\n RateLimitError,\n TransportError,\n UnexpectedResponseError,\n maskApiKey,\n} from \"./errors.js\";\nimport { type RetryPolicy, parseRetryAfter } from \"./retry.js\";\nimport { VERSION } from \"./version.js\";\n\n/** A `fetch`-compatible function. */\nexport type FetchLike = (url: string, init: RequestInit) => Promise<Response>;\n\nexport interface TransportOptions {\n apiKey: string;\n baseUrl: string;\n timeout: number;\n retry: RetryPolicy | null;\n fetch: FetchLike | null;\n}\n\ntype QueryParams = Record<string, string | number | boolean | null | undefined>;\n\nfunction userAgent(): string {\n let agent = `fraudnova-node/${VERSION}`;\n if (typeof process !== \"undefined\" && typeof process.versions?.node === \"string\") {\n agent += ` node/${process.versions.node}`;\n }\n return agent;\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n/** Internal HTTP layer: builds requests, decodes the envelope, maps errors, retries 429s. */\nexport class Transport {\n readonly baseUrl: string;\n readonly #apiKey: string;\n readonly #timeout: number;\n readonly #retry: RetryPolicy | null;\n readonly #fetch: FetchLike;\n readonly #userAgent: string;\n\n constructor(options: TransportOptions) {\n if (!/^[\\x21-\\x7e]+$/.test(options.apiKey)) {\n throw new InvalidArgumentError(\n \"apiKey must contain only printable ASCII characters, without whitespace.\",\n );\n }\n let fetchImpl: FetchLike | null = options.fetch ?? null;\n if (fetchImpl === null) {\n const globalFetch = (globalThis as { fetch?: typeof globalThis.fetch }).fetch;\n if (typeof globalFetch === \"function\") {\n fetchImpl = globalFetch.bind(globalThis);\n }\n }\n if (fetchImpl === null) {\n throw new InvalidArgumentError(\n \"No fetch implementation available; pass one in the fetch option\",\n );\n }\n this.#apiKey = options.apiKey;\n this.baseUrl = options.baseUrl.replace(/\\/+$/, \"\");\n this.#timeout = options.timeout;\n this.#retry = options.retry;\n this.#fetch = fetchImpl;\n this.#userAgent = userAgent();\n }\n\n get(\n path: string,\n params: QueryParams | null = null,\n authenticated = true,\n ): Promise<Record<string, unknown>> {\n const query = new URLSearchParams();\n for (const [name, value] of Object.entries(params ?? {})) {\n if (value === undefined || value === null) continue;\n query.append(name, String(value));\n }\n const search = query.toString();\n const url = this.baseUrl + path + (search !== \"\" ? `?${search}` : \"\");\n return this.#send(\"GET\", url, authenticated);\n }\n\n post(path: string, json: Record<string, unknown>): Promise<Record<string, unknown>> {\n return this.#send(\"POST\", this.baseUrl + path, true, JSON.stringify(json));\n }\n\n #headers(authenticated: boolean, hasBody: boolean): Record<string, string> {\n const headers: Record<string, string> = {\n Accept: \"application/json\",\n \"User-Agent\": this.#userAgent,\n };\n if (authenticated) headers.Authorization = `Bearer ${this.#apiKey}`;\n if (hasBody) headers[\"Content-Type\"] = \"application/json\";\n return headers;\n }\n\n async #send(\n method: string,\n url: string,\n authenticated: boolean,\n body?: string,\n ): Promise<Record<string, unknown>> {\n let attempt = 1;\n for (;;) {\n const init: RequestInit = {\n method,\n headers: this.#headers(authenticated, body !== undefined),\n };\n if (body !== undefined) init.body = body;\n\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.#timeout);\n let response: Response;\n let text: string;\n try {\n response = await this.#fetch(url, { ...init, signal: controller.signal });\n text = await response.text();\n } catch (error) {\n if (controller.signal.aborted) {\n throw new TransportError(`FraudNova request timed out after ${this.#timeout} ms`, {\n cause: error,\n });\n }\n const original = error instanceof Error ? error.message : String(error);\n const masked = original.split(this.#apiKey).join(maskApiKey(this.#apiKey));\n throw new TransportError(`FraudNova request failed: ${masked}`, { cause: error });\n } finally {\n clearTimeout(timer);\n }\n\n try {\n return Transport.#decode(response, text);\n } catch (error) {\n if (!(error instanceof RateLimitError)) throw error;\n const delay = this.#retry?.delayFor(error, attempt) ?? null;\n if (this.#retry === null || delay === null) throw error;\n await this.#retry.sleep(delay);\n attempt++;\n }\n }\n }\n\n static #decode(response: Response, text: string): Record<string, unknown> {\n const status = response.status;\n if (status < 200 || status >= 300) {\n throw ApiError.fromResponse(\n status,\n text,\n parseRetryAfter(response.headers.get(\"retry-after\")),\n );\n }\n let decoded: unknown = null;\n try {\n decoded = JSON.parse(text);\n } catch {\n decoded = null;\n }\n if (!isPlainObject(decoded) || decoded.success !== true) {\n throw new UnexpectedResponseError(\n status,\n \"Unexpected response body\",\n isPlainObject(decoded) ? decoded : {},\n );\n }\n return decoded;\n }\n}\n","import { InvalidArgumentError, UnexpectedResponseError, maskApiKey } from \"./errors.js\";\nimport {\n BatchItem,\n BatchResult,\n EmailResult,\n IpResult,\n PhoneResult,\n coerceInt,\n type CheckKind,\n} from \"./results.js\";\nimport type { RetryPolicy } from \"./retry.js\";\nimport { Transport, type FetchLike } from \"./transport.js\";\n\n/** The production API base URL. */\nexport const DEFAULT_BASE_URL = \"https://fraudnova.com/api/v1\";\n\n/** The most subjects the API accepts in one batch request; larger arrays are chunked. */\nexport const BATCH_MAX = 100;\n\nconst DEFAULT_TIMEOUT = 30_000;\n\nconst inspectSymbol = Symbol.for(\"nodejs.util.inspect.custom\");\n\nexport interface FraudNovaOptions {\n /** API base URL; defaults to {@link DEFAULT_BASE_URL}. */\n baseUrl?: string;\n /** Per-request timeout in milliseconds; defaults to 30 000. */\n timeout?: number;\n /** Opt-in retry policy for 429 responses. */\n retry?: RetryPolicy;\n /** A fetch-compatible function; defaults to the global `fetch`. */\n fetch?: FetchLike;\n}\n\nexport interface IpCheckOptions {\n strictness?: 0 | 1 | 2 | 3;\n mobile?: boolean;\n userAgent?: string;\n userLanguage?: string;\n}\n\nexport interface PhoneCheckOptions {\n country?: string;\n}\n\ntype Envelope = Record<string, unknown>;\n\nfunction subject(value: unknown, name: string): string {\n if (typeof value !== \"string\" || value.trim() === \"\") {\n throw new InvalidArgumentError(`${name} must be a non-empty string`);\n }\n return value.trim();\n}\n\nfunction strictness(value: unknown): number | undefined {\n if (value === undefined) return undefined;\n if (typeof value !== \"number\" || !Number.isInteger(value) || value < 0 || value > 3) {\n throw new InvalidArgumentError(\"strictness must be an integer from 0 to 3\");\n }\n return value;\n}\n\nfunction country(value: unknown): string | undefined {\n if (typeof value !== \"string\") return undefined;\n const cleaned = value.trim().toUpperCase();\n return cleaned !== \"\" ? cleaned : undefined;\n}\n\nfunction data(envelope: Envelope): Record<string, unknown> {\n const value = envelope.data;\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n throw new UnexpectedResponseError(200, \"Missing data\", envelope);\n }\n return value as Record<string, unknown>;\n}\n\n/** Build the IP options shared by the single and batch calls, keeping only defined values. */\nfunction ipOptions(options: IpCheckOptions): Record<string, number | boolean | string> {\n const out: Record<string, number | boolean | string> = {};\n const level = strictness(options.strictness);\n if (level !== undefined) out.strictness = level;\n if (options.mobile === true || options.mobile === false) out.mobile = options.mobile;\n if (options.userAgent !== undefined) out.user_agent = options.userAgent;\n if (options.userLanguage !== undefined) out.user_language = options.userLanguage;\n return out;\n}\n\n/** The FraudNova API client. */\nexport class FraudNova {\n readonly baseUrl: string;\n readonly #apiKey: string;\n readonly #transport: Transport;\n\n constructor(apiKey: string, options: FraudNovaOptions = {}) {\n if (typeof apiKey !== \"string\" || apiKey.trim() === \"\") {\n throw new InvalidArgumentError(\"apiKey must be a non-empty string\");\n }\n const baseUrl = options.baseUrl ?? DEFAULT_BASE_URL;\n if (typeof baseUrl !== \"string\" || !/^https?:\\/\\//i.test(baseUrl)) {\n throw new InvalidArgumentError(\"baseUrl must start with http:// or https://\");\n }\n const timeout = options.timeout ?? DEFAULT_TIMEOUT;\n if (\n typeof timeout !== \"number\" ||\n !Number.isFinite(timeout) ||\n timeout <= 0 ||\n timeout > 2_147_483_647\n ) {\n throw new InvalidArgumentError(\n \"timeout must be a positive number of milliseconds (at most 2147483647)\",\n );\n }\n this.#apiKey = apiKey.trim();\n this.#transport = new Transport({\n apiKey: this.#apiKey,\n baseUrl,\n timeout,\n retry: options.retry ?? null,\n fetch: options.fetch ?? null,\n });\n this.baseUrl = this.#transport.baseUrl;\n }\n\n /** Score one IP address. */\n async checkIp(ip: string, options: IpCheckOptions = {}): Promise<IpResult> {\n const params = { ip: subject(ip, \"ip\"), ...ipOptions(options) };\n const envelope = await this.#transport.get(\"/check\", params);\n return IpResult.fromData(data(envelope), coerceInt(envelope.credits_remaining));\n }\n\n /** Score one email address. */\n async checkEmail(email: string): Promise<EmailResult> {\n const params = { email: subject(email, \"email\") };\n const envelope = await this.#transport.get(\"/check/email\", params);\n return EmailResult.fromData(data(envelope), coerceInt(envelope.credits_remaining));\n }\n\n /** Score one phone number. */\n async checkPhone(phone: string, options: PhoneCheckOptions = {}): Promise<PhoneResult> {\n const params = { phone: subject(phone, \"phone\"), country: country(options.country) };\n const envelope = await this.#transport.get(\"/check/phone\", params);\n return PhoneResult.fromData(data(envelope), coerceInt(envelope.credits_remaining));\n }\n\n /** Score many IP addresses, chunked by {@link BATCH_MAX}. */\n async checkIps(\n ips: ReadonlyArray<string | null>,\n options: IpCheckOptions = {},\n ): Promise<BatchResult> {\n return this.#batch(\"/check/batch\", \"ips\", \"ip\", ips, ipOptions(options));\n }\n\n /** Score many email addresses, chunked by {@link BATCH_MAX}. */\n async checkEmails(emails: ReadonlyArray<string | null>): Promise<BatchResult> {\n return this.#batch(\"/check/email/batch\", \"emails\", \"email\", emails, {});\n }\n\n /** Score many phone numbers, chunked by {@link BATCH_MAX}. */\n async checkPhones(\n phones: ReadonlyArray<string | null>,\n options: PhoneCheckOptions = {},\n ): Promise<BatchResult> {\n const code = country(options.country);\n const extra: Record<string, string> = code !== undefined ? { country: code } : {};\n return this.#batch(\"/check/phone/batch\", \"phones\", \"phone\", phones, extra);\n }\n\n /** The account's remaining credits. */\n async balance(): Promise<number> {\n const envelope = await this.#transport.get(\"/balance\");\n const credits = coerceInt(envelope.credits_remaining);\n if (credits === null) {\n throw new UnexpectedResponseError(200, \"Missing credits_remaining\", envelope);\n }\n return credits;\n }\n\n /** The caller's public IP address as the API sees it (free, unauthenticated). */\n async myIp(): Promise<string> {\n const envelope = await this.#transport.get(\"/my-ip\", null, false);\n const ip = envelope.ip;\n if (typeof ip !== \"string\" || ip === \"\") {\n throw new UnexpectedResponseError(200, \"Missing ip\", envelope);\n }\n return ip;\n }\n\n async #batch(\n path: string,\n field: string,\n kind: CheckKind,\n subjects: ReadonlyArray<string | null>,\n extra: Record<string, unknown>,\n ): Promise<BatchResult> {\n if (!Array.isArray(subjects)) {\n throw new InvalidArgumentError(`${field} must be an array of values`);\n }\n if (subjects.length === 0) {\n throw new InvalidArgumentError(`${field} must not be empty`);\n }\n const cleaned: Array<string | null> = subjects.map((item: unknown, index) => {\n if (item !== null && typeof item !== \"string\") {\n throw new InvalidArgumentError(`${field}[${index}] must be a string or null`);\n }\n return item === null ? null : item.trim();\n });\n\n const items: BatchItem[] = [];\n let credits = 0;\n for (let start = 0; start < cleaned.length; start += BATCH_MAX) {\n const chunk = cleaned.slice(start, start + BATCH_MAX);\n const envelope = await this.#transport.post(path, { [field]: chunk, ...extra });\n if (!Array.isArray(envelope.results)) {\n throw new UnexpectedResponseError(200, \"Missing results\", envelope);\n }\n const part = BatchResult.fromEnvelope(envelope, kind);\n items.push(...part.items);\n credits = part.creditsRemaining;\n const rest = cleaned.slice(start + BATCH_MAX);\n if (rest.length > 0 && coerceInt(envelope.credits_remaining) === 0) {\n // The API answers 402 to a request sent with a zero balance, which would discard\n // the results above; mark the unsent items the way the API does.\n for (const item of rest) {\n items.push(new BatchItem(item ?? \"\", false, null, \"insufficient_credits\"));\n }\n break;\n }\n }\n return new BatchResult(items, credits);\n }\n\n toString(): string {\n return `FraudNova { apiKey: \"${maskApiKey(this.#apiKey)}\", baseUrl: \"${this.baseUrl}\" }`;\n }\n\n [inspectSymbol](): string {\n return this.toString();\n }\n}\n"],"mappings":"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","names":["data","subject","isPlainObject"]}
package/dist/index.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- declare const VERSION = "1.0.0";
1
+ declare const VERSION = "1.0.2";
2
2
 
3
3
  /** Typed, leniently parsed results for IP, email and phone checks. */
4
4
  type Raw = Record<string, unknown>;
@@ -181,7 +181,14 @@ declare class UnexpectedResponseError extends ApiError {
181
181
  constructor(statusCode: number, apiMessage: string, body?: Record<string, unknown>, message?: string);
182
182
  }
183
183
 
184
- /** Parse a Retry-After header (delta-seconds or HTTP-date) into whole seconds. */
184
+ /**
185
+ * Parse a Retry-After header into whole seconds.
186
+ *
187
+ * Accepts delta-seconds (digits only) or an RFC 7231 HTTP-date in one of its three
188
+ * forms: IMF-fixdate (`Sun, 06 Nov 1994 08:49:37 GMT`), RFC 850
189
+ * (`Sunday, 06-Nov-94 08:49:37 GMT`) or asctime (`Sun Nov 6 08:49:37 1994`, read as GMT).
190
+ * Anything else, or a day or time that `Date.parse` rejects (such as day 99), returns null.
191
+ */
185
192
  declare function parseRetryAfter(value: string | null, now?: Date): number | null;
186
193
  interface RetryPolicyOptions {
187
194
  maxAttempts?: number;
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- declare const VERSION = "1.0.0";
1
+ declare const VERSION = "1.0.2";
2
2
 
3
3
  /** Typed, leniently parsed results for IP, email and phone checks. */
4
4
  type Raw = Record<string, unknown>;
@@ -181,7 +181,14 @@ declare class UnexpectedResponseError extends ApiError {
181
181
  constructor(statusCode: number, apiMessage: string, body?: Record<string, unknown>, message?: string);
182
182
  }
183
183
 
184
- /** Parse a Retry-After header (delta-seconds or HTTP-date) into whole seconds. */
184
+ /**
185
+ * Parse a Retry-After header into whole seconds.
186
+ *
187
+ * Accepts delta-seconds (digits only) or an RFC 7231 HTTP-date in one of its three
188
+ * forms: IMF-fixdate (`Sun, 06 Nov 1994 08:49:37 GMT`), RFC 850
189
+ * (`Sunday, 06-Nov-94 08:49:37 GMT`) or asctime (`Sun Nov 6 08:49:37 1994`, read as GMT).
190
+ * Anything else, or a day or time that `Date.parse` rejects (such as day 99), returns null.
191
+ */
185
192
  declare function parseRetryAfter(value: string | null, now?: Date): number | null;
186
193
  interface RetryPolicyOptions {
187
194
  maxAttempts?: number;
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // src/version.ts
2
- var VERSION = "1.0.0";
2
+ var VERSION = "1.0.2";
3
3
 
4
4
  // src/errors.ts
5
5
  function maskApiKey(apiKey) {
@@ -152,6 +152,9 @@ function coerceInt(v) {
152
152
  function isPlainObject(v) {
153
153
  return typeof v === "object" && v !== null && !Array.isArray(v);
154
154
  }
155
+ function asRaw(data2) {
156
+ return isPlainObject(data2) ? data2 : {};
157
+ }
155
158
  function parseDateInfo(value) {
156
159
  if (typeof value === "string") {
157
160
  return { human: value, timestamp: null, iso: null };
@@ -234,7 +237,7 @@ var IpResult = class _IpResult extends FlaggableResult {
234
237
  this.deviceModel = coerceString(raw.device_model);
235
238
  }
236
239
  static fromData(data2, creditsRemaining = null) {
237
- return new _IpResult(data2, creditsRemaining);
240
+ return new _IpResult(asRaw(data2), creditsRemaining);
238
241
  }
239
242
  defaultFlagOn() {
240
243
  return _IpResult.DEFAULT_FLAG_ON;
@@ -292,7 +295,7 @@ var EmailResult = class _EmailResult extends FlaggableResult {
292
295
  this.domainAge = parseDateInfo(raw.domain_age);
293
296
  }
294
297
  static fromData(data2, creditsRemaining = null) {
295
- return new _EmailResult(data2, creditsRemaining);
298
+ return new _EmailResult(asRaw(data2), creditsRemaining);
296
299
  }
297
300
  defaultFlagOn() {
298
301
  return _EmailResult.DEFAULT_FLAG_ON;
@@ -346,7 +349,7 @@ var PhoneResult = class _PhoneResult extends FlaggableResult {
346
349
  this.localFormat = coerceString(raw.local_format);
347
350
  }
348
351
  static fromData(data2, creditsRemaining = null) {
349
- return new _PhoneResult(data2, creditsRemaining);
352
+ return new _PhoneResult(asRaw(data2), creditsRemaining);
350
353
  }
351
354
  defaultFlagOn() {
352
355
  return _PhoneResult.DEFAULT_FLAG_ON;
@@ -419,12 +422,22 @@ var BatchResult = class _BatchResult {
419
422
  };
420
423
 
421
424
  // src/retry.ts
425
+ var MONTH = "(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)";
426
+ var SHORT_DAY = "(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun)";
427
+ var LONG_DAY = "(?:Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday)";
428
+ var TIME = "[0-9]{2}:[0-9]{2}:[0-9]{2}";
429
+ var IMF_FIXDATE = new RegExp(`^${SHORT_DAY}, [0-9]{2} ${MONTH} [0-9]{4} ${TIME} GMT$`, "i");
430
+ var RFC_850 = new RegExp(`^${LONG_DAY}, [0-9]{2}-${MONTH}-[0-9]{2} ${TIME} GMT$`, "i");
431
+ var ASCTIME = new RegExp(`^${SHORT_DAY} ${MONTH} (?: [0-9]|[0-9]{2}) ${TIME} [0-9]{4}$`, "i");
422
432
  function parseRetryAfter(value, now = /* @__PURE__ */ new Date()) {
423
433
  if (value === null) return null;
424
434
  const text = value.trim();
425
435
  if (text === "") return null;
426
436
  if (/^[0-9]+$/.test(text)) return Number.parseInt(text, 10);
427
- const when = Date.parse(text);
437
+ let when;
438
+ if (IMF_FIXDATE.test(text) || RFC_850.test(text)) when = Date.parse(text);
439
+ else if (ASCTIME.test(text)) when = Date.parse(`${text} GMT`);
440
+ else return null;
428
441
  if (Number.isNaN(when)) return null;
429
442
  return Math.max(0, Math.ceil((when - now.getTime()) / 1e3));
430
443
  }
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/version.ts","../src/errors.ts","../src/results.ts","../src/retry.ts","../src/transport.ts","../src/client.ts"],"sourcesContent":["export const VERSION = \"1.0.0\";\n","/** Return a safe, shortened form of an API key for display. */\nexport function maskApiKey(apiKey: string): string {\n return apiKey.length > 12 ? `fn_…${apiKey.slice(-4)}` : \"fn_…\";\n}\n\n/** Base class for every error thrown by this SDK. */\nexport class FraudNovaError extends Error {\n constructor(message?: string, options?: ErrorOptions) {\n super(message, options);\n this.name = \"FraudNovaError\";\n }\n}\n\n/** A client-side input problem; no request was sent. */\nexport class InvalidArgumentError extends FraudNovaError {\n constructor(message?: string) {\n super(message);\n this.name = \"InvalidArgumentError\";\n }\n}\n\n/** The HTTP request itself failed (timeout, DNS, TLS). */\nexport class TransportError extends FraudNovaError {\n constructor(message: string, options?: { cause?: unknown }) {\n super(message, options);\n this.name = \"TransportError\";\n }\n}\n\n/** The API answered with an error status. */\nexport class ApiError extends FraudNovaError {\n readonly statusCode: number;\n readonly apiMessage: string;\n readonly body: Record<string, unknown>;\n\n constructor(\n statusCode: number,\n apiMessage: string,\n body: Record<string, unknown> = {},\n message?: string,\n ) {\n super(message ?? `FraudNova API error (HTTP ${statusCode}): ${apiMessage}`);\n this.name = \"ApiError\";\n this.statusCode = statusCode;\n this.apiMessage = apiMessage;\n this.body = body;\n }\n\n static fromResponse(\n statusCode: number,\n text: string,\n retryAfter: number | null = null,\n ): ApiError {\n let decoded: unknown = null;\n try {\n decoded = JSON.parse(text);\n } catch {\n decoded = null;\n }\n const isJson = typeof decoded === \"object\" && decoded !== null && !Array.isArray(decoded);\n const body: Record<string, unknown> = isJson ? (decoded as Record<string, unknown>) : {};\n const envelope = isJson && (\"success\" in body || \"message\" in body);\n\n let apiMessage = \"\";\n for (const key of [\"message\", \"error\"]) {\n const value = body[key];\n if (typeof value === \"string\" && value !== \"\") {\n apiMessage = value;\n break;\n }\n }\n if (apiMessage === \"\") {\n const stripped = text.trim();\n apiMessage = stripped !== \"\" ? stripped.slice(0, 200) : `HTTP ${statusCode}`;\n }\n\n const full = isJson ? undefined : `FraudNova API error (HTTP ${statusCode}): non-JSON response`;\n\n if (envelope && statusCode === 401)\n return new AuthenticationError(statusCode, apiMessage, body, full);\n if (envelope && statusCode === 402) {\n return new InsufficientCreditsError(statusCode, apiMessage, body, full);\n }\n if (envelope && statusCode === 403)\n return new AccountSuspendedError(statusCode, apiMessage, body, full);\n if (envelope && statusCode === 422)\n return new ValidationError(statusCode, apiMessage, body, full);\n if (statusCode === 429) {\n return new RateLimitError(statusCode, apiMessage, body, retryAfter, full);\n }\n if (statusCode === 502 || statusCode === 503 || statusCode === 504) {\n return new UpstreamError(statusCode, apiMessage, body, statusCode === 503 && envelope, full);\n }\n return new ApiError(statusCode, apiMessage, body, full);\n }\n}\n\n/** 401: missing or invalid API key. */\nexport class AuthenticationError extends ApiError {\n constructor(\n statusCode: number,\n apiMessage: string,\n body: Record<string, unknown> = {},\n message?: string,\n ) {\n super(statusCode, apiMessage, body, message);\n this.name = \"AuthenticationError\";\n }\n}\n\n/** 402: the account has no credits left. */\nexport class InsufficientCreditsError extends ApiError {\n constructor(\n statusCode: number,\n apiMessage: string,\n body: Record<string, unknown> = {},\n message?: string,\n ) {\n super(statusCode, apiMessage, body, message);\n this.name = \"InsufficientCreditsError\";\n }\n}\n\n/** 403: the account is suspended. */\nexport class AccountSuspendedError extends ApiError {\n constructor(\n statusCode: number,\n apiMessage: string,\n body: Record<string, unknown> = {},\n message?: string,\n ) {\n super(statusCode, apiMessage, body, message);\n this.name = \"AccountSuspendedError\";\n }\n}\n\n/** 422: the request failed validation. */\nexport class ValidationError extends ApiError {\n constructor(\n statusCode: number,\n apiMessage: string,\n body: Record<string, unknown> = {},\n message?: string,\n ) {\n super(statusCode, apiMessage, body, message);\n this.name = \"ValidationError\";\n }\n}\n\n/** 429: rate or concurrency limit hit. */\nexport class RateLimitError extends ApiError {\n readonly retryAfter: number | null;\n\n constructor(\n statusCode: number,\n apiMessage: string,\n body: Record<string, unknown> = {},\n retryAfter: number | null = null,\n message?: string,\n ) {\n super(statusCode, apiMessage, body, message);\n this.name = \"RateLimitError\";\n this.retryAfter = retryAfter;\n }\n\n get isConcurrencyLimit(): boolean {\n return this.apiMessage.includes(\"Too many concurrent requests\");\n }\n}\n\n/** 502/503/504: the lookup could not be completed. */\nexport class UpstreamError extends ApiError {\n readonly creditRefunded: boolean;\n\n constructor(\n statusCode: number,\n apiMessage: string,\n body: Record<string, unknown> = {},\n creditRefunded = false,\n message?: string,\n ) {\n let text = message ?? `FraudNova API error (HTTP ${statusCode}): ${apiMessage}`;\n if (creditRefunded) text += \" The credit for this check was refunded.\";\n super(statusCode, apiMessage, body, text);\n this.name = \"UpstreamError\";\n this.creditRefunded = creditRefunded;\n }\n}\n\n/** A 2xx response whose body is not the success envelope. */\nexport class UnexpectedResponseError extends ApiError {\n constructor(\n statusCode: number,\n apiMessage: string,\n body: Record<string, unknown> = {},\n message?: string,\n ) {\n super(statusCode, apiMessage, body, message);\n this.name = \"UnexpectedResponseError\";\n }\n}\n","/** Typed, leniently parsed results for IP, email and phone checks. */\n\ntype Raw = Record<string, unknown>;\n\nexport function coerceBool(v: unknown): boolean | null {\n return typeof v === \"boolean\" ? v : null;\n}\n\nexport function coerceString(v: unknown): string | null {\n return typeof v === \"string\" ? v : null;\n}\n\nfunction roundHalfAway(n: number): number {\n const r = Math.sign(n) * Math.floor(Math.abs(n) + 0.5);\n return r === 0 ? 0 : r;\n}\n\nexport function coerceInt(v: unknown): number | null {\n let n: number;\n if (typeof v === \"number\") {\n n = v;\n } else if (typeof v === \"string\" && (/^-?\\d+$/.test(v) || /^-?\\d+\\.\\d+$/.test(v))) {\n n = Number(v);\n } else {\n return null;\n }\n return Number.isFinite(n) ? roundHalfAway(n) : null;\n}\n\nfunction isPlainObject(v: unknown): v is Raw {\n return typeof v === \"object\" && v !== null && !Array.isArray(v);\n}\n\nexport interface DateInfo {\n human: string | null;\n timestamp: number | null;\n iso: string | null;\n}\n\nexport function parseDateInfo(value: unknown): DateInfo | null {\n if (typeof value === \"string\") {\n return { human: value, timestamp: null, iso: null };\n }\n if (isPlainObject(value)) {\n return {\n human: coerceString(value.human),\n timestamp: coerceInt(value.timestamp),\n iso: coerceString(value.iso),\n };\n }\n return null;\n}\n\nexport interface FlagOptions {\n riskThreshold?: number;\n flagOn?: readonly string[];\n}\n\nexport type CheckKind = \"ip\" | \"email\" | \"phone\";\n\nabstract class FlaggableResult {\n readonly creditsRemaining: number | null;\n readonly raw: Raw;\n\n protected constructor(raw: Raw, creditsRemaining: number | null) {\n this.raw = raw;\n this.creditsRemaining = creditsRemaining;\n }\n\n protected abstract defaultFlagOn(): readonly string[];\n\n toJSON(): Raw {\n return this.raw;\n }\n\n flagReasons(options: FlagOptions = {}): string[] {\n const threshold = options.riskThreshold ?? 75;\n const reasons: string[] = [];\n if ((coerceInt(this.raw.fraud_score) ?? 0) >= threshold) {\n reasons.push(\"fraud_score\");\n }\n for (const name of options.flagOn ?? this.defaultFlagOn()) {\n if (this.raw[name] === true) {\n reasons.push(name);\n }\n }\n return reasons;\n }\n\n flagged(options: FlagOptions = {}): boolean {\n return this.flagReasons(options).length > 0;\n }\n}\n\nexport class IpResult extends FlaggableResult {\n static readonly DEFAULT_FLAG_ON: readonly string[] = [\"proxy\", \"vpn\", \"tor\"];\n\n readonly fraudScore: number;\n readonly proxy: boolean | null;\n readonly vpn: boolean | null;\n readonly tor: boolean | null;\n readonly activeVpn: boolean | null;\n readonly activeTor: boolean | null;\n readonly recentAbuse: boolean | null;\n readonly botStatus: boolean | null;\n readonly countryCode: string | null;\n readonly region: string | null;\n readonly city: string | null;\n readonly isp: string | null;\n readonly organization: string | null;\n readonly asn: number | null;\n readonly operatingSystem: string | null;\n readonly browser: string | null;\n readonly deviceBrand: string | null;\n readonly deviceModel: string | null;\n\n private constructor(raw: Raw, creditsRemaining: number | null) {\n super(raw, creditsRemaining);\n this.fraudScore = coerceInt(raw.fraud_score) ?? 0;\n this.proxy = coerceBool(raw.proxy);\n this.vpn = coerceBool(raw.vpn);\n this.tor = coerceBool(raw.tor);\n this.activeVpn = coerceBool(raw.active_vpn);\n this.activeTor = coerceBool(raw.active_tor);\n this.recentAbuse = coerceBool(raw.recent_abuse);\n this.botStatus = coerceBool(raw.bot_status);\n this.countryCode = coerceString(raw.country_code);\n this.region = coerceString(raw.region);\n this.city = coerceString(raw.city);\n this.isp = coerceString(raw.ISP);\n this.organization = coerceString(raw.organization);\n this.asn = coerceInt(raw.ASN);\n this.operatingSystem = coerceString(raw.operating_system);\n this.browser = coerceString(raw.browser);\n this.deviceBrand = coerceString(raw.device_brand);\n this.deviceModel = coerceString(raw.device_model);\n }\n\n static fromData(data: Raw, creditsRemaining: number | null = null): IpResult {\n return new IpResult(data, creditsRemaining);\n }\n\n protected defaultFlagOn(): readonly string[] {\n return IpResult.DEFAULT_FLAG_ON;\n }\n}\n\nexport class EmailResult extends FlaggableResult {\n static readonly DEFAULT_FLAG_ON: readonly string[] = [\"disposable\"];\n\n readonly fraudScore: number;\n readonly valid: boolean | null;\n readonly disposable: boolean | null;\n readonly catchAll: boolean | null;\n readonly generic: boolean | null;\n readonly common: boolean | null;\n readonly dnsValid: boolean | null;\n readonly honeypot: boolean | null;\n readonly frequentComplainer: boolean | null;\n readonly suspect: boolean | null;\n readonly recentAbuse: boolean | null;\n readonly leaked: boolean | null;\n readonly riskyTld: boolean | null;\n readonly spfRecord: boolean | null;\n readonly dmarcRecord: boolean | null;\n readonly smtpScore: number | null;\n readonly overallScore: number | null;\n readonly deliverability: string | null;\n readonly spamTrapScore: string | null;\n readonly resultType: string | null;\n readonly sanitizedEmail: string | null;\n readonly firstSeen: DateInfo | null;\n readonly domainAge: DateInfo | null;\n\n private constructor(raw: Raw, creditsRemaining: number | null) {\n super(raw, creditsRemaining);\n this.fraudScore = coerceInt(raw.fraud_score) ?? 0;\n this.valid = coerceBool(raw.valid);\n this.disposable = coerceBool(raw.disposable);\n this.catchAll = coerceBool(raw.catch_all);\n this.generic = coerceBool(raw.generic);\n this.common = coerceBool(raw.common);\n this.dnsValid = coerceBool(raw.dns_valid);\n this.honeypot = coerceBool(raw.honeypot);\n this.frequentComplainer = coerceBool(raw.frequent_complainer);\n this.suspect = coerceBool(raw.suspect);\n this.recentAbuse = coerceBool(raw.recent_abuse);\n this.leaked = coerceBool(raw.leaked);\n this.riskyTld = coerceBool(raw.risky_tld);\n this.spfRecord = coerceBool(raw.spf_record);\n this.dmarcRecord = coerceBool(raw.dmarc_record);\n this.smtpScore = coerceInt(raw.smtp_score);\n this.overallScore = coerceInt(raw.overall_score);\n this.deliverability = coerceString(raw.deliverability);\n this.spamTrapScore = coerceString(raw.spam_trap_score);\n this.resultType = coerceString(raw.result_type);\n this.sanitizedEmail = coerceString(raw.sanitized_email);\n this.firstSeen = parseDateInfo(raw.first_seen);\n this.domainAge = parseDateInfo(raw.domain_age);\n }\n\n static fromData(data: Raw, creditsRemaining: number | null = null): EmailResult {\n return new EmailResult(data, creditsRemaining);\n }\n\n protected defaultFlagOn(): readonly string[] {\n return EmailResult.DEFAULT_FLAG_ON;\n }\n}\n\nexport class PhoneResult extends FlaggableResult {\n static readonly DEFAULT_FLAG_ON: readonly string[] = [\"VOIP\", \"risky\"];\n\n readonly fraudScore: number;\n readonly valid: boolean | null;\n readonly recentAbuse: boolean | null;\n readonly voip: boolean | null;\n readonly prepaid: boolean | null;\n readonly risky: boolean | null;\n readonly active: boolean | null;\n readonly doNotCall: boolean | null;\n readonly leaked: boolean | null;\n readonly spammer: boolean | null;\n readonly dialingCode: number | null;\n readonly carrier: string | null;\n readonly lineType: string | null;\n readonly country: string | null;\n readonly region: string | null;\n readonly city: string | null;\n readonly timezone: string | null;\n readonly zipCode: string | null;\n readonly activeStatus: string | null;\n readonly formatted: string | null;\n readonly localFormat: string | null;\n\n private constructor(raw: Raw, creditsRemaining: number | null) {\n super(raw, creditsRemaining);\n this.fraudScore = coerceInt(raw.fraud_score) ?? 0;\n this.valid = coerceBool(raw.valid);\n this.recentAbuse = coerceBool(raw.recent_abuse);\n this.voip = coerceBool(raw.VOIP);\n this.prepaid = coerceBool(raw.prepaid);\n this.risky = coerceBool(raw.risky);\n this.active = coerceBool(raw.active);\n this.doNotCall = coerceBool(raw.do_not_call);\n this.leaked = coerceBool(raw.leaked);\n this.spammer = coerceBool(raw.spammer);\n this.dialingCode = coerceInt(raw.dialing_code);\n this.carrier = coerceString(raw.carrier);\n this.lineType = coerceString(raw.line_type);\n this.country = coerceString(raw.country);\n this.region = coerceString(raw.region);\n this.city = coerceString(raw.city);\n this.timezone = coerceString(raw.timezone);\n this.zipCode = coerceString(raw.zip_code);\n this.activeStatus = coerceString(raw.active_status);\n this.formatted = coerceString(raw.formatted);\n this.localFormat = coerceString(raw.local_format);\n }\n\n static fromData(data: Raw, creditsRemaining: number | null = null): PhoneResult {\n return new PhoneResult(data, creditsRemaining);\n }\n\n protected defaultFlagOn(): readonly string[] {\n return PhoneResult.DEFAULT_FLAG_ON;\n }\n}\n\nexport type Result = IpResult | EmailResult | PhoneResult;\n\nexport class BatchItem {\n constructor(\n readonly subject: string,\n readonly success: boolean,\n readonly result: Result | null,\n readonly error: string | null,\n ) {}\n\n isInvalid(): boolean {\n return (\n this.error === \"invalid_ip\" ||\n this.error === \"invalid_email\" ||\n this.error === \"invalid_phone\"\n );\n }\n\n isInsufficientCredits(): boolean {\n return this.error === \"insufficient_credits\";\n }\n\n isUpstreamFailed(): boolean {\n return this.error === \"upstream_failed\";\n }\n}\n\nexport class BatchResult {\n constructor(\n readonly items: BatchItem[],\n readonly creditsRemaining: number,\n ) {}\n\n static fromEnvelope(envelope: Raw, kind: CheckKind): BatchResult {\n const items: BatchItem[] = [];\n const rawItems = envelope.results;\n if (Array.isArray(rawItems)) {\n for (const item of rawItems) {\n if (!isPlainObject(item)) {\n continue;\n }\n const success = coerceBool(item.success) ?? false;\n const data = item.data;\n let result: Result | null = null;\n if (success && isPlainObject(data)) {\n result =\n kind === \"ip\"\n ? IpResult.fromData(data)\n : kind === \"email\"\n ? EmailResult.fromData(data)\n : PhoneResult.fromData(data);\n }\n items.push(\n new BatchItem(coerceString(item[kind]) ?? \"\", success, result, coerceString(item.error)),\n );\n }\n }\n return new BatchResult(items, coerceInt(envelope.credits_remaining) ?? 0);\n }\n\n successful(): BatchItem[] {\n return this.items.filter((i) => i.success);\n }\n\n failed(): BatchItem[] {\n return this.items.filter((i) => !i.success);\n }\n\n hasFailures(): boolean {\n return this.items.some((i) => !i.success);\n }\n\n get length(): number {\n return this.items.length;\n }\n\n *[Symbol.iterator](): Iterator<BatchItem> {\n yield* this.items;\n }\n}\n","import { InvalidArgumentError, type RateLimitError } from \"./errors.js\";\n\n/** Parse a Retry-After header (delta-seconds or HTTP-date) into whole seconds. */\nexport function parseRetryAfter(value: string | null, now: Date = new Date()): number | null {\n if (value === null) return null;\n const text = value.trim();\n if (text === \"\") return null;\n if (/^[0-9]+$/.test(text)) return Number.parseInt(text, 10);\n const when = Date.parse(text);\n if (Number.isNaN(when)) return null;\n return Math.max(0, Math.ceil((when - now.getTime()) / 1000));\n}\n\nexport interface RetryPolicyOptions {\n maxAttempts?: number;\n concurrencyDelays?: readonly number[];\n rateLimitDelays?: readonly number[];\n honourRetryAfter?: boolean;\n maxRetryAfter?: number;\n sleep?: (seconds: number) => Promise<void>;\n}\n\nconst defaultSleep = (seconds: number): Promise<void> =>\n new Promise((resolve) => setTimeout(resolve, seconds * 1000));\n\n/** Opt-in retry policy for rate-limited (429) requests. All delays are in seconds. */\nexport class RetryPolicy {\n readonly maxAttempts: number;\n readonly concurrencyDelays: readonly number[];\n readonly rateLimitDelays: readonly number[];\n readonly honourRetryAfter: boolean;\n readonly maxRetryAfter: number;\n readonly #sleeper: (seconds: number) => Promise<void>;\n\n constructor(options: RetryPolicyOptions = {}) {\n this.maxAttempts = options.maxAttempts ?? 3;\n this.concurrencyDelays = [...(options.concurrencyDelays ?? [1, 2, 4])];\n this.rateLimitDelays = [...(options.rateLimitDelays ?? [5, 15, 40])];\n this.honourRetryAfter = options.honourRetryAfter ?? true;\n this.maxRetryAfter = options.maxRetryAfter ?? 60;\n this.#sleeper = options.sleep ?? defaultSleep;\n if (!Number.isInteger(this.maxAttempts) || this.maxAttempts < 1) {\n throw new InvalidArgumentError(\"maxAttempts must be an integer of at least 1.\");\n }\n if (!Number.isFinite(this.maxRetryAfter) || this.maxRetryAfter < 0) {\n throw new InvalidArgumentError(\"maxRetryAfter must be a finite number that is not negative.\");\n }\n for (const [name, delays] of [\n [\"concurrencyDelays\", this.concurrencyDelays],\n [\"rateLimitDelays\", this.rateLimitDelays],\n ] as const) {\n if (\n !delays.every((delay) => typeof delay === \"number\" && Number.isFinite(delay) && delay >= 0)\n ) {\n throw new InvalidArgumentError(\n `${name} must contain only finite numbers that are not negative.`,\n );\n }\n }\n }\n\n /** Seconds to wait before retrying after `attempt` (1-based) failed, or null to stop. */\n delayFor(error: RateLimitError, attempt: number): number | null {\n if (attempt >= this.maxAttempts) return null;\n if (this.honourRetryAfter && error.retryAfter !== null) {\n return error.retryAfter > this.maxRetryAfter ? null : error.retryAfter;\n }\n const schedule = error.isConcurrencyLimit ? this.concurrencyDelays : this.rateLimitDelays;\n const delay = schedule[attempt - 1];\n return delay === undefined ? null : delay;\n }\n\n sleep(seconds: number): Promise<void> {\n return this.#sleeper(seconds);\n }\n}\n","import {\n ApiError,\n InvalidArgumentError,\n RateLimitError,\n TransportError,\n UnexpectedResponseError,\n maskApiKey,\n} from \"./errors.js\";\nimport { type RetryPolicy, parseRetryAfter } from \"./retry.js\";\nimport { VERSION } from \"./version.js\";\n\n/** A `fetch`-compatible function. */\nexport type FetchLike = (url: string, init: RequestInit) => Promise<Response>;\n\nexport interface TransportOptions {\n apiKey: string;\n baseUrl: string;\n timeout: number;\n retry: RetryPolicy | null;\n fetch: FetchLike | null;\n}\n\ntype QueryParams = Record<string, string | number | boolean | null | undefined>;\n\nfunction userAgent(): string {\n let agent = `fraudnova-node/${VERSION}`;\n if (typeof process !== \"undefined\" && typeof process.versions?.node === \"string\") {\n agent += ` node/${process.versions.node}`;\n }\n return agent;\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n/** Internal HTTP layer: builds requests, decodes the envelope, maps errors, retries 429s. */\nexport class Transport {\n readonly baseUrl: string;\n readonly #apiKey: string;\n readonly #timeout: number;\n readonly #retry: RetryPolicy | null;\n readonly #fetch: FetchLike;\n readonly #userAgent: string;\n\n constructor(options: TransportOptions) {\n if (!/^[\\x21-\\x7e]+$/.test(options.apiKey)) {\n throw new InvalidArgumentError(\n \"apiKey must contain only printable ASCII characters, without whitespace.\",\n );\n }\n let fetchImpl: FetchLike | null = options.fetch ?? null;\n if (fetchImpl === null) {\n const globalFetch = (globalThis as { fetch?: typeof globalThis.fetch }).fetch;\n if (typeof globalFetch === \"function\") {\n fetchImpl = globalFetch.bind(globalThis);\n }\n }\n if (fetchImpl === null) {\n throw new InvalidArgumentError(\n \"No fetch implementation available; pass one in the fetch option\",\n );\n }\n this.#apiKey = options.apiKey;\n this.baseUrl = options.baseUrl.replace(/\\/+$/, \"\");\n this.#timeout = options.timeout;\n this.#retry = options.retry;\n this.#fetch = fetchImpl;\n this.#userAgent = userAgent();\n }\n\n get(\n path: string,\n params: QueryParams | null = null,\n authenticated = true,\n ): Promise<Record<string, unknown>> {\n const query = new URLSearchParams();\n for (const [name, value] of Object.entries(params ?? {})) {\n if (value === undefined || value === null) continue;\n query.append(name, String(value));\n }\n const search = query.toString();\n const url = this.baseUrl + path + (search !== \"\" ? `?${search}` : \"\");\n return this.#send(\"GET\", url, authenticated);\n }\n\n post(path: string, json: Record<string, unknown>): Promise<Record<string, unknown>> {\n return this.#send(\"POST\", this.baseUrl + path, true, JSON.stringify(json));\n }\n\n #headers(authenticated: boolean, hasBody: boolean): Record<string, string> {\n const headers: Record<string, string> = {\n Accept: \"application/json\",\n \"User-Agent\": this.#userAgent,\n };\n if (authenticated) headers.Authorization = `Bearer ${this.#apiKey}`;\n if (hasBody) headers[\"Content-Type\"] = \"application/json\";\n return headers;\n }\n\n async #send(\n method: string,\n url: string,\n authenticated: boolean,\n body?: string,\n ): Promise<Record<string, unknown>> {\n let attempt = 1;\n for (;;) {\n const init: RequestInit = {\n method,\n headers: this.#headers(authenticated, body !== undefined),\n };\n if (body !== undefined) init.body = body;\n\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.#timeout);\n let response: Response;\n let text: string;\n try {\n response = await this.#fetch(url, { ...init, signal: controller.signal });\n text = await response.text();\n } catch (error) {\n if (controller.signal.aborted) {\n throw new TransportError(`FraudNova request timed out after ${this.#timeout} ms`, {\n cause: error,\n });\n }\n const original = error instanceof Error ? error.message : String(error);\n const masked = original.split(this.#apiKey).join(maskApiKey(this.#apiKey));\n throw new TransportError(`FraudNova request failed: ${masked}`, { cause: error });\n } finally {\n clearTimeout(timer);\n }\n\n try {\n return Transport.#decode(response, text);\n } catch (error) {\n if (!(error instanceof RateLimitError)) throw error;\n const delay = this.#retry?.delayFor(error, attempt) ?? null;\n if (this.#retry === null || delay === null) throw error;\n await this.#retry.sleep(delay);\n attempt++;\n }\n }\n }\n\n static #decode(response: Response, text: string): Record<string, unknown> {\n const status = response.status;\n if (status < 200 || status >= 300) {\n throw ApiError.fromResponse(\n status,\n text,\n parseRetryAfter(response.headers.get(\"retry-after\")),\n );\n }\n let decoded: unknown = null;\n try {\n decoded = JSON.parse(text);\n } catch {\n decoded = null;\n }\n if (!isPlainObject(decoded) || decoded.success !== true) {\n throw new UnexpectedResponseError(\n status,\n \"Unexpected response body\",\n isPlainObject(decoded) ? decoded : {},\n );\n }\n return decoded;\n }\n}\n","import { InvalidArgumentError, UnexpectedResponseError, maskApiKey } from \"./errors.js\";\nimport {\n BatchItem,\n BatchResult,\n EmailResult,\n IpResult,\n PhoneResult,\n coerceInt,\n type CheckKind,\n} from \"./results.js\";\nimport type { RetryPolicy } from \"./retry.js\";\nimport { Transport, type FetchLike } from \"./transport.js\";\n\n/** The production API base URL. */\nexport const DEFAULT_BASE_URL = \"https://fraudnova.com/api/v1\";\n\n/** The most subjects the API accepts in one batch request; larger arrays are chunked. */\nexport const BATCH_MAX = 100;\n\nconst DEFAULT_TIMEOUT = 30_000;\n\nconst inspectSymbol = Symbol.for(\"nodejs.util.inspect.custom\");\n\nexport interface FraudNovaOptions {\n /** API base URL; defaults to {@link DEFAULT_BASE_URL}. */\n baseUrl?: string;\n /** Per-request timeout in milliseconds; defaults to 30 000. */\n timeout?: number;\n /** Opt-in retry policy for 429 responses. */\n retry?: RetryPolicy;\n /** A fetch-compatible function; defaults to the global `fetch`. */\n fetch?: FetchLike;\n}\n\nexport interface IpCheckOptions {\n strictness?: 0 | 1 | 2 | 3;\n mobile?: boolean;\n userAgent?: string;\n userLanguage?: string;\n}\n\nexport interface PhoneCheckOptions {\n country?: string;\n}\n\ntype Envelope = Record<string, unknown>;\n\nfunction subject(value: unknown, name: string): string {\n if (typeof value !== \"string\" || value.trim() === \"\") {\n throw new InvalidArgumentError(`${name} must be a non-empty string`);\n }\n return value.trim();\n}\n\nfunction strictness(value: unknown): number | undefined {\n if (value === undefined) return undefined;\n if (typeof value !== \"number\" || !Number.isInteger(value) || value < 0 || value > 3) {\n throw new InvalidArgumentError(\"strictness must be an integer from 0 to 3\");\n }\n return value;\n}\n\nfunction country(value: unknown): string | undefined {\n if (typeof value !== \"string\") return undefined;\n const cleaned = value.trim().toUpperCase();\n return cleaned !== \"\" ? cleaned : undefined;\n}\n\nfunction data(envelope: Envelope): Record<string, unknown> {\n const value = envelope.data;\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n throw new UnexpectedResponseError(200, \"Missing data\", envelope);\n }\n return value as Record<string, unknown>;\n}\n\n/** Build the IP options shared by the single and batch calls, keeping only defined values. */\nfunction ipOptions(options: IpCheckOptions): Record<string, number | boolean | string> {\n const out: Record<string, number | boolean | string> = {};\n const level = strictness(options.strictness);\n if (level !== undefined) out.strictness = level;\n if (options.mobile === true || options.mobile === false) out.mobile = options.mobile;\n if (options.userAgent !== undefined) out.user_agent = options.userAgent;\n if (options.userLanguage !== undefined) out.user_language = options.userLanguage;\n return out;\n}\n\n/** The FraudNova API client. */\nexport class FraudNova {\n readonly baseUrl: string;\n readonly #apiKey: string;\n readonly #transport: Transport;\n\n constructor(apiKey: string, options: FraudNovaOptions = {}) {\n if (typeof apiKey !== \"string\" || apiKey.trim() === \"\") {\n throw new InvalidArgumentError(\"apiKey must be a non-empty string\");\n }\n const baseUrl = options.baseUrl ?? DEFAULT_BASE_URL;\n if (typeof baseUrl !== \"string\" || !/^https?:\\/\\//i.test(baseUrl)) {\n throw new InvalidArgumentError(\"baseUrl must start with http:// or https://\");\n }\n const timeout = options.timeout ?? DEFAULT_TIMEOUT;\n if (\n typeof timeout !== \"number\" ||\n !Number.isFinite(timeout) ||\n timeout <= 0 ||\n timeout > 2_147_483_647\n ) {\n throw new InvalidArgumentError(\n \"timeout must be a positive number of milliseconds (at most 2147483647)\",\n );\n }\n this.#apiKey = apiKey.trim();\n this.#transport = new Transport({\n apiKey: this.#apiKey,\n baseUrl,\n timeout,\n retry: options.retry ?? null,\n fetch: options.fetch ?? null,\n });\n this.baseUrl = this.#transport.baseUrl;\n }\n\n /** Score one IP address. */\n async checkIp(ip: string, options: IpCheckOptions = {}): Promise<IpResult> {\n const params = { ip: subject(ip, \"ip\"), ...ipOptions(options) };\n const envelope = await this.#transport.get(\"/check\", params);\n return IpResult.fromData(data(envelope), coerceInt(envelope.credits_remaining));\n }\n\n /** Score one email address. */\n async checkEmail(email: string): Promise<EmailResult> {\n const params = { email: subject(email, \"email\") };\n const envelope = await this.#transport.get(\"/check/email\", params);\n return EmailResult.fromData(data(envelope), coerceInt(envelope.credits_remaining));\n }\n\n /** Score one phone number. */\n async checkPhone(phone: string, options: PhoneCheckOptions = {}): Promise<PhoneResult> {\n const params = { phone: subject(phone, \"phone\"), country: country(options.country) };\n const envelope = await this.#transport.get(\"/check/phone\", params);\n return PhoneResult.fromData(data(envelope), coerceInt(envelope.credits_remaining));\n }\n\n /** Score many IP addresses, chunked by {@link BATCH_MAX}. */\n async checkIps(\n ips: ReadonlyArray<string | null>,\n options: IpCheckOptions = {},\n ): Promise<BatchResult> {\n return this.#batch(\"/check/batch\", \"ips\", \"ip\", ips, ipOptions(options));\n }\n\n /** Score many email addresses, chunked by {@link BATCH_MAX}. */\n async checkEmails(emails: ReadonlyArray<string | null>): Promise<BatchResult> {\n return this.#batch(\"/check/email/batch\", \"emails\", \"email\", emails, {});\n }\n\n /** Score many phone numbers, chunked by {@link BATCH_MAX}. */\n async checkPhones(\n phones: ReadonlyArray<string | null>,\n options: PhoneCheckOptions = {},\n ): Promise<BatchResult> {\n const code = country(options.country);\n const extra: Record<string, string> = code !== undefined ? { country: code } : {};\n return this.#batch(\"/check/phone/batch\", \"phones\", \"phone\", phones, extra);\n }\n\n /** The account's remaining credits. */\n async balance(): Promise<number> {\n const envelope = await this.#transport.get(\"/balance\");\n const credits = coerceInt(envelope.credits_remaining);\n if (credits === null) {\n throw new UnexpectedResponseError(200, \"Missing credits_remaining\", envelope);\n }\n return credits;\n }\n\n /** The caller's public IP address as the API sees it (free, unauthenticated). */\n async myIp(): Promise<string> {\n const envelope = await this.#transport.get(\"/my-ip\", null, false);\n const ip = envelope.ip;\n if (typeof ip !== \"string\" || ip === \"\") {\n throw new UnexpectedResponseError(200, \"Missing ip\", envelope);\n }\n return ip;\n }\n\n async #batch(\n path: string,\n field: string,\n kind: CheckKind,\n subjects: ReadonlyArray<string | null>,\n extra: Record<string, unknown>,\n ): Promise<BatchResult> {\n if (!Array.isArray(subjects)) {\n throw new InvalidArgumentError(`${field} must be an array of values`);\n }\n if (subjects.length === 0) {\n throw new InvalidArgumentError(`${field} must not be empty`);\n }\n const cleaned: Array<string | null> = subjects.map((item: unknown, index) => {\n if (item !== null && typeof item !== \"string\") {\n throw new InvalidArgumentError(`${field}[${index}] must be a string or null`);\n }\n return item === null ? null : item.trim();\n });\n\n const items: BatchItem[] = [];\n let credits = 0;\n for (let start = 0; start < cleaned.length; start += BATCH_MAX) {\n const chunk = cleaned.slice(start, start + BATCH_MAX);\n const envelope = await this.#transport.post(path, { [field]: chunk, ...extra });\n if (!Array.isArray(envelope.results)) {\n throw new UnexpectedResponseError(200, \"Missing results\", envelope);\n }\n const part = BatchResult.fromEnvelope(envelope, kind);\n items.push(...part.items);\n credits = part.creditsRemaining;\n const rest = cleaned.slice(start + BATCH_MAX);\n if (rest.length > 0 && coerceInt(envelope.credits_remaining) === 0) {\n // The API answers 402 to a request sent with a zero balance, which would discard\n // the results above; mark the unsent items the way the API does.\n for (const item of rest) {\n items.push(new BatchItem(item ?? \"\", false, null, \"insufficient_credits\"));\n }\n break;\n }\n }\n return new BatchResult(items, credits);\n }\n\n toString(): string {\n return `FraudNova { apiKey: \"${maskApiKey(this.#apiKey)}\", baseUrl: \"${this.baseUrl}\" }`;\n }\n\n [inspectSymbol](): string {\n return this.toString();\n }\n}\n"],"mappings":"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","names":["data","subject","isPlainObject"]}
1
+ {"version":3,"sources":["../src/version.ts","../src/errors.ts","../src/results.ts","../src/retry.ts","../src/transport.ts","../src/client.ts"],"sourcesContent":["export const VERSION = \"1.0.2\";\n","/** Return a safe, shortened form of an API key for display. */\nexport function maskApiKey(apiKey: string): string {\n return apiKey.length > 12 ? `fn_…${apiKey.slice(-4)}` : \"fn_…\";\n}\n\n/** Base class for every error thrown by this SDK. */\nexport class FraudNovaError extends Error {\n constructor(message?: string, options?: ErrorOptions) {\n super(message, options);\n this.name = \"FraudNovaError\";\n }\n}\n\n/** A client-side input problem; no request was sent. */\nexport class InvalidArgumentError extends FraudNovaError {\n constructor(message?: string) {\n super(message);\n this.name = \"InvalidArgumentError\";\n }\n}\n\n/** The HTTP request itself failed (timeout, DNS, TLS). */\nexport class TransportError extends FraudNovaError {\n constructor(message: string, options?: { cause?: unknown }) {\n super(message, options);\n this.name = \"TransportError\";\n }\n}\n\n/** The API answered with an error status. */\nexport class ApiError extends FraudNovaError {\n readonly statusCode: number;\n readonly apiMessage: string;\n readonly body: Record<string, unknown>;\n\n constructor(\n statusCode: number,\n apiMessage: string,\n body: Record<string, unknown> = {},\n message?: string,\n ) {\n super(message ?? `FraudNova API error (HTTP ${statusCode}): ${apiMessage}`);\n this.name = \"ApiError\";\n this.statusCode = statusCode;\n this.apiMessage = apiMessage;\n this.body = body;\n }\n\n static fromResponse(\n statusCode: number,\n text: string,\n retryAfter: number | null = null,\n ): ApiError {\n let decoded: unknown = null;\n try {\n decoded = JSON.parse(text);\n } catch {\n decoded = null;\n }\n const isJson = typeof decoded === \"object\" && decoded !== null && !Array.isArray(decoded);\n const body: Record<string, unknown> = isJson ? (decoded as Record<string, unknown>) : {};\n const envelope = isJson && (\"success\" in body || \"message\" in body);\n\n let apiMessage = \"\";\n for (const key of [\"message\", \"error\"]) {\n const value = body[key];\n if (typeof value === \"string\" && value !== \"\") {\n apiMessage = value;\n break;\n }\n }\n if (apiMessage === \"\") {\n const stripped = text.trim();\n apiMessage = stripped !== \"\" ? stripped.slice(0, 200) : `HTTP ${statusCode}`;\n }\n\n const full = isJson ? undefined : `FraudNova API error (HTTP ${statusCode}): non-JSON response`;\n\n if (envelope && statusCode === 401)\n return new AuthenticationError(statusCode, apiMessage, body, full);\n if (envelope && statusCode === 402) {\n return new InsufficientCreditsError(statusCode, apiMessage, body, full);\n }\n if (envelope && statusCode === 403)\n return new AccountSuspendedError(statusCode, apiMessage, body, full);\n if (envelope && statusCode === 422)\n return new ValidationError(statusCode, apiMessage, body, full);\n if (statusCode === 429) {\n return new RateLimitError(statusCode, apiMessage, body, retryAfter, full);\n }\n if (statusCode === 502 || statusCode === 503 || statusCode === 504) {\n return new UpstreamError(statusCode, apiMessage, body, statusCode === 503 && envelope, full);\n }\n return new ApiError(statusCode, apiMessage, body, full);\n }\n}\n\n/** 401: missing or invalid API key. */\nexport class AuthenticationError extends ApiError {\n constructor(\n statusCode: number,\n apiMessage: string,\n body: Record<string, unknown> = {},\n message?: string,\n ) {\n super(statusCode, apiMessage, body, message);\n this.name = \"AuthenticationError\";\n }\n}\n\n/** 402: the account has no credits left. */\nexport class InsufficientCreditsError extends ApiError {\n constructor(\n statusCode: number,\n apiMessage: string,\n body: Record<string, unknown> = {},\n message?: string,\n ) {\n super(statusCode, apiMessage, body, message);\n this.name = \"InsufficientCreditsError\";\n }\n}\n\n/** 403: the account is suspended. */\nexport class AccountSuspendedError extends ApiError {\n constructor(\n statusCode: number,\n apiMessage: string,\n body: Record<string, unknown> = {},\n message?: string,\n ) {\n super(statusCode, apiMessage, body, message);\n this.name = \"AccountSuspendedError\";\n }\n}\n\n/** 422: the request failed validation. */\nexport class ValidationError extends ApiError {\n constructor(\n statusCode: number,\n apiMessage: string,\n body: Record<string, unknown> = {},\n message?: string,\n ) {\n super(statusCode, apiMessage, body, message);\n this.name = \"ValidationError\";\n }\n}\n\n/** 429: rate or concurrency limit hit. */\nexport class RateLimitError extends ApiError {\n readonly retryAfter: number | null;\n\n constructor(\n statusCode: number,\n apiMessage: string,\n body: Record<string, unknown> = {},\n retryAfter: number | null = null,\n message?: string,\n ) {\n super(statusCode, apiMessage, body, message);\n this.name = \"RateLimitError\";\n this.retryAfter = retryAfter;\n }\n\n get isConcurrencyLimit(): boolean {\n return this.apiMessage.includes(\"Too many concurrent requests\");\n }\n}\n\n/** 502/503/504: the lookup could not be completed. */\nexport class UpstreamError extends ApiError {\n readonly creditRefunded: boolean;\n\n constructor(\n statusCode: number,\n apiMessage: string,\n body: Record<string, unknown> = {},\n creditRefunded = false,\n message?: string,\n ) {\n let text = message ?? `FraudNova API error (HTTP ${statusCode}): ${apiMessage}`;\n if (creditRefunded) text += \" The credit for this check was refunded.\";\n super(statusCode, apiMessage, body, text);\n this.name = \"UpstreamError\";\n this.creditRefunded = creditRefunded;\n }\n}\n\n/** A 2xx response whose body is not the success envelope. */\nexport class UnexpectedResponseError extends ApiError {\n constructor(\n statusCode: number,\n apiMessage: string,\n body: Record<string, unknown> = {},\n message?: string,\n ) {\n super(statusCode, apiMessage, body, message);\n this.name = \"UnexpectedResponseError\";\n }\n}\n","/** Typed, leniently parsed results for IP, email and phone checks. */\n\ntype Raw = Record<string, unknown>;\n\nexport function coerceBool(v: unknown): boolean | null {\n return typeof v === \"boolean\" ? v : null;\n}\n\nexport function coerceString(v: unknown): string | null {\n return typeof v === \"string\" ? v : null;\n}\n\nfunction roundHalfAway(n: number): number {\n const r = Math.sign(n) * Math.floor(Math.abs(n) + 0.5);\n return r === 0 ? 0 : r;\n}\n\nexport function coerceInt(v: unknown): number | null {\n let n: number;\n if (typeof v === \"number\") {\n n = v;\n } else if (typeof v === \"string\" && (/^-?\\d+$/.test(v) || /^-?\\d+\\.\\d+$/.test(v))) {\n n = Number(v);\n } else {\n return null;\n }\n return Number.isFinite(n) ? roundHalfAway(n) : null;\n}\n\nfunction isPlainObject(v: unknown): v is Raw {\n return typeof v === \"object\" && v !== null && !Array.isArray(v);\n}\n\n/** Treat anything that is not a plain object as `{}`, so a malformed `data` yields defaults. */\nfunction asRaw(data: unknown): Raw {\n return isPlainObject(data) ? data : {};\n}\n\nexport interface DateInfo {\n human: string | null;\n timestamp: number | null;\n iso: string | null;\n}\n\nexport function parseDateInfo(value: unknown): DateInfo | null {\n if (typeof value === \"string\") {\n return { human: value, timestamp: null, iso: null };\n }\n if (isPlainObject(value)) {\n return {\n human: coerceString(value.human),\n timestamp: coerceInt(value.timestamp),\n iso: coerceString(value.iso),\n };\n }\n return null;\n}\n\nexport interface FlagOptions {\n riskThreshold?: number;\n flagOn?: readonly string[];\n}\n\nexport type CheckKind = \"ip\" | \"email\" | \"phone\";\n\nabstract class FlaggableResult {\n readonly creditsRemaining: number | null;\n readonly raw: Raw;\n\n protected constructor(raw: Raw, creditsRemaining: number | null) {\n this.raw = raw;\n this.creditsRemaining = creditsRemaining;\n }\n\n protected abstract defaultFlagOn(): readonly string[];\n\n toJSON(): Raw {\n return this.raw;\n }\n\n flagReasons(options: FlagOptions = {}): string[] {\n const threshold = options.riskThreshold ?? 75;\n const reasons: string[] = [];\n if ((coerceInt(this.raw.fraud_score) ?? 0) >= threshold) {\n reasons.push(\"fraud_score\");\n }\n for (const name of options.flagOn ?? this.defaultFlagOn()) {\n if (this.raw[name] === true) {\n reasons.push(name);\n }\n }\n return reasons;\n }\n\n flagged(options: FlagOptions = {}): boolean {\n return this.flagReasons(options).length > 0;\n }\n}\n\nexport class IpResult extends FlaggableResult {\n static readonly DEFAULT_FLAG_ON: readonly string[] = [\"proxy\", \"vpn\", \"tor\"];\n\n readonly fraudScore: number;\n readonly proxy: boolean | null;\n readonly vpn: boolean | null;\n readonly tor: boolean | null;\n readonly activeVpn: boolean | null;\n readonly activeTor: boolean | null;\n readonly recentAbuse: boolean | null;\n readonly botStatus: boolean | null;\n readonly countryCode: string | null;\n readonly region: string | null;\n readonly city: string | null;\n readonly isp: string | null;\n readonly organization: string | null;\n readonly asn: number | null;\n readonly operatingSystem: string | null;\n readonly browser: string | null;\n readonly deviceBrand: string | null;\n readonly deviceModel: string | null;\n\n private constructor(raw: Raw, creditsRemaining: number | null) {\n super(raw, creditsRemaining);\n this.fraudScore = coerceInt(raw.fraud_score) ?? 0;\n this.proxy = coerceBool(raw.proxy);\n this.vpn = coerceBool(raw.vpn);\n this.tor = coerceBool(raw.tor);\n this.activeVpn = coerceBool(raw.active_vpn);\n this.activeTor = coerceBool(raw.active_tor);\n this.recentAbuse = coerceBool(raw.recent_abuse);\n this.botStatus = coerceBool(raw.bot_status);\n this.countryCode = coerceString(raw.country_code);\n this.region = coerceString(raw.region);\n this.city = coerceString(raw.city);\n this.isp = coerceString(raw.ISP);\n this.organization = coerceString(raw.organization);\n this.asn = coerceInt(raw.ASN);\n this.operatingSystem = coerceString(raw.operating_system);\n this.browser = coerceString(raw.browser);\n this.deviceBrand = coerceString(raw.device_brand);\n this.deviceModel = coerceString(raw.device_model);\n }\n\n static fromData(data: Raw, creditsRemaining: number | null = null): IpResult {\n return new IpResult(asRaw(data), creditsRemaining);\n }\n\n protected defaultFlagOn(): readonly string[] {\n return IpResult.DEFAULT_FLAG_ON;\n }\n}\n\nexport class EmailResult extends FlaggableResult {\n static readonly DEFAULT_FLAG_ON: readonly string[] = [\"disposable\"];\n\n readonly fraudScore: number;\n readonly valid: boolean | null;\n readonly disposable: boolean | null;\n readonly catchAll: boolean | null;\n readonly generic: boolean | null;\n readonly common: boolean | null;\n readonly dnsValid: boolean | null;\n readonly honeypot: boolean | null;\n readonly frequentComplainer: boolean | null;\n readonly suspect: boolean | null;\n readonly recentAbuse: boolean | null;\n readonly leaked: boolean | null;\n readonly riskyTld: boolean | null;\n readonly spfRecord: boolean | null;\n readonly dmarcRecord: boolean | null;\n readonly smtpScore: number | null;\n readonly overallScore: number | null;\n readonly deliverability: string | null;\n readonly spamTrapScore: string | null;\n readonly resultType: string | null;\n readonly sanitizedEmail: string | null;\n readonly firstSeen: DateInfo | null;\n readonly domainAge: DateInfo | null;\n\n private constructor(raw: Raw, creditsRemaining: number | null) {\n super(raw, creditsRemaining);\n this.fraudScore = coerceInt(raw.fraud_score) ?? 0;\n this.valid = coerceBool(raw.valid);\n this.disposable = coerceBool(raw.disposable);\n this.catchAll = coerceBool(raw.catch_all);\n this.generic = coerceBool(raw.generic);\n this.common = coerceBool(raw.common);\n this.dnsValid = coerceBool(raw.dns_valid);\n this.honeypot = coerceBool(raw.honeypot);\n this.frequentComplainer = coerceBool(raw.frequent_complainer);\n this.suspect = coerceBool(raw.suspect);\n this.recentAbuse = coerceBool(raw.recent_abuse);\n this.leaked = coerceBool(raw.leaked);\n this.riskyTld = coerceBool(raw.risky_tld);\n this.spfRecord = coerceBool(raw.spf_record);\n this.dmarcRecord = coerceBool(raw.dmarc_record);\n this.smtpScore = coerceInt(raw.smtp_score);\n this.overallScore = coerceInt(raw.overall_score);\n this.deliverability = coerceString(raw.deliverability);\n this.spamTrapScore = coerceString(raw.spam_trap_score);\n this.resultType = coerceString(raw.result_type);\n this.sanitizedEmail = coerceString(raw.sanitized_email);\n this.firstSeen = parseDateInfo(raw.first_seen);\n this.domainAge = parseDateInfo(raw.domain_age);\n }\n\n static fromData(data: Raw, creditsRemaining: number | null = null): EmailResult {\n return new EmailResult(asRaw(data), creditsRemaining);\n }\n\n protected defaultFlagOn(): readonly string[] {\n return EmailResult.DEFAULT_FLAG_ON;\n }\n}\n\nexport class PhoneResult extends FlaggableResult {\n static readonly DEFAULT_FLAG_ON: readonly string[] = [\"VOIP\", \"risky\"];\n\n readonly fraudScore: number;\n readonly valid: boolean | null;\n readonly recentAbuse: boolean | null;\n readonly voip: boolean | null;\n readonly prepaid: boolean | null;\n readonly risky: boolean | null;\n readonly active: boolean | null;\n readonly doNotCall: boolean | null;\n readonly leaked: boolean | null;\n readonly spammer: boolean | null;\n readonly dialingCode: number | null;\n readonly carrier: string | null;\n readonly lineType: string | null;\n readonly country: string | null;\n readonly region: string | null;\n readonly city: string | null;\n readonly timezone: string | null;\n readonly zipCode: string | null;\n readonly activeStatus: string | null;\n readonly formatted: string | null;\n readonly localFormat: string | null;\n\n private constructor(raw: Raw, creditsRemaining: number | null) {\n super(raw, creditsRemaining);\n this.fraudScore = coerceInt(raw.fraud_score) ?? 0;\n this.valid = coerceBool(raw.valid);\n this.recentAbuse = coerceBool(raw.recent_abuse);\n this.voip = coerceBool(raw.VOIP);\n this.prepaid = coerceBool(raw.prepaid);\n this.risky = coerceBool(raw.risky);\n this.active = coerceBool(raw.active);\n this.doNotCall = coerceBool(raw.do_not_call);\n this.leaked = coerceBool(raw.leaked);\n this.spammer = coerceBool(raw.spammer);\n this.dialingCode = coerceInt(raw.dialing_code);\n this.carrier = coerceString(raw.carrier);\n this.lineType = coerceString(raw.line_type);\n this.country = coerceString(raw.country);\n this.region = coerceString(raw.region);\n this.city = coerceString(raw.city);\n this.timezone = coerceString(raw.timezone);\n this.zipCode = coerceString(raw.zip_code);\n this.activeStatus = coerceString(raw.active_status);\n this.formatted = coerceString(raw.formatted);\n this.localFormat = coerceString(raw.local_format);\n }\n\n static fromData(data: Raw, creditsRemaining: number | null = null): PhoneResult {\n return new PhoneResult(asRaw(data), creditsRemaining);\n }\n\n protected defaultFlagOn(): readonly string[] {\n return PhoneResult.DEFAULT_FLAG_ON;\n }\n}\n\nexport type Result = IpResult | EmailResult | PhoneResult;\n\nexport class BatchItem {\n constructor(\n readonly subject: string,\n readonly success: boolean,\n readonly result: Result | null,\n readonly error: string | null,\n ) {}\n\n isInvalid(): boolean {\n return (\n this.error === \"invalid_ip\" ||\n this.error === \"invalid_email\" ||\n this.error === \"invalid_phone\"\n );\n }\n\n isInsufficientCredits(): boolean {\n return this.error === \"insufficient_credits\";\n }\n\n isUpstreamFailed(): boolean {\n return this.error === \"upstream_failed\";\n }\n}\n\nexport class BatchResult {\n constructor(\n readonly items: BatchItem[],\n readonly creditsRemaining: number,\n ) {}\n\n static fromEnvelope(envelope: Raw, kind: CheckKind): BatchResult {\n const items: BatchItem[] = [];\n const rawItems = envelope.results;\n if (Array.isArray(rawItems)) {\n for (const item of rawItems) {\n if (!isPlainObject(item)) {\n continue;\n }\n const success = coerceBool(item.success) ?? false;\n const data = item.data;\n let result: Result | null = null;\n if (success && isPlainObject(data)) {\n result =\n kind === \"ip\"\n ? IpResult.fromData(data)\n : kind === \"email\"\n ? EmailResult.fromData(data)\n : PhoneResult.fromData(data);\n }\n items.push(\n new BatchItem(coerceString(item[kind]) ?? \"\", success, result, coerceString(item.error)),\n );\n }\n }\n return new BatchResult(items, coerceInt(envelope.credits_remaining) ?? 0);\n }\n\n successful(): BatchItem[] {\n return this.items.filter((i) => i.success);\n }\n\n failed(): BatchItem[] {\n return this.items.filter((i) => !i.success);\n }\n\n hasFailures(): boolean {\n return this.items.some((i) => !i.success);\n }\n\n get length(): number {\n return this.items.length;\n }\n\n *[Symbol.iterator](): Iterator<BatchItem> {\n yield* this.items;\n }\n}\n","import { InvalidArgumentError, type RateLimitError } from \"./errors.js\";\n\nconst MONTH = \"(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\";\nconst SHORT_DAY = \"(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun)\";\nconst LONG_DAY = \"(?:Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday)\";\nconst TIME = \"[0-9]{2}:[0-9]{2}:[0-9]{2}\";\n\n// RFC 7231 HTTP-date: IMF-fixdate, then the obsolete RFC 850 and asctime forms.\nconst IMF_FIXDATE = new RegExp(`^${SHORT_DAY}, [0-9]{2} ${MONTH} [0-9]{4} ${TIME} GMT$`, \"i\");\nconst RFC_850 = new RegExp(`^${LONG_DAY}, [0-9]{2}-${MONTH}-[0-9]{2} ${TIME} GMT$`, \"i\");\nconst ASCTIME = new RegExp(`^${SHORT_DAY} ${MONTH} (?: [0-9]|[0-9]{2}) ${TIME} [0-9]{4}$`, \"i\");\n\n/**\n * Parse a Retry-After header into whole seconds.\n *\n * Accepts delta-seconds (digits only) or an RFC 7231 HTTP-date in one of its three\n * forms: IMF-fixdate (`Sun, 06 Nov 1994 08:49:37 GMT`), RFC 850\n * (`Sunday, 06-Nov-94 08:49:37 GMT`) or asctime (`Sun Nov 6 08:49:37 1994`, read as GMT).\n * Anything else, or a day or time that `Date.parse` rejects (such as day 99), returns null.\n */\nexport function parseRetryAfter(value: string | null, now: Date = new Date()): number | null {\n if (value === null) return null;\n const text = value.trim();\n if (text === \"\") return null;\n if (/^[0-9]+$/.test(text)) return Number.parseInt(text, 10);\n let when: number;\n if (IMF_FIXDATE.test(text) || RFC_850.test(text)) when = Date.parse(text);\n else if (ASCTIME.test(text)) when = Date.parse(`${text} GMT`);\n else return null;\n if (Number.isNaN(when)) return null;\n return Math.max(0, Math.ceil((when - now.getTime()) / 1000));\n}\n\nexport interface RetryPolicyOptions {\n maxAttempts?: number;\n concurrencyDelays?: readonly number[];\n rateLimitDelays?: readonly number[];\n honourRetryAfter?: boolean;\n maxRetryAfter?: number;\n sleep?: (seconds: number) => Promise<void>;\n}\n\nconst defaultSleep = (seconds: number): Promise<void> =>\n new Promise((resolve) => setTimeout(resolve, seconds * 1000));\n\n/** Opt-in retry policy for rate-limited (429) requests. All delays are in seconds. */\nexport class RetryPolicy {\n readonly maxAttempts: number;\n readonly concurrencyDelays: readonly number[];\n readonly rateLimitDelays: readonly number[];\n readonly honourRetryAfter: boolean;\n readonly maxRetryAfter: number;\n readonly #sleeper: (seconds: number) => Promise<void>;\n\n constructor(options: RetryPolicyOptions = {}) {\n this.maxAttempts = options.maxAttempts ?? 3;\n this.concurrencyDelays = [...(options.concurrencyDelays ?? [1, 2, 4])];\n this.rateLimitDelays = [...(options.rateLimitDelays ?? [5, 15, 40])];\n this.honourRetryAfter = options.honourRetryAfter ?? true;\n this.maxRetryAfter = options.maxRetryAfter ?? 60;\n this.#sleeper = options.sleep ?? defaultSleep;\n if (!Number.isInteger(this.maxAttempts) || this.maxAttempts < 1) {\n throw new InvalidArgumentError(\"maxAttempts must be an integer of at least 1.\");\n }\n if (!Number.isFinite(this.maxRetryAfter) || this.maxRetryAfter < 0) {\n throw new InvalidArgumentError(\"maxRetryAfter must be a finite number that is not negative.\");\n }\n for (const [name, delays] of [\n [\"concurrencyDelays\", this.concurrencyDelays],\n [\"rateLimitDelays\", this.rateLimitDelays],\n ] as const) {\n if (\n !delays.every((delay) => typeof delay === \"number\" && Number.isFinite(delay) && delay >= 0)\n ) {\n throw new InvalidArgumentError(\n `${name} must contain only finite numbers that are not negative.`,\n );\n }\n }\n }\n\n /** Seconds to wait before retrying after `attempt` (1-based) failed, or null to stop. */\n delayFor(error: RateLimitError, attempt: number): number | null {\n if (attempt >= this.maxAttempts) return null;\n if (this.honourRetryAfter && error.retryAfter !== null) {\n return error.retryAfter > this.maxRetryAfter ? null : error.retryAfter;\n }\n const schedule = error.isConcurrencyLimit ? this.concurrencyDelays : this.rateLimitDelays;\n const delay = schedule[attempt - 1];\n return delay === undefined ? null : delay;\n }\n\n sleep(seconds: number): Promise<void> {\n return this.#sleeper(seconds);\n }\n}\n","import {\n ApiError,\n InvalidArgumentError,\n RateLimitError,\n TransportError,\n UnexpectedResponseError,\n maskApiKey,\n} from \"./errors.js\";\nimport { type RetryPolicy, parseRetryAfter } from \"./retry.js\";\nimport { VERSION } from \"./version.js\";\n\n/** A `fetch`-compatible function. */\nexport type FetchLike = (url: string, init: RequestInit) => Promise<Response>;\n\nexport interface TransportOptions {\n apiKey: string;\n baseUrl: string;\n timeout: number;\n retry: RetryPolicy | null;\n fetch: FetchLike | null;\n}\n\ntype QueryParams = Record<string, string | number | boolean | null | undefined>;\n\nfunction userAgent(): string {\n let agent = `fraudnova-node/${VERSION}`;\n if (typeof process !== \"undefined\" && typeof process.versions?.node === \"string\") {\n agent += ` node/${process.versions.node}`;\n }\n return agent;\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n/** Internal HTTP layer: builds requests, decodes the envelope, maps errors, retries 429s. */\nexport class Transport {\n readonly baseUrl: string;\n readonly #apiKey: string;\n readonly #timeout: number;\n readonly #retry: RetryPolicy | null;\n readonly #fetch: FetchLike;\n readonly #userAgent: string;\n\n constructor(options: TransportOptions) {\n if (!/^[\\x21-\\x7e]+$/.test(options.apiKey)) {\n throw new InvalidArgumentError(\n \"apiKey must contain only printable ASCII characters, without whitespace.\",\n );\n }\n let fetchImpl: FetchLike | null = options.fetch ?? null;\n if (fetchImpl === null) {\n const globalFetch = (globalThis as { fetch?: typeof globalThis.fetch }).fetch;\n if (typeof globalFetch === \"function\") {\n fetchImpl = globalFetch.bind(globalThis);\n }\n }\n if (fetchImpl === null) {\n throw new InvalidArgumentError(\n \"No fetch implementation available; pass one in the fetch option\",\n );\n }\n this.#apiKey = options.apiKey;\n this.baseUrl = options.baseUrl.replace(/\\/+$/, \"\");\n this.#timeout = options.timeout;\n this.#retry = options.retry;\n this.#fetch = fetchImpl;\n this.#userAgent = userAgent();\n }\n\n get(\n path: string,\n params: QueryParams | null = null,\n authenticated = true,\n ): Promise<Record<string, unknown>> {\n const query = new URLSearchParams();\n for (const [name, value] of Object.entries(params ?? {})) {\n if (value === undefined || value === null) continue;\n query.append(name, String(value));\n }\n const search = query.toString();\n const url = this.baseUrl + path + (search !== \"\" ? `?${search}` : \"\");\n return this.#send(\"GET\", url, authenticated);\n }\n\n post(path: string, json: Record<string, unknown>): Promise<Record<string, unknown>> {\n return this.#send(\"POST\", this.baseUrl + path, true, JSON.stringify(json));\n }\n\n #headers(authenticated: boolean, hasBody: boolean): Record<string, string> {\n const headers: Record<string, string> = {\n Accept: \"application/json\",\n \"User-Agent\": this.#userAgent,\n };\n if (authenticated) headers.Authorization = `Bearer ${this.#apiKey}`;\n if (hasBody) headers[\"Content-Type\"] = \"application/json\";\n return headers;\n }\n\n async #send(\n method: string,\n url: string,\n authenticated: boolean,\n body?: string,\n ): Promise<Record<string, unknown>> {\n let attempt = 1;\n for (;;) {\n const init: RequestInit = {\n method,\n headers: this.#headers(authenticated, body !== undefined),\n };\n if (body !== undefined) init.body = body;\n\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.#timeout);\n let response: Response;\n let text: string;\n try {\n response = await this.#fetch(url, { ...init, signal: controller.signal });\n text = await response.text();\n } catch (error) {\n if (controller.signal.aborted) {\n throw new TransportError(`FraudNova request timed out after ${this.#timeout} ms`, {\n cause: error,\n });\n }\n const original = error instanceof Error ? error.message : String(error);\n const masked = original.split(this.#apiKey).join(maskApiKey(this.#apiKey));\n throw new TransportError(`FraudNova request failed: ${masked}`, { cause: error });\n } finally {\n clearTimeout(timer);\n }\n\n try {\n return Transport.#decode(response, text);\n } catch (error) {\n if (!(error instanceof RateLimitError)) throw error;\n const delay = this.#retry?.delayFor(error, attempt) ?? null;\n if (this.#retry === null || delay === null) throw error;\n await this.#retry.sleep(delay);\n attempt++;\n }\n }\n }\n\n static #decode(response: Response, text: string): Record<string, unknown> {\n const status = response.status;\n if (status < 200 || status >= 300) {\n throw ApiError.fromResponse(\n status,\n text,\n parseRetryAfter(response.headers.get(\"retry-after\")),\n );\n }\n let decoded: unknown = null;\n try {\n decoded = JSON.parse(text);\n } catch {\n decoded = null;\n }\n if (!isPlainObject(decoded) || decoded.success !== true) {\n throw new UnexpectedResponseError(\n status,\n \"Unexpected response body\",\n isPlainObject(decoded) ? decoded : {},\n );\n }\n return decoded;\n }\n}\n","import { InvalidArgumentError, UnexpectedResponseError, maskApiKey } from \"./errors.js\";\nimport {\n BatchItem,\n BatchResult,\n EmailResult,\n IpResult,\n PhoneResult,\n coerceInt,\n type CheckKind,\n} from \"./results.js\";\nimport type { RetryPolicy } from \"./retry.js\";\nimport { Transport, type FetchLike } from \"./transport.js\";\n\n/** The production API base URL. */\nexport const DEFAULT_BASE_URL = \"https://fraudnova.com/api/v1\";\n\n/** The most subjects the API accepts in one batch request; larger arrays are chunked. */\nexport const BATCH_MAX = 100;\n\nconst DEFAULT_TIMEOUT = 30_000;\n\nconst inspectSymbol = Symbol.for(\"nodejs.util.inspect.custom\");\n\nexport interface FraudNovaOptions {\n /** API base URL; defaults to {@link DEFAULT_BASE_URL}. */\n baseUrl?: string;\n /** Per-request timeout in milliseconds; defaults to 30 000. */\n timeout?: number;\n /** Opt-in retry policy for 429 responses. */\n retry?: RetryPolicy;\n /** A fetch-compatible function; defaults to the global `fetch`. */\n fetch?: FetchLike;\n}\n\nexport interface IpCheckOptions {\n strictness?: 0 | 1 | 2 | 3;\n mobile?: boolean;\n userAgent?: string;\n userLanguage?: string;\n}\n\nexport interface PhoneCheckOptions {\n country?: string;\n}\n\ntype Envelope = Record<string, unknown>;\n\nfunction subject(value: unknown, name: string): string {\n if (typeof value !== \"string\" || value.trim() === \"\") {\n throw new InvalidArgumentError(`${name} must be a non-empty string`);\n }\n return value.trim();\n}\n\nfunction strictness(value: unknown): number | undefined {\n if (value === undefined) return undefined;\n if (typeof value !== \"number\" || !Number.isInteger(value) || value < 0 || value > 3) {\n throw new InvalidArgumentError(\"strictness must be an integer from 0 to 3\");\n }\n return value;\n}\n\nfunction country(value: unknown): string | undefined {\n if (typeof value !== \"string\") return undefined;\n const cleaned = value.trim().toUpperCase();\n return cleaned !== \"\" ? cleaned : undefined;\n}\n\nfunction data(envelope: Envelope): Record<string, unknown> {\n const value = envelope.data;\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n throw new UnexpectedResponseError(200, \"Missing data\", envelope);\n }\n return value as Record<string, unknown>;\n}\n\n/** Build the IP options shared by the single and batch calls, keeping only defined values. */\nfunction ipOptions(options: IpCheckOptions): Record<string, number | boolean | string> {\n const out: Record<string, number | boolean | string> = {};\n const level = strictness(options.strictness);\n if (level !== undefined) out.strictness = level;\n if (options.mobile === true || options.mobile === false) out.mobile = options.mobile;\n if (options.userAgent !== undefined) out.user_agent = options.userAgent;\n if (options.userLanguage !== undefined) out.user_language = options.userLanguage;\n return out;\n}\n\n/** The FraudNova API client. */\nexport class FraudNova {\n readonly baseUrl: string;\n readonly #apiKey: string;\n readonly #transport: Transport;\n\n constructor(apiKey: string, options: FraudNovaOptions = {}) {\n if (typeof apiKey !== \"string\" || apiKey.trim() === \"\") {\n throw new InvalidArgumentError(\"apiKey must be a non-empty string\");\n }\n const baseUrl = options.baseUrl ?? DEFAULT_BASE_URL;\n if (typeof baseUrl !== \"string\" || !/^https?:\\/\\//i.test(baseUrl)) {\n throw new InvalidArgumentError(\"baseUrl must start with http:// or https://\");\n }\n const timeout = options.timeout ?? DEFAULT_TIMEOUT;\n if (\n typeof timeout !== \"number\" ||\n !Number.isFinite(timeout) ||\n timeout <= 0 ||\n timeout > 2_147_483_647\n ) {\n throw new InvalidArgumentError(\n \"timeout must be a positive number of milliseconds (at most 2147483647)\",\n );\n }\n this.#apiKey = apiKey.trim();\n this.#transport = new Transport({\n apiKey: this.#apiKey,\n baseUrl,\n timeout,\n retry: options.retry ?? null,\n fetch: options.fetch ?? null,\n });\n this.baseUrl = this.#transport.baseUrl;\n }\n\n /** Score one IP address. */\n async checkIp(ip: string, options: IpCheckOptions = {}): Promise<IpResult> {\n const params = { ip: subject(ip, \"ip\"), ...ipOptions(options) };\n const envelope = await this.#transport.get(\"/check\", params);\n return IpResult.fromData(data(envelope), coerceInt(envelope.credits_remaining));\n }\n\n /** Score one email address. */\n async checkEmail(email: string): Promise<EmailResult> {\n const params = { email: subject(email, \"email\") };\n const envelope = await this.#transport.get(\"/check/email\", params);\n return EmailResult.fromData(data(envelope), coerceInt(envelope.credits_remaining));\n }\n\n /** Score one phone number. */\n async checkPhone(phone: string, options: PhoneCheckOptions = {}): Promise<PhoneResult> {\n const params = { phone: subject(phone, \"phone\"), country: country(options.country) };\n const envelope = await this.#transport.get(\"/check/phone\", params);\n return PhoneResult.fromData(data(envelope), coerceInt(envelope.credits_remaining));\n }\n\n /** Score many IP addresses, chunked by {@link BATCH_MAX}. */\n async checkIps(\n ips: ReadonlyArray<string | null>,\n options: IpCheckOptions = {},\n ): Promise<BatchResult> {\n return this.#batch(\"/check/batch\", \"ips\", \"ip\", ips, ipOptions(options));\n }\n\n /** Score many email addresses, chunked by {@link BATCH_MAX}. */\n async checkEmails(emails: ReadonlyArray<string | null>): Promise<BatchResult> {\n return this.#batch(\"/check/email/batch\", \"emails\", \"email\", emails, {});\n }\n\n /** Score many phone numbers, chunked by {@link BATCH_MAX}. */\n async checkPhones(\n phones: ReadonlyArray<string | null>,\n options: PhoneCheckOptions = {},\n ): Promise<BatchResult> {\n const code = country(options.country);\n const extra: Record<string, string> = code !== undefined ? { country: code } : {};\n return this.#batch(\"/check/phone/batch\", \"phones\", \"phone\", phones, extra);\n }\n\n /** The account's remaining credits. */\n async balance(): Promise<number> {\n const envelope = await this.#transport.get(\"/balance\");\n const credits = coerceInt(envelope.credits_remaining);\n if (credits === null) {\n throw new UnexpectedResponseError(200, \"Missing credits_remaining\", envelope);\n }\n return credits;\n }\n\n /** The caller's public IP address as the API sees it (free, unauthenticated). */\n async myIp(): Promise<string> {\n const envelope = await this.#transport.get(\"/my-ip\", null, false);\n const ip = envelope.ip;\n if (typeof ip !== \"string\" || ip === \"\") {\n throw new UnexpectedResponseError(200, \"Missing ip\", envelope);\n }\n return ip;\n }\n\n async #batch(\n path: string,\n field: string,\n kind: CheckKind,\n subjects: ReadonlyArray<string | null>,\n extra: Record<string, unknown>,\n ): Promise<BatchResult> {\n if (!Array.isArray(subjects)) {\n throw new InvalidArgumentError(`${field} must be an array of values`);\n }\n if (subjects.length === 0) {\n throw new InvalidArgumentError(`${field} must not be empty`);\n }\n const cleaned: Array<string | null> = subjects.map((item: unknown, index) => {\n if (item !== null && typeof item !== \"string\") {\n throw new InvalidArgumentError(`${field}[${index}] must be a string or null`);\n }\n return item === null ? null : item.trim();\n });\n\n const items: BatchItem[] = [];\n let credits = 0;\n for (let start = 0; start < cleaned.length; start += BATCH_MAX) {\n const chunk = cleaned.slice(start, start + BATCH_MAX);\n const envelope = await this.#transport.post(path, { [field]: chunk, ...extra });\n if (!Array.isArray(envelope.results)) {\n throw new UnexpectedResponseError(200, \"Missing results\", envelope);\n }\n const part = BatchResult.fromEnvelope(envelope, kind);\n items.push(...part.items);\n credits = part.creditsRemaining;\n const rest = cleaned.slice(start + BATCH_MAX);\n if (rest.length > 0 && coerceInt(envelope.credits_remaining) === 0) {\n // The API answers 402 to a request sent with a zero balance, which would discard\n // the results above; mark the unsent items the way the API does.\n for (const item of rest) {\n items.push(new BatchItem(item ?? \"\", false, null, \"insufficient_credits\"));\n }\n break;\n }\n }\n return new BatchResult(items, credits);\n }\n\n toString(): string {\n return `FraudNova { apiKey: \"${maskApiKey(this.#apiKey)}\", baseUrl: \"${this.baseUrl}\" }`;\n }\n\n [inspectSymbol](): string {\n return this.toString();\n }\n}\n"],"mappings":"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","names":["data","subject","isPlainObject"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fraudnova/sdk",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "Official Node.js client for the FraudNova fraud-detection API (IP, email and phone checks)",
5
5
  "license": "MIT",
6
6
  "author": {