"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.createLazyMap = exports.createCacheMap = exports.Mapping = void 0;
const lru_cache_1 = require("lru-cache");
const utils_1 = require("./utils");
/*
 * A map that lazily creates values. The factory function is called only when a key
 * is accessed for the first time. Subsequent calls to `get()` return the cached value
 * unless it has been evicted due to `maxSize` or `maxAge` limits.
 *
 * It is possible to implement e.g. positive cache by using `isCacheableValue()`
 * config option. If that method returns `false`, the value is not stored to cache.
 * Note that using this option doesn't change the concurrent promise handling:
 * also in this case all concurrent `get()` calls are grouped so that only one
 * call to `valueFactory` is made. (If we wouldn't group these calls, all concurrent
 * `get()` calls were cache misses, i.e. affecting significantly cases where the
 * `isCacheableValue()` returns `true`.)
 */
class Mapping {
    delegate;
    pendingPromises = new Map();
    opts;
    /**
     * Prefer constructing the class via createCacheMap() and createLazyMap()
     *
     * @internal
     **/
    constructor(opts) {
        if ('maxSize' in opts) {
            this.delegate = new lru_cache_1.LRUCache({
                maxSize: opts.maxSize,
                sizeCalculation: () => 1,
                ttl: opts.maxAge
            });
        }
        else {
            this.delegate = new Map();
        }
        this.opts = {
            isCacheableValue: () => true,
            ...opts
        };
    }
    async get(key) {
        const lookupKey = (0, utils_1.formLookupKey)(key);
        const pendingPromise = this.pendingPromises.get(lookupKey);
        if (pendingPromise !== undefined) {
            return await pendingPromise;
        }
        else {
            let item = this.delegate.get(lookupKey);
            if (item === undefined) {
                const promise = this.opts.valueFactory(key);
                this.pendingPromises.set(lookupKey, promise);
                let value;
                try {
                    value = await promise;
                }
                finally {
                    this.pendingPromises.delete(lookupKey);
                }
                item = { key, value };
                if (this.opts.isCacheableValue(value)) {
                    this.delegate.set(lookupKey, item);
                }
            }
            return item.value;
        }
    }
    set(key, value) {
        this.delegate.set((0, utils_1.formLookupKey)(key), { key, value });
    }
    invalidate(predicate) {
        for (const [lookupKey, item] of this.delegate.entries()) {
            if (predicate(item.key)) {
                this.delegate.delete(lookupKey);
            }
        }
    }
    *values() {
        for (const item of this.delegate.values()) {
            yield item.value;
        }
    }
}
exports.Mapping = Mapping;
const createCacheMap = (opts) => {
    return new Mapping(opts);
};
exports.createCacheMap = createCacheMap;
const createLazyMap = (opts) => {
    return new Mapping(opts);
};
exports.createLazyMap = createLazyMap;
//# sourceMappingURL=Mapping.js.map