Skip to content

Commit a5b2e84

Browse files
committed
Update
1 parent 7ff1f57 commit a5b2e84

File tree

14 files changed

+552
-523
lines changed

14 files changed

+552
-523
lines changed

CHANGELOG.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# Changelog
2+
3+
## 3.0.0-alpha.0 (2026-02-22)
4+
5+
### Breaking Changes
6+
- Removed the `range` field from the `lookup()` results.
7+
- Renamed `eu` to `isEu`.
8+
- Changed `isEu` to a boolean (`true`/`false`) instead of string values.
9+
10+
### Migration
11+
- Replace `<result>.eu` with `result.isEu`.
12+
- Remove any usage of `<result>.range`.
13+
14+
### Example
15+
#### 79.186.130.100
16+
```json
17+
{
18+
"country": "PL",
19+
"region": "32",
20+
"isEu": true,
21+
"timezone": "Europe/Warsaw",
22+
"city": "Szczecin",
23+
"ll": [53.4118, 14.5339],
24+
"metro": 0,
25+
"area": 200
26+
}
27+
```
28+
29+
##### 2a01:11bf:4222:900a:99ae:285f:7432:8f8e
30+
```json
31+
{
32+
"country": "PL",
33+
"region": "32",
34+
"isEu": true,
35+
"timezone": "Europe/Warsaw",
36+
"city": "Szczecin",
37+
"ll": [53.4518, 14.5556],
38+
"metro": 0,
39+
"area": 100
40+
}
41+
```

README.md

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,9 @@ console.log(geoIp.lookup(ip));
5151
### Output
5252
```json
5353
{
54-
"range": [ 2450746624, 2450746879 ],
5554
"country": "PL",
5655
"region": "14",
57-
"eu": "1",
56+
"isEu": true,
5857
"timezone": "Europe/Warsaw",
5958
"city": "Warsaw",
6059
"ll": [ 52.2296, 21.0067 ],
@@ -90,13 +89,12 @@ If the IP address was found, the `lookup` method returns an object with the foll
9089

9190
```js
9291
{
93-
range: [ <low bound of IP block>, <high bound of IP block> ],
9492
country: 'CC', // 2 letter ISO-3166-1 country code
9593
region: 'RR', // Up to 3 alphanumeric characters as ISO 3166-2 code
9694
// For US states this is the 2 letter state
9795
// For the United Kingdom this could be ENG as a country like "England"
9896
// FIPS 10-4 subcountry code
99-
eu: '0', // 1 if the country is a member state of the European Union, 0 otherwise.
97+
isEu: true, // true if the country is a member state of the European Union, otherwise false.
10098
timezone: 'Country/Zone', // Timezone from IANA Time Zone Database
10199
city: 'City name', // Full city name
102100
ll: [<latitude>, <longitude>], // Latitude and longitude of the city

data/geoip-city6.dat

0 Bytes
Binary file not shown.

data/geoip-country6.dat

0 Bytes
Binary file not shown.

index.d.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
interface GeoIp2Location {
2-
range: [number | null, number | null];
32
country: string;
43
region: string;
5-
eu: '0' | '1' | '';
4+
isEu: boolean;
65
timezone: string;
76
city: string;
87
ll: [number | null, number | null];

index.js

Lines changed: 81 additions & 180 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
const { open, fstat, read, close, openSync, fstatSync, readSync, closeSync } = require('node:fs');
1+
const { openSync, fstatSync, readSync, closeSync } = require('node:fs');
2+
const fsPromises = require('node:fs/promises');
23
const { basename, join, resolve } = require('node:path');
34
const { isIP } = require('node:net');
4-
const async = require('async');
55
const { aton4, aton6, cmp6, removeNullTerminator, readIp6, createGeoData, populateGeoDataFromLocation } = require('./scripts/utils.js');
66
const fsWatcher = require('./scripts/fsWatcher.js');
77
const { version } = require('./package.json');
@@ -81,8 +81,6 @@ const lookup4 = ip => {
8181
ceil = buffer.readUInt32BE(offset + 4);
8282

8383
if (floor <= ip && ceil >= ip) {
84-
geoData.range = [floor, ceil];
85-
8684
if (recordSize === RECORD_SIZE) {
8785
geoData.country = buffer.toString('utf8', offset + 8, offset + 10);
8886
} else {
@@ -180,105 +178,55 @@ const get4mapped = ip => {
180178
return null;
181179
};
182180

183-
const preload = callback => {
184-
let datFile;
185-
let datSize;
181+
const readFileBuffer = async filePath => {
182+
const fileHandle = await fsPromises.open(filePath, 'r');
183+
try {
184+
const { size } = await fileHandle.stat();
185+
const buffer = Buffer.alloc(size);
186+
if (size > 0) {
187+
await fileHandle.read(buffer, 0, size, 0);
188+
}
189+
return { buffer, size };
190+
} finally {
191+
await fileHandle.close();
192+
}
193+
};
194+
195+
const isExpectedMissingDataError = err => err?.code === 'ENOENT' || err?.code === 'EBADF';
196+
197+
const preloadAsync = async () => {
186198
const asyncCache = { ...conf4 };
199+
let mainData;
200+
201+
try {
202+
const cityNamesData = await readFileBuffer(dataFiles.cityNames);
203+
if (cityNamesData.size === 0) {
204+
const emptyFileError = new Error('geoip-city-names.dat is empty');
205+
emptyFileError.code = 'ENOENT';
206+
throw emptyFileError;
207+
}
208+
209+
asyncCache.locationBuffer = cityNamesData.buffer;
210+
mainData = await readFileBuffer(dataFiles.city);
211+
} catch (err) {
212+
if (!isExpectedMissingDataError(err)) throw err;
213+
mainData = await readFileBuffer(dataFiles.country);
214+
asyncCache.recordSize = RECORD_SIZE;
215+
}
216+
217+
asyncCache.mainBuffer = mainData.buffer;
218+
asyncCache.lastLine = (mainData.size / asyncCache.recordSize) - 1;
219+
asyncCache.lastIP = asyncCache.mainBuffer.readUInt32BE((asyncCache.lastLine * asyncCache.recordSize) + 4);
220+
asyncCache.firstIP = asyncCache.mainBuffer.readUInt32BE(0);
221+
cache4 = asyncCache;
222+
};
223+
224+
const preload = callback => {
187225
if (typeof callback === 'function') {
188-
void async.series([
189-
cb => {
190-
void async.series([
191-
cb2 => {
192-
open(dataFiles.cityNames, 'r', (err, file) => {
193-
datFile = file;
194-
cb2(err);
195-
});
196-
},
197-
cb2 => {
198-
fstat(datFile, (err, stats) => {
199-
if (err) {
200-
cb2(err);
201-
return;
202-
}
203-
datSize = stats.size;
204-
asyncCache.locationBuffer = Buffer.alloc(datSize);
205-
cb2();
206-
});
207-
},
208-
cb2 => {
209-
read(datFile, asyncCache.locationBuffer, 0, datSize, 0, cb2);
210-
},
211-
cb2 => {
212-
close(datFile, cb2);
213-
},
214-
cb2 => {
215-
open(dataFiles.city, 'r', (err, file) => {
216-
datFile = file;
217-
cb2(err);
218-
});
219-
},
220-
cb2 => {
221-
fstat(datFile, (err, stats) => {
222-
if (err) {
223-
cb2(err);
224-
return;
225-
}
226-
datSize = stats.size;
227-
cb2();
228-
});
229-
},
230-
], err => {
231-
if (err) {
232-
if (err.code !== 'ENOENT' && err.code !== 'EBADF') {
233-
cb(err);
234-
return;
235-
}
236-
237-
open(dataFiles.country, 'r', (err, file) => {
238-
if (err) {
239-
cb(err);
240-
} else {
241-
datFile = file;
242-
fstat(datFile, (err, stats) => {
243-
if (err) {
244-
cb(err);
245-
return;
246-
}
247-
datSize = stats.size;
248-
asyncCache.recordSize = RECORD_SIZE;
249-
250-
cb();
251-
});
252-
}
253-
});
254-
255-
} else {
256-
cb();
257-
}
258-
});
259-
},
260-
cb => {
261-
asyncCache.mainBuffer = Buffer.alloc(datSize);
262-
263-
void async.series([
264-
cb2 => {
265-
read(datFile, asyncCache.mainBuffer, 0, datSize, 0, cb2);
266-
},
267-
cb2 => {
268-
close(datFile, cb2);
269-
},
270-
], err => {
271-
if (!err) {
272-
asyncCache.lastLine = (datSize / asyncCache.recordSize) - 1;
273-
asyncCache.lastIP = asyncCache.mainBuffer.readUInt32BE((asyncCache.lastLine * asyncCache.recordSize) + 4);
274-
asyncCache.firstIP = asyncCache.mainBuffer.readUInt32BE(0);
275-
cache4 = asyncCache;
276-
}
277-
cb(err);
278-
});
279-
},
280-
], callback);
226+
preloadAsync().then(() => callback()).catch(callback);
281227
} else {
228+
let datFile;
229+
let datSize;
282230
try {
283231
datFile = openSync(dataFiles.cityNames, 'r');
284232
datSize = fstatSync(datFile).size;
@@ -315,81 +263,38 @@ const preload = callback => {
315263
}
316264
};
317265

318-
const preload6 = callback => {
319-
let datFile;
320-
let datSize;
266+
const preload6Async = async () => {
321267
const asyncCache6 = { ...conf6 };
268+
let mainData;
269+
270+
try {
271+
const cityData = await readFileBuffer(dataFiles.city6);
272+
if (cityData.size === 0) {
273+
const emptyFileError = new Error('geoip-city6.dat is empty');
274+
emptyFileError.code = 'ENOENT';
275+
throw emptyFileError;
276+
}
277+
278+
mainData = cityData;
279+
} catch (err) {
280+
if (!isExpectedMissingDataError(err)) throw err;
281+
mainData = await readFileBuffer(dataFiles.country6);
282+
asyncCache6.recordSize = RECORD_SIZE6;
283+
}
284+
285+
asyncCache6.mainBuffer = mainData.buffer;
286+
asyncCache6.lastLine = (mainData.size / asyncCache6.recordSize) - 1;
287+
asyncCache6.lastIP = readIp6(asyncCache6.mainBuffer, asyncCache6.lastLine, asyncCache6.recordSize, 1);
288+
asyncCache6.firstIP = readIp6(asyncCache6.mainBuffer, 0, asyncCache6.recordSize, 0);
289+
cache6 = asyncCache6;
290+
};
291+
292+
const preload6 = callback => {
322293
if (typeof callback === 'function') {
323-
void async.series([
324-
cb => {
325-
void async.series([
326-
cb2 => {
327-
open(dataFiles.city6, 'r', (err, file) => {
328-
datFile = file;
329-
cb2(err);
330-
});
331-
},
332-
cb2 => {
333-
fstat(datFile, (err, stats) => {
334-
if (err) {
335-
cb2(err);
336-
return;
337-
}
338-
datSize = stats.size;
339-
cb2();
340-
});
341-
},
342-
], err => {
343-
if (err) {
344-
if (err.code !== 'ENOENT' && err.code !== 'EBADF') {
345-
cb(err);
346-
return;
347-
}
348-
349-
open(dataFiles.country6, 'r', (err, file) => {
350-
if (err) {
351-
cb(err);
352-
} else {
353-
datFile = file;
354-
fstat(datFile, (err, stats) => {
355-
if (err) {
356-
cb(err);
357-
return;
358-
}
359-
datSize = stats.size;
360-
asyncCache6.recordSize = RECORD_SIZE6;
361-
362-
cb();
363-
});
364-
}
365-
});
366-
} else {
367-
cb();
368-
}
369-
});
370-
},
371-
cb => {
372-
asyncCache6.mainBuffer = Buffer.alloc(datSize);
373-
374-
void async.series([
375-
cb2 => {
376-
read(datFile, asyncCache6.mainBuffer, 0, datSize, 0, cb2);
377-
},
378-
cb2 => {
379-
close(datFile, cb2);
380-
},
381-
], err => {
382-
if (!err) {
383-
asyncCache6.lastLine = (datSize / asyncCache6.recordSize) - 1;
384-
asyncCache6.lastIP = readIp6(asyncCache6.mainBuffer, asyncCache6.lastLine, asyncCache6.recordSize, 1);
385-
asyncCache6.firstIP = readIp6(asyncCache6.mainBuffer, 0, asyncCache6.recordSize, 0);
386-
cache6 = asyncCache6;
387-
}
388-
cb(err);
389-
});
390-
},
391-
], callback);
294+
preload6Async().then(() => callback()).catch(callback);
392295
} else {
296+
let datFile;
297+
let datSize;
393298
try {
394299
datFile = openSync(dataFiles.city6, 'r');
395300
datSize = fstatSync(datFile).size;
@@ -421,14 +326,10 @@ const preload6 = callback => {
421326
};
422327

423328
const runAsyncReload = callback => {
424-
void async.series([
425-
cb => {
426-
preload(cb);
427-
},
428-
cb => {
429-
preload6(cb);
430-
},
431-
], callback);
329+
preloadAsync()
330+
.then(() => preload6Async())
331+
.then(() => callback())
332+
.catch(callback);
432333
};
433334

434335
module.exports = {

0 commit comments

Comments
 (0)