"use strict";
/**
 * @license
 * Copyright Google LLC All Rights Reserved.
 *
 * Use of this source code is governed by an MIT-style license that can be
 * found in the LICENSE file at https://angular.io/license
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.memoize = void 0;
/**
 * A decorator that memoizes methods and getters.
 *
 * **Note**: Be cautious where and how to use this decorator as the size of the cache will grow unbounded.
 *
 * @see https://en.wikipedia.org/wiki/Memoization
 */
function memoize(target, propertyKey, descriptor) {
    const descriptorPropertyName = descriptor.get ? 'get' : 'value';
    const originalMethod = descriptor[descriptorPropertyName];
    if (typeof originalMethod !== 'function') {
        throw new Error('Memoize decorator can only be used on methods or get accessors.');
    }
    const cache = new Map();
    return {
        ...descriptor,
        [descriptorPropertyName]: function (...args) {
            for (const arg of args) {
                if (!isJSONSerializable(arg)) {
                    throw new Error(`Argument ${isNonPrimitive(arg) ? arg.toString() : arg} is JSON serializable.`);
                }
            }
            const key = JSON.stringify(args);
            if (cache.has(key)) {
                return cache.get(key);
            }
            const result = originalMethod.apply(this, args);
            cache.set(key, result);
            return result;
        },
    };
}
exports.memoize = memoize;
/** Method to check if value is a non primitive. */
function isNonPrimitive(value) {
    return ((value !== null && typeof value === 'object') ||
        typeof value === 'function' ||
        typeof value === 'symbol');
}
/** Method to check if the values are JSON serializable */
function isJSONSerializable(value) {
    if (!isNonPrimitive(value)) {
        // Can be seralized since it's a primitive.
        return true;
    }
    let nestedValues;
    if (Array.isArray(value)) {
        // It's an array, check each item.
        nestedValues = value;
    }
    else if (Object.prototype.toString.call(value) === '[object Object]') {
        // It's a plain object, check each value.
        nestedValues = Object.values(value);
    }
    if (!nestedValues || nestedValues.some((v) => !isJSONSerializable(v))) {
        return false;
    }
    return true;
}
