Skip to content

Commit

Permalink
Use named exports and allow getting either IPv4 or IPv6 (#63)
Browse files Browse the repository at this point in the history
  • Loading branch information
Richienb committed Jun 13, 2022
1 parent 7c155f2 commit 4e1d895
Show file tree
Hide file tree
Showing 10 changed files with 174 additions and 103 deletions.
20 changes: 9 additions & 11 deletions browser.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import isIp from 'is-ip';
import {createPublicIp, IpNotFoundError} from './core.js';

export class CancelError extends Error {
constructor() {
Expand All @@ -11,12 +12,7 @@ export class CancelError extends Error {
}
}

export class IpNotFoundError extends Error {
constructor(options) {
super('Could not get the public IP address', options);
this.name = 'IpNotFoundError';
}
}
export {IpNotFoundError} from './core.js';

const defaults = {
timeout: 5000,
Expand Down Expand Up @@ -100,10 +96,12 @@ const queryHttps = (version, options) => {
return promise;
};

const publicIp = {};

publicIp.v4 = options => queryHttps('v4', {...defaults, ...options});
export default createPublicIp(publicIpv4, publicIpv6);

publicIp.v6 = options => queryHttps('v6', {...defaults, ...options});
export function publicIpv4(options) {
return queryHttps('v4', {...defaults, ...options});
}

export default publicIp;
export function publicIpv6(options) {
return queryHttps('v6', {...defaults, ...options});
}
40 changes: 40 additions & 0 deletions core.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import AggregateError from 'aggregate-error'; // Use built-in when targeting Node.js 16

export class IpNotFoundError extends Error {
constructor(options) {
super('Could not get the public IP address', options);
this.name = 'IpNotFoundError';
}
}

export function createPublicIp(publicIpv4, publicIpv6) {
return function publicIp(options) { // eslint-disable-line func-names
const ipv4Promise = publicIpv4(options);
const ipv6Promise = publicIpv6(options);

const promise = (async () => {
try {
const ipv6 = await ipv6Promise;
ipv4Promise.cancel();
return ipv6;
} catch (ipv6Error) {
if (!(ipv6Error instanceof IpNotFoundError)) {
throw ipv6Error;
}

try {
return await ipv4Promise;
} catch (ipv4Error) {
throw new AggregateError([ipv4Error, ipv6Error]);
}
}
})();

promise.cancel = () => {
ipv4Promise.cancel();
ipv6Promise.cancel();
};

return promise;
};
}
76 changes: 45 additions & 31 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ export interface Options {
@example
```
import publicIp from 'public-ip';
import {publicIpv6} from 'public-ip';
await publicIp.v6({
await publicIpv6({
fallbackUrls: [
'https://ifconfig.co/ip'
]
Expand All @@ -45,44 +45,58 @@ export type CancelablePromise<T> = Promise<T> & {
cancel(): void;
};

declare const publicIp: {
/**
Get your public IP address - very fast!
/**
Get your public IP address - very fast!
In Node.js, it queries the DNS records of OpenDNS, Google DNS, and HTTPS services to determine your IP address. In browsers, it uses the excellent [icanhaz](https://github.com/major/icanhaz) and [ipify](https://ipify.org) services through HTTPS.
In Node.js, it queries the DNS records of OpenDNS, Google DNS, and HTTPS services to determine your IP address. In browsers, it uses the excellent [icanhaz](https://github.com/major/icanhaz) and [ipify](https://ipify.org) services through HTTPS.
@returns Your public IPv4 address. A `.cancel()` method is available on the promise, which can be used to cancel the request.
@throws On error or timeout.
@returns Your public IPv6 address or as a fallback, your public IPv4 address. A `.cancel()` method is available on the promise, which can be used to cancel the request.
@throws On error or timeout.
@example
```
import publicIp from 'public-ip';
@example
```
import publicIp from 'public-ip';
console.log(await publicIp.v4());
//=> '46.5.21.123'
```
*/
v4(options?: Options): CancelablePromise<string>;
console.log(await publicIp()); // Falls back to IPv4
//=> 'fe80::200:f8ff:fe21:67cf'
```
*/
export function publicIp(options?: Options): CancelablePromise<string>;

/**
Get your public IP address - very fast!
/**
Get your public IP address - very fast!
In Node.js, it queries the DNS records of OpenDNS, Google DNS, and HTTPS services to determine your IP address. In browsers, it uses the excellent [icanhaz](https://github.com/major/icanhaz) and [ipify](https://ipify.org) services through HTTPS.
In Node.js, it queries the DNS records of OpenDNS, Google DNS, and HTTPS services to determine your IP address. In browsers, it uses the excellent [icanhaz](https://github.com/major/icanhaz) and [ipify](https://ipify.org) services through HTTPS.
@returns Your public IPv6 address. A `.cancel()` method is available on the promise, which can be used to cancel the request.
@throws On error or timeout.
@returns Your public IPv4 address. A `.cancel()` method is available on the promise, which can be used to cancel the request.
@throws On error or timeout.
@example
```
import publicIp from 'public-ip';
@example
```
import {publicIpv4} from 'public-ip';
console.log(await publicIp.v6());
//=> 'fe80::200:f8ff:fe21:67cf'
```
*/
v6(options?: Options): CancelablePromise<string>;
};
console.log(await publicIpv4());
//=> '46.5.21.123'
```
*/
export function publicIpv4(options?: Options): CancelablePromise<string>;

/**
Get your public IP address - very fast!
In Node.js, it queries the DNS records of OpenDNS, Google DNS, and HTTPS services to determine your IP address. In browsers, it uses the excellent [icanhaz](https://github.com/major/icanhaz) and [ipify](https://ipify.org) services through HTTPS.
@returns Your public IPv6 address. A `.cancel()` method is available on the promise, which can be used to cancel the request.
@throws On error or timeout.
export default publicIp;
@example
```
import {publicIpv6} from 'public-ip';
console.log(await publicIpv6());
//=> 'fe80::200:f8ff:fe21:67cf'
```
*/
export function publicIpv6(options?: Options): CancelablePromise<string>;

export {CancelError} from 'got';
20 changes: 7 additions & 13 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,9 @@ import dgram from 'node:dgram';
import dns from 'dns-socket';
import got, {CancelError} from 'got';
import isIp from 'is-ip';
import {createPublicIp, IpNotFoundError} from './core.js';

export class IpNotFoundError extends Error {
constructor(options) {
super('Could not get the public IP address', options);
this.name = 'IpNotFoundError';
}
}
export {IpNotFoundError} from './core.js';

const defaults = {
timeout: 5000,
Expand Down Expand Up @@ -228,9 +224,9 @@ const queryAll = (version, options) => {
return promise;
};

const publicIp = {};
export default createPublicIp(publicIpv4, publicIpv6);

publicIp.v4 = options => {
export function publicIpv4(options) {
options = {
...defaults,
...options,
Expand All @@ -245,9 +241,9 @@ publicIp.v4 = options => {
}

return queryDns('v4', options);
};
}

publicIp.v6 = options => {
export function publicIpv6(options) {
options = {
...defaults,
...options,
Expand All @@ -262,8 +258,6 @@ publicIp.v6 = options => {
}

return queryDns('v6', options);
};

export default publicIp;
}

export {CancelError} from 'got';
28 changes: 17 additions & 11 deletions index.test-d.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
import {expectType} from 'tsd';
import publicIp, {CancelablePromise} from './index.js';
import {publicIp, publicIpv4, publicIpv6, CancelablePromise} from './index.js';

expectType<CancelablePromise<string>>(publicIp.v4());
expectType<CancelablePromise<string>>(publicIp.v4({onlyHttps: true}));
expectType<CancelablePromise<string>>(publicIp.v4({timeout: 10}));
expectType<CancelablePromise<string>>(publicIp.v4({fallbackUrls: ['https://ifconfig.io']}));
publicIp.v4().cancel();
expectType<CancelablePromise<string>>(publicIpv4());
expectType<CancelablePromise<string>>(publicIpv4({onlyHttps: true}));
expectType<CancelablePromise<string>>(publicIpv4({timeout: 10}));
expectType<CancelablePromise<string>>(publicIpv4({fallbackUrls: ['https://ifconfig.io']}));
publicIpv4().cancel();

expectType<CancelablePromise<string>>(publicIp.v6());
expectType<CancelablePromise<string>>(publicIp.v6({onlyHttps: true}));
expectType<CancelablePromise<string>>(publicIp.v6({timeout: 10}));
expectType<CancelablePromise<string>>(publicIp.v6({fallbackUrls: ['https://ifconfig.io']}));
publicIp.v6().cancel();
expectType<CancelablePromise<string>>(publicIpv6());
expectType<CancelablePromise<string>>(publicIpv6({onlyHttps: true}));
expectType<CancelablePromise<string>>(publicIpv6({timeout: 10}));
expectType<CancelablePromise<string>>(publicIpv6({fallbackUrls: ['https://ifconfig.io']}));
publicIpv6().cancel();

expectType<CancelablePromise<string>>(publicIp());
expectType<CancelablePromise<string>>(publicIp({onlyHttps: true}));
expectType<CancelablePromise<string>>(publicIp({timeout: 10}));
expectType<CancelablePromise<string>>(publicIp({fallbackUrls: ['https://ifconfig.io']}));
publicIp().cancel();
4 changes: 2 additions & 2 deletions mocks/stub.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,12 @@ export default function stub(objectPath, propertyName, ignoreIndex) {
sinon.stub(objectPath, propertyName).callsFake(stub);

return {
ignore: _ignoreRegExp => {
ignore(_ignoreRegExp) {
ignoreRegExp = _ignoreRegExp;
},
ignored: () => ignored.length,
called: () => objectPath[propertyName].callCount,
restore: () => {
restore() {
ignoreRegExp = undefined;
ignored = [];
objectPath[propertyName].resetHistory();
Expand Down
11 changes: 7 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@
"index.js",
"index.d.ts",
"browser.js",
"browser.d.ts"
"browser.d.ts",
"core.js"
],
"keywords": [
"get",
Expand All @@ -41,15 +42,17 @@
"dns"
],
"dependencies": {
"aggregate-error": "^4.0.1",
"dns-socket": "^4.2.2",
"got": "^12.0.0",
"is-ip": "^3.1.0"
},
"devDependencies": {
"ava": "^3.15.0",
"ava": "^4.2.0",
"sinon": "^12.0.1",
"tsd": "^0.19.0",
"xo": "^0.47.0"
"time-span": "^5.0.0",
"tsd": "^0.20.0",
"xo": "^0.49.0"
},
"xo": {
"envs": [
Expand Down
18 changes: 11 additions & 7 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,23 @@ npm install public-ip
## Usage

```js
import publicIp from 'public-ip';
import publicIp, {publicIpv4, publicIpv6} from 'public-ip';

console.log(await publicIp.v4());
console.log(await publicIpv4());
//=> '46.5.21.123'

console.log(await publicIp.v6());
console.log(await publicIpv6());
//=> 'fe80::200:f8ff:fe21:67cf'

console.log(await publicIp()); // Falls back to IPv4
//=> 'fe80::200:f8ff:fe21:67cf'
```

## API

### publicIp.v4(options?)
### publicIp.v6(options?)
### publicIp(options?)
### publicIpv4(options?)
### publicIpv6(options?)

Returns a `Promise<string>` with your public IPv4 or IPv6 address. Rejects on error or timeout. A `.cancel()` method is available on the promise, which can be used to cancel the request.

Expand All @@ -48,9 +52,9 @@ Default: `[]`
Add your own custom HTTPS endpoints to get the public IP from. They will only be used if everything else fails. Any service used as fallback *must* return the IP as a plain string.

```js
import publicIp from 'public-ip';
import {publicIpv6} from 'public-ip';

await publicIp.v6({
await publicIpv6({
fallbackUrls: [
'https://ifconfig.co/ip'
]
Expand Down
7 changes: 4 additions & 3 deletions test-browser.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
// Comment out the `is-ip` dependency, launch a local server, and then load the HTMl file.
import publicIp from './browser.js';
import publicIp, {publicIpv4} from './browser.js';

console.log('IP:', await publicIp.v4());
console.log('IP:', await publicIp.v4({
console.log('IP:', await publicIpv4());
console.log('IP:', await publicIpv4({
fallbackUrls: [
'https://ifconfig.me',
],
}));
console.log('IP:', await publicIp());
Loading

0 comments on commit 4e1d895

Please sign in to comment.