UNPKG

express-route-cache

Version:
786 lines (778 loc) 26.9 kB
var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __markAsModule = (target) => __defProp(target, "__esModule", {value: true}); var __commonJS = (cb, mod) => function __require() { return mod || (0, cb[Object.keys(cb)[0]])((mod = {exports: {}}).exports, mod), mod.exports; }; var __reExport = (target, module2, desc) => { if (module2 && typeof module2 === "object" || typeof module2 === "function") { for (let key of __getOwnPropNames(module2)) if (!__hasOwnProp.call(target, key) && key !== "default") __defProp(target, key, {get: () => module2[key], enumerable: !(desc = __getOwnPropDesc(module2, key)) || desc.enumerable}); } return target; }; var __toModule = (module2) => { return __reExport(__markAsModule(__defProp(module2 != null ? __create(__getProtoOf(module2)) : {}, "default", module2 && module2.__esModule && "default" in module2 ? {get: () => module2.default, enumerable: true} : {value: module2, enumerable: true})), module2); }; // node_modules/clone/clone.js var require_clone = __commonJS({ "node_modules/clone/clone.js"(exports2, module2) { var clone = function() { "use strict"; function _instanceof(obj, type) { return type != null && obj instanceof type; } var nativeMap; try { nativeMap = Map; } catch (_) { nativeMap = function() { }; } var nativeSet; try { nativeSet = Set; } catch (_) { nativeSet = function() { }; } var nativePromise; try { nativePromise = Promise; } catch (_) { nativePromise = function() { }; } function clone2(parent, circular, depth, prototype, includeNonEnumerable) { if (typeof circular === "object") { depth = circular.depth; prototype = circular.prototype; includeNonEnumerable = circular.includeNonEnumerable; circular = circular.circular; } var allParents = []; var allChildren = []; var useBuffer = typeof Buffer != "undefined"; if (typeof circular == "undefined") circular = true; if (typeof depth == "undefined") depth = Infinity; function _clone(parent2, depth2) { if (parent2 === null) return null; if (depth2 === 0) return parent2; var child; var proto; if (typeof parent2 != "object") { return parent2; } if (_instanceof(parent2, nativeMap)) { child = new nativeMap(); } else if (_instanceof(parent2, nativeSet)) { child = new nativeSet(); } else if (_instanceof(parent2, nativePromise)) { child = new nativePromise(function(resolve, reject) { parent2.then(function(value) { resolve(_clone(value, depth2 - 1)); }, function(err) { reject(_clone(err, depth2 - 1)); }); }); } else if (clone2.__isArray(parent2)) { child = []; } else if (clone2.__isRegExp(parent2)) { child = new RegExp(parent2.source, __getRegExpFlags(parent2)); if (parent2.lastIndex) child.lastIndex = parent2.lastIndex; } else if (clone2.__isDate(parent2)) { child = new Date(parent2.getTime()); } else if (useBuffer && Buffer.isBuffer(parent2)) { if (Buffer.allocUnsafe) { child = Buffer.allocUnsafe(parent2.length); } else { child = new Buffer(parent2.length); } parent2.copy(child); return child; } else if (_instanceof(parent2, Error)) { child = Object.create(parent2); } else { if (typeof prototype == "undefined") { proto = Object.getPrototypeOf(parent2); child = Object.create(proto); } else { child = Object.create(prototype); proto = prototype; } } if (circular) { var index = allParents.indexOf(parent2); if (index != -1) { return allChildren[index]; } allParents.push(parent2); allChildren.push(child); } if (_instanceof(parent2, nativeMap)) { parent2.forEach(function(value, key) { var keyChild = _clone(key, depth2 - 1); var valueChild = _clone(value, depth2 - 1); child.set(keyChild, valueChild); }); } if (_instanceof(parent2, nativeSet)) { parent2.forEach(function(value) { var entryChild = _clone(value, depth2 - 1); child.add(entryChild); }); } for (var i in parent2) { var attrs; if (proto) { attrs = Object.getOwnPropertyDescriptor(proto, i); } if (attrs && attrs.set == null) { continue; } child[i] = _clone(parent2[i], depth2 - 1); } if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(parent2); for (var i = 0; i < symbols.length; i++) { var symbol = symbols[i]; var descriptor = Object.getOwnPropertyDescriptor(parent2, symbol); if (descriptor && !descriptor.enumerable && !includeNonEnumerable) { continue; } child[symbol] = _clone(parent2[symbol], depth2 - 1); if (!descriptor.enumerable) { Object.defineProperty(child, symbol, { enumerable: false }); } } } if (includeNonEnumerable) { var allPropertyNames = Object.getOwnPropertyNames(parent2); for (var i = 0; i < allPropertyNames.length; i++) { var propertyName = allPropertyNames[i]; var descriptor = Object.getOwnPropertyDescriptor(parent2, propertyName); if (descriptor && descriptor.enumerable) { continue; } child[propertyName] = _clone(parent2[propertyName], depth2 - 1); Object.defineProperty(child, propertyName, { enumerable: false }); } } return child; } return _clone(parent, depth); } clone2.clonePrototype = function clonePrototype(parent) { if (parent === null) return null; var c = function() { }; c.prototype = parent; return new c(); }; function __objToStr(o) { return Object.prototype.toString.call(o); } clone2.__objToStr = __objToStr; function __isDate(o) { return typeof o === "object" && __objToStr(o) === "[object Date]"; } clone2.__isDate = __isDate; function __isArray(o) { return typeof o === "object" && __objToStr(o) === "[object Array]"; } clone2.__isArray = __isArray; function __isRegExp(o) { return typeof o === "object" && __objToStr(o) === "[object RegExp]"; } clone2.__isRegExp = __isRegExp; function __getRegExpFlags(re) { var flags = ""; if (re.global) flags += "g"; if (re.ignoreCase) flags += "i"; if (re.multiline) flags += "m"; return flags; } clone2.__getRegExpFlags = __getRegExpFlags; return clone2; }(); if (typeof module2 === "object" && module2.exports) { module2.exports = clone; } } }); // node_modules/node-cache/lib/node_cache.js var require_node_cache = __commonJS({ "node_modules/node-cache/lib/node_cache.js"(exports2, module2) { (function() { var EventEmitter, NodeCache2, clone, splice = [].splice, boundMethodCheck = function(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new Error("Bound instance method accessed before binding"); } }, indexOf = [].indexOf; clone = require_clone(); EventEmitter = require("events").EventEmitter; module2.exports = NodeCache2 = function() { class NodeCache3 extends EventEmitter { constructor(options = {}) { super(); this.get = this.get.bind(this); this.mget = this.mget.bind(this); this.set = this.set.bind(this); this.mset = this.mset.bind(this); this.del = this.del.bind(this); this.take = this.take.bind(this); this.ttl = this.ttl.bind(this); this.getTtl = this.getTtl.bind(this); this.keys = this.keys.bind(this); this.has = this.has.bind(this); this.getStats = this.getStats.bind(this); this.flushAll = this.flushAll.bind(this); this.flushStats = this.flushStats.bind(this); this.close = this.close.bind(this); this._checkData = this._checkData.bind(this); this._check = this._check.bind(this); this._isInvalidKey = this._isInvalidKey.bind(this); this._wrap = this._wrap.bind(this); this._getValLength = this._getValLength.bind(this); this._error = this._error.bind(this); this._initErrors = this._initErrors.bind(this); this.options = options; this._initErrors(); this.data = {}; this.options = Object.assign({ forceString: false, objectValueSize: 80, promiseValueSize: 80, arrayValueSize: 40, stdTTL: 0, checkperiod: 600, useClones: true, deleteOnExpire: true, enableLegacyCallbacks: false, maxKeys: -1 }, this.options); if (this.options.enableLegacyCallbacks) { console.warn("WARNING! node-cache legacy callback support will drop in v6.x"); ["get", "mget", "set", "del", "ttl", "getTtl", "keys", "has"].forEach((methodKey) => { var oldMethod; oldMethod = this[methodKey]; this[methodKey] = function(...args) { var cb, err, ref, res; ref = args, [...args] = ref, [cb] = splice.call(args, -1); if (typeof cb === "function") { try { res = oldMethod(...args); cb(null, res); } catch (error1) { err = error1; cb(err); } } else { return oldMethod(...args, cb); } }; }); } this.stats = { hits: 0, misses: 0, keys: 0, ksize: 0, vsize: 0 }; this.validKeyTypes = ["string", "number"]; this._checkData(); return; } get(key) { var _ret, err; boundMethodCheck(this, NodeCache3); if ((err = this._isInvalidKey(key)) != null) { throw err; } if (this.data[key] != null && this._check(key, this.data[key])) { this.stats.hits++; _ret = this._unwrap(this.data[key]); return _ret; } else { this.stats.misses++; return void 0; } } mget(keys) { var _err, err, i, key, len, oRet; boundMethodCheck(this, NodeCache3); if (!Array.isArray(keys)) { _err = this._error("EKEYSTYPE"); throw _err; } oRet = {}; for (i = 0, len = keys.length; i < len; i++) { key = keys[i]; if ((err = this._isInvalidKey(key)) != null) { throw err; } if (this.data[key] != null && this._check(key, this.data[key])) { this.stats.hits++; oRet[key] = this._unwrap(this.data[key]); } else { this.stats.misses++; } } return oRet; } set(key, value, ttl) { var _err, err, existent; boundMethodCheck(this, NodeCache3); if (this.options.maxKeys > -1 && this.stats.keys >= this.options.maxKeys) { _err = this._error("ECACHEFULL"); throw _err; } if (this.options.forceString && !typeof value === "string") { value = JSON.stringify(value); } if (ttl == null) { ttl = this.options.stdTTL; } if ((err = this._isInvalidKey(key)) != null) { throw err; } existent = false; if (this.data[key]) { existent = true; this.stats.vsize -= this._getValLength(this._unwrap(this.data[key], false)); } this.data[key] = this._wrap(value, ttl); this.stats.vsize += this._getValLength(value); if (!existent) { this.stats.ksize += this._getKeyLength(key); this.stats.keys++; } this.emit("set", key, value); return true; } mset(keyValueSet) { var _err, err, i, j, key, keyValuePair, len, len1, ttl, val; boundMethodCheck(this, NodeCache3); if (this.options.maxKeys > -1 && this.stats.keys + keyValueSet.length >= this.options.maxKeys) { _err = this._error("ECACHEFULL"); throw _err; } for (i = 0, len = keyValueSet.length; i < len; i++) { keyValuePair = keyValueSet[i]; ({key, val, ttl} = keyValuePair); if (ttl && typeof ttl !== "number") { _err = this._error("ETTLTYPE"); throw _err; } if ((err = this._isInvalidKey(key)) != null) { throw err; } } for (j = 0, len1 = keyValueSet.length; j < len1; j++) { keyValuePair = keyValueSet[j]; ({key, val, ttl} = keyValuePair); this.set(key, val, ttl); } return true; } del(keys) { var delCount, err, i, key, len, oldVal; boundMethodCheck(this, NodeCache3); if (!Array.isArray(keys)) { keys = [keys]; } delCount = 0; for (i = 0, len = keys.length; i < len; i++) { key = keys[i]; if ((err = this._isInvalidKey(key)) != null) { throw err; } if (this.data[key] != null) { this.stats.vsize -= this._getValLength(this._unwrap(this.data[key], false)); this.stats.ksize -= this._getKeyLength(key); this.stats.keys--; delCount++; oldVal = this.data[key]; delete this.data[key]; this.emit("del", key, oldVal.v); } } return delCount; } take(key) { var _ret; boundMethodCheck(this, NodeCache3); _ret = this.get(key); if (_ret != null) { this.del(key); } return _ret; } ttl(key, ttl) { var err; boundMethodCheck(this, NodeCache3); ttl || (ttl = this.options.stdTTL); if (!key) { return false; } if ((err = this._isInvalidKey(key)) != null) { throw err; } if (this.data[key] != null && this._check(key, this.data[key])) { if (ttl >= 0) { this.data[key] = this._wrap(this.data[key].v, ttl, false); } else { this.del(key); } return true; } else { return false; } } getTtl(key) { var _ttl, err; boundMethodCheck(this, NodeCache3); if (!key) { return void 0; } if ((err = this._isInvalidKey(key)) != null) { throw err; } if (this.data[key] != null && this._check(key, this.data[key])) { _ttl = this.data[key].t; return _ttl; } else { return void 0; } } keys() { var _keys; boundMethodCheck(this, NodeCache3); _keys = Object.keys(this.data); return _keys; } has(key) { var _exists; boundMethodCheck(this, NodeCache3); _exists = this.data[key] != null && this._check(key, this.data[key]); return _exists; } getStats() { boundMethodCheck(this, NodeCache3); return this.stats; } flushAll(_startPeriod = true) { boundMethodCheck(this, NodeCache3); this.data = {}; this.stats = { hits: 0, misses: 0, keys: 0, ksize: 0, vsize: 0 }; this._killCheckPeriod(); this._checkData(_startPeriod); this.emit("flush"); } flushStats() { boundMethodCheck(this, NodeCache3); this.stats = { hits: 0, misses: 0, keys: 0, ksize: 0, vsize: 0 }; this.emit("flush_stats"); } close() { boundMethodCheck(this, NodeCache3); this._killCheckPeriod(); } _checkData(startPeriod = true) { var key, ref, value; boundMethodCheck(this, NodeCache3); ref = this.data; for (key in ref) { value = ref[key]; this._check(key, value); } if (startPeriod && this.options.checkperiod > 0) { this.checkTimeout = setTimeout(this._checkData, this.options.checkperiod * 1e3, startPeriod); if (this.checkTimeout != null && this.checkTimeout.unref != null) { this.checkTimeout.unref(); } } } _killCheckPeriod() { if (this.checkTimeout != null) { return clearTimeout(this.checkTimeout); } } _check(key, data) { var _retval; boundMethodCheck(this, NodeCache3); _retval = true; if (data.t !== 0 && data.t < Date.now()) { if (this.options.deleteOnExpire) { _retval = false; this.del(key); } this.emit("expired", key, this._unwrap(data)); } return _retval; } _isInvalidKey(key) { var ref; boundMethodCheck(this, NodeCache3); if (ref = typeof key, indexOf.call(this.validKeyTypes, ref) < 0) { return this._error("EKEYTYPE", { type: typeof key }); } } _wrap(value, ttl, asClone = true) { var livetime, now, oReturn, ttlMultiplicator; boundMethodCheck(this, NodeCache3); if (!this.options.useClones) { asClone = false; } now = Date.now(); livetime = 0; ttlMultiplicator = 1e3; if (ttl === 0) { livetime = 0; } else if (ttl) { livetime = now + ttl * ttlMultiplicator; } else { if (this.options.stdTTL === 0) { livetime = this.options.stdTTL; } else { livetime = now + this.options.stdTTL * ttlMultiplicator; } } return oReturn = { t: livetime, v: asClone ? clone(value) : value }; } _unwrap(value, asClone = true) { if (!this.options.useClones) { asClone = false; } if (value.v != null) { if (asClone) { return clone(value.v); } else { return value.v; } } return null; } _getKeyLength(key) { return key.toString().length; } _getValLength(value) { boundMethodCheck(this, NodeCache3); if (typeof value === "string") { return value.length; } else if (this.options.forceString) { return JSON.stringify(value).length; } else if (Array.isArray(value)) { return this.options.arrayValueSize * value.length; } else if (typeof value === "number") { return 8; } else if (typeof (value != null ? value.then : void 0) === "function") { return this.options.promiseValueSize; } else if (typeof Buffer !== "undefined" && Buffer !== null ? Buffer.isBuffer(value) : void 0) { return value.length; } else if (value != null && typeof value === "object") { return this.options.objectValueSize * Object.keys(value).length; } else if (typeof value === "boolean") { return 8; } else { return 0; } } _error(type, data = {}) { var error; boundMethodCheck(this, NodeCache3); error = new Error(); error.name = type; error.errorcode = type; error.message = this.ERRORS[type] != null ? this.ERRORS[type](data) : "-"; error.data = data; return error; } _initErrors() { var _errMsg, _errT, ref; boundMethodCheck(this, NodeCache3); this.ERRORS = {}; ref = this._ERRORS; for (_errT in ref) { _errMsg = ref[_errT]; this.ERRORS[_errT] = this.createErrorMessage(_errMsg); } } createErrorMessage(errMsg) { return function(args) { return errMsg.replace("__key", args.type); }; } } ; NodeCache3.prototype._ERRORS = { "ENOTFOUND": "Key `__key` not found", "ECACHEFULL": "Cache max keys amount exceeded", "EKEYTYPE": "The key argument has to be of type `string` or `number`. Found: `__key`", "EKEYSTYPE": "The keys argument has to be an array.", "ETTLTYPE": "The ttl argument has to be a number." }; return NodeCache3; }.call(this); }).call(exports2); } }); // node_modules/node-cache/index.js var require_node_cache2 = __commonJS({ "node_modules/node-cache/index.js"(exports2, module2) { (function() { var exports3; exports3 = module2.exports = require_node_cache(); exports3.version = "5.1.2"; }).call(exports2); } }); // src/cache/in-memory-cache.ts var import_node_cache = __toModule(require_node_cache2()); var InMemoryCache = class { constructor() { this._cache = new import_node_cache.default(); } async get(key) { return this._cache.get(key); } async set(key, value, ttlSeconds) { return this._cache.set(key, value, ttlSeconds); } async has(key) { return this._cache.has(key); } async del(key) { const deleted = this._cache.del(key); return deleted > 0; } async flush() { return this._cache.flushAll(); } async keys() { return this._cache.keys(); } }; // src/request-queue/request-queue.ts var RequestQueue = class { constructor() { this.queues = {}; } init(key) { this.queues[key] = []; } add(key, requestFunction) { if (!this.queues[key]) this.init(key); this.queues[key].push(requestFunction); } has(key) { return this.queues[key] !== void 0; } drain(key) { if (!this.queues[key]) return false; while (this.queues[key].length > 0) { this.queues[key].shift()(); } delete this.queues[key]; return true; } flush(key) { delete this.queues[key]; } }; // src/express-route-cache.ts var ExpressRouteCache = class { constructor(config = {}) { this.cacher = config.cache || new InMemoryCache(); this.queue = new RequestQueue(); } cache(ttlSeconds) { return async (req, res, next) => { const cacheKey = req.originalUrl; const cacheResponse = await this.cacher.get(cacheKey); if (cacheResponse !== void 0) { if (cacheResponse.json) return res.json(cacheResponse.body); return res.send(cacheResponse.body); } if (this.queue.has(cacheKey)) { this.queue.add(cacheKey, async () => { const cacheResponse2 = await this.cacher.get(cacheKey); if (cacheResponse2 !== void 0) { if (cacheResponse2.json) return res.json(cacheResponse2.body); return res.send(cacheResponse2.body); } }); return; } res._originalSend = res.send; res._originalJson = res.json; if (!this.queue.has(cacheKey)) this.queue.init(cacheKey); let wasCached = false; const sender = ({body, json}) => { if (res.statusCode < 400) { this.cacher.set(cacheKey, {body, json}, ttlSeconds); } wasCached = true; this.queue.drain(cacheKey); if (json) return res._originalJson(body); return res._originalSend(body); }; res.send = (body) => wasCached ? res._originalSend(body) : sender({body}); res.json = (body) => sender({body, json: true}); next(); }; } async has(cacheKey) { return this.cacher.has(cacheKey); } async del(cacheKey) { return this.cacher.del(cacheKey); } async flush() { return this.cacher.flush(); } }; // src/index.ts module.exports = ExpressRouteCache;