Source: Functions/Strings/index.js

/* Requires */
const crypto = require('crypto');
const fs = require('fs');

/* JSON */
const envInfo = JSON.parse(fs.readFileSync(`${__dirname}/utils.json`));

/**
 * Retorna todos os detalhes do ambiente (`envInfo`).
 *
 * @returns {Object} O objeto `envInfo`, que contém os detalhes do ambiente da execução.
 */
function ambientDetails() {
    /* Retorna a envData */
    return envInfo;
}

/**
 * Função robusta para verificar e processar strings Base64
 * com manipulação de prefixos e múltiplas conversões
 * @param {string} str - String a ser validada como Base64 (pode conter prefixo data URI)
 * @returns {object} Objeto contendo detalhes de validação, conversões e metadados
 */
function formatBase64(
    baseValue = envInfo.functions.base64.arguments.baseValue.value,
) {
    /* Inicializa resultados com valores padrão */
    envInfo.results.value = {
        valid: false,
        base64: baseValue,
        buffer: false,
        string: false,
        json: false,
        type: false,
        size: 0,
        decodedSize: 0,
    };

    /* Executa em try-catch caso o parse falhe */
    try {
        /* Verificação inicial de tipo e existência da string */
        if (typeof baseValue !== 'string' || !baseValue.trim()) {
            /* Retorna os dados na forma de como chegaram */
            return logging.postResults(envInfo);
        }

        /* Processamento do prefixo data URI com operação segura */
        const hasPrefix = baseValue.startsWith('data:') && baseValue.includes('base64,');
        const [prefix, base64Part = baseValue] = hasPrefix ? baseValue.split('base64,') : [null, baseValue];

        /* Extrai tipo MIME do prefixo de forma otimizada */
        const type = prefix?.replace(/^data:([^;]+).*/, '$1') || 'unknown';

        /* Regex aprimorada para validação Base64 com suporte a whitespace */
        const base64Regex = /^[\s]*([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{2}==)?[\s]*$/;
        const isValidBase64 = base64Regex.test(base64Part);

        /* Conversões condicionais somente se for Base64 válido */
        const conversions = {
            json: false,
        };

        /* Caso seja mesmo uma base64 */
        if (isValidBase64) {
            /* Faz trim dela para tirar caracteres invalidos */
            const cleanBase64 = base64Part.trim();

            /* Converte em Buffer */
            const buffer = Buffer.from(cleanBase64, 'base64');

            /* Converte em string */
            const stringValue = buffer.toString('utf8');

            /* Tenta parsear como JSON de forma segura */
            try {
                /* Inserindo como JSON se assim for */
                conversions.json = JSON.parse(stringValue);

                /* Ou em caso de erro onde não é JSON */
            } catch { /* Faz é nada, já será false automaticamente */ }

            /* Preenche objeto de resultados */
            envInfo.results.value.valid = true;
            envInfo.results.value.base64 = cleanBase64;
            envInfo.results.value.buffer = buffer;
            envInfo.results.value.string = stringValue;
            envInfo.results.value.json = conversions.json;
            envInfo.results.value.type = type;
            envInfo.results.value.size = buffer.length;
            envInfo.results.value.decodedSize = buffer.byteLength;

            /* Define o sucesso */
            envInfo.results.success = true;
        }

        /* Se der algum erro */
    } catch (error) {
        /* Insere tudo na envInfo em caso de erro */
        logging.echoError(error, envInfo, __dirname);
    }

    /* Retorna o valor resultante */
    return logging.postResults(envInfo);
}

/* Converte a primeira letra de uma string para maiúscula */
function capitalizeStrings(
    inputString = envInfo.functions.upperland.arguments.inputString.value,
    capitalizeEveryWord = envInfo.functions.upperland.arguments.capitalizeEveryWord.value,
) {
    /* Reseta o sucesso */
    envInfo.results.success = false;

    /* Define o valor padrão */
    envInfo.results.value = envInfo.parameters.upper.value;

    /* Try-Catch para casos de erro */
    try {
        /* Verifica se a entrada é uma string válida */
        if (typeof inputString === 'string') {
            /* Se deve capitalizar cada palavra */
            if (capitalizeEveryWord) {
                /* Separa a string em palavras */
                const words = inputString.split(' ');

                /* Capitaliza cada palavra */
                const capitalizedWords = words.map((word) => `${word.charAt(0).toUpperCase()}${word.slice(1)}`);

                /* Junta as palavras novamente */
                envInfo.results.value = capitalizedWords.join(' ');

                /* Se não for para fazer em cada palavra */
            } else {
                /* Capitaliza apenas a primeira letra da string */
                envInfo.results.value = `${inputString.charAt(0).toUpperCase()}${inputString.slice(1)}`;
            }

            /* Define sucesso */
            envInfo.results.success = true;
        }

        /* Se der algum erro */
    } catch (error) {
        /* Insere tudo na envInfo em caso de erro */
        logging.echoError(error, envInfo, __dirname);
    }

    /* Retorna o valor resultante */
    return logging.postResults(envInfo);
}

/* Cria uma string aleatória com o tamanho especificado */
function createRandomString(
    stringSize = envInfo.functions.generate.arguments.stringSize.value,
) {
    /* Reseta o sucesso */
    envInfo.results.success = false;

    /* Define o tamanho padrão */
    let cryptoSize = envInfo.functions.generate.arguments.stringSize.value;

    /* Try-Catch para casos de erro */
    try {
        /* Determina temporariamente o tamanho gerado */
        cryptoSize = Number(stringSize) || envInfo.functions.generate.arguments.stringSize.value;

        /* Gera a string aleatória */
        const randomBytes = crypto.randomBytes(cryptoSize);
        envInfo.results.value = randomBytes.toString('hex');

        /* Determina sucesso */
        envInfo.results.success = true;

        /* Se der algum erro */
    } catch (error) {
        /* Insere tudo na envInfo em caso de erro */
        logging.echoError(error, envInfo, __dirname);
    }

    /* Ajusta o tamanho */
    envInfo.results.value = envInfo.results.value.slice(0, cryptoSize);

    /* Retorna o valor gerado com o tamanho especificado */
    return logging.postResults(envInfo);
}

/* Converte um Buffer em uma string Data URI base64 */
function bufferToDataURI(
    mimetype = envInfo.functions.dataURI.arguments.mimetype.value,
    bufferData = envInfo.functions.dataURI.arguments.bufferData.value,
) {
    /* Reseta o sucesso */
    envInfo.results.success = false;

    /* Define a base64 padrão */
    envInfo.results.value = envInfo.parameters.base64.value;

    /* Try-Catch para casos de erro */
    try {
        /* Verifica se os parâmetros são válidos */
        if (typeof mimetype === 'string' && (bufferData instanceof Buffer || typeof bufferData === 'string')) {
            /* Converte o Buffer para base64 e cria a string Data URI */
            envInfo.results.value = `data:${mimetype};base64,${Buffer.from(bufferData).toString('base64')}`;
        }

        /* Determina sucesso */
        envInfo.results.success = true;

        /* Se der um erro */
    } catch (error) {
        /* Insere tudo na envInfo */
        logging.echoError(error, envInfo, __dirname);
    }

    /* Retorna a string Data URI ou o valor padrão de base64 */
    return logging.postResults(envInfo);
}

/* Conta as ocorrências de uma palavra em uma string */
function stringCounter(
    phrase = envInfo.functions.counter.arguments.phrase.value,
    specWord = envInfo.functions.counter.arguments.specWord.value,
) {
    /* Reseta o sucesso */
    envInfo.results.success = false;

    /* Define o resultado padrão */
    envInfo.results.value = envInfo.parameters.lengoter.value;

    /* Try-Catch para casos de erro */
    try {
        /* Verifica se ambos os parâmetros são strings */
        if (typeof phrase === 'string' && typeof specWord === 'string') {
            /* Faz a contagem de ocorrências */
            const occurrences = phrase.split(specWord).length - 1;

            /* Atualiza o valor no resultado */
            envInfo.results.value = occurrences;
        }

        /* Determina sucesso */
        envInfo.results.success = true;

        /* Se der erro */
    } catch (error) {
        /* Insere tudo na envInfo */
        logging.echoError(error, envInfo, __dirname);
    }

    /* Retorna o número de ocorrências ou o valor padrão */
    return logging.postResults(envInfo);
}

/* Encontra a posição de uma palavra em uma string a partir de uma determinada posição */
function findPosition(
    longText = envInfo.functions.searching.arguments.longText.value,
    wordFind = envInfo.functions.searching.arguments.wordFind.value,
    startAt = envInfo.functions.searching.arguments.startAt.value,
) {
    /* Reseta o sucesso */
    envInfo.results.success = false;

    /* Define o resultado padrão */
    envInfo.results.value = envInfo.parameters.lengoter.value;

    /* Try-Catch para casos de erro */
    try {
        /* Verifica se os parâmetros são válidos */
        if (typeof longText === 'string' && typeof wordFind === 'string' && !Number.isNaN(Number(startAt))) {
            /* Encontra a posição da palavra na string começando a busca da posição especificada */
            envInfo.results.value = longText.indexOf(wordFind, startAt);

            /* Verifica se a palavra foi encontrada */
            if (envInfo.results.value !== -1) {
                /* Se encontrada, atualiza a posição para a posição especificada */
                envInfo.results.value = longText.lastIndexOf(wordFind, startAt);

                /* Adiciona o comprimento da palavra para obter a posição final */
                envInfo.results.value += wordFind.length;
            }
        }

        /* Determina sucesso */
        envInfo.results.success = true;

        /* Se der um erro */
    } catch (error) {
        /* Insere tudo na envInfo */
        logging.echoError(error, envInfo, __dirname);
    }

    /* Retorna a posição ou o valor padrão */
    return logging.postResults(envInfo);
}

/* Aleatoriza as letras de uma string */
function shuffleChars(
    inputString = envInfo.functions.shuffle.arguments.inputString.value,
) {
    /* Reseta o sucesso */
    envInfo.results.success = false;

    /* Define o resultado padrão como a string original */
    envInfo.results.value = inputString;

    /* Try para caso dê algum erro */
    try {
        /* Verifica se o parâmetro é uma string válida */
        if (typeof inputString === 'string') {
            /* Converte a string em um array de caracteres */
            const charArray = inputString.split('');

            /* Embaralha os caracteres utilizando o algoritmo Fisher-Yates (Knuth Shuffle) */
            for (let i = charArray.length - 1; i > 0; i -= 1) {
                const j = Math.floor(Math.random() * (i + 1));
                [charArray[i], charArray[j]] = [charArray[j], charArray[i]];
            }

            /* Junta o array embaralhado de volta em uma string */
            envInfo.results.value = charArray.join('');
        }

        /* Determina sucesso */
        envInfo.results.success = true;

        /* E se der */
    } catch (error) {
        /* Registra o erro retornando o mesmo valor enviado */
        logging.echoError(error, envInfo, __dirname);
    }

    /* Retorna os resultados */
    return logging.postResults(envInfo);
}

/* Reset profundo para evitar circular */
/**
 * Restaura o ambiente e atualiza as exportações do módulo com a funcionalidade principal
 * @param {Object} [changeKey={}] - Chaves personalizadas para atualizar o envInfo
 * @param {Object} [envFile=envInfo] - Objeto com informações do ambiente
 * @param {string} [dirname=__dirname] - Caminho do diretório atual
 * @returns {Object} Exportações do módulo com todas as funções configuradas
 */
/* eslint-disable-next-line no-return-assign */
const resetLocal = (
    changeKey = {},
    envFile = envInfo,
    dirname = __dirname,
) => module.exports = logging.resetAmbient({
    functions: {
        [envInfo.exports.counter]: { value: stringCounter },
        [envInfo.exports.dataURI]: { value: bufferToDataURI },
        [envInfo.exports.messedup]: { value: logging.echoError },
        [envInfo.exports.env]: { value: ambientDetails },
        [envInfo.exports.shuffle]: { value: shuffleChars },
        [envInfo.exports.base64]: { value: formatBase64 },
        [envInfo.exports.generate]: { value: createRandomString },
        [envInfo.exports.reset]: { value: resetLocal },
        [envInfo.exports.searching]: { value: findPosition },
        [envInfo.exports.upperland]: { value: capitalizeStrings },
        [envInfo.exports.poswork]: { value: logging.postResults },
    },
    parameters: {
        location: { value: __filename },
    },
}, envFile, changeKey, dirname);
resetLocal();