ip-finder-client 1.0.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.
package/README.md ADDED
@@ -0,0 +1,209 @@
1
+ # ip-finder-client
2
+
3
+ A lightweight, zero-dependency Node.js client for the IP Finder API. Get IP geolocation, ASN information, and network details with ease.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install ip-finder-client
9
+ ```
10
+
11
+ ```bash
12
+ yarn add ip-finder-client
13
+ ```
14
+
15
+ ```bash
16
+ pnpm add ip-finder-client
17
+ ```
18
+
19
+ ## Quick Start
20
+
21
+ ```typescript
22
+ import { IPFinder } from 'ip-finder-client';
23
+
24
+ const client = new IPFinder({
25
+ apiKey: 'your-api-key',
26
+ baseUrl: 'https://api.your-domain.com' // Your API endpoint
27
+ });
28
+
29
+ // Look up an IP address
30
+ const result = await client.lookup('8.8.8.8');
31
+
32
+ console.log(result.geo?.country); // "United States"
33
+ console.log(result.geo?.city); // "Mountain View"
34
+ console.log(result.asn?.organization); // "Google LLC"
35
+ ```
36
+
37
+ ## Configuration
38
+
39
+ ```typescript
40
+ const client = new IPFinder({
41
+ // Required: Your API key
42
+ apiKey: 'your-api-key',
43
+
44
+ // Optional: API base URL (defaults to production)
45
+ baseUrl: 'https://api.your-domain.com',
46
+
47
+ // Optional: Request timeout in ms (default: 10000)
48
+ timeout: 5000,
49
+
50
+ // Optional: Number of retries for failed requests (default: 2)
51
+ retries: 3,
52
+ });
53
+ ```
54
+
55
+ ## API Reference
56
+
57
+ ### `lookup(ip: string): Promise<IPLookupResult>`
58
+
59
+ Look up information for a single IP address.
60
+
61
+ ```typescript
62
+ const result = await client.lookup('8.8.8.8');
63
+
64
+ // Result structure:
65
+ {
66
+ ip: '8.8.8.8',
67
+ geo: {
68
+ city: 'Mountain View',
69
+ region: 'California',
70
+ regionCode: 'CA',
71
+ country: 'United States',
72
+ countryCode: 'US',
73
+ continent: 'North America',
74
+ continentCode: 'NA',
75
+ latitude: 37.4056,
76
+ longitude: -122.0775,
77
+ timezone: 'America/Los_Angeles',
78
+ postalCode: '94043'
79
+ },
80
+ asn: {
81
+ asn: 15169,
82
+ organization: 'Google LLC',
83
+ isp: 'Google LLC'
84
+ },
85
+ network: {
86
+ cidr: '8.8.8.0/24',
87
+ startIp: '8.8.8.0',
88
+ endIp: '8.8.8.255',
89
+ type: 'assigned',
90
+ registry: 'arin'
91
+ },
92
+ version: 4
93
+ }
94
+ ```
95
+
96
+ ### `lookupBatch(ips: string[]): Promise<(IPLookupResult | IPFinderError)[]>`
97
+
98
+ Look up multiple IP addresses in parallel.
99
+
100
+ ```typescript
101
+ const results = await client.lookupBatch(['8.8.8.8', '1.1.1.1', '9.9.9.9']);
102
+
103
+ results.forEach(result => {
104
+ if (result instanceof IPFinderError) {
105
+ console.error(`Error: ${result.message}`);
106
+ } else {
107
+ console.log(`${result.ip}: ${result.geo?.country}`);
108
+ }
109
+ });
110
+ ```
111
+
112
+ ### `healthCheck(): Promise<boolean>`
113
+
114
+ Check if the API is healthy and reachable.
115
+
116
+ ```typescript
117
+ const isHealthy = await client.healthCheck();
118
+ console.log(isHealthy ? 'API is up!' : 'API is down');
119
+ ```
120
+
121
+ ### Rate Limiting
122
+
123
+ Rate limit information is available after each request:
124
+
125
+ ```typescript
126
+ await client.lookup('8.8.8.8');
127
+
128
+ console.log(client.rateLimit);
129
+ // { limit: 1000, remaining: 999, reset: 1703980800 }
130
+ ```
131
+
132
+ ## Error Handling
133
+
134
+ ```typescript
135
+ import { IPFinder, IPFinderError } from 'ip-finder-client';
136
+
137
+ try {
138
+ const result = await client.lookup('invalid-ip');
139
+ } catch (error) {
140
+ if (error instanceof IPFinderError) {
141
+ console.error(`API Error: ${error.message}`);
142
+ console.error(`Status Code: ${error.statusCode}`);
143
+ console.error(`Error Code: ${error.code}`);
144
+ }
145
+ }
146
+ ```
147
+
148
+ ### Error Codes
149
+
150
+ | Code | Description |
151
+ |------|-------------|
152
+ | `TIMEOUT` | Request timed out |
153
+ | `NETWORK_ERROR` | Network connectivity issue |
154
+ | `BATCH_ERROR` | Error during batch lookup |
155
+
156
+ ### HTTP Status Codes
157
+
158
+ | Status | Description |
159
+ |--------|-------------|
160
+ | 400 | Invalid IP address format |
161
+ | 401 | Invalid or missing API key |
162
+ | 404 | IP not found in database |
163
+ | 429 | Rate limit exceeded |
164
+ | 500 | Server error |
165
+
166
+ ## CommonJS Usage
167
+
168
+ ```javascript
169
+ const { IPFinder } = require('ip-finder-client');
170
+
171
+ const client = new IPFinder({ apiKey: 'your-api-key' });
172
+ ```
173
+
174
+ ## Factory Function
175
+
176
+ Alternatively, use the factory function:
177
+
178
+ ```typescript
179
+ import { createClient } from 'ip-finder-client';
180
+
181
+ const client = createClient({
182
+ apiKey: 'your-api-key',
183
+ baseUrl: 'https://api.your-domain.com'
184
+ });
185
+ ```
186
+
187
+ ## TypeScript Support
188
+
189
+ Full TypeScript support with exported types:
190
+
191
+ ```typescript
192
+ import type {
193
+ IPFinderConfig,
194
+ IPLookupResult,
195
+ GeoLocation,
196
+ ASNInfo,
197
+ NetworkInfo,
198
+ RateLimitInfo
199
+ } from 'ip-finder-client';
200
+ ```
201
+
202
+ ## Requirements
203
+
204
+ - Node.js 18+ (uses native `fetch`)
205
+
206
+ ## License
207
+
208
+ MIT
209
+
@@ -0,0 +1,96 @@
1
+ import type { IPFinderConfig, IPLookupResult, APIError, RateLimitInfo, GeoLocation, ASNInfo, NetworkInfo } from './types';
2
+ export type { IPFinderConfig, IPLookupResult, APIError, RateLimitInfo, GeoLocation, ASNInfo, NetworkInfo, };
3
+ /**
4
+ * Custom error class for IP Finder API errors
5
+ */
6
+ export declare class IPFinderError extends Error {
7
+ readonly statusCode: number;
8
+ readonly code?: string;
9
+ constructor(message: string, statusCode: number, code?: string);
10
+ }
11
+ /**
12
+ * IP Finder client for looking up IP address information
13
+ *
14
+ * @example
15
+ * ```ts
16
+ * import { IPFinder } from 'ip-finder-client';
17
+ *
18
+ * const client = new IPFinder({ apiKey: 'your-api-key' });
19
+ * const result = await client.lookup('8.8.8.8');
20
+ * console.log(result.geo?.country); // "United States"
21
+ * ```
22
+ */
23
+ export declare class IPFinder {
24
+ private readonly apiKey;
25
+ private readonly baseUrl;
26
+ private readonly timeout;
27
+ private readonly retries;
28
+ /** Rate limit info from the last request */
29
+ rateLimit?: RateLimitInfo;
30
+ constructor(config: IPFinderConfig);
31
+ /**
32
+ * Look up information for an IP address
33
+ *
34
+ * @param ip - IPv4 or IPv6 address to look up
35
+ * @returns IP lookup result with geo, ASN, and network information
36
+ * @throws {IPFinderError} When the API returns an error
37
+ *
38
+ * @example
39
+ * ```ts
40
+ * const result = await client.lookup('8.8.8.8');
41
+ * console.log(result.geo?.city); // "Mountain View"
42
+ * console.log(result.asn?.organization); // "Google LLC"
43
+ * ```
44
+ */
45
+ lookup(ip: string): Promise<IPLookupResult>;
46
+ /**
47
+ * Look up the current request's IP address (useful for "what's my IP" functionality)
48
+ * Note: This requires your API to support a special endpoint for this
49
+ *
50
+ * @returns IP lookup result for the requesting IP
51
+ */
52
+ lookupSelf(): Promise<IPLookupResult>;
53
+ /**
54
+ * Batch lookup multiple IP addresses
55
+ *
56
+ * @param ips - Array of IP addresses to look up
57
+ * @returns Array of lookup results (or errors for failed lookups)
58
+ *
59
+ * @example
60
+ * ```ts
61
+ * const results = await client.lookupBatch(['8.8.8.8', '1.1.1.1']);
62
+ * results.forEach(r => console.log(r.ip, r.geo?.country));
63
+ * ```
64
+ */
65
+ lookupBatch(ips: string[]): Promise<(IPLookupResult | IPFinderError)[]>;
66
+ /**
67
+ * Check if the API is healthy and reachable
68
+ *
69
+ * @returns true if the API is healthy
70
+ */
71
+ healthCheck(): Promise<boolean>;
72
+ private fetch;
73
+ private parseRateLimitHeaders;
74
+ private sleep;
75
+ }
76
+ /**
77
+ * Create a new IP Finder client instance
78
+ *
79
+ * @param config - Client configuration
80
+ * @returns New IPFinder instance
81
+ *
82
+ * @example
83
+ * ```ts
84
+ * import { createClient } from 'ip-finder-client';
85
+ *
86
+ * const client = createClient({
87
+ * apiKey: 'your-api-key',
88
+ * baseUrl: 'https://api.example.com'
89
+ * });
90
+ *
91
+ * const result = await client.lookup('8.8.8.8');
92
+ * ```
93
+ */
94
+ export declare function createClient(config: IPFinderConfig): IPFinder;
95
+ export default IPFinder;
96
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,cAAc,EACd,cAAc,EACd,QAAQ,EACR,aAAa,EACb,WAAW,EACX,OAAO,EACP,WAAW,EACZ,MAAM,SAAS,CAAC;AAGjB,YAAY,EACV,cAAc,EACd,cAAc,EACd,QAAQ,EACR,aAAa,EACb,WAAW,EACX,OAAO,EACP,WAAW,GACZ,CAAC;AAMF;;GAEG;AACH,qBAAa,aAAc,SAAQ,KAAK;IACtC,SAAgB,UAAU,EAAE,MAAM,CAAC;IACnC,SAAgB,IAAI,CAAC,EAAE,MAAM,CAAC;gBAElB,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM;CAO/D;AAED;;;;;;;;;;;GAWG;AACH,qBAAa,QAAQ;IACnB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IAEjC,4CAA4C;IACrC,SAAS,CAAC,EAAE,aAAa,CAAC;gBAErB,MAAM,EAAE,cAAc;IAWlC;;;;;;;;;;;;;OAaG;IACG,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IA+BjD;;;;;OAKG;IACG,UAAU,IAAI,OAAO,CAAC,cAAc,CAAC;IAO3C;;;;;;;;;;;OAWG;IACG,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,CAAC,cAAc,GAAG,aAAa,CAAC,EAAE,CAAC;IAqB7E;;;;OAIG;IACG,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;YAgBvB,KAAK;IAuDnB,OAAO,CAAC,qBAAqB;IAc7B,OAAO,CAAC,KAAK;CAGd;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,cAAc,GAAG,QAAQ,CAE7D;AAGD,eAAe,QAAQ,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,223 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var src_exports = {};
22
+ __export(src_exports, {
23
+ IPFinder: () => IPFinder,
24
+ IPFinderError: () => IPFinderError,
25
+ createClient: () => createClient,
26
+ default: () => src_default
27
+ });
28
+ module.exports = __toCommonJS(src_exports);
29
+ var DEFAULT_BASE_URL = "https://your-api-domain.com";
30
+ var DEFAULT_TIMEOUT = 1e4;
31
+ var DEFAULT_RETRIES = 2;
32
+ var IPFinderError = class _IPFinderError extends Error {
33
+ statusCode;
34
+ code;
35
+ constructor(message, statusCode, code) {
36
+ super(message);
37
+ this.name = "IPFinderError";
38
+ this.statusCode = statusCode;
39
+ this.code = code;
40
+ Object.setPrototypeOf(this, _IPFinderError.prototype);
41
+ }
42
+ };
43
+ var IPFinder = class {
44
+ apiKey;
45
+ baseUrl;
46
+ timeout;
47
+ retries;
48
+ /** Rate limit info from the last request */
49
+ rateLimit;
50
+ constructor(config) {
51
+ if (!config.apiKey) {
52
+ throw new Error("API key is required");
53
+ }
54
+ this.apiKey = config.apiKey;
55
+ this.baseUrl = (config.baseUrl || DEFAULT_BASE_URL).replace(/\/$/, "");
56
+ this.timeout = config.timeout ?? DEFAULT_TIMEOUT;
57
+ this.retries = config.retries ?? DEFAULT_RETRIES;
58
+ }
59
+ /**
60
+ * Look up information for an IP address
61
+ *
62
+ * @param ip - IPv4 or IPv6 address to look up
63
+ * @returns IP lookup result with geo, ASN, and network information
64
+ * @throws {IPFinderError} When the API returns an error
65
+ *
66
+ * @example
67
+ * ```ts
68
+ * const result = await client.lookup('8.8.8.8');
69
+ * console.log(result.geo?.city); // "Mountain View"
70
+ * console.log(result.asn?.organization); // "Google LLC"
71
+ * ```
72
+ */
73
+ async lookup(ip) {
74
+ if (!ip || typeof ip !== "string") {
75
+ throw new Error("IP address is required and must be a string");
76
+ }
77
+ const url = `${this.baseUrl}/v1/ip/${encodeURIComponent(ip.trim())}`;
78
+ let lastError = null;
79
+ for (let attempt = 0; attempt <= this.retries; attempt++) {
80
+ try {
81
+ const response = await this.fetch(url);
82
+ return response;
83
+ } catch (err) {
84
+ lastError = err;
85
+ if (err instanceof IPFinderError && err.statusCode >= 400 && err.statusCode < 500) {
86
+ throw err;
87
+ }
88
+ if (attempt < this.retries) {
89
+ await this.sleep(Math.pow(2, attempt) * 100);
90
+ }
91
+ }
92
+ }
93
+ throw lastError || new Error("Request failed");
94
+ }
95
+ /**
96
+ * Look up the current request's IP address (useful for "what's my IP" functionality)
97
+ * Note: This requires your API to support a special endpoint for this
98
+ *
99
+ * @returns IP lookup result for the requesting IP
100
+ */
101
+ async lookupSelf() {
102
+ const url = `${this.baseUrl}/v1/ip/me`;
103
+ const response = await this.fetch(url);
104
+ return response;
105
+ }
106
+ /**
107
+ * Batch lookup multiple IP addresses
108
+ *
109
+ * @param ips - Array of IP addresses to look up
110
+ * @returns Array of lookup results (or errors for failed lookups)
111
+ *
112
+ * @example
113
+ * ```ts
114
+ * const results = await client.lookupBatch(['8.8.8.8', '1.1.1.1']);
115
+ * results.forEach(r => console.log(r.ip, r.geo?.country));
116
+ * ```
117
+ */
118
+ async lookupBatch(ips) {
119
+ const results = await Promise.allSettled(
120
+ ips.map((ip) => this.lookup(ip))
121
+ );
122
+ return results.map((result, index) => {
123
+ if (result.status === "fulfilled") {
124
+ return result.value;
125
+ }
126
+ const err = result.reason;
127
+ if (err instanceof IPFinderError) {
128
+ return err;
129
+ }
130
+ return new IPFinderError(
131
+ err?.message || "Unknown error",
132
+ 0,
133
+ "BATCH_ERROR"
134
+ );
135
+ });
136
+ }
137
+ /**
138
+ * Check if the API is healthy and reachable
139
+ *
140
+ * @returns true if the API is healthy
141
+ */
142
+ async healthCheck() {
143
+ try {
144
+ const controller = new AbortController();
145
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
146
+ const response = await fetch(`${this.baseUrl}/healthz`, {
147
+ signal: controller.signal
148
+ });
149
+ clearTimeout(timeoutId);
150
+ return response.ok;
151
+ } catch {
152
+ return false;
153
+ }
154
+ }
155
+ async fetch(url) {
156
+ const controller = new AbortController();
157
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
158
+ try {
159
+ const response = await fetch(url, {
160
+ method: "GET",
161
+ headers: {
162
+ "X-API-Key": this.apiKey,
163
+ "Accept": "application/json",
164
+ "User-Agent": "ip-finder-client/1.0.0"
165
+ },
166
+ signal: controller.signal
167
+ });
168
+ clearTimeout(timeoutId);
169
+ this.parseRateLimitHeaders(response.headers);
170
+ if (!response.ok) {
171
+ let errorBody = null;
172
+ try {
173
+ errorBody = await response.json();
174
+ } catch {
175
+ }
176
+ throw new IPFinderError(
177
+ errorBody?.error || `HTTP ${response.status}`,
178
+ response.status,
179
+ errorBody?.code
180
+ );
181
+ }
182
+ const data = await response.json();
183
+ return data;
184
+ } catch (err) {
185
+ clearTimeout(timeoutId);
186
+ if (err instanceof IPFinderError) {
187
+ throw err;
188
+ }
189
+ if (err instanceof Error) {
190
+ if (err.name === "AbortError") {
191
+ throw new IPFinderError("Request timeout", 408, "TIMEOUT");
192
+ }
193
+ throw new IPFinderError(err.message, 0, "NETWORK_ERROR");
194
+ }
195
+ throw new IPFinderError("Unknown error", 0, "UNKNOWN");
196
+ }
197
+ }
198
+ parseRateLimitHeaders(headers) {
199
+ const limit = headers.get("X-RateLimit-Limit");
200
+ const remaining = headers.get("X-RateLimit-Remaining");
201
+ const reset = headers.get("X-RateLimit-Reset");
202
+ if (limit && remaining && reset) {
203
+ this.rateLimit = {
204
+ limit: parseInt(limit, 10),
205
+ remaining: parseInt(remaining, 10),
206
+ reset: parseInt(reset, 10)
207
+ };
208
+ }
209
+ }
210
+ sleep(ms) {
211
+ return new Promise((resolve) => setTimeout(resolve, ms));
212
+ }
213
+ };
214
+ function createClient(config) {
215
+ return new IPFinder(config);
216
+ }
217
+ var src_default = IPFinder;
218
+ // Annotate the CommonJS export names for ESM import in node:
219
+ 0 && (module.exports = {
220
+ IPFinder,
221
+ IPFinderError,
222
+ createClient
223
+ });
package/dist/index.mjs ADDED
@@ -0,0 +1,196 @@
1
+ // src/index.ts
2
+ var DEFAULT_BASE_URL = "https://your-api-domain.com";
3
+ var DEFAULT_TIMEOUT = 1e4;
4
+ var DEFAULT_RETRIES = 2;
5
+ var IPFinderError = class _IPFinderError extends Error {
6
+ statusCode;
7
+ code;
8
+ constructor(message, statusCode, code) {
9
+ super(message);
10
+ this.name = "IPFinderError";
11
+ this.statusCode = statusCode;
12
+ this.code = code;
13
+ Object.setPrototypeOf(this, _IPFinderError.prototype);
14
+ }
15
+ };
16
+ var IPFinder = class {
17
+ apiKey;
18
+ baseUrl;
19
+ timeout;
20
+ retries;
21
+ /** Rate limit info from the last request */
22
+ rateLimit;
23
+ constructor(config) {
24
+ if (!config.apiKey) {
25
+ throw new Error("API key is required");
26
+ }
27
+ this.apiKey = config.apiKey;
28
+ this.baseUrl = (config.baseUrl || DEFAULT_BASE_URL).replace(/\/$/, "");
29
+ this.timeout = config.timeout ?? DEFAULT_TIMEOUT;
30
+ this.retries = config.retries ?? DEFAULT_RETRIES;
31
+ }
32
+ /**
33
+ * Look up information for an IP address
34
+ *
35
+ * @param ip - IPv4 or IPv6 address to look up
36
+ * @returns IP lookup result with geo, ASN, and network information
37
+ * @throws {IPFinderError} When the API returns an error
38
+ *
39
+ * @example
40
+ * ```ts
41
+ * const result = await client.lookup('8.8.8.8');
42
+ * console.log(result.geo?.city); // "Mountain View"
43
+ * console.log(result.asn?.organization); // "Google LLC"
44
+ * ```
45
+ */
46
+ async lookup(ip) {
47
+ if (!ip || typeof ip !== "string") {
48
+ throw new Error("IP address is required and must be a string");
49
+ }
50
+ const url = `${this.baseUrl}/v1/ip/${encodeURIComponent(ip.trim())}`;
51
+ let lastError = null;
52
+ for (let attempt = 0; attempt <= this.retries; attempt++) {
53
+ try {
54
+ const response = await this.fetch(url);
55
+ return response;
56
+ } catch (err) {
57
+ lastError = err;
58
+ if (err instanceof IPFinderError && err.statusCode >= 400 && err.statusCode < 500) {
59
+ throw err;
60
+ }
61
+ if (attempt < this.retries) {
62
+ await this.sleep(Math.pow(2, attempt) * 100);
63
+ }
64
+ }
65
+ }
66
+ throw lastError || new Error("Request failed");
67
+ }
68
+ /**
69
+ * Look up the current request's IP address (useful for "what's my IP" functionality)
70
+ * Note: This requires your API to support a special endpoint for this
71
+ *
72
+ * @returns IP lookup result for the requesting IP
73
+ */
74
+ async lookupSelf() {
75
+ const url = `${this.baseUrl}/v1/ip/me`;
76
+ const response = await this.fetch(url);
77
+ return response;
78
+ }
79
+ /**
80
+ * Batch lookup multiple IP addresses
81
+ *
82
+ * @param ips - Array of IP addresses to look up
83
+ * @returns Array of lookup results (or errors for failed lookups)
84
+ *
85
+ * @example
86
+ * ```ts
87
+ * const results = await client.lookupBatch(['8.8.8.8', '1.1.1.1']);
88
+ * results.forEach(r => console.log(r.ip, r.geo?.country));
89
+ * ```
90
+ */
91
+ async lookupBatch(ips) {
92
+ const results = await Promise.allSettled(
93
+ ips.map((ip) => this.lookup(ip))
94
+ );
95
+ return results.map((result, index) => {
96
+ if (result.status === "fulfilled") {
97
+ return result.value;
98
+ }
99
+ const err = result.reason;
100
+ if (err instanceof IPFinderError) {
101
+ return err;
102
+ }
103
+ return new IPFinderError(
104
+ err?.message || "Unknown error",
105
+ 0,
106
+ "BATCH_ERROR"
107
+ );
108
+ });
109
+ }
110
+ /**
111
+ * Check if the API is healthy and reachable
112
+ *
113
+ * @returns true if the API is healthy
114
+ */
115
+ async healthCheck() {
116
+ try {
117
+ const controller = new AbortController();
118
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
119
+ const response = await fetch(`${this.baseUrl}/healthz`, {
120
+ signal: controller.signal
121
+ });
122
+ clearTimeout(timeoutId);
123
+ return response.ok;
124
+ } catch {
125
+ return false;
126
+ }
127
+ }
128
+ async fetch(url) {
129
+ const controller = new AbortController();
130
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
131
+ try {
132
+ const response = await fetch(url, {
133
+ method: "GET",
134
+ headers: {
135
+ "X-API-Key": this.apiKey,
136
+ "Accept": "application/json",
137
+ "User-Agent": "ip-finder-client/1.0.0"
138
+ },
139
+ signal: controller.signal
140
+ });
141
+ clearTimeout(timeoutId);
142
+ this.parseRateLimitHeaders(response.headers);
143
+ if (!response.ok) {
144
+ let errorBody = null;
145
+ try {
146
+ errorBody = await response.json();
147
+ } catch {
148
+ }
149
+ throw new IPFinderError(
150
+ errorBody?.error || `HTTP ${response.status}`,
151
+ response.status,
152
+ errorBody?.code
153
+ );
154
+ }
155
+ const data = await response.json();
156
+ return data;
157
+ } catch (err) {
158
+ clearTimeout(timeoutId);
159
+ if (err instanceof IPFinderError) {
160
+ throw err;
161
+ }
162
+ if (err instanceof Error) {
163
+ if (err.name === "AbortError") {
164
+ throw new IPFinderError("Request timeout", 408, "TIMEOUT");
165
+ }
166
+ throw new IPFinderError(err.message, 0, "NETWORK_ERROR");
167
+ }
168
+ throw new IPFinderError("Unknown error", 0, "UNKNOWN");
169
+ }
170
+ }
171
+ parseRateLimitHeaders(headers) {
172
+ const limit = headers.get("X-RateLimit-Limit");
173
+ const remaining = headers.get("X-RateLimit-Remaining");
174
+ const reset = headers.get("X-RateLimit-Reset");
175
+ if (limit && remaining && reset) {
176
+ this.rateLimit = {
177
+ limit: parseInt(limit, 10),
178
+ remaining: parseInt(remaining, 10),
179
+ reset: parseInt(reset, 10)
180
+ };
181
+ }
182
+ }
183
+ sleep(ms) {
184
+ return new Promise((resolve) => setTimeout(resolve, ms));
185
+ }
186
+ };
187
+ function createClient(config) {
188
+ return new IPFinder(config);
189
+ }
190
+ var src_default = IPFinder;
191
+ export {
192
+ IPFinder,
193
+ IPFinderError,
194
+ createClient,
195
+ src_default as default
196
+ };
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Configuration options for the IP Finder client
3
+ */
4
+ export interface IPFinderConfig {
5
+ /** Your API key for authentication */
6
+ apiKey: string;
7
+ /** Base URL of the IP Finder API (optional, defaults to production) */
8
+ baseUrl?: string;
9
+ /** Request timeout in milliseconds (default: 10000) */
10
+ timeout?: number;
11
+ /** Number of retry attempts for failed requests (default: 2) */
12
+ retries?: number;
13
+ }
14
+ /**
15
+ * Geographic location information
16
+ */
17
+ export interface GeoLocation {
18
+ city?: string;
19
+ region?: string;
20
+ regionCode?: string;
21
+ country?: string;
22
+ countryCode?: string;
23
+ continent?: string;
24
+ continentCode?: string;
25
+ latitude?: number;
26
+ longitude?: number;
27
+ timezone?: string;
28
+ postalCode?: string;
29
+ }
30
+ /**
31
+ * ASN (Autonomous System Number) information
32
+ */
33
+ export interface ASNInfo {
34
+ asn?: number;
35
+ organization?: string;
36
+ isp?: string;
37
+ }
38
+ /**
39
+ * Network allocation information
40
+ */
41
+ export interface NetworkInfo {
42
+ cidr?: string;
43
+ startIp?: string;
44
+ endIp?: string;
45
+ type?: string;
46
+ registry?: string;
47
+ registryCountry?: string;
48
+ allocatedDate?: string;
49
+ }
50
+ /**
51
+ * Complete IP lookup result
52
+ */
53
+ export interface IPLookupResult {
54
+ ip: string;
55
+ geo?: GeoLocation;
56
+ asn?: ASNInfo;
57
+ network?: NetworkInfo;
58
+ isPrivate?: boolean;
59
+ isReserved?: boolean;
60
+ version?: 4 | 6;
61
+ }
62
+ /**
63
+ * Error response from the API
64
+ */
65
+ export interface APIError {
66
+ error: string;
67
+ code?: string;
68
+ statusCode: number;
69
+ }
70
+ /**
71
+ * Rate limit information returned in response headers
72
+ */
73
+ export interface RateLimitInfo {
74
+ limit: number;
75
+ remaining: number;
76
+ reset: number;
77
+ }
78
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,sCAAsC;IACtC,MAAM,EAAE,MAAM,CAAC;IACf,uEAAuE;IACvE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,uDAAuD;IACvD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gEAAgE;IAChE,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,OAAO;IACtB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,CAAC,EAAE,WAAW,CAAC;IAClB,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,OAAO,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;CACf"}
package/package.json ADDED
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "ip-finder-client",
3
+ "version": "1.0.0",
4
+ "description": "Lightweight client SDK for IP Finder API - Get IP geolocation, ASN, and network information",
5
+ "main": "dist/index.js",
6
+ "module": "dist/index.mjs",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "require": "./dist/index.js",
11
+ "import": "./dist/index.mjs",
12
+ "types": "./dist/index.d.ts"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist"
17
+ ],
18
+ "scripts": {
19
+ "build": "npm run build:cjs && npm run build:esm && npm run build:types",
20
+ "build:cjs": "esbuild src/index.ts --bundle --platform=node --target=node18 --outfile=dist/index.js --format=cjs",
21
+ "build:esm": "esbuild src/index.ts --bundle --platform=node --target=node18 --outfile=dist/index.mjs --format=esm",
22
+ "build:types": "tsc --emitDeclarationOnly --declaration --outDir dist",
23
+ "prepublishOnly": "npm run build",
24
+ "test": "node test/test.js"
25
+ },
26
+ "keywords": [
27
+ "ip",
28
+ "geolocation",
29
+ "ip-lookup",
30
+ "geoip",
31
+ "asn",
32
+ "whois",
33
+ "ip-address",
34
+ "network",
35
+ "ipv4",
36
+ "ipv6"
37
+ ],
38
+ "author": "",
39
+ "license": "MIT",
40
+ "repository": {
41
+ "type": "git",
42
+ "url": ""
43
+ },
44
+ "engines": {
45
+ "node": ">=18.0.0"
46
+ },
47
+ "devDependencies": {
48
+ "@types/node": "^25.0.3",
49
+ "esbuild": "^0.20.0",
50
+ "typescript": "^5.3.0"
51
+ }
52
+ }