mailintel 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ahmed Atef
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,240 @@
1
+ # Mailintel
2
+
3
+ Resolve email providers and IMAP/SMTP settings from any email address or domain. Zero npm dependencies.
4
+
5
+ If you're building an app where users connect their email - a CRM, helpdesk, mail client, or anything that sends/receives on their behalf - you normally have to ask them to manually enter their SMTP host, IMAP port, SSL toggle, and username. Mailintel does this automatically. Give it an email or domain and it returns everything you need to connect.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npm install mailintel
11
+ ```
12
+
13
+ ## API
14
+
15
+ ```typescript
16
+ import { mailintel } from 'mailintel';
17
+
18
+ // With an email - username is resolved automatically
19
+ const result = await mailintel('ahmed@atef.dev');
20
+ // result.provider => 'google'
21
+ // result.connection.imap => { host: 'imap.gmail.com', port: 993, secure: true }
22
+ // result.connection.username => 'ahmed@atef.dev'
23
+ // result.type => 'business'
24
+ // result.confidence => 'high'
25
+
26
+ // With just a domain - same result, username stays as template
27
+ const result = await mailintel('atef.dev');
28
+ // result.connection.username => '%EMAILADDRESS%'
29
+
30
+ // Multiple inputs at once
31
+ const results = await mailintel(['ahmed@atef.dev', 'example.com', 'user@gmail.com']);
32
+ ```
33
+
34
+ <details>
35
+ <summary>Full result example</summary>
36
+
37
+ ```json
38
+ {
39
+ "email": "ahmed@atef.dev",
40
+ "domain": "atef.dev",
41
+ "provider": "google",
42
+ "mx": "smtp.google.com",
43
+ "type": "business",
44
+ "isFree": false,
45
+ "hasMx": true,
46
+ "connection": {
47
+ "imap": { "host": "imap.gmail.com", "port": 993, "secure": true },
48
+ "smtp": { "host": "smtp.gmail.com", "port": 465, "secure": true },
49
+ "username": "ahmed@atef.dev",
50
+ "usernameTemplate": "%EMAILADDRESS%"
51
+ },
52
+ "confidence": "high",
53
+ "source": "mx-lookup",
54
+ "raw": {
55
+ "mx": [{ "priority": 1, "exchange": "smtp.google.com" }],
56
+ "dns": { "Status": 0, "Answer": [{ "name": "atef.dev", "type": 15, "TTL": 300, "data": "1 smtp.google.com." }] },
57
+ "ispdb": null
58
+ }
59
+ }
60
+ ```
61
+
62
+ The API and CLI return the same structure. The CLI's `--json` flag outputs this exact format.
63
+
64
+ </details>
65
+
66
+ ### Factory
67
+
68
+ Use `createMailintel()` for reusable config, custom overrides, or a shared cache:
69
+
70
+ ```typescript
71
+ import { createMailintel } from 'mailintel';
72
+
73
+ const lookup = createMailintel({
74
+ timeout: 3000,
75
+ providers: {
76
+ 'megacorp.io': {
77
+ provider: 'internal',
78
+ imap: { host: 'imap.megacorp.io', port: 993, secure: true },
79
+ smtp: { host: 'smtp.megacorp.io', port: 465, secure: true },
80
+ },
81
+ },
82
+ });
83
+
84
+ await lookup('admin@megacorp.io'); // uses override, no network call
85
+ lookup.clearCache();
86
+ ```
87
+
88
+ ### Options
89
+
90
+ | Option | Type | Default | Description |
91
+ |--------|------|---------|-------------|
92
+ | `timeout` | `number` | `5000` | Network request timeout (ms) |
93
+ | `cache` | `boolean` | `true` | In-memory domain caching |
94
+ | `cacheTtl` | `number` | `1800000` | Cache TTL in ms (30 min) |
95
+ | `mxLookup` | `boolean \| 'cloudflare' \| 'google'` | `true` | `true` or `'cloudflare'` = Cloudflare DNS, `'google'` = Google DNS, `false` = disabled |
96
+ | `ispdb` | `boolean` | `true` | Thunderbird ISPDB fallback |
97
+ | `fetch` | `typeof fetch` | `globalThis.fetch` | Custom fetch (useful for proxies or testing) |
98
+ | `providers` | `Record<string, ProviderOverride>` | - | Domain-to-provider overrides |
99
+ | `mxOverrides` | `Record<string, ProviderOverride>` | - | MX suffix-to-provider overrides |
100
+
101
+ ## CLI
102
+
103
+ ```bash
104
+ npx mailintel ahmed@atef.dev
105
+ ```
106
+
107
+ ```
108
+ ahmed@atef.dev
109
+
110
+ provider google
111
+ domain atef.dev
112
+ type business
113
+ confidence high
114
+ mx smtp.google.com
115
+ source mx-lookup
116
+
117
+ IMAP imap.gmail.com:993 SSL
118
+ SMTP smtp.gmail.com:465 SSL
119
+ Username ahmed@atef.dev
120
+ ```
121
+
122
+ Supports multiple emails, domain-only lookups, and JSON output:
123
+
124
+ ```bash
125
+ mailintel user@gmail.com admin@example.com # multiple emails
126
+ mailintel atef.dev # domain-only lookup
127
+ mailintel ahmed@atef.dev --json # machine-readable JSON
128
+ ```
129
+
130
+ ### CLI options
131
+
132
+ | Flag | Description |
133
+ |------|-------------|
134
+ | `--json` | Output raw JSON |
135
+ | `--timeout <ms>` | Network timeout (default: 5000) |
136
+ | `--resolver <name>` | DNS resolver: `cloudflare` (default) or `google` |
137
+ | `--no-cache` | Disable caching |
138
+ | `--no-ispdb` | Skip Thunderbird ISPDB fallback |
139
+ | `-h, --help` | Show help |
140
+ | `-v, --version` | Show version |
141
+
142
+ ## How it works
143
+
144
+ ```mermaid
145
+ flowchart TD
146
+ classDef external fill:#dbeafe,stroke:#3b82f6,color:#1e3a5f
147
+
148
+ A(["ahmed@atef.dev"]) --> B[Check cache]
149
+
150
+ B -->|hit| Z1([return cached result])
151
+ B -->|miss| C{User domain override?}
152
+
153
+ C -->|hit| Z2(["return — confidence: high, source: override"])
154
+ C -->|miss| D{"Known domain?<br/>gmail.com, outlook.com<br/>~50 built-in"}
155
+
156
+ D -->|hit| Z3(["return — confidence: high, source: known-domain"])
157
+ D -->|miss| E["🌐 DNS MX lookup<br/>cloudflare-dns.com or dns.google"]
158
+
159
+ E --> F{User MX override?}
160
+ F -->|hit| Z4(["return — confidence: high, source: mx-lookup"])
161
+ F -->|miss| G{"Known MX pattern?<br/>*.google.com, *.zoho.com<br/>~30 built-in"}
162
+
163
+ G -->|hit| H["Get provider settings<br/>apply regional variant if needed"]
164
+ G -->|miss| I["🌐 Thunderbird ISPDB<br/>autoconfig.thunderbird.net"]
165
+
166
+ H --> J(["return — confidence: high, source: mx-lookup"])
167
+ I -->|found| K(["return — confidence: medium, source: mx-lookup"])
168
+ I -->|not found| L(["return — connection: null, confidence: low"])
169
+
170
+ class E,I external
171
+ ```
172
+
173
+ At most 2 HTTP requests per uncached lookup - one DNS query and one ISPDB fetch. Known domains resolve instantly with zero network calls.
174
+
175
+ ### How it gets the data
176
+
177
+ Every email domain has **MX records** in DNS that say which mail server handles its email. `atef.dev` points to `smtp.google.com` (Google Workspace), another domain might point to `mx.zoho.com` (Zoho).
178
+
179
+ Mailintel queries these using **DNS-over-HTTPS** - plain HTTPS requests to public DNS resolvers, not system-level DNS calls. Works in serverless functions, edge runtimes, and anywhere `fetch` is available. No native modules, no UDP sockets, no special permissions.
180
+
181
+ | Service | URL | What it returns |
182
+ |---------|-----|-----------------|
183
+ | **Cloudflare DNS** (default) | `cloudflare-dns.com/dns-query?name=atef.dev&type=MX` * | MX records - which mail server handles the domain |
184
+ | **Google DNS** (alternative) | `dns.google/resolve?name=atef.dev&type=MX` | Same MX records, different resolver |
185
+ | **Thunderbird ISPDB** (fallback) | `autoconfig.thunderbird.net/v1.1/{domain}` | IMAP/SMTP settings from Mozilla's open database |
186
+
187
+ \* Requires an `Accept: application/dns-json` header. Opening this URL in a browser returns an HTML page, not JSON. Mailintel sets this header automatically.
188
+
189
+ **Rate limits**: Cloudflare DNS has no published limits and is free ([docs](https://developers.cloudflare.com/1.1.1.1/encryption/dns-over-https/make-api-requests/dns-json/)). Google DNS has undisclosed limits and returns HTTP 429 when exceeded ([docs](https://developers.google.com/speed/public-dns/docs/doh)). Thunderbird ISPDB is a static file server with no published limits. The built-in cache means you'll rarely hit any of these more than once per domain.
190
+
191
+ Once the MX server is known, it's matched against ~30 provider patterns. For well-known free domains like `gmail.com`, the network is skipped entirely - settings come from a built-in map of [~50 known domains](src/data/domains.ts).
192
+
193
+ ## Result type
194
+
195
+ ```typescript
196
+ interface MailintelResult {
197
+ email: string; // input email, or '' for domain-only lookups
198
+ domain: string;
199
+ provider: string | null; // 'google', 'microsoft', 'zoho', etc.
200
+ mx: string | null; // primary MX hostname
201
+ type: 'free' | 'business' | 'unknown';
202
+ isFree: boolean;
203
+ hasMx: boolean;
204
+ connection: { // null when settings can't be determined
205
+ imap: { host: string; port: number; secure: boolean };
206
+ smtp: { host: string; port: number; secure: boolean };
207
+ username: string; // resolved: 'ahmed@atef.dev'
208
+ usernameTemplate: string; // raw template: '%EMAILADDRESS%'
209
+ } | null;
210
+ confidence: 'high' | 'medium' | 'low';
211
+ source: 'override' | 'known-domain' | 'mx-lookup' | null;
212
+ raw: { mx, dns, ispdb }; // raw data for debugging
213
+ }
214
+ ```
215
+
216
+ ### Username
217
+
218
+ `username` is the resolved value you pass directly to the mail server. When the input is a domain (no email), it stays as the raw template since there's no email to resolve.
219
+
220
+ `usernameTemplate` is the raw template, useful if you're building a config UI. Templates follow the [Thunderbird ISPDB convention](https://wiki.mozilla.org/Thunderbird:Autoconfiguration:ConfigFileFormat):
221
+
222
+ | Template | Resolves to | Used by |
223
+ |----------|------------|---------|
224
+ | `%EMAILADDRESS%` | `ahmed@atef.dev` | Nearly all modern providers |
225
+ | `%EMAILLOCALPART%` | `ahmed` | Some older/self-hosted setups |
226
+ | `%EMAILDOMAIN%` | `atef.dev` | Rare |
227
+
228
+ All built-in providers use `%EMAILADDRESS%`. The ISPDB fallback may return any of the three depending on what Thunderbird's database has for that provider.
229
+
230
+ ## Supported providers
231
+
232
+ Google, Microsoft, Yahoo, Zoho (with EU/IN/AU/JP regional variants), iCloud, Fastmail, ProtonMail, GoDaddy, Titan, OVH, IONOS, Hostinger, Yandex, AOL, Rackspace, DreamHost - plus any provider discoverable via MX records or Thunderbird ISPDB.
233
+
234
+ ## Contributing
235
+
236
+ Missing a provider or found incorrect settings? [Open an issue](https://github.com/AhmedAtef07/mailintel/issues) or submit a PR.
237
+
238
+ ## License
239
+
240
+ MIT