// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
import LRU from 'lru-cache';
import { needs, isEncryptionMaterial, isDecryptionMaterial, } from '@aws-crypto/material-management';
export function getLocalCryptographicMaterialsCache(capacity, proactiveFrequency = 1000 * 60) {
    const cache = new LRU({
        max: capacity,
        dispose(_key, value) {
            /* Zero out the unencrypted dataKey, when the material is removed from the cache. */
            value.response.zeroUnencryptedDataKey();
        },
    });
    (function proactivelyTryAndEvictTail() {
        const timeout = setTimeout(() => {
            mayEvictTail();
            proactivelyTryAndEvictTail();
        }, proactiveFrequency);
        /* In Node.js the event loop will _only_ exit if there are no outstanding events.
         * This means that if I did nothing the event loop would *always* be blocked.
         * This is unfortunate and very bad for things like Lambda.
         * So, I tell Node.js to not wait for this timer.
         * See: https://nodejs.org/api/timers.html#timers_timeout_unref
         */
        // @ts-ignore
        timeout.unref && timeout.unref();
    })();
    return {
        putEncryptionMaterial(key, material, plaintextLength, maxAge) {
            /* Precondition: putEncryptionMaterial plaintextLength can not be negative. */
            needs(plaintextLength >= 0, 'Malformed plaintextLength');
            /* Precondition: Only cache EncryptionMaterial. */
            needs(isEncryptionMaterial(material), 'Malformed response.');
            /* Precondition: Only cache EncryptionMaterial that is cacheSafe. */
            needs(material.suite.cacheSafe, 'Can not cache non-cache safe material');
            const entry = Object.seal({
                response: material,
                bytesEncrypted: plaintextLength,
                messagesEncrypted: 1,
                now: Date.now(),
            });
            cache.set(key, entry, maxAge);
        },
        putDecryptionMaterial(key, material, maxAge) {
            /* Precondition: Only cache DecryptionMaterial. */
            needs(isDecryptionMaterial(material), 'Malformed response.');
            /* Precondition: Only cache DecryptionMaterial that is cacheSafe. */
            needs(material.suite.cacheSafe, 'Can not cache non-cache safe material');
            const entry = Object.seal({
                response: material,
                bytesEncrypted: 0,
                messagesEncrypted: 0,
                now: Date.now(),
            });
            cache.set(key, entry, maxAge);
        },
        getEncryptionMaterial(key, plaintextLength) {
            /* Precondition: plaintextLength can not be negative. */
            needs(plaintextLength >= 0, 'Malformed plaintextLength');
            const entry = cache.get(key);
            /* Check for early return (Postcondition): If this key does not have an EncryptionMaterial, return false. */
            if (!entry)
                return false;
            /* Postcondition: Only return EncryptionMaterial. */
            needs(isEncryptionMaterial(entry.response), 'Malformed response.');
            entry.bytesEncrypted += plaintextLength;
            entry.messagesEncrypted += 1;
            return entry;
        },
        getDecryptionMaterial(key) {
            const entry = cache.get(key);
            /* Check for early return (Postcondition): If this key does not have a DecryptionMaterial, return false. */
            if (!entry)
                return false;
            /* Postcondition: Only return DecryptionMaterial. */
            needs(isDecryptionMaterial(entry.response), 'Malformed response.');
            return entry;
        },
        del(key) {
            cache.del(key);
        },
    };
    function mayEvictTail() {
        // @ts-ignore
        const { tail } = cache.dumpLru();
        /* Check for early return (Postcondition) UNTESTED: If there is no tail, then the cache is empty. */
        if (!tail)
            return;
        /* The underlying Yallist tail Node has a `value`.
         * This value is a lru-cache Entry and has a `key`.
         */
        const { key } = tail.value;
        // Peek will evict, but not update the "recently used"-ness of the key.
        cache.peek(key);
    }
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZ2V0X2xvY2FsX2NyeXB0b2dyYXBoaWNfbWF0ZXJpYWxzX2NhY2hlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL2dldF9sb2NhbF9jcnlwdG9ncmFwaGljX21hdGVyaWFsc19jYWNoZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxvRUFBb0U7QUFDcEUsc0NBQXNDO0FBRXRDLE9BQU8sR0FBRyxNQUFNLFdBQVcsQ0FBQTtBQUMzQixPQUFPLEVBSUwsS0FBSyxFQUNMLG9CQUFvQixFQUNwQixvQkFBb0IsR0FDckIsTUFBTSxpQ0FBaUMsQ0FBQTtBQVN4QyxNQUFNLFVBQVUsbUNBQW1DLENBR2pELFFBQWdCLEVBQ2hCLHFCQUE2QixJQUFJLEdBQUcsRUFBRTtJQUV0QyxNQUFNLEtBQUssR0FBRyxJQUFJLEdBQUcsQ0FBbUI7UUFDdEMsR0FBRyxFQUFFLFFBQVE7UUFDYixPQUFPLENBQUMsSUFBSSxFQUFFLEtBQUs7WUFDakIsb0ZBQW9GO1lBQ3BGLEtBQUssQ0FBQyxRQUFRLENBQUMsc0JBQXNCLEVBQUUsQ0FBQTtRQUN6QyxDQUFDO0tBQ0YsQ0FBQyxDQWVEO0lBQUEsQ0FBQyxTQUFTLDBCQUEwQjtRQUNuQyxNQUFNLE9BQU8sR0FBRyxVQUFVLENBQUMsR0FBRyxFQUFFO1lBQzlCLFlBQVksRUFBRSxDQUFBO1lBQ2QsMEJBQTBCLEVBQUUsQ0FBQTtRQUM5QixDQUFDLEVBQUUsa0JBQWtCLENBQUMsQ0FBQTtRQUN0Qjs7Ozs7V0FLRztRQUNILGFBQWE7UUFDYixPQUFPLENBQUMsS0FBSyxJQUFJLE9BQU8sQ0FBQyxLQUFLLEVBQUUsQ0FBQTtJQUNsQyxDQUFDLENBQUMsRUFBRSxDQUFBO0lBRUosT0FBTztRQUNMLHFCQUFxQixDQUNuQixHQUFXLEVBQ1gsUUFBK0IsRUFDL0IsZUFBdUIsRUFDdkIsTUFBZTtZQUVmLDhFQUE4RTtZQUM5RSxLQUFLLENBQUMsZUFBZSxJQUFJLENBQUMsRUFBRSwyQkFBMkIsQ0FBQyxD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