wildcard-domain-finder-plus 1.0.3 ā 1.0.5
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/app.js +182 -267
- package/available_domains.txt +1389 -0
- package/checked_domains.jsonl +13889 -0
- package/package.json +4 -1
package/app.js
CHANGED
|
@@ -1,23 +1,24 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
// wildcard-domain-finder (
|
|
4
|
-
//
|
|
5
|
-
// -
|
|
3
|
+
// wildcard-domain-finder (streaming, old UX, round-robin DNS)
|
|
4
|
+
//
|
|
5
|
+
// - Wildcard patterns (* = single char)
|
|
6
6
|
// - TLD selection (explicit, all, premium)
|
|
7
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
8
|
// - Streaming generation (no heap blowups)
|
|
12
9
|
// - Concurrency + timeout
|
|
13
|
-
// -
|
|
10
|
+
// - Round-robin across many public resolvers (dns2)
|
|
11
|
+
// - Old-style UX: emoji progress, stats, recent found, current domain
|
|
12
|
+
// - Interactive: p = pause, r = resume, q = quit, Ctrl+C = interrupt
|
|
14
13
|
|
|
15
14
|
const fs = require('fs');
|
|
16
|
-
const dns = require('dns').promises;
|
|
17
15
|
const readline = require('readline');
|
|
16
|
+
const { DNSClient } = require('dns2');
|
|
18
17
|
|
|
18
|
+
// Character set for wildcard expansion
|
|
19
19
|
const CHARSET = 'abcdefghijklmnopqrstuvwxyz0123456789';
|
|
20
20
|
|
|
21
|
+
// TLD lists
|
|
21
22
|
const IANA_TLDS = [
|
|
22
23
|
'com','net','org','edu','gov','mil','int',
|
|
23
24
|
'ca','us','uk','de','fr','au','jp','cn','io','ai','co','me','tv','cc',
|
|
@@ -37,10 +38,35 @@ const IANA_TLDS = [
|
|
|
37
38
|
|
|
38
39
|
const PREMIUM_TLDS = ['com','net','org','io','ai','co','dev','app','xyz','tech'];
|
|
39
40
|
|
|
41
|
+
// Domain validation regex
|
|
40
42
|
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
43
|
|
|
44
|
+
// Large public resolver pool (round-robin)
|
|
45
|
+
const RESOLVERS = [
|
|
46
|
+
'1.1.1.1','1.0.0.1',
|
|
47
|
+
'8.8.8.8','8.8.4.4',
|
|
48
|
+
'9.9.9.9','149.112.112.112',
|
|
49
|
+
'208.67.222.222','208.67.220.220',
|
|
50
|
+
'185.228.168.9','185.228.169.9',
|
|
51
|
+
'185.228.168.10','185.228.169.11',
|
|
52
|
+
'94.140.14.14','94.140.15.15',
|
|
53
|
+
'156.154.70.1','156.154.71.1',
|
|
54
|
+
'4.2.2.1','4.2.2.2',
|
|
55
|
+
'64.6.64.6','64.6.65.6'
|
|
56
|
+
];
|
|
57
|
+
|
|
58
|
+
let resolverIndex = 0;
|
|
59
|
+
function nextResolver() {
|
|
60
|
+
const ip = RESOLVERS[resolverIndex];
|
|
61
|
+
resolverIndex = (resolverIndex + 1) % RESOLVERS.length;
|
|
62
|
+
return ip;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Interactive state
|
|
42
66
|
let paused = false;
|
|
43
67
|
let quitting = false;
|
|
68
|
+
let currentDomain = '';
|
|
69
|
+
let recentlyFound = []; // last 3 available domains
|
|
44
70
|
|
|
45
71
|
function isValidDomain(domain) {
|
|
46
72
|
return DOMAIN_REGEX.test(domain);
|
|
@@ -69,6 +95,11 @@ function setupInteractiveControls() {
|
|
|
69
95
|
process.stdout.write('\nš Quitting gracefully...\n');
|
|
70
96
|
}
|
|
71
97
|
});
|
|
98
|
+
|
|
99
|
+
process.on('SIGINT', () => {
|
|
100
|
+
console.log('\n\nā¹ļø Process interrupted by user (Ctrl+C). Exiting...');
|
|
101
|
+
process.exit(0);
|
|
102
|
+
});
|
|
72
103
|
}
|
|
73
104
|
|
|
74
105
|
// ---------- CLI PARSING ----------
|
|
@@ -77,28 +108,19 @@ function parseArgs() {
|
|
|
77
108
|
const args = process.argv.slice(2);
|
|
78
109
|
const opts = {
|
|
79
110
|
pattern: null,
|
|
80
|
-
regex: null,
|
|
81
111
|
tlds: null,
|
|
82
112
|
allTlds: false,
|
|
83
113
|
premiumTlds: false,
|
|
84
114
|
filters: [],
|
|
85
|
-
sort: null,
|
|
86
|
-
format: 'txt',
|
|
87
115
|
output: 'available_domains.txt',
|
|
88
|
-
concurrency:
|
|
89
|
-
timeout:
|
|
90
|
-
resume: false,
|
|
91
|
-
cacheFile: 'checked_domains.jsonl',
|
|
92
|
-
useCache: true,
|
|
93
|
-
maxLength: 4
|
|
116
|
+
concurrency: 50,
|
|
117
|
+
timeout: 3000
|
|
94
118
|
};
|
|
95
119
|
|
|
96
120
|
for (let i = 0; i < args.length; i++) {
|
|
97
121
|
const a = args[i];
|
|
98
122
|
if (a === '-d' || a === '--domain') {
|
|
99
123
|
opts.pattern = args[++i];
|
|
100
|
-
} else if (a === '-r' || a === '--regex') {
|
|
101
|
-
opts.regex = args[++i];
|
|
102
124
|
} else if (a === '-t' || a === '--tlds') {
|
|
103
125
|
const v = args[++i];
|
|
104
126
|
if (v === 'all') opts.allTlds = true;
|
|
@@ -106,26 +128,12 @@ function parseArgs() {
|
|
|
106
128
|
else opts.tlds = v.split(',').map(s => s.trim().toLowerCase()).filter(Boolean);
|
|
107
129
|
} else if (a === '-f' || a === '--filter') {
|
|
108
130
|
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
131
|
} else if (a === '-o' || a === '--output') {
|
|
114
132
|
opts.output = args[++i];
|
|
115
133
|
} else if (a === '-c' || a === '--concurrency') {
|
|
116
|
-
opts.concurrency = parseInt(args[++i], 10) ||
|
|
134
|
+
opts.concurrency = parseInt(args[++i], 10) || opts.concurrency;
|
|
117
135
|
} else if (a === '-T' || a === '--timeout') {
|
|
118
|
-
opts.timeout = parseInt(args[++i], 10) ||
|
|
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;
|
|
136
|
+
opts.timeout = parseInt(args[++i], 10) || opts.timeout;
|
|
129
137
|
} else if (a === '-h' || a === '--help') {
|
|
130
138
|
printHelp();
|
|
131
139
|
process.exit(0);
|
|
@@ -137,18 +145,15 @@ function parseArgs() {
|
|
|
137
145
|
|
|
138
146
|
function printHelp() {
|
|
139
147
|
console.log(`
|
|
140
|
-
Wildcard Domain Finder (
|
|
148
|
+
š Wildcard Domain Finder (streaming, old-style UX, round-robin DNS)
|
|
141
149
|
|
|
142
150
|
Usage:
|
|
143
|
-
|
|
151
|
+
node app.js [options]
|
|
144
152
|
|
|
145
153
|
Domain Input:
|
|
146
154
|
-d, --domain <pattern> Wildcard pattern (* = single char)
|
|
147
|
-
-
|
|
148
|
-
|
|
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)
|
|
155
|
+
-t, --tlds <list|all|premium>
|
|
156
|
+
e.g. com,net,io | all | premium
|
|
152
157
|
|
|
153
158
|
Filtering:
|
|
154
159
|
-f, --filter <rule> Filter results:
|
|
@@ -159,44 +164,29 @@ Filtering:
|
|
|
159
164
|
starts:go
|
|
160
165
|
ends:ai
|
|
161
166
|
|
|
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
167
|
Output:
|
|
170
|
-
-
|
|
171
|
-
-o, --output <file> Output file path
|
|
168
|
+
-o, --output <file> Output file path (txt, one domain per line)
|
|
172
169
|
|
|
173
170
|
Performance:
|
|
174
|
-
-c, --concurrency <n> DNS concurrency (default:
|
|
175
|
-
-T, --timeout <ms> DNS timeout (default:
|
|
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
|
|
171
|
+
-c, --concurrency <n> DNS concurrency (default: 50)
|
|
172
|
+
-T, --timeout <ms> DNS timeout (default: 3000)
|
|
182
173
|
|
|
183
174
|
Interactive Controls:
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
175
|
+
p Pause
|
|
176
|
+
r Resume
|
|
177
|
+
q Quit gracefully
|
|
178
|
+
Ctrl+C Interrupt immediately
|
|
187
179
|
`);
|
|
188
180
|
}
|
|
189
181
|
|
|
190
|
-
// ---------- FILTERS
|
|
182
|
+
// ---------- FILTERS ----------
|
|
191
183
|
|
|
192
184
|
function parseFilterRule(rule) {
|
|
193
|
-
// tld:com,io
|
|
194
185
|
if (rule.startsWith('tld:')) {
|
|
195
186
|
const list = rule.slice(4).split(',').map(s => s.trim().toLowerCase()).filter(Boolean);
|
|
196
187
|
return { type: 'tld', list };
|
|
197
188
|
}
|
|
198
189
|
|
|
199
|
-
// length<=3, length>=2
|
|
200
190
|
if (rule.startsWith('length<=')) {
|
|
201
191
|
const n = parseInt(rule.slice('length<='.length), 10);
|
|
202
192
|
return { type: 'lengthMax', value: n };
|
|
@@ -206,13 +196,11 @@ function parseFilterRule(rule) {
|
|
|
206
196
|
return { type: 'lengthMin', value: n };
|
|
207
197
|
}
|
|
208
198
|
|
|
209
|
-
// starts:go
|
|
210
199
|
if (rule.startsWith('starts:')) {
|
|
211
200
|
const v = rule.slice('starts:'.length).toLowerCase();
|
|
212
201
|
return { type: 'starts', value: v };
|
|
213
202
|
}
|
|
214
203
|
|
|
215
|
-
// ends:ai
|
|
216
204
|
if (rule.startsWith('ends:')) {
|
|
217
205
|
const v = rule.slice('ends:'.length).toLowerCase();
|
|
218
206
|
return { type: 'ends', value: v };
|
|
@@ -254,43 +242,9 @@ function passesFilters(domain, filters) {
|
|
|
254
242
|
return true;
|
|
255
243
|
}
|
|
256
244
|
|
|
257
|
-
|
|
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 ----------
|
|
245
|
+
// ---------- PATTERN GENERATION ----------
|
|
291
246
|
|
|
292
247
|
function* expandWildcardPattern(pattern) {
|
|
293
|
-
// * = single char from CHARSET
|
|
294
248
|
function* helper(index, prefix) {
|
|
295
249
|
if (index === pattern.length) {
|
|
296
250
|
yield prefix;
|
|
@@ -310,7 +264,7 @@ function* expandWildcardPattern(pattern) {
|
|
|
310
264
|
|
|
311
265
|
function* expandPatternWithTlds(pattern, tlds) {
|
|
312
266
|
if (pattern.endsWith('.*')) {
|
|
313
|
-
const core = pattern.slice(0, -2);
|
|
267
|
+
const core = pattern.slice(0, -2);
|
|
314
268
|
for (const base of expandWildcardPattern(core)) {
|
|
315
269
|
for (const tld of tlds) {
|
|
316
270
|
yield base + tld;
|
|
@@ -321,116 +275,83 @@ function* expandPatternWithTlds(pattern, tlds) {
|
|
|
321
275
|
}
|
|
322
276
|
}
|
|
323
277
|
|
|
324
|
-
|
|
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 ----------
|
|
278
|
+
// ---------- DNS (round-robin resolvers via dns2) ----------
|
|
343
279
|
|
|
344
280
|
async function checkDomain(domain, timeoutMs) {
|
|
281
|
+
const resolver = nextResolver();
|
|
282
|
+
const client = DNSClient({
|
|
283
|
+
dns: resolver,
|
|
284
|
+
port: 53,
|
|
285
|
+
recursive: true
|
|
286
|
+
});
|
|
287
|
+
|
|
345
288
|
const timeout = new Promise((_, reject) =>
|
|
346
289
|
setTimeout(() => reject(new Error('TIMEOUT')), timeoutMs)
|
|
347
290
|
);
|
|
348
291
|
|
|
349
292
|
try {
|
|
350
|
-
await Promise.race([
|
|
351
|
-
|
|
293
|
+
const res = await Promise.race([
|
|
294
|
+
client.resolve(domain, 'A'),
|
|
352
295
|
timeout
|
|
353
296
|
]);
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
if (err.code === 'ENOTFOUND' || err.code === 'ENODATA') {
|
|
297
|
+
const answers = res && res.answers ? res.answers : [];
|
|
298
|
+
if (answers.length === 0) {
|
|
357
299
|
return { domain, available: true, error: null };
|
|
358
300
|
}
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
return { domain, available:
|
|
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
|
-
}
|
|
301
|
+
return { domain, available: false, error: null };
|
|
302
|
+
} catch (err) {
|
|
303
|
+
const msg = err && err.message ? err.message : String(err);
|
|
304
|
+
return { domain, available: true, error: msg };
|
|
378
305
|
}
|
|
379
|
-
return map;
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
function appendToCache(cacheFile, obj) {
|
|
383
|
-
fs.appendFileSync(cacheFile, JSON.stringify(obj) + '\n');
|
|
384
306
|
}
|
|
385
307
|
|
|
386
|
-
// ----------
|
|
387
|
-
|
|
388
|
-
function
|
|
389
|
-
const
|
|
390
|
-
const
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
if (
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
308
|
+
// ---------- PROGRESS DISPLAY (old-style UX) ----------
|
|
309
|
+
|
|
310
|
+
function displayProgress(stats) {
|
|
311
|
+
const elapsed = (Date.now() - stats.startTime) / 1000;
|
|
312
|
+
const rate = stats.checked > 0 ? stats.checked / elapsed : 0;
|
|
313
|
+
const percentage = stats.totalCandidates > 0
|
|
314
|
+
? (stats.checked / stats.totalCandidates) * 100
|
|
315
|
+
: 0;
|
|
316
|
+
|
|
317
|
+
const barWidth = 40;
|
|
318
|
+
const filledWidth = Math.round((percentage / 100) * barWidth);
|
|
319
|
+
const emptyWidth = barWidth - filledWidth;
|
|
320
|
+
const progressBar = 'ā'.repeat(filledWidth) + 'ā'.repeat(emptyWidth);
|
|
321
|
+
|
|
322
|
+
console.clear();
|
|
323
|
+
console.log('š Wildcard Domain Finder - Live Progress');
|
|
324
|
+
console.log('='.repeat(70));
|
|
325
|
+
console.log(`Progress: [${progressBar}] ${percentage.toFixed(1)}%`);
|
|
326
|
+
console.log(`Status: ${stats.checked.toLocaleString()}/${stats.totalCandidates.toLocaleString()} domains checked`);
|
|
327
|
+
console.log('');
|
|
328
|
+
console.log('š Statistics:');
|
|
329
|
+
console.log(` ā
Available: ${stats.available.toLocaleString()}`);
|
|
330
|
+
console.log(` ā Errors: ${stats.errors.toLocaleString()}`);
|
|
331
|
+
console.log(` ā±ļø Elapsed: ${elapsed.toFixed(1)}s`);
|
|
332
|
+
console.log(` š Rate: ${rate.toFixed(1)} domains/sec`);
|
|
333
|
+
console.log('');
|
|
334
|
+
if (recentlyFound.length > 0) {
|
|
335
|
+
console.log('šÆ Recently Found Available Domains:');
|
|
336
|
+
recentlyFound.forEach((domain, index) => {
|
|
337
|
+
const icon = index === 0 ? 'š' : index === 1 ? 'š' : 'š';
|
|
338
|
+
console.log(` ${icon} ${domain}`);
|
|
339
|
+
});
|
|
340
|
+
} else {
|
|
341
|
+
console.log('š No available domains found yet...');
|
|
420
342
|
}
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
console.log(`ā
Saved ${results.length} domains to ${out} (txt fallback).`);
|
|
343
|
+
console.log('');
|
|
344
|
+
console.log(`š Currently checking: ${currentDomain || 'Initializing...'}`);
|
|
345
|
+
console.log('='.repeat(70));
|
|
346
|
+
console.log('š” Keys: p = pause, r = resume, q = quit, Ctrl+C = interrupt');
|
|
426
347
|
}
|
|
427
348
|
|
|
428
|
-
// ---------- MAIN RUN ----------
|
|
349
|
+
// ---------- MAIN RUN (STREAMING) ----------
|
|
429
350
|
|
|
430
351
|
async function run() {
|
|
431
352
|
const opts = parseArgs();
|
|
432
353
|
|
|
433
|
-
if (!opts.pattern
|
|
354
|
+
if (!opts.pattern) {
|
|
434
355
|
printHelp();
|
|
435
356
|
process.exit(1);
|
|
436
357
|
}
|
|
@@ -443,85 +364,66 @@ async function run() {
|
|
|
443
364
|
else if (opts.tlds && opts.tlds.length) tlds = opts.tlds;
|
|
444
365
|
else tlds = ['com'];
|
|
445
366
|
|
|
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
367
|
console.log(`š Starting domain search`);
|
|
459
|
-
|
|
460
|
-
if (regex) console.log(` Regex: ${opts.regex}`);
|
|
368
|
+
console.log(` Pattern: ${opts.pattern}`);
|
|
461
369
|
console.log(` TLDs: ${tlds.join(', ')}`);
|
|
462
370
|
console.log(` Concurrency: ${opts.concurrency}, Timeout: ${opts.timeout}ms`);
|
|
463
|
-
console.log(` Output: ${opts.output}
|
|
464
|
-
|
|
371
|
+
console.log(` Output: ${opts.output}`);
|
|
372
|
+
console.log('\nš” Keys: p = pause, r = resume, q = quit, Ctrl+C = interrupt\n');
|
|
465
373
|
|
|
466
374
|
setupInteractiveControls();
|
|
467
375
|
|
|
468
|
-
const iterator =
|
|
469
|
-
|
|
470
|
-
|
|
376
|
+
const iterator = expandPatternWithTlds(opts.pattern, tlds);
|
|
377
|
+
const outStream = fs.createWriteStream(opts.output, { flags: 'w' });
|
|
378
|
+
|
|
379
|
+
const stats = {
|
|
380
|
+
totalCandidates: 0,
|
|
381
|
+
checked: 0,
|
|
382
|
+
available: 0,
|
|
383
|
+
errors: 0,
|
|
384
|
+
startTime: Date.now()
|
|
385
|
+
};
|
|
471
386
|
|
|
472
|
-
const availableResults = [];
|
|
473
|
-
const tasks = [];
|
|
474
387
|
let active = 0;
|
|
475
|
-
|
|
476
|
-
let
|
|
477
|
-
|
|
388
|
+
const tasks = [];
|
|
389
|
+
let lastProgressUpdate = 0;
|
|
390
|
+
let done = false;
|
|
478
391
|
|
|
479
392
|
async function scheduleNext() {
|
|
480
|
-
if (quitting) return;
|
|
481
|
-
while (!paused && active < opts.concurrency) {
|
|
482
|
-
const
|
|
483
|
-
if (
|
|
484
|
-
|
|
485
|
-
|
|
393
|
+
if (quitting || done) return;
|
|
394
|
+
while (!paused && active < opts.concurrency && !done) {
|
|
395
|
+
const { value: domain, done: isDone } = iterator.next();
|
|
396
|
+
if (isDone) {
|
|
397
|
+
done = true;
|
|
398
|
+
break;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
stats.totalCandidates++;
|
|
486
402
|
|
|
487
403
|
if (!isValidDomain(domain)) continue;
|
|
488
404
|
if (!passesFilters(domain, filters)) continue;
|
|
489
405
|
|
|
490
|
-
if (opts.useCache && cache.has(domain)) {
|
|
491
|
-
continue;
|
|
492
|
-
}
|
|
493
|
-
|
|
494
406
|
active++;
|
|
407
|
+
currentDomain = domain;
|
|
408
|
+
|
|
495
409
|
const task = (async () => {
|
|
496
410
|
const res = await checkDomain(domain, opts.timeout);
|
|
497
|
-
checked++;
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
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);
|
|
411
|
+
stats.checked++;
|
|
412
|
+
|
|
413
|
+
if (res.available) {
|
|
414
|
+
stats.available++;
|
|
415
|
+
outStream.write(domain + '\n');
|
|
416
|
+
|
|
417
|
+
recentlyFound.unshift(domain);
|
|
418
|
+
if (recentlyFound.length > 3) recentlyFound.pop();
|
|
419
|
+
} else if (res.error) {
|
|
420
|
+
stats.errors++;
|
|
518
421
|
}
|
|
519
422
|
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
);
|
|
423
|
+
const now = Date.now();
|
|
424
|
+
if (now - lastProgressUpdate > 500 || (done && active === 0)) {
|
|
425
|
+
lastProgressUpdate = now;
|
|
426
|
+
displayProgress(stats);
|
|
525
427
|
}
|
|
526
428
|
})().finally(() => {
|
|
527
429
|
active--;
|
|
@@ -531,28 +433,41 @@ async function run() {
|
|
|
531
433
|
}
|
|
532
434
|
}
|
|
533
435
|
|
|
534
|
-
while (
|
|
535
|
-
if (quitting) break;
|
|
436
|
+
while (!done || active > 0) {
|
|
437
|
+
if (quitting && active === 0) break;
|
|
536
438
|
if (!paused) {
|
|
537
439
|
await scheduleNext();
|
|
538
440
|
}
|
|
539
|
-
|
|
540
|
-
break;
|
|
541
|
-
}
|
|
542
|
-
await sleep(100);
|
|
441
|
+
await sleep(50);
|
|
543
442
|
}
|
|
544
443
|
|
|
545
444
|
await Promise.all(tasks);
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
console.
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
445
|
+
outStream.end();
|
|
446
|
+
|
|
447
|
+
const totalTime = (Date.now() - stats.startTime) / 1000;
|
|
448
|
+
console.clear();
|
|
449
|
+
console.log('š Domain Search Completed');
|
|
450
|
+
console.log('='.repeat(60));
|
|
451
|
+
console.log(`š Total candidates generated: ${stats.totalCandidates.toLocaleString()}`);
|
|
452
|
+
console.log(`š Total domains checked: ${stats.checked.toLocaleString()}`);
|
|
453
|
+
console.log(`ā
Available domains found: ${stats.available.toLocaleString()}`);
|
|
454
|
+
console.log(`ā Errors encountered: ${stats.errors.toLocaleString()}`);
|
|
455
|
+
console.log(`ā±ļø Total time: ${totalTime.toFixed(1)}s`);
|
|
456
|
+
console.log(`š Average rate: ${(stats.checked / totalTime).toFixed(1)} domains/sec`);
|
|
457
|
+
console.log(`š Results saved to: ${opts.output}`);
|
|
458
|
+
if (stats.available > 0) {
|
|
459
|
+
console.log('');
|
|
460
|
+
console.log('šÆ Recently Found Available Domains:');
|
|
461
|
+
recentlyFound.forEach((d, i) => {
|
|
462
|
+
console.log(` ${i + 1}. ${d}`);
|
|
463
|
+
});
|
|
464
|
+
} else {
|
|
465
|
+
console.log('');
|
|
466
|
+
console.log('š No available domains found with this pattern.');
|
|
467
|
+
}
|
|
468
|
+
console.log('='.repeat(60));
|
|
553
469
|
}
|
|
554
470
|
|
|
555
471
|
run().catch(err => {
|
|
556
472
|
console.error('Fatal error:', err);
|
|
557
|
-
process
|
|
558
|
-
});
|
|
473
|
+
process
|