UNPKG

v8r

Version:

A command-line JSON, YAML and TOML validator that's on your wavelength

98 lines (87 loc) 2.5 kB
import got from "got"; import Mutex from "p-mutex"; import logger from "./logger.js"; import { parseSchema } from "./parser.js"; class Cache { constructor(flatCache) { this.cache = flatCache; this.ttl = this.cache._cache.ttl || 0; this.callCounter = {}; this.locks = {}; this.callLimit = 10; if (this.ttl === 0) { this.cache.clear(); } } getMutex(url) { if (!(url in this.locks)) { this.locks[url] = new Mutex(); } return this.locks[url]; } async limitDepth(url) { /* It is possible to create cyclic dependencies with external references in JSON schema. We try to mitigate this issue during cache pre-warming. Ajv doesn't detect this when resolving external references, so we keep a count of how many times we've called the same URL. If we are calling the same URL over and over we've probably hit a circular external reference and we need to break the loop. */ const mutex = this.getMutex(url); await mutex.withLock(async () => { if (url in this.callCounter) { this.callCounter[url]++; } else { this.callCounter[url] = 1; } if (this.callCounter[url] > this.callLimit) { throw new Error( `Called ${url} >${this.callLimit} times. Possible circular reference.`, ); } }); } resetCounters() { this.callCounter = {}; } async fetch(url, persist = true) { await this.limitDepth(url); const cachedResponse = this.cache.get(url); if (cachedResponse !== undefined) { logger.debug(`Cache hit: using cached response from ${url}`); return cachedResponse.body; } try { logger.debug(`Cache miss: calling ${url}`); const resp = await got(url); const parsedBody = parseSchema(resp.body, url); if (this.ttl > 0) { this.cache.set(url, { body: parsedBody }); const finalUrl = resp.url; if (finalUrl !== url) { this.cache.set(finalUrl, { body: parsedBody }); } if (persist) { this.cache.save(true); } } return parsedBody; } catch (error) { if (error.response) { throw new Error(`Failed fetching ${url}\n${error.response.body}`, { cause: error, }); } throw new Error(`Failed fetching ${url}`, { cause: error }); } } persist() { this.cache.save(true); } get(key) { return this.cache.get(key); } } export { Cache };