wildcard-domain-finder-plus 1.0.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/CHANGELOG.md +37 -0
- package/CONTRIBUTING.md +112 -0
- package/README.md +345 -0
- package/README.txt +219 -0
- package/app.js +558 -0
- package/package.json +33 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented here.
|
|
4
|
+
|
|
5
|
+
This project follows Semantic Versioning:
|
|
6
|
+
MAJOR.MINOR.PATCH
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## 1.0.0 ā Initial Public Release
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- Wildcard domain generation
|
|
14
|
+
- Regex mode
|
|
15
|
+
- Premium TLD support
|
|
16
|
+
- Filtering (tld, length, starts, ends)
|
|
17
|
+
- Sorting (comfirst, tld, length, alpha)
|
|
18
|
+
- Output formats: txt, json, jsonl, csv
|
|
19
|
+
- Streaming generation (no memory blowups)
|
|
20
|
+
- Caching + resume mode
|
|
21
|
+
- Interactive mode
|
|
22
|
+
- Concurrency + timeout controls
|
|
23
|
+
- Full CLI help system
|
|
24
|
+
- Complete README with usage guide and regex helper
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## 1.0.1 ā Documentation Improvements
|
|
29
|
+
|
|
30
|
+
### Added
|
|
31
|
+
- Quick Start section
|
|
32
|
+
- Common Recipes section
|
|
33
|
+
- Performance Tips section
|
|
34
|
+
- CONTRIBUTING.md
|
|
35
|
+
- CHANGELOG.md
|
|
36
|
+
|
|
37
|
+
---
|
package/CONTRIBUTING.md
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# Contributing to Wildcard Domain Finder Plus
|
|
2
|
+
|
|
3
|
+
Thank you for your interest in contributing! This project welcomes improvements, bug fixes, documentation updates, and new features.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Code of Conduct
|
|
8
|
+
|
|
9
|
+
By participating in this project, you agree to uphold respectful, constructive communication. Be kind, be clear, and collaborate openly.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# How to Contribute
|
|
14
|
+
|
|
15
|
+
## 1. Fork the Repository
|
|
16
|
+
|
|
17
|
+
Create your own fork of the project:
|
|
18
|
+
|
|
19
|
+
https://github.com/nbcr/wildcard-domain-finder-plus
|
|
20
|
+
|
|
21
|
+
Clone your fork:
|
|
22
|
+
|
|
23
|
+
git clone https://github.com/YOUR_USERNAME/wildcard-domain-finder-plus
|
|
24
|
+
cd wildcard-domain-finder-plus
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## 2. Create a Feature Branch
|
|
29
|
+
|
|
30
|
+
git checkout -b feature/my-new-feature
|
|
31
|
+
|
|
32
|
+
Use descriptive branch names such as:
|
|
33
|
+
|
|
34
|
+
- feature/regex-improvements
|
|
35
|
+
- fix/cache-bug
|
|
36
|
+
- docs/readme-update
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## 3. Install Dependencies
|
|
41
|
+
|
|
42
|
+
npm install
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## 4. Make Your Changes
|
|
47
|
+
|
|
48
|
+
Follow these guidelines:
|
|
49
|
+
|
|
50
|
+
- Keep code modular and readable
|
|
51
|
+
- Add comments where clarity helps
|
|
52
|
+
- Maintain consistent formatting
|
|
53
|
+
- Avoid breaking existing functionality
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## 5. Add or Update Tests (if applicable)
|
|
58
|
+
|
|
59
|
+
Place tests under:
|
|
60
|
+
|
|
61
|
+
/tests
|
|
62
|
+
|
|
63
|
+
Run tests:
|
|
64
|
+
|
|
65
|
+
npm test
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## 6. Commit Your Changes
|
|
70
|
+
|
|
71
|
+
Use clear, conventional commit messages:
|
|
72
|
+
|
|
73
|
+
git commit -m "feat: add premium TLD filtering"
|
|
74
|
+
git commit -m "fix: correct wildcard expansion bug"
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## 7. Push and Open a Pull Request
|
|
79
|
+
|
|
80
|
+
git push origin feature/my-new-feature
|
|
81
|
+
|
|
82
|
+
Then open a PR on GitHub:
|
|
83
|
+
|
|
84
|
+
- Describe what you changed
|
|
85
|
+
- Explain why
|
|
86
|
+
- Link related issues
|
|
87
|
+
|
|
88
|
+
A maintainer will review your PR and work with you to merge it.
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## 8. Style Guidelines
|
|
93
|
+
|
|
94
|
+
- Use modern JavaScript (ES modules)
|
|
95
|
+
- Prefer async/await
|
|
96
|
+
- Avoid unnecessary dependencies
|
|
97
|
+
- Keep CLI output clean and readable
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## 9. Reporting Issues
|
|
102
|
+
|
|
103
|
+
If you find a bug, open an issue with:
|
|
104
|
+
|
|
105
|
+
- Steps to reproduce
|
|
106
|
+
- Expected behavior
|
|
107
|
+
- Actual behavior
|
|
108
|
+
- Environment details
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
Thank you for contributing!
|
package/README.md
ADDED
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
## Attribution
|
|
2
|
+
|
|
3
|
+
This project is an enhanced fork of
|
|
4
|
+
**besoeasy/wildcard-domain-finder**
|
|
5
|
+
Original work Ā© the respective author(s), licensed under the ISC License.
|
|
6
|
+
|
|
7
|
+
This fork adds major new capabilities including:
|
|
8
|
+
- streaming domain generation (no memory blowups)
|
|
9
|
+
- regex mode
|
|
10
|
+
- wildcard mode
|
|
11
|
+
- TLD selection (explicit, all, premium)
|
|
12
|
+
- filtering (tld, length, starts, ends)
|
|
13
|
+
- sorting (comfirst, tld, length, alpha)
|
|
14
|
+
- JSON / JSONL / CSV / TXT output
|
|
15
|
+
- caching + resume mode
|
|
16
|
+
- interactive pause/resume/quit controls
|
|
17
|
+
- concurrency + timeout controls
|
|
18
|
+
- full CLI help system
|
|
19
|
+
|
|
20
|
+
All original licensing terms are preserved.
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
# Wildcard Domain Finder Plus
|
|
25
|
+
|
|
26
|
+
A command-line tool to find available domain names using wildcard patterns, regex patterns, or structured filters ā now with streaming generation, caching, and advanced TLD control.
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
# Quick Start
|
|
31
|
+
|
|
32
|
+
Install globally:
|
|
33
|
+
|
|
34
|
+
npm install -g wildcard-domain-finder-plus
|
|
35
|
+
|
|
36
|
+
Run a simple wildcard scan:
|
|
37
|
+
|
|
38
|
+
wildcard-domain-finder-plus -d "test*.com"
|
|
39
|
+
|
|
40
|
+
Run a regex scan:
|
|
41
|
+
|
|
42
|
+
wildcard-domain-finder-plus --regex "^[a-z]{3}\\.com$"
|
|
43
|
+
|
|
44
|
+
Scan all TLDs:
|
|
45
|
+
|
|
46
|
+
wildcard-domain-finder-plus -d "go*" --tlds all
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
# Installation
|
|
51
|
+
|
|
52
|
+
### Via NPX (no installation required)
|
|
53
|
+
|
|
54
|
+
npx wildcard-domain-finder-plus
|
|
55
|
+
|
|
56
|
+
### Or install globally
|
|
57
|
+
|
|
58
|
+
npm install -g wildcard-domain-finder-plus
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
# Usage Guide
|
|
63
|
+
|
|
64
|
+
Wildcard Domain Finder Plus supports three primary modes:
|
|
65
|
+
|
|
66
|
+
1. Wildcard mode
|
|
67
|
+
2. Regex mode
|
|
68
|
+
3. Structured filtering mode
|
|
69
|
+
|
|
70
|
+
Each mode can be combined with TLD selection, sorting, output formatting, and caching.
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
## Wildcard Mode
|
|
75
|
+
|
|
76
|
+
Use * to represent any single alphanumeric character (aāz, 0ā9).
|
|
77
|
+
|
|
78
|
+
Example:
|
|
79
|
+
|
|
80
|
+
wildcard-domain-finder-plus -d "test*.com"
|
|
81
|
+
|
|
82
|
+
This expands to:
|
|
83
|
+
|
|
84
|
+
- testa.com
|
|
85
|
+
- testb.com
|
|
86
|
+
- test1.com
|
|
87
|
+
- ā¦and so on
|
|
88
|
+
|
|
89
|
+
Wildcard mode is ideal when you know the pattern but not the exact characters.
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## Regex Mode
|
|
94
|
+
|
|
95
|
+
Regex mode gives you full control over domain label generation.
|
|
96
|
+
|
|
97
|
+
Example:
|
|
98
|
+
|
|
99
|
+
wildcard-domain-finder-plus --regex "^[a-z0-9]{3}\\.com$"
|
|
100
|
+
|
|
101
|
+
This generates:
|
|
102
|
+
|
|
103
|
+
- aaa.com
|
|
104
|
+
- aab.com
|
|
105
|
+
- ā¦
|
|
106
|
+
- zzz.com
|
|
107
|
+
|
|
108
|
+
Regex mode supports:
|
|
109
|
+
|
|
110
|
+
- character classes
|
|
111
|
+
- alternation
|
|
112
|
+
- anchors
|
|
113
|
+
- quantifiers
|
|
114
|
+
- grouping
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
# Regex Helper
|
|
119
|
+
|
|
120
|
+
### Threeāletter domains
|
|
121
|
+
|
|
122
|
+
^[a-z]{3}\\.(com|net|org)$
|
|
123
|
+
|
|
124
|
+
### Two letters + one digit
|
|
125
|
+
|
|
126
|
+
^[a-z]{2}[0-9]\\.com$
|
|
127
|
+
|
|
128
|
+
### Start with āgoā, then any 2 chars
|
|
129
|
+
|
|
130
|
+
^go[a-z0-9]{2}\\.io$
|
|
131
|
+
|
|
132
|
+
### Premium TLDs only
|
|
133
|
+
|
|
134
|
+
^[a-z]{3,5}\\.(ai|io|dev)$
|
|
135
|
+
|
|
136
|
+
### Only letters (no digits)
|
|
137
|
+
|
|
138
|
+
^[a-z]+\\.(com|net)$
|
|
139
|
+
|
|
140
|
+
### Letters + optional hyphen
|
|
141
|
+
|
|
142
|
+
^[a-z]+(-[a-z]+)?\\.com$
|
|
143
|
+
|
|
144
|
+
### Multiple TLDs
|
|
145
|
+
|
|
146
|
+
^[a-z]{4}\\.(com|io|ai|co)$
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
# TLD Selection
|
|
151
|
+
|
|
152
|
+
Choose from:
|
|
153
|
+
|
|
154
|
+
- explicit list
|
|
155
|
+
- all known TLDs
|
|
156
|
+
- premium curated list
|
|
157
|
+
|
|
158
|
+
Examples:
|
|
159
|
+
|
|
160
|
+
wildcard-domain-finder-plus -d "go*" --tlds all
|
|
161
|
+
|
|
162
|
+
wildcard-domain-finder-plus -d "ai*" --tlds premium
|
|
163
|
+
|
|
164
|
+
wildcard-domain-finder-plus -d "shop*" --tlds com,net,org,io
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
# Filtering
|
|
169
|
+
|
|
170
|
+
Filters allow you to refine generated domains.
|
|
171
|
+
|
|
172
|
+
Supported filters:
|
|
173
|
+
|
|
174
|
+
- tld:com
|
|
175
|
+
- length<=3
|
|
176
|
+
- starts:go
|
|
177
|
+
- ends:ai
|
|
178
|
+
|
|
179
|
+
Examples:
|
|
180
|
+
|
|
181
|
+
wildcard-domain-finder-plus -d "***.com" --filter length<=3
|
|
182
|
+
|
|
183
|
+
wildcard-domain-finder-plus -d "*ai" --filter ends:ai
|
|
184
|
+
|
|
185
|
+
wildcard-domain-finder-plus -d "go*" --filter starts:go
|
|
186
|
+
|
|
187
|
+
---
|
|
188
|
+
|
|
189
|
+
# Sorting
|
|
190
|
+
|
|
191
|
+
Sorting options:
|
|
192
|
+
|
|
193
|
+
- comfirst
|
|
194
|
+
- tld
|
|
195
|
+
- length
|
|
196
|
+
- alpha
|
|
197
|
+
|
|
198
|
+
Example:
|
|
199
|
+
|
|
200
|
+
wildcard-domain-finder-plus -d "***.com" --sort comfirst
|
|
201
|
+
|
|
202
|
+
---
|
|
203
|
+
|
|
204
|
+
# Output Formats
|
|
205
|
+
|
|
206
|
+
Choose from:
|
|
207
|
+
|
|
208
|
+
- txt
|
|
209
|
+
- json
|
|
210
|
+
- jsonl
|
|
211
|
+
- csv
|
|
212
|
+
|
|
213
|
+
Example:
|
|
214
|
+
|
|
215
|
+
wildcard-domain-finder-plus -d "go*" -F jsonl -o results.jsonl
|
|
216
|
+
|
|
217
|
+
---
|
|
218
|
+
|
|
219
|
+
# Caching and Resume Mode
|
|
220
|
+
|
|
221
|
+
Large scans can be resumed:
|
|
222
|
+
|
|
223
|
+
wildcard-domain-finder-plus -R
|
|
224
|
+
|
|
225
|
+
Disable caching:
|
|
226
|
+
|
|
227
|
+
wildcard-domain-finder-plus --no-cache
|
|
228
|
+
|
|
229
|
+
---
|
|
230
|
+
|
|
231
|
+
# Interactive Mode
|
|
232
|
+
|
|
233
|
+
If no domain or regex is provided:
|
|
234
|
+
|
|
235
|
+
wildcard-domain-finder-plus
|
|
236
|
+
|
|
237
|
+
You will be prompted for:
|
|
238
|
+
|
|
239
|
+
- mode
|
|
240
|
+
- pattern
|
|
241
|
+
- TLDs
|
|
242
|
+
- filters
|
|
243
|
+
- output format
|
|
244
|
+
|
|
245
|
+
---
|
|
246
|
+
|
|
247
|
+
# Common Recipes
|
|
248
|
+
|
|
249
|
+
### Find all 3āletter .com domains
|
|
250
|
+
|
|
251
|
+
wildcard-domain-finder-plus --regex "^[a-z]{3}\\.com$"
|
|
252
|
+
|
|
253
|
+
### Find all domains starting with āgoā across all TLDs
|
|
254
|
+
|
|
255
|
+
wildcard-domain-finder-plus -d "go*" --tlds all
|
|
256
|
+
|
|
257
|
+
### Find short premium domains
|
|
258
|
+
|
|
259
|
+
wildcard-domain-finder-plus -d "***" --tlds premium --filter length<=3
|
|
260
|
+
|
|
261
|
+
### Find 4āletter .io or .ai domains
|
|
262
|
+
|
|
263
|
+
wildcard-domain-finder-plus --regex "^[a-z]{4}\\.(io|ai)$"
|
|
264
|
+
|
|
265
|
+
### Wildcard + filtering + sorting
|
|
266
|
+
|
|
267
|
+
wildcard-domain-finder-plus -d "go**.com" --filter length<=4 --sort alpha
|
|
268
|
+
|
|
269
|
+
---
|
|
270
|
+
|
|
271
|
+
# Performance Tips
|
|
272
|
+
|
|
273
|
+
### 1. Increase concurrency for faster scans
|
|
274
|
+
|
|
275
|
+
--concurrency 50
|
|
276
|
+
|
|
277
|
+
### 2. Reduce DNS timeout for faster failures
|
|
278
|
+
|
|
279
|
+
--timeout 2000
|
|
280
|
+
|
|
281
|
+
### 3. Use JSONL for huge output sets
|
|
282
|
+
|
|
283
|
+
-F jsonl
|
|
284
|
+
|
|
285
|
+
### 4. Use resume mode for long scans
|
|
286
|
+
|
|
287
|
+
-R
|
|
288
|
+
|
|
289
|
+
### 5. Limit search space with filters
|
|
290
|
+
|
|
291
|
+
--filter length<=4
|
|
292
|
+
--filter starts:go
|
|
293
|
+
--filter tld:com
|
|
294
|
+
|
|
295
|
+
### 6. Prefer regex for structured patterns
|
|
296
|
+
|
|
297
|
+
Regex mode avoids generating unnecessary combinations.
|
|
298
|
+
|
|
299
|
+
---
|
|
300
|
+
|
|
301
|
+
# Full Examples
|
|
302
|
+
|
|
303
|
+
### Wildcard + premium TLDs
|
|
304
|
+
|
|
305
|
+
wildcard-domain-finder-plus -d "ai*" --tlds premium
|
|
306
|
+
|
|
307
|
+
### Regex + JSONL output
|
|
308
|
+
|
|
309
|
+
wildcard-domain-finder-plus --regex "^[a-z]{4}\\.(io|ai)$" -F jsonl
|
|
310
|
+
|
|
311
|
+
### Resume a long scan
|
|
312
|
+
|
|
313
|
+
wildcard-domain-finder-plus -R
|
|
314
|
+
|
|
315
|
+
---
|
|
316
|
+
|
|
317
|
+
# Features
|
|
318
|
+
|
|
319
|
+
- ā” Streaming domain generation (no memory blowups)
|
|
320
|
+
- š Wildcard mode (* = single character)
|
|
321
|
+
- š Regex mode
|
|
322
|
+
- š TLD selection (explicit, all, premium)
|
|
323
|
+
- š§¹ Filtering (tld, length, starts, ends)
|
|
324
|
+
- š Sorting (comfirst, tld, length, alpha)
|
|
325
|
+
- š Multiple output formats (txt, json, jsonl, csv)
|
|
326
|
+
- š¾ Caching + resume mode
|
|
327
|
+
- āøļø Interactive pause/resume/quit
|
|
328
|
+
- ā±ļø Concurrency + timeout controls
|
|
329
|
+
- š”ļø Graceful error handling
|
|
330
|
+
- ā Full CLI help system
|
|
331
|
+
|
|
332
|
+
---
|
|
333
|
+
|
|
334
|
+
# License
|
|
335
|
+
|
|
336
|
+
ISC
|
|
337
|
+
All original licensing terms are preserved.
|
|
338
|
+
|
|
339
|
+
---
|
|
340
|
+
|
|
341
|
+
# Links
|
|
342
|
+
|
|
343
|
+
- NPM Package: https://www.npmjs.com/package/wildcard-domain-finder-plus
|
|
344
|
+
- GitHub Repository: https://github.com/nbcr/wildcard-domain-finder-plus
|
|
345
|
+
- Issues: https://github.com/nbcr/wildcard-domain-finder-plus/issues
|
package/README.txt
ADDED
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
Attribution
|
|
2
|
+
This project is an enhanced fork of
|
|
3
|
+
besoeasy/wildcard-domain-finder
|
|
4
|
+
Original work Ā© the respective author(s), licensed under the ISC License.
|
|
5
|
+
|
|
6
|
+
This fork adds major new capabilities including:
|
|
7
|
+
|
|
8
|
+
streaming domain generation (no memory blowups)
|
|
9
|
+
regex mode
|
|
10
|
+
wildcard mode
|
|
11
|
+
TLD selection (explicit, all, premium)
|
|
12
|
+
filtering (tld, length, starts, ends)
|
|
13
|
+
sorting (comfirst, tld, length, alpha)
|
|
14
|
+
JSON / JSONL / CSV / TXT output
|
|
15
|
+
caching + resume mode
|
|
16
|
+
interactive pause/resume/quit controls
|
|
17
|
+
concurrency + timeout controls
|
|
18
|
+
full CLI help system
|
|
19
|
+
All original licensing terms are preserved.
|
|
20
|
+
|
|
21
|
+
Wildcard Domain Finder Plus
|
|
22
|
+
A command-line tool to find available domain names using wildcard patterns, regex patterns, or structured filters ā now with streaming generation, caching, and advanced TLD control.
|
|
23
|
+
|
|
24
|
+
Quick Start
|
|
25
|
+
Install globally:
|
|
26
|
+
|
|
27
|
+
npm install -g wildcard-domain-finder-plus
|
|
28
|
+
Run a simple wildcard scan:
|
|
29
|
+
|
|
30
|
+
wildcard-domain-finder-plus -d "test*.com"
|
|
31
|
+
Run a regex scan:
|
|
32
|
+
|
|
33
|
+
wildcard-domain-finder-plus --regex "^[a-z]{3}\\.com$"
|
|
34
|
+
Scan all TLDs:
|
|
35
|
+
|
|
36
|
+
wildcard-domain-finder-plus -d "go*" --tlds all
|
|
37
|
+
Installation
|
|
38
|
+
Via NPX (no installation required)
|
|
39
|
+
npx wildcard-domain-finder-plus
|
|
40
|
+
Or install globally
|
|
41
|
+
npm install -g wildcard-domain-finder-plus
|
|
42
|
+
Usage Guide
|
|
43
|
+
Wildcard Domain Finder Plus supports three primary modes:
|
|
44
|
+
|
|
45
|
+
Wildcard mode
|
|
46
|
+
Regex mode
|
|
47
|
+
Structured filtering mode
|
|
48
|
+
Each mode can be combined with TLD selection, sorting, output formatting, and caching.
|
|
49
|
+
|
|
50
|
+
Wildcard Mode
|
|
51
|
+
Use * to represent any single alphanumeric character (aāz, 0ā9).
|
|
52
|
+
|
|
53
|
+
Example:
|
|
54
|
+
|
|
55
|
+
wildcard-domain-finder-plus -d "test*.com"
|
|
56
|
+
This expands to:
|
|
57
|
+
|
|
58
|
+
testa.com
|
|
59
|
+
testb.com
|
|
60
|
+
test1.com
|
|
61
|
+
ā¦and so on
|
|
62
|
+
Wildcard mode is ideal when you know the pattern but not the exact characters.
|
|
63
|
+
|
|
64
|
+
Regex Mode
|
|
65
|
+
Regex mode gives you full control over domain label generation.
|
|
66
|
+
|
|
67
|
+
Example:
|
|
68
|
+
|
|
69
|
+
wildcard-domain-finder-plus --regex "^[a-z0-9]{3}\\.com$"
|
|
70
|
+
This generates:
|
|
71
|
+
|
|
72
|
+
aaa.com
|
|
73
|
+
aab.com
|
|
74
|
+
ā¦
|
|
75
|
+
zzz.com
|
|
76
|
+
Regex mode supports:
|
|
77
|
+
|
|
78
|
+
character classes
|
|
79
|
+
alternation
|
|
80
|
+
anchors
|
|
81
|
+
quantifiers
|
|
82
|
+
grouping
|
|
83
|
+
Regex Helper
|
|
84
|
+
Threeāletter domains
|
|
85
|
+
^[a-z]{3}\\.(com|net|org)$
|
|
86
|
+
Two letters + one digit
|
|
87
|
+
^[a-z]{2}[0-9]\\.com$
|
|
88
|
+
Start with āgoā, then any 2 chars
|
|
89
|
+
^go[a-z0-9]{2}\\.io$
|
|
90
|
+
Premium TLDs only
|
|
91
|
+
^[a-z]{3,5}\\.(ai|io|dev)$
|
|
92
|
+
Only letters (no digits)
|
|
93
|
+
^[a-z]+\\.(com|net)$
|
|
94
|
+
Letters + optional hyphen
|
|
95
|
+
^[a-z]+(-[a-z]+)?\\.com$
|
|
96
|
+
Multiple TLDs
|
|
97
|
+
^[a-z]{4}\\.(com|io|ai|co)$
|
|
98
|
+
TLD Selection
|
|
99
|
+
Choose from:
|
|
100
|
+
|
|
101
|
+
explicit list
|
|
102
|
+
all known TLDs
|
|
103
|
+
premium curated list
|
|
104
|
+
Examples:
|
|
105
|
+
|
|
106
|
+
wildcard-domain-finder-plus -d "go*" --tlds all
|
|
107
|
+
|
|
108
|
+
wildcard-domain-finder-plus -d "ai*" --tlds premium
|
|
109
|
+
|
|
110
|
+
wildcard-domain-finder-plus -d "shop*" --tlds com,net,org,io
|
|
111
|
+
Filtering
|
|
112
|
+
Filters allow you to refine generated domains.
|
|
113
|
+
|
|
114
|
+
Supported filters:
|
|
115
|
+
|
|
116
|
+
tld:com
|
|
117
|
+
length<=3
|
|
118
|
+
starts:go
|
|
119
|
+
ends:ai
|
|
120
|
+
Examples:
|
|
121
|
+
|
|
122
|
+
wildcard-domain-finder-plus -d "***.com" --filter length<=3
|
|
123
|
+
|
|
124
|
+
wildcard-domain-finder-plus -d "*ai" --filter ends:ai
|
|
125
|
+
|
|
126
|
+
wildcard-domain-finder-plus -d "go*" --filter starts:go
|
|
127
|
+
Sorting
|
|
128
|
+
Sorting options:
|
|
129
|
+
|
|
130
|
+
comfirst
|
|
131
|
+
tld
|
|
132
|
+
length
|
|
133
|
+
alpha
|
|
134
|
+
Example:
|
|
135
|
+
|
|
136
|
+
wildcard-domain-finder-plus -d "***.com" --sort comfirst
|
|
137
|
+
Output Formats
|
|
138
|
+
Choose from:
|
|
139
|
+
|
|
140
|
+
txt
|
|
141
|
+
json
|
|
142
|
+
jsonl
|
|
143
|
+
csv
|
|
144
|
+
Example:
|
|
145
|
+
|
|
146
|
+
wildcard-domain-finder-plus -d "go*" -F jsonl -o results.jsonl
|
|
147
|
+
Caching and Resume Mode
|
|
148
|
+
Large scans can be resumed:
|
|
149
|
+
|
|
150
|
+
wildcard-domain-finder-plus -R
|
|
151
|
+
Disable caching:
|
|
152
|
+
|
|
153
|
+
wildcard-domain-finder-plus --no-cache
|
|
154
|
+
Interactive Mode
|
|
155
|
+
If no domain or regex is provided:
|
|
156
|
+
|
|
157
|
+
wildcard-domain-finder-plus
|
|
158
|
+
You will be prompted for:
|
|
159
|
+
|
|
160
|
+
mode
|
|
161
|
+
pattern
|
|
162
|
+
TLDs
|
|
163
|
+
filters
|
|
164
|
+
output format
|
|
165
|
+
Common Recipes
|
|
166
|
+
Find all 3āletter .com domains
|
|
167
|
+
wildcard-domain-finder-plus --regex "^[a-z]{3}\\.com$"
|
|
168
|
+
Find all domains starting with āgoā across all TLDs
|
|
169
|
+
wildcard-domain-finder-plus -d "go*" --tlds all
|
|
170
|
+
Find short premium domains
|
|
171
|
+
wildcard-domain-finder-plus -d "***" --tlds premium --filter length<=3
|
|
172
|
+
Find 4āletter .io or .ai domains
|
|
173
|
+
wildcard-domain-finder-plus --regex "^[a-z]{4}\\.(io|ai)$"
|
|
174
|
+
Wildcard + filtering + sorting
|
|
175
|
+
wildcard-domain-finder-plus -d "go**.com" --filter length<=4 --sort alpha
|
|
176
|
+
Performance Tips
|
|
177
|
+
1. Increase concurrency for faster scans
|
|
178
|
+
--concurrency 50
|
|
179
|
+
2. Reduce DNS timeout for faster failures
|
|
180
|
+
--timeout 2000
|
|
181
|
+
3. Use JSONL for huge output sets
|
|
182
|
+
-F jsonl
|
|
183
|
+
4. Use resume mode for long scans
|
|
184
|
+
-R
|
|
185
|
+
5. Limit search space with filters
|
|
186
|
+
--filter length<=4
|
|
187
|
+
--filter starts:go
|
|
188
|
+
--filter tld:com
|
|
189
|
+
6. Prefer regex for structured patterns
|
|
190
|
+
Regex mode avoids generating unnecessary combinations.
|
|
191
|
+
|
|
192
|
+
Full Examples
|
|
193
|
+
Wildcard + premium TLDs
|
|
194
|
+
wildcard-domain-finder-plus -d "ai*" --tlds premium
|
|
195
|
+
Regex + JSONL output
|
|
196
|
+
wildcard-domain-finder-plus --regex "^[a-z]{4}\\.(io|ai)$" -F jsonl
|
|
197
|
+
Resume a long scan
|
|
198
|
+
wildcard-domain-finder-plus -R
|
|
199
|
+
Features
|
|
200
|
+
ā” Streaming domain generation (no memory blowups)
|
|
201
|
+
š Wildcard mode (* = single character)
|
|
202
|
+
š Regex mode
|
|
203
|
+
š TLD selection (explicit, all, premium)
|
|
204
|
+
š§¹ Filtering (tld, length, starts, ends)
|
|
205
|
+
š Sorting (comfirst, tld, length, alpha)
|
|
206
|
+
š Multiple output formats (txt, json, jsonl, csv)
|
|
207
|
+
š¾ Caching + resume mode
|
|
208
|
+
āøļø Interactive pause/resume/quit
|
|
209
|
+
ā±ļø Concurrency + timeout controls
|
|
210
|
+
š”ļø Graceful error handling
|
|
211
|
+
ā Full CLI help system
|
|
212
|
+
License
|
|
213
|
+
ISC
|
|
214
|
+
All original licensing terms are preserved.
|
|
215
|
+
|
|
216
|
+
Links
|
|
217
|
+
NPM Package: https://www.npmjs.com/package/wildcard-domain-finder-plus
|
|
218
|
+
GitHub Repository: https://github.com/nbcr/wildcard-domain-finder-plus
|
|
219
|
+
Issues: https://github.com/nbcr/wildcard-domain-finder-plus/issues
|
package/app.js
ADDED
|
@@ -0,0 +1,558 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// wildcard-domain-finder (upgraded)
|
|
4
|
+
// - Supports wildcard patterns (* = single char)
|
|
5
|
+
// - Optional regex mode
|
|
6
|
+
// - TLD selection (explicit, all, premium)
|
|
7
|
+
// - Filtering (tld, length, starts, ends)
|
|
8
|
+
// - Sorting (comfirst, tld, length, alpha)
|
|
9
|
+
// - Output formats: txt, json, jsonl, csv
|
|
10
|
+
// - Caching + resume via JSONL
|
|
11
|
+
// - Streaming generation (no heap blowups)
|
|
12
|
+
// - Concurrency + timeout
|
|
13
|
+
// - Interactive pause/resume/quit (p/r/q)
|
|
14
|
+
|
|
15
|
+
const fs = require('fs');
|
|
16
|
+
const dns = require('dns').promises;
|
|
17
|
+
const readline = require('readline');
|
|
18
|
+
|
|
19
|
+
const CHARSET = 'abcdefghijklmnopqrstuvwxyz0123456789';
|
|
20
|
+
|
|
21
|
+
const IANA_TLDS = [
|
|
22
|
+
'com','net','org','edu','gov','mil','int',
|
|
23
|
+
'ca','us','uk','de','fr','au','jp','cn','io','ai','co','me','tv','cc',
|
|
24
|
+
'xyz','info','biz','name','pro','tech','dev','app','cloud','store','shop',
|
|
25
|
+
'site','online','space','fun','live','world','today','news','media','social',
|
|
26
|
+
'group','club','team','company','agency','solutions','systems','network',
|
|
27
|
+
'software','digital','finance','capital','partners','ventures','consulting',
|
|
28
|
+
'services','support','help','care','health','clinic','law','legal',
|
|
29
|
+
'design','photo','photos','gallery','art','music','video','games','game',
|
|
30
|
+
'blog','wiki','school','academy','training','university','science',
|
|
31
|
+
'research','energy','solar','green','eco','earth','bio','farm','garden',
|
|
32
|
+
'coffee','pizza','bar','restaurant','kitchen','food','wine','beer',
|
|
33
|
+
'fashion','style','beauty','spa','travel','vacations','holiday','flights',
|
|
34
|
+
'hotel','rentals','cars','auto','car','bike','homes','house','realty',
|
|
35
|
+
'estate','property','land','city','zone','global','africa','asia','europe'
|
|
36
|
+
];
|
|
37
|
+
|
|
38
|
+
const PREMIUM_TLDS = ['com','net','org','io','ai','co','dev','app','xyz','tech'];
|
|
39
|
+
|
|
40
|
+
const DOMAIN_REGEX = /^(?=.{1,253}$)(?!.*\.\.)([A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?\.)+[A-Za-z]{2,63}$/;
|
|
41
|
+
|
|
42
|
+
let paused = false;
|
|
43
|
+
let quitting = false;
|
|
44
|
+
|
|
45
|
+
function isValidDomain(domain) {
|
|
46
|
+
return DOMAIN_REGEX.test(domain);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function sleep(ms) {
|
|
50
|
+
return new Promise(res => setTimeout(res, ms));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function setupInteractiveControls() {
|
|
54
|
+
readline.emitKeypressEvents(process.stdin);
|
|
55
|
+
if (process.stdin.isTTY) process.stdin.setRawMode(true);
|
|
56
|
+
|
|
57
|
+
process.stdin.on('keypress', (str, key) => {
|
|
58
|
+
if (!key) return;
|
|
59
|
+
if (key.name === 'p') {
|
|
60
|
+
paused = true;
|
|
61
|
+
process.stdout.write('\nāøļø Paused. Press r to resume, q to quit.\n');
|
|
62
|
+
}
|
|
63
|
+
if (key.name === 'r') {
|
|
64
|
+
paused = false;
|
|
65
|
+
process.stdout.write('\nā¶ļø Resumed.\n');
|
|
66
|
+
}
|
|
67
|
+
if (key.name === 'q') {
|
|
68
|
+
quitting = true;
|
|
69
|
+
process.stdout.write('\nš Quitting gracefully...\n');
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// ---------- CLI PARSING ----------
|
|
75
|
+
|
|
76
|
+
function parseArgs() {
|
|
77
|
+
const args = process.argv.slice(2);
|
|
78
|
+
const opts = {
|
|
79
|
+
pattern: null,
|
|
80
|
+
regex: null,
|
|
81
|
+
tlds: null,
|
|
82
|
+
allTlds: false,
|
|
83
|
+
premiumTlds: false,
|
|
84
|
+
filters: [],
|
|
85
|
+
sort: null,
|
|
86
|
+
format: 'txt',
|
|
87
|
+
output: 'available_domains.txt',
|
|
88
|
+
concurrency: 10,
|
|
89
|
+
timeout: 5000,
|
|
90
|
+
resume: false,
|
|
91
|
+
cacheFile: 'checked_domains.jsonl',
|
|
92
|
+
useCache: true,
|
|
93
|
+
maxLength: 4
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
for (let i = 0; i < args.length; i++) {
|
|
97
|
+
const a = args[i];
|
|
98
|
+
if (a === '-d' || a === '--domain') {
|
|
99
|
+
opts.pattern = args[++i];
|
|
100
|
+
} else if (a === '-r' || a === '--regex') {
|
|
101
|
+
opts.regex = args[++i];
|
|
102
|
+
} else if (a === '-t' || a === '--tlds') {
|
|
103
|
+
const v = args[++i];
|
|
104
|
+
if (v === 'all') opts.allTlds = true;
|
|
105
|
+
else if (v === 'premium') opts.premiumTlds = true;
|
|
106
|
+
else opts.tlds = v.split(',').map(s => s.trim().toLowerCase()).filter(Boolean);
|
|
107
|
+
} else if (a === '-f' || a === '--filter') {
|
|
108
|
+
opts.filters.push(args[++i]);
|
|
109
|
+
} else if (a === '-s' || a === '--sort') {
|
|
110
|
+
opts.sort = args[++i]; // comfirst | tld | length | alpha
|
|
111
|
+
} else if (a === '-F' || a === '--format') {
|
|
112
|
+
opts.format = args[++i].toLowerCase(); // txt | json | jsonl | csv
|
|
113
|
+
} else if (a === '-o' || a === '--output') {
|
|
114
|
+
opts.output = args[++i];
|
|
115
|
+
} else if (a === '-c' || a === '--concurrency') {
|
|
116
|
+
opts.concurrency = parseInt(args[++i], 10) || 10;
|
|
117
|
+
} else if (a === '-T' || a === '--timeout') {
|
|
118
|
+
opts.timeout = parseInt(args[++i], 10) || 5000;
|
|
119
|
+
} else if (a === '-R' || a === '--resume') {
|
|
120
|
+
opts.resume = true;
|
|
121
|
+
} else if (a === '--no-resume') {
|
|
122
|
+
opts.resume = false;
|
|
123
|
+
} else if (a === '-C' || a === '--cache') {
|
|
124
|
+
opts.cacheFile = args[++i];
|
|
125
|
+
} else if (a === '--no-cache') {
|
|
126
|
+
opts.useCache = false;
|
|
127
|
+
} else if (a === '--max-length') {
|
|
128
|
+
opts.maxLength = parseInt(args[++i], 10) || 4;
|
|
129
|
+
} else if (a === '-h' || a === '--help') {
|
|
130
|
+
printHelp();
|
|
131
|
+
process.exit(0);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return opts;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function printHelp() {
|
|
139
|
+
console.log(`
|
|
140
|
+
Wildcard Domain Finder (upgraded)
|
|
141
|
+
|
|
142
|
+
Usage:
|
|
143
|
+
wildcard-domain-finder [options]
|
|
144
|
+
|
|
145
|
+
Domain Input:
|
|
146
|
+
-d, --domain <pattern> Wildcard pattern (* = single char)
|
|
147
|
+
-r, --regex <regex> Regex pattern for full domain
|
|
148
|
+
-t, --tlds <list> Comma-separated TLDs (e.g. com,net,io)
|
|
149
|
+
--tlds all Use all known TLDs
|
|
150
|
+
--tlds premium Use premium TLD list
|
|
151
|
+
--max-length <n> Max label length for regex mode (default: 4)
|
|
152
|
+
|
|
153
|
+
Filtering:
|
|
154
|
+
-f, --filter <rule> Filter results:
|
|
155
|
+
tld:com
|
|
156
|
+
tld:com,io,net
|
|
157
|
+
length<=3
|
|
158
|
+
length>=2
|
|
159
|
+
starts:go
|
|
160
|
+
ends:ai
|
|
161
|
+
|
|
162
|
+
Sorting:
|
|
163
|
+
-s, --sort <mode> Sort results:
|
|
164
|
+
comfirst (.com first)
|
|
165
|
+
tld group by TLD
|
|
166
|
+
length shortest first
|
|
167
|
+
alpha alphabetical
|
|
168
|
+
|
|
169
|
+
Output:
|
|
170
|
+
-F, --format <fmt> txt | json | jsonl | csv
|
|
171
|
+
-o, --output <file> Output file path
|
|
172
|
+
|
|
173
|
+
Performance:
|
|
174
|
+
-c, --concurrency <n> DNS concurrency (default: 10)
|
|
175
|
+
-T, --timeout <ms> DNS timeout (default: 5000)
|
|
176
|
+
|
|
177
|
+
Resume / Cache:
|
|
178
|
+
-R, --resume Resume from cache (skip already checked)
|
|
179
|
+
--no-resume Ignore cache
|
|
180
|
+
-C, --cache <file> Cache file (default: checked_domains.jsonl)
|
|
181
|
+
--no-cache Disable caching
|
|
182
|
+
|
|
183
|
+
Interactive Controls:
|
|
184
|
+
p Pause
|
|
185
|
+
r Resume
|
|
186
|
+
q Quit gracefully
|
|
187
|
+
`);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// ---------- FILTERS & SORTING ----------
|
|
191
|
+
|
|
192
|
+
function parseFilterRule(rule) {
|
|
193
|
+
// tld:com,io
|
|
194
|
+
if (rule.startsWith('tld:')) {
|
|
195
|
+
const list = rule.slice(4).split(',').map(s => s.trim().toLowerCase()).filter(Boolean);
|
|
196
|
+
return { type: 'tld', list };
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// length<=3, length>=2
|
|
200
|
+
if (rule.startsWith('length<=')) {
|
|
201
|
+
const n = parseInt(rule.slice('length<='.length), 10);
|
|
202
|
+
return { type: 'lengthMax', value: n };
|
|
203
|
+
}
|
|
204
|
+
if (rule.startsWith('length>=')) {
|
|
205
|
+
const n = parseInt(rule.slice('length>='.length), 10);
|
|
206
|
+
return { type: 'lengthMin', value: n };
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// starts:go
|
|
210
|
+
if (rule.startsWith('starts:')) {
|
|
211
|
+
const v = rule.slice('starts:'.length).toLowerCase();
|
|
212
|
+
return { type: 'starts', value: v };
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// ends:ai
|
|
216
|
+
if (rule.startsWith('ends:')) {
|
|
217
|
+
const v = rule.slice('ends:'.length).toLowerCase();
|
|
218
|
+
return { type: 'ends', value: v };
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
return null;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function buildFilters(filterStrings) {
|
|
225
|
+
return filterStrings
|
|
226
|
+
.map(parseFilterRule)
|
|
227
|
+
.filter(Boolean);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function passesFilters(domain, filters) {
|
|
231
|
+
if (!filters.length) return true;
|
|
232
|
+
|
|
233
|
+
const [name, tld] = (() => {
|
|
234
|
+
const parts = domain.split('.');
|
|
235
|
+
const tld = parts.pop().toLowerCase();
|
|
236
|
+
const name = parts.join('.').toLowerCase();
|
|
237
|
+
return [name, tld];
|
|
238
|
+
})();
|
|
239
|
+
|
|
240
|
+
for (const f of filters) {
|
|
241
|
+
if (f.type === 'tld') {
|
|
242
|
+
if (!f.list.includes(tld)) return false;
|
|
243
|
+
} else if (f.type === 'lengthMax') {
|
|
244
|
+
if (name.length > f.value) return false;
|
|
245
|
+
} else if (f.type === 'lengthMin') {
|
|
246
|
+
if (name.length < f.value) return false;
|
|
247
|
+
} else if (f.type === 'starts') {
|
|
248
|
+
if (!name.startsWith(f.value)) return false;
|
|
249
|
+
} else if (f.type === 'ends') {
|
|
250
|
+
if (!name.endsWith(f.value)) return false;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
return true;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function sortResults(results, mode) {
|
|
258
|
+
if (!mode) return results;
|
|
259
|
+
|
|
260
|
+
if (mode === 'comfirst') {
|
|
261
|
+
return results.sort((a, b) => {
|
|
262
|
+
const aCom = a.tld === 'com' ? 0 : 1;
|
|
263
|
+
const bCom = b.tld === 'com' ? 0 : 1;
|
|
264
|
+
if (aCom !== bCom) return aCom - bCom;
|
|
265
|
+
return a.domain.localeCompare(b.domain);
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
if (mode === 'tld') {
|
|
270
|
+
return results.sort((a, b) => {
|
|
271
|
+
if (a.tld !== b.tld) return a.tld.localeCompare(b.tld);
|
|
272
|
+
return a.domain.localeCompare(b.domain);
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
if (mode === 'length') {
|
|
277
|
+
return results.sort((a, b) => {
|
|
278
|
+
if (a.name.length !== b.name.length) return a.name.length - b.name.length;
|
|
279
|
+
return a.domain.localeCompare(b.domain);
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
if (mode === 'alpha') {
|
|
284
|
+
return results.sort((a, b) => a.domain.localeCompare(b.domain));
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
return results;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// ---------- PATTERN / REGEX GENERATION ----------
|
|
291
|
+
|
|
292
|
+
function* expandWildcardPattern(pattern) {
|
|
293
|
+
// * = single char from CHARSET
|
|
294
|
+
function* helper(index, prefix) {
|
|
295
|
+
if (index === pattern.length) {
|
|
296
|
+
yield prefix;
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
const ch = pattern[index];
|
|
300
|
+
if (ch === '*') {
|
|
301
|
+
for (const c of CHARSET) {
|
|
302
|
+
yield* helper(index + 1, prefix + c);
|
|
303
|
+
}
|
|
304
|
+
} else {
|
|
305
|
+
yield* helper(index + 1, prefix + ch);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
yield* helper(0, '');
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
function* expandPatternWithTlds(pattern, tlds) {
|
|
312
|
+
if (pattern.endsWith('.*')) {
|
|
313
|
+
const core = pattern.slice(0, -2); // keep trailing dot
|
|
314
|
+
for (const base of expandWildcardPattern(core)) {
|
|
315
|
+
for (const tld of tlds) {
|
|
316
|
+
yield base + tld;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
} else {
|
|
320
|
+
yield* expandWildcardPattern(pattern);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
function* generateAllDomainsForRegex(maxLength, tlds) {
|
|
325
|
+
function* build(prefix, depth) {
|
|
326
|
+
if (depth === 0) {
|
|
327
|
+
for (const tld of tlds) {
|
|
328
|
+
yield prefix + '.' + tld;
|
|
329
|
+
}
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
for (const c of CHARSET) {
|
|
333
|
+
yield* build(prefix + c, depth - 1);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
for (let len = 1; len <= maxLength; len++) {
|
|
338
|
+
yield* build('', len);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// ---------- DNS + CACHE ----------
|
|
343
|
+
|
|
344
|
+
async function checkDomain(domain, timeoutMs) {
|
|
345
|
+
const timeout = new Promise((_, reject) =>
|
|
346
|
+
setTimeout(() => reject(new Error('TIMEOUT')), timeoutMs)
|
|
347
|
+
);
|
|
348
|
+
|
|
349
|
+
try {
|
|
350
|
+
await Promise.race([
|
|
351
|
+
dns.resolveAny(domain),
|
|
352
|
+
timeout
|
|
353
|
+
]);
|
|
354
|
+
return { domain, available: false, error: null };
|
|
355
|
+
} catch (err) {
|
|
356
|
+
if (err.code === 'ENOTFOUND' || err.code === 'ENODATA') {
|
|
357
|
+
return { domain, available: true, error: null };
|
|
358
|
+
}
|
|
359
|
+
if (err.message === 'TIMEOUT') {
|
|
360
|
+
return { domain, available: null, error: 'timeout' };
|
|
361
|
+
}
|
|
362
|
+
return { domain, available: null, error: err.code || err.message };
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
function loadCache(cacheFile) {
|
|
367
|
+
const map = new Map();
|
|
368
|
+
if (!fs.existsSync(cacheFile)) return map;
|
|
369
|
+
const lines = fs.readFileSync(cacheFile, 'utf8').split('\n');
|
|
370
|
+
for (const line of lines) {
|
|
371
|
+
if (!line.trim()) continue;
|
|
372
|
+
try {
|
|
373
|
+
const obj = JSON.parse(line);
|
|
374
|
+
if (obj.domain) map.set(obj.domain, obj);
|
|
375
|
+
} catch {
|
|
376
|
+
// ignore bad lines
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
return map;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
function appendToCache(cacheFile, obj) {
|
|
383
|
+
fs.appendFileSync(cacheFile, JSON.stringify(obj) + '\n');
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
// ---------- OUTPUT ----------
|
|
387
|
+
|
|
388
|
+
function writeOutput(results, opts) {
|
|
389
|
+
const out = opts.output;
|
|
390
|
+
const fmt = opts.format;
|
|
391
|
+
|
|
392
|
+
if (fmt === 'txt') {
|
|
393
|
+
const lines = results.map(r => r.domain);
|
|
394
|
+
fs.writeFileSync(out, lines.join('\n') + '\n', 'utf8');
|
|
395
|
+
console.log(`ā
Saved ${results.length} domains to ${out} (txt).`);
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
if (fmt === 'json') {
|
|
400
|
+
fs.writeFileSync(out, JSON.stringify(results, null, 2), 'utf8');
|
|
401
|
+
console.log(`ā
Saved ${results.length} domains to ${out} (json).`);
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
if (fmt === 'jsonl') {
|
|
406
|
+
const lines = results.map(r => JSON.stringify(r));
|
|
407
|
+
fs.writeFileSync(out, lines.join('\n') + '\n', 'utf8');
|
|
408
|
+
console.log(`ā
Saved ${results.length} domains to ${out} (jsonl).`);
|
|
409
|
+
return;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
if (fmt === 'csv') {
|
|
413
|
+
const header = 'domain,tld,name,available,checkedAt,error\n';
|
|
414
|
+
const rows = results.map(r =>
|
|
415
|
+
`${r.domain},${r.tld},${r.name},${r.available},${r.checkedAt},${r.error || ''}`
|
|
416
|
+
);
|
|
417
|
+
fs.writeFileSync(out, header + rows.join('\n') + '\n', 'utf8');
|
|
418
|
+
console.log(`ā
Saved ${results.length} domains to ${out} (csv).`);
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
// fallback
|
|
423
|
+
const lines = results.map(r => r.domain);
|
|
424
|
+
fs.writeFileSync(out, lines.join('\n') + '\n', 'utf8');
|
|
425
|
+
console.log(`ā
Saved ${results.length} domains to ${out} (txt fallback).`);
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
// ---------- MAIN RUN ----------
|
|
429
|
+
|
|
430
|
+
async function run() {
|
|
431
|
+
const opts = parseArgs();
|
|
432
|
+
|
|
433
|
+
if (!opts.pattern && !opts.regex) {
|
|
434
|
+
printHelp();
|
|
435
|
+
process.exit(1);
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
const filters = buildFilters(opts.filters);
|
|
439
|
+
|
|
440
|
+
let tlds;
|
|
441
|
+
if (opts.allTlds) tlds = IANA_TLDS;
|
|
442
|
+
else if (opts.premiumTlds) tlds = PREMIUM_TLDS;
|
|
443
|
+
else if (opts.tlds && opts.tlds.length) tlds = opts.tlds;
|
|
444
|
+
else tlds = ['com'];
|
|
445
|
+
|
|
446
|
+
let regex = null;
|
|
447
|
+
if (opts.regex) {
|
|
448
|
+
try {
|
|
449
|
+
regex = new RegExp(opts.regex);
|
|
450
|
+
} catch (err) {
|
|
451
|
+
console.error('Invalid regex:', err.message);
|
|
452
|
+
process.exit(1);
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
const cache = opts.useCache ? loadCache(opts.cacheFile) : new Map();
|
|
457
|
+
|
|
458
|
+
console.log(`š Starting domain search`);
|
|
459
|
+
if (opts.pattern) console.log(` Pattern: ${opts.pattern}`);
|
|
460
|
+
if (regex) console.log(` Regex: ${opts.regex}`);
|
|
461
|
+
console.log(` TLDs: ${tlds.join(', ')}`);
|
|
462
|
+
console.log(` Concurrency: ${opts.concurrency}, Timeout: ${opts.timeout}ms`);
|
|
463
|
+
console.log(` Output: ${opts.output} (${opts.format})`);
|
|
464
|
+
if (opts.useCache) console.log(` Cache: ${opts.cacheFile} (${cache.size} entries loaded)`);
|
|
465
|
+
|
|
466
|
+
setupInteractiveControls();
|
|
467
|
+
|
|
468
|
+
const iterator = regex
|
|
469
|
+
? generateAllDomainsForRegex(opts.maxLength, tlds)
|
|
470
|
+
: expandPatternWithTlds(opts.pattern, tlds);
|
|
471
|
+
|
|
472
|
+
const availableResults = [];
|
|
473
|
+
const tasks = [];
|
|
474
|
+
let active = 0;
|
|
475
|
+
let checked = 0;
|
|
476
|
+
let totalCandidates = 0;
|
|
477
|
+
const startTime = Date.now();
|
|
478
|
+
|
|
479
|
+
async function scheduleNext() {
|
|
480
|
+
if (quitting) return;
|
|
481
|
+
while (!paused && active < opts.concurrency) {
|
|
482
|
+
const next = iterator.next();
|
|
483
|
+
if (next.done) break;
|
|
484
|
+
const domain = next.value;
|
|
485
|
+
totalCandidates++;
|
|
486
|
+
|
|
487
|
+
if (!isValidDomain(domain)) continue;
|
|
488
|
+
if (!passesFilters(domain, filters)) continue;
|
|
489
|
+
|
|
490
|
+
if (opts.useCache && cache.has(domain)) {
|
|
491
|
+
continue;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
active++;
|
|
495
|
+
const task = (async () => {
|
|
496
|
+
const res = await checkDomain(domain, opts.timeout);
|
|
497
|
+
checked++;
|
|
498
|
+
|
|
499
|
+
const [name, tld] = (() => {
|
|
500
|
+
const parts = domain.split('.');
|
|
501
|
+
const tld = parts.pop().toLowerCase();
|
|
502
|
+
const name = parts.join('.').toLowerCase();
|
|
503
|
+
return [name, tld];
|
|
504
|
+
})();
|
|
505
|
+
|
|
506
|
+
const record = {
|
|
507
|
+
domain,
|
|
508
|
+
name,
|
|
509
|
+
tld,
|
|
510
|
+
available: res.available,
|
|
511
|
+
checkedAt: new Date().toISOString(),
|
|
512
|
+
error: res.error
|
|
513
|
+
};
|
|
514
|
+
|
|
515
|
+
if (opts.useCache) {
|
|
516
|
+
cache.set(domain, record);
|
|
517
|
+
appendToCache(opts.cacheFile, record);
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
if (res.available === true) {
|
|
521
|
+
availableResults.push(record);
|
|
522
|
+
process.stdout.write(
|
|
523
|
+
`\rChecked: ${checked.toLocaleString()} | Available: ${availableResults.length.toLocaleString()} `
|
|
524
|
+
);
|
|
525
|
+
}
|
|
526
|
+
})().finally(() => {
|
|
527
|
+
active--;
|
|
528
|
+
});
|
|
529
|
+
|
|
530
|
+
tasks.push(task);
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
while (true) {
|
|
535
|
+
if (quitting) break;
|
|
536
|
+
if (!paused) {
|
|
537
|
+
await scheduleNext();
|
|
538
|
+
}
|
|
539
|
+
if (active === 0 && iterator.next().done) {
|
|
540
|
+
break;
|
|
541
|
+
}
|
|
542
|
+
await sleep(100);
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
await Promise.all(tasks);
|
|
546
|
+
|
|
547
|
+
const duration = ((Date.now() - startTime) / 1000).toFixed(1);
|
|
548
|
+
console.log(`\nā± Done in ${duration}s. Checked ${checked.toLocaleString()} domains.`);
|
|
549
|
+
console.log(`ā
Available: ${availableResults.length.toLocaleString()}`);
|
|
550
|
+
|
|
551
|
+
const sorted = sortResults(availableResults, opts.sort);
|
|
552
|
+
writeOutput(sorted, opts);
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
run().catch(err => {
|
|
556
|
+
console.error('Fatal error:', err);
|
|
557
|
+
process.exit(1);
|
|
558
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "wildcard-domain-finder-plus",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Advanced wildcard and regex-based domain availability scanner with streaming generation, filtering, sorting, and caching.",
|
|
5
|
+
"main": "app.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"wildcard-domain-finder-plus": "app.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
11
|
+
},
|
|
12
|
+
"repository": {
|
|
13
|
+
"type": "git",
|
|
14
|
+
"url": "git+https://github.com/nbcr/wildcard-domain-finder-plus.git"
|
|
15
|
+
},
|
|
16
|
+
"author": "nbcr",
|
|
17
|
+
"license": "ISC",
|
|
18
|
+
"bugs": {
|
|
19
|
+
"url": "https://github.com/nbcr/wildcard-domain-finder-plus/issues"
|
|
20
|
+
},
|
|
21
|
+
"homepage": "https://github.com/nbcr/wildcard-domain-finder-plus#readme",
|
|
22
|
+
"keywords": [
|
|
23
|
+
"domain",
|
|
24
|
+
"dns",
|
|
25
|
+
"wildcard",
|
|
26
|
+
"regex",
|
|
27
|
+
"tld",
|
|
28
|
+
"availability",
|
|
29
|
+
"scanner",
|
|
30
|
+
"cli",
|
|
31
|
+
"domain-finder"
|
|
32
|
+
]
|
|
33
|
+
}
|