@whatsmyfyi/sdk 0.1.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 +61 -0
- package/dist/index.d.mts +63 -0
- package/dist/index.d.ts +63 -0
- package/dist/index.js +99 -0
- package/dist/index.mjs +73 -0
- package/package.json +28 -0
package/README.md
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# @whatsmyfyi/sdk
|
|
2
|
+
|
|
3
|
+
Official TypeScript SDK for the [whatsmy.fyi](https://whatsmy.fyi) IP geolocation API.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @whatsmyfyi/sdk
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import { WhatsMyFyi } from '@whatsmyfyi/sdk'
|
|
15
|
+
|
|
16
|
+
const client = new WhatsMyFyi({ apiKey: 'wmf_your_key_here' })
|
|
17
|
+
|
|
18
|
+
// Full IP response
|
|
19
|
+
const data = await client.ip.lookup()
|
|
20
|
+
console.log(data.ip) // "203.0.113.42"
|
|
21
|
+
console.log(data.city) // "Amsterdam"
|
|
22
|
+
console.log(data.country) // "NL"
|
|
23
|
+
|
|
24
|
+
// Quick helpers
|
|
25
|
+
const ip = await client.ip.address() // "203.0.113.42"
|
|
26
|
+
const location = await client.ip.location() // { city, country, countryCode, lat, lng }
|
|
27
|
+
const org = await client.ip.org() // { asn, name }
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Get an API Key
|
|
31
|
+
|
|
32
|
+
Sign up at [whatsmy.fyi/signup](https://whatsmy.fyi/signup) and create a free API key in the dashboard. Free tier includes 10,000 requests/day.
|
|
33
|
+
|
|
34
|
+
## Features
|
|
35
|
+
|
|
36
|
+
- TypeScript-first — full type definitions included
|
|
37
|
+
- Zero dependencies
|
|
38
|
+
- Automatic retry on 5xx errors (max 3 attempts, exponential backoff)
|
|
39
|
+
- Works in Node.js, Deno, Bun, and browser (fetch-based)
|
|
40
|
+
- Throws `WhatsMyFyiError` with `code`, `message`, and `status` fields
|
|
41
|
+
|
|
42
|
+
## Error Handling
|
|
43
|
+
|
|
44
|
+
```typescript
|
|
45
|
+
import { WhatsMyFyi, WhatsMyFyiError } from '@whatsmyfyi/sdk'
|
|
46
|
+
|
|
47
|
+
const client = new WhatsMyFyi({ apiKey: 'wmf_...' })
|
|
48
|
+
|
|
49
|
+
try {
|
|
50
|
+
const data = await client.ip.lookup()
|
|
51
|
+
} catch (err) {
|
|
52
|
+
if (err instanceof WhatsMyFyiError) {
|
|
53
|
+
console.error(err.code) // e.g. "rate_limit_exceeded"
|
|
54
|
+
console.error(err.status) // HTTP status code
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## License
|
|
60
|
+
|
|
61
|
+
MIT — [whatsmy.fyi](https://whatsmy.fyi)
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
interface IpResponse {
|
|
2
|
+
status: 'success';
|
|
3
|
+
ip: string | null;
|
|
4
|
+
ipv6: string | null;
|
|
5
|
+
city?: string | null;
|
|
6
|
+
region?: string | null;
|
|
7
|
+
region_code?: string | null;
|
|
8
|
+
country?: string | null;
|
|
9
|
+
country_name?: string | null;
|
|
10
|
+
continent?: string | null;
|
|
11
|
+
latitude?: number | null;
|
|
12
|
+
longitude?: number | null;
|
|
13
|
+
timezone?: string | null;
|
|
14
|
+
timezone_offset?: number | null;
|
|
15
|
+
postal_code?: string | null;
|
|
16
|
+
asn?: number | null;
|
|
17
|
+
org?: string | null;
|
|
18
|
+
as?: string | null;
|
|
19
|
+
is_eu?: boolean | null;
|
|
20
|
+
currency?: string | null;
|
|
21
|
+
http_protocol?: string | null;
|
|
22
|
+
tls_version?: string | null;
|
|
23
|
+
tls_cipher?: string | null;
|
|
24
|
+
rtt?: number | null;
|
|
25
|
+
colo?: string | null;
|
|
26
|
+
proxy?: boolean | null;
|
|
27
|
+
hosting?: boolean | null;
|
|
28
|
+
}
|
|
29
|
+
interface LocationResult {
|
|
30
|
+
city: string | null;
|
|
31
|
+
country: string | null;
|
|
32
|
+
countryCode: string | null;
|
|
33
|
+
lat: number | null;
|
|
34
|
+
lng: number | null;
|
|
35
|
+
}
|
|
36
|
+
interface OrgResult {
|
|
37
|
+
asn: number | null;
|
|
38
|
+
name: string | null;
|
|
39
|
+
}
|
|
40
|
+
interface WhatsMyFyiOptions {
|
|
41
|
+
apiKey: string;
|
|
42
|
+
baseUrl?: string;
|
|
43
|
+
}
|
|
44
|
+
declare class WhatsMyFyiError extends Error {
|
|
45
|
+
readonly code: string;
|
|
46
|
+
readonly status: number;
|
|
47
|
+
constructor(code: string, message: string, status: number);
|
|
48
|
+
}
|
|
49
|
+
declare class IpClient {
|
|
50
|
+
private readonly apiKey;
|
|
51
|
+
private readonly baseUrl;
|
|
52
|
+
constructor(apiKey: string, baseUrl: string);
|
|
53
|
+
lookup(): Promise<IpResponse>;
|
|
54
|
+
address(): Promise<string | null>;
|
|
55
|
+
location(): Promise<LocationResult>;
|
|
56
|
+
org(): Promise<OrgResult>;
|
|
57
|
+
}
|
|
58
|
+
declare class WhatsMyFyi {
|
|
59
|
+
readonly ip: IpClient;
|
|
60
|
+
constructor({ apiKey, baseUrl }: WhatsMyFyiOptions);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export { type IpResponse, type LocationResult, type OrgResult, WhatsMyFyi, WhatsMyFyiError, type WhatsMyFyiOptions };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
interface IpResponse {
|
|
2
|
+
status: 'success';
|
|
3
|
+
ip: string | null;
|
|
4
|
+
ipv6: string | null;
|
|
5
|
+
city?: string | null;
|
|
6
|
+
region?: string | null;
|
|
7
|
+
region_code?: string | null;
|
|
8
|
+
country?: string | null;
|
|
9
|
+
country_name?: string | null;
|
|
10
|
+
continent?: string | null;
|
|
11
|
+
latitude?: number | null;
|
|
12
|
+
longitude?: number | null;
|
|
13
|
+
timezone?: string | null;
|
|
14
|
+
timezone_offset?: number | null;
|
|
15
|
+
postal_code?: string | null;
|
|
16
|
+
asn?: number | null;
|
|
17
|
+
org?: string | null;
|
|
18
|
+
as?: string | null;
|
|
19
|
+
is_eu?: boolean | null;
|
|
20
|
+
currency?: string | null;
|
|
21
|
+
http_protocol?: string | null;
|
|
22
|
+
tls_version?: string | null;
|
|
23
|
+
tls_cipher?: string | null;
|
|
24
|
+
rtt?: number | null;
|
|
25
|
+
colo?: string | null;
|
|
26
|
+
proxy?: boolean | null;
|
|
27
|
+
hosting?: boolean | null;
|
|
28
|
+
}
|
|
29
|
+
interface LocationResult {
|
|
30
|
+
city: string | null;
|
|
31
|
+
country: string | null;
|
|
32
|
+
countryCode: string | null;
|
|
33
|
+
lat: number | null;
|
|
34
|
+
lng: number | null;
|
|
35
|
+
}
|
|
36
|
+
interface OrgResult {
|
|
37
|
+
asn: number | null;
|
|
38
|
+
name: string | null;
|
|
39
|
+
}
|
|
40
|
+
interface WhatsMyFyiOptions {
|
|
41
|
+
apiKey: string;
|
|
42
|
+
baseUrl?: string;
|
|
43
|
+
}
|
|
44
|
+
declare class WhatsMyFyiError extends Error {
|
|
45
|
+
readonly code: string;
|
|
46
|
+
readonly status: number;
|
|
47
|
+
constructor(code: string, message: string, status: number);
|
|
48
|
+
}
|
|
49
|
+
declare class IpClient {
|
|
50
|
+
private readonly apiKey;
|
|
51
|
+
private readonly baseUrl;
|
|
52
|
+
constructor(apiKey: string, baseUrl: string);
|
|
53
|
+
lookup(): Promise<IpResponse>;
|
|
54
|
+
address(): Promise<string | null>;
|
|
55
|
+
location(): Promise<LocationResult>;
|
|
56
|
+
org(): Promise<OrgResult>;
|
|
57
|
+
}
|
|
58
|
+
declare class WhatsMyFyi {
|
|
59
|
+
readonly ip: IpClient;
|
|
60
|
+
constructor({ apiKey, baseUrl }: WhatsMyFyiOptions);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export { type IpResponse, type LocationResult, type OrgResult, WhatsMyFyi, WhatsMyFyiError, type WhatsMyFyiOptions };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
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 index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
WhatsMyFyi: () => WhatsMyFyi,
|
|
24
|
+
WhatsMyFyiError: () => WhatsMyFyiError
|
|
25
|
+
});
|
|
26
|
+
module.exports = __toCommonJS(index_exports);
|
|
27
|
+
var DEFAULT_BASE_URL = "https://whatsmy.fyi";
|
|
28
|
+
var MAX_RETRIES = 3;
|
|
29
|
+
var WhatsMyFyiError = class extends Error {
|
|
30
|
+
constructor(code, message, status) {
|
|
31
|
+
super(message);
|
|
32
|
+
this.name = "WhatsMyFyiError";
|
|
33
|
+
this.code = code;
|
|
34
|
+
this.status = status;
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
async function fetchWithRetry(url, apiKey, attempt = 1) {
|
|
38
|
+
const res = await fetch(url, {
|
|
39
|
+
headers: { Authorization: `Bearer ${apiKey}` }
|
|
40
|
+
});
|
|
41
|
+
if (res.status >= 500 && attempt < MAX_RETRIES) {
|
|
42
|
+
const delay = Math.pow(2, attempt) * 100;
|
|
43
|
+
await new Promise((r) => setTimeout(r, delay));
|
|
44
|
+
return fetchWithRetry(url, apiKey, attempt + 1);
|
|
45
|
+
}
|
|
46
|
+
if (!res.ok) {
|
|
47
|
+
let code = "internal_error";
|
|
48
|
+
let message = `HTTP ${res.status}`;
|
|
49
|
+
try {
|
|
50
|
+
const body = await res.json();
|
|
51
|
+
if (body.error) code = body.error;
|
|
52
|
+
if (body.message) message = body.message;
|
|
53
|
+
} catch {
|
|
54
|
+
}
|
|
55
|
+
throw new WhatsMyFyiError(code, message, res.status);
|
|
56
|
+
}
|
|
57
|
+
return res.json();
|
|
58
|
+
}
|
|
59
|
+
var IpClient = class {
|
|
60
|
+
constructor(apiKey, baseUrl) {
|
|
61
|
+
this.apiKey = apiKey;
|
|
62
|
+
this.baseUrl = baseUrl;
|
|
63
|
+
}
|
|
64
|
+
async lookup() {
|
|
65
|
+
return fetchWithRetry(`${this.baseUrl}/api/v1/ip`, this.apiKey);
|
|
66
|
+
}
|
|
67
|
+
async address() {
|
|
68
|
+
const data = await this.lookup();
|
|
69
|
+
return data.ip ?? data.ipv6 ?? null;
|
|
70
|
+
}
|
|
71
|
+
async location() {
|
|
72
|
+
const data = await this.lookup();
|
|
73
|
+
return {
|
|
74
|
+
city: data.city ?? null,
|
|
75
|
+
country: data.country_name ?? null,
|
|
76
|
+
countryCode: data.country ?? null,
|
|
77
|
+
lat: data.latitude ?? null,
|
|
78
|
+
lng: data.longitude ?? null
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
async org() {
|
|
82
|
+
const data = await this.lookup();
|
|
83
|
+
return {
|
|
84
|
+
asn: data.asn ?? null,
|
|
85
|
+
name: data.org ?? null
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
var WhatsMyFyi = class {
|
|
90
|
+
constructor({ apiKey, baseUrl = DEFAULT_BASE_URL }) {
|
|
91
|
+
if (!apiKey) throw new Error("apiKey is required");
|
|
92
|
+
this.ip = new IpClient(apiKey, baseUrl.replace(/\/$/, ""));
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
96
|
+
0 && (module.exports = {
|
|
97
|
+
WhatsMyFyi,
|
|
98
|
+
WhatsMyFyiError
|
|
99
|
+
});
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
var DEFAULT_BASE_URL = "https://whatsmy.fyi";
|
|
3
|
+
var MAX_RETRIES = 3;
|
|
4
|
+
var WhatsMyFyiError = class extends Error {
|
|
5
|
+
constructor(code, message, status) {
|
|
6
|
+
super(message);
|
|
7
|
+
this.name = "WhatsMyFyiError";
|
|
8
|
+
this.code = code;
|
|
9
|
+
this.status = status;
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
async function fetchWithRetry(url, apiKey, attempt = 1) {
|
|
13
|
+
const res = await fetch(url, {
|
|
14
|
+
headers: { Authorization: `Bearer ${apiKey}` }
|
|
15
|
+
});
|
|
16
|
+
if (res.status >= 500 && attempt < MAX_RETRIES) {
|
|
17
|
+
const delay = Math.pow(2, attempt) * 100;
|
|
18
|
+
await new Promise((r) => setTimeout(r, delay));
|
|
19
|
+
return fetchWithRetry(url, apiKey, attempt + 1);
|
|
20
|
+
}
|
|
21
|
+
if (!res.ok) {
|
|
22
|
+
let code = "internal_error";
|
|
23
|
+
let message = `HTTP ${res.status}`;
|
|
24
|
+
try {
|
|
25
|
+
const body = await res.json();
|
|
26
|
+
if (body.error) code = body.error;
|
|
27
|
+
if (body.message) message = body.message;
|
|
28
|
+
} catch {
|
|
29
|
+
}
|
|
30
|
+
throw new WhatsMyFyiError(code, message, res.status);
|
|
31
|
+
}
|
|
32
|
+
return res.json();
|
|
33
|
+
}
|
|
34
|
+
var IpClient = class {
|
|
35
|
+
constructor(apiKey, baseUrl) {
|
|
36
|
+
this.apiKey = apiKey;
|
|
37
|
+
this.baseUrl = baseUrl;
|
|
38
|
+
}
|
|
39
|
+
async lookup() {
|
|
40
|
+
return fetchWithRetry(`${this.baseUrl}/api/v1/ip`, this.apiKey);
|
|
41
|
+
}
|
|
42
|
+
async address() {
|
|
43
|
+
const data = await this.lookup();
|
|
44
|
+
return data.ip ?? data.ipv6 ?? null;
|
|
45
|
+
}
|
|
46
|
+
async location() {
|
|
47
|
+
const data = await this.lookup();
|
|
48
|
+
return {
|
|
49
|
+
city: data.city ?? null,
|
|
50
|
+
country: data.country_name ?? null,
|
|
51
|
+
countryCode: data.country ?? null,
|
|
52
|
+
lat: data.latitude ?? null,
|
|
53
|
+
lng: data.longitude ?? null
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
async org() {
|
|
57
|
+
const data = await this.lookup();
|
|
58
|
+
return {
|
|
59
|
+
asn: data.asn ?? null,
|
|
60
|
+
name: data.org ?? null
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
var WhatsMyFyi = class {
|
|
65
|
+
constructor({ apiKey, baseUrl = DEFAULT_BASE_URL }) {
|
|
66
|
+
if (!apiKey) throw new Error("apiKey is required");
|
|
67
|
+
this.ip = new IpClient(apiKey, baseUrl.replace(/\/$/, ""));
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
export {
|
|
71
|
+
WhatsMyFyi,
|
|
72
|
+
WhatsMyFyiError
|
|
73
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@whatsmyfyi/sdk",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Official SDK for the whatsmy.fyi IP geolocation API",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"module": "dist/index.mjs",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.mjs",
|
|
12
|
+
"require": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "tsup src/index.ts --format cjs,esm --dts --clean",
|
|
17
|
+
"typecheck": "tsc --noEmit"
|
|
18
|
+
},
|
|
19
|
+
"files": ["dist", "README.md"],
|
|
20
|
+
"keywords": ["ip", "geolocation", "api", "whatsmy.fyi", "ip-address", "ip-lookup"],
|
|
21
|
+
"author": "whatsmy.fyi",
|
|
22
|
+
"homepage": "https://whatsmy.fyi",
|
|
23
|
+
"license": "MIT",
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"tsup": "^8.5.1",
|
|
26
|
+
"typescript": "^5.0.0"
|
|
27
|
+
}
|
|
28
|
+
}
|