@emailcheck/email-validator-js 5.0.0-beta.2 → 5.1.0-beta.1
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 +40 -4
- package/dist/cli/index.js +34 -2
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.esm.js +36 -3
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +36 -2
- package/dist/index.js.map +1 -1
- package/dist/sender-strategy.d.ts +26 -0
- package/dist/serverless/adapters/aws-lambda.cjs.js +5 -5
- package/dist/serverless/adapters/aws-lambda.cjs.js.map +1 -1
- package/dist/serverless/adapters/aws-lambda.esm.js +5 -5
- package/dist/serverless/adapters/aws-lambda.esm.js.map +1 -1
- package/dist/serverless/adapters/azure.cjs.js +5 -5
- package/dist/serverless/adapters/azure.cjs.js.map +1 -1
- package/dist/serverless/adapters/azure.esm.js +5 -5
- package/dist/serverless/adapters/azure.esm.js.map +1 -1
- package/dist/serverless/adapters/cloudflare.cjs.js +5 -5
- package/dist/serverless/adapters/cloudflare.cjs.js.map +1 -1
- package/dist/serverless/adapters/cloudflare.esm.js +5 -5
- package/dist/serverless/adapters/cloudflare.esm.js.map +1 -1
- package/dist/serverless/adapters/gcp.cjs.js +5 -5
- package/dist/serverless/adapters/gcp.cjs.js.map +1 -1
- package/dist/serverless/adapters/gcp.esm.js +5 -5
- package/dist/serverless/adapters/gcp.esm.js.map +1 -1
- package/dist/serverless/adapters/netlify.cjs.js +5 -5
- package/dist/serverless/adapters/netlify.cjs.js.map +1 -1
- package/dist/serverless/adapters/netlify.esm.js +5 -5
- package/dist/serverless/adapters/netlify.esm.js.map +1 -1
- package/dist/serverless/adapters/vercel.cjs.js +5 -5
- package/dist/serverless/adapters/vercel.cjs.js.map +1 -1
- package/dist/serverless/adapters/vercel.esm.js +5 -5
- package/dist/serverless/adapters/vercel.esm.js.map +1 -1
- package/dist/serverless/index.cjs.js +5 -5
- package/dist/serverless/index.cjs.js.map +1 -1
- package/dist/serverless/index.esm.js +5 -5
- package/dist/serverless/index.esm.js.map +1 -1
- package/dist/serverless/verifier.cjs.js +5 -5
- package/dist/serverless/verifier.cjs.js.map +1 -1
- package/dist/serverless/verifier.esm.js +5 -5
- package/dist/serverless/verifier.esm.js.map +1 -1
- package/dist/serverless/verifier.min.js +1 -1
- package/dist/types.d.ts +72 -0
- package/package.json +3 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"verifier.cjs.js","sources":["../../node_modules/string-similarity-js/dist/string-similarity.js","../../src/serverless/verifier.ts"],"sourcesContent":["\"use strict\";\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.stringSimilarity = void 0;\n/* global exports, Map */\n/**\n * Calculate similarity between two strings\n * @param {string} str1 First string to match\n * @param {string} str2 Second string to match\n * @param {number} [substringLength=2] Optional. Length of substring to be used in calculating similarity. Default 2.\n * @param {boolean} [caseSensitive=false] Optional. Whether you want to consider case in string matching. Default false;\n * @returns Number between 0 and 1, with 0 being a low match score.\n */\nvar stringSimilarity = function (str1, str2, substringLength, caseSensitive) {\n if (substringLength === void 0) { substringLength = 2; }\n if (caseSensitive === void 0) { caseSensitive = false; }\n if (!caseSensitive) {\n str1 = str1.toLowerCase();\n str2 = str2.toLowerCase();\n }\n if (str1.length < substringLength || str2.length < substringLength)\n return 0;\n var map = new Map();\n for (var i = 0; i < str1.length - (substringLength - 1); i++) {\n var substr1 = str1.substr(i, substringLength);\n map.set(substr1, map.has(substr1) ? map.get(substr1) + 1 : 1);\n }\n var match = 0;\n for (var j = 0; j < str2.length - (substringLength - 1); j++) {\n var substr2 = str2.substr(j, substringLength);\n var count = map.has(substr2) ? map.get(substr2) : 0;\n if (count > 0) {\n map.set(substr2, count - 1);\n match++;\n }\n }\n return (match * 2) / (str1.length + str2.length - ((substringLength - 1) * 2));\n};\nexports.stringSimilarity = stringSimilarity;\nexports.default = exports.stringSimilarity;\n//# sourceMappingURL=string-similarity.js.map","/**\n * Edge-runtime / serverless email validator.\n *\n * No Node.js APIs (no `node:net`, no `node:dns`) — DNS is delegated to a\n * caller-supplied `DNSResolver`, so the same code runs on Cloudflare Workers,\n * Vercel Edge, Lambda@Edge, and Deno Deploy.\n *\n * Shares data with the main validator: `commonEmailDomains` and the typo map\n * are imported from `src/data/`, so we never drift between the two surfaces.\n */\n\nimport { stringSimilarity } from 'string-similarity-js';\nimport commonEmailDomainsJson from '../data/common-email-domains.json';\nimport typoPatternsJson from '../data/typo-patterns.json';\nimport disposableProviders from '../disposable-email-providers.json';\nimport freeProviders from '../free-email-providers.json';\nimport type { DomainSuggesterOptions, EmailValidationResult, ValidateEmailOptions } from '../types';\n\n/** Compact LRU/TTL cache. One Map, expiry stamp per entry, batched eviction. */\nexport class EdgeCache<T> {\n private readonly cache = new Map<string, { value: T; expires: number }>();\n\n constructor(\n private readonly maxSize = 1000,\n private readonly ttl = 3_600_000\n ) {}\n\n get(key: string): T | undefined {\n const item = this.cache.get(key);\n if (!item) return undefined;\n if (Date.now() > item.expires) {\n this.cache.delete(key);\n return undefined;\n }\n return item.value;\n }\n\n set(key: string, value: T): void {\n if (this.cache.size >= this.maxSize) this.evict();\n this.cache.set(key, { value, expires: Date.now() + this.ttl });\n }\n\n clear(): void {\n this.cache.clear();\n }\n\n size(): number {\n return this.cache.size;\n }\n\n private evict(): void {\n // Drop the oldest 10% in one pass — Map preserves insertion order so\n // `keys()` walks oldest-first.\n const drop = Math.max(1, Math.floor(this.maxSize * 0.1));\n let n = 0;\n for (const key of this.cache.keys()) {\n if (n++ >= drop) break;\n this.cache.delete(key);\n }\n }\n}\n\n// Module-level per-isolate caches. Edge runtimes get cold-start fresh; warm\n// invocations benefit from the in-memory hits.\nexport const validationCache = new EdgeCache<EmailValidationResult>(1000);\nexport const mxCache = new EdgeCache<string[]>(500);\n\n/** Same regex the main validator uses — kept inline because edge runtimes don't auto-resolve psl. */\nconst VALID_EMAIL_REGEX =\n /^(([a-zA-Z0-9_+'-]+(\\.[a-zA-Z0-9_+'-]+)*)|(\".+\"))@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}])|(([a-zA-Z0-9-]+\\.)+[a-zA-Z]{2,}))$/;\n\n/**\n * Common email domains — re-exported so callers (Vercel Edge, etc.) can pass a\n * custom subset via `DomainSuggesterOptions.customDomains`.\n */\nexport const COMMON_DOMAINS: readonly string[] = commonEmailDomainsJson as string[];\n\nconst TYPO_PATTERNS = typoPatternsJson as Record<string, string[]>;\n/** Reverse index for O(1) typo → canonical lookup. */\nconst TYPO_LOOKUP = new Map<string, string>();\nfor (const [canonical, typos] of Object.entries(TYPO_PATTERNS)) {\n for (const typo of typos) TYPO_LOOKUP.set(typo, canonical);\n}\n\n/** DNS resolver contract — caller-supplied so we don't import `node:dns`. */\nexport interface DNSResolver {\n resolveMx(domain: string): Promise<Array<{ exchange: string; priority: number }>>;\n resolveTxt(domain: string): Promise<string[]>;\n}\n\n/** No-op resolver for environments where DNS isn't available. */\nexport class StubDNSResolver implements DNSResolver {\n async resolveMx(): Promise<Array<{ exchange: string; priority: number }>> {\n return [];\n }\n async resolveTxt(): Promise<string[]> {\n return [];\n }\n}\n\n/**\n * DNS-over-HTTPS resolver — works in any runtime with `fetch` (Cloudflare\n * Workers, Vercel Edge, Deno, browsers, Node 18+). Defaults to Cloudflare's\n * 1.1.1.1 endpoint; pass `endpoint` to use Google (8.8.8.8), NextDNS, or\n * a self-hosted resolver.\n *\n * Compatible with [`cf-doh`](https://www.npmjs.com/package/cf-doh) — if you\n * already use that, drop it in directly. This built-in keeps the package\n * zero-dep so the same code works on every edge runtime without an extra\n * install step.\n */\nexport interface DoHResolverOptions {\n /** DoH endpoint URL. Default: https://cloudflare-dns.com/dns-query */\n endpoint?: string;\n /** Per-query request timeout, ms. Default: 5000 */\n timeoutMs?: number;\n /** Custom fetch (e.g. to add headers / proxy). Default: globalThis.fetch */\n fetch?: typeof fetch;\n}\n\ninterface DoHAnswer {\n name: string;\n type: number;\n TTL: number;\n data: string;\n}\n\ninterface DoHResponse {\n Status: number;\n Answer?: DoHAnswer[];\n}\n\nconst DOH_RECORD_TYPE = { MX: 15, TXT: 16 } as const;\n\nexport class DoHResolver implements DNSResolver {\n private readonly endpoint: string;\n private readonly timeoutMs: number;\n private readonly fetchFn: typeof fetch;\n\n constructor(options: DoHResolverOptions = {}) {\n this.endpoint = options.endpoint ?? 'https://cloudflare-dns.com/dns-query';\n this.timeoutMs = options.timeoutMs ?? 5000;\n this.fetchFn = options.fetch ?? globalThis.fetch;\n }\n\n async resolveMx(domain: string): Promise<Array<{ exchange: string; priority: number }>> {\n const records = await this.query(domain, DOH_RECORD_TYPE.MX);\n if (!records) return [];\n return records\n .map((answer) => {\n // MX answer data: \"<priority> <exchange>\" (with optional trailing dot).\n const match = answer.data.match(/^(\\d+)\\s+(.+?)\\.?$/);\n if (!match) return null;\n return { priority: Number(match[1]), exchange: match[2] as string };\n })\n .filter((r): r is { exchange: string; priority: number } => r !== null)\n .sort((a, b) => a.priority - b.priority);\n }\n\n async resolveTxt(domain: string): Promise<string[]> {\n const records = await this.query(domain, DOH_RECORD_TYPE.TXT);\n if (!records) return [];\n // TXT answers come back as quoted strings — strip the surrounding quotes.\n return records.map((answer) => answer.data.replace(/^\"(.*)\"$/, '$1'));\n }\n\n private async query(domain: string, type: number): Promise<DoHAnswer[] | null> {\n const url = `${this.endpoint}?name=${encodeURIComponent(domain)}&type=${type}`;\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.timeoutMs);\n try {\n const response = await this.fetchFn(url, {\n headers: { Accept: 'application/dns-json' },\n signal: controller.signal,\n });\n if (!response.ok) return null;\n const json = (await response.json()) as DoHResponse;\n // Status 0 = NOERROR. Anything else (NXDOMAIN, SERVFAIL, etc.) → no answers.\n if (json.Status !== 0) return [];\n return json.Answer ?? [];\n } catch {\n return null;\n } finally {\n clearTimeout(timer);\n }\n }\n}\n\n/**\n * Suggest a corrected domain. Returns the canonical for a known typo,\n * otherwise the closest match within the threshold, otherwise null.\n */\nexport function suggestDomain(domain: string, options?: DomainSuggesterOptions): string | null {\n const lower = domain.toLowerCase();\n\n // Hand-curated typo map first — beats similarity for common cases.\n const known = TYPO_LOOKUP.get(lower);\n if (known) return known;\n\n const domains = options?.customDomains ?? COMMON_DOMAINS;\n if (domains.includes(lower)) return null;\n\n const threshold = options?.threshold ?? 2;\n let minDistance = Infinity;\n let suggestion: string | null = null;\n\n for (const candidate of domains) {\n const candidateLower = candidate.toLowerCase();\n if (lower === candidateLower) return null;\n const similarity = stringSimilarity(lower, candidateLower);\n const distance = Math.round((1 - similarity) * Math.max(domain.length, candidate.length));\n if (distance > 0 && distance <= threshold && distance < minDistance) {\n minDistance = distance;\n suggestion = candidate;\n }\n }\n return suggestion;\n}\n\n/**\n * Validate one email — syntax / typo / disposable / free / MX (if a resolver\n * is supplied). Each step is independently flag-gated so callers pay only for\n * what they use.\n */\nexport async function validateEmailCore(\n email: string,\n options?: ValidateEmailOptions & { dnsResolver?: DNSResolver }\n): Promise<EmailValidationResult> {\n const normalized = email.toLowerCase().trim();\n\n if (!options?.skipCache) {\n const cached = validationCache.get(normalized);\n if (cached) return cached;\n }\n\n const result: EmailValidationResult = { valid: false, email: normalized, validators: {} };\n\n if (options?.validateSyntax !== false) {\n const syntaxValid = VALID_EMAIL_REGEX.test(normalized);\n result.validators.syntax = { valid: syntaxValid };\n if (!syntaxValid) {\n validationCache.set(normalized, result);\n return result;\n }\n }\n\n const [local, domain] = normalized.split('@');\n result.local = local;\n result.domain = domain;\n\n if (options?.validateTypo !== false) {\n const suggestion = suggestDomain(domain, options?.domainSuggesterOptions);\n result.validators.typo = { valid: !suggestion, suggestion: suggestion ?? undefined };\n }\n\n if (options?.validateDisposable !== false) {\n result.validators.disposable = { valid: !disposableProviders.includes(domain) };\n }\n\n if (options?.validateFree !== false) {\n result.validators.free = { valid: !freeProviders.includes(domain) };\n }\n\n if (options?.validateMx && options.dnsResolver) {\n try {\n const records = await options.dnsResolver.resolveMx(domain);\n const hasMx = records.length > 0;\n result.validators.mx = {\n valid: hasMx,\n records: hasMx ? records.map((r) => r.exchange) : undefined,\n };\n } catch (error) {\n result.validators.mx = {\n valid: false,\n error: error instanceof Error ? error.message : 'MX validation failed',\n };\n }\n }\n\n // Free-provider detection is informational; only the hard validators gate validity.\n result.valid = (['syntax', 'typo', 'disposable', 'mx'] as const).every((key) => {\n const validator = result.validators[key];\n return !validator || validator.valid !== false;\n });\n\n if (!options?.skipCache) validationCache.set(normalized, result);\n return result;\n}\n\nexport async function validateEmailBatch(\n emails: string[],\n options?: ValidateEmailOptions & { dnsResolver?: DNSResolver }\n): Promise<EmailValidationResult[]> {\n const chunkSize = options?.batchSize ?? 10;\n const results: EmailValidationResult[] = [];\n for (let i = 0; i < emails.length; i += chunkSize) {\n const chunk = emails.slice(i, i + chunkSize);\n const chunkResults = await Promise.all(chunk.map((email) => validateEmailCore(email, options)));\n results.push(...chunkResults);\n }\n return results;\n}\n\nexport function clearCache(): void {\n validationCache.clear();\n mxCache.clear();\n}\n\nexport type { DomainSuggesterOptions, EmailValidationResult, ValidateEmailOptions } from '../types';\n"],"names":["exports","stringSimilarity"],"mappings":"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","x_google_ignoreList":[0]}
|
|
1
|
+
{"version":3,"file":"verifier.cjs.js","sources":["../../node_modules/string-similarity-js/dist/string-similarity.js","../../src/serverless/verifier.ts"],"sourcesContent":["\"use strict\";\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.stringSimilarity = void 0;\n/* global exports, Map */\n/**\n * Calculate similarity between two strings\n * @param {string} str1 First string to match\n * @param {string} str2 Second string to match\n * @param {number} [substringLength=2] Optional. Length of substring to be used in calculating similarity. Default 2.\n * @param {boolean} [caseSensitive=false] Optional. Whether you want to consider case in string matching. Default false;\n * @returns Number between 0 and 1, with 0 being a low match score.\n */\nvar stringSimilarity = function (str1, str2, substringLength, caseSensitive) {\n if (substringLength === void 0) { substringLength = 2; }\n if (caseSensitive === void 0) { caseSensitive = false; }\n if (!caseSensitive) {\n str1 = str1.toLowerCase();\n str2 = str2.toLowerCase();\n }\n if (str1.length < substringLength || str2.length < substringLength)\n return 0;\n var map = new Map();\n for (var i = 0; i < str1.length - (substringLength - 1); i++) {\n var substr1 = str1.substr(i, substringLength);\n map.set(substr1, map.has(substr1) ? map.get(substr1) + 1 : 1);\n }\n var match = 0;\n for (var j = 0; j < str2.length - (substringLength - 1); j++) {\n var substr2 = str2.substr(j, substringLength);\n var count = map.has(substr2) ? map.get(substr2) : 0;\n if (count > 0) {\n map.set(substr2, count - 1);\n match++;\n }\n }\n return (match * 2) / (str1.length + str2.length - ((substringLength - 1) * 2));\n};\nexports.stringSimilarity = stringSimilarity;\nexports.default = exports.stringSimilarity;\n//# sourceMappingURL=string-similarity.js.map","/**\n * Edge-runtime / serverless email validator.\n *\n * No Node.js APIs (no `node:net`, no `node:dns`) — DNS is delegated to a\n * caller-supplied `DNSResolver`, so the same code runs on Cloudflare Workers,\n * Vercel Edge, Lambda@Edge, and Deno Deploy.\n *\n * Shares data with the main validator: `commonEmailDomains` and the typo map\n * are imported from `src/data/`, so we never drift between the two surfaces.\n */\n\nimport { stringSimilarity } from 'string-similarity-js';\nimport commonEmailDomainsJson from '../data/common-email-domains.json';\nimport typoPatternsJson from '../data/typo-patterns.json';\nimport disposableProviders from '../disposable-email-providers.json';\nimport freeProviders from '../free-email-providers.json';\nimport type { DomainSuggesterOptions, EmailValidationResult, ValidateEmailOptions } from '../types';\n\n/** Compact LRU/TTL cache. One Map, expiry stamp per entry, batched eviction. */\nexport class EdgeCache<T> {\n private readonly cache = new Map<string, { value: T; expires: number }>();\n\n constructor(\n private readonly maxSize = 1000,\n private readonly ttl = 3_600_000\n ) {}\n\n get(key: string): T | undefined {\n const item = this.cache.get(key);\n if (!item) return undefined;\n if (Date.now() > item.expires) {\n this.cache.delete(key);\n return undefined;\n }\n return item.value;\n }\n\n set(key: string, value: T): void {\n if (this.cache.size >= this.maxSize) this.evict();\n this.cache.set(key, { value, expires: Date.now() + this.ttl });\n }\n\n clear(): void {\n this.cache.clear();\n }\n\n size(): number {\n return this.cache.size;\n }\n\n private evict(): void {\n // Drop the oldest 10% in one pass — Map preserves insertion order so\n // `keys()` walks oldest-first.\n const drop = Math.max(1, Math.floor(this.maxSize * 0.1));\n let n = 0;\n for (const key of this.cache.keys()) {\n if (n++ >= drop) break;\n this.cache.delete(key);\n }\n }\n}\n\n// Module-level per-isolate caches. Edge runtimes get cold-start fresh; warm\n// invocations benefit from the in-memory hits.\nexport const validationCache = new EdgeCache<EmailValidationResult>(1000);\nexport const mxCache = new EdgeCache<string[]>(500);\n\n/** Same regex the main validator uses — kept inline because edge runtimes don't auto-resolve psl. */\nconst VALID_EMAIL_REGEX =\n /^(([a-zA-Z0-9_+'-]+(\\.[a-zA-Z0-9_+'-]+)*)|(\".+\"))@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}])|(([a-zA-Z0-9-]+\\.)+[a-zA-Z]{2,}))$/;\n\n/**\n * Common email domains — re-exported so callers (Vercel Edge, etc.) can pass a\n * custom subset via `DomainSuggesterOptions.customDomains`.\n */\nexport const COMMON_DOMAINS: readonly string[] = commonEmailDomainsJson as string[];\n\nconst TYPO_PATTERNS = typoPatternsJson as Record<string, string[]>;\n/** Reverse index for O(1) typo → canonical lookup. */\nconst TYPO_LOOKUP = new Map<string, string>();\nfor (const [canonical, typos] of Object.entries(TYPO_PATTERNS)) {\n for (const typo of typos) TYPO_LOOKUP.set(typo, canonical);\n}\n\n/** DNS resolver contract — caller-supplied so we don't import `node:dns`. */\nexport interface DNSResolver {\n resolveMx(domain: string): Promise<Array<{ exchange: string; priority: number }>>;\n resolveTxt(domain: string): Promise<string[]>;\n}\n\n/** No-op resolver for environments where DNS isn't available. */\nexport class StubDNSResolver implements DNSResolver {\n async resolveMx(): Promise<Array<{ exchange: string; priority: number }>> {\n return [];\n }\n async resolveTxt(): Promise<string[]> {\n return [];\n }\n}\n\n/**\n * DNS-over-HTTPS resolver — works in any runtime with `fetch` (Cloudflare\n * Workers, Vercel Edge, Deno, browsers, Node 18+). Defaults to Cloudflare's\n * 1.1.1.1 endpoint; pass `endpoint` to use Google (8.8.8.8), NextDNS, or\n * a self-hosted resolver.\n *\n * Compatible with [`cf-doh`](https://www.npmjs.com/package/cf-doh) — if you\n * already use that, drop it in directly. This built-in keeps the package\n * zero-dep so the same code works on every edge runtime without an extra\n * install step.\n */\nexport interface DoHResolverOptions {\n /** DoH endpoint URL. Default: https://cloudflare-dns.com/dns-query */\n endpoint?: string;\n /** Per-query request timeout, ms. Default: 5000 */\n timeoutMs?: number;\n /** Custom fetch (e.g. to add headers / proxy). Default: globalThis.fetch */\n fetch?: typeof fetch;\n}\n\ninterface DoHAnswer {\n name: string;\n type: number;\n TTL: number;\n data: string;\n}\n\ninterface DoHResponse {\n Status: number;\n Answer?: DoHAnswer[];\n}\n\nconst DOH_RECORD_TYPE = { MX: 15, TXT: 16 } as const;\n\nexport class DoHResolver implements DNSResolver {\n private readonly endpoint: string;\n private readonly timeoutMs: number;\n private readonly fetchFn: typeof fetch;\n\n constructor(options: DoHResolverOptions = {}) {\n this.endpoint = options.endpoint ?? 'https://cloudflare-dns.com/dns-query';\n this.timeoutMs = options.timeoutMs ?? 5000;\n this.fetchFn = options.fetch ?? globalThis.fetch;\n }\n\n async resolveMx(domain: string): Promise<Array<{ exchange: string; priority: number }>> {\n const records = await this.query(domain, DOH_RECORD_TYPE.MX);\n if (!records) return [];\n return records\n .map((answer) => {\n // MX answer data: \"<priority> <exchange>\" (with optional trailing dot).\n const match = answer.data.match(/^(\\d+)\\s+(.+?)\\.?$/);\n if (!match) return null;\n return { priority: Number(match[1]), exchange: match[2] as string };\n })\n .filter((r): r is { exchange: string; priority: number } => r !== null)\n .sort((a, b) => a.priority - b.priority);\n }\n\n async resolveTxt(domain: string): Promise<string[]> {\n const records = await this.query(domain, DOH_RECORD_TYPE.TXT);\n if (!records) return [];\n // TXT answers come back as quoted strings — strip the surrounding quotes.\n return records.map((answer) => answer.data.replace(/^\"(.*)\"$/, '$1'));\n }\n\n private async query(domain: string, type: number): Promise<DoHAnswer[] | null> {\n const url = `${this.endpoint}?name=${encodeURIComponent(domain)}&type=${type}`;\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.timeoutMs);\n try {\n const response = await this.fetchFn(url, {\n headers: { Accept: 'application/dns-json' },\n signal: controller.signal,\n });\n if (!response.ok) return null;\n const json = (await response.json()) as DoHResponse;\n // Status 0 = NOERROR. Anything else (NXDOMAIN, SERVFAIL, etc.) → no answers.\n if (json.Status !== 0) return [];\n return json.Answer ?? [];\n } catch {\n return null;\n } finally {\n clearTimeout(timer);\n }\n }\n}\n\n/**\n * Suggest a corrected domain. Returns the canonical for a known typo,\n * otherwise the closest match within the threshold, otherwise null.\n */\nexport function suggestDomain(domain: string, options?: DomainSuggesterOptions): string | null {\n const lower = domain.toLowerCase();\n\n // Hand-curated typo map first — beats similarity for common cases.\n const known = TYPO_LOOKUP.get(lower);\n if (known) return known;\n\n const domains = options?.customDomains ?? COMMON_DOMAINS;\n if (domains.includes(lower)) return null;\n\n const threshold = options?.threshold ?? 2;\n let minDistance = Infinity;\n let suggestion: string | null = null;\n\n for (const candidate of domains) {\n const candidateLower = candidate.toLowerCase();\n if (lower === candidateLower) return null;\n const similarity = stringSimilarity(lower, candidateLower);\n const distance = Math.round((1 - similarity) * Math.max(domain.length, candidate.length));\n if (distance > 0 && distance <= threshold && distance < minDistance) {\n minDistance = distance;\n suggestion = candidate;\n }\n }\n return suggestion;\n}\n\n/**\n * Validate one email — syntax / typo / disposable / free / MX (if a resolver\n * is supplied). Each step is independently flag-gated so callers pay only for\n * what they use.\n */\nexport async function validateEmailCore(\n email: string,\n options?: ValidateEmailOptions & { dnsResolver?: DNSResolver }\n): Promise<EmailValidationResult> {\n const normalized = email.toLowerCase().trim();\n\n if (!options?.skipCache) {\n const cached = validationCache.get(normalized);\n if (cached) return cached;\n }\n\n const result: EmailValidationResult = { valid: false, email: normalized, validators: {} };\n\n if (options?.validateSyntax !== false) {\n const syntaxValid = VALID_EMAIL_REGEX.test(normalized);\n result.validators.syntax = { valid: syntaxValid };\n if (!syntaxValid) {\n validationCache.set(normalized, result);\n return result;\n }\n }\n\n const [local, domain] = normalized.split('@');\n result.local = local;\n result.domain = domain;\n\n if (options?.validateTypo !== false) {\n const suggestion = suggestDomain(domain, options?.domainSuggesterOptions);\n result.validators.typo = { valid: !suggestion, suggestion: suggestion ?? undefined };\n }\n\n if (options?.validateDisposable !== false) {\n result.validators.disposable = { valid: !disposableProviders.includes(domain) };\n }\n\n if (options?.validateFree !== false) {\n result.validators.free = { valid: !freeProviders.includes(domain) };\n }\n\n if (options?.validateMx && options.dnsResolver) {\n try {\n const records = await options.dnsResolver.resolveMx(domain);\n const hasMx = records.length > 0;\n result.validators.mx = {\n valid: hasMx,\n records: hasMx ? records.map((r) => r.exchange) : undefined,\n };\n } catch (error) {\n result.validators.mx = {\n valid: false,\n error: error instanceof Error ? error.message : 'MX validation failed',\n };\n }\n }\n\n // Free-provider detection is informational; only the hard validators gate validity.\n result.valid = (['syntax', 'typo', 'disposable', 'mx'] as const).every((key) => {\n const validator = result.validators[key];\n return !validator || validator.valid !== false;\n });\n\n if (!options?.skipCache) validationCache.set(normalized, result);\n return result;\n}\n\nexport async function validateEmailBatch(\n emails: string[],\n options?: ValidateEmailOptions & { dnsResolver?: DNSResolver }\n): Promise<EmailValidationResult[]> {\n const chunkSize = options?.batchSize ?? 10;\n const results: EmailValidationResult[] = [];\n for (let i = 0; i < emails.length; i += chunkSize) {\n const chunk = emails.slice(i, i + chunkSize);\n const chunkResults = await Promise.all(chunk.map((email) => validateEmailCore(email, options)));\n results.push(...chunkResults);\n }\n return results;\n}\n\nexport function clearCache(): void {\n validationCache.clear();\n mxCache.clear();\n}\n\nexport type { DomainSuggesterOptions, EmailValidationResult, ValidateEmailOptions } from '../types';\n"],"names":["stringSimilarity"],"mappings":"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","x_google_ignoreList":[0]}
|
|
@@ -5,9 +5,9 @@ var hasRequiredStringSimilarity;
|
|
|
5
5
|
function requireStringSimilarity () {
|
|
6
6
|
if (hasRequiredStringSimilarity) return stringSimilarity;
|
|
7
7
|
hasRequiredStringSimilarity = 1;
|
|
8
|
-
(function (exports
|
|
9
|
-
Object.defineProperty(exports
|
|
10
|
-
exports
|
|
8
|
+
(function (exports) {
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.stringSimilarity = void 0;
|
|
11
11
|
/* global exports, Map */
|
|
12
12
|
/**
|
|
13
13
|
* Calculate similarity between two strings
|
|
@@ -42,8 +42,8 @@ function requireStringSimilarity () {
|
|
|
42
42
|
}
|
|
43
43
|
return (match * 2) / (str1.length + str2.length - ((substringLength - 1) * 2));
|
|
44
44
|
};
|
|
45
|
-
exports
|
|
46
|
-
exports
|
|
45
|
+
exports.stringSimilarity = stringSimilarity;
|
|
46
|
+
exports.default = exports.stringSimilarity;
|
|
47
47
|
|
|
48
48
|
} (stringSimilarity));
|
|
49
49
|
return stringSimilarity;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"verifier.esm.js","sources":["../../node_modules/string-similarity-js/dist/string-similarity.js","../../src/serverless/verifier.ts"],"sourcesContent":["\"use strict\";\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.stringSimilarity = void 0;\n/* global exports, Map */\n/**\n * Calculate similarity between two strings\n * @param {string} str1 First string to match\n * @param {string} str2 Second string to match\n * @param {number} [substringLength=2] Optional. Length of substring to be used in calculating similarity. Default 2.\n * @param {boolean} [caseSensitive=false] Optional. Whether you want to consider case in string matching. Default false;\n * @returns Number between 0 and 1, with 0 being a low match score.\n */\nvar stringSimilarity = function (str1, str2, substringLength, caseSensitive) {\n if (substringLength === void 0) { substringLength = 2; }\n if (caseSensitive === void 0) { caseSensitive = false; }\n if (!caseSensitive) {\n str1 = str1.toLowerCase();\n str2 = str2.toLowerCase();\n }\n if (str1.length < substringLength || str2.length < substringLength)\n return 0;\n var map = new Map();\n for (var i = 0; i < str1.length - (substringLength - 1); i++) {\n var substr1 = str1.substr(i, substringLength);\n map.set(substr1, map.has(substr1) ? map.get(substr1) + 1 : 1);\n }\n var match = 0;\n for (var j = 0; j < str2.length - (substringLength - 1); j++) {\n var substr2 = str2.substr(j, substringLength);\n var count = map.has(substr2) ? map.get(substr2) : 0;\n if (count > 0) {\n map.set(substr2, count - 1);\n match++;\n }\n }\n return (match * 2) / (str1.length + str2.length - ((substringLength - 1) * 2));\n};\nexports.stringSimilarity = stringSimilarity;\nexports.default = exports.stringSimilarity;\n//# sourceMappingURL=string-similarity.js.map","/**\n * Edge-runtime / serverless email validator.\n *\n * No Node.js APIs (no `node:net`, no `node:dns`) — DNS is delegated to a\n * caller-supplied `DNSResolver`, so the same code runs on Cloudflare Workers,\n * Vercel Edge, Lambda@Edge, and Deno Deploy.\n *\n * Shares data with the main validator: `commonEmailDomains` and the typo map\n * are imported from `src/data/`, so we never drift between the two surfaces.\n */\n\nimport { stringSimilarity } from 'string-similarity-js';\nimport commonEmailDomainsJson from '../data/common-email-domains.json';\nimport typoPatternsJson from '../data/typo-patterns.json';\nimport disposableProviders from '../disposable-email-providers.json';\nimport freeProviders from '../free-email-providers.json';\nimport type { DomainSuggesterOptions, EmailValidationResult, ValidateEmailOptions } from '../types';\n\n/** Compact LRU/TTL cache. One Map, expiry stamp per entry, batched eviction. */\nexport class EdgeCache<T> {\n private readonly cache = new Map<string, { value: T; expires: number }>();\n\n constructor(\n private readonly maxSize = 1000,\n private readonly ttl = 3_600_000\n ) {}\n\n get(key: string): T | undefined {\n const item = this.cache.get(key);\n if (!item) return undefined;\n if (Date.now() > item.expires) {\n this.cache.delete(key);\n return undefined;\n }\n return item.value;\n }\n\n set(key: string, value: T): void {\n if (this.cache.size >= this.maxSize) this.evict();\n this.cache.set(key, { value, expires: Date.now() + this.ttl });\n }\n\n clear(): void {\n this.cache.clear();\n }\n\n size(): number {\n return this.cache.size;\n }\n\n private evict(): void {\n // Drop the oldest 10% in one pass — Map preserves insertion order so\n // `keys()` walks oldest-first.\n const drop = Math.max(1, Math.floor(this.maxSize * 0.1));\n let n = 0;\n for (const key of this.cache.keys()) {\n if (n++ >= drop) break;\n this.cache.delete(key);\n }\n }\n}\n\n// Module-level per-isolate caches. Edge runtimes get cold-start fresh; warm\n// invocations benefit from the in-memory hits.\nexport const validationCache = new EdgeCache<EmailValidationResult>(1000);\nexport const mxCache = new EdgeCache<string[]>(500);\n\n/** Same regex the main validator uses — kept inline because edge runtimes don't auto-resolve psl. */\nconst VALID_EMAIL_REGEX =\n /^(([a-zA-Z0-9_+'-]+(\\.[a-zA-Z0-9_+'-]+)*)|(\".+\"))@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}])|(([a-zA-Z0-9-]+\\.)+[a-zA-Z]{2,}))$/;\n\n/**\n * Common email domains — re-exported so callers (Vercel Edge, etc.) can pass a\n * custom subset via `DomainSuggesterOptions.customDomains`.\n */\nexport const COMMON_DOMAINS: readonly string[] = commonEmailDomainsJson as string[];\n\nconst TYPO_PATTERNS = typoPatternsJson as Record<string, string[]>;\n/** Reverse index for O(1) typo → canonical lookup. */\nconst TYPO_LOOKUP = new Map<string, string>();\nfor (const [canonical, typos] of Object.entries(TYPO_PATTERNS)) {\n for (const typo of typos) TYPO_LOOKUP.set(typo, canonical);\n}\n\n/** DNS resolver contract — caller-supplied so we don't import `node:dns`. */\nexport interface DNSResolver {\n resolveMx(domain: string): Promise<Array<{ exchange: string; priority: number }>>;\n resolveTxt(domain: string): Promise<string[]>;\n}\n\n/** No-op resolver for environments where DNS isn't available. */\nexport class StubDNSResolver implements DNSResolver {\n async resolveMx(): Promise<Array<{ exchange: string; priority: number }>> {\n return [];\n }\n async resolveTxt(): Promise<string[]> {\n return [];\n }\n}\n\n/**\n * DNS-over-HTTPS resolver — works in any runtime with `fetch` (Cloudflare\n * Workers, Vercel Edge, Deno, browsers, Node 18+). Defaults to Cloudflare's\n * 1.1.1.1 endpoint; pass `endpoint` to use Google (8.8.8.8), NextDNS, or\n * a self-hosted resolver.\n *\n * Compatible with [`cf-doh`](https://www.npmjs.com/package/cf-doh) — if you\n * already use that, drop it in directly. This built-in keeps the package\n * zero-dep so the same code works on every edge runtime without an extra\n * install step.\n */\nexport interface DoHResolverOptions {\n /** DoH endpoint URL. Default: https://cloudflare-dns.com/dns-query */\n endpoint?: string;\n /** Per-query request timeout, ms. Default: 5000 */\n timeoutMs?: number;\n /** Custom fetch (e.g. to add headers / proxy). Default: globalThis.fetch */\n fetch?: typeof fetch;\n}\n\ninterface DoHAnswer {\n name: string;\n type: number;\n TTL: number;\n data: string;\n}\n\ninterface DoHResponse {\n Status: number;\n Answer?: DoHAnswer[];\n}\n\nconst DOH_RECORD_TYPE = { MX: 15, TXT: 16 } as const;\n\nexport class DoHResolver implements DNSResolver {\n private readonly endpoint: string;\n private readonly timeoutMs: number;\n private readonly fetchFn: typeof fetch;\n\n constructor(options: DoHResolverOptions = {}) {\n this.endpoint = options.endpoint ?? 'https://cloudflare-dns.com/dns-query';\n this.timeoutMs = options.timeoutMs ?? 5000;\n this.fetchFn = options.fetch ?? globalThis.fetch;\n }\n\n async resolveMx(domain: string): Promise<Array<{ exchange: string; priority: number }>> {\n const records = await this.query(domain, DOH_RECORD_TYPE.MX);\n if (!records) return [];\n return records\n .map((answer) => {\n // MX answer data: \"<priority> <exchange>\" (with optional trailing dot).\n const match = answer.data.match(/^(\\d+)\\s+(.+?)\\.?$/);\n if (!match) return null;\n return { priority: Number(match[1]), exchange: match[2] as string };\n })\n .filter((r): r is { exchange: string; priority: number } => r !== null)\n .sort((a, b) => a.priority - b.priority);\n }\n\n async resolveTxt(domain: string): Promise<string[]> {\n const records = await this.query(domain, DOH_RECORD_TYPE.TXT);\n if (!records) return [];\n // TXT answers come back as quoted strings — strip the surrounding quotes.\n return records.map((answer) => answer.data.replace(/^\"(.*)\"$/, '$1'));\n }\n\n private async query(domain: string, type: number): Promise<DoHAnswer[] | null> {\n const url = `${this.endpoint}?name=${encodeURIComponent(domain)}&type=${type}`;\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.timeoutMs);\n try {\n const response = await this.fetchFn(url, {\n headers: { Accept: 'application/dns-json' },\n signal: controller.signal,\n });\n if (!response.ok) return null;\n const json = (await response.json()) as DoHResponse;\n // Status 0 = NOERROR. Anything else (NXDOMAIN, SERVFAIL, etc.) → no answers.\n if (json.Status !== 0) return [];\n return json.Answer ?? [];\n } catch {\n return null;\n } finally {\n clearTimeout(timer);\n }\n }\n}\n\n/**\n * Suggest a corrected domain. Returns the canonical for a known typo,\n * otherwise the closest match within the threshold, otherwise null.\n */\nexport function suggestDomain(domain: string, options?: DomainSuggesterOptions): string | null {\n const lower = domain.toLowerCase();\n\n // Hand-curated typo map first — beats similarity for common cases.\n const known = TYPO_LOOKUP.get(lower);\n if (known) return known;\n\n const domains = options?.customDomains ?? COMMON_DOMAINS;\n if (domains.includes(lower)) return null;\n\n const threshold = options?.threshold ?? 2;\n let minDistance = Infinity;\n let suggestion: string | null = null;\n\n for (const candidate of domains) {\n const candidateLower = candidate.toLowerCase();\n if (lower === candidateLower) return null;\n const similarity = stringSimilarity(lower, candidateLower);\n const distance = Math.round((1 - similarity) * Math.max(domain.length, candidate.length));\n if (distance > 0 && distance <= threshold && distance < minDistance) {\n minDistance = distance;\n suggestion = candidate;\n }\n }\n return suggestion;\n}\n\n/**\n * Validate one email — syntax / typo / disposable / free / MX (if a resolver\n * is supplied). Each step is independently flag-gated so callers pay only for\n * what they use.\n */\nexport async function validateEmailCore(\n email: string,\n options?: ValidateEmailOptions & { dnsResolver?: DNSResolver }\n): Promise<EmailValidationResult> {\n const normalized = email.toLowerCase().trim();\n\n if (!options?.skipCache) {\n const cached = validationCache.get(normalized);\n if (cached) return cached;\n }\n\n const result: EmailValidationResult = { valid: false, email: normalized, validators: {} };\n\n if (options?.validateSyntax !== false) {\n const syntaxValid = VALID_EMAIL_REGEX.test(normalized);\n result.validators.syntax = { valid: syntaxValid };\n if (!syntaxValid) {\n validationCache.set(normalized, result);\n return result;\n }\n }\n\n const [local, domain] = normalized.split('@');\n result.local = local;\n result.domain = domain;\n\n if (options?.validateTypo !== false) {\n const suggestion = suggestDomain(domain, options?.domainSuggesterOptions);\n result.validators.typo = { valid: !suggestion, suggestion: suggestion ?? undefined };\n }\n\n if (options?.validateDisposable !== false) {\n result.validators.disposable = { valid: !disposableProviders.includes(domain) };\n }\n\n if (options?.validateFree !== false) {\n result.validators.free = { valid: !freeProviders.includes(domain) };\n }\n\n if (options?.validateMx && options.dnsResolver) {\n try {\n const records = await options.dnsResolver.resolveMx(domain);\n const hasMx = records.length > 0;\n result.validators.mx = {\n valid: hasMx,\n records: hasMx ? records.map((r) => r.exchange) : undefined,\n };\n } catch (error) {\n result.validators.mx = {\n valid: false,\n error: error instanceof Error ? error.message : 'MX validation failed',\n };\n }\n }\n\n // Free-provider detection is informational; only the hard validators gate validity.\n result.valid = (['syntax', 'typo', 'disposable', 'mx'] as const).every((key) => {\n const validator = result.validators[key];\n return !validator || validator.valid !== false;\n });\n\n if (!options?.skipCache) validationCache.set(normalized, result);\n return result;\n}\n\nexport async function validateEmailBatch(\n emails: string[],\n options?: ValidateEmailOptions & { dnsResolver?: DNSResolver }\n): Promise<EmailValidationResult[]> {\n const chunkSize = options?.batchSize ?? 10;\n const results: EmailValidationResult[] = [];\n for (let i = 0; i < emails.length; i += chunkSize) {\n const chunk = emails.slice(i, i + chunkSize);\n const chunkResults = await Promise.all(chunk.map((email) => validateEmailCore(email, options)));\n results.push(...chunkResults);\n }\n return results;\n}\n\nexport function clearCache(): void {\n validationCache.clear();\n mxCache.clear();\n}\n\nexport type { DomainSuggesterOptions, EmailValidationResult, ValidateEmailOptions } from '../types';\n"],"names":["exports","stringSimilarity"],"mappings":"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","x_google_ignoreList":[0]}
|
|
1
|
+
{"version":3,"file":"verifier.esm.js","sources":["../../node_modules/string-similarity-js/dist/string-similarity.js","../../src/serverless/verifier.ts"],"sourcesContent":["\"use strict\";\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.stringSimilarity = void 0;\n/* global exports, Map */\n/**\n * Calculate similarity between two strings\n * @param {string} str1 First string to match\n * @param {string} str2 Second string to match\n * @param {number} [substringLength=2] Optional. Length of substring to be used in calculating similarity. Default 2.\n * @param {boolean} [caseSensitive=false] Optional. Whether you want to consider case in string matching. Default false;\n * @returns Number between 0 and 1, with 0 being a low match score.\n */\nvar stringSimilarity = function (str1, str2, substringLength, caseSensitive) {\n if (substringLength === void 0) { substringLength = 2; }\n if (caseSensitive === void 0) { caseSensitive = false; }\n if (!caseSensitive) {\n str1 = str1.toLowerCase();\n str2 = str2.toLowerCase();\n }\n if (str1.length < substringLength || str2.length < substringLength)\n return 0;\n var map = new Map();\n for (var i = 0; i < str1.length - (substringLength - 1); i++) {\n var substr1 = str1.substr(i, substringLength);\n map.set(substr1, map.has(substr1) ? map.get(substr1) + 1 : 1);\n }\n var match = 0;\n for (var j = 0; j < str2.length - (substringLength - 1); j++) {\n var substr2 = str2.substr(j, substringLength);\n var count = map.has(substr2) ? map.get(substr2) : 0;\n if (count > 0) {\n map.set(substr2, count - 1);\n match++;\n }\n }\n return (match * 2) / (str1.length + str2.length - ((substringLength - 1) * 2));\n};\nexports.stringSimilarity = stringSimilarity;\nexports.default = exports.stringSimilarity;\n//# sourceMappingURL=string-similarity.js.map","/**\n * Edge-runtime / serverless email validator.\n *\n * No Node.js APIs (no `node:net`, no `node:dns`) — DNS is delegated to a\n * caller-supplied `DNSResolver`, so the same code runs on Cloudflare Workers,\n * Vercel Edge, Lambda@Edge, and Deno Deploy.\n *\n * Shares data with the main validator: `commonEmailDomains` and the typo map\n * are imported from `src/data/`, so we never drift between the two surfaces.\n */\n\nimport { stringSimilarity } from 'string-similarity-js';\nimport commonEmailDomainsJson from '../data/common-email-domains.json';\nimport typoPatternsJson from '../data/typo-patterns.json';\nimport disposableProviders from '../disposable-email-providers.json';\nimport freeProviders from '../free-email-providers.json';\nimport type { DomainSuggesterOptions, EmailValidationResult, ValidateEmailOptions } from '../types';\n\n/** Compact LRU/TTL cache. One Map, expiry stamp per entry, batched eviction. */\nexport class EdgeCache<T> {\n private readonly cache = new Map<string, { value: T; expires: number }>();\n\n constructor(\n private readonly maxSize = 1000,\n private readonly ttl = 3_600_000\n ) {}\n\n get(key: string): T | undefined {\n const item = this.cache.get(key);\n if (!item) return undefined;\n if (Date.now() > item.expires) {\n this.cache.delete(key);\n return undefined;\n }\n return item.value;\n }\n\n set(key: string, value: T): void {\n if (this.cache.size >= this.maxSize) this.evict();\n this.cache.set(key, { value, expires: Date.now() + this.ttl });\n }\n\n clear(): void {\n this.cache.clear();\n }\n\n size(): number {\n return this.cache.size;\n }\n\n private evict(): void {\n // Drop the oldest 10% in one pass — Map preserves insertion order so\n // `keys()` walks oldest-first.\n const drop = Math.max(1, Math.floor(this.maxSize * 0.1));\n let n = 0;\n for (const key of this.cache.keys()) {\n if (n++ >= drop) break;\n this.cache.delete(key);\n }\n }\n}\n\n// Module-level per-isolate caches. Edge runtimes get cold-start fresh; warm\n// invocations benefit from the in-memory hits.\nexport const validationCache = new EdgeCache<EmailValidationResult>(1000);\nexport const mxCache = new EdgeCache<string[]>(500);\n\n/** Same regex the main validator uses — kept inline because edge runtimes don't auto-resolve psl. */\nconst VALID_EMAIL_REGEX =\n /^(([a-zA-Z0-9_+'-]+(\\.[a-zA-Z0-9_+'-]+)*)|(\".+\"))@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}])|(([a-zA-Z0-9-]+\\.)+[a-zA-Z]{2,}))$/;\n\n/**\n * Common email domains — re-exported so callers (Vercel Edge, etc.) can pass a\n * custom subset via `DomainSuggesterOptions.customDomains`.\n */\nexport const COMMON_DOMAINS: readonly string[] = commonEmailDomainsJson as string[];\n\nconst TYPO_PATTERNS = typoPatternsJson as Record<string, string[]>;\n/** Reverse index for O(1) typo → canonical lookup. */\nconst TYPO_LOOKUP = new Map<string, string>();\nfor (const [canonical, typos] of Object.entries(TYPO_PATTERNS)) {\n for (const typo of typos) TYPO_LOOKUP.set(typo, canonical);\n}\n\n/** DNS resolver contract — caller-supplied so we don't import `node:dns`. */\nexport interface DNSResolver {\n resolveMx(domain: string): Promise<Array<{ exchange: string; priority: number }>>;\n resolveTxt(domain: string): Promise<string[]>;\n}\n\n/** No-op resolver for environments where DNS isn't available. */\nexport class StubDNSResolver implements DNSResolver {\n async resolveMx(): Promise<Array<{ exchange: string; priority: number }>> {\n return [];\n }\n async resolveTxt(): Promise<string[]> {\n return [];\n }\n}\n\n/**\n * DNS-over-HTTPS resolver — works in any runtime with `fetch` (Cloudflare\n * Workers, Vercel Edge, Deno, browsers, Node 18+). Defaults to Cloudflare's\n * 1.1.1.1 endpoint; pass `endpoint` to use Google (8.8.8.8), NextDNS, or\n * a self-hosted resolver.\n *\n * Compatible with [`cf-doh`](https://www.npmjs.com/package/cf-doh) — if you\n * already use that, drop it in directly. This built-in keeps the package\n * zero-dep so the same code works on every edge runtime without an extra\n * install step.\n */\nexport interface DoHResolverOptions {\n /** DoH endpoint URL. Default: https://cloudflare-dns.com/dns-query */\n endpoint?: string;\n /** Per-query request timeout, ms. Default: 5000 */\n timeoutMs?: number;\n /** Custom fetch (e.g. to add headers / proxy). Default: globalThis.fetch */\n fetch?: typeof fetch;\n}\n\ninterface DoHAnswer {\n name: string;\n type: number;\n TTL: number;\n data: string;\n}\n\ninterface DoHResponse {\n Status: number;\n Answer?: DoHAnswer[];\n}\n\nconst DOH_RECORD_TYPE = { MX: 15, TXT: 16 } as const;\n\nexport class DoHResolver implements DNSResolver {\n private readonly endpoint: string;\n private readonly timeoutMs: number;\n private readonly fetchFn: typeof fetch;\n\n constructor(options: DoHResolverOptions = {}) {\n this.endpoint = options.endpoint ?? 'https://cloudflare-dns.com/dns-query';\n this.timeoutMs = options.timeoutMs ?? 5000;\n this.fetchFn = options.fetch ?? globalThis.fetch;\n }\n\n async resolveMx(domain: string): Promise<Array<{ exchange: string; priority: number }>> {\n const records = await this.query(domain, DOH_RECORD_TYPE.MX);\n if (!records) return [];\n return records\n .map((answer) => {\n // MX answer data: \"<priority> <exchange>\" (with optional trailing dot).\n const match = answer.data.match(/^(\\d+)\\s+(.+?)\\.?$/);\n if (!match) return null;\n return { priority: Number(match[1]), exchange: match[2] as string };\n })\n .filter((r): r is { exchange: string; priority: number } => r !== null)\n .sort((a, b) => a.priority - b.priority);\n }\n\n async resolveTxt(domain: string): Promise<string[]> {\n const records = await this.query(domain, DOH_RECORD_TYPE.TXT);\n if (!records) return [];\n // TXT answers come back as quoted strings — strip the surrounding quotes.\n return records.map((answer) => answer.data.replace(/^\"(.*)\"$/, '$1'));\n }\n\n private async query(domain: string, type: number): Promise<DoHAnswer[] | null> {\n const url = `${this.endpoint}?name=${encodeURIComponent(domain)}&type=${type}`;\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.timeoutMs);\n try {\n const response = await this.fetchFn(url, {\n headers: { Accept: 'application/dns-json' },\n signal: controller.signal,\n });\n if (!response.ok) return null;\n const json = (await response.json()) as DoHResponse;\n // Status 0 = NOERROR. Anything else (NXDOMAIN, SERVFAIL, etc.) → no answers.\n if (json.Status !== 0) return [];\n return json.Answer ?? [];\n } catch {\n return null;\n } finally {\n clearTimeout(timer);\n }\n }\n}\n\n/**\n * Suggest a corrected domain. Returns the canonical for a known typo,\n * otherwise the closest match within the threshold, otherwise null.\n */\nexport function suggestDomain(domain: string, options?: DomainSuggesterOptions): string | null {\n const lower = domain.toLowerCase();\n\n // Hand-curated typo map first — beats similarity for common cases.\n const known = TYPO_LOOKUP.get(lower);\n if (known) return known;\n\n const domains = options?.customDomains ?? COMMON_DOMAINS;\n if (domains.includes(lower)) return null;\n\n const threshold = options?.threshold ?? 2;\n let minDistance = Infinity;\n let suggestion: string | null = null;\n\n for (const candidate of domains) {\n const candidateLower = candidate.toLowerCase();\n if (lower === candidateLower) return null;\n const similarity = stringSimilarity(lower, candidateLower);\n const distance = Math.round((1 - similarity) * Math.max(domain.length, candidate.length));\n if (distance > 0 && distance <= threshold && distance < minDistance) {\n minDistance = distance;\n suggestion = candidate;\n }\n }\n return suggestion;\n}\n\n/**\n * Validate one email — syntax / typo / disposable / free / MX (if a resolver\n * is supplied). Each step is independently flag-gated so callers pay only for\n * what they use.\n */\nexport async function validateEmailCore(\n email: string,\n options?: ValidateEmailOptions & { dnsResolver?: DNSResolver }\n): Promise<EmailValidationResult> {\n const normalized = email.toLowerCase().trim();\n\n if (!options?.skipCache) {\n const cached = validationCache.get(normalized);\n if (cached) return cached;\n }\n\n const result: EmailValidationResult = { valid: false, email: normalized, validators: {} };\n\n if (options?.validateSyntax !== false) {\n const syntaxValid = VALID_EMAIL_REGEX.test(normalized);\n result.validators.syntax = { valid: syntaxValid };\n if (!syntaxValid) {\n validationCache.set(normalized, result);\n return result;\n }\n }\n\n const [local, domain] = normalized.split('@');\n result.local = local;\n result.domain = domain;\n\n if (options?.validateTypo !== false) {\n const suggestion = suggestDomain(domain, options?.domainSuggesterOptions);\n result.validators.typo = { valid: !suggestion, suggestion: suggestion ?? undefined };\n }\n\n if (options?.validateDisposable !== false) {\n result.validators.disposable = { valid: !disposableProviders.includes(domain) };\n }\n\n if (options?.validateFree !== false) {\n result.validators.free = { valid: !freeProviders.includes(domain) };\n }\n\n if (options?.validateMx && options.dnsResolver) {\n try {\n const records = await options.dnsResolver.resolveMx(domain);\n const hasMx = records.length > 0;\n result.validators.mx = {\n valid: hasMx,\n records: hasMx ? records.map((r) => r.exchange) : undefined,\n };\n } catch (error) {\n result.validators.mx = {\n valid: false,\n error: error instanceof Error ? error.message : 'MX validation failed',\n };\n }\n }\n\n // Free-provider detection is informational; only the hard validators gate validity.\n result.valid = (['syntax', 'typo', 'disposable', 'mx'] as const).every((key) => {\n const validator = result.validators[key];\n return !validator || validator.valid !== false;\n });\n\n if (!options?.skipCache) validationCache.set(normalized, result);\n return result;\n}\n\nexport async function validateEmailBatch(\n emails: string[],\n options?: ValidateEmailOptions & { dnsResolver?: DNSResolver }\n): Promise<EmailValidationResult[]> {\n const chunkSize = options?.batchSize ?? 10;\n const results: EmailValidationResult[] = [];\n for (let i = 0; i < emails.length; i += chunkSize) {\n const chunk = emails.slice(i, i + chunkSize);\n const chunkResults = await Promise.all(chunk.map((email) => validateEmailCore(email, options)));\n results.push(...chunkResults);\n }\n return results;\n}\n\nexport function clearCache(): void {\n validationCache.clear();\n mxCache.clear();\n}\n\nexport type { DomainSuggesterOptions, EmailValidationResult, ValidateEmailOptions } from '../types';\n"],"names":["stringSimilarity"],"mappings":"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","x_google_ignoreList":[0]}
|