zynor 0.0.44 → 0.0.48
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/LICENSE +88 -203
- package/README.md +507 -543
- package/dist/index.cjs +3 -3
- package/dist/index.d.ts +20 -115
- package/dist/index.js +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,758 +1,722 @@
|
|
|
1
1
|
# zynor
|
|
2
2
|
|
|
3
3
|
[](https://badge.fury.io/js/zynor)
|
|
4
|
-
[](https://opensource.org/licenses/MIT)
|
|
5
4
|
[](http://www.typescriptlang.org/)
|
|
6
5
|
[](https://nodejs.org/)
|
|
7
6
|
|
|
8
|
-
A
|
|
9
|
-
|
|
10
|
-
##
|
|
11
|
-
|
|
12
|
-
- [
|
|
13
|
-
- [
|
|
14
|
-
- [
|
|
15
|
-
- [
|
|
16
|
-
- [
|
|
17
|
-
- [
|
|
18
|
-
- [
|
|
7
|
+
A DNS resolution and email intelligence library for Node.js. Zynor combines four DNS providers behind a single API with built-in concurrency control, caching, automatic failover, and a full-featured email validation engine.
|
|
8
|
+
|
|
9
|
+
## Table of Contents
|
|
10
|
+
|
|
11
|
+
- [Installation](#installation)
|
|
12
|
+
- [Quick Start](#quick-start)
|
|
13
|
+
- [DNS Resolution](#dns-resolution)
|
|
14
|
+
- [Standalone Functions](#standalone-functions)
|
|
15
|
+
- [Zynor Class](#zynor-class)
|
|
16
|
+
- [Supported Record Types](#supported-record-types)
|
|
17
|
+
- [Provider Selection](#provider-selection)
|
|
18
|
+
- [AbortSignal and Timeout](#abortsignal-and-timeout)
|
|
19
|
+
- [DNS Caching](#dns-caching)
|
|
20
|
+
- [Request Deduplication](#request-deduplication)
|
|
21
|
+
- [LruCache](#lrucache)
|
|
22
|
+
- [Provider Configuration](#provider-configuration)
|
|
23
|
+
- [Concurrency and Rate Limiting](#concurrency-and-rate-limiting)
|
|
24
|
+
- [Runtime Configuration Updates](#runtime-configuration-updates)
|
|
19
25
|
- [Email Validation](#email-validation)
|
|
20
|
-
- [
|
|
21
|
-
- [
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
-
|
|
28
|
-
-
|
|
29
|
-
-
|
|
30
|
-
-
|
|
31
|
-
-
|
|
32
|
-
-
|
|
33
|
-
- **📊 Complete DNS API**: All standard DNS record types supported
|
|
34
|
-
- **📧 Email Validation**: Comprehensive email domain validation and webmail detection
|
|
35
|
-
- **🛡️ Error Handling**: Robust error handling with detailed error information
|
|
36
|
-
- **🔄 Automatic Retry**: Built-in retry logic with exponential backoff
|
|
37
|
-
- **📈 Queue Management**: Advanced queue management with rate limiter
|
|
38
|
-
- **🎛️ Runtime Configuration**: Dynamic configuration updates without restart
|
|
26
|
+
- [Basic Validation](#basic-validation)
|
|
27
|
+
- [Deep Validation](#deep-validation)
|
|
28
|
+
- [Bulk Validation](#bulk-validation)
|
|
29
|
+
- [Provider Detection](#provider-detection)
|
|
30
|
+
- [Disposable Email Detection](#disposable-email-detection)
|
|
31
|
+
- [Role-Based Email Detection](#role-based-email-detection)
|
|
32
|
+
- [Free Domain Detection](#free-domain-detection)
|
|
33
|
+
- [Webmail URL Lookup](#webmail-url-lookup)
|
|
34
|
+
- [Email Extraction](#email-extraction)
|
|
35
|
+
- [Persistent Caching](#persistent-caching)
|
|
36
|
+
- [Error Handling](#error-handling)
|
|
37
|
+
- [TypeScript Support](#typescript-support)
|
|
38
|
+
- [License](#license)
|
|
39
39
|
|
|
40
|
-
|
|
40
|
+
---
|
|
41
41
|
|
|
42
|
-
|
|
42
|
+
## Installation
|
|
43
43
|
|
|
44
|
-
**Using npm:**
|
|
45
44
|
```bash
|
|
46
45
|
npm install zynor
|
|
47
46
|
```
|
|
48
47
|
|
|
49
|
-
**Using Yarn:**
|
|
50
48
|
```bash
|
|
51
49
|
yarn add zynor
|
|
52
50
|
```
|
|
53
51
|
|
|
54
|
-
**Using pnpm:**
|
|
55
52
|
```bash
|
|
56
53
|
pnpm add zynor
|
|
57
54
|
```
|
|
58
55
|
|
|
59
|
-
**Requirements:**
|
|
60
|
-
- Node.js 18.0.0 or higher
|
|
61
|
-
- TypeScript 4.5+ (for TypeScript projects)
|
|
62
|
-
- Modern JavaScript environment with Promise support
|
|
63
|
-
|
|
64
|
-
## 🚀 Quick Start
|
|
65
|
-
|
|
66
|
-
Get started with Zynor in seconds! The library offers two main approaches: standalone functions for simple DNS queries and the Zynor class for advanced configuration and control. Both approaches provide the same powerful DNS resolution capabilities with automatic provider selection and intelligent error handling.
|
|
56
|
+
**Requirements:** Node.js 18.0.0 or higher.
|
|
67
57
|
|
|
68
|
-
|
|
58
|
+
---
|
|
69
59
|
|
|
70
|
-
|
|
60
|
+
## Quick Start
|
|
71
61
|
|
|
72
62
|
```typescript
|
|
73
|
-
|
|
74
|
-
import { resolve4, resolveMx, resolveAny } from 'zynor';
|
|
75
|
-
|
|
76
|
-
// Resolve A records
|
|
77
|
-
const addresses = await resolve4('example.com');
|
|
78
|
-
console.log(addresses); // ['93.184.216.34']
|
|
63
|
+
import { resolve4, resolveMx } from "zynor";
|
|
79
64
|
|
|
80
|
-
// Resolve
|
|
81
|
-
const
|
|
82
|
-
|
|
65
|
+
// Resolve IPv4 addresses
|
|
66
|
+
const ips = await resolve4("example.com");
|
|
67
|
+
// => ['93.184.216.34']
|
|
83
68
|
|
|
84
|
-
// Resolve
|
|
85
|
-
const
|
|
69
|
+
// Resolve mail servers
|
|
70
|
+
const mx = await resolveMx("example.com");
|
|
71
|
+
// => [{ priority: 10, exchange: 'mail.example.com' }]
|
|
86
72
|
```
|
|
87
73
|
|
|
88
|
-
|
|
74
|
+
Import a function, pass a hostname, get results. No configuration needed.
|
|
89
75
|
|
|
90
|
-
|
|
76
|
+
For more control:
|
|
91
77
|
|
|
92
78
|
```typescript
|
|
93
|
-
|
|
94
|
-
import { Zynor } from 'zynor';
|
|
79
|
+
import { Zynor } from "zynor";
|
|
95
80
|
|
|
96
81
|
const dns = new Zynor({
|
|
97
82
|
google: { enabled: true, limit: { concurrency: 10 } },
|
|
98
|
-
cloudflare: { enabled: true, limit: { concurrency: 5 } }
|
|
83
|
+
cloudflare: { enabled: true, limit: { concurrency: 5 } },
|
|
84
|
+
quad9: { enabled: false },
|
|
85
|
+
native: { enabled: true },
|
|
99
86
|
});
|
|
100
87
|
|
|
101
|
-
const
|
|
88
|
+
const ips = await dns.resolve4("example.com");
|
|
102
89
|
```
|
|
103
90
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
Zynor fully supports CommonJS environments for maximum compatibility:
|
|
91
|
+
CommonJS is also supported:
|
|
107
92
|
|
|
108
93
|
```javascript
|
|
109
|
-
|
|
110
|
-
const { resolve4, Zynor } = require('zynor');
|
|
111
|
-
|
|
112
|
-
const addresses = await resolve4('example.com');
|
|
94
|
+
const { resolve4, Zynor } = require("zynor");
|
|
113
95
|
```
|
|
114
96
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
Zynor integrates with multiple DNS providers to ensure maximum reliability, performance, and flexibility. Each provider offers unique advantages and can be configured independently with custom settings for concurrency, timeouts, and retry behavior. The library automatically selects the best available provider or allows manual provider specification for specific use cases.
|
|
118
|
-
|
|
119
|
-
### Native Provider
|
|
120
|
-
|
|
121
|
-
The native provider leverages Node.js's built-in DNS resolution capabilities, providing seamless integration with system DNS settings and local network configurations.
|
|
122
|
-
|
|
123
|
-
- **Endpoint**: Node.js built-in `dns/promises` module
|
|
124
|
-
- **Features**: Supports all DNS methods including `lookup()`
|
|
125
|
-
- **Use Case**: Local/internal DNS resolution, system DNS settings
|
|
126
|
-
- **Advantages**: No external dependencies, respects system DNS configuration
|
|
127
|
-
- **Best For**: Internal networks, development environments, system-integrated applications
|
|
128
|
-
|
|
129
|
-
### Google DoH (DNS-over-HTTPS)
|
|
130
|
-
|
|
131
|
-
Google's public DNS service provides enterprise-grade DNS resolution with global infrastructure and comprehensive DNSSEC validation.
|
|
97
|
+
---
|
|
132
98
|
|
|
133
|
-
|
|
134
|
-
- **Features**: DNSSEC validation, high reliability, global anycast network
|
|
135
|
-
- **Use Case**: Public DNS resolution with Google's infrastructure
|
|
136
|
-
- **Advantages**: Global CDN, excellent uptime, DNSSEC support, comprehensive logging
|
|
137
|
-
- **Best For**: Production applications, global services, DNSSEC-required environments
|
|
99
|
+
## DNS Resolution
|
|
138
100
|
|
|
139
|
-
###
|
|
101
|
+
### Standalone Functions
|
|
140
102
|
|
|
141
|
-
|
|
103
|
+
You don't need to create an instance or configure anything to start resolving DNS records. Every DNS method is exported as a standalone function you can import and call directly. Each function handles provider selection, queuing, caching, and error translation behind the scenes — just pass a hostname and get back structured results:
|
|
142
104
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
105
|
+
```typescript
|
|
106
|
+
import {
|
|
107
|
+
resolve4, // A records (IPv4)
|
|
108
|
+
resolve6, // AAAA records (IPv6)
|
|
109
|
+
resolveMx, // MX records (mail servers)
|
|
110
|
+
resolveTxt, // TXT records (SPF, DKIM, verification)
|
|
111
|
+
resolveCname, // CNAME records (aliases)
|
|
112
|
+
resolveNs, // NS records (name servers)
|
|
113
|
+
resolveSoa, // SOA records (zone authority)
|
|
114
|
+
resolveSrv, // SRV records (service discovery)
|
|
115
|
+
resolvePtr, // PTR records (reverse DNS)
|
|
116
|
+
resolveCaa, // CAA records (certificate authority)
|
|
117
|
+
resolveNaptr, // NAPTR records
|
|
118
|
+
resolveTlsa, // TLSA records (DANE)
|
|
119
|
+
resolveAny, // ALL available records
|
|
120
|
+
reverse, // Reverse DNS lookup
|
|
121
|
+
resolve, // Generic resolve by record type
|
|
122
|
+
} from "zynor";
|
|
123
|
+
```
|
|
148
124
|
|
|
149
|
-
|
|
125
|
+
Each function accepts an optional provider name as the last argument:
|
|
150
126
|
|
|
151
|
-
|
|
127
|
+
```typescript
|
|
128
|
+
const ips = await resolve4("example.com", "google");
|
|
129
|
+
const mx = await resolveMx("example.com", "cloudflare");
|
|
130
|
+
const ns = await resolve("example.com", "NS");
|
|
131
|
+
const nsViaGoogle = await resolve("example.com", "NS", "google");
|
|
132
|
+
```
|
|
152
133
|
|
|
153
|
-
|
|
154
|
-
- **Features**: Security-focused, threat blocking, privacy protection
|
|
155
|
-
- **Use Case**: Security-sensitive applications, enterprise environments
|
|
156
|
-
- **Advantages**: Malware blocking, threat intelligence, non-profit organization, no user tracking
|
|
157
|
-
- **Best For**: Security-critical applications, enterprise networks, threat-aware environments
|
|
134
|
+
Request TTL information for A and AAAA records:
|
|
158
135
|
|
|
159
|
-
|
|
136
|
+
```typescript
|
|
137
|
+
const withTtl = await resolve4("example.com", { ttl: true });
|
|
138
|
+
// => [{ address: '93.184.216.34', ttl: 300 }]
|
|
139
|
+
```
|
|
160
140
|
|
|
161
|
-
Zynor
|
|
141
|
+
### Zynor Class
|
|
162
142
|
|
|
163
|
-
|
|
143
|
+
The `Zynor` class gives full control over providers, concurrency, and caching:
|
|
164
144
|
|
|
165
145
|
```typescript
|
|
146
|
+
import { Zynor } from "zynor";
|
|
147
|
+
|
|
166
148
|
const dns = new Zynor({
|
|
167
149
|
google: {
|
|
168
150
|
enabled: true,
|
|
169
|
-
limit: { concurrency: 10,
|
|
170
|
-
timeout: 5000,
|
|
171
|
-
retries: 3
|
|
151
|
+
limit: { concurrency: 10, interval: 1000, intervalCap: 500 },
|
|
172
152
|
},
|
|
173
153
|
cloudflare: {
|
|
174
154
|
enabled: true,
|
|
175
|
-
limit: { concurrency:
|
|
155
|
+
limit: { concurrency: 10 },
|
|
176
156
|
},
|
|
177
157
|
quad9: { enabled: false },
|
|
178
|
-
native: { enabled: true }
|
|
158
|
+
native: { enabled: true },
|
|
159
|
+
cache: {
|
|
160
|
+
enabled: true,
|
|
161
|
+
maxSize: 100_000,
|
|
162
|
+
dnsTtl: 1_440_000,
|
|
163
|
+
},
|
|
179
164
|
});
|
|
165
|
+
|
|
166
|
+
const ips = await dns.resolve4("example.com");
|
|
167
|
+
const mx = await dns.resolveMx("example.com", "google");
|
|
168
|
+
const txt = await dns.resolveTxt("example.com", { timeout: 5000 });
|
|
180
169
|
```
|
|
181
170
|
|
|
182
|
-
###
|
|
171
|
+
### Supported Record Types
|
|
172
|
+
|
|
173
|
+
| Record Type | Method | Returns | Description |
|
|
174
|
+
| ----------- | ---------------- | --------------- | ------------------------------------------------------------------ |
|
|
175
|
+
| **A** | `resolve4()` | `string[]` | IPv4 addresses for a domain |
|
|
176
|
+
| **AAAA** | `resolve6()` | `string[]` | IPv6 addresses for a domain |
|
|
177
|
+
| **MX** | `resolveMx()` | `MxRecord[]` | Mail servers with priority, used to find where email is handled |
|
|
178
|
+
| **TXT** | `resolveTxt()` | `string[][]` | Text records holding SPF rules, DKIM keys, and verification tokens |
|
|
179
|
+
| **CNAME** | `resolveCname()` | `string[]` | Canonical name aliases showing what a hostname points to |
|
|
180
|
+
| **NS** | `resolveNs()` | `string[]` | Authoritative name servers for a domain |
|
|
181
|
+
| **SOA** | `resolveSoa()` | `SoaRecord` | Zone serial number, refresh intervals, and admin contact |
|
|
182
|
+
| **SRV** | `resolveSrv()` | `SrvRecord[]` | Service discovery records with priority, weight, and port |
|
|
183
|
+
| **PTR** | `resolvePtr()` | `string[]` | Maps an IP address back to a hostname |
|
|
184
|
+
| **CAA** | `resolveCaa()` | `CaaRecord[]` | Which certificate authorities can issue certificates for a domain |
|
|
185
|
+
| **NAPTR** | `resolveNaptr()` | `NaptrRecord[]` | Complex service resolution for ENUM and SIP routing |
|
|
186
|
+
| **TLSA** | `resolveTlsa()` | `TlsaRecord[]` | DANE TLS certificate pinning |
|
|
187
|
+
| **ANY** | `resolveAny()` | `AnyRecord[]` | All available record types in a single query |
|
|
183
188
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
189
|
+
```typescript
|
|
190
|
+
const mx = await resolveMx("gmail.com");
|
|
191
|
+
// => [
|
|
192
|
+
// { priority: 5, exchange: 'gmail-smtp-in.l.google.com' },
|
|
193
|
+
// { priority: 10, exchange: 'alt1.gmail-smtp-in.l.google.com' },
|
|
194
|
+
// ]
|
|
195
|
+
|
|
196
|
+
const soa = await resolveSoa("example.com");
|
|
197
|
+
// => {
|
|
198
|
+
// nsname: 'ns1.example.com',
|
|
199
|
+
// hostmaster: 'admin.example.com',
|
|
200
|
+
// serial: 2024010101,
|
|
201
|
+
// refresh: 3600,
|
|
202
|
+
// retry: 900,
|
|
203
|
+
// expire: 604800,
|
|
204
|
+
// minttl: 86400
|
|
205
|
+
// }
|
|
206
|
+
|
|
207
|
+
const srv = await resolveSrv("_sip._tcp.example.com");
|
|
208
|
+
// => [{ priority: 10, weight: 5, port: 5060, name: 'sip.example.com' }]
|
|
209
|
+
|
|
210
|
+
const hosts = await reverse("8.8.8.8");
|
|
211
|
+
// => ['dns.google']
|
|
212
|
+
```
|
|
190
213
|
|
|
191
|
-
###
|
|
214
|
+
### Provider Selection
|
|
215
|
+
|
|
216
|
+
When you don't specify which provider to use, zynor picks the best one for you automatically. It looks at how busy each provider's queue is and routes your request to the one with the most available capacity. This means that under heavy load, requests naturally spread across all your enabled providers rather than piling up on a single one — giving you faster response times and better reliability without any manual balancing on your part.
|
|
217
|
+
|
|
218
|
+
If you need a specific provider for a particular query (for example, Quad9 for its built-in malware filtering, or native for internal DNS resolution), you can always override the automatic selection:
|
|
192
219
|
|
|
193
220
|
```typescript
|
|
194
|
-
|
|
195
|
-
dns.
|
|
196
|
-
dns.setConfigs({ cloudflare: { limit: { concurrency: 15 } } });
|
|
221
|
+
const ips = await resolve4("example.com", "google");
|
|
222
|
+
const mx = await dns.resolveMx("example.com", "cloudflare");
|
|
197
223
|
```
|
|
198
224
|
|
|
199
|
-
|
|
225
|
+
Four providers are included:
|
|
200
226
|
|
|
201
|
-
|
|
227
|
+
| Provider | Protocol | Best For |
|
|
228
|
+
| -------------- | ---------------------------- | ---------------------------------------------------- |
|
|
229
|
+
| **native** | Node.js `dns/promises` | Local/internal DNS, system DNS settings, development |
|
|
230
|
+
| **google** | DNS-over-HTTPS | Production, global services, DNSSEC validation |
|
|
231
|
+
| **cloudflare** | DNS-over-HTTPS | Privacy-sensitive apps, fast global resolution |
|
|
232
|
+
| **quad9** | DNS-over-HTTPS (wire format) | Security-focused apps, built-in malware blocking |
|
|
202
233
|
|
|
203
|
-
###
|
|
234
|
+
### AbortSignal and Timeout
|
|
204
235
|
|
|
205
|
-
|
|
236
|
+
Every DNS method in zynor can be cancelled mid-flight or given a time limit. This is essential for production applications where you can't afford to wait forever on a DNS query that might be hanging due to network issues. The default timeout is 30 seconds, but you can set your own per-request timeout, pass in an `AbortSignal` for manual cancellation, or combine both — whichever fires first will cancel the operation and free up resources immediately.
|
|
206
237
|
|
|
207
238
|
```typescript
|
|
208
|
-
|
|
239
|
+
// Timeout after 5 seconds
|
|
240
|
+
const ips = await resolve4("example.com", { timeout: 5000 });
|
|
241
|
+
|
|
242
|
+
// Manual cancellation
|
|
243
|
+
const controller = new AbortController();
|
|
244
|
+
setTimeout(() => controller.abort(), 3000);
|
|
245
|
+
const mx = await resolveMx("example.com", { signal: controller.signal });
|
|
246
|
+
|
|
247
|
+
// Both together -- whichever fires first cancels the operation
|
|
248
|
+
const txt = await resolveTxt("example.com", {
|
|
249
|
+
signal: controller.signal,
|
|
250
|
+
timeout: 10000,
|
|
251
|
+
});
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
Cancellation works across the entire operation: queue wait, network request, and response parsing.
|
|
255
|
+
|
|
256
|
+
---
|
|
257
|
+
|
|
258
|
+
## DNS Caching
|
|
209
259
|
|
|
210
|
-
|
|
211
|
-
dns.resolve4(hostname: string): Promise<string[]> // IPv4 addresses
|
|
212
|
-
dns.resolve6(hostname: string): Promise<string[]> // IPv6 addresses
|
|
213
|
-
dns.resolveMx(hostname: string): Promise<MxRecord[]> // Mail exchange records
|
|
214
|
-
dns.resolveTxt(hostname: string): Promise<string[][]> // Text records
|
|
215
|
-
dns.resolveCname(hostname: string): Promise<string[]> // Canonical name records
|
|
216
|
-
dns.resolveNs(hostname: string): Promise<string[]> // Name server records
|
|
217
|
-
dns.resolveSoa(hostname: string): Promise<SoaRecord> // Start of Authority
|
|
218
|
-
dns.resolvePtr(ip: string): Promise<string[]> // Reverse DNS lookup
|
|
219
|
-
dns.resolveSrv(hostname: string): Promise<SrvRecord[]> // Service records
|
|
220
|
-
dns.resolveAny(hostname: string): Promise<AnyRecord[]> // All available records
|
|
260
|
+
### Request Deduplication
|
|
221
261
|
|
|
222
|
-
|
|
223
|
-
dns.setConfig(provider: string, config: ProviderConfig): void
|
|
224
|
-
dns.setConfigs(configs: Partial<ZynorConfig>): void
|
|
262
|
+
If your application fires off the same DNS query from multiple places at the same time — say, ten different parts of your code all resolve `example.com` within the same moment — zynor recognizes that these are duplicate requests and only sends a single query to the DNS provider. Every caller that asked for the same record receives the same result as soon as it comes back. This eliminates redundant network calls, reduces your DNS provider usage, and speeds up your application when multiple components depend on the same domain.
|
|
225
263
|
|
|
226
|
-
|
|
227
|
-
|
|
264
|
+
```typescript
|
|
265
|
+
// Only ONE DNS query is made, all three get the same result
|
|
266
|
+
const [a, b, c] = await Promise.all([
|
|
267
|
+
resolve4("example.com"),
|
|
268
|
+
resolve4("example.com"),
|
|
269
|
+
resolve4("example.com"),
|
|
270
|
+
]);
|
|
228
271
|
```
|
|
229
272
|
|
|
230
|
-
|
|
273
|
+
Results are then stored in an LRU cache so subsequent requests are served instantly without any network call.
|
|
231
274
|
|
|
232
|
-
|
|
275
|
+
### LruCache
|
|
276
|
+
|
|
277
|
+
Zynor includes a general-purpose Least Recently Used (LRU) cache that you can use in your own application code — it's the same cache that powers zynor's internal DNS caching. An LRU cache automatically evicts the oldest unused entries when it reaches its maximum size, and each entry can have a time-to-live (TTL) so stale data expires on its own. This is useful for caching API responses, database queries, computed results, or anything else you want to keep in memory temporarily without worrying about unbounded growth:
|
|
233
278
|
|
|
234
279
|
```typescript
|
|
235
|
-
import {
|
|
280
|
+
import { LruCache } from "zynor";
|
|
281
|
+
|
|
282
|
+
const cache = new LruCache<string>(500); // max 500 entries
|
|
283
|
+
|
|
284
|
+
cache.set("key", "value", 60_000); // expires in 60 seconds
|
|
285
|
+
cache.get("key"); // => "value"
|
|
286
|
+
cache.peek("key"); // => "value" (without affecting eviction order)
|
|
287
|
+
cache.has("key"); // => true
|
|
288
|
+
cache.ttl("key"); // => remaining ms until expiry
|
|
289
|
+
cache.delete("key"); // => true
|
|
290
|
+
cache.size; // => 0
|
|
291
|
+
cache.prune(); // remove all expired entries, returns count
|
|
292
|
+
cache.clear(); // remove everything
|
|
293
|
+
|
|
294
|
+
// Iterate non-expired entries
|
|
295
|
+
for (const key of cache.keys()) {
|
|
296
|
+
/* ... */
|
|
297
|
+
}
|
|
298
|
+
for (const value of cache.values()) {
|
|
299
|
+
/* ... */
|
|
300
|
+
}
|
|
301
|
+
for (const [key, value] of cache.entries()) {
|
|
302
|
+
/* ... */
|
|
303
|
+
}
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
---
|
|
236
307
|
|
|
237
|
-
|
|
238
|
-
const addresses = await resolve4('example.com'); // Quick IPv4 resolution
|
|
239
|
-
const mxRecords = await resolveMx('example.com'); // Mail server lookup
|
|
240
|
-
const anyRecords = await resolveAny('example.com'); // Comprehensive DNS query
|
|
308
|
+
## Provider Configuration
|
|
241
309
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
310
|
+
### Concurrency and Rate Limiting
|
|
311
|
+
|
|
312
|
+
Each DNS provider runs its own independent request queue, which means you have fine-grained control over how aggressively your application talks to each provider. You can set how many requests are allowed to be in flight at the same time (`concurrency`), define a rate-limit window (`interval` in milliseconds), and cap how many requests can be sent within that window (`intervalCap`). This prevents you from being throttled or banned by DNS providers and lets you tune performance based on your use case — high throughput for batch processing, or conservative limits for shared environments:
|
|
313
|
+
|
|
314
|
+
```typescript
|
|
315
|
+
const dns = new Zynor({
|
|
316
|
+
google: {
|
|
317
|
+
enabled: true,
|
|
318
|
+
limit: {
|
|
319
|
+
concurrency: 10, // max 10 requests in flight at once
|
|
320
|
+
interval: 1000, // rate limit window in ms
|
|
321
|
+
intervalCap: 500, // max requests per window
|
|
322
|
+
carryoverConcurrencyCount: true,
|
|
323
|
+
},
|
|
324
|
+
},
|
|
325
|
+
cloudflare: {
|
|
326
|
+
enabled: true,
|
|
327
|
+
limit: { concurrency: 5 },
|
|
328
|
+
},
|
|
329
|
+
quad9: { enabled: false },
|
|
330
|
+
native: {
|
|
331
|
+
enabled: true,
|
|
332
|
+
limit: { concurrency: 10, interval: 1000, intervalCap: 750 },
|
|
333
|
+
},
|
|
334
|
+
});
|
|
245
335
|
```
|
|
246
336
|
|
|
247
|
-
|
|
337
|
+
| Option | Type | Description |
|
|
338
|
+
| --------------------------------- | --------- | ----------------------------------------------- |
|
|
339
|
+
| `enabled` | `boolean` | Turn the provider on or off. Default: `true`. |
|
|
340
|
+
| `limit.concurrency` | `number` | Maximum requests running simultaneously. |
|
|
341
|
+
| `limit.interval` | `number` | Time window in ms for rate limiting. |
|
|
342
|
+
| `limit.intervalCap` | `number` | Maximum requests allowed per interval window. |
|
|
343
|
+
| `limit.carryoverConcurrencyCount` | `boolean` | Carry over concurrency count between intervals. |
|
|
248
344
|
|
|
249
|
-
|
|
345
|
+
### Runtime Configuration Updates
|
|
250
346
|
|
|
251
|
-
|
|
252
|
-
- **options**: Optional configuration object (varies by method)
|
|
253
|
-
- **provider**: Optional provider name for specific provider usage (string)
|
|
347
|
+
You can reconfigure providers on the fly without having to tear down and recreate your `Zynor` instance. This is useful when you need to react to changing conditions — for example, disabling a provider that's returning errors, increasing concurrency during off-peak hours, or enabling a provider that was previously turned off. All in-flight requests continue with their original settings; only new requests pick up the changes:
|
|
254
348
|
|
|
255
|
-
|
|
349
|
+
```typescript
|
|
350
|
+
dns.setConfig("google", { enabled: false });
|
|
351
|
+
dns.setConfig("cloudflare", { limit: { concurrency: 20 } });
|
|
256
352
|
|
|
257
|
-
|
|
353
|
+
dns.setConfigs({
|
|
354
|
+
google: { enabled: true, limit: { concurrency: 15 } },
|
|
355
|
+
cloudflare: { enabled: false },
|
|
356
|
+
});
|
|
357
|
+
```
|
|
258
358
|
|
|
359
|
+
---
|
|
259
360
|
|
|
361
|
+
## Email Validation
|
|
260
362
|
|
|
261
|
-
|
|
363
|
+
A full email intelligence engine that validates addresses, identifies the hosting provider, detects disposable and role-based emails, and resolves webmail URLs.
|
|
262
364
|
|
|
263
|
-
|
|
365
|
+
### Basic Validation
|
|
264
366
|
|
|
265
|
-
|
|
367
|
+
Validates an email address in a single call — checking syntax, screening for disposable domains, detecting role-based prefixes like `admin@` or `support@`, and identifying the email provider by domain or MX record lookup. Returns the provider name, free/paid status, role flag, and webmail URL when available.
|
|
266
368
|
|
|
267
|
-
|
|
369
|
+
```typescript
|
|
370
|
+
import { Zynor } from "zynor";
|
|
268
371
|
|
|
269
|
-
|
|
372
|
+
const validator = Zynor.emailValidator;
|
|
270
373
|
|
|
271
|
-
|
|
374
|
+
const result = await validator.validate("user@gmail.com");
|
|
375
|
+
|
|
376
|
+
if (result.success) {
|
|
377
|
+
console.log(result.data.provider); // "Gmail"
|
|
378
|
+
console.log(result.data.email); // "user@gmail.com"
|
|
379
|
+
console.log(result.data.isFree); // true
|
|
380
|
+
console.log(result.data.role); // false
|
|
381
|
+
console.log(result.data.webmail); // "https://mail.google.com"
|
|
382
|
+
} else {
|
|
383
|
+
console.log(result.type); // "Syntax" | "Invalid" | "Rejected" | "Disposable" | "Error"
|
|
384
|
+
console.log(result.email); // the email that failed
|
|
385
|
+
}
|
|
386
|
+
```
|
|
272
387
|
|
|
273
|
-
|
|
274
|
-
- `email`: Email address to validate (string)
|
|
275
|
-
- `deep` (optional): Enable deep validation with additional DNS lookups and extended checks (default: false)
|
|
388
|
+
Validation checks syntax, inappropriate terms, rejected patterns (noreply, marketing senders, platform notifications), disposable domains, and then identifies the email provider through domain matching and MX record analysis.
|
|
276
389
|
|
|
277
|
-
**
|
|
390
|
+
**Success response:**
|
|
278
391
|
|
|
279
|
-
**Success Response Structure:**
|
|
280
392
|
```typescript
|
|
281
393
|
{
|
|
282
394
|
success: true,
|
|
283
395
|
data: {
|
|
284
|
-
provider: string,
|
|
285
|
-
email: string,
|
|
286
|
-
isFree: boolean,
|
|
287
|
-
role: boolean,
|
|
288
|
-
webmail?: string
|
|
396
|
+
provider: string, // "Gmail", "Office 365", "Zoho", etc.
|
|
397
|
+
email: string, // normalized (lowercased) email
|
|
398
|
+
isFree: boolean, // true for free providers (Gmail, Yahoo, etc.)
|
|
399
|
+
role: boolean, // true for admin@, support@, info@, etc.
|
|
400
|
+
webmail?: string, // webmail URL if known
|
|
289
401
|
}
|
|
290
402
|
}
|
|
291
403
|
```
|
|
292
404
|
|
|
293
|
-
**Error
|
|
405
|
+
**Error response:**
|
|
406
|
+
|
|
294
407
|
```typescript
|
|
295
408
|
{
|
|
296
409
|
success: false,
|
|
297
|
-
type:
|
|
410
|
+
type: "Syntax" | "Rejected" | "Invalid" | "Disposable" | "Error",
|
|
298
411
|
email: string,
|
|
299
|
-
role: boolean,
|
|
300
|
-
message?: string
|
|
412
|
+
role: boolean,
|
|
413
|
+
message?: string, // only for "Error" type
|
|
301
414
|
}
|
|
302
415
|
```
|
|
303
416
|
|
|
304
|
-
|
|
417
|
+
### Deep Validation
|
|
305
418
|
|
|
306
|
-
|
|
307
|
-
```typescript
|
|
308
|
-
import { Zynor } from 'zynor';
|
|
419
|
+
When basic validation can't identify the provider, deep validation goes further — probing the domain's web presence and querying IP intelligence services to classify the hosting provider. The deep phase is time-capped (default 3 seconds) and results are cached, so repeated lookups are instant.
|
|
309
420
|
|
|
310
|
-
|
|
421
|
+
```typescript
|
|
422
|
+
const validator = Zynor.createEmailValidator({
|
|
423
|
+
options: {
|
|
424
|
+
enableDeepValidation: true,
|
|
425
|
+
deepValidationOptions: {
|
|
426
|
+
maxTimeout: 3000, // max 3 seconds for the deep validation phase
|
|
427
|
+
http: { timeout: 2500, maxRetry: 0 },
|
|
428
|
+
},
|
|
429
|
+
ipResolver: {
|
|
430
|
+
ipApi: true,
|
|
431
|
+
findip: { enabled: true, apiKey: ["your-api-key"] },
|
|
432
|
+
},
|
|
433
|
+
},
|
|
434
|
+
});
|
|
311
435
|
|
|
312
|
-
|
|
313
|
-
const result = await validator.validate('user@gmail.com');
|
|
314
|
-
if (result.success) {
|
|
315
|
-
console.log('✅ Valid email');
|
|
316
|
-
console.log('Provider:', result.data.provider); // "Gmail"
|
|
317
|
-
console.log('Webmail:', result.data.webmail); // "https://mail.google.com"
|
|
318
|
-
console.log('Role email:', result.data.role); // false
|
|
319
|
-
} else {
|
|
320
|
-
console.log('❌ Invalid email:', result.type);
|
|
321
|
-
}
|
|
436
|
+
const result = await validator.validate("employee@obscure-company.com", true);
|
|
322
437
|
```
|
|
323
438
|
|
|
324
|
-
|
|
439
|
+
Deep validation with abort support:
|
|
440
|
+
|
|
325
441
|
```typescript
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
}
|
|
442
|
+
const result = await validator.validate("user@company.com", {
|
|
443
|
+
deep: true,
|
|
444
|
+
timeout: 5000,
|
|
445
|
+
signal: controller.signal,
|
|
446
|
+
});
|
|
332
447
|
```
|
|
333
448
|
|
|
334
|
-
|
|
449
|
+
The deep phase is always capped by `maxTimeout` (default 3s) so it never blocks indefinitely.
|
|
335
450
|
|
|
336
|
-
|
|
337
|
-
```typescript
|
|
338
|
-
// Gmail detection
|
|
339
|
-
const gmailResult = await validator.validate('john.doe@gmail.com');
|
|
340
|
-
console.log(gmailResult.data?.provider); // "Gmail"
|
|
451
|
+
### Bulk Validation
|
|
341
452
|
|
|
342
|
-
|
|
343
|
-
const outlookResult = await validator.validate('jane@outlook.com');
|
|
344
|
-
console.log(outlookResult.data?.provider); // "Outlook"
|
|
453
|
+
Validate hundreds or thousands of email addresses concurrently with configurable parallelism. Results come back in the same order as the input array, making it easy to map results back to your original data:
|
|
345
454
|
|
|
346
|
-
|
|
347
|
-
const
|
|
348
|
-
|
|
455
|
+
```typescript
|
|
456
|
+
const results = await Zynor.validateBulk(
|
|
457
|
+
["user@gmail.com", "admin@company.com", "test@mailinator.com"],
|
|
458
|
+
{
|
|
459
|
+
concurrency: 10,
|
|
460
|
+
deep: false,
|
|
461
|
+
timeout: 30000,
|
|
462
|
+
},
|
|
463
|
+
);
|
|
349
464
|
|
|
350
|
-
//
|
|
351
|
-
|
|
352
|
-
console.log(corpResult.data?.provider); // "Microsoft"
|
|
465
|
+
// results[0] => { success: true, data: { provider: "Gmail", ... } }
|
|
466
|
+
// results[2] => { success: false, type: "Disposable", ... }
|
|
353
467
|
```
|
|
354
468
|
|
|
355
|
-
|
|
469
|
+
Results are returned in the same order as the input array. Concurrency defaults to the sum of all enabled provider concurrency limits.
|
|
356
470
|
|
|
357
|
-
|
|
358
|
-
```typescript
|
|
359
|
-
// Common role-based emails
|
|
360
|
-
const roleEmails = [
|
|
361
|
-
'admin@company.com',
|
|
362
|
-
'support@service.com',
|
|
363
|
-
'info@business.org',
|
|
364
|
-
'sales@enterprise.net',
|
|
365
|
-
'noreply@platform.io'
|
|
366
|
-
];
|
|
367
|
-
|
|
368
|
-
for (const email of roleEmails) {
|
|
369
|
-
const result = await validator.validate(email);
|
|
370
|
-
if (result.success && result.data.role) {
|
|
371
|
-
console.log(`${email} is a role-based email`);
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
|
-
```
|
|
471
|
+
### Provider Detection
|
|
375
472
|
|
|
376
|
-
|
|
473
|
+
Zynor identifies 60+ email providers by direct domain matching and 100+ by analyzing MX record patterns. Known domains like `gmail.com` or `outlook.com` are detected instantly without any DNS query. For custom domains (like `user@company.com`), zynor looks up the domain's MX records to figure out which provider handles their email:
|
|
377
474
|
|
|
378
|
-
**Webmail URL Extraction:**
|
|
379
475
|
```typescript
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
476
|
+
const validator = Zynor.emailValidator;
|
|
477
|
+
|
|
478
|
+
// Instant detection by domain (no DNS needed)
|
|
479
|
+
await validator.validate("user@gmail.com"); // provider: "Gmail"
|
|
480
|
+
await validator.validate("user@outlook.com"); // provider: "Outlook"
|
|
481
|
+
await validator.validate("user@protonmail.com"); // provider: "ProtonMail"
|
|
482
|
+
await validator.validate("user@qq.com"); // provider: "QQ"
|
|
483
|
+
await validator.validate("user@mail.ru"); // provider: "Mail.ru"
|
|
484
|
+
await validator.validate("user@icloud.com"); // provider: "iCloud"
|
|
485
|
+
await validator.validate("user@zoho.com"); // provider: "Zoho"
|
|
486
|
+
await validator.validate("user@fastmail.com"); // provider: "Fastmail"
|
|
487
|
+
|
|
488
|
+
// Detection by MX record (requires one DNS lookup)
|
|
489
|
+
await validator.validate("user@company.com");
|
|
490
|
+
// MX points to Google => provider: "Gmail"
|
|
491
|
+
|
|
492
|
+
await validator.validate("user@enterprise.org");
|
|
493
|
+
// MX points to Outlook => provider: "Office 365"
|
|
494
|
+
|
|
495
|
+
await validator.validate("user@startup.io");
|
|
496
|
+
// MX points to Amazon SES => provider: "Amazon"
|
|
394
497
|
```
|
|
395
498
|
|
|
396
|
-
|
|
499
|
+
Detected providers include Gmail, Outlook, Office 365, Yahoo, Turbify, AOL, iCloud, ProtonMail, Zoho, Mail.ru, Yandex, GMX, Web.de, Mail.com, Fastmail, AT&T, Comcast, QQ, 163, Naver, Daum, Rackspace, Mimecast, Godaddy, Namecheap, SendGrid, Mailgun, Postmark, Amazon, Elastic Email, Zendesk, Intermedia, Mailjet, Front, and many more.
|
|
500
|
+
|
|
501
|
+
### Disposable Email Detection
|
|
502
|
+
|
|
503
|
+
Detects disposable (temporary) email domains and automated/marketing sender patterns:
|
|
397
504
|
|
|
398
|
-
**Validating Multiple Emails:**
|
|
399
505
|
```typescript
|
|
400
|
-
|
|
401
|
-
const results = await Promise.allSettled(
|
|
402
|
-
emails.map(email => validator.validate(email))
|
|
403
|
-
);
|
|
404
|
-
|
|
405
|
-
return emails.map((email, index) => {
|
|
406
|
-
const result = results[index];
|
|
407
|
-
if (result.status === 'fulfilled' && result.value.success) {
|
|
408
|
-
return {
|
|
409
|
-
email,
|
|
410
|
-
valid: true,
|
|
411
|
-
provider: result.value.data.provider,
|
|
412
|
-
role: result.value.data.role,
|
|
413
|
-
webmail: result.value.data.webmail
|
|
414
|
-
};
|
|
415
|
-
} else {
|
|
416
|
-
return {
|
|
417
|
-
email,
|
|
418
|
-
valid: false,
|
|
419
|
-
error: result.status === 'fulfilled' ? result.value.type : 'validation_failed'
|
|
420
|
-
};
|
|
421
|
-
}
|
|
422
|
-
});
|
|
423
|
-
}
|
|
506
|
+
const validator = Zynor.emailValidator;
|
|
424
507
|
|
|
425
|
-
//
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
'invalid.email',
|
|
429
|
-
'admin@company.com',
|
|
430
|
-
'user@nonexistent-domain.xyz'
|
|
431
|
-
];
|
|
508
|
+
validator.isDisposable("test@mailinator.com"); // true
|
|
509
|
+
validator.isDisposable("user@guerrillamail.com"); // true
|
|
510
|
+
validator.isDisposable("user@gmail.com"); // false
|
|
432
511
|
|
|
433
|
-
|
|
434
|
-
|
|
512
|
+
// Also caught during validation
|
|
513
|
+
const result = await validator.validate("test@tempmail.com");
|
|
514
|
+
// => { success: false, type: "Disposable", ... }
|
|
435
515
|
```
|
|
436
516
|
|
|
437
|
-
|
|
517
|
+
Additionally catches automated senders (noreply@, notifications@), marketing platforms (@mailchimp.com, @sendgrid.net, @hubspot.com), and platform notifications (GitHub, Amazon, Facebook, LinkedIn).
|
|
438
518
|
|
|
439
|
-
|
|
440
|
-
```typescript
|
|
441
|
-
async function handleEmailValidation(email: string) {
|
|
442
|
-
try {
|
|
443
|
-
const result = await validator.validate(email);
|
|
444
|
-
|
|
445
|
-
if (result.success) {
|
|
446
|
-
return {
|
|
447
|
-
status: 'valid',
|
|
448
|
-
data: result.data
|
|
449
|
-
};
|
|
450
|
-
} else {
|
|
451
|
-
// Handle different error types
|
|
452
|
-
switch (result.type) {
|
|
453
|
-
case 'Syntax':
|
|
454
|
-
return { status: 'syntax_error', message: 'Invalid email format' };
|
|
455
|
-
case 'Invalid':
|
|
456
|
-
return { status: 'invalid_domain', message: 'Domain does not exist' };
|
|
457
|
-
case 'Rejected':
|
|
458
|
-
return { status: 'rejected', message: 'Email rejected by provider' };
|
|
459
|
-
case 'Disposable':
|
|
460
|
-
return { status: 'disposable', message: 'Disposable email address' };
|
|
461
|
-
case 'Error':
|
|
462
|
-
return { status: 'validation_error', message: result.message };
|
|
463
|
-
default:
|
|
464
|
-
return { status: 'unknown_error', message: 'Validation failed' };
|
|
465
|
-
}
|
|
466
|
-
}
|
|
467
|
-
} catch (error) {
|
|
468
|
-
return {
|
|
469
|
-
status: 'exception',
|
|
470
|
-
message: error instanceof Error ? error.message : 'Unexpected error'
|
|
471
|
-
};
|
|
472
|
-
}
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
// Usage
|
|
476
|
-
const validationResult = await handleEmailValidation('test@example.com');
|
|
477
|
-
console.log(validationResult);
|
|
478
|
-
```
|
|
519
|
+
### Role-Based Email Detection
|
|
479
520
|
|
|
480
|
-
|
|
521
|
+
Detects 250+ role-based prefixes -- addresses that represent a function rather than a person:
|
|
481
522
|
|
|
482
|
-
**Email Domain Analysis:**
|
|
483
523
|
```typescript
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
if (result.success) {
|
|
488
|
-
const domain = email.split('@')[1];
|
|
489
|
-
return {
|
|
490
|
-
email: result.data.email,
|
|
491
|
-
domain,
|
|
492
|
-
provider: result.data.provider,
|
|
493
|
-
hasWebmail: !!result.data.webmail,
|
|
494
|
-
webmailUrl: result.data.webmail,
|
|
495
|
-
isRoleBased: result.data.role,
|
|
496
|
-
isPersonal: ['Gmail', 'Yahoo', 'Outlook', 'iCloud'].includes(result.data.provider),
|
|
497
|
-
isCorporate: !['Gmail', 'Yahoo', 'Outlook', 'iCloud'].includes(result.data.provider)
|
|
498
|
-
};
|
|
499
|
-
} else {
|
|
500
|
-
return {
|
|
501
|
-
email,
|
|
502
|
-
valid: false,
|
|
503
|
-
errorType: result.type,
|
|
504
|
-
errorMessage: result.message
|
|
505
|
-
};
|
|
506
|
-
}
|
|
507
|
-
}
|
|
524
|
+
const result = await validator.validate("admin@company.com");
|
|
525
|
+
console.log(result.success && result.data.role); // true
|
|
508
526
|
|
|
509
|
-
const
|
|
510
|
-
console.log(
|
|
527
|
+
const result2 = await validator.validate("john.doe@company.com");
|
|
528
|
+
console.log(result2.success && result2.data.role); // false
|
|
511
529
|
```
|
|
512
530
|
|
|
513
|
-
|
|
531
|
+
Recognized prefixes include `admin`, `support`, `info`, `sales`, `billing`, `help`, `contact`, `hr`, `legal`, `marketing`, `security`, `postmaster`, `webmaster`, `noreply`, `abuse`, `careers`, `ceo`, `press`, `media`, `feedback`, `office`, `accounting`, `engineering`, `operations`, and many more.
|
|
514
532
|
|
|
515
|
-
|
|
533
|
+
### Free Domain Detection
|
|
516
534
|
|
|
517
|
-
|
|
535
|
+
Check whether an email or domain belongs to a free email provider:
|
|
518
536
|
|
|
519
|
-
Performs advanced email format validation with structural constraints.
|
|
520
|
-
|
|
521
|
-
**Validation Rules:**
|
|
522
|
-
- Maximum 35 characters for the local part
|
|
523
|
-
- Maximum 3 hyphens in the local part
|
|
524
|
-
- Maximum 3 dots in the local part
|
|
525
|
-
- Maximum 3 dots in the domain part
|
|
526
|
-
- Maximum 2 hyphens in the domain part
|
|
527
|
-
- Maximum 55 total characters in the email address
|
|
528
|
-
- Local part must be at least 2 characters
|
|
529
|
-
- Domain part must be at least 5 characters
|
|
530
|
-
|
|
531
|
-
**Example:**
|
|
532
537
|
```typescript
|
|
533
|
-
|
|
538
|
+
validator.isFreeDomain("user@gmail.com"); // true
|
|
539
|
+
validator.isFreeDomain("yahoo.com"); // true
|
|
540
|
+
validator.isFreeDomain("user@company.com"); // false
|
|
534
541
|
|
|
535
|
-
|
|
536
|
-
validator.
|
|
537
|
-
|
|
542
|
+
// Also in validation results
|
|
543
|
+
const result = await validator.validate("user@gmail.com");
|
|
544
|
+
console.log(result.success && result.data.isFree); // true
|
|
538
545
|
```
|
|
539
546
|
|
|
540
|
-
|
|
547
|
+
### Webmail URL Lookup
|
|
541
548
|
|
|
542
|
-
|
|
549
|
+
Resolve webmail login URLs for 500+ domains including regional variants:
|
|
543
550
|
|
|
544
|
-
**Example:**
|
|
545
551
|
```typescript
|
|
546
|
-
validator.
|
|
547
|
-
validator.isFreeDomain('gmail.com'); // true
|
|
548
|
-
validator.isFreeDomain('user@company.org'); // false
|
|
549
|
-
```
|
|
552
|
+
const validator = Zynor.emailValidator;
|
|
550
553
|
|
|
551
|
-
|
|
554
|
+
validator.getProviderWebmailUrl("Gmail");
|
|
555
|
+
// => "https://mail.google.com"
|
|
552
556
|
|
|
553
|
-
|
|
557
|
+
validator.getProviderWebmailUrl("outlook.com");
|
|
558
|
+
// => "https://outlook.live.com/mail/"
|
|
554
559
|
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
validator.isDisposable('test@mailinator.com'); // true
|
|
558
|
-
validator.isDisposable('john.doe@gmail.com'); // false
|
|
559
|
-
```
|
|
560
|
+
validator.getProviderWebmailUrl("user@yahoo.fr");
|
|
561
|
+
// => "https://fr.mail.yahoo.com/"
|
|
560
562
|
|
|
561
|
-
|
|
563
|
+
validator.getProviderWebmailUrl("Gmail", "google.com");
|
|
564
|
+
// => "https://mail.google.com"
|
|
562
565
|
|
|
563
|
-
|
|
566
|
+
validator.getProviderWebmailUrl("unknown-provider.com");
|
|
567
|
+
// => null
|
|
568
|
+
```
|
|
564
569
|
|
|
565
|
-
|
|
570
|
+
Regional examples: `yahoo.fr` resolves to `https://fr.mail.yahoo.com/`, `hotmail.de` to `https://outlook.live.com/mail/`, `gmx.at` to `https://www.gmx.at/mail/`, `att.net` to `https://currently.att.yahoo.com/`.
|
|
566
571
|
|
|
567
|
-
|
|
568
|
-
```typescript
|
|
569
|
-
validator.isJson('["hello@site.com", "test@a.io"]');
|
|
570
|
-
// Returns: ["hello@site.com", "test@a.io"]
|
|
572
|
+
Also included in validation results:
|
|
571
573
|
|
|
572
|
-
|
|
573
|
-
validator.
|
|
574
|
+
```typescript
|
|
575
|
+
const result = await validator.validate("user@gmail.com");
|
|
576
|
+
console.log(result.success && result.data.webmail);
|
|
577
|
+
// => "https://mail.google.com"
|
|
574
578
|
```
|
|
575
579
|
|
|
576
|
-
|
|
580
|
+
### Email Extraction
|
|
577
581
|
|
|
578
|
-
|
|
579
|
-
- Plain email addresses
|
|
580
|
-
- Comma-separated values
|
|
581
|
-
- URL-encoded strings (automatically decoded)
|
|
582
|
-
- JSON arrays
|
|
582
|
+
Extract valid emails from mixed input formats:
|
|
583
583
|
|
|
584
|
-
**Example:**
|
|
585
584
|
```typescript
|
|
586
|
-
|
|
585
|
+
const validator = Zynor.emailValidator;
|
|
586
|
+
|
|
587
|
+
// Plain emails
|
|
587
588
|
validator.extractEmails(["user@example.com"]);
|
|
588
|
-
//
|
|
589
|
+
// => ["user@example.com"]
|
|
589
590
|
|
|
590
|
-
// Comma-separated
|
|
591
|
-
validator.extractEmails(["
|
|
592
|
-
//
|
|
591
|
+
// Comma-separated
|
|
592
|
+
validator.extractEmails(["a@test.com,b@test.com"]);
|
|
593
|
+
// => ["a@test.com", "b@test.com"]
|
|
593
594
|
|
|
594
595
|
// URL-encoded
|
|
595
|
-
validator.extractEmails(["user%40example.com
|
|
596
|
-
//
|
|
596
|
+
validator.extractEmails(["user%40example.com"]);
|
|
597
|
+
// => ["user@example.com"]
|
|
597
598
|
|
|
598
|
-
// JSON
|
|
599
|
-
validator.extractEmails(['["
|
|
600
|
-
//
|
|
599
|
+
// JSON arrays
|
|
600
|
+
validator.extractEmails(['["a@test.com", "b@test.com"]']);
|
|
601
|
+
// => ["a@test.com", "b@test.com"]
|
|
601
602
|
|
|
602
603
|
// Mixed formats
|
|
603
604
|
validator.extractEmails([
|
|
604
|
-
"
|
|
605
|
-
"test
|
|
606
|
-
'["
|
|
605
|
+
"one@test.com",
|
|
606
|
+
"two@test.com,three@test.com",
|
|
607
|
+
'["four@test.com"]',
|
|
607
608
|
]);
|
|
608
|
-
//
|
|
609
|
+
// => ["one@test.com", "two@test.com", "three@test.com", "four@test.com"]
|
|
609
610
|
```
|
|
610
611
|
|
|
611
|
-
|
|
612
|
-
##### `getProviderWebmailUrl(provider: ProviderName, domainOrEmail: string): string | null`
|
|
613
|
-
##### `getProviderWebmailUrl(domainOrEmail: string): string | null`
|
|
614
|
-
|
|
615
|
-
Retrieves the webmail URL for a given email provider. Supports multiple overloaded signatures for flexibility.
|
|
616
|
-
|
|
617
|
-
**Parameters:**
|
|
618
|
-
- `provider` - Provider name (e.g., "Gmail", "Outlook") or domain/email
|
|
619
|
-
- `domainOrEmail` (optional) - Domain name or full email address
|
|
620
|
-
|
|
621
|
-
**Returns:** Webmail URL string if found, `null` otherwise.
|
|
612
|
+
Utility methods:
|
|
622
613
|
|
|
623
|
-
**Example:**
|
|
624
614
|
```typescript
|
|
625
|
-
//
|
|
626
|
-
validator.
|
|
627
|
-
// Returns: "https://mail.google.com"
|
|
615
|
+
validator.isEmail("john.doe@example.com"); // true
|
|
616
|
+
validator.isEmail("x@y.co"); // false
|
|
628
617
|
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
// Returns: "https://outlook.live.com"
|
|
618
|
+
validator.isJson('["a@test.com", "b@test.com"]');
|
|
619
|
+
// => ["a@test.com", "b@test.com"]
|
|
632
620
|
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
// Returns: "https://mail.yahoo.com"
|
|
636
|
-
|
|
637
|
-
// Using provider name with domain
|
|
638
|
-
validator.getProviderWebmailUrl('Gmail', 'google.com');
|
|
639
|
-
// Returns: "https://mail.google.com"
|
|
640
|
-
|
|
641
|
-
// Unknown provider
|
|
642
|
-
validator.getProviderWebmailUrl('unknown-provider.com');
|
|
643
|
-
// Returns: null
|
|
621
|
+
validator.isJson("not json");
|
|
622
|
+
// => null
|
|
644
623
|
```
|
|
645
624
|
|
|
646
|
-
|
|
625
|
+
### Persistent Caching
|
|
647
626
|
|
|
648
|
-
|
|
627
|
+
Validation results are automatically cached to disk and survive process restarts:
|
|
649
628
|
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
| **MX** | Mail exchange records | Email routing, mail server discovery | `MxRecord[]` |
|
|
655
|
-
| **TXT** | Text records | Domain verification, SPF, DKIM, configuration | `string[][]` |
|
|
656
|
-
| **CNAME** | Canonical name records | Domain aliases, CDN configuration | `string[]` |
|
|
657
|
-
| **NS** | Name server records | DNS delegation, authoritative servers | `string[]` |
|
|
658
|
-
| **SOA** | Start of Authority | Zone information, DNS administration | `SoaRecord` |
|
|
659
|
-
| **SRV** | Service records | Service discovery, load balancing | `SrvRecord[]` |
|
|
660
|
-
| **PTR** | Pointer records (reverse DNS) | IP to domain mapping, security verification | `string[]` |
|
|
661
|
-
| **CAA** | Certificate Authority Authorization | SSL certificate validation | `CaaRecord[]` |
|
|
662
|
-
| **NAPTR** | Name Authority Pointer | Complex service resolution | `NaptrRecord[]` |
|
|
663
|
-
| **TLSA** | Transport Layer Security Authentication | Certificate pinning, security | `TlsaRecord[]` |
|
|
664
|
-
| **ANY** | All available records | Comprehensive domain analysis | `AnyRecord[]` |
|
|
629
|
+
- Results are cached at the domain level so different emails at the same domain share one entry.
|
|
630
|
+
- Entries expire after 48 hours.
|
|
631
|
+
- If a domain was cached as "Unknown" from basic validation, requesting deep validation bypasses the cache and performs a fresh lookup.
|
|
632
|
+
- Falls back to in-memory only if the disk cache directory is not writable.
|
|
665
633
|
|
|
666
|
-
|
|
634
|
+
---
|
|
667
635
|
|
|
668
|
-
|
|
669
|
-
// A records - IPv4 addresses
|
|
670
|
-
const ipv4 = await resolve4('example.com');
|
|
671
|
-
// Returns: ['93.184.216.34']
|
|
636
|
+
## Error Handling
|
|
672
637
|
|
|
673
|
-
|
|
674
|
-
const mailServers = await resolveMx('example.com');
|
|
675
|
-
// Returns: [{ priority: 10, exchange: 'mail.example.com' }]
|
|
638
|
+
Specific error classes for each failure mode:
|
|
676
639
|
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
640
|
+
```typescript
|
|
641
|
+
import {
|
|
642
|
+
InvalidHostnameError,
|
|
643
|
+
NoEnabledProvidersError,
|
|
644
|
+
ProviderNotEnabledError,
|
|
645
|
+
InvalidProviderError,
|
|
646
|
+
} from "zynor";
|
|
647
|
+
|
|
648
|
+
try {
|
|
649
|
+
await resolve4("");
|
|
650
|
+
} catch (err) {
|
|
651
|
+
if (err instanceof InvalidHostnameError) {
|
|
652
|
+
// empty or invalid hostname
|
|
653
|
+
}
|
|
654
|
+
}
|
|
680
655
|
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
656
|
+
try {
|
|
657
|
+
await resolve4("example.com", "google");
|
|
658
|
+
} catch (err) {
|
|
659
|
+
if (err instanceof ProviderNotEnabledError) {
|
|
660
|
+
// provider is disabled
|
|
661
|
+
}
|
|
662
|
+
}
|
|
684
663
|
```
|
|
685
664
|
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
## 🔷 TypeScript Support
|
|
689
|
-
|
|
690
|
-
Zynor is built with TypeScript from the ground up, providing comprehensive type definitions, full IntelliSense support, and compile-time type checking. The library exports all necessary types and interfaces, ensuring type safety across your entire DNS resolution workflow and eliminating runtime type errors.
|
|
691
|
-
|
|
692
|
-
### Complete Type Safety
|
|
665
|
+
DNS errors are returned as Node.js-compatible error objects with `code`, `errno`, `syscall`, and `hostname`:
|
|
693
666
|
|
|
694
667
|
```typescript
|
|
695
|
-
|
|
668
|
+
try {
|
|
669
|
+
await resolve4("this-domain-does-not-exist.invalid");
|
|
670
|
+
} catch (err) {
|
|
671
|
+
err.code; // "ENOTFOUND"
|
|
672
|
+
err.syscall; // "queryA"
|
|
673
|
+
err.hostname; // "this-domain-does-not-exist.invalid"
|
|
674
|
+
}
|
|
675
|
+
```
|
|
696
676
|
|
|
697
|
-
|
|
698
|
-
const mxRecords: MxRecord[] = await resolveMx('example.com');
|
|
699
|
-
const srvRecords: SrvRecord[] = await resolveSrv('_sip._tcp.example.com');
|
|
677
|
+
---
|
|
700
678
|
|
|
701
|
-
|
|
702
|
-
const provider: ProviderName = 'google';
|
|
703
|
-
const addresses = await resolve4('example.com', undefined, provider);
|
|
704
|
-
```
|
|
679
|
+
## TypeScript Support
|
|
705
680
|
|
|
706
|
-
|
|
681
|
+
Written in TypeScript with every type exported:
|
|
707
682
|
|
|
708
683
|
```typescript
|
|
709
|
-
// Core types
|
|
710
684
|
import {
|
|
711
|
-
Zynor,
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
MxRecord,
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
CaaRecord,
|
|
721
|
-
NaptrRecord,
|
|
722
|
-
TlsaRecord,
|
|
723
|
-
AnyRecord,
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
const resolveWithType = async <T>(hostname: string, type: string): Promise<T> => {
|
|
738
|
-
// Type-safe resolution with custom return types
|
|
739
|
-
return await dns.resolveAny(hostname) as T;
|
|
740
|
-
};
|
|
741
|
-
|
|
742
|
-
// Usage with automatic type inference
|
|
743
|
-
const mxRecords = await resolveWithType<MxRecord[]>('example.com', 'MX');
|
|
685
|
+
Zynor,
|
|
686
|
+
ZynorConfig,
|
|
687
|
+
ProviderConfig,
|
|
688
|
+
ProviderName,
|
|
689
|
+
RecordType,
|
|
690
|
+
AbortOptions,
|
|
691
|
+
MxRecord,
|
|
692
|
+
SoaRecord,
|
|
693
|
+
SrvRecord,
|
|
694
|
+
CaaRecord,
|
|
695
|
+
NaptrRecord,
|
|
696
|
+
TlsaRecord,
|
|
697
|
+
AnyRecord,
|
|
698
|
+
RecordWithTtl,
|
|
699
|
+
ResolveOptions,
|
|
700
|
+
ResolveWithTtlOptions,
|
|
701
|
+
LruCache,
|
|
702
|
+
CacheConfig,
|
|
703
|
+
EmailValidator,
|
|
704
|
+
EmailResponse,
|
|
705
|
+
ValidationConfig,
|
|
706
|
+
NoEnabledProvidersError,
|
|
707
|
+
InvalidHostnameError,
|
|
708
|
+
ProviderNotEnabledError,
|
|
709
|
+
InvalidProviderError,
|
|
710
|
+
} from "zynor";
|
|
744
711
|
```
|
|
745
712
|
|
|
713
|
+
Full overload signatures with correct return types:
|
|
746
714
|
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
715
|
+
```typescript
|
|
716
|
+
const strings: string[] = await resolve4("example.com");
|
|
717
|
+
const withTtl: RecordWithTtl[] = await resolve4("example.com", { ttl: true });
|
|
718
|
+
const mx: MxRecord[] = await resolveMx("example.com");
|
|
719
|
+
const soa: SoaRecord = await resolveSoa("example.com");
|
|
720
|
+
```
|
|
753
721
|
|
|
754
722
|
---
|
|
755
|
-
|
|
756
|
-
**Made with ❤️ by [Yuniq Solutions Tech](https://yuniq.solutions)**
|
|
757
|
-
|
|
758
|
-
For more information, visit our [GitHub repository](https://github.com/yuniqsolutions/zynor) or check out our [documentation](https://github.com/yuniqsolutions/zynor#readme).
|