heapstash
Version:
HeapStash is a library that allows for easy caching in Node.js, with many advanced features such as TTL, maximum items in memory cache, external cache support, and more.
253 lines • 11.6 kB
JavaScript
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
const debug_1 = __importDefault(require("debug"));
const primaryDebug = (0, debug_1.default)("HeapStash");
const primaryDebugGet = (0, debug_1.default)("HeapStash:get");
const primaryDebugFetch = (0, debug_1.default)("HeapStash:fetch");
const primaryDebugPut = (0, debug_1.default)("HeapStash:put");
const primaryDebugRemove = (0, debug_1.default)("HeapStash:remove");
const primaryDebugClear = (0, debug_1.default)("HeapStash:clear");
class HeapStash {
constructor(settings = {}) {
primaryDebug(`Creating cache instance with settings: ${JSON.stringify(settings)}`);
this.settings = settings;
this.plugins = [];
const refreshinternalcache = () => {
Object.keys(this._.internalcache).forEach((key) => {
const item = this._.internalcache[key];
if (item.ttl <= Date.now()) {
delete this._.internalcache[key];
}
});
};
this._ = {
"internalcache": {},
"internalcachearray": [],
"inprogressfetchpromises": {},
refreshinternalcache
};
}
async get(id, settings = {}) {
primaryDebugGet(`Getting item with ID: ${id}`);
if (!id) {
throw new Error("ID required to get item from cache.");
}
if (this.settings.idPrefix) {
id = `${this.settings.idPrefix}${id}`;
primaryDebugGet(`Changing ID to include idPrefix. New ID: ${id}`);
}
function checkItem(item) {
if (item) {
primaryDebugGet(`Got item: ${JSON.stringify(item)}`);
if (typeof item.ttl === "number") {
if (item.ttl > Date.now()) {
primaryDebugGet(`Returning item: ${item}`);
return item;
}
else {
primaryDebugGet("TTL in past, item expired. Not returning anything.");
}
}
else {
primaryDebugGet(`Returning item: ${item}`);
return item;
}
}
else {
primaryDebugGet("No item passed into checkItem. Not returning anything.");
}
}
let item = this._.internalcache[id];
primaryDebugGet(`Item in internal cache: ${item}`);
let checkItemResult = checkItem(item);
if (checkItemResult) {
return checkItemResult.data;
}
else if (!settings.internalCacheOnly) {
primaryDebugGet("Item not in internal cache, running plugins.");
for (let i = 0; i < this.plugins.length; i++) {
const plugin = this.plugins[i];
try {
const result = await plugin.run("get")(id);
primaryDebugGet(`Found item using cache plugins: ${result}`);
item = result;
checkItemResult = checkItem(item);
if (checkItemResult) {
return checkItemResult.data;
}
}
catch (e) { }
}
primaryDebugGet("Done running plugins.");
}
}
fetch(id, settings, retrieveFunction) {
primaryDebugFetch(`Fetching item with ID: ${id}`);
if (!id) {
throw new Error("ID required to fetch item.");
}
if (typeof settings === "function") {
retrieveFunction = settings;
settings = {};
}
let internalID = id;
if (this.settings.idPrefix) {
internalID = `${this.settings.idPrefix}${id}`;
primaryDebugFetch(`Changing ID to include idPrefix. New ID: ${internalID}`);
}
if (!retrieveFunction) {
throw new Error("Retrieve function required to fetch item.");
}
return new Promise(async (resolve, reject) => {
const cacheItem = await this.get(id, { "internalCacheOnly": true });
if (cacheItem) {
primaryDebugFetch(`Resolving with cached item: ${cacheItem}`);
resolve(cacheItem);
}
else if (this._.inprogressfetchpromises[internalID]) {
primaryDebugFetch("Fetch in progress, adding to queue to resolve when complete.");
this._.inprogressfetchpromises[internalID].push({ resolve, reject });
}
else {
primaryDebugFetch("Fetch not in progress, running retrieveFunction.");
this._.inprogressfetchpromises[internalID] = [];
let result, inprogressfetchpromises;
let action = resolve;
try {
let didRunRetrieveFunction = false;
if (!settings.internalCacheOnly) {
primaryDebugFetch("Running get plugin with plugins");
try {
result = await this.get(id);
}
catch (e) { }
}
if (!result) {
primaryDebugFetch("Running retrieve function");
result = await retrieveFunction(id);
didRunRetrieveFunction = true;
primaryDebugFetch("Done running retrieve function");
}
primaryDebugFetch(`Got result from retrieveFunction, putting item: ${result}`);
const putSettings = Object.assign({}, settings);
if (!putSettings.internalCacheOnly && !didRunRetrieveFunction) {
putSettings.internalCacheOnly = true;
}
await this.put(id, result, putSettings);
inprogressfetchpromises = this._.inprogressfetchpromises[internalID].map((item) => item.resolve);
}
catch (e) {
primaryDebugFetch(`Got error from retrieveFunction: ${e}`);
result = e;
action = reject;
inprogressfetchpromises = this._.inprogressfetchpromises[internalID].map((item) => item.reject);
}
primaryDebugFetch(`Resolving all ${inprogressfetchpromises.length} pending promises.`);
inprogressfetchpromises.forEach((action) => action(result));
delete this._.inprogressfetchpromises[internalID];
primaryDebugFetch("Resolving original promise.");
action(result);
}
});
}
async put(id, item, settings = {}) {
primaryDebugPut(`Putting item: ${item} with ID: ${id}`);
if (!id || Array.isArray(id) && id.length <= 0) {
throw new Error("ID required to put item in cache.");
}
if (item === undefined || item === null) {
throw new Error("Item required to put item in cache.");
}
if (this.settings.idPrefix) {
id = `${this.settings.idPrefix}${id}`;
primaryDebugPut(`Changing ID to include idPrefix. New ID: ${id}`);
}
while (this.settings.maxItems && this._.internalcachearray.length >= this.settings.maxItems) {
primaryDebugPut("Too many items, removing item.");
const removeID = this._.internalcachearray.shift();
await this.remove(removeID, { "internalCacheOnly": true });
primaryDebugPut(`Removed item: ${removeID}`);
}
const storedObject = { "data": item };
const currentTime = Date.now();
const ttlToUse = settings.ttl || this.settings.ttl;
if (ttlToUse && settings.ttl !== false) {
storedObject.ttl = currentTime + ttlToUse;
primaryDebugPut(`Adding TTL: ${storedObject.ttl}`);
}
primaryDebugPut(`Storing item in cache: ${JSON.stringify(storedObject)}`);
const ids = Array.isArray(id) ? id : [id];
for (const id of ids) {
this._.internalcache[id] = storedObject;
this._.internalcachearray.push(id);
}
if (!settings.internalCacheOnly) {
const pluginStoredObject = Object.assign({}, storedObject);
if (settings.pluginTTL !== undefined && settings.pluginTTL !== ttlToUse) {
if (settings.pluginTTL === false) {
primaryDebugPut("Deleting ttl for plugin storage.");
delete pluginStoredObject.ttl;
}
else {
primaryDebugPut(`Setting ttl to ${settings.pluginTTL} for plugin storage.`);
pluginStoredObject.ttl = currentTime + settings.pluginTTL;
}
}
primaryDebugPut(`Storing item in plugins: ${JSON.stringify(pluginStoredObject)}`);
await Promise.all(this.plugins.map((plugin) => plugin.run("put")(id, pluginStoredObject)));
primaryDebugPut("Done storing item in plugins.");
}
else {
primaryDebugPut("Not storing item from plugins since internalCacheOnly is set to false.");
}
}
async remove(id, settings = {}) {
primaryDebugRemove(`Removing item with ID: ${id}`);
if (!id) {
throw new Error("ID required to delete item from cache.");
}
if (this._.internalcache[id]) {
const internalcachearrayIndex = this._.internalcachearray.findIndex((item) => item === id);
if (internalcachearrayIndex >= 0) {
primaryDebugRemove(`Found item to remove in internalcachearray at index: ${internalcachearrayIndex}`);
this._.internalcachearray.splice(internalcachearrayIndex, 1);
}
delete this._.internalcache[id];
}
if (!settings.internalCacheOnly) {
primaryDebugRemove(`Removing item from plugins with ID: ${id}`);
await Promise.all(this.plugins.map((plugin) => plugin.run("remove")(id)));
primaryDebugRemove("Done removing item in plugins.");
}
else {
primaryDebugRemove("Not removing item from plugins since internalCacheOnly is set to false.");
}
}
async clear(settings = {}) {
primaryDebugClear("Clearing cache");
this._.internalcache = {};
this._.internalcachearray = [];
if (!settings.internalCacheOnly) {
primaryDebugClear("Clearing cache from plugins");
await Promise.all(this.plugins.map((plugin) => plugin.run("clear")()));
primaryDebugClear("Done clearing cache in plugins.");
}
else {
primaryDebugClear("Not clearing cache from plugins since internalCacheOnly is set to false.");
}
}
}
const plugin_1 = __importDefault(require("./plugin"));
const DynamoDB_1 = __importDefault(require("./plugin/DynamoDB"));
const FileSystem_1 = __importDefault(require("./plugin/FileSystem"));
const MongoDB_1 = __importDefault(require("./plugin/MongoDB"));
const Redis_1 = __importDefault(require("./plugin/Redis"));
plugin_1.default.DynamoDB = DynamoDB_1.default;
plugin_1.default.FileSystem = FileSystem_1.default;
plugin_1.default.MongoDB = MongoDB_1.default;
plugin_1.default.Redis = Redis_1.default;
HeapStash.Plugin = plugin_1.default;
module.exports = HeapStash;
//# sourceMappingURL=index.js.map
;