From 8494c2b4bdab9dad0539812dd358a578c3b12055 Mon Sep 17 00:00:00 2001 From: fadyehabamer Date: Thu, 24 Sep 2026 14:48:16 +0300 Subject: [PATCH 01/11] fix: avoid consecutive dots when the email already contains dots "john.doe@gmail.com" produced 96 results of which 64 contained ".." (invalid addresses). Existing dots are now removed before generating, giving the correct 64 distinct variations. --- main.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/main.js b/main.js index 7e9ceff..c084f45 100644 --- a/main.js +++ b/main.js @@ -1,6 +1,11 @@ function generateEmailVariations(email) { let [localPart, domain] = email.split("@"); + // Gmail ignores dots in the username, so start from the dot-free form. + // Otherwise existing dots get combined with inserted ones and produce + // invalid addresses such as "john..doe@gmail.com". + localPart = localPart.replace(/\./g, ""); + let variations = new Set(); function addDots(prefix, remaining) { From d2f0dee0a1ce099f769d592cf947b6ec1d651d8b Mon Sep 17 00:00:00 2001 From: fadyehabamer Date: Thu, 24 Sep 2026 14:48:28 +0300 Subject: [PATCH 02/11] fix: keep +tag suffix unchanged when generating dot variations Previously dots were inserted around and inside the "+tag" part (e.g. "ab.+n.e.w.s@gmail.com"); only the username before "+" is dot-insensitive in Gmail. --- main.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/main.js b/main.js index c084f45..db1e5a4 100644 --- a/main.js +++ b/main.js @@ -4,13 +4,16 @@ function generateEmailVariations(email) { // Gmail ignores dots in the username, so start from the dot-free form. // Otherwise existing dots get combined with inserted ones and produce // invalid addresses such as "john..doe@gmail.com". - localPart = localPart.replace(/\./g, ""); + // A "+tag" suffix is kept as-is; only the username before it is varied. + let plusIndex = localPart.indexOf("+"); + let tag = plusIndex === -1 ? "" : localPart.slice(plusIndex); + localPart = (plusIndex === -1 ? localPart : localPart.slice(0, plusIndex)).replace(/\./g, ""); let variations = new Set(); function addDots(prefix, remaining) { if (!remaining) { - variations.add(prefix + "@" + domain); + variations.add(prefix + tag + "@" + domain); return; } From b9fb66665384015baf42352abf4e0e360e723c25 Mon Sep 17 00:00:00 2001 From: fadyehabamer Date: Thu, 24 Sep 2026 14:48:41 +0300 Subject: [PATCH 03/11] fix: accept +tags and long TLDs, reject malformed dots in email validation The old pattern rejected valid addresses such as "a+b@gmail.com" and "x@example.photography" while accepting invalid ones like "a..b@gmail.com", ".ab@gmail.com" and "a@b..com". --- main.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/main.js b/main.js index db1e5a4..a92001d 100644 --- a/main.js +++ b/main.js @@ -81,7 +81,10 @@ function generateAndDisplayVariations() { } function validateEmail(email) { - var re = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,6}$/; + // Local part: dot-separated atoms (no leading, trailing or double dots), + // "+" allowed for tags. Domain: dot-separated labels that don't start or + // end with "-", followed by a TLD of 2+ letters (e.g. ".photography"). + var re = /^[a-zA-Z0-9_%+-]+(\.[a-zA-Z0-9_%+-]+)*@([a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$/; return re.test(email); } From 66c91cddf3138a08c9dd70fca553c2f9626c86f1 Mon Sep 17 00:00:00 2001 From: fadyehabamer Date: Thu, 24 Sep 2026 14:49:05 +0300 Subject: [PATCH 04/11] fix: allow generating variations for a different email without reloading After the first run the input was disabled and re-submitting showed an "already generated" alert, so the page had to be reloaded to try another address. The export also re-read the input instead of using the list shown. The displayed email/variations are now kept in state and exported as-is. --- main.js | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/main.js b/main.js index a92001d..2546fa2 100644 --- a/main.js +++ b/main.js @@ -31,11 +31,13 @@ function generateEmailVariations(email) { return Array.from(variations); } -let previouslyGeneratedEmail = ''; +// Email and variations currently shown in the table (used by the export). +let currentEmail = ''; +let currentVariations = []; function generateAndDisplayVariations() { let emailInput = document.getElementById('emailInput'); - let email = emailInput.value; + let email = emailInput.value.trim(); let errorMsg = document.getElementById('errorMsg'); let exportBtn = document.getElementById('exportBtn'); @@ -46,12 +48,6 @@ function generateAndDisplayVariations() { return; } - // Check if the email is the same as the previously generated one - if (email === previouslyGeneratedEmail) { - alert("This email has already been generated!"); - return; - } - let resultsTable = document.getElementById('results'); let validEmails = generateEmailVariations(email); @@ -73,11 +69,8 @@ function generateAndDisplayVariations() { // Enable the export button exportBtn.disabled = false; - // Set this email as the previously generated one - previouslyGeneratedEmail = email; - - // Disable the email input to prevent changing the email - emailInput.disabled = true; + currentEmail = email; + currentVariations = validEmails; } function validateEmail(email) { @@ -89,8 +82,10 @@ function validateEmail(email) { } function exportToExcel() { - let email = document.getElementById('emailInput').value; - let validEmails = generateEmailVariations(email); + // Export exactly what is displayed, even if the input was edited since. + let email = currentEmail; + let validEmails = currentVariations; + if (!validEmails.length) return; let wb = XLSX.utils.book_new(); let ws_name = "EmailVariations"; From 681f00222a176597ee0d92f7a5adde60e0456622 Mon Sep 17 00:00:00 2001 From: fadyehabamer Date: Thu, 24 Sep 2026 14:49:15 +0300 Subject: [PATCH 05/11] fix(a11y): show validation errors inline in a live region instead of alert() Uses the previously unused #errorMsg element with role="alert", marks the input aria-invalid and links it via aria-describedby. --- index.html | 4 ++-- main.js | 6 +++++- style.css | 5 +++++ 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/index.html b/index.html index 7251c5a..79f36dd 100644 --- a/index.html +++ b/index.html @@ -13,14 +13,14 @@

Email Variations Generator

FEA in the url is an abbr for : Fady Ehab Amer

- +
- + diff --git a/main.js b/main.js index 2546fa2..f69a041 100644 --- a/main.js +++ b/main.js @@ -44,9 +44,13 @@ function generateAndDisplayVariations() { // Email validation if (!validateEmail(email)) { - alert("Please enter a valid email address!"); + errorMsg.textContent = "Please enter a valid email address."; + emailInput.setAttribute('aria-invalid', 'true'); + emailInput.focus(); return; } + errorMsg.textContent = ''; + emailInput.removeAttribute('aria-invalid'); let resultsTable = document.getElementById('results'); let validEmails = generateEmailVariations(email); diff --git a/style.css b/style.css index e4f97d1..55caad9 100644 --- a/style.css +++ b/style.css @@ -81,3 +81,8 @@ td { .controls button { flex-grow: 1; } + +#errorMsg { + color: #b00020; + font-weight: bold; +} From b65085d069278a278bd1f00d29987900e01593b2 Mon Sep 17 00:00:00 2001 From: fadyehabamer Date: Thu, 24 Sep 2026 14:49:40 +0300 Subject: [PATCH 06/11] fix: cap generated variations to keep the page responsive The count doubles with each username character (2^(n-1)); a 20-char username produced 524,288 table cells and longer ones never finished. Output is now capped at 8192 (all variations for usernames up to 14 characters) and a status message reports the total when truncated. --- index.html | 1 + main.js | 20 +++++++++++++++++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/index.html b/index.html index 79f36dd..0d2f825 100644 --- a/index.html +++ b/index.html @@ -21,6 +21,7 @@

FEA in the url is an abbr for : Fady Eh +

diff --git a/main.js b/main.js index f69a041..4879faf 100644 --- a/main.js +++ b/main.js @@ -1,4 +1,15 @@ -function generateEmailVariations(email) { +// The number of variations doubles with every character of the username +// (2^(n-1)), so a long username would freeze the page. Cap the output; 8192 +// covers every variation of usernames up to 14 characters. +const MAX_VARIATIONS = 8192; + +// Total number of dot variations for an email (may exceed MAX_VARIATIONS). +function countEmailVariations(email) { + let localPart = email.split("@")[0].split("+")[0].replace(/\./g, ""); + return 2 ** Math.max(localPart.length - 1, 0); +} + +function generateEmailVariations(email, limit = MAX_VARIATIONS) { let [localPart, domain] = email.split("@"); // Gmail ignores dots in the username, so start from the dot-free form. @@ -12,6 +23,8 @@ function generateEmailVariations(email) { let variations = new Set(); function addDots(prefix, remaining) { + if (variations.size >= limit) return; + if (!remaining) { variations.add(prefix + tag + "@" + domain); return; @@ -39,6 +52,7 @@ function generateAndDisplayVariations() { let emailInput = document.getElementById('emailInput'); let email = emailInput.value.trim(); let errorMsg = document.getElementById('errorMsg'); + let statusMsg = document.getElementById('statusMsg'); let exportBtn = document.getElementById('exportBtn'); @@ -54,6 +68,10 @@ function generateAndDisplayVariations() { let resultsTable = document.getElementById('results'); let validEmails = generateEmailVariations(email); + let total = countEmailVariations(email); + statusMsg.textContent = total > validEmails.length + ? `Showing the first ${validEmails.length.toLocaleString()} of ${total.toLocaleString()} possible variations.` + : `${validEmails.length.toLocaleString()} variations generated.`; // Clear previous results resultsTable.innerHTML = ''; From 0b10b51fd3cd97e74fc8737e30a0079027743c61 Mon Sep 17 00:00:00 2001 From: fadyehabamer Date: Thu, 24 Sep 2026 14:49:58 +0300 Subject: [PATCH 07/11] fix(html): close heading tags correctly and use a single h1 The subtitle opened with

but closed with

, and the page had no h1. The title is now the h1 and the attribution line a paragraph. --- index.html | 4 ++-- style.css | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/index.html b/index.html index 0d2f825..fc2c245 100644 --- a/index.html +++ b/index.html @@ -9,8 +9,8 @@ -

Email Variations Generator

-

FEA in the url is an abbr for : Fady Ehab Amer

+

Email Variations Generator

+

FEA in the URL is an abbreviation for: Fady Ehab Amer

diff --git a/style.css b/style.css index 55caad9..9a1f9dd 100644 --- a/style.css +++ b/style.css @@ -4,7 +4,8 @@ body { background-color: #f4f4f4; } -h2 { +h1, +.subtitle { text-align: center; color: #333; } From 1b0468e5fcd57144a75015e509fa05fed7102b69 Mon Sep 17 00:00:00 2001 From: fadyehabamer Date: Thu, 24 Sep 2026 14:49:58 +0300 Subject: [PATCH 08/11] feat: add meta description to index.html --- index.html | 1 + 1 file changed, 1 insertion(+) diff --git a/index.html b/index.html index fc2c245..2012527 100644 --- a/index.html +++ b/index.html @@ -5,6 +5,7 @@ Email Variations Generator + From cb329334192b1febcdf67681cc11b60fce214439 Mon Sep 17 00:00:00 2001 From: fadyehabamer Date: Thu, 24 Sep 2026 14:50:13 +0300 Subject: [PATCH 09/11] feat: generate variations when pressing Enter in the email field Wraps the input and buttons in a form (novalidate, so the app's own validation message is used) with the generate button as its submit. --- index.html | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/index.html b/index.html index 2012527..d9da2c2 100644 --- a/index.html +++ b/index.html @@ -13,12 +13,14 @@

Email Variations Generator

FEA in the URL is an abbreviation for: Fady Ehab Amer

- - -
- - -
+
+ + +
+ + +
+
From 2a23f4264cbddedd1f6ae3dde02be8f0ffc6170f Mon Sep 17 00:00:00 2001 From: fadyehabamer Date: Thu, 24 Sep 2026 14:50:32 +0300 Subject: [PATCH 10/11] chore: upgrade SheetJS from 0.17.0 to 0.20.3 via the official CDN SheetJS no longer publishes to npm/cdnjs (cdnjs stops at 0.18.5), and versions before 0.20.2 carry CVE-2023-30533 and CVE-2024-22363. Those affect parsing rather than the write-only use here, but staying on a maintained release avoids shipping known-vulnerable code. Export was verified in Node: book_new/aoa_to_sheet/book_append_sheet/write produce the same sheet (header + 64 rows for john.doe@gmail.com). --- index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.html b/index.html index d9da2c2..811f01c 100644 --- a/index.html +++ b/index.html @@ -31,7 +31,7 @@

Email Variations Generator

- + From f560076475659fbfbbe43aea0ea0ee8c44434198 Mon Sep 17 00:00:00 2001 From: fadyehabamer Date: Thu, 24 Sep 2026 14:50:47 +0300 Subject: [PATCH 11/11] docs: document how variations are generated, limits and local usage --- README.md | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/README.md b/README.md index 5bb09f0..267e595 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,33 @@ # Email-Variations-Generator Generate variation of duplicate valid Email for your email address + +**Live demo:** https://fea-email-variations-generator.vercel.app/ + +## How it works + +Gmail ignores dots (`.`) in the username part of an address, so +`johndoe@gmail.com`, `john.doe@gmail.com` and `j.o.h.n.d.o.e@gmail.com` all +deliver to the same inbox. This tool lists every way of placing dots in your +username, which is handy for sign-up testing, filtering, or spotting where an +address was shared. + +- Existing dots are ignored, so `john.doe@gmail.com` and `johndoe@gmail.com` + give the same 64 variations (never `john..doe`). +- A `+tag` suffix (e.g. `john+news@gmail.com`) is kept unchanged; only the + part before `+` is varied. +- A username of *n* characters has 2^(n-1) variations. The output is capped at + 8,192 (every variation for usernames up to 14 characters) to keep the page + responsive; the page tells you when results are truncated. +- Results can be exported to an `.xlsx` file (via [SheetJS](https://sheetjs.com/)). + +> Dot-insensitivity is a Gmail / Google Workspace behavior. Most other +> providers treat dotted addresses as different mailboxes. + +## Run locally + +It's a static page with no build step: open `index.html` in a browser, or +serve the folder (e.g. `npx serve .`). + +## License + +[MIT](LICENSE)