diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index c1870cf..441975c 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -10,12 +10,10 @@ jobs: fail-fast: false matrix: node-version: - - 14 - - 12 - - 10 + - 16 steps: - uses: actions/checkout@v2 - - uses: actions/setup-node@v1 + - uses: actions/setup-node@v2 with: node-version: ${{ matrix.node-version }} - run: npm install diff --git a/browser.d.ts b/browser.d.ts new file mode 100644 index 0000000..ee01cc9 --- /dev/null +++ b/browser.d.ts @@ -0,0 +1 @@ +export * from './index.js'; diff --git a/browser.js b/browser.js index 0befb48..57d4b7a 100644 --- a/browser.js +++ b/browser.js @@ -1,5 +1,4 @@ /* eslint-env browser */ -'use strict'; import pEvent from 'p-event'; import isIp from 'is-ip'; @@ -11,58 +10,63 @@ const getIp = async ({isSecondTry = false} = {}) => { peerConnection.createOffer(peerConnection.setLocalDescription.bind(peerConnection), () => {}); const {candidate} = await pEvent(peerConnection, 'icecandidate', { - timeout: 10000 + timeout: 10_000, }); peerConnection.close(); - if (candidate && candidate.candidate) { - const result = candidate.candidate.split(' ')[4]; - if (result.endsWith('.local')) { - if (isSecondTry) { - return; - } - - const inputDevices = await navigator.mediaDevices.enumerateDevices(); - const inputDeviceTypes = new Set(inputDevices.map(({kind}) => kind)); + if (!(candidate && candidate.candidate)) { + return; + } - const constraints = {}; + const result = candidate.candidate.split(' ')[4]; + if (!result.endsWith('.local')) { + return result; + } - if (inputDeviceTypes.has('audioinput')) { - constraints.audio = true; - } else if (inputDeviceTypes.has('videoinput')) { - constraints.video = true; - } else { - return; - } + if (isSecondTry) { + return; + } - const mediaStream = await navigator.mediaDevices.getUserMedia(constraints); - for (const track of mediaStream.getTracks()) { - track.stop(); - } + const inputDevices = await navigator.mediaDevices.enumerateDevices(); + const inputDeviceTypes = new Set(inputDevices.map(({kind}) => kind)); - return await getIp({isSecondTry: true}); - } + const constraints = {}; + if (inputDeviceTypes.has('audioinput')) { + constraints.audio = true; + } else if (inputDeviceTypes.has('videoinput')) { + constraints.video = true; + } else { + return; + } - return result; + const mediaStream = await navigator.mediaDevices.getUserMedia(constraints); + for (const track of mediaStream.getTracks()) { + track.stop(); } + + return await getIp({isSecondTry: true}); } catch {} }; -export const v4 = async () => { +export async function internalIpV6() { const result = await getIp(); - if (isIp.v4(result)) { + if (isIp.v6(result)) { return result; } -}; - -v4.sync = () => undefined; +} -export const v6 = async () => { +export async function internalIpV4() { const result = await getIp(); - if (isIp.v6(result)) { + if (isIp.v4(result)) { return result; } -}; +} + +export function internalIpV6Sync() { + return undefined; +} -v6.sync = () => undefined; +export function internalIpV4Sync() { + return undefined; +} diff --git a/index.d.ts b/index.d.ts index 11f4481..665208f 100644 --- a/index.d.ts +++ b/index.d.ts @@ -1,62 +1,51 @@ -interface v6 { - /** - @returns The IPv6 address of the internet-facing interface, as determined from the default gateway. When the address cannot be determined for any reason, `undefined` will be returned. - - @example - ``` - import internalIp = require('internal-ip'); - - console.log(internalIp.v6.sync()); - //=> 'fe80::1' - ``` - */ - sync: () => string | undefined; - - /** - @returns The IPv6 address of the internet-facing interface, as determined from the default gateway. When the address cannot be determined for any reason, `undefined` will be returned. - - @example - ``` - import internalIp = require('internal-ip'); - - console.log(await internalIp.v6()); - //=> 'fe80::1' - ``` - */ - (): Promise; -} - -interface v4 { - /** - @returns The IPv4 address of the internet-facing interface, as determined from the default gateway. When the address cannot be determined for any reason, `undefined` will be returned. - - @example - ``` - import internalIp = require('internal-ip'); - - console.log(internalIp.v4.sync()) - //=> '10.0.0.79' - ``` - */ - sync: () => string | undefined; - - /** - @returns The IPv4 address of the internet-facing interface, as determined from the default gateway. When the address cannot be determined for any reason, `undefined` will be returned. - - @example - ``` - import internalIp = require('internal-ip'); - - console.log(await internalIp.v4()) - //=> '10.0.0.79' - ``` - */ - (): Promise; -} - -declare const internalIp: { - v6: v6; - v4: v4; -}; - -export = internalIp; +/** +@returns The IPv6 address of the internet-facing interface, as determined from the default gateway. When the address cannot be determined for any reason, `undefined` will be returned. + +@example +``` +import {internalIpV6} from 'internal-ip'; + +console.log(await internalIpV6()); +//=> 'fe80::1' +``` +*/ +export function internalIpV6(): Promise; + +/** +@returns The IPv4 address of the internet-facing interface, as determined from the default gateway. When the address cannot be determined for any reason, `undefined` will be returned. + +@example +``` +import {internalIpV4} from 'internal-ip'; + +console.log(await internalIpV4()); +//=> '10.0.0.79' +``` +*/ +export function internalIpV4(): Promise; + +/** +@returns The IPv6 address of the internet-facing interface, as determined from the default gateway. When the address cannot be determined for any reason, `undefined` will be returned. + +@example +``` +import {internalIpV6Sync} from 'internal-ip'; + +console.log(internalIpV6Sync()); +//=> 'fe80::1' +``` +*/ +export function internalIpV6Sync(): string | undefined; + +/** +@returns The IPv4 address of the internet-facing interface, as determined from the default gateway. When the address cannot be determined for any reason, `undefined` will be returned. + +@example +``` +import {internalIpV4Sync} from 'internal-ip'; + +console.log(internalIpV4Sync()); +//=> '10.0.0.79' +``` +*/ +export function internalIpV4Sync(): string | undefined; diff --git a/index.js b/index.js index a04e358..b3f79a6 100644 --- a/index.js +++ b/index.js @@ -1,16 +1,16 @@ -'use strict'; -const defaultGateway = require('default-gateway'); -const {networkInterfaces} = require('os'); -const {parse, parseCIDR} = require('ipaddr.js'); +import {networkInterfaces} from 'node:os'; +import defaultGateway from 'default-gateway'; +import ip from 'ipaddr.js'; function findIp(gateway) { - const gatewayIp = parse(gateway); + const gatewayIp = ip.parse(gateway); // Look for the matching interface in all local interfaces. for (const addresses of Object.values(networkInterfaces())) { for (const {cidr} of addresses) { - const net = parseCIDR(cidr); + const net = ip.parseCIDR(cidr); + // eslint-disable-next-line unicorn/prefer-regexp-test if (net[0] && net[0].kind() === gatewayIp.kind() && gatewayIp.match(net)) { return net[0].toString(); } @@ -18,7 +18,7 @@ function findIp(gateway) { } } -async function promise(family) { +async function async(family) { try { const {gateway} = await defaultGateway[family](); return findIp(gateway); @@ -32,10 +32,18 @@ function sync(family) { } catch {} } -const internalIp = {}; -internalIp.v6 = () => promise('v6'); -internalIp.v4 = () => promise('v4'); -internalIp.v6.sync = () => sync('v6'); -internalIp.v4.sync = () => sync('v4'); +export async function internalIpV6() { + return async('v6'); +} + +export async function internalIpV4() { + return async('v4'); +} -module.exports = internalIp; +export function internalIpV6Sync() { + return sync('v6'); +} + +export function internalIpV4Sync() { + return sync('v4'); +} diff --git a/index.test-d.ts b/index.test-d.ts index 8318eb9..e590f78 100644 --- a/index.test-d.ts +++ b/index.test-d.ts @@ -1,8 +1,8 @@ import {expectType} from 'tsd'; -import internalIp = require('.'); +import {internalIpV4, internalIpV6, internalIpV4Sync, internalIpV6Sync} from './index.js'; -expectType(await internalIp.v4()); -expectType(await internalIp.v6()); +expectType(await internalIpV4()); +expectType(await internalIpV6()); -expectType(internalIp.v4.sync()); -expectType(internalIp.v6.sync()); +expectType(internalIpV4Sync()); +expectType(internalIpV6Sync()); diff --git a/package.json b/package.json index a9d8818..ff4be13 100644 --- a/package.json +++ b/package.json @@ -10,8 +10,13 @@ "email": "sindresorhus@gmail.com", "url": "https://sindresorhus.com" }, + "type": "module", + "exports": { + "node": "./index.js", + "default": "./browser.js" + }, "engines": { - "node": ">=10" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, "scripts": { "test": "xo && ava && tsd" @@ -19,12 +24,9 @@ "files": [ "index.js", "index.d.ts", - "browser.js" + "browser.js", + "browser.d.ts" ], - "exports": { - "browser": "./browser.js", - "default": "./index.js" - }, "keywords": [ "ip", "ipv6", @@ -38,15 +40,14 @@ "gateway" ], "dependencies": { - "default-gateway": "^6.0.0", - "ipaddr.js": "^1.9.1", + "default-gateway": "^6.0.3", + "ipaddr.js": "^2.0.1", "is-ip": "^3.1.0", "p-event": "^4.2.0" }, "devDependencies": { - "ava": "^2.4.0", - "tsd": "^0.13.1", - "xo": "^0.32.1" - }, - "browser": "browser.js" + "ava": "^3.15.0", + "tsd": "^0.18.0", + "xo": "^0.45.0" + } } diff --git a/readme.md b/readme.md index 29fef98..57c8d15 100644 --- a/readme.md +++ b/readme.md @@ -4,37 +4,47 @@ ## Install -``` -$ npm install internal-ip +```sh +npm install internal-ip ``` ## Usage ```js -const internalIp = require('internal-ip'); - -(async () => { - console.log(await internalIp.v6()); - //=> 'fe80::1' - - console.log(await internalIp.v4()); - //=> '10.0.0.79' -})(); +import {internalIpV6, internalIpV4} from 'internal-ip'; -console.log(internalIp.v6.sync()) +console.log(await internalIpV6()); //=> 'fe80::1' -console.log(internalIp.v4.sync()) +console.log(await internalIpV4()); //=> '10.0.0.79' ``` -The module returns the address of the internet-facing interface, as determined from the default gateway. When the address cannot be determined for any reason, `undefined` will be returned. +## API + +The package returns the address of the internet-facing interface, as determined from the default gateway. When the address cannot be determined for any reason, `undefined` will be returned. + +The package relies on operating systems tools. On Linux and Android, the `ip` command must be available, which depending on distribution might not be installed by default. It is usually provided by the `iproute2` package. `internalIpV6Sync()` and `internalIpV4Sync()` are not supported in browsers and just return `undefined`. + +### internalIpV6() + +Returns the internal IPv6 address asynchronously. + +### internalIpV4() + +Returns the internal IPv4 address asynchronously. + +### internalIpV6Sync() + +Returns the internal IPv6 address synchronously. + +### internalIpV4Sync() -The module relies on operating systems tools. On Linux and Android, the `ip` command must be available, which depending on distribution might not be installed by default. It is usually provided by the `iproute2` package. `.v4.sync()` and `.v6.sync()` are not supported in browsers and just return `undefined`. +Returns the internal IPv4 address synchronously. ## Related -- [internal-ip-cli](https://github.com/sindresorhus/internal-ip-cli) - CLI for this module +- [internal-ip-cli](https://github.com/sindresorhus/internal-ip-cli) - CLI for this package - [public-ip](https://github.com/sindresorhus/public-ip) - Get your public IP address - [default-gateway](https://github.com/silverwind/default-gateway) - Get your default gateway address diff --git a/test.js b/test.js index 6d6fc27..d48cd6d 100644 --- a/test.js +++ b/test.js @@ -1,13 +1,12 @@ -import {isIPv4, isIPv6} from 'net'; +import {isIPv4, isIPv6} from 'node:net'; +import process from 'node:process'; import test from 'ava'; -import internalIp from '.'; +import {internalIpV6, internalIpV4, internalIpV6Sync, internalIpV4Sync} from './index.js'; -// Travis VMs don't have IPs on their interfaces. -// https://docs.travis-ci.com/user/ci-environment/#Networking const isCI = Boolean(process.env.CI); -test('IPv6', async t => { - const ip = await internalIp.v6(); +test('IPv6 - async', async t => { + const ip = await internalIpV6(); if (isCI) { t.is(ip, undefined); } else { @@ -15,17 +14,13 @@ test('IPv6', async t => { } }); -test('IPv4', async t => { - const ip = await internalIp.v4(); - if (isCI) { - t.is(ip, undefined); - } else { - t.true(isIPv4(ip)); - } +test('IPv4 - async', async t => { + const ip = await internalIpV4(); + t.true(isIPv4(ip)); }); -test('synchronous IPv6', t => { - const ip = internalIp.v6.sync(); +test('IPv6 - sync', t => { + const ip = internalIpV6Sync(); if (isCI) { t.is(ip, undefined); } else { @@ -33,11 +28,7 @@ test('synchronous IPv6', t => { } }); -test('synchronous IPv4', t => { - const ip = internalIp.v4.sync(); - if (isCI) { - t.is(ip, undefined); - } else { - t.true(isIPv4(ip)); - } +test('IPv4 - sync', t => { + const ip = internalIpV4Sync(); + t.true(isIPv4(ip)); });