@k03mad/ip2geo 4.2.1 → 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.
@@ -25,6 +25,9 @@ jobs:
25
25
  with:
26
26
  node-version-file: '.nvmrc'
27
27
 
28
+ - name: Install PNPM
29
+ run: npm i pnpm -g
30
+
28
31
  - name: Run setup
29
32
  run: npm run setup
30
33
 
@@ -25,6 +25,9 @@ jobs:
25
25
  with:
26
26
  node-version-file: '.nvmrc'
27
27
 
28
+ - name: Install PNPM
29
+ run: npm i pnpm -g
30
+
28
31
  - name: Run setup
29
32
  run: npm run setup
30
33
 
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
- rps: 3, // useful in Promise.all with IPs array
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.2.1",
3
+ "version": "4.3.0",
4
4
  "description": "GeoIP library",
5
5
  "maintainers": [
6
6
  "Kirill Molchanov <k03.mad@gmail.com"
@@ -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 && npm i",
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
+ });
@@ -30,4 +30,9 @@ describe(testName, () => {
30
30
  ...opts,
31
31
  response: REQUEST_IPV4,
32
32
  }));
33
+
34
+ it('should have 1 correct cache entry', () => {
35
+ assert.equal(opts.cacheMap.size, 1);
36
+ assert.deepEqual(opts.cacheMap.get(REQUEST_IPV4.ip), REQUEST_IPV4);
37
+ });
33
38
  });
@@ -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 response = {ip: '10.10.10.10'};
15
-
16
- Object.keys(REQUEST_IPV4).forEach(elem => {
17
- if (elem !== 'ip') {
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
- it(`should return empty response for IP: "${response.ip}"`, async () => {
30
- const data = await ip2geo(response.ip, opts);
31
- assert.deepEqual(data, response);
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
  });
@@ -20,7 +20,6 @@ describe(testName, () => {
20
20
 
21
21
  it(`should return correct response for IP: "${REQUEST_IPV6.ip}"`, async () => {
22
22
  const data = await ip2geo(REQUEST_IPV6.ip, opts);
23
-
24
23
  assert.deepEqual(data, REQUEST_IPV6);
25
24
  });
26
25
 
File without changes