@lacspace/email-verify 1.0.4 → 1.0.6
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 +12 -6
- package/dist/index.cjs +3 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -81,18 +81,24 @@ await smtpCheck("user@example.com", "mx.example.com", { timeout: 8000 });
|
|
|
81
81
|
|
|
82
82
|
## Licensing
|
|
83
83
|
|
|
84
|
-
This package is **free** under the **[Lacspace Free Licence](https://lacspace.com/licenses/lacspace-free-1.0)** —
|
|
84
|
+
This package is **free** under the **[Lacspace Free Licence](https://lacspace.com/licenses/lacspace-free-1.0)** — permissive freedoms. Use it in personal and commercial projects at no cost; just keep the notice.
|
|
85
85
|
|
|
86
86
|
Not every Lacspace package is free. We also offer **Commercial** (paid), **Client-specific**, and **Private** (proprietary) packages under separate terms. See the full **[Lacspace Licence Centre](https://lacspace.com/licenses)**.
|
|
87
87
|
|
|
88
|
+
<!-- LACSPACE-DEV-PLATFORM -->
|
|
89
|
+
|
|
88
90
|
---
|
|
89
91
|
|
|
90
|
-
|
|
92
|
+
## The Lacspace Developer Platform
|
|
91
93
|
|
|
92
|
-
|
|
94
|
+
`@lacspace/email-verify` is part of **63 zero-dependency, isomorphic TypeScript packages** — one standard library for the modern web. Explore the ecosystem:
|
|
93
95
|
|
|
94
|
-
|
|
96
|
+
- 📦 **This package, documented** — https://developer.lacspace.com/packages/email-verify
|
|
97
|
+
- 🗂️ **All 63 packages** — https://developer.lacspace.com/packages
|
|
98
|
+
- 🧭 **Developer handbook** — guides & runnable recipes — https://developer.lacspace.com/handbook
|
|
99
|
+
- 🧪 **Live playground** — run any package in your browser — https://developer.lacspace.com/playground
|
|
100
|
+
- 🖥️ **Finished app templates** — https://templates.lacspace.com
|
|
101
|
+
- 🚀 **Scaffold a full app** — `npm create lacspace-app@latest`
|
|
95
102
|
|
|
96
|
-
|
|
103
|
+
Free under the **[Lacspace Free Licence](https://lacspace.com/licenses/lacspace-free-1.0)** — a permissive, free-to-use licence.
|
|
97
104
|
|
|
98
|
-
<div align="center"><sub>Built with care by <a href="https://lacspace.com">Lacspace</a> · Lacspace Free Licence · <a href="https://github.com/lacspace/npm-packages">source</a></sub></div>
|
package/dist/index.cjs
CHANGED
|
@@ -21,6 +21,9 @@ async function resolveMx(domain) {
|
|
|
21
21
|
function smtpCheck(email, mxHost, opts = {}) {
|
|
22
22
|
const timeout = opts.timeout ?? 1e4;
|
|
23
23
|
const from = opts.fromAddress ?? `verify@${os.hostname() || "localhost"}`;
|
|
24
|
+
if (/[\r\n]/.test(email) || /[\r\n]/.test(mxHost) || /[\r\n]/.test(from)) {
|
|
25
|
+
return Promise.resolve("undeliverable");
|
|
26
|
+
}
|
|
24
27
|
return new Promise((resolve) => {
|
|
25
28
|
const socket = net__default.default.connect({ host: mxHost, port: 25 });
|
|
26
29
|
socket.setEncoding("utf8");
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"names":["dnsResolveMx","hostname","net","validateEmail"],"mappings":";;;;;;;;;;;;AAoDA,eAAsB,UAAU,MAAA,EAAqC;AACnE,EAAA,IAAI;AACF,IAAA,MAAM,OAAA,GAAU,MAAMA,kBAAA,CAAa,MAAM,CAAA;AACzC,IAAA,OAAO,OAAA,CAAQ,KAAK,CAAC,CAAA,EAAG,MAAM,CAAA,CAAE,QAAA,GAAW,EAAE,QAAQ,CAAA;AAAA,EACvD,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,EAAC;AAAA,EACV;AACF;AAMO,SAAS,SAAA,CACd,KAAA,EACA,MAAA,EACA,IAAA,GAAmD,EAAC,EAC9B;AACtB,EAAA,MAAM,OAAA,GAAU,KAAK,OAAA,IAAW,GAAA;AAChC,EAAA,MAAM,OAAO,IAAA,CAAK,WAAA,IAAe,CAAA,OAAA,EAAUC,WAAA,MAAc,WAAW,CAAA,CAAA;
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"names":["dnsResolveMx","hostname","net","validateEmail"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * @lacspace/email-verify\n * Does this mailbox actually exist? — best-effort deliverability checks.\n *\n * Combines syntax + disposable checks (@lacspace/email-validate) with a real MX\n * record lookup and an optional SMTP RCPT probe, over Node's built-in `dns` and\n * `net`. No message is ever sent.\n *\n * ⚠️ Live SMTP verification is inherently unreliable: many servers greylist,\n * accept-all (catch-all), or block probes — expect \"unknown\" often. Treat a\n * positive as \"likely deliverable\", never as a guarantee.\n *\n * Node.js only · zero npm dependencies · fully typed.\n */\n\nimport net from \"node:net\";\nimport { resolveMx as dnsResolveMx } from \"node:dns/promises\";\nimport { hostname } from \"node:os\";\nimport { validateEmail } from \"@lacspace/email-validate\";\n\nexport interface MxRecord {\n exchange: string;\n priority: number;\n}\n\nexport type SmtpVerdict = \"deliverable\" | \"undeliverable\" | \"unknown\";\n\nexport interface VerifyOptions {\n /** Also run the live SMTP RCPT probe (default true). */\n checkSmtp?: boolean;\n /** MAIL FROM address used in the probe. Default `verify@<your hostname>`. */\n fromAddress?: string;\n /** Per-connection timeout in ms (default 10000). */\n timeout?: number;\n /** Extra disposable domains. */\n extraDisposable?: string[];\n}\n\nexport interface VerifyResult {\n email: string;\n /** Overall best-effort verdict: syntax ok, MX present, and SMTP not undeliverable. */\n valid: boolean;\n syntax: boolean;\n disposable: boolean;\n role: boolean;\n mxFound: boolean;\n mxRecords: MxRecord[];\n smtp: SmtpVerdict;\n reason?: string;\n}\n\n/** Look up and sort a domain's MX records (best priority first). */\nexport async function resolveMx(domain: string): Promise<MxRecord[]> {\n try {\n const records = await dnsResolveMx(domain);\n return records.sort((a, b) => a.priority - b.priority);\n } catch {\n return [];\n }\n}\n\n/**\n * Probe a mailbox with SMTP MAIL FROM / RCPT TO (no DATA, nothing sent).\n * Returns \"deliverable\" (250), \"undeliverable\" (550-class), or \"unknown\".\n */\nexport function smtpCheck(\n email: string,\n mxHost: string,\n opts: { fromAddress?: string; timeout?: number } = {},\n): Promise<SmtpVerdict> {\n const timeout = opts.timeout ?? 10000;\n const from = opts.fromAddress ?? `verify@${hostname() || \"localhost\"}`;\n\n // Guard against SMTP command injection: a CR or LF in the email or MX host\n // would let a caller inject additional SMTP commands into the probe.\n if (/[\\r\\n]/.test(email) || /[\\r\\n]/.test(mxHost) || /[\\r\\n]/.test(from)) {\n return Promise.resolve(\"undeliverable\");\n }\n\n return new Promise((resolve) => {\n const socket = net.connect({ host: mxHost, port: 25 });\n socket.setEncoding(\"utf8\");\n socket.setTimeout(timeout);\n\n let buffer = \"\";\n let step = 0;\n let verdict: SmtpVerdict = \"unknown\";\n const steps = [\n `EHLO ${hostname() || \"localhost\"}`,\n `MAIL FROM:<${from}>`,\n `RCPT TO:<${email}>`,\n \"QUIT\",\n ];\n\n const finish = (v: SmtpVerdict) => {\n verdict = v;\n try {\n socket.write(\"QUIT\\r\\n\");\n } catch {\n /* ignore */\n }\n socket.destroy();\n resolve(verdict);\n };\n\n const send = () => {\n if (step < steps.length) socket.write(steps[step]! + \"\\r\\n\");\n };\n\n socket.on(\"connect\", () => {\n /* wait for 220 greeting before first command */\n });\n\n socket.on(\"data\", (chunk: string) => {\n buffer += chunk;\n const lines = buffer.split(\"\\r\\n\");\n buffer = lines.pop() ?? \"\";\n for (const line of lines) {\n if (!/^\\d{3} /.test(line)) continue; // wait for final line of a reply\n const code = parseInt(line.slice(0, 3), 10);\n\n if (step === 0) {\n if (code !== 220) return finish(\"unknown\");\n send(); // EHLO\n step = 1;\n } else if (step === 1) {\n send(); // MAIL FROM\n step = 2;\n } else if (step === 2) {\n if (code >= 400) return finish(\"unknown\");\n send(); // RCPT TO\n step = 3;\n } else if (step === 3) {\n if (code === 250 || code === 251) return finish(\"deliverable\");\n if (code === 550 || code === 551 || code === 553) return finish(\"undeliverable\");\n return finish(\"unknown\");\n }\n }\n });\n\n socket.on(\"timeout\", () => finish(\"unknown\"));\n socket.on(\"error\", () => finish(\"unknown\"));\n socket.on(\"close\", () => resolve(verdict));\n });\n}\n\n/** Full verification: syntax → disposable → MX → (optional) SMTP probe. */\nexport async function verifyEmail(email: string, opts: VerifyOptions = {}): Promise<VerifyResult> {\n const v = validateEmail(email, { extraDisposable: opts.extraDisposable, suggestions: false });\n const base: VerifyResult = {\n email,\n valid: false,\n syntax: v.valid,\n disposable: v.disposable,\n role: v.role,\n mxFound: false,\n mxRecords: [],\n smtp: \"unknown\",\n };\n\n if (!v.valid || !v.domain) return { ...base, reason: \"invalid syntax\" };\n\n const mx = await resolveMx(v.domain);\n base.mxRecords = mx;\n base.mxFound = mx.length > 0;\n if (!base.mxFound) return { ...base, reason: \"no MX records for domain\" };\n\n if (opts.checkSmtp === false) {\n return { ...base, valid: !v.disposable };\n }\n\n const verdict = await smtpCheck(email, mx[0]!.exchange, {\n fromAddress: opts.fromAddress,\n timeout: opts.timeout,\n });\n base.smtp = verdict;\n\n return {\n ...base,\n valid: !v.disposable && verdict !== \"undeliverable\",\n reason: verdict === \"undeliverable\" ? \"mailbox rejected by server\" : undefined,\n };\n}\n"]}
|
package/dist/index.js
CHANGED
|
@@ -15,6 +15,9 @@ async function resolveMx(domain) {
|
|
|
15
15
|
function smtpCheck(email, mxHost, opts = {}) {
|
|
16
16
|
const timeout = opts.timeout ?? 1e4;
|
|
17
17
|
const from = opts.fromAddress ?? `verify@${hostname() || "localhost"}`;
|
|
18
|
+
if (/[\r\n]/.test(email) || /[\r\n]/.test(mxHost) || /[\r\n]/.test(from)) {
|
|
19
|
+
return Promise.resolve("undeliverable");
|
|
20
|
+
}
|
|
18
21
|
return new Promise((resolve) => {
|
|
19
22
|
const socket = net.connect({ host: mxHost, port: 25 });
|
|
20
23
|
socket.setEncoding("utf8");
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"names":["dnsResolveMx"],"mappings":";;;;;;AAoDA,eAAsB,UAAU,MAAA,EAAqC;AACnE,EAAA,IAAI;AACF,IAAA,MAAM,OAAA,GAAU,MAAMA,WAAA,CAAa,MAAM,CAAA;AACzC,IAAA,OAAO,OAAA,CAAQ,KAAK,CAAC,CAAA,EAAG,MAAM,CAAA,CAAE,QAAA,GAAW,EAAE,QAAQ,CAAA;AAAA,EACvD,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,EAAC;AAAA,EACV;AACF;AAMO,SAAS,SAAA,CACd,KAAA,EACA,MAAA,EACA,IAAA,GAAmD,EAAC,EAC9B;AACtB,EAAA,MAAM,OAAA,GAAU,KAAK,OAAA,IAAW,GAAA;AAChC,EAAA,MAAM,OAAO,IAAA,CAAK,WAAA,IAAe,CAAA,OAAA,EAAU,QAAA,MAAc,WAAW,CAAA,CAAA;
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"names":["dnsResolveMx"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * @lacspace/email-verify\n * Does this mailbox actually exist? — best-effort deliverability checks.\n *\n * Combines syntax + disposable checks (@lacspace/email-validate) with a real MX\n * record lookup and an optional SMTP RCPT probe, over Node's built-in `dns` and\n * `net`. No message is ever sent.\n *\n * ⚠️ Live SMTP verification is inherently unreliable: many servers greylist,\n * accept-all (catch-all), or block probes — expect \"unknown\" often. Treat a\n * positive as \"likely deliverable\", never as a guarantee.\n *\n * Node.js only · zero npm dependencies · fully typed.\n */\n\nimport net from \"node:net\";\nimport { resolveMx as dnsResolveMx } from \"node:dns/promises\";\nimport { hostname } from \"node:os\";\nimport { validateEmail } from \"@lacspace/email-validate\";\n\nexport interface MxRecord {\n exchange: string;\n priority: number;\n}\n\nexport type SmtpVerdict = \"deliverable\" | \"undeliverable\" | \"unknown\";\n\nexport interface VerifyOptions {\n /** Also run the live SMTP RCPT probe (default true). */\n checkSmtp?: boolean;\n /** MAIL FROM address used in the probe. Default `verify@<your hostname>`. */\n fromAddress?: string;\n /** Per-connection timeout in ms (default 10000). */\n timeout?: number;\n /** Extra disposable domains. */\n extraDisposable?: string[];\n}\n\nexport interface VerifyResult {\n email: string;\n /** Overall best-effort verdict: syntax ok, MX present, and SMTP not undeliverable. */\n valid: boolean;\n syntax: boolean;\n disposable: boolean;\n role: boolean;\n mxFound: boolean;\n mxRecords: MxRecord[];\n smtp: SmtpVerdict;\n reason?: string;\n}\n\n/** Look up and sort a domain's MX records (best priority first). */\nexport async function resolveMx(domain: string): Promise<MxRecord[]> {\n try {\n const records = await dnsResolveMx(domain);\n return records.sort((a, b) => a.priority - b.priority);\n } catch {\n return [];\n }\n}\n\n/**\n * Probe a mailbox with SMTP MAIL FROM / RCPT TO (no DATA, nothing sent).\n * Returns \"deliverable\" (250), \"undeliverable\" (550-class), or \"unknown\".\n */\nexport function smtpCheck(\n email: string,\n mxHost: string,\n opts: { fromAddress?: string; timeout?: number } = {},\n): Promise<SmtpVerdict> {\n const timeout = opts.timeout ?? 10000;\n const from = opts.fromAddress ?? `verify@${hostname() || \"localhost\"}`;\n\n // Guard against SMTP command injection: a CR or LF in the email or MX host\n // would let a caller inject additional SMTP commands into the probe.\n if (/[\\r\\n]/.test(email) || /[\\r\\n]/.test(mxHost) || /[\\r\\n]/.test(from)) {\n return Promise.resolve(\"undeliverable\");\n }\n\n return new Promise((resolve) => {\n const socket = net.connect({ host: mxHost, port: 25 });\n socket.setEncoding(\"utf8\");\n socket.setTimeout(timeout);\n\n let buffer = \"\";\n let step = 0;\n let verdict: SmtpVerdict = \"unknown\";\n const steps = [\n `EHLO ${hostname() || \"localhost\"}`,\n `MAIL FROM:<${from}>`,\n `RCPT TO:<${email}>`,\n \"QUIT\",\n ];\n\n const finish = (v: SmtpVerdict) => {\n verdict = v;\n try {\n socket.write(\"QUIT\\r\\n\");\n } catch {\n /* ignore */\n }\n socket.destroy();\n resolve(verdict);\n };\n\n const send = () => {\n if (step < steps.length) socket.write(steps[step]! + \"\\r\\n\");\n };\n\n socket.on(\"connect\", () => {\n /* wait for 220 greeting before first command */\n });\n\n socket.on(\"data\", (chunk: string) => {\n buffer += chunk;\n const lines = buffer.split(\"\\r\\n\");\n buffer = lines.pop() ?? \"\";\n for (const line of lines) {\n if (!/^\\d{3} /.test(line)) continue; // wait for final line of a reply\n const code = parseInt(line.slice(0, 3), 10);\n\n if (step === 0) {\n if (code !== 220) return finish(\"unknown\");\n send(); // EHLO\n step = 1;\n } else if (step === 1) {\n send(); // MAIL FROM\n step = 2;\n } else if (step === 2) {\n if (code >= 400) return finish(\"unknown\");\n send(); // RCPT TO\n step = 3;\n } else if (step === 3) {\n if (code === 250 || code === 251) return finish(\"deliverable\");\n if (code === 550 || code === 551 || code === 553) return finish(\"undeliverable\");\n return finish(\"unknown\");\n }\n }\n });\n\n socket.on(\"timeout\", () => finish(\"unknown\"));\n socket.on(\"error\", () => finish(\"unknown\"));\n socket.on(\"close\", () => resolve(verdict));\n });\n}\n\n/** Full verification: syntax → disposable → MX → (optional) SMTP probe. */\nexport async function verifyEmail(email: string, opts: VerifyOptions = {}): Promise<VerifyResult> {\n const v = validateEmail(email, { extraDisposable: opts.extraDisposable, suggestions: false });\n const base: VerifyResult = {\n email,\n valid: false,\n syntax: v.valid,\n disposable: v.disposable,\n role: v.role,\n mxFound: false,\n mxRecords: [],\n smtp: \"unknown\",\n };\n\n if (!v.valid || !v.domain) return { ...base, reason: \"invalid syntax\" };\n\n const mx = await resolveMx(v.domain);\n base.mxRecords = mx;\n base.mxFound = mx.length > 0;\n if (!base.mxFound) return { ...base, reason: \"no MX records for domain\" };\n\n if (opts.checkSmtp === false) {\n return { ...base, valid: !v.disposable };\n }\n\n const verdict = await smtpCheck(email, mx[0]!.exchange, {\n fromAddress: opts.fromAddress,\n timeout: opts.timeout,\n });\n base.smtp = verdict;\n\n return {\n ...base,\n valid: !v.disposable && verdict !== \"undeliverable\",\n reason: verdict === \"undeliverable\" ? \"mailbox rejected by server\" : undefined,\n };\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lacspace/email-verify",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.6",
|
|
4
4
|
"description": "Best-effort email deliverability checks for Node — syntax + disposable, MX record lookup and an optional SMTP RCPT probe (no mail sent). Zero npm dependencies.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
],
|
|
44
44
|
"author": "Lacspace <contact@lacspace.com>",
|
|
45
45
|
"license": "SEE LICENSE IN LICENSE",
|
|
46
|
-
"homepage": "https://lacspace.com/packages",
|
|
46
|
+
"homepage": "https://developer.lacspace.com/packages/email-verify",
|
|
47
47
|
"repository": {
|
|
48
48
|
"type": "git",
|
|
49
49
|
"url": "git+https://github.com/lacspace/npm-packages.git",
|