Skip to content

Commit

Permalink
feat: convert to typescript (#53)
Browse files Browse the repository at this point in the history
BREAKING CHANGE: ESM only publishing
  • Loading branch information
achingbrain authored Dec 14, 2021
1 parent 4333adb commit 4eac9f6
Show file tree
Hide file tree
Showing 8 changed files with 449 additions and 445 deletions.
25 changes: 15 additions & 10 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,41 +12,46 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: 16
- run: npm install
- run: npm run lint
- run: npm run build
- run: npx aegir dep-check
- uses: ipfs/aegir/actions/bundle-size@master
name: size
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
- run: npm run dep-check
test-node:
needs: check
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [windows-latest, ubuntu-latest, macos-latest]
node: [14, 15]
node: [16]
fail-fast: true
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
- uses: actions/setup-node@v2
with:
node-version: ${{ matrix.node }}
- run: npm install
- run: npx nyc --reporter=lcov aegir test -t node -- --bail
- run: npx nyc --reporter=lcov npm run test -- -t node -- --bail
- uses: codecov/codecov-action@v1
test-chrome:
needs: check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: 16
- run: npm install
- run: npx aegir test -t browser --bail
- run: npm run test -- -t browser --bail
test-firefox:
needs: check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: 16
- run: npm install
- run: npx aegir test -t browser --bail -- --browsers FirefoxHeadless
- run: npm run test -- -t browser --bail -- --browser firefox
55 changes: 40 additions & 15 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,50 @@
"name": "dns-over-http-resolver",
"version": "1.2.3",
"description": "DNS over HTTP resolver",
"main": "src/index.js",
"author": "Vasco Santos",
"scripts": {
"test": "aegir test -t node -t browser",
"test:browser": "aegir test -t browser",
"test:node": "aegir test -t node",
"lint": "aegir lint",
"release": "aegir release --docs",
"release-minor": "aegir release --type minor --docs",
"release-major": "aegir release -t node -t browser --type major --docs",
"build": "aegir build"
},
"type": "module",
"files": [
"src",
"dist"
"dist/src"
],
"exports": {
".": {
"import": "./dist/src/index.js",
"types": "./dist/src/index.d.ts"
}
},
"typesVersions": {
"*": {
"src/*": [
"dist/src/*",
"dist/src/*/index"
]
}
},
"eslintConfig": {
"extends": "ipfs",
"parserOptions": {
"sourceType": "module"
}
},
"scripts": {
"pretest": "npm run build",
"test": "aegir test -f ./dist/test/*.js",
"test:browser": "aegir test -t browser -f ./dist/test/*.js",
"test:node": "aegir test -t node -f ./dist/test/*.js",
"test:electron": "aegir test -t electron-main -f ./dist/test/*.js",
"lint": "aegir ts -p check && aegir lint",
"release": "aegir release --docs",
"release-minor": "aegir release --target node --type minor --docs",
"release-major": "aegir release --type major --docs",
"build": "tsc",
"dep-check": "aegir dep-check dist/src/**/*.js dist/test/**/*.js"
},
"devDependencies": {
"aegir": "^33.1.0",
"sinon": "^11.1.2",
"@types/debug": "^4.1.7",
"aegir": "^36.0.2",
"sinon": "^12.0.1",
"ts-sinon": "^2.0.2",
"util": "^0.12.3"
},
"repository": {
Expand All @@ -39,7 +64,7 @@
"homepage": "https://github.com/vasco-santos/dns-over-http-resolver#readme",
"dependencies": {
"debug": "^4.3.1",
"native-fetch": "^3.0.0",
"native-fetch": "^4.0.2",
"receptacle": "^1.3.2"
},
"contributors": [
Expand Down
132 changes: 70 additions & 62 deletions src/index.js → src/index.ts
Original file line number Diff line number Diff line change
@@ -1,35 +1,48 @@
'use strict'
const debug = require('debug')
const log = debug('dns-over-http-resolver')
log.error = debug('dns-over-http-resolver:error')
import debug from 'debug'
import Receptacle from 'receptacle'
import * as utils from './utils.js'
import type { DNSJSON } from './utils'

const Receptacle = require('receptacle')
const log = Object.assign(debug('dns-over-http-resolver'), {
error: debug('dns-over-http-resolver:error')
})

const utils = require('./utils')
export interface Request { (resource: string): Promise<DNSJSON> }

interface ResolverOptions {
maxCache?: number
request?: Request
}

/**
* DNS over HTTP resolver.
* Uses a list of servers to resolve DNS records with HTTP requests.
*/
class Resolver {
private readonly _cache: Receptacle<string[]>
private readonly _TXTcache: Receptacle<string[][]>
private _servers: string[]
private readonly _request: Request

/**
* @class
* @param {object} [properties]
* @param {number} [properties.maxCache = 100] - maximum number of cached dns records.
* @param {object} [options]
* @param {number} [options.maxCache = 100] - maximum number of cached dns records
* @param {Request} [options.request] - function to return DNSJSON
*/
constructor ({ maxCache = 100 } = {}) {
this._cache = new Receptacle({ max: maxCache })
constructor (options: ResolverOptions = {}) {
this._cache = new Receptacle({ max: options?.maxCache ?? 100 })
this._TXTcache = new Receptacle({ max: options?.maxCache ?? 100 })
this._servers = [
'https://cloudflare-dns.com/dns-query',
'https://dns.google/resolve'
]
this._request = options.request ?? utils.request
}

/**
* Get an array of the IP addresses currently configured for DNS resolution.
* These addresses are formatted according to RFC 5952. It can include a custom port.
*
* @returns {Array<string>}
*/
getServers () {
return this._servers
Expand All @@ -38,11 +51,9 @@ class Resolver {
/**
* Get a shuffled array of the IP addresses currently configured for DNS resolution.
* These addresses are formatted according to RFC 5952. It can include a custom port.
*
* @returns {Array<string>}
*/
_getShuffledServers () {
const newServers = [].concat(this._servers)
const newServers = [...this._servers]

for (let i = newServers.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * i)
Expand All @@ -57,56 +68,53 @@ class Resolver {
/**
* Sets the IP address and port of servers to be used when performing DNS resolution.
*
* @param {Array<string>} servers - array of RFC 5952 formatted addresses.
* @param {string[]} servers - array of RFC 5952 formatted addresses.
*/
setServers (servers) {
setServers (servers: string[]) {
this._servers = servers
}

/**
* Uses the DNS protocol to resolve the given host name into the appropriate DNS record.
* Uses the DNS protocol to resolve the given host name into the appropriate DNS record
*
* @param {string} hostname - host name to resolve.
* @param {string} [rrType = 'A'] - resource record type.
* @returns {Promise<*>}
* @param {string} hostname - host name to resolve
* @param {string} [rrType = 'A'] - resource record type
*/
resolve (hostname, rrType = 'A') {
async resolve (hostname: string, rrType = 'A') {
switch (rrType) {
case 'A':
return this.resolve4(hostname)
return await this.resolve4(hostname)
case 'AAAA':
return this.resolve6(hostname)
return await this.resolve6(hostname)
case 'TXT':
return this.resolveTxt(hostname)
return await this.resolveTxt(hostname)
default:
throw new Error(`${rrType} is not supported`)
}
}

/**
* Uses the DNS protocol to resolve the given host name into IPv4 addresses.
* Uses the DNS protocol to resolve the given host name into IPv4 addresses
*
* @param {string} hostname - host name to resolve.
* @returns {Promise<Array<string>>}
* @param {string} hostname - host name to resolve
*/
async resolve4 (hostname) {
async resolve4 (hostname: string) {
const recordType = 'A'
const cached = this._cache.get(utils.getCacheKey(hostname, recordType))
if (cached) {
if (cached != null) {
return cached
}

for (const server of this._getShuffledServers()) {
try {
const response = await utils.fetch(utils.buildResource({
serverResolver: server,
const response = await this._request(utils.buildResource(
server,
hostname,
recordType
}))
))

const d = await response.json()
const data = d.Answer.map(a => a.data)
const ttl = Math.min(d.Answer.map(a => a.TTL))
const data = response.Answer.map(a => a.data)
const ttl = Math.min(...response.Answer.map(a => a.TTL))

this._cache.set(utils.getCacheKey(hostname, recordType), data, { ttl })

Expand All @@ -120,29 +128,27 @@ class Resolver {
}

/**
* Uses the DNS protocol to resolve the given host name into IPv6 addresses.
* Uses the DNS protocol to resolve the given host name into IPv6 addresses
*
* @param {string} hostname - host name to resolve.
* @returns {Promise<Array<string>>}
* @param {string} hostname - host name to resolve
*/
async resolve6 (hostname) {
async resolve6 (hostname: string) {
const recordType = 'AAAA'
const cached = this._cache.get(utils.getCacheKey(hostname, recordType))
if (cached) {
if (cached != null) {
return cached
}

for (const server of this._getShuffledServers()) {
try {
const response = await utils.fetch(utils.buildResource({
serverResolver: server,
const response = await this._request(utils.buildResource(
server,
hostname,
recordType
}))
))

const d = await response.json()
const data = d.Answer.map(a => a.data)
const ttl = Math.min(d.Answer.map(a => a.TTL))
const data = response.Answer.map(a => a.data)
const ttl = Math.min(...response.Answer.map(a => a.TTL))

this._cache.set(utils.getCacheKey(hostname, recordType), data, { ttl })

Expand All @@ -156,31 +162,29 @@ class Resolver {
}

/**
* Uses the DNS protocol to resolve the given host name into a Text record.
* Uses the DNS protocol to resolve the given host name into a Text record
*
* @param {string} hostname - host name to resolve.
* @returns {Promise<Array<Array<string>>>}
* @param {string} hostname - host name to resolve
*/
async resolveTxt (hostname) {
async resolveTxt (hostname: string) {
const recordType = 'TXT'
const cached = this._cache.get(utils.getCacheKey(hostname, recordType))
if (cached) {
const cached = this._TXTcache.get(utils.getCacheKey(hostname, recordType))
if (cached != null) {
return cached
}

for (const server of this._getShuffledServers()) {
try {
const response = await utils.fetch(utils.buildResource({
serverResolver: server,
const response = await this._request(utils.buildResource(
server,
hostname,
recordType
}))
))

const d = await response.json()
const data = d.Answer.map(a => [a.data.replace(/['"]+/g, '')])
const ttl = Math.min(d.Answer.map(a => a.TTL))
const data = response.Answer.map(a => [a.data.replace(/['"]+/g, '')])
const ttl = Math.min(...response.Answer.map(a => a.TTL))

this._cache.set(utils.getCacheKey(hostname, recordType), data, { ttl })
this._TXTcache.set(utils.getCacheKey(hostname, recordType), data, { ttl })

return data
} catch (err) {
Expand All @@ -190,7 +194,11 @@ class Resolver {

throw new Error(`Could not resolve ${hostname} record ${recordType}`)
}

clearCache () {
this._cache.clear()
this._TXTcache.clear()
}
}

Resolver.Resolver = Resolver
module.exports = Resolver
export default Resolver
Loading

0 comments on commit 4eac9f6

Please sign in to comment.