geoip-lite2 2.2.8-beta.4 → 2.2.8-beta.7

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.
@@ -1 +1 @@
1
- f4a4c624512ef085feded9b74f5a797c1fedfae6c9a31d1a2c65462e23338d24 GeoLite2-City-CSV_20251107.zip
1
+ 1e2d74273ff207a2a7d129a195b300dbc45b22a4358cd3e45de17ae47ccab4f8 GeoLite2-City-CSV_20251128.zip
@@ -1 +1 @@
1
- f6acd69d1aae734a4ccf1130d6133c99042c716ff814a4cc045b2109406c543b GeoLite2-Country-CSV_20251107.zip
1
+ 67ac2aeddc82fc748089eaf5b01edf2ce0895c0c383a4a3f6812270a93bb3333 GeoLite2-Country-CSV_20251128.zip
Binary file
Binary file
Binary file
Binary file
Binary file
package/fsWatcher.js ADDED
@@ -0,0 +1,85 @@
1
+ // ============================================================================
2
+ // File System Watcher for Auto-Reloading GeoIP Data
3
+ // ============================================================================
4
+
5
+ const { access, constants, watch } = require('node:fs');
6
+ const { join } = require('node:path');
7
+ const FSWatcher = {};
8
+
9
+ // ============================================================================
10
+ // Watcher Management
11
+ // ============================================================================
12
+
13
+ /**
14
+ * Takes an FSWatcher object and closes it.
15
+ * @param {string} name - The name of the watcher to close.
16
+ */
17
+ const stopWatching = name => FSWatcher[name].close();
18
+
19
+ // ============================================================================
20
+ // File System Watch with Debounce
21
+ // ============================================================================
22
+
23
+ /**
24
+ * Takes a directory/file and watch for change. Upon change, call the callback.
25
+ *
26
+ * @param {String} name - name of this watcher
27
+ * @param {String} directory - path to the directory to watch
28
+ * @param {String} [filename] - (optional) specific filename to watch for, watches for all files in the directory if unspecified
29
+ * @param {Number} cdDelay - delay to wait before triggering the callback
30
+ * @param {Function} callback - function() - called when changes are detected
31
+ */
32
+ const makeFsWatchFilter = (name, directory, filename, cdDelay, callback) => {
33
+ let cdId = null;
34
+
35
+ // Delete the cdId and callback the outer function
36
+ function timeoutCallback() {
37
+ cdId = null;
38
+ callback();
39
+ }
40
+
41
+ // This function is called when there is a change in the data directory
42
+ // It sets a timer to wait for the change to be completed
43
+ function onWatchEvent(event, changedFile) {
44
+ // Check to make sure changedFile is not null
45
+ if (!changedFile) return;
46
+
47
+ const filePath = join(directory, changedFile);
48
+ if (!filename || filename === changedFile) {
49
+ access(filePath, constants.F_OK, err => {
50
+ if (err) return console.error(err);
51
+
52
+ // At this point, a new file system activity has been detected,
53
+ // We have to wait for file transfer to be finished before moving on.
54
+
55
+ // If a cdId already exists, we delete it
56
+ if (cdId !== null) {
57
+ clearTimeout(cdId);
58
+ cdId = null;
59
+ }
60
+
61
+ // Once the cdDelay has passed, the timeoutCallback function will be called
62
+ cdId = setTimeout(timeoutCallback, cdDelay);
63
+ });
64
+ }
65
+ }
66
+
67
+ // Manage the case where filename is missing (because it's optional)
68
+ if (typeof cdDelay === 'function') {
69
+ callback = cdDelay;
70
+ cdDelay = filename;
71
+ filename = null;
72
+ }
73
+
74
+ if (FSWatcher[name]) {
75
+ stopWatching(name);
76
+ }
77
+
78
+ FSWatcher[name] = watch(directory, onWatchEvent);
79
+ };
80
+
81
+ // ============================================================================
82
+ // Exports
83
+ // ============================================================================
84
+
85
+ module.exports = { makeFsWatchFilter, stopWatching };
package/index.js ADDED
@@ -0,0 +1,571 @@
1
+ // ============================================================================
2
+ // Dependencies
3
+ // ============================================================================
4
+
5
+ const { open, fstat, read, close, openSync, fstatSync, readSync, closeSync } = require('node:fs');
6
+ const { join, resolve } = require('node:path');
7
+ const { isIP } = require('node:net');
8
+ const async = require('async');
9
+ const { aton4, aton6, cmp6, ntoa4, ntoa6, cmp } = require('./utils.js');
10
+ const fsWatcher = require('./fsWatcher.js');
11
+ const { version } = require('./package.json');
12
+
13
+ // ============================================================================
14
+ // Configuration
15
+ // ============================================================================
16
+
17
+ const watcherName = 'dataWatcher';
18
+
19
+ const geoDataDir = resolve(
20
+ __dirname,
21
+ global.geoDataDir || process.env.GEODATADIR || './data/'
22
+ );
23
+
24
+ const dataFiles = {
25
+ city: join(geoDataDir, 'geoip-city.dat'),
26
+ city6: join(geoDataDir, 'geoip-city6.dat'),
27
+ cityNames: join(geoDataDir, 'geoip-city-names.dat'),
28
+ country: join(geoDataDir, 'geoip-country.dat'),
29
+ country6: join(geoDataDir, 'geoip-country6.dat'),
30
+ };
31
+
32
+ const privateRange4 = [
33
+ [aton4('10.0.0.0'), aton4('10.255.255.255')],
34
+ [aton4('172.16.0.0'), aton4('172.31.255.255')],
35
+ [aton4('192.168.0.0'), aton4('192.168.255.255')],
36
+ ];
37
+
38
+ // ============================================================================
39
+ // Cache Configuration
40
+ // ============================================================================
41
+
42
+ const conf4 = {
43
+ firstIP: null,
44
+ lastIP: null,
45
+ lastLine: 0,
46
+ locationBuffer: null,
47
+ locationRecordSize: 88,
48
+ mainBuffer: null,
49
+ recordSize: 24,
50
+ };
51
+
52
+ const conf6 = {
53
+ firstIP: null,
54
+ lastIP: null,
55
+ lastLine: 0,
56
+ mainBuffer: null,
57
+ recordSize: 48,
58
+ };
59
+
60
+ let cache4 = { ...conf4 };
61
+ let cache6 = { ...conf6 };
62
+
63
+ const RECORD_SIZE = 10;
64
+ const RECORD_SIZE6 = 34;
65
+
66
+ // ============================================================================
67
+ // Helper Functions
68
+ // ============================================================================
69
+
70
+ const removeNullTerminator = str => {
71
+ const nullIndex = str.indexOf('\0');
72
+ return nullIndex === -1 ? str : str.substring(0, nullIndex);
73
+ };
74
+
75
+ const readIp6 = (buffer, line, recordSize, offset) => {
76
+ const ipArray = [];
77
+ for (let i = 0; i < 2; i++) {
78
+ ipArray.push(buffer.readUInt32BE((line * recordSize) + (offset * 16) + (i * 4)));
79
+ }
80
+ return ipArray;
81
+ };
82
+
83
+ // ============================================================================
84
+ // IPv4 Lookup Function
85
+ // ============================================================================
86
+
87
+ const lookup4 = ip => {
88
+ let fline = 0;
89
+ let cline = cache4.lastLine;
90
+ let floor = cache4.lastIP;
91
+ let ceil = cache4.firstIP;
92
+ let line, locId;
93
+
94
+ const buffer = cache4.mainBuffer;
95
+ const locBuffer = cache4.locationBuffer;
96
+ const privateRange = privateRange4;
97
+ const recordSize = cache4.recordSize;
98
+ const locRecordSize = cache4.locationRecordSize;
99
+
100
+ const geoData = {
101
+ range: [null, null],
102
+ country: '',
103
+ region: '',
104
+ eu: '',
105
+ timezone: '',
106
+ city: '',
107
+ ll: [null, null],
108
+ metro: null,
109
+ area: null,
110
+ };
111
+
112
+ // Outside IPv4 range
113
+ if (ip > cache4.lastIP || ip < cache4.firstIP) return null;
114
+
115
+ // Private IP
116
+ for (let i = 0; i < privateRange.length; i++) {
117
+ if (ip >= privateRange[i][0] && ip <= privateRange[i][1]) return null;
118
+ }
119
+
120
+ while (true) {
121
+ line = Math.round((cline - fline) / 2) + fline;
122
+ const offset = line * recordSize;
123
+ floor = buffer.readUInt32BE(offset);
124
+ ceil = buffer.readUInt32BE(offset + 4);
125
+
126
+ if (floor <= ip && ceil >= ip) {
127
+ geoData.range = [floor, ceil];
128
+
129
+ if (recordSize === RECORD_SIZE) {
130
+ geoData.country = buffer.toString('utf8', offset + 8, offset + 10);
131
+ } else {
132
+ locId = buffer.readUInt32BE(offset + 8);
133
+
134
+ // -1>>>0 is a marker for "No Location Info"
135
+ if (-1 >>> 0 > locId) {
136
+ const locOffset = locId * locRecordSize;
137
+ geoData.country = removeNullTerminator(locBuffer.toString('utf8', locOffset, locOffset + 2));
138
+ geoData.region = removeNullTerminator(locBuffer.toString('utf8', locOffset + 2, locOffset + 5));
139
+ geoData.metro = locBuffer.readInt32BE(locOffset + 5);
140
+ geoData.ll[0] = buffer.readInt32BE(offset + 12) / 10000; // latitude
141
+ geoData.ll[1] = buffer.readInt32BE(offset + 16) / 10000; // longitude
142
+ geoData.area = buffer.readUInt32BE(offset + 20);
143
+ geoData.eu = removeNullTerminator(locBuffer.toString('utf8', locOffset + 9, locOffset + 10));
144
+ geoData.timezone = removeNullTerminator(locBuffer.toString('utf8', locOffset + 10, locOffset + 42));
145
+ geoData.city = removeNullTerminator(locBuffer.toString('utf8', locOffset + 42, locOffset + locRecordSize));
146
+ }
147
+ }
148
+
149
+ return geoData;
150
+ } else if (fline === cline) {
151
+ return null;
152
+ } else if (fline === (cline - 1)) {
153
+ if (line === fline) {
154
+ fline = cline;
155
+ } else {
156
+ cline = fline;
157
+ }
158
+ } else if (floor > ip) {
159
+ cline = line;
160
+ } else if (ceil < ip) {
161
+ fline = line;
162
+ }
163
+ }
164
+ };
165
+
166
+ // ============================================================================
167
+ // IPv6 Lookup Function
168
+ // ============================================================================
169
+
170
+ const lookup6 = ip => {
171
+ const buffer = cache6.mainBuffer;
172
+ const recordSize = cache6.recordSize;
173
+ const locBuffer = cache4.locationBuffer;
174
+ const locRecordSize = cache4.locationRecordSize;
175
+
176
+ const geoData = {
177
+ range: [null, null],
178
+ country: '',
179
+ region: '',
180
+ eu: '',
181
+ timezone: '',
182
+ city: '',
183
+ ll: [null, null],
184
+ metro: null,
185
+ area: null,
186
+ };
187
+
188
+ let fline = 0;
189
+ let cline = cache6.lastLine;
190
+ let floor = cache6.lastIP;
191
+ let ceil = cache6.firstIP;
192
+ let line, locId;
193
+
194
+ if (cmp6(ip, cache6.lastIP) > 0 || cmp6(ip, cache6.firstIP) < 0) return null;
195
+
196
+ while (true) {
197
+ line = Math.round((cline - fline) / 2) + fline;
198
+ floor = readIp6(buffer, line, recordSize, 0);
199
+ ceil = readIp6(buffer, line, recordSize, 1);
200
+
201
+ if (cmp6(floor, ip) <= 0 && cmp6(ceil, ip) >= 0) {
202
+ const offset = line * recordSize;
203
+ if (recordSize === RECORD_SIZE6) {
204
+ geoData.country = removeNullTerminator(buffer.toString('utf8', offset + 32, offset + 34));
205
+ } else {
206
+ locId = buffer.readUInt32BE(offset + 32);
207
+
208
+ // -1>>>0 is a marker for "No Location Info"
209
+ if (-1 >>> 0 > locId) {
210
+ const locOffset = locId * locRecordSize;
211
+ geoData.country = removeNullTerminator(locBuffer.toString('utf8', locOffset, locOffset + 2));
212
+ geoData.region = removeNullTerminator(locBuffer.toString('utf8', locOffset + 2, locOffset + 5));
213
+ geoData.metro = locBuffer.readInt32BE(locOffset + 5);
214
+ geoData.ll[0] = buffer.readInt32BE(offset + 36) / 10000; // latitude
215
+ geoData.ll[1] = buffer.readInt32BE(offset + 40) / 10000; // longitude
216
+ geoData.area = buffer.readUInt32BE(offset + 44); // area
217
+ geoData.eu = removeNullTerminator(locBuffer.toString('utf8', locOffset + 9, locOffset + 10));
218
+ geoData.timezone = removeNullTerminator(locBuffer.toString('utf8', locOffset + 10, locOffset + 42));
219
+ geoData.city = removeNullTerminator(locBuffer.toString('utf8', locOffset + 42, locOffset + locRecordSize));
220
+ }
221
+ }
222
+ // We do not currently have detailed region/city info for IPv6, but finally have coords
223
+ return geoData;
224
+ } else if (fline === cline) {
225
+ return null;
226
+ } else if (fline === (cline - 1)) {
227
+ if (line === fline) {
228
+ fline = cline;
229
+ } else {
230
+ cline = fline;
231
+ }
232
+ } else if (cmp6(floor, ip) > 0) {
233
+ cline = line;
234
+ } else if (cmp6(ceil, ip) < 0) {
235
+ fline = line;
236
+ }
237
+ }
238
+ };
239
+
240
+ // ============================================================================
241
+ // IPv4-Mapped IPv6 Handler
242
+ // ============================================================================
243
+
244
+ const V6_PREFIX_1 = '0:0:0:0:0:FFFF:';
245
+ const V6_PREFIX_2 = '::FFFF:';
246
+ const get4mapped = ip => {
247
+ const ipv6 = ip.toUpperCase();
248
+ if (ipv6.startsWith(V6_PREFIX_1)) return ipv6.substring(V6_PREFIX_1.length);
249
+ if (ipv6.startsWith(V6_PREFIX_2)) return ipv6.substring(V6_PREFIX_2.length);
250
+ return null;
251
+ };
252
+
253
+ // ============================================================================
254
+ // Data Loading Functions - IPv4
255
+ // ============================================================================
256
+
257
+ function preload(callback) {
258
+ let datFile;
259
+ let datSize;
260
+ const asyncCache = { ...conf4 };
261
+
262
+ // When the preload function receives a callback, do the task asynchronously
263
+ if (typeof arguments[0] === 'function') {
264
+ async.series([
265
+ cb => {
266
+ async.series([
267
+ cb2 => {
268
+ open(dataFiles.cityNames, 'r', (err, file) => {
269
+ datFile = file;
270
+ cb2(err);
271
+ });
272
+ },
273
+ cb2 => {
274
+ fstat(datFile, (err, stats) => {
275
+ datSize = stats.size;
276
+ asyncCache.locationBuffer = Buffer.alloc(datSize);
277
+ cb2(err);
278
+ });
279
+ },
280
+ cb2 => {
281
+ read(datFile, asyncCache.locationBuffer, 0, datSize, 0, cb2);
282
+ },
283
+ cb2 => {
284
+ close(datFile, cb2);
285
+ },
286
+ cb2 => {
287
+ open(dataFiles.city, 'r', (err, file) => {
288
+ datFile = file;
289
+ cb2(err);
290
+ });
291
+ },
292
+ cb2 => {
293
+ fstat(datFile, (err, stats) => {
294
+ datSize = stats.size;
295
+ cb2(err);
296
+ });
297
+ },
298
+ ], err => {
299
+ if (err) {
300
+ if (err.code !== 'ENOENT' && err.code !== 'EBADF') {
301
+ throw err;
302
+ }
303
+
304
+ open(dataFiles.country, 'r', (err, file) => {
305
+ if (err) {
306
+ cb(err);
307
+ } else {
308
+ datFile = file;
309
+ fstat(datFile, (err, stats) => {
310
+ datSize = stats.size;
311
+ asyncCache.recordSize = RECORD_SIZE;
312
+
313
+ cb();
314
+ });
315
+ }
316
+ });
317
+
318
+ } else {
319
+ cb();
320
+ }
321
+ });
322
+ }, () => {
323
+ asyncCache.mainBuffer = Buffer.alloc(datSize);
324
+
325
+ async.series([
326
+ cb2 => {
327
+ read(datFile, asyncCache.mainBuffer, 0, datSize, 0, cb2);
328
+ },
329
+ cb2 => {
330
+ close(datFile, cb2);
331
+ },
332
+ ], err => {
333
+ if (!err) {
334
+ asyncCache.lastLine = (datSize / asyncCache.recordSize) - 1;
335
+ asyncCache.lastIP = asyncCache.mainBuffer.readUInt32BE((asyncCache.lastLine * asyncCache.recordSize) + 4);
336
+ asyncCache.firstIP = asyncCache.mainBuffer.readUInt32BE(0);
337
+ cache4 = asyncCache;
338
+ }
339
+ callback(err);
340
+ });
341
+ },
342
+ ]);
343
+ } else {
344
+ try {
345
+ datFile = openSync(dataFiles.cityNames, 'r');
346
+ datSize = fstatSync(datFile).size;
347
+ if (datSize === 0) {
348
+ const err = new Error('Empty file');
349
+ err.code = 'EMPTY_FILE';
350
+ throw err;
351
+ }
352
+
353
+ cache4.locationBuffer = Buffer.alloc(datSize);
354
+ readSync(datFile, cache4.locationBuffer, 0, datSize, 0);
355
+ closeSync(datFile);
356
+
357
+ datFile = openSync(dataFiles.city, 'r');
358
+ datSize = fstatSync(datFile).size;
359
+ } catch (err) {
360
+ if (err.code !== 'ENOENT' && err.code !== 'EBADF' && err.code !== 'EMPTY_FILE') {
361
+ throw err;
362
+ }
363
+
364
+ datFile = openSync(dataFiles.country, 'r');
365
+ datSize = fstatSync(datFile).size;
366
+ cache4.recordSize = RECORD_SIZE;
367
+ }
368
+
369
+ cache4.mainBuffer = Buffer.alloc(datSize);
370
+ readSync(datFile, cache4.mainBuffer, 0, datSize, 0);
371
+ closeSync(datFile);
372
+
373
+ cache4.lastLine = (datSize / cache4.recordSize) - 1;
374
+ cache4.lastIP = cache4.mainBuffer.readUInt32BE((cache4.lastLine * cache4.recordSize) + 4);
375
+ cache4.firstIP = cache4.mainBuffer.readUInt32BE(0);
376
+ }
377
+ }
378
+
379
+ // ============================================================================
380
+ // Data Loading Functions - IPv6
381
+ // ============================================================================
382
+
383
+ function preload6(callback) {
384
+ let datFile;
385
+ let datSize;
386
+ const asyncCache6 = { ...conf6 };
387
+
388
+ // When the preload function receives a callback, do the task asynchronously
389
+ if (typeof arguments[0] === 'function') {
390
+ async.series([
391
+ cb => {
392
+ async.series([
393
+ cb2 => {
394
+ open(dataFiles.city6, 'r', (err, file) => {
395
+ datFile = file;
396
+ cb2(err);
397
+ });
398
+ },
399
+ cb2 => {
400
+ fstat(datFile, (err, stats) => {
401
+ datSize = stats.size;
402
+ cb2(err);
403
+ });
404
+ },
405
+ ], err => {
406
+ if (err) {
407
+ if (err.code !== 'ENOENT' && err.code !== 'EBADF') {
408
+ throw err;
409
+ }
410
+
411
+ open(dataFiles.country6, 'r', (err, file) => {
412
+ if (err) {
413
+ cb(err);
414
+ } else {
415
+ datFile = file;
416
+ fstat(datFile, (err, stats) => {
417
+ datSize = stats.size;
418
+ asyncCache6.recordSize = RECORD_SIZE6;
419
+
420
+ cb();
421
+ });
422
+ }
423
+ });
424
+ } else {
425
+ cb();
426
+ }
427
+ });
428
+ }, () => {
429
+ asyncCache6.mainBuffer = Buffer.alloc(datSize);
430
+
431
+ async.series([
432
+ cb2 => {
433
+ read(datFile, asyncCache6.mainBuffer, 0, datSize, 0, cb2);
434
+ },
435
+ cb2 => {
436
+ close(datFile, cb2);
437
+ },
438
+ ], err => {
439
+ if (!err) {
440
+ asyncCache6.lastLine = (datSize / asyncCache6.recordSize) - 1;
441
+ asyncCache6.lastIP = readIp6(asyncCache6.mainBuffer, asyncCache6.lastLine, asyncCache6.recordSize, 1);
442
+ asyncCache6.firstIP = readIp6(asyncCache6.mainBuffer, 0, asyncCache6.recordSize, 0);
443
+ cache6 = asyncCache6;
444
+ }
445
+ callback(err);
446
+ });
447
+ },
448
+ ]);
449
+ } else {
450
+ try {
451
+ datFile = openSync(dataFiles.city6, 'r');
452
+ datSize = fstatSync(datFile).size;
453
+
454
+ if (datSize === 0) {
455
+ const err = new Error('Empty file');
456
+ err.code = 'EMPTY_FILE';
457
+ throw err;
458
+ }
459
+ } catch (err) {
460
+ if (err.code !== 'ENOENT' && err.code !== 'EBADF' && err.code !== 'EMPTY_FILE') {
461
+ throw err;
462
+ }
463
+
464
+ datFile = openSync(dataFiles.country6, 'r');
465
+ datSize = fstatSync(datFile).size;
466
+ cache6.recordSize = RECORD_SIZE6;
467
+ }
468
+
469
+ cache6.mainBuffer = Buffer.alloc(datSize);
470
+ readSync(datFile, cache6.mainBuffer, 0, datSize, 0);
471
+ closeSync(datFile);
472
+
473
+ cache6.lastLine = (datSize / cache6.recordSize) - 1;
474
+ cache6.lastIP = readIp6(cache6.mainBuffer, cache6.lastLine, cache6.recordSize, 1);
475
+ cache6.firstIP = readIp6(cache6.mainBuffer, 0, cache6.recordSize, 0);
476
+ }
477
+ }
478
+
479
+ // ============================================================================
480
+ // Public API
481
+ // ============================================================================
482
+
483
+ module.exports = {
484
+ cmp,
485
+
486
+ lookup: ip => {
487
+ if (!ip) {
488
+ return null;
489
+ } else if (typeof ip === 'number') {
490
+ return lookup4(ip);
491
+ } else if (isIP(ip) === 4) {
492
+ return lookup4(aton4(ip));
493
+ } else if (isIP(ip) === 6) {
494
+ const ipv4 = get4mapped(ip);
495
+ if (ipv4) {
496
+ return lookup4(aton4(ipv4));
497
+ } else {
498
+ return lookup6(aton6(ip));
499
+ }
500
+ }
501
+
502
+ return null;
503
+ },
504
+
505
+ pretty: n => {
506
+ if (typeof n === 'string') {
507
+ return n;
508
+ } else if (typeof n === 'number') {
509
+ return ntoa4(n);
510
+ } else if (Array.isArray(n)) {
511
+ return ntoa6(n);
512
+ }
513
+
514
+ return n;
515
+ },
516
+
517
+ // Start watching for data updates. The watcher waits one minute for file transfer to
518
+ // complete before triggering the callback.
519
+ startWatchingDataUpdate: callback => {
520
+ fsWatcher.makeFsWatchFilter(watcherName, geoDataDir, 60 * 1000, () => {
521
+ // Reload data
522
+ async.series([
523
+ cb => {
524
+ preload(cb);
525
+ }, cb => {
526
+ preload6(cb);
527
+ },
528
+ ], callback);
529
+ });
530
+ },
531
+
532
+ // Stop watching for data updates.
533
+ stopWatchingDataUpdate: () => fsWatcher.stopWatching(watcherName),
534
+
535
+ // Clear data
536
+ clear: () => {
537
+ cache4 = { ...conf4 };
538
+ cache6 = { ...conf6 };
539
+ },
540
+
541
+ // Reload data synchronously
542
+ reloadDataSync: () => {
543
+ preload();
544
+ preload6();
545
+ },
546
+
547
+ // Reload data asynchronously
548
+ reloadData: callback => {
549
+ // Reload data
550
+ async.series([
551
+ cb => {
552
+ preload(cb);
553
+ },
554
+ cb => {
555
+ preload6(cb);
556
+ },
557
+ ], callback);
558
+ },
559
+
560
+ version,
561
+ };
562
+
563
+ // ============================================================================
564
+ // Initialize - Load data on module startup
565
+ // ============================================================================
566
+
567
+ preload();
568
+ preload6();
569
+
570
+ // lookup4 = gen_lookup('geoip-country.dat', 4);
571
+ // lookup6 = gen_lookup('geoip-country6.dat', 16);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "geoip-lite2",
3
- "version": "2.2.8-beta.4",
3
+ "version": "2.2.8-beta.7",
4
4
  "description": "A light weight native JavaScript implementation of GeoIP API from MaxMind. Improved and faster version by Sefinek.",
5
5
  "keywords": [
6
6
  "city",
@@ -52,7 +52,7 @@
52
52
  "license": "Apache-2.0",
53
53
  "author": "Philip Tellis <philip@bluesmoon.info> (https://bluesmoon.info)",
54
54
  "type": "commonjs",
55
- "main": "dist/main.js",
55
+ "main": "index.js",
56
56
  "types": "index.d.ts",
57
57
  "directories": {
58
58
  "lib": "lib",
@@ -60,15 +60,18 @@
60
60
  },
61
61
  "files": [
62
62
  "data",
63
- "dist",
64
63
  "tools",
65
64
  "AUTHORS",
65
+ "fsWatcher.js",
66
66
  "index.d.ts",
67
+ "index.js",
67
68
  "LICENSE",
68
- "README.md"
69
+ "README.md",
70
+ "utils.js"
69
71
  ],
70
72
  "scripts": {
71
- "minify": "node minify.js",
73
+ "lint": "eslint .",
74
+ "lint:fix": "eslint . --fix",
72
75
  "test": "jest test",
73
76
  "up": "ncu -u && npm install && npm update && npm audit fix",
74
77
  "updatedb": "node tools/updatedb.js",
@@ -80,15 +83,14 @@
80
83
  "async": "^3.2.6",
81
84
  "iconv-lite": "0.7.0",
82
85
  "ip-address": "^10.1.0",
83
- "rimraf": "^6.1.0"
86
+ "rimraf": "^6.1.2"
84
87
  },
85
88
  "devDependencies": {
86
89
  "@eslint/js": "^9.39.1",
87
90
  "globals": "^16.5.0",
88
- "jest": "^30.2.0",
89
- "terser": "^5.44.1"
91
+ "jest": "^30.2.0"
90
92
  },
91
93
  "engines": {
92
- "node": ">=10.3.0"
94
+ "node": ">=20.0.0"
93
95
  }
94
96
  }
package/tools/updatedb.js CHANGED
@@ -23,7 +23,7 @@ const async = require('async');
23
23
  const { decodeStream } = require('iconv-lite');
24
24
  const rimraf = require('rimraf').sync;
25
25
  const AdmZip = require('adm-zip');
26
- const utils = require('../lib/utils.js');
26
+ const utils = require('../utils.js');
27
27
  const { Address6, Address4 } = require('ip-address');
28
28
 
29
29
  // ============================================================================
@@ -32,12 +32,10 @@ const { Address6, Address4 } = require('ip-address');
32
32
 
33
33
  // Logging utility for consistent and readable output
34
34
  const log = {
35
- info: (msg, ...logArgs) => console.log('[INFO]', msg, ...logArgs),
36
- success: (msg, ...logArgs) => console.log('[SUCCESS]', msg, ...logArgs),
37
- warn: (msg, ...logArgs) => console.warn('[WARN]', msg, ...logArgs),
38
- error: (msg, ...logArgs) => console.error('[ERROR]', msg, ...logArgs),
39
- progress: (msg) => process.stdout.write(`[INFO] ${msg}... `),
40
- done: () => console.log('Done'),
35
+ info: (msg, ...logArgs) => console.log(`[INFO] ${msg}`, ...logArgs),
36
+ success: (msg, ...logArgs) => console.log(`[SUCCESS] ${msg}`, ...logArgs),
37
+ warn: (msg, ...logArgs) => console.warn(`[WARN] ${msg}`, ...logArgs),
38
+ error: (msg, ...logArgs) => console.error(`[ERROR] ${msg}`, ...logArgs),
41
39
  };
42
40
 
43
41
  // ============================================================================
@@ -114,8 +112,8 @@ function tryFixingLine(line) {
114
112
  pos1 = pos2;
115
113
  pos2 = line.indexOf(',', pos1 + 1);
116
114
  if (pos2 < 0) pos2 = line.length;
117
- if (line.indexOf('\'', (pos1 || 0)) > -1 && line.indexOf('\'', pos1) < pos2 && line[pos1 + 1] != '"' && line[pos2 - 1] != '"') {
118
- line = line.substr(0, pos1 + 1) + '"' + line.substr(pos1 + 1, pos2 - pos1 - 1) + '"' + line.substr(pos2, line.length - pos2);
115
+ if (line.indexOf('\'', (pos1 || 0)) > -1 && line.indexOf('\'', pos1) < pos2 && line[pos1 + 1] !== '"' && line[pos2 - 1] !== '"') {
116
+ line = line.substring(0, pos1 + 1) + '"' + line.substring(pos1 + 1, pos2) + '"' + line.substring(pos2);
119
117
  pos2 = line.indexOf(',', pos2 + 1);
120
118
  if (pos2 < 0) pos2 = line.length;
121
119
  }
@@ -267,14 +265,12 @@ function fetch(database, cb) {
267
265
  }
268
266
 
269
267
  tmpFilePipe.on('close', () => {
270
- log.done();
268
+ log.info(`Retrieved ${fileName}`);
271
269
  cb(null, tmpFile, fileName, database);
272
270
  });
273
271
  }
274
272
 
275
273
  mkdir(tmpFile);
276
-
277
- log.progress(`Retrieving ${fileName}`);
278
274
  }
279
275
 
280
276
  function extract(tmpFile, tmpFileName, database, cb) {
@@ -283,7 +279,7 @@ function extract(tmpFile, tmpFileName, database, cb) {
283
279
  if (path.extname(tmpFileName) !== '.zip') {
284
280
  cb(null, database);
285
281
  } else {
286
- log.progress('Extracting ' + tmpFileName);
282
+ log.info('Extracting ' + tmpFileName);
287
283
  const zip = new AdmZip(tmpFile);
288
284
  const zipEntries = zip.getEntries();
289
285
 
@@ -297,7 +293,7 @@ function extract(tmpFile, tmpFileName, database, cb) {
297
293
  fs.writeFileSync(destinationPath, entry.getData());
298
294
  });
299
295
 
300
- log.done();
296
+ log.info('Extracted ' + tmpFileName);
301
297
  cb(null, database);
302
298
  }
303
299
  }
@@ -313,7 +309,7 @@ function processLookupCountry(src, cb) {
313
309
  }
314
310
  const tmpDataFile = path.join(tmpPath, src);
315
311
 
316
- log.progress('Processing lookup data (this may take a moment)');
312
+ log.info('Processing lookup data');
317
313
 
318
314
  const rl = readline.createInterface({ input: fs.createReadStream(tmpDataFile).pipe(decodeStream('latin1')), output: process.stdout, terminal: false });
319
315
 
@@ -324,7 +320,7 @@ function processLookupCountry(src, cb) {
324
320
  });
325
321
 
326
322
  rl.on('close', () => {
327
- log.done();
323
+ log.info('Processed lookup data');
328
324
  cb();
329
325
  });
330
326
  }
@@ -341,8 +337,7 @@ async function processCountryData(src, dest) {
341
337
  rimraf(dataFile);
342
338
  mkdir(dataFile);
343
339
 
344
- process.stdout.write('\n');
345
- log.progress('Processing country data (this may take a moment)');
340
+ log.info('Processing country data');
346
341
  let tstart = Date.now();
347
342
  const datFile = fs.createWriteStream(dataFile);
348
343
 
@@ -380,8 +375,8 @@ async function processCountryData(src, dest) {
380
375
  bsz = 10;
381
376
 
382
377
  rngip = new Address4(fields[0]);
383
- sip = parseInt(rngip.startAddress().bigInt(), 10);
384
- eip = parseInt(rngip.endAddress().bigInt(), 10);
378
+ sip = parseInt(rngip.startAddress().bigInt().toString(), 10);
379
+ eip = parseInt(rngip.endAddress().bigInt().toString(), 10);
385
380
 
386
381
  b = Buffer.alloc(bsz);
387
382
  b.fill(0);
@@ -392,7 +387,7 @@ async function processCountryData(src, dest) {
392
387
  b.write(cc, bsz - 2);
393
388
  if (Date.now() - tstart > 5000) {
394
389
  tstart = Date.now();
395
- process.stdout.write(`\nStill working (${lines})...`);
390
+ log.info(`Processing country data (${lines} entries)`);
396
391
  }
397
392
 
398
393
  if (datFile._writableState.needDrain) {
@@ -405,15 +400,53 @@ async function processCountryData(src, dest) {
405
400
  }
406
401
  }
407
402
 
408
- const rl = readline.createInterface({ input: fs.createReadStream(tmpDataFile), crlfDelay: Infinity });
409
- let i = 0;
410
- for await (const line of rl) {
411
- i++;
412
- if (i === 1) continue;
413
- await processLine(line);
414
- }
403
+ await new Promise((resolve, reject) => {
404
+ const rl = readline.createInterface({ input: fs.createReadStream(tmpDataFile), crlfDelay: Infinity });
405
+ let settled = false;
406
+ let i = 0;
407
+
408
+ function finish(err) {
409
+ if (settled) return;
410
+ settled = true;
411
+ if (!rl.closed) rl.close();
412
+ if (err) reject(err);
413
+ else resolve();
414
+ }
415
+
416
+ function resume() {
417
+ if (!settled && !rl.closed) rl.resume();
418
+ }
419
+
420
+ rl.on('line', line => {
421
+ rl.pause();
422
+ i++;
423
+ if (i === 1) {
424
+ resume();
425
+ return;
426
+ }
427
+
428
+ let result;
429
+ try {
430
+ result = processLine(line);
431
+ } catch (err) {
432
+ finish(err);
433
+ return;
434
+ }
435
+
436
+ if (result && typeof result.then === 'function') {
437
+ result.then(() => {
438
+ resume();
439
+ }).catch(finish);
440
+ } else {
441
+ resume();
442
+ }
443
+ });
444
+
445
+ rl.on('close', () => finish());
446
+ rl.on('error', finish);
447
+ });
415
448
  datFile.close();
416
- log.done();
449
+ log.info('Processed country data');
417
450
  }
418
451
 
419
452
  async function processCityData(src, dest) {
@@ -423,8 +456,7 @@ async function processCityData(src, dest) {
423
456
 
424
457
  rimraf(dataFile);
425
458
 
426
- process.stdout.write('\n');
427
- log.progress('Processing city data (this may take a moment)');
459
+ log.info('Processing city data');
428
460
  let tstart = Date.now();
429
461
  const datFile = fs.createWriteStream(dataFile);
430
462
 
@@ -482,8 +514,8 @@ async function processCityData(src, dest) {
482
514
  bsz = 24;
483
515
 
484
516
  rngip = new Address4(fields[0]);
485
- sip = parseInt(rngip.startAddress().bigInt(), 10);
486
- eip = parseInt(rngip.endAddress().bigInt(), 10);
517
+ sip = parseInt(rngip.startAddress().bigInt().toString(), 10);
518
+ eip = parseInt(rngip.endAddress().bigInt().toString(), 10);
487
519
  locId = parseInt(fields[1], 10);
488
520
  locId = cityLookup[locId];
489
521
  b = Buffer.alloc(bsz);
@@ -502,7 +534,7 @@ async function processCityData(src, dest) {
502
534
 
503
535
  if (Date.now() - tstart > 5000) {
504
536
  tstart = Date.now();
505
- process.stdout.write('\n[INFO] Processing... (' + lines + ' entries) ');
537
+ log.info(`Processing city data (${lines} entries)`);
506
538
  }
507
539
 
508
540
  if (datFile._writableState.needDrain) {
@@ -514,13 +546,51 @@ async function processCityData(src, dest) {
514
546
  }
515
547
  }
516
548
 
517
- const rl = readline.createInterface({ input: fs.createReadStream(tmpDataFile), crlfDelay: Infinity });
518
- let i = 0;
519
- for await (const line of rl) {
520
- i++;
521
- if (i === 1) continue;
522
- await processLine(line);
523
- }
549
+ await new Promise((resolve, reject) => {
550
+ const rl = readline.createInterface({ input: fs.createReadStream(tmpDataFile), crlfDelay: Infinity });
551
+ let settled = false;
552
+ let i = 0;
553
+
554
+ function finish(err) {
555
+ if (settled) return;
556
+ settled = true;
557
+ if (!rl.closed) rl.close();
558
+ if (err) reject(err);
559
+ else resolve();
560
+ }
561
+
562
+ function resume() {
563
+ if (!settled && !rl.closed) rl.resume();
564
+ }
565
+
566
+ rl.on('line', line => {
567
+ rl.pause();
568
+ i++;
569
+ if (i === 1) {
570
+ resume();
571
+ return;
572
+ }
573
+
574
+ let result;
575
+ try {
576
+ result = processLine(line);
577
+ } catch (err) {
578
+ finish(err);
579
+ return;
580
+ }
581
+
582
+ if (result && typeof result.then === 'function') {
583
+ result.then(() => {
584
+ resume();
585
+ }).catch(finish);
586
+ } else {
587
+ resume();
588
+ }
589
+ });
590
+
591
+ rl.on('close', () => finish());
592
+ rl.on('error', finish);
593
+ });
524
594
  datFile.close();
525
595
  }
526
596
 
@@ -610,11 +680,10 @@ function processData(database, cb) {
610
680
  } else if (type === 'city') {
611
681
  processCityDataNames(src[0], dest[0], () => {
612
682
  processCityData(src[1], dest[1]).then(() => {
613
- process.stdout.write('\n');
614
- log.info('City IPv4 data processed');
683
+ log.info('Processed city IPv4 data');
615
684
  return processCityData(src[2], dest[2]);
616
685
  }).then(() => {
617
- log.info('City IPv6 data processed');
686
+ log.info('Processed city IPv6 data');
618
687
  cb(null, database);
619
688
  });
620
689
  });
@@ -653,10 +722,9 @@ async.eachSeries(databases, (database, nextDatabase) => {
653
722
  log.error('Failed to update databases from MaxMind!', err);
654
723
  process.exit(1);
655
724
  } else {
656
- process.stdout.write('\n');
657
725
  log.success('All databases have been successfully updated from MaxMind');
658
726
  if (args.indexOf('debug') !== -1) {
659
- console.debug('Notice: temporary files are not deleted for debug purposes');
727
+ log.info('Debug mode: temporary files preserved at ' + tmpPath);
660
728
  } else {
661
729
  rimraf(tmpPath);
662
730
  }
package/utils.js ADDED
@@ -0,0 +1,103 @@
1
+ // ============================================================================
2
+ // Utility Functions for IP Address Conversion and Comparison
3
+ // ============================================================================
4
+
5
+ const utils = module.exports = {};
6
+
7
+ // ============================================================================
8
+ // IPv4 Conversion Functions
9
+ // ============================================================================
10
+
11
+ utils.aton4 = a => {
12
+ const parts = a.split('.');
13
+ return ((parseInt(parts[0], 10) << 24) >>> 0) + ((parseInt(parts[1], 10) << 16) >>> 0) + ((parseInt(parts[2], 10) << 8) >>> 0) + (parseInt(parts[3], 10) >>> 0);
14
+ };
15
+
16
+ utils.ntoa4 = n => {
17
+ return ((n >>> 24) & 0xff) + '.' + ((n >>> 16) & 0xff) + '.' + ((n >>> 8) & 0xff) + '.' + (n & 0xff);
18
+ };
19
+
20
+ // ============================================================================
21
+ // IPv6 Conversion Functions
22
+ // ============================================================================
23
+
24
+ utils.aton6 = a => {
25
+ a = a.replace(/"/g, '').split(':');
26
+
27
+ const l = a.length - 1;
28
+ let i;
29
+
30
+ if (a[l] === '') {
31
+ a[l] = 0;
32
+ }
33
+
34
+ if (l < 7) {
35
+ a.length = 8;
36
+
37
+ for (i = l; i >= 0 && a[i] !== ''; i--) {
38
+ a[7 - l + i] = a[i];
39
+ }
40
+ }
41
+
42
+ for (i = 0; i < 8; i++) {
43
+ if (!a[i]) {
44
+ a[i] = 0;
45
+ } else {
46
+ a[i] = parseInt(a[i], 16);
47
+ }
48
+ }
49
+
50
+ const r = [];
51
+ for (i = 0; i < 4; i++) {
52
+ r.push(((a[2 * i] << 16) + a[2 * i + 1]) >>> 0);
53
+ }
54
+
55
+ return r;
56
+ };
57
+
58
+ utils.ntoa6 = n => {
59
+ let a = '[';
60
+
61
+ for (let i = 0; i < n.length; i++) {
62
+ a += (n[i] >>> 16).toString(16) + ':';
63
+ a += (n[i] & 0xffff).toString(16) + ':';
64
+ }
65
+
66
+ a = a.replace(/:$/, ']').replace(/:0+/g, ':').replace(/::+/, '::');
67
+ return a;
68
+ };
69
+
70
+ // ============================================================================
71
+ // Comparison Functions
72
+ // ============================================================================
73
+
74
+ utils.cmp = (a, b) => {
75
+ if (typeof a === 'number' && typeof b === 'number') return (a < b ? -1 : (a > b ? 1 : 0));
76
+ if (a instanceof Array && b instanceof Array) return utils.cmp6(a, b);
77
+
78
+ return null;
79
+ };
80
+
81
+ utils.cmp6 = (a, b) => {
82
+ for (let ii = 0; ii < 2; ii++) {
83
+ if (a[ii] < b[ii]) return -1;
84
+ if (a[ii] > b[ii]) return 1;
85
+ }
86
+
87
+ return 0;
88
+ };
89
+
90
+ // ============================================================================
91
+ // IP Address Validation
92
+ // ============================================================================
93
+
94
+ utils.isPrivateIP = addr => {
95
+ const str = addr.toString();
96
+ return str.startsWith('10.') ||
97
+ str.startsWith('192.168.') ||
98
+ str.startsWith('172.16.') ||
99
+ str.startsWith('127.') ||
100
+ str.startsWith('169.254.') ||
101
+ str.startsWith('fc00:') ||
102
+ str.startsWith('fe80:');
103
+ };
package/dist/fsWatcher.js DELETED
@@ -1 +0,0 @@
1
- const{access:e,constants:n,watch:t}=require('node:fs'),{join:o}=require('node:path'),l={},u=e=>l[e].close();module.exports={makeFsWatchFilter:(c,r,s,i,a)=>{let f=null;function h(){f=null,a()}'function'==typeof i&&(a=i,i=s,s=null),l[c]&&u(c),l[c]=t(r,function(t,l){if(!l)return;const u=o(r,l);s&&s!==l||e(u,n.F_OK,e=>{if(e)return console.error(e);null!==f&&(clearTimeout(f),f=null),f=setTimeout(h,i)})})},stopWatching:u};
package/dist/main.js DELETED
@@ -1 +0,0 @@
1
- const{open:e,fstat:r,read:t,close:n,openSync:i,fstatSync:o,readSync:a,closeSync:l}=require('node:fs'),{join:f,resolve:u}=require('node:path'),{isIP:c}=require('node:net'),s=require('async'),{aton4:d,aton6:B,cmp6:y,ntoa4:I,ntoa6:m,cmp:E}=require('./utils.js'),S=require('./fsWatcher.js'),{version:g}=require('../package.json'),z='dataWatcher',p=u(__dirname,global.geoDataDir||process.env.GEODATADIR||'../data/'),P={city:f(p,'geoip-city.dat'),city6:f(p,'geoip-city6.dat'),cityNames:f(p,'geoip-city-names.dat'),country:f(p,'geoip-country.dat'),country6:f(p,'geoip-country6.dat')},h=[[d('10.0.0.0'),d('10.255.255.255')],[d('172.16.0.0'),d('172.31.255.255')],[d('192.168.0.0'),d('192.168.255.255')]],F={firstIP:null,lastIP:null,lastLine:0,locationBuffer:null,locationRecordSize:88,mainBuffer:null,recordSize:24},L={firstIP:null,lastIP:null,lastLine:0,mainBuffer:null,recordSize:48};let U={...F},D={...L};const N=e=>{const r=e.indexOf('\0');return-1===r?e:e.substring(0,r)},T=(e,r,t,n)=>{const i=[];for(let o=0;o<2;o++)i.push(e.readUInt32BE(r*t+16*n+4*o));return i},w=e=>{let r,t,n=0,i=U.lastLine,o=U.lastIP,a=U.firstIP;const l=U.mainBuffer,f=U.locationBuffer,u=h,c=U.recordSize,s=U.locationRecordSize,d={range:[null,null],country:'',region:'',eu:'',timezone:'',city:'',ll:[null,null],metro:null,area:null};if(e>U.lastIP||e<U.firstIP)return null;for(let r=0;r<u.length;r++)if(e>=u[r][0]&&e<=u[r][1])return null;for(;;){r=Math.round((i-n)/2)+n;const u=r*c;if(o=l.readUInt32BE(u),a=l.readUInt32BE(u+4),o<=e&&a>=e){if(d.range=[o,a],10===c)d.country=l.toString('utf8',u+8,u+10);else if(t=l.readUInt32BE(u+8),-1>>>0>t){const e=t*s;d.country=N(f.toString('utf8',e,e+2)),d.region=N(f.toString('utf8',e+2,e+5)),d.metro=f.readInt32BE(e+5),d.ll[0]=l.readInt32BE(u+12)/1e4,d.ll[1]=l.readInt32BE(u+16)/1e4,d.area=l.readUInt32BE(u+20),d.eu=N(f.toString('utf8',e+9,e+10)),d.timezone=N(f.toString('utf8',e+10,e+42)),d.city=N(f.toString('utf8',e+42,e+s))}return d}if(n===i)return null;n===i-1?r===n?n=i:i=n:o>e?i=r:a<e&&(n=r)}};function A(f){let u,c;const d={...F};if('function'==typeof arguments[0])s.series([i=>{s.series([r=>{e(P.cityNames,'r',(e,t)=>{u=t,r(e)})},e=>{r(u,(r,t)=>{c=t.size,d.locationBuffer=Buffer.alloc(c),e(r)})},e=>{t(u,d.locationBuffer,0,c,0,e)},e=>{n(u,e)},r=>{e(P.city,'r',(e,t)=>{u=t,r(e)})},e=>{r(u,(r,t)=>{c=t.size,e(r)})}],t=>{if(t){if('ENOENT'!==t.code&&'EBADF'!==t.code)throw t;e(P.country,'r',(e,t)=>{e?i(e):(u=t,r(u,(e,r)=>{c=r.size,d.recordSize=10,i()}))})}else i()})},()=>{d.mainBuffer=Buffer.alloc(c),s.series([e=>{t(u,d.mainBuffer,0,c,0,e)},e=>{n(u,e)}],e=>{e||(d.lastLine=c/d.recordSize-1,d.lastIP=d.mainBuffer.readUInt32BE(d.lastLine*d.recordSize+4),d.firstIP=d.mainBuffer.readUInt32BE(0),U=d),f(e)})}]);else{try{if(u=i(P.cityNames,'r'),c=o(u).size,0===c){const e=new Error('Empty file');throw e.code='EMPTY_FILE',e}U.locationBuffer=Buffer.alloc(c),a(u,U.locationBuffer,0,c,0),l(u),u=i(P.city,'r'),c=o(u).size}catch(e){if('ENOENT'!==e.code&&'EBADF'!==e.code&&'EMPTY_FILE'!==e.code)throw e;u=i(P.country,'r'),c=o(u).size,U.recordSize=10}U.mainBuffer=Buffer.alloc(c),a(u,U.mainBuffer,0,c,0),l(u),U.lastLine=c/U.recordSize-1,U.lastIP=U.mainBuffer.readUInt32BE(U.lastLine*U.recordSize+4),U.firstIP=U.mainBuffer.readUInt32BE(0)}}function W(f){let u,c;const d={...L};if('function'==typeof arguments[0])s.series([t=>{s.series([r=>{e(P.city6,'r',(e,t)=>{u=t,r(e)})},e=>{r(u,(r,t)=>{c=t.size,e(r)})}],n=>{if(n){if('ENOENT'!==n.code&&'EBADF'!==n.code)throw n;e(P.country6,'r',(e,n)=>{e?t(e):(u=n,r(u,(e,r)=>{c=r.size,d.recordSize=34,t()}))})}else t()})},()=>{d.mainBuffer=Buffer.alloc(c),s.series([e=>{t(u,d.mainBuffer,0,c,0,e)},e=>{n(u,e)}],e=>{e||(d.lastLine=c/d.recordSize-1,d.lastIP=T(d.mainBuffer,d.lastLine,d.recordSize,1),d.firstIP=T(d.mainBuffer,0,d.recordSize,0),D=d),f(e)})}]);else{try{if(u=i(P.city6,'r'),c=o(u).size,0===c){const e=new Error('Empty file');throw e.code='EMPTY_FILE',e}}catch(e){if('ENOENT'!==e.code&&'EBADF'!==e.code&&'EMPTY_FILE'!==e.code)throw e;u=i(P.country6,'r'),c=o(u).size,D.recordSize=34}D.mainBuffer=Buffer.alloc(c),a(u,D.mainBuffer,0,c,0),l(u),D.lastLine=c/D.recordSize-1,D.lastIP=T(D.mainBuffer,D.lastLine,D.recordSize,1),D.firstIP=T(D.mainBuffer,0,D.recordSize,0)}}module.exports={cmp:E,lookup:e=>{if(!e)return null;if('number'==typeof e)return w(e);if(4===c(e))return w(d(e));if(6===c(e)){const r=(e=>{const r=e.toUpperCase();return r.startsWith("0:0:0:0:0:FFFF:")?r.substring(15):r.startsWith("::FFFF:")?r.substring(7):null})(e);return r?w(d(r)):(e=>{const r=D.mainBuffer,t=D.recordSize,n=U.locationBuffer,i=U.locationRecordSize,o={range:[null,null],country:'',region:'',eu:'',timezone:'',city:'',ll:[null,null],metro:null,area:null};let a,l,f=0,u=D.lastLine,c=D.lastIP,s=D.firstIP;if(y(e,D.lastIP)>0||y(e,D.firstIP)<0)return null;for(;;){if(a=Math.round((u-f)/2)+f,c=T(r,a,t,0),s=T(r,a,t,1),y(c,e)<=0&&y(s,e)>=0){const e=a*t;if(34===t)o.country=N(r.toString('utf8',e+32,e+34));else if(l=r.readUInt32BE(e+32),-1>>>0>l){const t=l*i;o.country=N(n.toString('utf8',t,t+2)),o.region=N(n.toString('utf8',t+2,t+5)),o.metro=n.readInt32BE(t+5),o.ll[0]=r.readInt32BE(e+36)/1e4,o.ll[1]=r.readInt32BE(e+40)/1e4,o.area=r.readUInt32BE(e+44),o.eu=N(n.toString('utf8',t+9,t+10)),o.timezone=N(n.toString('utf8',t+10,t+42)),o.city=N(n.toString('utf8',t+42,t+i))}return o}if(f===u)return null;f===u-1?a===f?f=u:u=f:y(c,e)>0?u=a:y(s,e)<0&&(f=a)}})(B(e))}return null},pretty:e=>'string'==typeof e?e:'number'==typeof e?I(e):Array.isArray(e)?m(e):e,startWatchingDataUpdate:e=>{S.makeFsWatchFilter(z,p,6e4,()=>{s.series([e=>{A(e)},e=>{W(e)}],e)})},stopWatchingDataUpdate:()=>S.stopWatching(z),clear:()=>{U={...F},D={...L}},reloadDataSync:()=>{A(),W()},reloadData:e=>{s.series([e=>{A(e)},e=>{W(e)}],e)},version:g},A(),W();
package/dist/utils.js DELETED
@@ -1 +0,0 @@
1
- const t=module.exports={};t.aton4=t=>{const r=t.split('.');return(parseInt(r[0],10)<<24>>>0)+(parseInt(r[1],10)<<16>>>0)+(parseInt(r[2],10)<<8>>>0)+(parseInt(r[3],10)>>>0)},t.ntoa4=t=>(t>>>24&255)+'.'+(t>>>16&255)+'.'+(t>>>8&255)+'.'+(255&t),t.aton6=t=>{const r=(t=t.replace(/"/g,'').split(':')).length-1;let e;if(''===t[r]&&(t[r]=0),r<7)for(t.length=8,e=r;e>=0&&''!==t[e];e--)t[7-r+e]=t[e];for(e=0;e<8;e++)t[e]?t[e]=parseInt(t[e],16):t[e]=0;const n=[];for(e=0;e<4;e++)n.push((t[2*e]<<16)+t[2*e+1]>>>0);return n},t.ntoa6=t=>{let r='[';for(let e=0;e<t.length;e++)r+=(t[e]>>>16).toString(16)+':',r+=(65535&t[e]).toString(16)+':';return r=r.replace(/:$/,']').replace(/:0+/g,':').replace(/::+/,'::'),r},t.cmp=(r,e)=>'number'==typeof r&&'number'==typeof e?r<e?-1:r>e?1:0:r instanceof Array&&e instanceof Array?t.cmp6(r,e):null,t.cmp6=(t,r)=>{for(let e=0;e<2;e++){if(t[e]<r[e])return-1;if(t[e]>r[e])return 1}return 0},t.isPrivateIP=t=>{const r=t.toString();return r.startsWith('10.')||r.startsWith('192.168.')||r.startsWith('172.16.')||r.startsWith('127.')||r.startsWith('169.254.')||r.startsWith('fc00:')||r.startsWith('fe80:')};