@k03mad/ip2geo 4.2.0 → 4.3.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/.github/workflows/lint.yml +3 -0
- package/.github/workflows/test.yml +3 -0
- package/README.md +2 -1
- package/app/lib/ip2geo.js +17 -0
- package/package.json +3 -3
- package/tests/{opts-assigned-cache-map.js → cache-map-entries.js} +6 -8
- package/tests/cache-map-max-entries.js +56 -0
- package/tests/{opts-assigned-multi-requests.js → ip-multi-requests-cache.js} +5 -0
- package/tests/{opts-assigned-no-data.js → ip-no-data.js} +15 -10
- package/tests/{opts-assigned-ipv6.js → ip-v6.js} +0 -1
- /package/tests/{opts-assigned-all.js → cache-file-modify.js} +0 -0
- /package/tests/{opts-assigned-subfolder.js → cache-file-subfolder.js} +0 -0
- /package/tests/{opts-default.js → cache-map-default.js} +0 -0
package/README.md
CHANGED
|
@@ -45,7 +45,8 @@ const info = await ip2geo('1.1.1.1', {
|
|
|
45
45
|
cacheFileSeparator: ';;',
|
|
46
46
|
cacheFileNewline: '\n',
|
|
47
47
|
cacheMap: new Map(),
|
|
48
|
-
|
|
48
|
+
cacheMapMaxEntries: 1000, // store last N requests
|
|
49
|
+
rps: 3, // API RPS, useful in Promise.all with IPs array
|
|
49
50
|
});
|
|
50
51
|
|
|
51
52
|
// info {
|
package/app/lib/ip2geo.js
CHANGED
|
@@ -26,6 +26,7 @@ const DEFAULT_CACHE_FILE_DIR = path.join(os.tmpdir(), '.ip2geo');
|
|
|
26
26
|
const DEFAULT_CACHE_FILE_NAME = 'ips.log';
|
|
27
27
|
const DEFAULT_CACHE_FILE_SEPARATOR = ';;';
|
|
28
28
|
const DEFAULT_CACHE_FILE_NEWLINE = '\n';
|
|
29
|
+
const DEFAULT_CACHE_MAP_MAX_ENTRIES = 1000;
|
|
29
30
|
const DEFAULT_RPS = 3;
|
|
30
31
|
|
|
31
32
|
export const cacheStorage = new Map();
|
|
@@ -112,6 +113,7 @@ const writeToFsCache = async (ip, data, cacheDir, cacheFileName, cacheFileSepara
|
|
|
112
113
|
* @param {string} [opts.cacheFileSeparator]
|
|
113
114
|
* @param {string} [opts.cacheFileNewline]
|
|
114
115
|
* @param {Map} [opts.cacheMap]
|
|
116
|
+
* @param {number} opts.cacheMapMaxEntries
|
|
115
117
|
* @param {number} opts.rps
|
|
116
118
|
* @returns {Promise<GeoIpOutput>}
|
|
117
119
|
*/
|
|
@@ -120,6 +122,7 @@ export const ip2geo = async (ip = '', {
|
|
|
120
122
|
cacheFileName = DEFAULT_CACHE_FILE_NAME,
|
|
121
123
|
cacheFileSeparator = DEFAULT_CACHE_FILE_SEPARATOR,
|
|
122
124
|
cacheFileNewline = DEFAULT_CACHE_FILE_NEWLINE,
|
|
125
|
+
cacheMapMaxEntries = DEFAULT_CACHE_MAP_MAX_ENTRIES,
|
|
123
126
|
cacheMap = cacheStorage,
|
|
124
127
|
rps = DEFAULT_RPS,
|
|
125
128
|
} = {}) => {
|
|
@@ -179,5 +182,19 @@ export const ip2geo = async (ip = '', {
|
|
|
179
182
|
);
|
|
180
183
|
|
|
181
184
|
debug('set to fs cache: %o', outputData);
|
|
185
|
+
|
|
186
|
+
if (cacheMap.size > cacheMapMaxEntries) {
|
|
187
|
+
debug('remove from map cache by limit: size %o, limit: %o', cacheMap.size, cacheMapMaxEntries);
|
|
188
|
+
|
|
189
|
+
for (const [key] of cacheMap) {
|
|
190
|
+
debug('remove from map cache by limit: key %o', key);
|
|
191
|
+
cacheMap.delete(key);
|
|
192
|
+
|
|
193
|
+
if (cacheMap.size <= cacheMapMaxEntries) {
|
|
194
|
+
break;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
182
199
|
return outputData;
|
|
183
200
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@k03mad/ip2geo",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.3.0",
|
|
4
4
|
"description": "GeoIP library",
|
|
5
5
|
"maintainers": [
|
|
6
6
|
"Kirill Molchanov <k03.mad@gmail.com"
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"node": ">=20"
|
|
20
20
|
},
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@k03mad/request": "5.
|
|
22
|
+
"@k03mad/request": "5.6.0",
|
|
23
23
|
"@k03mad/simple-log": "2.1.0",
|
|
24
24
|
"chalk": "5.3.0",
|
|
25
25
|
"debug": "4.3.4"
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"clean": "npm run clean:modules && npm run clean:eslint:cache",
|
|
38
38
|
"clean:modules": "rm -rf ./node_modules || true",
|
|
39
39
|
"clean:eslint:cache": "rm -rf .eslintcache || true",
|
|
40
|
-
"setup": "npm run clean &&
|
|
40
|
+
"setup": "npm run clean && pnpm i",
|
|
41
41
|
"prepare": "husky install || true"
|
|
42
42
|
}
|
|
43
43
|
}
|
|
@@ -11,11 +11,9 @@ import {removeCacheFolder} from './shared/fs.js';
|
|
|
11
11
|
const testName = getCurrentFilename(import.meta.url);
|
|
12
12
|
|
|
13
13
|
describe(testName, () => {
|
|
14
|
-
const cacheMap = new Map();
|
|
15
|
-
|
|
16
14
|
const opts = {
|
|
17
15
|
cacheDir: getTestFolder(testName),
|
|
18
|
-
cacheMap,
|
|
16
|
+
cacheMap: new Map(),
|
|
19
17
|
};
|
|
20
18
|
|
|
21
19
|
it('should remove fs cache dir if exist', () => removeCacheFolder(opts.cacheDir));
|
|
@@ -26,8 +24,8 @@ describe(testName, () => {
|
|
|
26
24
|
});
|
|
27
25
|
|
|
28
26
|
it('should have 1 correct cache entry', () => {
|
|
29
|
-
assert.equal(cacheMap.size, 1);
|
|
30
|
-
assert.deepEqual(cacheMap.get(REQUEST_IPV4.ip), REQUEST_IPV4);
|
|
27
|
+
assert.equal(opts.cacheMap.size, 1);
|
|
28
|
+
assert.deepEqual(opts.cacheMap.get(REQUEST_IPV4.ip), REQUEST_IPV4);
|
|
31
29
|
});
|
|
32
30
|
|
|
33
31
|
it(`should return correct response for IP: "${REQUEST_IPV6.ip}"`, async () => {
|
|
@@ -36,8 +34,8 @@ describe(testName, () => {
|
|
|
36
34
|
});
|
|
37
35
|
|
|
38
36
|
it('should have 2 correct cache entries', () => {
|
|
39
|
-
assert.equal(cacheMap.size, 2);
|
|
40
|
-
assert.deepEqual(cacheMap.get(REQUEST_IPV4.ip), REQUEST_IPV4);
|
|
41
|
-
assert.deepEqual(cacheMap.get(REQUEST_IPV6.ip), REQUEST_IPV6);
|
|
37
|
+
assert.equal(opts.cacheMap.size, 2);
|
|
38
|
+
assert.deepEqual(opts.cacheMap.get(REQUEST_IPV4.ip), REQUEST_IPV4);
|
|
39
|
+
assert.deepEqual(opts.cacheMap.get(REQUEST_IPV6.ip), REQUEST_IPV6);
|
|
42
40
|
});
|
|
43
41
|
});
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
|
|
3
|
+
import {describe, it} from 'mocha';
|
|
4
|
+
|
|
5
|
+
import {ip2geo} from '../app/index.js';
|
|
6
|
+
|
|
7
|
+
import {getCurrentFilename, getTestFolder} from './helpers/path.js';
|
|
8
|
+
import {removeCacheFolder} from './shared/fs.js';
|
|
9
|
+
|
|
10
|
+
const testName = getCurrentFilename(import.meta.url);
|
|
11
|
+
|
|
12
|
+
describe(testName, () => {
|
|
13
|
+
const firstReqIps = [
|
|
14
|
+
'10.10.10.10',
|
|
15
|
+
'20.20.20.20',
|
|
16
|
+
'30.30.30.30',
|
|
17
|
+
'40.40.40.40',
|
|
18
|
+
'50.50.50.50',
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
const secondReqIps = ['60.60.60.60'];
|
|
22
|
+
|
|
23
|
+
const opts = {
|
|
24
|
+
cacheDir: getTestFolder(testName),
|
|
25
|
+
cacheMap: new Map(),
|
|
26
|
+
cacheMapMaxEntries: 2,
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
it('should remove fs cache dir if exist', () => removeCacheFolder(opts.cacheDir));
|
|
30
|
+
|
|
31
|
+
firstReqIps.forEach(ip => {
|
|
32
|
+
it(`should request geo for IP: "${ip}"`, async () => {
|
|
33
|
+
await ip2geo(ip, opts);
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it(`should have ${opts.cacheMapMaxEntries} correct cache entries`, () => {
|
|
38
|
+
assert.equal(opts.cacheMap.size, opts.cacheMapMaxEntries);
|
|
39
|
+
assert.deepEqual([...opts.cacheMap.keys()], firstReqIps.slice(-opts.cacheMapMaxEntries));
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
secondReqIps.forEach(ip => {
|
|
43
|
+
it(`should request geo for IP: "${ip}"`, async () => {
|
|
44
|
+
await ip2geo(ip, opts);
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it(`should have ${opts.cacheMapMaxEntries} correct cache entries`, () => {
|
|
49
|
+
assert.equal(opts.cacheMap.size, opts.cacheMapMaxEntries);
|
|
50
|
+
|
|
51
|
+
assert.deepEqual(
|
|
52
|
+
[...opts.cacheMap.keys()],
|
|
53
|
+
[firstReqIps, secondReqIps].flat().slice(-opts.cacheMapMaxEntries),
|
|
54
|
+
);
|
|
55
|
+
});
|
|
56
|
+
});
|
|
@@ -11,13 +11,10 @@ import {removeCacheFolder} from './shared/fs.js';
|
|
|
11
11
|
const testName = getCurrentFilename(import.meta.url);
|
|
12
12
|
|
|
13
13
|
describe(testName, () => {
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
response[elem] = undefined;
|
|
19
|
-
}
|
|
20
|
-
});
|
|
14
|
+
const responses = [
|
|
15
|
+
{ip: '10.10.10.10'},
|
|
16
|
+
{ip: 'test'},
|
|
17
|
+
];
|
|
21
18
|
|
|
22
19
|
const opts = {
|
|
23
20
|
cacheDir: getTestFolder(testName),
|
|
@@ -26,8 +23,16 @@ describe(testName, () => {
|
|
|
26
23
|
|
|
27
24
|
it('should remove fs cache dir if exist', () => removeCacheFolder(opts.cacheDir));
|
|
28
25
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
26
|
+
responses.forEach(response => {
|
|
27
|
+
Object.keys(REQUEST_IPV4).forEach(elem => {
|
|
28
|
+
if (elem !== 'ip') {
|
|
29
|
+
response[elem] = undefined;
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it(`should return empty response for IP: "${response.ip}"`, async () => {
|
|
34
|
+
const data = await ip2geo(response.ip, opts);
|
|
35
|
+
assert.deepEqual(data, response);
|
|
36
|
+
});
|
|
32
37
|
});
|
|
33
38
|
});
|
|
File without changes
|
|
File without changes
|
|
File without changes
|