@prosopo/ipinfo 0.2.40 → 0.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.
Files changed (52) hide show
  1. package/.turbo/turbo-build$colon$cjs.log +12 -10
  2. package/.turbo/turbo-build$colon$tsc.log +10 -10
  3. package/.turbo/turbo-build.log +14 -11
  4. package/CHANGELOG.md +19 -0
  5. package/dist/IpInfoService.d.ts +8 -1
  6. package/dist/IpInfoService.d.ts.map +1 -1
  7. package/dist/IpInfoService.js +77 -84
  8. package/dist/IpInfoService.js.map +1 -1
  9. package/dist/_virtual/_rolldown/runtime.js +3 -0
  10. package/dist/backends/ipapi.d.ts +6 -0
  11. package/dist/backends/ipapi.d.ts.map +1 -1
  12. package/dist/backends/ipapi.js +117 -104
  13. package/dist/backends/ipapi.js.map +1 -1
  14. package/dist/backends/maxmind.d.ts +4 -0
  15. package/dist/backends/maxmind.d.ts.map +1 -1
  16. package/dist/backends/maxmind.js +126 -145
  17. package/dist/backends/maxmind.js.map +1 -1
  18. package/dist/cjs/IpInfoService.cjs +80 -86
  19. package/dist/cjs/backends/ipapi.cjs +118 -104
  20. package/dist/cjs/backends/maxmind.cjs +124 -165
  21. package/dist/cjs/index.cjs +7 -3
  22. package/dist/index.d.ts +6 -1
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/index.js +5 -4
  25. package/dist/index.js.map +1 -1
  26. package/dist/tests/ipInfoService.unit.test.d.ts +2 -0
  27. package/dist/tests/ipInfoService.unit.test.d.ts.map +1 -0
  28. package/dist/tests/ipInfoService.unit.test.js +244 -0
  29. package/dist/tests/ipInfoService.unit.test.js.map +1 -0
  30. package/dist/tests/ipapi.unit.test.d.ts +2 -0
  31. package/dist/tests/ipapi.unit.test.d.ts.map +1 -0
  32. package/dist/tests/ipapi.unit.test.js +337 -0
  33. package/dist/tests/ipapi.unit.test.js.map +1 -0
  34. package/dist/tests/ipinfo.test-d.d.ts +2 -0
  35. package/dist/tests/ipinfo.test-d.d.ts.map +1 -0
  36. package/dist/tests/ipinfo.test-d.js +97 -0
  37. package/dist/tests/ipinfo.test-d.js.map +1 -0
  38. package/dist/tests/maxmind.unit.test.d.ts +2 -0
  39. package/dist/tests/maxmind.unit.test.d.ts.map +1 -0
  40. package/dist/tests/maxmind.unit.test.js +391 -0
  41. package/dist/tests/maxmind.unit.test.js.map +1 -0
  42. package/package.json +10 -8
  43. package/src/IpInfoService.ts +19 -2
  44. package/src/backends/ipapi.ts +57 -10
  45. package/src/backends/maxmind.ts +22 -4
  46. package/src/index.ts +9 -1
  47. package/src/tests/ipInfoService.unit.test.ts +422 -0
  48. package/src/tests/ipapi.unit.test.ts +509 -0
  49. package/src/tests/ipinfo.test-d.ts +189 -0
  50. package/src/tests/maxmind.unit.test.ts +562 -0
  51. package/tsconfig.tsbuildinfo +1 -1
  52. package/vite.test.config.ts +18 -0
@@ -0,0 +1,244 @@
1
+ import { beforeEach, describe, expect, it, vi } from "vitest";
2
+ import { IpInfoService, isNonRoutable, } from "../IpInfoService.js";
3
+ const IP = "8.8.8.8";
4
+ const result = (source) => ({
5
+ isValid: true,
6
+ ip: IP,
7
+ countryCode: source,
8
+ isVPN: false,
9
+ isTor: false,
10
+ isProxy: false,
11
+ isDatacenter: false,
12
+ isAbuser: false,
13
+ isMobile: false,
14
+ isSatellite: false,
15
+ isCrawler: false,
16
+ });
17
+ const failure = (error) => ({
18
+ isValid: false,
19
+ error,
20
+ ip: IP,
21
+ });
22
+ const stub = (available, answer = result("stub")) => ({
23
+ initialize: vi.fn(async () => { }),
24
+ isAvailable: vi.fn(() => available),
25
+ lookup: vi.fn(async () => answer),
26
+ });
27
+ const service = (backends, config = {}) => new IpInfoService(config, {
28
+ maxmind: backends.maxmind,
29
+ ipapi: backends.ipapi,
30
+ });
31
+ describe("isNonRoutable", () => {
32
+ it("rejects the IPv4 loopback and private ranges", () => {
33
+ for (const ip of [
34
+ "127.0.0.1",
35
+ "127.255.255.255",
36
+ "10.0.0.1",
37
+ "192.168.1.1",
38
+ "169.254.1.1",
39
+ "0.0.0.0",
40
+ ]) {
41
+ expect(isNonRoutable(ip), ip).toBe(true);
42
+ }
43
+ });
44
+ it("rejects the whole 172.16/12 block and nothing either side of it", () => {
45
+ expect(isNonRoutable("172.16.0.0")).toBe(true);
46
+ expect(isNonRoutable("172.31.255.255")).toBe(true);
47
+ expect(isNonRoutable("172.15.0.1")).toBe(false);
48
+ expect(isNonRoutable("172.32.0.1")).toBe(false);
49
+ expect(isNonRoutable("172.217.16.1")).toBe(false);
50
+ });
51
+ it("treats a malformed 172. address as routable rather than crashing", () => {
52
+ expect(isNonRoutable("172.")).toBe(false);
53
+ expect(isNonRoutable("172.abc.0.1")).toBe(false);
54
+ });
55
+ it("sees through the IPv4-mapped IPv6 prefix", () => {
56
+ expect(isNonRoutable("::ffff:127.0.0.1")).toBe(true);
57
+ expect(isNonRoutable("::FFFF:192.168.0.1")).toBe(true);
58
+ expect(isNonRoutable("::ffff:8.8.8.8")).toBe(false);
59
+ });
60
+ it("rejects the IPv6 loopback, unspecified, ULA and link-local ranges", () => {
61
+ for (const ip of [
62
+ "::1",
63
+ "::",
64
+ "fc00::1",
65
+ "fd12:3456::1",
66
+ "FD00::1",
67
+ "fe80::1",
68
+ "feb0::1",
69
+ "FE80::1",
70
+ ]) {
71
+ expect(isNonRoutable(ip), ip).toBe(true);
72
+ }
73
+ });
74
+ it("accepts public IPv4 and IPv6 addresses", () => {
75
+ for (const ip of [
76
+ "8.8.8.8",
77
+ "1.1.1.1",
78
+ "2001:4860:4860::8888",
79
+ "fec0::1",
80
+ ]) {
81
+ expect(isNonRoutable(ip), ip).toBe(false);
82
+ }
83
+ });
84
+ it("treats an empty string as routable", () => {
85
+ expect(isNonRoutable("")).toBe(false);
86
+ });
87
+ });
88
+ describe("IpInfoService construction", () => {
89
+ it("builds no backends when nothing is configured", () => {
90
+ expect(new IpInfoService({}).isAvailable()).toBe(false);
91
+ });
92
+ it("builds MaxMind from either database path alone", async () => {
93
+ const city = new IpInfoService({ maxmindCityDbPath: "/city.mmdb" });
94
+ const asn = new IpInfoService({ maxmindAsnDbPath: "/asn.mmdb" });
95
+ expect(await city.lookup(IP)).toEqual(failure("No IP info backend available"));
96
+ expect(await asn.lookup(IP)).toEqual(failure("No IP info backend available"));
97
+ });
98
+ it("builds ipapi when a url is configured", () => {
99
+ expect(new IpInfoService({ ipapiUrl: "https://api.ipapi.is" }).isAvailable()).toBe(true);
100
+ });
101
+ it("does not build ipapi from a key with no url", () => {
102
+ expect(new IpInfoService({ ipapiKey: "secret" }).isAvailable()).toBe(false);
103
+ });
104
+ it("uses injected backends in place of configured ones", () => {
105
+ const ipapi = stub(false);
106
+ const svc = service({ ipapi }, { ipapiUrl: "https://api.ipapi.is" });
107
+ expect(svc.isAvailable()).toBe(false);
108
+ expect(ipapi.isAvailable).toHaveBeenCalled();
109
+ });
110
+ });
111
+ describe("IpInfoService.initialize", () => {
112
+ it("initializes MaxMind and leaves ipapi alone", async () => {
113
+ const maxmind = stub(true);
114
+ const ipapi = stub(true);
115
+ await service({ maxmind, ipapi }).initialize();
116
+ expect(maxmind.initialize).toHaveBeenCalledTimes(1);
117
+ expect(ipapi.initialize).not.toHaveBeenCalled();
118
+ });
119
+ it("succeeds when there are no backends at all", async () => {
120
+ await expect(new IpInfoService({}).initialize()).resolves.toBeUndefined();
121
+ });
122
+ it("logs the availability of both backends", async () => {
123
+ const info = vi.fn();
124
+ await service({ maxmind: stub(true), ipapi: stub(false) }, { logger: { info } }).initialize();
125
+ expect(info).toHaveBeenCalledTimes(1);
126
+ const entry = info.mock.calls[0]?.[0];
127
+ expect(typeof entry).toBe("function");
128
+ expect(entry()).toMatchObject({
129
+ data: { maxmindAvailable: true, ipapiAvailable: false },
130
+ });
131
+ });
132
+ it("propagates a MaxMind initialize failure", async () => {
133
+ const maxmind = stub(true);
134
+ maxmind.initialize = vi.fn(async () => {
135
+ throw new Error("disk on fire");
136
+ });
137
+ await expect(service({ maxmind }).initialize()).rejects.toThrow("disk on fire");
138
+ });
139
+ });
140
+ describe("IpInfoService.isAvailable", () => {
141
+ it.each([
142
+ [true, true, true],
143
+ [true, false, true],
144
+ [false, true, true],
145
+ [false, false, false],
146
+ ])("maxmind=%s ipapi=%s -> %s", (maxmindUp, ipapiUp, expected) => {
147
+ expect(service({
148
+ maxmind: stub(maxmindUp),
149
+ ipapi: stub(ipapiUp),
150
+ }).isAvailable()).toBe(expected);
151
+ });
152
+ });
153
+ describe("IpInfoService.lookup", () => {
154
+ let maxmind;
155
+ let ipapi;
156
+ beforeEach(() => {
157
+ maxmind = stub(true, result("maxmind"));
158
+ ipapi = stub(true, result("ipapi"));
159
+ });
160
+ it("short-circuits a non-routable IP without touching a backend", async () => {
161
+ await expect(service({ maxmind, ipapi }).lookup("127.0.0.1")).resolves.toEqual({
162
+ isValid: false,
163
+ error: "Non-routable IP address",
164
+ ip: "127.0.0.1",
165
+ });
166
+ expect(ipapi.lookup).not.toHaveBeenCalled();
167
+ expect(maxmind.lookup).not.toHaveBeenCalled();
168
+ });
169
+ it("prefers ipapi when both are available", async () => {
170
+ const response = await service({ maxmind, ipapi }).lookup(IP);
171
+ expect(response).toEqual(result("ipapi"));
172
+ expect(maxmind.lookup).not.toHaveBeenCalled();
173
+ });
174
+ it("falls back to MaxMind when ipapi returns an invalid result", async () => {
175
+ ipapi = stub(true, failure("upstream 503"));
176
+ await expect(service({ maxmind, ipapi }).lookup(IP)).resolves.toEqual(result("maxmind"));
177
+ });
178
+ it("returns the ipapi error when MaxMind cannot cover for it", async () => {
179
+ ipapi = stub(true, failure("upstream 503"));
180
+ await expect(service({ maxmind: stub(false), ipapi }).lookup(IP)).resolves.toEqual(failure("upstream 503"));
181
+ });
182
+ it("returns the ipapi error when MaxMind is not configured at all", async () => {
183
+ ipapi = stub(true, failure("upstream 503"));
184
+ await expect(service({ ipapi }).lookup(IP)).resolves.toEqual(failure("upstream 503"));
185
+ });
186
+ it("logs the fallback with the upstream error attached", async () => {
187
+ const debug = vi.fn();
188
+ ipapi = stub(true, failure("upstream 503"));
189
+ await service({ maxmind, ipapi }, { logger: { debug } }).lookup(IP);
190
+ expect(debug).toHaveBeenCalledTimes(1);
191
+ expect(debug.mock.calls[0]?.[0]()).toMatchObject({
192
+ data: { ip: IP, error: "upstream 503" },
193
+ });
194
+ });
195
+ it("logs 'unknown' when the failed result carries no error field", async () => {
196
+ const debug = vi.fn();
197
+ ipapi = stub(true, { isValid: false, ip: IP });
198
+ await service({ maxmind, ipapi }, { logger: { debug } }).lookup(IP);
199
+ expect(debug.mock.calls[0]?.[0]()).toMatchObject({
200
+ data: { error: "unknown" },
201
+ });
202
+ });
203
+ it("does not log a fallback when ipapi succeeds", async () => {
204
+ const debug = vi.fn();
205
+ await service({ maxmind, ipapi }, { logger: { debug } }).lookup(IP);
206
+ expect(debug).not.toHaveBeenCalled();
207
+ });
208
+ it("uses MaxMind alone when ipapi is unavailable", async () => {
209
+ const down = stub(false);
210
+ const response = await service({ maxmind, ipapi: down }).lookup(IP);
211
+ expect(response).toEqual(result("maxmind"));
212
+ expect(down.lookup).not.toHaveBeenCalled();
213
+ });
214
+ it("reports that no backend is available when neither is", async () => {
215
+ await expect(service({ maxmind: stub(false), ipapi: stub(false) }).lookup(IP)).resolves.toEqual(failure("No IP info backend available"));
216
+ });
217
+ it("reports that no backend is available when none is configured", async () => {
218
+ await expect(new IpInfoService({}).lookup(IP)).resolves.toEqual(failure("No IP info backend available"));
219
+ });
220
+ it("passes the IP through to the backend unmodified", async () => {
221
+ await service({ ipapi }).lookup("2001:4860:4860::8888");
222
+ expect(ipapi.lookup).toHaveBeenCalledWith("2001:4860:4860::8888");
223
+ });
224
+ it("does not catch a backend that throws", async () => {
225
+ ipapi.lookup = vi.fn(async () => {
226
+ throw new Error("unexpected");
227
+ });
228
+ await expect(service({ maxmind, ipapi }).lookup(IP)).rejects.toThrow("unexpected");
229
+ expect(maxmind.lookup).not.toHaveBeenCalled();
230
+ });
231
+ it("re-checks availability on every lookup", async () => {
232
+ let up = false;
233
+ const flaky = {
234
+ initialize: vi.fn(async () => { }),
235
+ isAvailable: vi.fn(() => up),
236
+ lookup: vi.fn(async () => result("recovered")),
237
+ };
238
+ const svc = service({ ipapi: flaky });
239
+ expect(await svc.lookup(IP)).toEqual(failure("No IP info backend available"));
240
+ up = true;
241
+ expect(await svc.lookup(IP)).toEqual(result("recovered"));
242
+ });
243
+ });
244
+ //# sourceMappingURL=ipInfoService.unit.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ipInfoService.unit.test.js","sourceRoot":"","sources":["../../src/tests/ipInfoService.unit.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=ipapi.unit.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ipapi.unit.test.d.ts","sourceRoot":"","sources":["../../src/tests/ipapi.unit.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,337 @@
1
+ import { describe, expect, it, vi } from "vitest";
2
+ import { DEFAULT_TIMEOUT_MS, IpapiBackend, parseAbuserScore, } from "../backends/ipapi.js";
3
+ const BASE_URL = "https://api.ipapi.test/";
4
+ const IP = "8.8.8.8";
5
+ const baseResponse = (overrides = {}) => ({
6
+ ip: IP,
7
+ rir: "ARIN",
8
+ is_bogon: false,
9
+ is_mobile: false,
10
+ is_satellite: false,
11
+ is_crawler: false,
12
+ is_datacenter: false,
13
+ is_tor: false,
14
+ is_proxy: false,
15
+ is_vpn: false,
16
+ is_abuser: false,
17
+ elapsed_ms: 1,
18
+ ...overrides,
19
+ });
20
+ const respondWith = (body, init = {}) => {
21
+ return async () => new Response(JSON.stringify(body), {
22
+ status: init.status ?? 200,
23
+ statusText: init.statusText ?? "OK",
24
+ headers: { "Content-Type": "application/json" },
25
+ });
26
+ };
27
+ const respondNever = (_url, init) => new Promise((_resolve, reject) => {
28
+ init.signal?.addEventListener("abort", () => {
29
+ const error = new Error("The operation was aborted");
30
+ error.name = "AbortError";
31
+ reject(error);
32
+ });
33
+ });
34
+ const backend = (fetchFn, extra = {}) => new IpapiBackend({ baseUrl: BASE_URL, fetch: fetchFn, ...extra });
35
+ const expectValid = (response) => {
36
+ if (!response.isValid) {
37
+ throw new Error(`expected a valid result, got: ${response.error}`);
38
+ }
39
+ return response;
40
+ };
41
+ describe("parseAbuserScore", () => {
42
+ it("reads the numeric prefix of a scored string", () => {
43
+ expect(parseAbuserScore("0.0012 (Low)")).toBeCloseTo(0.0012);
44
+ });
45
+ it("reads a bare number with no qualifier", () => {
46
+ expect(parseAbuserScore("0.5")).toBe(0.5);
47
+ });
48
+ it("reports a missing score as unknown rather than throwing", () => {
49
+ expect(parseAbuserScore(undefined)).toBeUndefined();
50
+ });
51
+ it("reports an empty string as unknown", () => {
52
+ expect(parseAbuserScore("")).toBeUndefined();
53
+ });
54
+ it("reports a non-numeric score as unknown, never NaN", () => {
55
+ expect(parseAbuserScore("unknown")).toBeUndefined();
56
+ expect(parseAbuserScore("(Low)")).toBeUndefined();
57
+ });
58
+ it("distinguishes a genuine zero from an unknown score", () => {
59
+ expect(parseAbuserScore("0 (Very Low)")).toBe(0);
60
+ expect(parseAbuserScore("0")).toBe(0);
61
+ expect(parseAbuserScore(undefined)).toBeUndefined();
62
+ });
63
+ });
64
+ describe("IpapiBackend.isAvailable", () => {
65
+ it("is available when a base url is configured", () => {
66
+ expect(backend(respondWith(baseResponse())).isAvailable()).toBe(true);
67
+ });
68
+ it("is unavailable when the base url is blank", () => {
69
+ const blank = new IpapiBackend({ baseUrl: "" });
70
+ expect(blank.isAvailable()).toBe(false);
71
+ });
72
+ });
73
+ describe("IpapiBackend.lookup request", () => {
74
+ it("posts the ip as JSON to the configured url", async () => {
75
+ const fetchFn = vi.fn(respondWith(baseResponse()));
76
+ await backend(fetchFn).lookup(IP);
77
+ const [url, init] = fetchFn.mock.calls[0] ?? [];
78
+ expect(url).toBe(BASE_URL);
79
+ expect(init?.method).toBe("POST");
80
+ expect(JSON.parse(String(init?.body))).toEqual({ q: IP });
81
+ });
82
+ it("includes the api key when one is configured", async () => {
83
+ const fetchFn = vi.fn(respondWith(baseResponse()));
84
+ await backend(fetchFn, { apiKey: "secret" }).lookup(IP);
85
+ const init = fetchFn.mock.calls[0]?.[1];
86
+ expect(JSON.parse(String(init?.body))).toEqual({ q: IP, key: "secret" });
87
+ });
88
+ it("omits the key entirely when none is configured", async () => {
89
+ const fetchFn = vi.fn(respondWith(baseResponse()));
90
+ await backend(fetchFn).lookup(IP);
91
+ expect(JSON.parse(String(fetchFn.mock.calls[0]?.[1]?.body))).not.toHaveProperty("key");
92
+ });
93
+ it("asks for and declares JSON", async () => {
94
+ const fetchFn = vi.fn(respondWith(baseResponse()));
95
+ await backend(fetchFn).lookup(IP);
96
+ const headers = fetchFn.mock.calls[0]?.[1]?.headers;
97
+ expect(headers).toMatchObject({
98
+ "Content-Type": "application/json",
99
+ Accept: "application/json",
100
+ });
101
+ });
102
+ });
103
+ describe("IpapiBackend.lookup input validation", () => {
104
+ it("rejects an empty ip without calling out", async () => {
105
+ const fetchFn = vi.fn(respondWith(baseResponse()));
106
+ const result = await backend(fetchFn).lookup("");
107
+ expect(result).toEqual({
108
+ isValid: false,
109
+ error: "Invalid IP address provided",
110
+ ip: "undefined",
111
+ });
112
+ expect(fetchFn).not.toHaveBeenCalled();
113
+ });
114
+ it("does not send a lookup for a non-string ip from an untyped caller", async () => {
115
+ const fetchFn = vi.fn(respondWith(baseResponse()));
116
+ const untyped = (ip) => backend(fetchFn).lookup(ip);
117
+ await expect(untyped(null)).resolves.toMatchObject({ isValid: false });
118
+ expect(fetchFn).not.toHaveBeenCalled();
119
+ });
120
+ });
121
+ describe("IpapiBackend.lookup responses", () => {
122
+ it("maps a full response onto the shared result shape", async () => {
123
+ const result = expectValid(await backend(respondWith(baseResponse({
124
+ is_vpn: true,
125
+ is_datacenter: true,
126
+ company: {
127
+ name: "Example Co",
128
+ abuser_score: "0.01 (Low)",
129
+ domain: "example.com",
130
+ type: "hosting",
131
+ network: "8.8.8.0/24",
132
+ whois: "",
133
+ },
134
+ asn: {
135
+ asn: 15169,
136
+ abuser_score: "0.002 (Very Low)",
137
+ route: "8.8.8.0/24",
138
+ descr: "",
139
+ country: "US",
140
+ active: true,
141
+ org: "Google LLC",
142
+ domain: "google.com",
143
+ abuse: "",
144
+ type: "hosting",
145
+ created: "",
146
+ updated: "",
147
+ rir: "ARIN",
148
+ whois: "",
149
+ },
150
+ location: {
151
+ is_eu_member: false,
152
+ calling_code: "1",
153
+ currency_code: "USD",
154
+ continent: "NA",
155
+ country: "United States",
156
+ country_code: "US",
157
+ state: "California",
158
+ city: "Mountain View",
159
+ latitude: 37.4,
160
+ longitude: -122.1,
161
+ zip: "94035",
162
+ timezone: "America/Los_Angeles",
163
+ local_time: "",
164
+ local_time_unix: 0,
165
+ is_dst: false,
166
+ },
167
+ vpn: {
168
+ ip: IP,
169
+ service: "ExampleVPN",
170
+ url: "",
171
+ type: "exit_node",
172
+ last_seen: 0,
173
+ last_seen_str: "",
174
+ country_code: "US",
175
+ city_name: "Mountain View",
176
+ latitude: 37.4,
177
+ longitude: -122.1,
178
+ },
179
+ }))).lookup(IP));
180
+ expect(result).toMatchObject({
181
+ ip: IP,
182
+ isValid: true,
183
+ isVPN: true,
184
+ isDatacenter: true,
185
+ providerName: "Example Co",
186
+ providerType: "hosting",
187
+ asnNumber: 15169,
188
+ asnOrganization: "Google LLC",
189
+ country: "United States",
190
+ countryCode: "US",
191
+ region: "California",
192
+ city: "Mountain View",
193
+ timezone: "America/Los_Angeles",
194
+ vpnService: "ExampleVPN",
195
+ vpnType: "exit_node",
196
+ });
197
+ expect(result.abuserScore).toBeCloseTo(0.002);
198
+ expect(result.companyAbuserScore).toBeCloseTo(0.01);
199
+ });
200
+ it("survives a response missing every optional section", async () => {
201
+ const result = expectValid(await backend(respondWith(baseResponse())).lookup(IP));
202
+ expect(result.isValid).toBe(true);
203
+ expect(result.country).toBeUndefined();
204
+ expect(result.asnNumber).toBeUndefined();
205
+ expect(result.providerName).toBeUndefined();
206
+ expect(result.abuserScore).toBeUndefined();
207
+ expect(result.companyAbuserScore).toBeUndefined();
208
+ });
209
+ it("keeps a successful lookup when the abuser scores are absent", async () => {
210
+ const result = expectValid(await backend(respondWith(baseResponse({
211
+ asn: {
212
+ asn: 64512,
213
+ route: "",
214
+ descr: "",
215
+ country: "US",
216
+ active: true,
217
+ org: "Test",
218
+ domain: "",
219
+ abuse: "",
220
+ type: "isp",
221
+ created: "",
222
+ updated: "",
223
+ rir: "ARIN",
224
+ whois: "",
225
+ },
226
+ }))).lookup(IP));
227
+ expect(result.isValid).toBe(true);
228
+ expect(result.asnNumber).toBe(64512);
229
+ expect(result.abuserScore).toBeUndefined();
230
+ });
231
+ it("prefers the company name but falls back to the datacenter", async () => {
232
+ const result = expectValid(await backend(respondWith(baseResponse({
233
+ datacenter: { datacenter: "AWS", network: "1.2.3.0/24" },
234
+ }))).lookup(IP));
235
+ expect(result.providerName).toBe("AWS");
236
+ expect(result.datacenterName).toBe("AWS");
237
+ });
238
+ it("reports a bogon address as invalid rather than mapping it", async () => {
239
+ const result = await backend(respondWith(baseResponse({ is_bogon: true }))).lookup(IP);
240
+ expect(result).toEqual({
241
+ isValid: false,
242
+ error: "IP address is bogon (non-routable)",
243
+ ip: IP,
244
+ });
245
+ });
246
+ it("reports a non-2xx status with the code and text", async () => {
247
+ const result = await backend(respondWith({}, { status: 429, statusText: "Too Many Requests" })).lookup(IP);
248
+ expect(result).toMatchObject({
249
+ isValid: false,
250
+ ip: IP,
251
+ error: "API request failed with status 429: Too Many Requests",
252
+ });
253
+ });
254
+ it("reports a 500 without throwing", async () => {
255
+ const result = await backend(respondWith({}, { status: 500, statusText: "Internal Server Error" })).lookup(IP);
256
+ expect(result.isValid).toBe(false);
257
+ });
258
+ it("turns malformed JSON into an error response", async () => {
259
+ const malformed = async () => new Response("not json", { status: 200 });
260
+ const result = await backend(malformed).lookup(IP);
261
+ expect(result.isValid).toBe(false);
262
+ expect(result).toMatchObject({ ip: IP });
263
+ if (!result.isValid) {
264
+ expect(result.error).toContain("Network or parsing error");
265
+ }
266
+ });
267
+ it("turns a network failure into an error response", async () => {
268
+ const failing = async () => {
269
+ throw new Error("ECONNREFUSED");
270
+ };
271
+ const result = await backend(failing).lookup(IP);
272
+ expect(result).toEqual({
273
+ isValid: false,
274
+ error: "Network or parsing error: ECONNREFUSED",
275
+ ip: IP,
276
+ });
277
+ });
278
+ it("describes a non-Error rejection rather than printing [object Object]", async () => {
279
+ const failing = async () => {
280
+ throw "socket hang up";
281
+ };
282
+ const result = await backend(failing).lookup(IP);
283
+ expect(result).toMatchObject({
284
+ isValid: false,
285
+ error: "Network or parsing error: socket hang up",
286
+ });
287
+ });
288
+ });
289
+ describe("IpapiBackend.lookup timeout", () => {
290
+ it("gives up on a hanging upstream and says so", async () => {
291
+ const result = await backend(respondNever, { timeoutMs: 20 }).lookup(IP);
292
+ expect(result).toEqual({
293
+ isValid: false,
294
+ error: "Request timed out after 20ms",
295
+ ip: IP,
296
+ });
297
+ });
298
+ it("reports the default budget when none is configured", async () => {
299
+ expect(DEFAULT_TIMEOUT_MS).toBe(700);
300
+ const configured = new IpapiBackend({
301
+ baseUrl: BASE_URL,
302
+ fetch: respondNever,
303
+ });
304
+ const result = await backend(respondNever, {
305
+ timeoutMs: DEFAULT_TIMEOUT_MS,
306
+ }).lookup(IP);
307
+ expect(result).toMatchObject({
308
+ error: `Request timed out after ${DEFAULT_TIMEOUT_MS}ms`,
309
+ });
310
+ expect(configured.isAvailable()).toBe(true);
311
+ }, 5000);
312
+ it("does not time out a response that arrives in time", async () => {
313
+ const slowButFine = async () => {
314
+ await new Promise((resolve) => {
315
+ setTimeout(resolve, 5);
316
+ });
317
+ return new Response(JSON.stringify(baseResponse()), { status: 200 });
318
+ };
319
+ const result = await backend(slowButFine, { timeoutMs: 200 }).lookup(IP);
320
+ expect(result.isValid).toBe(true);
321
+ });
322
+ it("passes an abort signal the upstream can observe", async () => {
323
+ const fetchFn = vi.fn(respondWith(baseResponse()));
324
+ await backend(fetchFn).lookup(IP);
325
+ expect(fetchFn.mock.calls[0]?.[1]?.signal).toBeInstanceOf(AbortSignal);
326
+ });
327
+ });
328
+ describe("IpapiBackend default fetch", () => {
329
+ it("uses the global fetch when none is injected", async () => {
330
+ const result = await new IpapiBackend({
331
+ baseUrl: "http://127.0.0.1:1/",
332
+ timeoutMs: 2000,
333
+ }).lookup(IP);
334
+ expect(result.isValid).toBe(false);
335
+ });
336
+ });
337
+ //# sourceMappingURL=ipapi.unit.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ipapi.unit.test.js","sourceRoot":"","sources":["../../src/tests/ipapi.unit.test.ts"],"names":[],"mappings":"AAyBA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAClD,OAAO,EACN,kBAAkB,EAElB,YAAY,EACZ,gBAAgB,GAChB,MAAM,sBAAsB,CAAC;AAE9B,MAAM,QAAQ,GAAG,yBAAyB,CAAC;AAC3C,MAAM,EAAE,GAAG,SAAS,CAAC;AAGrB,MAAM,YAAY,GAAG,CACpB,YAAoC,EAAE,EACtB,EAAE,CAAC,CAAC;IACpB,EAAE,EAAE,EAAE;IACN,GAAG,EAAE,MAAM;IACX,QAAQ,EAAE,KAAK;IACf,SAAS,EAAE,KAAK;IAChB,YAAY,EAAE,KAAK;IACnB,UAAU,EAAE,KAAK;IACjB,aAAa,EAAE,KAAK;IACpB,MAAM,EAAE,KAAK;IACb,QAAQ,EAAE,KAAK;IACf,MAAM,EAAE,KAAK;IACb,SAAS,EAAE,KAAK;IAChB,UAAU,EAAE,CAAC;IACb,GAAG,SAAS;CACZ,CAAC,CAAC;AAGH,MAAM,WAAW,GAAG,CACnB,IAAa,EACb,OAAiD,EAAE,EACzC,EAAE;IACZ,OAAO,KAAK,IAAkC,EAAE,CAC/C,IAAI,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE;QAClC,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,GAAG;QAC1B,UAAU,EAAE,IAAI,CAAC,UAAU,IAAI,IAAI;QACnC,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;KAC/C,CAAC,CAAC;AACL,CAAC,CAAC;AAGF,MAAM,YAAY,GAAY,CAAC,IAAY,EAAE,IAAiB,EAAE,EAAE,CACjE,IAAI,OAAO,CAAsB,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE;IACrD,IAAI,CAAC,MAAM,EAAE,gBAAgB,CAAC,OAAO,EAAE,GAAG,EAAE;QAG3C,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC;QACrD,KAAK,CAAC,IAAI,GAAG,YAAY,CAAC;QAC1B,MAAM,CAAC,KAAK,CAAC,CAAC;IACf,CAAC,CAAC,CAAC;AACJ,CAAC,CAAC,CAAC;AAEJ,MAAM,OAAO,GAAG,CACf,OAAgB,EAChB,QAAiD,EAAE,EACpC,EAAE,CACjB,IAAI,YAAY,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,KAAK,EAAE,CAAC,CAAC;AAGnE,MAAM,WAAW,GAAG,CACnB,QAAqD,EACtC,EAAE;IACjB,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CAAC,iCAAiC,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC;IACpE,CAAC;IACD,OAAO,QAAQ,CAAC;AACjB,CAAC,CAAC;AAEF,QAAQ,CAAC,kBAAkB,EAAE,GAAG,EAAE;IACjC,EAAE,CAAC,6CAA6C,EAAE,GAAG,EAAE;QACtD,MAAM,CAAC,gBAAgB,CAAC,cAAc,CAAC,CAAC,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;IAC9D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uCAAuC,EAAE,GAAG,EAAE;QAChD,MAAM,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC3C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yDAAyD,EAAE,GAAG,EAAE;QAKlE,MAAM,CAAC,gBAAgB,CAAC,SAAS,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;IACrD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oCAAoC,EAAE,GAAG,EAAE;QAC7C,MAAM,CAAC,gBAAgB,CAAC,EAAE,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;IAC9C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mDAAmD,EAAE,GAAG,EAAE;QAI5D,MAAM,CAAC,gBAAgB,CAAC,SAAS,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;QACpD,MAAM,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;IACnD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oDAAoD,EAAE,GAAG,EAAE;QAI7D,MAAM,CAAC,gBAAgB,CAAC,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACjD,MAAM,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACtC,MAAM,CAAC,gBAAgB,CAAC,SAAS,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;IACrD,CAAC,CAAC,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,0BAA0B,EAAE,GAAG,EAAE;IACzC,EAAE,CAAC,4CAA4C,EAAE,GAAG,EAAE;QACrD,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACvE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2CAA2C,EAAE,GAAG,EAAE;QAGpD,MAAM,KAAK,GAAG,IAAI,YAAY,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC,CAAC;QAChD,MAAM,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACzC,CAAC,CAAC,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,6BAA6B,EAAE,GAAG,EAAE;IAC5C,EAAE,CAAC,4CAA4C,EAAE,KAAK,IAAI,EAAE;QAC3D,MAAM,OAAO,GAAG,EAAE,CAAC,EAAE,CAAU,WAAW,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC;QAC5D,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QAElC,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QAChD,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC3B,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAClC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;IAC3D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6CAA6C,EAAE,KAAK,IAAI,EAAE;QAC5D,MAAM,OAAO,GAAG,EAAE,CAAC,EAAE,CAAU,WAAW,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC;QAC5D,MAAM,OAAO,CAAC,OAAO,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QAExD,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QACxC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,GAAG,EAAE,QAAQ,EAAE,CAAC,CAAC;IAC1E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gDAAgD,EAAE,KAAK,IAAI,EAAE;QAG/D,MAAM,OAAO,GAAG,EAAE,CAAC,EAAE,CAAU,WAAW,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC;QAC5D,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QAElC,MAAM,CACL,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CACpD,CAAC,GAAG,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;IAC7B,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4BAA4B,EAAE,KAAK,IAAI,EAAE;QAC3C,MAAM,OAAO,GAAG,EAAE,CAAC,EAAE,CAAU,WAAW,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC;QAC5D,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QAElC,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC;QACpD,MAAM,CAAC,OAAO,CAAC,CAAC,aAAa,CAAC;YAC7B,cAAc,EAAE,kBAAkB;YAClC,MAAM,EAAE,kBAAkB;SAC1B,CAAC,CAAC;IACJ,CAAC,CAAC,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,sCAAsC,EAAE,GAAG,EAAE;IACrD,EAAE,CAAC,yCAAyC,EAAE,KAAK,IAAI,EAAE;QAGxD,MAAM,OAAO,GAAG,EAAE,CAAC,EAAE,CAAU,WAAW,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC;QAC5D,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QAEjD,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC;YACtB,OAAO,EAAE,KAAK;YACd,KAAK,EAAE,6BAA6B;YACpC,EAAE,EAAE,WAAW;SACf,CAAC,CAAC;QACH,MAAM,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;IACxC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mEAAmE,EAAE,KAAK,IAAI,EAAE;QAGlF,MAAM,OAAO,GAAG,EAAE,CAAC,EAAE,CAAU,WAAW,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC;QAC5D,MAAM,OAAO,GAAsC,CAAC,EAAW,EAAE,EAAE,CAClE,OAAO,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,EAAY,CAAC,CAAC;QAEvC,MAAM,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,aAAa,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC;QACvE,MAAM,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;IACxC,CAAC,CAAC,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,+BAA+B,EAAE,GAAG,EAAE;IAC9C,EAAE,CAAC,mDAAmD,EAAE,KAAK,IAAI,EAAE;QAClE,MAAM,MAAM,GAAG,WAAW,CACzB,MAAM,OAAO,CACZ,WAAW,CACV,YAAY,CAAC;YACZ,MAAM,EAAE,IAAI;YACZ,aAAa,EAAE,IAAI;YACnB,OAAO,EAAE;gBACR,IAAI,EAAE,YAAY;gBAClB,YAAY,EAAE,YAAY;gBAC1B,MAAM,EAAE,aAAa;gBACrB,IAAI,EAAE,SAAS;gBACf,OAAO,EAAE,YAAY;gBACrB,KAAK,EAAE,EAAE;aACT;YACD,GAAG,EAAE;gBACJ,GAAG,EAAE,KAAK;gBACV,YAAY,EAAE,kBAAkB;gBAChC,KAAK,EAAE,YAAY;gBACnB,KAAK,EAAE,EAAE;gBACT,OAAO,EAAE,IAAI;gBACb,MAAM,EAAE,IAAI;gBACZ,GAAG,EAAE,YAAY;gBACjB,MAAM,EAAE,YAAY;gBACpB,KAAK,EAAE,EAAE;gBACT,IAAI,EAAE,SAAS;gBACf,OAAO,EAAE,EAAE;gBACX,OAAO,EAAE,EAAE;gBACX,GAAG,EAAE,MAAM;gBACX,KAAK,EAAE,EAAE;aACT;YACD,QAAQ,EAAE;gBACT,YAAY,EAAE,KAAK;gBACnB,YAAY,EAAE,GAAG;gBACjB,aAAa,EAAE,KAAK;gBACpB,SAAS,EAAE,IAAI;gBACf,OAAO,EAAE,eAAe;gBACxB,YAAY,EAAE,IAAI;gBAClB,KAAK,EAAE,YAAY;gBACnB,IAAI,EAAE,eAAe;gBACrB,QAAQ,EAAE,IAAI;gBACd,SAAS,EAAE,CAAC,KAAK;gBACjB,GAAG,EAAE,OAAO;gBACZ,QAAQ,EAAE,qBAAqB;gBAC/B,UAAU,EAAE,EAAE;gBACd,eAAe,EAAE,CAAC;gBAClB,MAAM,EAAE,KAAK;aACb;YACD,GAAG,EAAE;gBACJ,EAAE,EAAE,EAAE;gBACN,OAAO,EAAE,YAAY;gBACrB,GAAG,EAAE,EAAE;gBACP,IAAI,EAAE,WAAW;gBACjB,SAAS,EAAE,CAAC;gBACZ,aAAa,EAAE,EAAE;gBACjB,YAAY,EAAE,IAAI;gBAClB,SAAS,EAAE,eAAe;gBAC1B,QAAQ,EAAE,IAAI;gBACd,SAAS,EAAE,CAAC,KAAK;aACjB;SACD,CAAC,CACF,CACD,CAAC,MAAM,CAAC,EAAE,CAAC,CACZ,CAAC;QAEF,MAAM,CAAC,MAAM,CAAC,CAAC,aAAa,CAAC;YAC5B,EAAE,EAAE,EAAE;YACN,OAAO,EAAE,IAAI;YACb,KAAK,EAAE,IAAI;YACX,YAAY,EAAE,IAAI;YAClB,YAAY,EAAE,YAAY;YAC1B,YAAY,EAAE,SAAS;YACvB,SAAS,EAAE,KAAK;YAChB,eAAe,EAAE,YAAY;YAC7B,OAAO,EAAE,eAAe;YACxB,WAAW,EAAE,IAAI;YACjB,MAAM,EAAE,YAAY;YACpB,IAAI,EAAE,eAAe;YACrB,QAAQ,EAAE,qBAAqB;YAC/B,UAAU,EAAE,YAAY;YACxB,OAAO,EAAE,WAAW;SACpB,CAAC,CAAC;QACH,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;QAC9C,MAAM,CAAC,MAAM,CAAC,kBAAkB,CAAC,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;IACrD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oDAAoD,EAAE,KAAK,IAAI,EAAE;QAGnE,MAAM,MAAM,GAAG,WAAW,CACzB,MAAM,OAAO,CAAC,WAAW,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CACrD,CAAC;QAEF,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAClC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,aAAa,EAAE,CAAC;QACvC,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,aAAa,EAAE,CAAC;QACzC,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,aAAa,EAAE,CAAC;QAC5C,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,aAAa,EAAE,CAAC;QAC3C,MAAM,CAAC,MAAM,CAAC,kBAAkB,CAAC,CAAC,aAAa,EAAE,CAAC;IACnD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6DAA6D,EAAE,KAAK,IAAI,EAAE;QAI5E,MAAM,MAAM,GAAG,WAAW,CACzB,MAAM,OAAO,CACZ,WAAW,CACV,YAAY,CAAC;YACZ,GAAG,EAAE;gBACJ,GAAG,EAAE,KAAK;gBACV,KAAK,EAAE,EAAE;gBACT,KAAK,EAAE,EAAE;gBACT,OAAO,EAAE,IAAI;gBACb,MAAM,EAAE,IAAI;gBACZ,GAAG,EAAE,MAAM;gBACX,MAAM,EAAE,EAAE;gBACV,KAAK,EAAE,EAAE;gBACT,IAAI,EAAE,KAAK;gBACX,OAAO,EAAE,EAAE;gBACX,OAAO,EAAE,EAAE;gBACX,GAAG,EAAE,MAAM;gBACX,KAAK,EAAE,EAAE;aACe;SACzB,CAAC,CACF,CACD,CAAC,MAAM,CAAC,EAAE,CAAC,CACZ,CAAC;QAEF,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAClC,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACrC,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,aAAa,EAAE,CAAC;IAC5C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2DAA2D,EAAE,KAAK,IAAI,EAAE;QAC1E,MAAM,MAAM,GAAG,WAAW,CACzB,MAAM,OAAO,CACZ,WAAW,CACV,YAAY,CAAC;YACZ,UAAU,EAAE,EAAE,UAAU,EAAE,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE;SACxD,CAAC,CACF,CACD,CAAC,MAAM,CAAC,EAAE,CAAC,CACZ,CAAC;QAEF,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAGxC,MAAM,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC3C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2DAA2D,EAAE,KAAK,IAAI,EAAE;QAC1E,MAAM,MAAM,GAAG,MAAM,OAAO,CAC3B,WAAW,CAAC,YAAY,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAC7C,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QAEb,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC;YACtB,OAAO,EAAE,KAAK;YACd,KAAK,EAAE,oCAAoC;YAC3C,EAAE,EAAE,EAAE;SACN,CAAC,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iDAAiD,EAAE,KAAK,IAAI,EAAE;QAChE,MAAM,MAAM,GAAG,MAAM,OAAO,CAC3B,WAAW,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,UAAU,EAAE,mBAAmB,EAAE,CAAC,CACjE,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QAEb,MAAM,CAAC,MAAM,CAAC,CAAC,aAAa,CAAC;YAC5B,OAAO,EAAE,KAAK;YACd,EAAE,EAAE,EAAE;YACN,KAAK,EAAE,uDAAuD;SAC9D,CAAC,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gCAAgC,EAAE,KAAK,IAAI,EAAE;QAG/C,MAAM,MAAM,GAAG,MAAM,OAAO,CAC3B,WAAW,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,UAAU,EAAE,uBAAuB,EAAE,CAAC,CACrE,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QAEb,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACpC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6CAA6C,EAAE,KAAK,IAAI,EAAE;QAC5D,MAAM,SAAS,GAAY,KAAK,IAAkC,EAAE,CACnE,IAAI,QAAQ,CAAC,UAAU,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;QAE3C,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QAEnD,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACnC,MAAM,CAAC,MAAM,CAAC,CAAC,aAAa,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;QACzC,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YACrB,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,0BAA0B,CAAC,CAAC;QAC5D,CAAC;IACF,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gDAAgD,EAAE,KAAK,IAAI,EAAE;QAC/D,MAAM,OAAO,GAAY,KAAK,IAAkC,EAAE;YACjE,MAAM,IAAI,KAAK,CAAC,cAAc,CAAC,CAAC;QACjC,CAAC,CAAC;QAEF,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QAEjD,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC;YACtB,OAAO,EAAE,KAAK;YACd,KAAK,EAAE,wCAAwC;YAC/C,EAAE,EAAE,EAAE;SACN,CAAC,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sEAAsE,EAAE,KAAK,IAAI,EAAE;QACrF,MAAM,OAAO,GAAY,KAAK,IAAkC,EAAE;YAEjE,MAAM,gBAAgB,CAAC;QACxB,CAAC,CAAC;QAEF,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QAEjD,MAAM,CAAC,MAAM,CAAC,CAAC,aAAa,CAAC;YAC5B,OAAO,EAAE,KAAK;YACd,KAAK,EAAE,0CAA0C;SACjD,CAAC,CAAC;IACJ,CAAC,CAAC,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,6BAA6B,EAAE,GAAG,EAAE;IAC5C,EAAE,CAAC,4CAA4C,EAAE,KAAK,IAAI,EAAE;QAG3D,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,YAAY,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QAEzE,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC;YACtB,OAAO,EAAE,KAAK;YACd,KAAK,EAAE,8BAA8B;YACrC,EAAE,EAAE,EAAE;SACN,CAAC,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oDAAoD,EAAE,KAAK,IAAI,EAAE;QACnE,MAAM,CAAC,kBAAkB,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACrC,MAAM,UAAU,GAAG,IAAI,YAAY,CAAC;YACnC,OAAO,EAAE,QAAQ;YACjB,KAAK,EAAE,YAAY;SACnB,CAAC,CAAC;QAGH,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,YAAY,EAAE;YAC1C,SAAS,EAAE,kBAAkB;SAC7B,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACd,MAAM,CAAC,MAAM,CAAC,CAAC,aAAa,CAAC;YAC5B,KAAK,EAAE,2BAA2B,kBAAkB,IAAI;SACxD,CAAC,CAAC;QACH,MAAM,CAAC,UAAU,CAAC,WAAW,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC7C,CAAC,EAAE,IAAI,CAAC,CAAC;IAET,EAAE,CAAC,mDAAmD,EAAE,KAAK,IAAI,EAAE;QAClE,MAAM,WAAW,GAAY,KAAK,IAAkC,EAAE;YACrE,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;gBACnC,UAAU,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;YACxB,CAAC,CAAC,CAAC;YACH,OAAO,IAAI,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,YAAY,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;QACtE,CAAC,CAAC;QAEF,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,WAAW,EAAE,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QAEzE,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACnC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iDAAiD,EAAE,KAAK,IAAI,EAAE;QAChE,MAAM,OAAO,GAAG,EAAE,CAAC,EAAE,CAAU,WAAW,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC;QAC5D,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QAElC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,cAAc,CAAC,WAAW,CAAC,CAAC;IACxE,CAAC,CAAC,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,4BAA4B,EAAE,GAAG,EAAE;IAC3C,EAAE,CAAC,6CAA6C,EAAE,KAAK,IAAI,EAAE;QAI5D,MAAM,MAAM,GAAG,MAAM,IAAI,YAAY,CAAC;YACrC,OAAO,EAAE,qBAAqB;YAC9B,SAAS,EAAE,IAAI;SACf,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QAEd,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACpC,CAAC,CAAC,CAAC;AACJ,CAAC,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=ipinfo.test-d.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ipinfo.test-d.d.ts","sourceRoot":"","sources":["../../src/tests/ipinfo.test-d.ts"],"names":[],"mappings":""}