@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 +21 -0
- package/iplookup.cjs +145 -0
- package/iplookup.mjs +143 -0
- package/package.json +6 -2
- /package/{ip_lookup.js → iplookup.js} +0 -0
- /package/{ip_lookup.min.js → iplookup.min.js} +0 -0
package/README.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# @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.
|
|
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": "
|
|
27
|
+
"main": "iplookup.cjs",
|
|
28
|
+
"exports": {
|
|
29
|
+
"import": "./iplookup.mjs",
|
|
30
|
+
"require": "./iplookup.cjs"
|
|
31
|
+
}
|
|
28
32
|
}
|
|
File without changes
|
|
File without changes
|