@iplookup/country 0.1.0 → 0.1.1

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/README.md ADDED
@@ -0,0 +1,21 @@
1
+ # @iplookup/country [![NPM version](https://badge.fury.io/js/@iplookup/country)](https://badge.fury.io/js/@iplookup/country)
2
+
3
+ ## Synopsis
4
+
5
+ ```html
6
+ <script src="https://cdn.jsdelivr.net/npm/@iplookup/country/iplookup.min.js"></script>
7
+ <script type="text/javascript">
8
+ var ip = "207.97.227.239"
9
+ var location = await IpLookup(ip)
10
+ console.log(location) // {country: 'FR'}
11
+ </script>
12
+ ```
13
+
14
+ If you need extra information about country, try to use [@iplookup/country-extra](https://github.com/sapics/ip-location-api/tree/main/browser/country-extra).
15
+
16
+
17
+ ## License
18
+
19
+ Since each user download a partial database, we use the CC0 Licensed database [geo-whois-asn-country](https://github.com/sapics/ip-location-db/tree/main/geo-whois-asn-country) for ip to country mapping to avoid license problem.
20
+
21
+ The license for the software itself is published under MIT License by [sapics](https://github.com/sapics).
package/iplookup.cjs ADDED
@@ -0,0 +1,145 @@
1
+ 'use strict';
2
+
3
+ const aton4 = (a) => {
4
+ a = a.split(/\./);
5
+ return (a[0] << 24 | a[1] << 16 | a[2] << 8 | a[3]) >>> 0
6
+ };
7
+
8
+ const aton6Start = (a) => {
9
+ if(a.includes('.')){
10
+ return aton4(a.split(':').pop())
11
+ }
12
+ a = a.split(/:/);
13
+ const l = a.length - 1;
14
+ var i, r = 0n;
15
+ if (l < 7) {
16
+ const omitStart = a.indexOf('');
17
+ if(omitStart < 4){
18
+ const omitted = 8 - a.length, omitEnd = omitStart + omitted;
19
+ for (i = 7; i >= omitStart; i--) {
20
+ a[i] = i > omitEnd ? a[i - omitted] : 0;
21
+ }
22
+ }
23
+ }
24
+ for (i = 0; i < 4; i++) {
25
+ if(a[i]) r += BigInt(parseInt(a[i], 16)) << BigInt(16 * (3 - i));
26
+ }
27
+ return r
28
+ };
29
+
30
+ const getUnderberFill = (num, len) => {
31
+ if(num.length > len) return num
32
+ return '_'.repeat(len - num.length) + num
33
+ };
34
+ const numberToDir = (num) => {
35
+ return getUnderberFill(num.toString(36), 2)
36
+ };
37
+
38
+ //----------------------------
39
+ // COUNTRY: IndexLoop = 10
40
+ //----------------------------
41
+ // IPv4: 1172844 >> 2 = 293211 ips
42
+ // INDEX_FILE_SIZE = (2^IndexLoop)*4 = 4096 bytes
43
+ // COUNTRY_FILE_SIZE = Math.ceil(293211 / IndexSize) * (4 + 4 + 2) = 2870 bytes
44
+ // IPv6: 1914064 >> 3 = 146605 ips
45
+ // INDEX_FILE_SIZE = (2^IndexLoop)*8 = 8192 bytes
46
+ // COUNTRY_FILE_SIZE = Math.ceil(146605 / IndexSize) * (8 + 8 + 2) = 144 * 18 = 2592 bytes
47
+
48
+
49
+ //----------------------------
50
+ // LATITUDE + LONGITUDE: IndexLoop = 11
51
+ //----------------------------
52
+ // IPv4: 6474072 >> 2 = 1618518 ips
53
+ // INDEX_FILE_SIZE = (2^IndexLoop)*4 = 8192 bytes
54
+ // COUNTRY_FILE_SIZE = Math.ceil(1618518 / IndexSize) * (4 + 4 + 4 + 4) = 791 * 16 = 12656 bytes
55
+ // IPv6: 7621144 >> 3 = 952643 ips
56
+ // INDEX_FILE_SIZE = (2^IndexLoop)*8 = 16384 bytes
57
+ // COUNTRY_FILE_SIZE = Math.ceil(952643 / IndexSize) * (8 + 8 + 4 + 4) = 466 * 24 = 11184 bytes
58
+
59
+
60
+ const downloadBuffer = async (url) => {
61
+ return fetch(url, {cache: 'no-cache'}).then(res => res.arrayBuffer())
62
+ };
63
+
64
+ const IndexLoop = 10;
65
+ const IndexSize = Math.pow(2, IndexLoop);
66
+ const IndexLineEnd = IndexSize - 1;
67
+
68
+ //const TOP_URL = 'https://cdn.test.com/data/'
69
+ // const TOP_URL = 'country/'
70
+
71
+ const TOP_URL = "https://cdn.jsdelivr.net/npm/@iplookup/country/";
72
+
73
+ const MAIN_RECORD_SIZE = 2;
74
+
75
+ const Idx = {};
76
+ const Preload = {
77
+ 4: downloadBuffer(TOP_URL + '4.idx').then(buf => {
78
+ return Idx[4] = new Uint32Array(buf)
79
+ }),
80
+ 6: downloadBuffer(TOP_URL + '6.idx').then(buf => {
81
+ return Idx[6] = new BigUint64Array(buf)
82
+ })
83
+ };
84
+
85
+ var browser = async (ipString) => {
86
+ var ip, version, isv4 = true;
87
+ if(ipString.includes(':')) {
88
+ ip = aton6Start(ipString);
89
+ version = ip.constructor === BigInt ? 6 : 4;
90
+ if(version === 6) isv4 = false;
91
+ } else {
92
+ ip = aton4(ipString);
93
+ version = 4;
94
+ }
95
+
96
+ const ipIndexes = Idx[version] || (await Preload[version]);
97
+ if(!(ip >= ipIndexes[0])) return null
98
+ var fline = 0, cline = IndexLineEnd, line;
99
+ for(;;){
100
+ line = (fline + cline) >> 1;
101
+ if(ip < ipIndexes[line]){
102
+ if(cline - fline < 2) return null
103
+ cline = line - 1;
104
+ } else {
105
+ if(fline === line) {
106
+ if(cline > line && ip >= ipIndexes[cline]){
107
+ line = cline;
108
+ }
109
+ break;
110
+ }
111
+ fline = line;
112
+ }
113
+ }
114
+
115
+ const fileName = numberToDir(line);
116
+ const dataBuffer = await downloadBuffer(TOP_URL + version + '/' + fileName);
117
+ const ipSize = (version - 2) * 2;
118
+ const recordSize = MAIN_RECORD_SIZE + ipSize * 2;
119
+ const recordCount = dataBuffer.byteLength / recordSize;
120
+ const startList = isv4 ? new Uint32Array(dataBuffer.slice(0, 4 * recordCount)) : new BigUint64Array(dataBuffer.slice(0, 8 * recordCount));
121
+ fline = 0, cline = recordCount - 1;
122
+ for(;;){
123
+ line = fline + cline >> 1;
124
+ if(ip < startList[line]){
125
+ if(cline - fline < 2) return null
126
+ cline = line - 1;
127
+ } else {
128
+ if(fline === line) {
129
+ if(cline > line && ip >= startList[cline]){
130
+ line = cline;
131
+ }
132
+ break;
133
+ }
134
+ fline = line;
135
+ }
136
+ }
137
+ const endIp = new Uint32Array(dataBuffer.slice((recordCount+line)*ipSize , (recordCount+line+1)*ipSize))[0];
138
+ if(ip >= startList[line] && ip <= endIp){
139
+ const ccCode = new Uint16Array(dataBuffer.slice(recordCount*ipSize*2+line*2, recordCount*ipSize*2+line*2+2))[0];
140
+ return {country: String.fromCharCode(ccCode&255, ccCode>>8)}
141
+ }
142
+ return null
143
+ };
144
+
145
+ module.exports = browser;
package/iplookup.mjs ADDED
@@ -0,0 +1,143 @@
1
+ const aton4 = (a) => {
2
+ a = a.split(/\./);
3
+ return (a[0] << 24 | a[1] << 16 | a[2] << 8 | a[3]) >>> 0
4
+ };
5
+
6
+ const aton6Start = (a) => {
7
+ if(a.includes('.')){
8
+ return aton4(a.split(':').pop())
9
+ }
10
+ a = a.split(/:/);
11
+ const l = a.length - 1;
12
+ var i, r = 0n;
13
+ if (l < 7) {
14
+ const omitStart = a.indexOf('');
15
+ if(omitStart < 4){
16
+ const omitted = 8 - a.length, omitEnd = omitStart + omitted;
17
+ for (i = 7; i >= omitStart; i--) {
18
+ a[i] = i > omitEnd ? a[i - omitted] : 0;
19
+ }
20
+ }
21
+ }
22
+ for (i = 0; i < 4; i++) {
23
+ if(a[i]) r += BigInt(parseInt(a[i], 16)) << BigInt(16 * (3 - i));
24
+ }
25
+ return r
26
+ };
27
+
28
+ const getUnderberFill = (num, len) => {
29
+ if(num.length > len) return num
30
+ return '_'.repeat(len - num.length) + num
31
+ };
32
+ const numberToDir = (num) => {
33
+ return getUnderberFill(num.toString(36), 2)
34
+ };
35
+
36
+ //----------------------------
37
+ // COUNTRY: IndexLoop = 10
38
+ //----------------------------
39
+ // IPv4: 1172844 >> 2 = 293211 ips
40
+ // INDEX_FILE_SIZE = (2^IndexLoop)*4 = 4096 bytes
41
+ // COUNTRY_FILE_SIZE = Math.ceil(293211 / IndexSize) * (4 + 4 + 2) = 2870 bytes
42
+ // IPv6: 1914064 >> 3 = 146605 ips
43
+ // INDEX_FILE_SIZE = (2^IndexLoop)*8 = 8192 bytes
44
+ // COUNTRY_FILE_SIZE = Math.ceil(146605 / IndexSize) * (8 + 8 + 2) = 144 * 18 = 2592 bytes
45
+
46
+
47
+ //----------------------------
48
+ // LATITUDE + LONGITUDE: IndexLoop = 11
49
+ //----------------------------
50
+ // IPv4: 6474072 >> 2 = 1618518 ips
51
+ // INDEX_FILE_SIZE = (2^IndexLoop)*4 = 8192 bytes
52
+ // COUNTRY_FILE_SIZE = Math.ceil(1618518 / IndexSize) * (4 + 4 + 4 + 4) = 791 * 16 = 12656 bytes
53
+ // IPv6: 7621144 >> 3 = 952643 ips
54
+ // INDEX_FILE_SIZE = (2^IndexLoop)*8 = 16384 bytes
55
+ // COUNTRY_FILE_SIZE = Math.ceil(952643 / IndexSize) * (8 + 8 + 4 + 4) = 466 * 24 = 11184 bytes
56
+
57
+
58
+ const downloadBuffer = async (url) => {
59
+ return fetch(url, {cache: 'no-cache'}).then(res => res.arrayBuffer())
60
+ };
61
+
62
+ const IndexLoop = 10;
63
+ const IndexSize = Math.pow(2, IndexLoop);
64
+ const IndexLineEnd = IndexSize - 1;
65
+
66
+ //const TOP_URL = 'https://cdn.test.com/data/'
67
+ // const TOP_URL = 'country/'
68
+
69
+ const TOP_URL = "https://cdn.jsdelivr.net/npm/@iplookup/country/";
70
+
71
+ const MAIN_RECORD_SIZE = 2;
72
+
73
+ const Idx = {};
74
+ const Preload = {
75
+ 4: downloadBuffer(TOP_URL + '4.idx').then(buf => {
76
+ return Idx[4] = new Uint32Array(buf)
77
+ }),
78
+ 6: downloadBuffer(TOP_URL + '6.idx').then(buf => {
79
+ return Idx[6] = new BigUint64Array(buf)
80
+ })
81
+ };
82
+
83
+ var browser = async (ipString) => {
84
+ var ip, version, isv4 = true;
85
+ if(ipString.includes(':')) {
86
+ ip = aton6Start(ipString);
87
+ version = ip.constructor === BigInt ? 6 : 4;
88
+ if(version === 6) isv4 = false;
89
+ } else {
90
+ ip = aton4(ipString);
91
+ version = 4;
92
+ }
93
+
94
+ const ipIndexes = Idx[version] || (await Preload[version]);
95
+ if(!(ip >= ipIndexes[0])) return null
96
+ var fline = 0, cline = IndexLineEnd, line;
97
+ for(;;){
98
+ line = (fline + cline) >> 1;
99
+ if(ip < ipIndexes[line]){
100
+ if(cline - fline < 2) return null
101
+ cline = line - 1;
102
+ } else {
103
+ if(fline === line) {
104
+ if(cline > line && ip >= ipIndexes[cline]){
105
+ line = cline;
106
+ }
107
+ break;
108
+ }
109
+ fline = line;
110
+ }
111
+ }
112
+
113
+ const fileName = numberToDir(line);
114
+ const dataBuffer = await downloadBuffer(TOP_URL + version + '/' + fileName);
115
+ const ipSize = (version - 2) * 2;
116
+ const recordSize = MAIN_RECORD_SIZE + ipSize * 2;
117
+ const recordCount = dataBuffer.byteLength / recordSize;
118
+ const startList = isv4 ? new Uint32Array(dataBuffer.slice(0, 4 * recordCount)) : new BigUint64Array(dataBuffer.slice(0, 8 * recordCount));
119
+ fline = 0, cline = recordCount - 1;
120
+ for(;;){
121
+ line = fline + cline >> 1;
122
+ if(ip < startList[line]){
123
+ if(cline - fline < 2) return null
124
+ cline = line - 1;
125
+ } else {
126
+ if(fline === line) {
127
+ if(cline > line && ip >= startList[cline]){
128
+ line = cline;
129
+ }
130
+ break;
131
+ }
132
+ fline = line;
133
+ }
134
+ }
135
+ const endIp = new Uint32Array(dataBuffer.slice((recordCount+line)*ipSize , (recordCount+line+1)*ipSize))[0];
136
+ if(ip >= startList[line] && ip <= endIp){
137
+ const ccCode = new Uint16Array(dataBuffer.slice(recordCount*ipSize*2+line*2, recordCount*ipSize*2+line*2+2))[0];
138
+ return {country: String.fromCharCode(ccCode&255, ccCode>>8)}
139
+ }
140
+ return null
141
+ };
142
+
143
+ export { browser as default };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@iplookup/country",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Browser api to lookup country from IP address",
5
5
  "keywords": [
6
6
  "location",
@@ -24,5 +24,9 @@
24
24
  "access": "public"
25
25
  },
26
26
  "license": "MIT",
27
- "main": "ip_lookup.min.js"
27
+ "main": "iplookup.cjs",
28
+ "exports": {
29
+ "import": "./iplookup.mjs",
30
+ "require": "./iplookup.cjs"
31
+ }
28
32
  }
File without changes
File without changes