{"size": 2260, "ext": "js", "max_stars_repo_path": "cache.js", "max_stars_repo_name": "easyatworkas/cache-node", "max_stars_repo_stars_event_min_datetime": null, "max_stars_repo_stars_event_max_datetime": null, "max_issues_repo_path": "cache.js", "max_issues_repo_name": "easyatworkas/cache-node", "max_issues_repo_issues_event_min_datetime": null, "max_issues_repo_issues_event_max_datetime": null, "max_forks_repo_path": "cache.js", "max_forks_repo_name": "easyatworkas/cache-node", "max_forks_repo_forks_event_min_datetime": null, "max_forks_repo_forks_event_max_datetime": null}
{"max_stars_count": null, "max_issues_count": null, "max_forks_count": null, "avg_line_length": 20.9259259259, "max_line_length": 88, "alphanum_fraction": 0.4867256637}
'use strict';

class Cache {
    #driver;

    constructor(driver = null) {
        this.#driver = driver || new MemDriver();
    }

    getDriver() {
        return this.#driver;
    }

    async set(key, value, staleAfter = 0, expireAfter = 0) {
        return this.getDriver().set(key, {
            value,
            timestamp: time(),
            staleAfter,
            expireAfter
        });
    }

    async get(key, provider = null, staleAfter = 0, expireAfter = 0) {
        let item = await this.getDriver().get(key);

        if (item === null) {
            return provider ? this.set(key, provider(), staleAfter, expireAfter) : null;
        }

        const now = time();
        const age = now - item.timestamp;

        if (item.expireAfter && age >= item.expireAfter) {
            this.getDriver().del(key);

            return provider ? this.set(key, provider(), staleAfter, expireAfter) : null;
        }

        if (item.staleAfter && age >= item.staleAfter) {
            item.timestamp = now;

            this.getDriver().set(key, item);

            item.stale = true;

            if (provider) {
                setTimeout(() => {
                    this.set(key, provider(), staleAfter, expireAfter);
                });
            }
        }

        return item;
    }

    end() {
        this.getDriver().end();
    }
}

class MemDriver {
    #data = {};
    #interval;

    constructor() {
        this.#interval = setInterval(() => this.prune(), 60000);
    }

    prune() {
        const now = time();

        for (let key in this.#data) {
            const item = this.#data[key];

            if (item.expireAfter && now - item.timestamp >= item.expireAfter) {
                delete this.#data[key];
            }
        }
    }

    async set(key, value) {
        return this.#data[key] = value;
    }

    async get(key) {
        return key in this.#data ? this.#data[key] : null;
    }

    async del(key) {
        return delete this.#data[key];
    }

    end() {
        this.#data = {};

        if (this.#interval) {
            clearInterval(this.#interval);
            this.#interval = null;
        }
    }
}

function time() {
    return Math.floor(new Date() / 1000);
}

module.exports = Cache;
