Adiciona uma extensão MV3 (Firefox e Chromium/Brave) que insere CPF ou CNPJ
válido direto no campo focado, pelo menu de contexto de qualquer input,
textarea ou contenteditable.
Extensões rodam em sandbox sem acesso a processos, então não há como invocar
os binários cpfgen/cnpjgen; fazer isso exigiria um host de native messaging
registrado no SO. Como os algoritmos são curtos e puros, foram portados para
JS em extension/generator.js, a partir de main.go (CPF) e de
cnpj_generator/cnpj/generate.go (CNPJ), trocando math/rand por
crypto.getRandomValues. test_generator.mjs compara as duas implementações
para que não divirjam.
A inserção usa document.execCommand("insertText"), que emite os eventos
nativos e portanto funciona em campos controlados por React/Vue — atribuir
.value diretamente seria descartado por esses frameworks.
Somente desktop: o Firefox para Android não implementa a API menus.
Testes carregam a extensão de verdade nos dois navegadores e verificam que os
8 itens de menu são criados e que a inserção funciona nos três tipos de campo.
101 lines
3.5 KiB
JavaScript
101 lines
3.5 KiB
JavaScript
// Valida CPFs/CNPJs vindos do Go e do porte JS usando uma checagem
|
|
// independente (recalcula os DVs a partir dos 9/12 primeiros caracteres).
|
|
import {
|
|
generateCPF, formatCPF,
|
|
generateCNPJ, generateCNPJAlpha, formatCNPJ,
|
|
} from "./generator.js";
|
|
import { execFileSync } from "node:child_process";
|
|
import { existsSync } from "node:fs";
|
|
import path from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
|
|
function validCPF(s) {
|
|
if (!/^\d{11}$/.test(s)) return false;
|
|
const d = [...s].map(Number);
|
|
let sum = 0;
|
|
for (let i = 0; i < 9; i++) sum += d[i] * (10 - i);
|
|
let r = sum % 11;
|
|
if (d[9] !== (r < 2 ? 0 : 11 - r)) return false;
|
|
sum = 0;
|
|
for (let i = 0; i < 10; i++) sum += d[i] * (11 - i);
|
|
r = sum % 11;
|
|
return d[10] === (r < 2 ? 0 : 11 - r);
|
|
}
|
|
|
|
const P1 = [5,4,3,2,9,8,7,6,5,4,3,2];
|
|
const P2 = [6,5,4,3,2,9,8,7,6,5,4,3,2];
|
|
const val = (c) => c.charCodeAt(0) - 48;
|
|
|
|
function validCNPJ(s) {
|
|
if (!/^[0-9A-Z]{12}\d{2}$/.test(s)) return false;
|
|
const dv = (str, t) => {
|
|
let tot = 0;
|
|
for (let i = 0; i < t.length; i++) tot += val(str[i]) * t[i];
|
|
const r = tot % 11;
|
|
return r >= 2 ? 11 - r : 0;
|
|
};
|
|
if (Number(s[12]) !== dv(s, P1)) return false;
|
|
return Number(s[13]) === dv(s.slice(0, 13), P2);
|
|
}
|
|
|
|
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
const CPF = path.join(here, "..", "bin", "cpfgen");
|
|
// cnpjgen vive em outro repo, ao lado deste.
|
|
const CNPJ = path.join(here, "..", "..", "cnpj_generator", "bin", "cnpjgen");
|
|
const run = (bin, args) => execFileSync(bin, args, { encoding: "utf8" }).trim();
|
|
|
|
let fail = 0;
|
|
const check = (label, ok) => {
|
|
if (!ok) { fail++; console.log(`FAIL ${label}`); }
|
|
};
|
|
|
|
const N = 300;
|
|
|
|
// Referência: saída dos binários Go, quando compilados. Servem de oráculo para
|
|
// o porte JS; se não existirem, os testes do JS abaixo rodam do mesmo jeito.
|
|
const hasCPF = existsSync(CPF);
|
|
const hasCNPJ = existsSync(CNPJ);
|
|
if (!hasCPF) console.log("aviso: bin/cpfgen ausente (rode `make build`), pulando");
|
|
if (!hasCNPJ) console.log("aviso: cnpj_generator/bin/cnpjgen ausente, pulando");
|
|
|
|
for (let i = 0; i < N; i++) {
|
|
if (hasCPF) check("go cpf", validCPF(run(CPF, ["-num"])));
|
|
if (hasCNPJ) {
|
|
check("go cnpj", validCNPJ(run(CNPJ, ["-num"])));
|
|
check("go cnpj alpha", validCNPJ(run(CNPJ, ["-alpha", "-num"])));
|
|
}
|
|
}
|
|
|
|
// Porte JS.
|
|
for (let i = 0; i < N; i++) {
|
|
check("js cpf", validCPF(generateCPF()));
|
|
check("js cnpj", validCNPJ(generateCNPJ()));
|
|
check("js cnpj alpha", validCNPJ(generateCNPJAlpha()));
|
|
}
|
|
|
|
// Formatação deve casar com a do Go.
|
|
check("fmt cpf", /^\d{3}\.\d{3}\.\d{3}-\d{2}$/.test(formatCPF(generateCPF())));
|
|
check("fmt cnpj", /^\d{2}\.\d{3}\.\d{3}\/\d{4}-\d{2}$/.test(formatCNPJ(generateCNPJ())));
|
|
check("fmt cnpj alpha",
|
|
/^[0-9A-Z]{2}\.[0-9A-Z]{3}\.[0-9A-Z]{3}\/[0-9A-Z]{4}-\d{2}$/
|
|
.test(formatCNPJ(generateCNPJAlpha())));
|
|
|
|
// Formato do Go, lado a lado, para confirmar que o layout é idêntico.
|
|
console.log("go cpf :", run(CPF, []));
|
|
console.log("js cpf :", formatCPF(generateCPF()));
|
|
console.log("go cnpj :", run(CNPJ, []));
|
|
console.log("js cnpj :", formatCNPJ(generateCNPJ()));
|
|
console.log("go alpha :", run(CNPJ, ["-alpha"]));
|
|
console.log("js alpha :", formatCNPJ(generateCNPJAlpha()));
|
|
|
|
// Distribuição: nenhum dígito verificador deve ficar preso num valor só.
|
|
const dvs = new Set();
|
|
for (let i = 0; i < 200; i++) dvs.add(generateCPF().slice(9));
|
|
console.log(`\nDVs de CPF distintos em 200: ${dvs.size}`);
|
|
|
|
if (fail === 0) {
|
|
console.log("\nOK: geradores JS conferem com os binários Go");
|
|
} else {
|
|
console.log(`\n${fail} falhas`);
|
|
process.exit(1);
|
|
}
|