const FORBIDDEN_PROPERTY_VALUE_KEYS = new Set(['__proto__', 'constructor', 'prototype']);
function isScalarPropValue(value) {
    return (value === null
        || typeof value === 'string'
        || typeof value === 'number'
        || typeof value === 'boolean'
        || value instanceof Uint8Array);
}
function isPropValueArray(value, seen) {
    if (!Array.isArray(value)) {
        return false;
    }
    if (seen.has(value)) {
        return false;
    }
    seen.add(value);
    try {
        return value.every((entry) => isPropValueWithSeen(entry, seen));
    }
    finally {
        seen.delete(value);
    }
}
function isPropValueObjectCandidate(value) {
    if (value === null || typeof value !== 'object') {
        return false;
    }
    return isNonArrayPlainObject(value);
}
function isForbiddenPropertyValueKey(key) {
    return FORBIDDEN_PROPERTY_VALUE_KEYS.has(key);
}
function canTraversePropValueObject(value, seen) {
    return isPropValueObjectCandidate(value) && !seen.has(value);
}
function isPropValueObject(value, seen) {
    if (!canTraversePropValueObject(value, seen)) {
        return false;
    }
    seen.add(value);
    try {
        return propValueObjectEntriesAreValid(value, seen);
    }
    finally {
        seen.delete(value);
    }
}
function propValueObjectEntriesAreValid(value, seen) {
    for (const [key, entry] of Object.entries(value)) {
        if (isForbiddenPropertyValueKey(key) || !isPropValueWithSeen(entry, seen)) {
            return false;
        }
    }
    return true;
}
function isNonArrayPlainObject(value) {
    if (Array.isArray(value) || value instanceof Uint8Array) {
        return false;
    }
    return Object.getPrototypeOf(value) === Object.prototype
        || Object.getPrototypeOf(value) === null;
}
export function isPropValue(value) {
    return isPropValueWithSeen(value, new WeakSet());
}
function isPropValueWithSeen(value, seen) {
    return isScalarPropValue(value)
        || isPropValueArray(value, seen)
        || isPropValueObject(value, seen);
}
