Socket
Socket
Sign inDemoInstall

cacheable-lookup

Package Overview
Dependencies
0
Maintainers
2
Versions
34
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 2.0.0 to 3.0.0

28

index.d.ts
import Keyv = require('keyv');
import {Resolver, LookupAddress} from 'dns';
import {Resolver, LookupAddress, promises as dnsPromises} from 'dns';
import {Agent} from 'http';
type AsyncResolver = dnsPromises.Resolver;
type IPFamily = 4 | 6;

@@ -9,7 +11,2 @@

/**
* A Keyv adapter which stores the cache.
* @default new Map()
*/
cacheAdapter?: Keyv;
/**
* Limits the cache time (TTL). If set to `0`, it will make a new DNS query each time.

@@ -21,5 +18,5 @@ * @default Infinity

* DNS Resolver used to make DNS queries.
* @default new dns.Resolver()
* @default new dns.promises.Resolver()
*/
resolver?: Resolver;
resolver?: Resolver | AsyncResolver;
}

@@ -60,2 +57,5 @@

all?: boolean;
}
interface AsyncLookupOptions extends LookupOptions {
/**

@@ -84,4 +84,4 @@ * Throw when there's no match. If set to `false` and it gets no match, it will return `undefined`.

*/
lookupAsync(hostname: string, options: LookupOptions & {all: true}): Promise<ReadonlyArray<EntryObject>>;
lookupAsync(hostname: string, options: LookupOptions): Promise<EntryObject>;
lookupAsync(hostname: string, options: AsyncLookupOptions & {all: true}): Promise<ReadonlyArray<EntryObject>>;
lookupAsync(hostname: string, options: AsyncLookupOptions): Promise<EntryObject>;
lookupAsync(hostname: string): Promise<EntryObject>;

@@ -92,7 +92,7 @@ lookupAsync(hostname: string, family: IPFamily): Promise<EntryObject>;

*/
query(hostname: string, family: IPFamily): Promise<ReadonlyArray<EntryObject>>;
query(hostname: string): Promise<ReadonlyArray<EntryObject>>;
/**
* An asynchronous function which makes a new DNS lookup query and updates the database. This is used by `query(hostname, family)` if no entry in the database is present. Returns an array of objects with `address`, `family`, `ttl` and `expires` properties.
*/
queryAndCache(hostname: string, family: IPFamily): Promise<ReadonlyArray<EntryObject>>;
queryAndCache(hostname: string): Promise<ReadonlyArray<EntryObject>>;
/**

@@ -110,2 +110,6 @@ * Attaches itself to an Agent instance.

updateInterfaceInfo(): void;
/**
* Clears the cache.
*/
clear(): void;
}
'use strict';
const {Resolver, V4MAPPED, ADDRCONFIG} = require('dns');
const {V4MAPPED, ADDRCONFIG, promises: dnsPromises} = require('dns');
const {promisify} = require('util');
const os = require('os');
const Keyv = require('keyv');
const {Resolver: AsyncResolver} = dnsPromises;
const kCacheableLookupData = Symbol('cacheableLookupData');

@@ -48,16 +49,55 @@ const kCacheableLookupInstance = Symbol('cacheableLookupInstance');

class TTLMap {
constructor() {
this.values = new Map();
this.expiries = new Map();
}
set(key, value, ttl) {
this.values.set(key, value);
this.expiries.set(key, ttl && (ttl + Date.now()));
}
get(key) {
const expiry = this.expiries.get(key);
if (typeof expiry === 'number') {
if (Date.now() > expiry) {
this.values.delete(key);
this.expiries.delete(key);
return;
}
}
return this.values.get(key);
}
clear() {
this.values.clear();
this.expiries.clear();
}
get size() {
return this.values.size;
}
}
const ttl = {ttl: true};
class CacheableLookup {
constructor({cacheAdapter, maxTtl = Infinity, resolver} = {}) {
this.cache = new Keyv({
uri: typeof cacheAdapter === 'string' && cacheAdapter,
store: typeof cacheAdapter !== 'string' && cacheAdapter,
namespace: 'cached-lookup'
});
constructor({maxTtl = Infinity, resolver} = {}) {
this.maxTtl = maxTtl;
this._resolver = resolver || new Resolver();
this._resolve4 = promisify(this._resolver.resolve4.bind(this._resolver));
this._resolve6 = promisify(this._resolver.resolve6.bind(this._resolver));
this._cache = new TTLMap();
this._resolver = resolver || new AsyncResolver();
if (this._resolver instanceof AsyncResolver) {
this._resolve4 = this._resolver.resolve4.bind(this._resolver);
this._resolve6 = this._resolver.resolve6.bind(this._resolver);
} else {
this._resolve4 = promisify(this._resolver.resolve4.bind(this._resolver));
this._resolve6 = promisify(this._resolver.resolve6.bind(this._resolver));
}
this._iface = getIfaceInfo();

@@ -94,13 +134,14 @@

async lookupAsync(hostname, options = {}) {
let cached;
if (!options.family && options.all) {
const [cached4, cached6] = await Promise.all([this.lookupAsync(hostname, {all: true, family: 4}), this.lookupAsync(hostname, {all: true, family: 6})]);
cached = [...cached4, ...cached6];
} else {
cached = await this.query(hostname, options.family || 4);
let cached = await this.query(hostname);
if (cached.length === 0 && options.family === 6 && options.hints & V4MAPPED) {
cached = await this.query(hostname, 4);
if (options.family === 6) {
const filtered = cached.filter(entry => entry.family === 6);
if (filtered.length === 0 && options.hints & V4MAPPED) {
map4to6(cached);
} else {
cached = filtered;
}
} else if (!options.all || options.family === 4) {
cached = cached.filter(entry => entry.family === 4);
}

@@ -139,32 +180,45 @@

async query(hostname, family) {
let cached = await this.cache.get(`${hostname}:${family}`);
async query(hostname) {
let cached = this._cache.get(hostname);
if (!cached) {
cached = await this.queryAndCache(hostname, family);
cached = await this.queryAndCache(hostname);
}
cached = cached.map(entry => {
return {...entry};
});
return cached;
}
async queryAndCache(hostname, family) {
const resolve = family === 4 ? this._resolve4 : this._resolve6;
const entries = await resolve(hostname, {ttl: true});
async queryAndCache(hostname) {
const [As, AAAAs] = await Promise.all([this._resolve4(hostname, ttl).catch(() => []), this._resolve6(hostname, ttl).catch(() => [])]);
if (entries === undefined) {
return [];
let cacheTtl = 0;
const now = Date.now();
if (As) {
for (const entry of As) {
entry.family = 4;
entry.expires = now + (entry.ttl * 1000);
cacheTtl = Math.max(cacheTtl, entry.ttl);
}
}
const now = Date.now();
if (AAAAs) {
for (const entry of AAAAs) {
entry.family = 6;
entry.expires = now + (entry.ttl * 1000);
let cacheTtl = 0;
for (const entry of entries) {
cacheTtl = Math.max(cacheTtl, entry.ttl);
entry.family = family;
entry.expires = now + (entry.ttl * 1000);
cacheTtl = Math.max(cacheTtl, entry.ttl);
}
}
const entries = [...(As || []), ...(AAAAs || [])];
cacheTtl = Math.min(this.maxTtl, cacheTtl) * 1000;
if (this.maxTtl !== 0 && cacheTtl !== 0) {
await this.cache.set(`${hostname}:${family}`, entries, cacheTtl);
if (this.maxTtl > 0 && cacheTtl > 0) {
this._cache.set(hostname, entries, cacheTtl);
}

@@ -215,3 +269,8 @@

this._iface = getIfaceInfo();
this._cache.clear();
}
clear() {
this._cache.clear();
}
}

@@ -218,0 +277,0 @@

{
"name": "cacheable-lookup",
"version": "2.0.0",
"version": "3.0.0",
"description": "A cacheable dns.lookup(…) that respects the TTL",

@@ -41,6 +41,3 @@ "engines": {

"xo": "^0.25.3"
},
"dependencies": {
"keyv": "^4.0.0"
}
}

@@ -10,4 +10,3 @@ # cacheable-lookup

Making lots of HTTP requests? You can save some time by caching DNS lookups.<br>
Don't worry, this package respects TTL :smiley:
Making lots of HTTP requests? You can save some time by caching DNS lookups :zap:

@@ -50,3 +49,3 @@ ## Usage

Type: `Object`<br>
Type: `object`<br>
Default: `{}`

@@ -56,9 +55,2 @@

##### options.cacheAdapter
Type: [Keyv adapter instance](https://github.com/lukechilds/keyv)<br>
Default: `new Map()`
A Keyv adapter which stores the cache.
##### options.maxTtl

@@ -75,4 +67,4 @@

Type: `Function`<br>
Default: [`new dns.Resolver()`](https://nodejs.org/api/dns.html#dns_class_dns_resolver)
Type: `dns.Resolver | dns.promises.Resolver`<br>
Default: [`new dns.promises.Resolver()`](https://nodejs.org/api/dns.html#dns_class_dns_resolver)

@@ -83,3 +75,3 @@ An instance of [DNS Resolver](https://nodejs.org/api/dns.html#dns_class_dns_resolver) used to make DNS queries.

Type: `Object`
Type: `object`

@@ -134,3 +126,3 @@ #### address

**Note**: If entry(ies) were not found, it will return `undefined`.
**Note**: If entry(ies) were not found, it will return `undefined` by default.

@@ -143,3 +135,3 @@ ##### hostname

Type: `Object`
Type: `object`

@@ -153,11 +145,11 @@ The same as the [`dns.lookup(…)`](https://nodejs.org/api/dns.html#dns_dns_lookup_hostname_options_callback) options.

Throw when there's no match.
If set to `false` and it gets no match, it will return `undefined`.
If set to `true` and it gets no match, it will throw `ENOTFOUND` error.
**Note**: This option is meant **only** for the asynchronous implementation! The synchronous version will always give an error if no match found.
**Note**: This option is meant **only** for the asynchronous implementation! The callback version will always pass an error if no match found.
#### query(hostname, family)
#### query(hostname)
An asynchronous function which returns cached DNS lookup entries. This is the base for `lookupAsync(hostname, options)` and `lookup(hostname, options, callback)`.
An asynchronous function which returns cached DNS lookup entries.<br>
This is the base for `lookupAsync(hostname, options)` and `lookup(hostname, options, callback)`.

@@ -168,5 +160,6 @@ **Note**: This function has no options.

#### queryAndCache(hostname, family)
#### queryAndCache(hostname)
An asynchronous function which makes a new DNS lookup query and updates the database. This is used by `query(hostname, family)` if no entry in the database is present.
An asynchronous function which makes two DNS queries: A and AAAA. The result is cached.<br>
This is used by `query(hostname)` if no entry in the database is present.

@@ -179,2 +172,8 @@ Returns an array of objects with `address`, `family`, `ttl` and `expires` properties.

**Note:** Running `updateInterfaceInfo()` will also trigger `clear()`!
#### clear()
Clears the cache.
## High performance

@@ -185,15 +184,15 @@

```
CacheableLookup#lookupAsync x 265,390 ops/sec ±0.65% (89 runs sampled)
CacheableLookup#lookupAsync.all x 119,187 ops/sec ±2.57% (87 runs sampled)
CacheableLookup#lookupAsync.all.ADDRCONFIG x 119,666 ops/sec ±0.75% (89 runs sampled)
CacheableLookup#lookup x 116,604 ops/sec ±0.68% (88 runs sampled)
CacheableLookup#lookup.all x 115,627 ops/sec ±0.72% (89 runs sampled)
CacheableLookup#lookup.all.ADDRCONFIG x 115,578 ops/sec ±0.90% (88 runs sampled)
CacheableLookup#lookupAsync - zero TTL x 60.83 ops/sec ±7.43% (51 runs sampled)
CacheableLookup#lookup - zero TTL x 49.22 ops/sec ±20.58% (49 runs sampled)
dns#resolve4 x 63.00 ops/sec ±5.88% (51 runs sampled)
dns#lookup x 21,303 ops/sec ±29.06% (35 runs sampled)
dns#lookup.all x 22,283 ops/sec ±21.96% (38 runs sampled)
dns#lookup.all.ADDRCONFIG x 5,922 ops/sec ±10.18% (38 runs sampled)
Fastest is CacheableLookup#lookupAsync
CacheableLookup#lookupAsync x 2,525,219 ops/sec ±1.66% (86 runs sampled)
CacheableLookup#lookupAsync.all x 2,648,106 ops/sec ±0.59% (88 runs sampled)
CacheableLookup#lookupAsync.all.ADDRCONFIG x 2,263,173 ops/sec ±0.95% (88 runs sampled)
CacheableLookup#lookup x 2,108,952 ops/sec ±0.97% (89 runs sampled)
CacheableLookup#lookup.all x 2,081,357 ops/sec ±1.19% (83 runs sampled)
CacheableLookup#lookup.all.ADDRCONFIG x 1,913,955 ops/sec ±0.60% (89 runs sampled)
CacheableLookup#lookupAsync - zero TTL x 36.50 ops/sec ±11.21% (39 runs sampled)
CacheableLookup#lookup - zero TTL x 33.66 ops/sec ±7.57% (47 runs sampled)
dns#resolve4 x 40.31 ops/sec ±16.10% (49 runs sampled)
dns#lookup x 13,722 ops/sec ±20.69% (37 runs sampled)
dns#lookup.all x 30,343 ops/sec ±28.97% (47 runs sampled)
dns#lookup.all.ADDRCONFIG x 7,023 ops/sec ±15.86% (31 runs sampled)
Fastest is CacheableLookup#lookupAsync.all
```

@@ -200,0 +199,0 @@

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc