Buckets:
tahamajs/Other_Lessons / AP /TA_1 /AP-ECE-UT.github.io /node_modules /cacheable-lookup /source /index.js
| import { | |
| V4MAPPED, | |
| ADDRCONFIG, | |
| ALL, | |
| promises as dnsPromises, | |
| lookup as dnsLookup | |
| } from 'node:dns'; | |
| import {promisify} from 'node:util'; | |
| import os from 'node:os'; | |
| const {Resolver: AsyncResolver} = dnsPromises; | |
| const kCacheableLookupCreateConnection = Symbol('cacheableLookupCreateConnection'); | |
| const kCacheableLookupInstance = Symbol('cacheableLookupInstance'); | |
| const kExpires = Symbol('expires'); | |
| const supportsALL = typeof ALL === 'number'; | |
| const verifyAgent = agent => { | |
| if (!(agent && typeof agent.createConnection === 'function')) { | |
| throw new Error('Expected an Agent instance as the first argument'); | |
| } | |
| }; | |
| const map4to6 = entries => { | |
| for (const entry of entries) { | |
| if (entry.family === 6) { | |
| continue; | |
| } | |
| entry.address = `::ffff:${entry.address}`; | |
| entry.family = 6; | |
| } | |
| }; | |
| const getIfaceInfo = () => { | |
| let has4 = false; | |
| let has6 = false; | |
| for (const device of Object.values(os.networkInterfaces())) { | |
| for (const iface of device) { | |
| if (iface.internal) { | |
| continue; | |
| } | |
| if (iface.family === 'IPv6') { | |
| has6 = true; | |
| } else { | |
| has4 = true; | |
| } | |
| if (has4 && has6) { | |
| return {has4, has6}; | |
| } | |
| } | |
| } | |
| return {has4, has6}; | |
| }; | |
| const isIterable = map => { | |
| return Symbol.iterator in map; | |
| }; | |
| const ignoreNoResultErrors = dnsPromise => { | |
| return dnsPromise.catch(error => { | |
| if ( | |
| error.code === 'ENODATA' || | |
| error.code === 'ENOTFOUND' || | |
| error.code === 'ENOENT' // Windows: name exists, but not this record type | |
| ) { | |
| return []; | |
| } | |
| throw error; | |
| }); | |
| }; | |
| const ttl = {ttl: true}; | |
| const all = {all: true}; | |
| const all4 = {all: true, family: 4}; | |
| const all6 = {all: true, family: 6}; | |
| export default class CacheableLookup { | |
| constructor({ | |
| cache = new Map(), | |
| maxTtl = Infinity, | |
| fallbackDuration = 3600, | |
| errorTtl = 0.15, | |
| resolver = new AsyncResolver(), | |
| lookup = dnsLookup | |
| } = {}) { | |
| this.maxTtl = maxTtl; | |
| this.errorTtl = errorTtl; | |
| this._cache = cache; | |
| this._resolver = resolver; | |
| this._dnsLookup = lookup && promisify(lookup); | |
| this.stats = { | |
| cache: 0, | |
| query: 0 | |
| }; | |
| 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(); | |
| this._pending = {}; | |
| this._nextRemovalTime = false; | |
| this._hostnamesToFallback = new Set(); | |
| this.fallbackDuration = fallbackDuration; | |
| if (fallbackDuration > 0) { | |
| const interval = setInterval(() => { | |
| this._hostnamesToFallback.clear(); | |
| }, fallbackDuration * 1000); | |
| /* istanbul ignore next: There is no `interval.unref()` when running inside an Electron renderer */ | |
| if (interval.unref) { | |
| interval.unref(); | |
| } | |
| this._fallbackInterval = interval; | |
| } | |
| this.lookup = this.lookup.bind(this); | |
| this.lookupAsync = this.lookupAsync.bind(this); | |
| } | |
| set servers(servers) { | |
| this.clear(); | |
| this._resolver.setServers(servers); | |
| } | |
| get servers() { | |
| return this._resolver.getServers(); | |
| } | |
| lookup(hostname, options, callback) { | |
| if (typeof options === 'function') { | |
| callback = options; | |
| options = {}; | |
| } else if (typeof options === 'number') { | |
| options = { | |
| family: options | |
| }; | |
| } | |
| if (!callback) { | |
| throw new Error('Callback must be a function.'); | |
| } | |
| // eslint-disable-next-line promise/prefer-await-to-then | |
| this.lookupAsync(hostname, options).then(result => { | |
| if (options.all) { | |
| callback(null, result); | |
| } else { | |
| callback(null, result.address, result.family, result.expires, result.ttl, result.source); | |
| } | |
| }, callback); | |
| } | |
| async lookupAsync(hostname, options = {}) { | |
| if (typeof options === 'number') { | |
| options = { | |
| family: options | |
| }; | |
| } | |
| let cached = await this.query(hostname); | |
| if (options.family === 6) { | |
| const filtered = cached.filter(entry => entry.family === 6); | |
| if (options.hints & V4MAPPED) { | |
| if ((supportsALL && options.hints & ALL) || filtered.length === 0) { | |
| map4to6(cached); | |
| } else { | |
| cached = filtered; | |
| } | |
| } else { | |
| cached = filtered; | |
| } | |
| } else if (options.family === 4) { | |
| cached = cached.filter(entry => entry.family === 4); | |
| } | |
| if (options.hints & ADDRCONFIG) { | |
| const {_iface} = this; | |
| cached = cached.filter(entry => entry.family === 6 ? _iface.has6 : _iface.has4); | |
| } | |
| if (cached.length === 0) { | |
| const error = new Error(`cacheableLookup ENOTFOUND ${hostname}`); | |
| error.code = 'ENOTFOUND'; | |
| error.hostname = hostname; | |
| throw error; | |
| } | |
| if (options.all) { | |
| return cached; | |
| } | |
| return cached[0]; | |
| } | |
| async query(hostname) { | |
| let source = 'cache'; | |
| let cached = await this._cache.get(hostname); | |
| if (cached) { | |
| this.stats.cache++; | |
| } | |
| if (!cached) { | |
| const pending = this._pending[hostname]; | |
| if (pending) { | |
| this.stats.cache++; | |
| cached = await pending; | |
| } else { | |
| source = 'query'; | |
| const newPromise = this.queryAndCache(hostname); | |
| this._pending[hostname] = newPromise; | |
| this.stats.query++; | |
| try { | |
| cached = await newPromise; | |
| } finally { | |
| delete this._pending[hostname]; | |
| } | |
| } | |
| } | |
| cached = cached.map(entry => { | |
| return {...entry, source}; | |
| }); | |
| return cached; | |
| } | |
| async _resolve(hostname) { | |
| // ANY is unsafe as it doesn't trigger new queries in the underlying server. | |
| const [A, AAAA] = await Promise.all([ | |
| ignoreNoResultErrors(this._resolve4(hostname, ttl)), | |
| ignoreNoResultErrors(this._resolve6(hostname, ttl)) | |
| ]); | |
| let aTtl = 0; | |
| let aaaaTtl = 0; | |
| let cacheTtl = 0; | |
| const now = Date.now(); | |
| for (const entry of A) { | |
| entry.family = 4; | |
| entry.expires = now + (entry.ttl * 1000); | |
| aTtl = Math.max(aTtl, entry.ttl); | |
| } | |
| for (const entry of AAAA) { | |
| entry.family = 6; | |
| entry.expires = now + (entry.ttl * 1000); | |
| aaaaTtl = Math.max(aaaaTtl, entry.ttl); | |
| } | |
| if (A.length > 0) { | |
| if (AAAA.length > 0) { | |
| cacheTtl = Math.min(aTtl, aaaaTtl); | |
| } else { | |
| cacheTtl = aTtl; | |
| } | |
| } else { | |
| cacheTtl = aaaaTtl; | |
| } | |
| return { | |
| entries: [ | |
| ...A, | |
| ...AAAA | |
| ], | |
| cacheTtl | |
| }; | |
| } | |
| async _lookup(hostname) { | |
| try { | |
| const [A, AAAA] = await Promise.all([ | |
| // Passing {all: true} doesn't return all IPv4 and IPv6 entries. | |
| // See https://github.com/szmarczak/cacheable-lookup/issues/42 | |
| ignoreNoResultErrors(this._dnsLookup(hostname, all4)), | |
| ignoreNoResultErrors(this._dnsLookup(hostname, all6)) | |
| ]); | |
| return { | |
| entries: [ | |
| ...A, | |
| ...AAAA | |
| ], | |
| cacheTtl: 0 | |
| }; | |
| } catch { | |
| return { | |
| entries: [], | |
| cacheTtl: 0 | |
| }; | |
| } | |
| } | |
| async _set(hostname, data, cacheTtl) { | |
| if (this.maxTtl > 0 && cacheTtl > 0) { | |
| cacheTtl = Math.min(cacheTtl, this.maxTtl) * 1000; | |
| data[kExpires] = Date.now() + cacheTtl; | |
| try { | |
| await this._cache.set(hostname, data, cacheTtl); | |
| } catch (error) { | |
| this.lookupAsync = async () => { | |
| const cacheError = new Error('Cache Error. Please recreate the CacheableLookup instance.'); | |
| cacheError.cause = error; | |
| throw cacheError; | |
| }; | |
| } | |
| if (isIterable(this._cache)) { | |
| this._tick(cacheTtl); | |
| } | |
| } | |
| } | |
| async queryAndCache(hostname) { | |
| if (this._hostnamesToFallback.has(hostname)) { | |
| return this._dnsLookup(hostname, all); | |
| } | |
| let query = await this._resolve(hostname); | |
| if (query.entries.length === 0 && this._dnsLookup) { | |
| query = await this._lookup(hostname); | |
| if (query.entries.length !== 0 && this.fallbackDuration > 0) { | |
| // Use `dns.lookup(...)` for that particular hostname | |
| this._hostnamesToFallback.add(hostname); | |
| } | |
| } | |
| const cacheTtl = query.entries.length === 0 ? this.errorTtl : query.cacheTtl; | |
| await this._set(hostname, query.entries, cacheTtl); | |
| return query.entries; | |
| } | |
| _tick(ms) { | |
| const nextRemovalTime = this._nextRemovalTime; | |
| if (!nextRemovalTime || ms < nextRemovalTime) { | |
| clearTimeout(this._removalTimeout); | |
| this._nextRemovalTime = ms; | |
| this._removalTimeout = setTimeout(() => { | |
| this._nextRemovalTime = false; | |
| let nextExpiry = Infinity; | |
| const now = Date.now(); | |
| for (const [hostname, entries] of this._cache) { | |
| const expires = entries[kExpires]; | |
| if (now >= expires) { | |
| this._cache.delete(hostname); | |
| } else if (expires < nextExpiry) { | |
| nextExpiry = expires; | |
| } | |
| } | |
| if (nextExpiry !== Infinity) { | |
| this._tick(nextExpiry - now); | |
| } | |
| }, ms); | |
| /* istanbul ignore next: There is no `timeout.unref()` when running inside an Electron renderer */ | |
| if (this._removalTimeout.unref) { | |
| this._removalTimeout.unref(); | |
| } | |
| } | |
| } | |
| install(agent) { | |
| verifyAgent(agent); | |
| if (kCacheableLookupCreateConnection in agent) { | |
| throw new Error('CacheableLookup has been already installed'); | |
| } | |
| agent[kCacheableLookupCreateConnection] = agent.createConnection; | |
| agent[kCacheableLookupInstance] = this; | |
| agent.createConnection = (options, callback) => { | |
| if (!('lookup' in options)) { | |
| options.lookup = this.lookup; | |
| } | |
| return agent[kCacheableLookupCreateConnection](options, callback); | |
| }; | |
| } | |
| uninstall(agent) { | |
| verifyAgent(agent); | |
| if (agent[kCacheableLookupCreateConnection]) { | |
| if (agent[kCacheableLookupInstance] !== this) { | |
| throw new Error('The agent is not owned by this CacheableLookup instance'); | |
| } | |
| agent.createConnection = agent[kCacheableLookupCreateConnection]; | |
| delete agent[kCacheableLookupCreateConnection]; | |
| delete agent[kCacheableLookupInstance]; | |
| } | |
| } | |
| updateInterfaceInfo() { | |
| const {_iface} = this; | |
| this._iface = getIfaceInfo(); | |
| if ((_iface.has4 && !this._iface.has4) || (_iface.has6 && !this._iface.has6)) { | |
| this._cache.clear(); | |
| } | |
| } | |
| clear(hostname) { | |
| if (hostname) { | |
| this._cache.delete(hostname); | |
| return; | |
| } | |
| this._cache.clear(); | |
| } | |
| } | |
Xet Storage Details
- Size:
- 9.89 kB
- Xet hash:
- 8d8f2185bd3ab2e2e6d6f02516ed79e9a4758d7d8ad3229b63e676d60c8eeaae
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.