@prosopo/ipinfo 0.2.40 → 0.3.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/.turbo/turbo-build$colon$cjs.log +12 -10
- package/.turbo/turbo-build$colon$tsc.log +10 -10
- package/.turbo/turbo-build.log +14 -11
- package/CHANGELOG.md +28 -0
- package/dist/IpInfoService.d.ts +8 -1
- package/dist/IpInfoService.d.ts.map +1 -1
- package/dist/IpInfoService.js +77 -84
- package/dist/IpInfoService.js.map +1 -1
- package/dist/_virtual/_rolldown/runtime.js +3 -0
- package/dist/backends/ipapi.d.ts +6 -0
- package/dist/backends/ipapi.d.ts.map +1 -1
- package/dist/backends/ipapi.js +117 -104
- package/dist/backends/ipapi.js.map +1 -1
- package/dist/backends/maxmind.d.ts +4 -0
- package/dist/backends/maxmind.d.ts.map +1 -1
- package/dist/backends/maxmind.js +126 -145
- package/dist/backends/maxmind.js.map +1 -1
- package/dist/cjs/IpInfoService.cjs +80 -86
- package/dist/cjs/backends/ipapi.cjs +118 -104
- package/dist/cjs/backends/maxmind.cjs +124 -165
- package/dist/cjs/index.cjs +7 -3
- package/dist/index.d.ts +6 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -4
- package/dist/index.js.map +1 -1
- package/dist/tests/ipInfoService.unit.test.d.ts +2 -0
- package/dist/tests/ipInfoService.unit.test.d.ts.map +1 -0
- package/dist/tests/ipInfoService.unit.test.js +244 -0
- package/dist/tests/ipInfoService.unit.test.js.map +1 -0
- package/dist/tests/ipapi.unit.test.d.ts +2 -0
- package/dist/tests/ipapi.unit.test.d.ts.map +1 -0
- package/dist/tests/ipapi.unit.test.js +337 -0
- package/dist/tests/ipapi.unit.test.js.map +1 -0
- package/dist/tests/ipinfo.test-d.d.ts +2 -0
- package/dist/tests/ipinfo.test-d.d.ts.map +1 -0
- package/dist/tests/ipinfo.test-d.js +97 -0
- package/dist/tests/ipinfo.test-d.js.map +1 -0
- package/dist/tests/maxmind.unit.test.d.ts +2 -0
- package/dist/tests/maxmind.unit.test.d.ts.map +1 -0
- package/dist/tests/maxmind.unit.test.js +391 -0
- package/dist/tests/maxmind.unit.test.js.map +1 -0
- package/package.json +10 -8
- package/src/IpInfoService.ts +19 -2
- package/src/backends/ipapi.ts +57 -10
- package/src/backends/maxmind.ts +22 -4
- package/src/index.ts +9 -1
- package/src/tests/ipInfoService.unit.test.ts +422 -0
- package/src/tests/ipapi.unit.test.ts +509 -0
- package/src/tests/ipinfo.test-d.ts +189 -0
- package/src/tests/maxmind.unit.test.ts +562 -0
- package/tsconfig.tsbuildinfo +1 -1
- package/vite.test.config.ts +18 -0
|
@@ -1,105 +1,119 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Parse an upstream abuser score, which arrives as a string like "0.0012 (Low)".
|
|
3
|
+
*
|
|
4
|
+
* The field is declared required by the response type but is not guaranteed by
|
|
5
|
+
* the wire: it comes from `response.json()`, which is cast, not validated. A
|
|
6
|
+
* missing value used to throw, and the throw was caught far above as a generic
|
|
7
|
+
* "Network or parsing error" — discarding an otherwise complete and successful
|
|
8
|
+
* lookup over one absent score.
|
|
9
|
+
*
|
|
10
|
+
* Returns `undefined` — not 0 — when the field is absent or unparseable. The
|
|
11
|
+
* scale is 0..1 with 0 meaning "clean" (see `abuserScoreThreshold`, declared
|
|
12
|
+
* `{min: 0, max: 1}`), so a literal 0 would assert cleanliness we have not
|
|
13
|
+
* established. `undefined` says "unknown" instead, and the one consumer
|
|
14
|
+
* (checkTrafficFilter) already resolves it with `?? 0`, so the effective
|
|
15
|
+
* blocking behaviour is unchanged while the distinction stays available.
|
|
16
|
+
*
|
|
17
|
+
* Fail-closed alternatives were rejected: 1 turns any upstream formatting
|
|
18
|
+
* change into a silent max-severity block indistinguishable from a genuine
|
|
19
|
+
* 1.0, and throwing reintroduces exactly the bug above — a cosmetic sub-field
|
|
20
|
+
* collapsing a good lookup into a total failure.
|
|
21
|
+
*/
|
|
22
|
+
var parseAbuserScore = (score) => {
|
|
23
|
+
const head = score?.split(" ")[0];
|
|
24
|
+
if (!head) return;
|
|
25
|
+
const parsed = Number.parseFloat(head);
|
|
26
|
+
return Number.isNaN(parsed) ? void 0 : parsed;
|
|
27
|
+
};
|
|
28
|
+
var IpapiBackend = class {
|
|
29
|
+
constructor(config) {
|
|
30
|
+
this.config = config;
|
|
31
|
+
}
|
|
32
|
+
isAvailable() {
|
|
33
|
+
return Boolean(this.config.baseUrl);
|
|
34
|
+
}
|
|
35
|
+
get timeoutMs() {
|
|
36
|
+
return this.config.timeoutMs ?? 700;
|
|
37
|
+
}
|
|
38
|
+
async lookup(ip) {
|
|
39
|
+
try {
|
|
40
|
+
if (!ip || typeof ip !== "string") return {
|
|
41
|
+
isValid: false,
|
|
42
|
+
error: "Invalid IP address provided",
|
|
43
|
+
ip: ip || "undefined"
|
|
44
|
+
};
|
|
45
|
+
const body = { q: ip };
|
|
46
|
+
if (this.config.apiKey) body.key = this.config.apiKey;
|
|
47
|
+
const controller = new AbortController();
|
|
48
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeoutMs);
|
|
49
|
+
try {
|
|
50
|
+
const response = await (this.config.fetch ?? globalThis.fetch)(this.config.baseUrl, {
|
|
51
|
+
method: "POST",
|
|
52
|
+
headers: {
|
|
53
|
+
"Content-Type": "application/json",
|
|
54
|
+
Accept: "application/json"
|
|
55
|
+
},
|
|
56
|
+
body: JSON.stringify(body),
|
|
57
|
+
signal: controller.signal
|
|
58
|
+
});
|
|
59
|
+
clearTimeout(timeoutId);
|
|
60
|
+
if (!response.ok) return {
|
|
61
|
+
isValid: false,
|
|
62
|
+
error: `API request failed with status ${response.status}: ${response.statusText}`,
|
|
63
|
+
ip
|
|
64
|
+
};
|
|
65
|
+
const data = await response.json();
|
|
66
|
+
if (data.is_bogon) return {
|
|
67
|
+
isValid: false,
|
|
68
|
+
error: "IP address is bogon (non-routable)",
|
|
69
|
+
ip
|
|
70
|
+
};
|
|
71
|
+
return {
|
|
72
|
+
ip: data.ip,
|
|
73
|
+
isValid: true,
|
|
74
|
+
isVPN: data.is_vpn,
|
|
75
|
+
isTor: data.is_tor,
|
|
76
|
+
isProxy: data.is_proxy,
|
|
77
|
+
isDatacenter: data.is_datacenter,
|
|
78
|
+
isAbuser: data.is_abuser,
|
|
79
|
+
isMobile: data.is_mobile,
|
|
80
|
+
isSatellite: data.is_satellite,
|
|
81
|
+
isCrawler: data.is_crawler,
|
|
82
|
+
providerName: data.company?.name || data.datacenter?.datacenter,
|
|
83
|
+
providerType: data.company?.type || data.asn?.type,
|
|
84
|
+
asnNumber: data.asn?.asn,
|
|
85
|
+
asnOrganization: data.asn?.org,
|
|
86
|
+
datacenterName: data.datacenter?.datacenter,
|
|
87
|
+
country: data.location?.country,
|
|
88
|
+
countryCode: data.location?.country_code,
|
|
89
|
+
region: data.location?.state,
|
|
90
|
+
city: data.location?.city,
|
|
91
|
+
latitude: data.location?.latitude,
|
|
92
|
+
longitude: data.location?.longitude,
|
|
93
|
+
timezone: data.location?.timezone,
|
|
94
|
+
vpnService: data.vpn?.service,
|
|
95
|
+
vpnType: data.vpn?.type,
|
|
96
|
+
abuserScore: parseAbuserScore(data.asn?.abuser_score),
|
|
97
|
+
companyAbuserScore: parseAbuserScore(data.company?.abuser_score)
|
|
98
|
+
};
|
|
99
|
+
} catch (fetchError) {
|
|
100
|
+
clearTimeout(timeoutId);
|
|
101
|
+
if (fetchError instanceof Error && fetchError.name === "AbortError") return {
|
|
102
|
+
isValid: false,
|
|
103
|
+
error: `Request timed out after ${this.timeoutMs}ms`,
|
|
104
|
+
ip
|
|
105
|
+
};
|
|
106
|
+
throw fetchError;
|
|
107
|
+
}
|
|
108
|
+
} catch (error) {
|
|
109
|
+
return {
|
|
110
|
+
isValid: false,
|
|
111
|
+
error: `Network or parsing error: ${error instanceof Error ? error.message : String(error)}`,
|
|
112
|
+
ip
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
//#endregion
|
|
105
118
|
exports.IpapiBackend = IpapiBackend;
|
|
119
|
+
exports.parseAbuserScore = parseAbuserScore;
|
|
@@ -1,169 +1,128 @@
|
|
|
1
|
-
|
|
2
|
-
var
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
var
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
1
|
+
//#region src/backends/maxmind.ts
|
|
2
|
+
var openReaderFromFile = async (dbPath) => {
|
|
3
|
+
const { Reader } = await import("@maxmind/geoip2-node");
|
|
4
|
+
return Reader.open(dbPath);
|
|
5
|
+
};
|
|
6
|
+
var MaxMindBackend = class {
|
|
7
|
+
constructor(config) {
|
|
8
|
+
this.cityReader = null;
|
|
9
|
+
this.asnReader = null;
|
|
10
|
+
this.config = config;
|
|
11
|
+
}
|
|
12
|
+
async initialize() {
|
|
13
|
+
const openReader = this.config.openReader ?? openReaderFromFile;
|
|
14
|
+
if (this.config.cityDbPath) try {
|
|
15
|
+
this.cityReader = await openReader(this.config.cityDbPath);
|
|
16
|
+
this.config.logger?.info(() => ({
|
|
17
|
+
msg: "MaxMind City reader initialized",
|
|
18
|
+
data: { dbPath: this.config.cityDbPath }
|
|
19
|
+
}));
|
|
20
|
+
} catch (error) {
|
|
21
|
+
this.config.logger?.warn(() => ({
|
|
22
|
+
msg: "Failed to initialize MaxMind City reader",
|
|
23
|
+
err: error,
|
|
24
|
+
data: { dbPath: this.config.cityDbPath }
|
|
25
|
+
}));
|
|
26
|
+
}
|
|
27
|
+
if (this.config.asnDbPath) try {
|
|
28
|
+
this.asnReader = await openReader(this.config.asnDbPath);
|
|
29
|
+
this.config.logger?.info(() => ({
|
|
30
|
+
msg: "MaxMind ASN reader initialized",
|
|
31
|
+
data: { dbPath: this.config.asnDbPath }
|
|
32
|
+
}));
|
|
33
|
+
} catch (error) {
|
|
34
|
+
this.config.logger?.warn(() => ({
|
|
35
|
+
msg: "Failed to initialize MaxMind ASN reader",
|
|
36
|
+
err: error,
|
|
37
|
+
data: { dbPath: this.config.asnDbPath }
|
|
38
|
+
}));
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
isAvailable() {
|
|
42
|
+
return this.cityReader !== null || this.asnReader !== null;
|
|
43
|
+
}
|
|
44
|
+
async lookup(ip) {
|
|
45
|
+
if (!this.isAvailable()) return {
|
|
46
|
+
isValid: false,
|
|
47
|
+
error: "MaxMind readers not initialized",
|
|
48
|
+
ip
|
|
49
|
+
};
|
|
50
|
+
try {
|
|
51
|
+
let cityData;
|
|
52
|
+
let asnData;
|
|
53
|
+
if (this.cityReader) try {
|
|
54
|
+
cityData = this.cityReader.city(ip);
|
|
55
|
+
} catch (error) {
|
|
56
|
+
this.config.logger?.debug(() => ({
|
|
57
|
+
msg: "MaxMind City lookup failed",
|
|
58
|
+
data: { ip },
|
|
59
|
+
err: error
|
|
60
|
+
}));
|
|
61
|
+
}
|
|
62
|
+
if (this.asnReader) try {
|
|
63
|
+
asnData = this.asnReader.asn(ip);
|
|
64
|
+
} catch (error) {
|
|
65
|
+
this.config.logger?.debug(() => ({
|
|
66
|
+
msg: "MaxMind ASN lookup failed",
|
|
67
|
+
data: { ip },
|
|
68
|
+
err: error
|
|
69
|
+
}));
|
|
70
|
+
}
|
|
71
|
+
if (!cityData && !asnData) return {
|
|
72
|
+
isValid: false,
|
|
73
|
+
error: "No MaxMind data available for IP",
|
|
74
|
+
ip
|
|
75
|
+
};
|
|
76
|
+
return {
|
|
77
|
+
ip,
|
|
78
|
+
isValid: true,
|
|
79
|
+
isVPN: cityData?.traits?.isAnonymousVpn ?? false,
|
|
80
|
+
isTor: cityData?.traits?.isTorExitNode ?? false,
|
|
81
|
+
isProxy: (cityData?.traits?.isPublicProxy ?? false) || (cityData?.traits?.isResidentialProxy ?? false),
|
|
82
|
+
isDatacenter: cityData?.traits?.isHostingProvider ?? false,
|
|
83
|
+
isAbuser: false,
|
|
84
|
+
isMobile: false,
|
|
85
|
+
isSatellite: cityData?.traits?.isSatelliteProvider ?? false,
|
|
86
|
+
isCrawler: false,
|
|
87
|
+
country: cityData?.country?.names?.en,
|
|
88
|
+
countryCode: cityData?.country?.isoCode,
|
|
89
|
+
region: cityData?.subdivisions?.[0]?.names?.en,
|
|
90
|
+
city: cityData?.city?.names?.en,
|
|
91
|
+
latitude: cityData?.location?.latitude,
|
|
92
|
+
longitude: cityData?.location?.longitude,
|
|
93
|
+
timezone: cityData?.location?.timeZone,
|
|
94
|
+
asnNumber: cityData?.traits?.autonomousSystemNumber ?? asnData?.autonomousSystemNumber,
|
|
95
|
+
asnOrganization: cityData?.traits?.autonomousSystemOrganization ?? asnData?.autonomousSystemOrganization,
|
|
96
|
+
providerName: cityData?.traits?.autonomousSystemOrganization ?? asnData?.autonomousSystemOrganization,
|
|
97
|
+
providerType: mapUserType(cityData?.traits?.userType)
|
|
98
|
+
};
|
|
99
|
+
} catch (error) {
|
|
100
|
+
return {
|
|
101
|
+
isValid: false,
|
|
102
|
+
error: `MaxMind lookup error: ${error instanceof Error ? error.message : String(error)}`,
|
|
103
|
+
ip
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
}
|
|
15
107
|
};
|
|
16
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
17
|
-
// If the importer is in node compatibility mode or this is not an ESM
|
|
18
|
-
// file that has been converted to a CommonJS file using a Babel-
|
|
19
|
-
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
20
|
-
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
21
|
-
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
22
|
-
mod
|
|
23
|
-
));
|
|
24
|
-
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
25
|
-
class MaxMindBackend {
|
|
26
|
-
constructor(config) {
|
|
27
|
-
this.cityReader = null;
|
|
28
|
-
this.asnReader = null;
|
|
29
|
-
this.config = config;
|
|
30
|
-
}
|
|
31
|
-
async initialize() {
|
|
32
|
-
const { Reader } = await import("@maxmind/geoip2-node");
|
|
33
|
-
if (this.config.cityDbPath) {
|
|
34
|
-
try {
|
|
35
|
-
this.cityReader = await Reader.open(this.config.cityDbPath);
|
|
36
|
-
this.config.logger?.info(() => ({
|
|
37
|
-
msg: "MaxMind City reader initialized",
|
|
38
|
-
data: { dbPath: this.config.cityDbPath }
|
|
39
|
-
}));
|
|
40
|
-
} catch (error) {
|
|
41
|
-
this.config.logger?.warn(() => ({
|
|
42
|
-
msg: "Failed to initialize MaxMind City reader",
|
|
43
|
-
err: error,
|
|
44
|
-
data: { dbPath: this.config.cityDbPath }
|
|
45
|
-
}));
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
if (this.config.asnDbPath) {
|
|
49
|
-
try {
|
|
50
|
-
this.asnReader = await Reader.open(this.config.asnDbPath);
|
|
51
|
-
this.config.logger?.info(() => ({
|
|
52
|
-
msg: "MaxMind ASN reader initialized",
|
|
53
|
-
data: { dbPath: this.config.asnDbPath }
|
|
54
|
-
}));
|
|
55
|
-
} catch (error) {
|
|
56
|
-
this.config.logger?.warn(() => ({
|
|
57
|
-
msg: "Failed to initialize MaxMind ASN reader",
|
|
58
|
-
err: error,
|
|
59
|
-
data: { dbPath: this.config.asnDbPath }
|
|
60
|
-
}));
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
isAvailable() {
|
|
65
|
-
return this.cityReader !== null || this.asnReader !== null;
|
|
66
|
-
}
|
|
67
|
-
async lookup(ip) {
|
|
68
|
-
if (!this.isAvailable()) {
|
|
69
|
-
return {
|
|
70
|
-
isValid: false,
|
|
71
|
-
error: "MaxMind readers not initialized",
|
|
72
|
-
ip
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
|
-
try {
|
|
76
|
-
let cityData;
|
|
77
|
-
let asnData;
|
|
78
|
-
if (this.cityReader) {
|
|
79
|
-
try {
|
|
80
|
-
cityData = this.cityReader.city(ip);
|
|
81
|
-
} catch (error) {
|
|
82
|
-
this.config.logger?.debug(() => ({
|
|
83
|
-
msg: "MaxMind City lookup failed",
|
|
84
|
-
data: { ip },
|
|
85
|
-
err: error
|
|
86
|
-
}));
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
if (this.asnReader) {
|
|
90
|
-
try {
|
|
91
|
-
asnData = this.asnReader.asn(ip);
|
|
92
|
-
} catch (error) {
|
|
93
|
-
this.config.logger?.debug(() => ({
|
|
94
|
-
msg: "MaxMind ASN lookup failed",
|
|
95
|
-
data: { ip },
|
|
96
|
-
err: error
|
|
97
|
-
}));
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
if (!cityData && !asnData) {
|
|
101
|
-
return {
|
|
102
|
-
isValid: false,
|
|
103
|
-
error: "No MaxMind data available for IP",
|
|
104
|
-
ip
|
|
105
|
-
};
|
|
106
|
-
}
|
|
107
|
-
const result = {
|
|
108
|
-
ip,
|
|
109
|
-
isValid: true,
|
|
110
|
-
// Threat indicators - GeoLite2 free DBs do not populate these
|
|
111
|
-
isVPN: cityData?.traits?.isAnonymousVpn ?? false,
|
|
112
|
-
isTor: cityData?.traits?.isTorExitNode ?? false,
|
|
113
|
-
isProxy: (cityData?.traits?.isPublicProxy ?? false) || (cityData?.traits?.isResidentialProxy ?? false),
|
|
114
|
-
isDatacenter: cityData?.traits?.isHostingProvider ?? false,
|
|
115
|
-
isAbuser: false,
|
|
116
|
-
isMobile: false,
|
|
117
|
-
isSatellite: cityData?.traits?.isSatelliteProvider ?? false,
|
|
118
|
-
isCrawler: false,
|
|
119
|
-
// Geolocation from City DB
|
|
120
|
-
country: cityData?.country?.names?.en,
|
|
121
|
-
countryCode: cityData?.country?.isoCode,
|
|
122
|
-
region: cityData?.subdivisions?.[0]?.names?.en,
|
|
123
|
-
city: cityData?.city?.names?.en,
|
|
124
|
-
latitude: cityData?.location?.latitude,
|
|
125
|
-
longitude: cityData?.location?.longitude,
|
|
126
|
-
timezone: cityData?.location?.timeZone,
|
|
127
|
-
// ASN info - prefer City DB traits, fall back to ASN DB
|
|
128
|
-
asnNumber: cityData?.traits?.autonomousSystemNumber ?? asnData?.autonomousSystemNumber,
|
|
129
|
-
asnOrganization: cityData?.traits?.autonomousSystemOrganization ?? asnData?.autonomousSystemOrganization,
|
|
130
|
-
// Provider info from ASN
|
|
131
|
-
providerName: cityData?.traits?.autonomousSystemOrganization ?? asnData?.autonomousSystemOrganization,
|
|
132
|
-
providerType: mapUserType(cityData?.traits?.userType)
|
|
133
|
-
};
|
|
134
|
-
return result;
|
|
135
|
-
} catch (error) {
|
|
136
|
-
return {
|
|
137
|
-
isValid: false,
|
|
138
|
-
error: `MaxMind lookup error: ${error instanceof Error ? error.message : String(error)}`,
|
|
139
|
-
ip
|
|
140
|
-
};
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
108
|
function mapUserType(userType) {
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
case "traveler":
|
|
163
|
-
case "router":
|
|
164
|
-
return "isp";
|
|
165
|
-
default:
|
|
166
|
-
return void 0;
|
|
167
|
-
}
|
|
109
|
+
switch (userType) {
|
|
110
|
+
case "hosting":
|
|
111
|
+
case "content_delivery_network": return "hosting";
|
|
112
|
+
case "college":
|
|
113
|
+
case "school":
|
|
114
|
+
case "library": return "education";
|
|
115
|
+
case "government":
|
|
116
|
+
case "military": return "government";
|
|
117
|
+
case "business": return "business";
|
|
118
|
+
case "residential":
|
|
119
|
+
case "cellular":
|
|
120
|
+
case "dialup":
|
|
121
|
+
case "cafe":
|
|
122
|
+
case "traveler":
|
|
123
|
+
case "router": return "isp";
|
|
124
|
+
default: return;
|
|
125
|
+
}
|
|
168
126
|
}
|
|
127
|
+
//#endregion
|
|
169
128
|
exports.MaxMindBackend = MaxMindBackend;
|
package/dist/cjs/index.cjs
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
-
const
|
|
4
|
-
|
|
2
|
+
const require_ipapi = require("./backends/ipapi.cjs");
|
|
3
|
+
const require_maxmind = require("./backends/maxmind.cjs");
|
|
4
|
+
const require_IpInfoService = require("./IpInfoService.cjs");
|
|
5
|
+
exports.IpInfoService = require_IpInfoService.IpInfoService;
|
|
6
|
+
exports.IpapiBackend = require_ipapi.IpapiBackend;
|
|
7
|
+
exports.MaxMindBackend = require_maxmind.MaxMindBackend;
|
|
8
|
+
exports.isNonRoutable = require_IpInfoService.isNonRoutable;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
-
export { IpInfoService } from "./IpInfoService.js";
|
|
1
|
+
export { IpInfoService, isNonRoutable } from "./IpInfoService.js";
|
|
2
|
+
export type { IpInfoBackends } from "./IpInfoService.js";
|
|
3
|
+
export { IpapiBackend } from "./backends/ipapi.js";
|
|
4
|
+
export type { FetchFn, IpapiBackendConfig } from "./backends/ipapi.js";
|
|
5
|
+
export { MaxMindBackend } from "./backends/maxmind.js";
|
|
6
|
+
export type { MaxMindBackendConfig, OpenReader } from "./backends/maxmind.js";
|
|
2
7
|
export type { IIpInfoService, IpInfoServiceConfig } from "./types.js";
|
|
3
8
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAcA,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,YAAY,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAcA,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAClE,YAAY,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAIzD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,YAAY,EAAE,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACvE,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,YAAY,EAAE,oBAAoB,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAC9E,YAAY,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
};
|
|
1
|
+
import "./_virtual/_rolldown/runtime.js";
|
|
2
|
+
import { IpapiBackend } from "./backends/ipapi.js";
|
|
3
|
+
import { MaxMindBackend } from "./backends/maxmind.js";
|
|
4
|
+
import { IpInfoService, isNonRoutable } from "./IpInfoService.js";
|
|
5
|
+
export { IpInfoService, IpapiBackend, MaxMindBackend, isNonRoutable };
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAcA,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAcA,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAKlE,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAEnD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ipInfoService.unit.test.d.ts","sourceRoot":"","sources":["../../src/tests/ipInfoService.unit.test.ts"],"names":[],"mappings":""}
|