function executar(count = 1, country = 'DE', formatted = true) {
const BRAZIL_DDDS = [11, 12, 13, 14, 15, 16, 17, 18, 19, 21, 22, 24, 27, 28, 31, 32, 33, 34, 35, 37, 38, 41, 42, 43, 44, 45, 46, 47, 48, 49, 51, 53, 54, 55, 61, 62, 63, 64, 65, 66, 67, 68, 69, 71, 73, 74, 75, 77, 79, 81, 82, 83, 84, 85, 86, 87, 88, 89, 91, 92, 93, 94, 95, 96, 97, 98, 99];
const ADDRESS_CITIES = [
{ id: 'SP', city: 'São Paulo', state: 'SP', prefix: '010' },
{ id: 'RJ', city: 'Rio de Janeiro', state: 'RJ', prefix: '200' },
{ id: 'MG', city: 'Belo Horizonte', state: 'MG', prefix: '301' },
{ id: 'PR', city: 'Curitiba', state: 'PR', prefix: '800' },
{ id: 'RS', city: 'Porto Alegre', state: 'RS', prefix: '900' },
{ id: 'BA', city: 'Salvador', state: 'BA', prefix: '400' },
{ id: 'PE', city: 'Recife', state: 'PE', prefix: '500' },
{ id: 'DF', city: 'Brasília', state: 'DF', prefix: '700' },
];
const URL_ALPHABET = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz-';
function secureInt(maximum) {
if (!Number.isSafeInteger(maximum) || maximum < 1 || maximum > 0x100000000)
throw new Error('Intervalo aleatório inválido.');
const buffer = new Uint32Array(1);
const limit = Math.floor(0x100000000 / maximum) * maximum;
do {
crypto.getRandomValues(buffer);
} while (buffer[0] >= limit);
return buffer[0] % maximum;
}
function randomText(length, alphabet = '0123456789') {
if (!Number.isInteger(length) || length < 1 || length > 256 || !alphabet)
throw new Error('Comprimento ou alfabeto inválido.');
return Array.from({ length }, () => alphabet[secureInt(alphabet.length)]).join('');
}
function luhnDigit(base) {
if (!/^\d+$/.test(base))
throw new Error('A base deve conter somente dígitos.');
const total = [...base].reverse().reduce((sum, character, index) => {
const value = Number(character) * (index % 2 === 0 ? 2 : 1);
return sum + (value > 9 ? value - 9 : value);
}, 0);
return String((10 - total % 10) % 10);
}
function nifDigit(base) {
if (!/^\d{8}$/.test(base))
throw new Error('A base do NIF deve conter oito dígitos.');
const remainder = [...base].reduce((sum, c, i) => sum + Number(c) * (9 - i), 0) % 11;
return String(remainder < 2 ? 0 : 11 - remainder);
}
function germanVatDigit(base) {
if (!/^[1-9]\d{7}$/.test(base))
throw new Error('Base de VAT alemão inválida.');
let product = 10;
for (const character of base)
product = (((product + Number(character)) % 10 || 10) * 2) % 11;
return String((11 - product) % 10);
}
function mod97(value) {
if (!/^[A-Z0-9]+$/.test(value))
throw new Error('Use letras maiúsculas e dígitos no IBAN.');
let remainder = 0;
for (const character of value) {
const decimal = /[A-Z]/.test(character) ? String(character.charCodeAt(0) - 55) : character;
for (const digit of decimal)
remainder = (remainder * 10 + Number(digit)) % 97;
}
return remainder;
}
function completeIban(country, bban) {
const patterns = { DE: /^\d{18}$/, PT: /^\d{21}$/, GB: /^[A-Z]{4}\d{14}$/ };
if (!patterns[country]?.test(bban))
throw new Error('País ou estrutura de IBAN não suportado.');
return country + String(98 - mod97(bban + country + '00')).padStart(2, '0') + bban;
}
const uuidNode = new Uint8Array(6);
let uuidNodeInitialized = false;
let lastV1Timestamp = BigInt(0);
let v1Sequence = 0;
function uuidString(bytes) {
const hex = [...bytes].map(byte => byte.toString(16).padStart(2, '0')).join('');
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
}
function generateUuid(version, now = Date.now()) {
if (!Number.isSafeInteger(now) || now < 0 || now > 281474976710655)
throw new Error('Timestamp inválido.');
const bytes = crypto.getRandomValues(new Uint8Array(16));
if (version === '4')
bytes[6] = (bytes[6] & 15) | 64;
else if (version === '7') {
let timestamp = BigInt(now);
for (let index = 5; index >= 0; index--) {
bytes[index] = Number(timestamp & BigInt(255));
timestamp >>= BigInt(8);
}
bytes[6] = (bytes[6] & 15) | 112;
}
else if (version === '1') {
if (!uuidNodeInitialized) {
crypto.getRandomValues(uuidNode);
uuidNode[0] |= 1; // Multicast: nó aleatório, sem MAC do dispositivo.
v1Sequence = secureInt(16384);
uuidNodeInitialized = true;
}
let timestamp = BigInt(now) * BigInt(10000) + BigInt('122192928000000000');
if (timestamp <= lastV1Timestamp) {
if (timestamp < lastV1Timestamp - BigInt(10000))
v1Sequence = (v1Sequence + 1) & 16383;
timestamp = lastV1Timestamp + BigInt(1);
}
lastV1Timestamp = timestamp;
const low = Number(timestamp & BigInt('0xffffffff'));
bytes[0] = low >>> 24;
bytes[1] = low >>> 16;
bytes[2] = low >>> 8;
bytes[3] = low;
const mid = Number((timestamp >> BigInt(32)) & BigInt('0xffff'));
bytes[4] = mid >>> 8;
bytes[5] = mid;
const high = Number((timestamp >> BigInt(48)) & BigInt('0xfff'));
bytes[6] = (high >>> 8) | 16;
bytes[7] = high;
bytes[8] = v1Sequence >>> 8;
bytes[9] = v1Sequence;
bytes.set(uuidNode, 10);
}
else
throw new Error('Escolha UUID v1, v4 ou v7.');
bytes[8] = (bytes[8] & 63) | 128;
return uuidString(bytes);
}
function choose(options, key, allowed, fallback) {
const value = options[key] ?? fallback;
if (!allowed.includes(value))
throw new Error(`Opção inválida: ${key}.`);
return value;
}
function generateBrazilianPhone(options) {
const ddd = options.ddd ?? '11';
if (!BRAZIL_DDDS.includes(Number(ddd)))
throw new Error('DDD inválido.');
const type = choose(options, 'phoneType', ['mobile', 'landline'], 'mobile');
const local = type === 'mobile' ? '9' + randomText(8) : String(secureInt(4) + 2) + randomText(7);
return { ddd, local };
}
function generateData(kind, options = {}) {
const formatted = options.formatted !== 'false';
switch (kind) {
case 'ssn': {
let area;
do {
area = secureInt(899) + 1;
} while (area === 666);
const parts = [String(area).padStart(3, '0'), String(secureInt(99) + 1).padStart(2, '0'), String(secureInt(9999) + 1).padStart(4, '0')];
return parts.join(formatted ? '-' : '');
}
case 'nif': {
const prefix = choose(options, 'nifType', ['person', 'company'], 'person') === 'company' ? '5' : String(secureInt(3) + 1);
const base = prefix + randomText(7);
return base + nifDigit(base);
}
case 'vat': {
const country = choose(options, 'country', ['PT', 'DE', 'FR', 'IT'], 'PT');
if (country === 'PT')
return 'PT' + generateData('nif', { nifType: 'company' });
if (country === 'DE') {
const base = String(secureInt(9) + 1) + randomText(7);
return 'DE' + base + germanVatDigit(base);
}
if (country === 'FR') {
const base = String(secureInt(9) + 1) + randomText(7);
const siren = base + luhnDigit(base);
return 'FR' + String((12 + 3 * (Number(siren) % 97)) % 97).padStart(2, '0') + siren;
}
const base = String(secureInt(9999999) + 1).padStart(7, '0') + String(secureInt(100) + 1).padStart(3, '0');
return 'IT' + base + luhnDigit(base);
}
case 'iban': {
const country = choose(options, 'country', ['PT', 'DE', 'GB'], 'DE');
let bban = randomText(18);
if (country === 'GB')
bban = randomText(4, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ') + randomText(14);
if (country === 'PT') {
const base = randomText(19);
bban = base + String(98 - mod97(base + '00')).padStart(2, '0');
}
const iban = completeIban(country, bban);
return formatted ? iban.match(/.{1,4}/g).join(' ') : iban;
}
case 'passport': {
const country = choose(options, 'country', ['BR', 'PT', 'US'], 'BR');
// BR/PT: duas letras + seis dígitos. EUA: formato numérico legado.
return country === 'US' ? String(secureInt(9) + 1) + randomText(8) : randomText(2, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ') + randomText(6);
}
case 'phone-br': {
const { ddd, local } = generateBrazilianPhone(options);
return formatted ? `(${ddd}) ${local.slice(0, -4)}-${local.slice(-4)}` : ddd + local;
}
case 'phone-international': {
const country = choose(options, 'country', ['BR', 'US', 'PT', 'GB'], 'US');
if (country === 'BR') {
const { ddd, local } = generateBrazilianPhone(options);
return '+55' + ddd + local;
}
if (country === 'US')
return '+1202555' + String(secureInt(100) + 100).padStart(4, '0');
if (country === 'GB')
return '+447700900' + String(secureInt(1000)).padStart(3, '0');
return '+3519' + randomText(1, '1236') + randomText(7);
}
case 'email': {
const domain = choose(options, 'domain', ['example.com', 'example.net', 'example.org'], 'example.com');
return 'dev.' + randomText(12, 'abcdefghijklmnopqrstuvwxyz0123456789') + '@' + domain;
}
case 'temporary-email': return 'temp.' + randomText(16, 'abcdefghijklmnopqrstuvwxyz0123456789') + '@example.net';
case 'cep': {
const value = randomText(8);
return formatted ? value.slice(0, 5) + '-' + value.slice(5) : value;
}
case 'address': {
const cityId = choose(options, 'city', ADDRESS_CITIES.map(city => city.id), 'SP');
const city = ADDRESS_CITIES.find(item => item.id === cityId);
const street = ['Rua Exemplo', 'Avenida de Teste', 'Travessa Modelo'][secureInt(3)];
const cep = city.prefix + randomText(5);
return JSON.stringify({ logradouro: street, numero: secureInt(9999) + 1, complemento: 'Apto ' + (secureInt(200) + 1), bairro: 'Bairro Exemplo', cidade: city.city, uf: city.state, cep: cep.slice(0, 5) + '-' + cep.slice(5), pais: 'Brasil' }, null, 2);
}
case 'coordinates': {
const precision = Number(options.precision ?? '6');
if (!Number.isInteger(precision) || precision < 0 || precision > 8)
throw new Error('Escolha entre 0 e 8 casas decimais.');
// Área uniforme sobre a esfera: latitude derivada de sin(latitude).
const latitude = Math.asin(secureInt(0x100000000) / 0x100000000 * 2 - 1) * 180 / Math.PI;
const longitude = secureInt(0x100000000) / 0x100000000 * 360 - 180;
return `${latitude.toFixed(precision)}, ${longitude.toFixed(precision)}`;
}
case 'uuid': return generateUuid(choose(options, 'version', ['1', '4', '7'], '4'));
case 'nanoid': {
const length = Number(options.length ?? '21');
if (!Number.isInteger(length) || length < 1 || length > 128)
throw new Error('O NanoID deve ter entre 1 e 128 caracteres.');
return randomText(length, URL_ALPHABET);
}
default: throw new Error('Gerador não suportado.');
}
}
if (!Number.isInteger(count) || count < 1 || count > 100)
throw new Error('Quantidade: de 1 a 100');
if (typeof formatted !== 'boolean')
throw new Error('Máscara deve ser booleana');
const values = new Set();
for (let attempts = 0; values.size < count && attempts < count * 30; attempts++) {
values.add(generateData('iban', { country, formatted: String(formatted) }));
}
if (values.size !== count)
throw new Error('Não foi possível completar o lote sem repetições');
return [...values].join('\n');
}