cpf-cnpj-validator
Version:
Valida, formata e gera strings de CPF ou CNPJ, com suporte ao novo formato alfanumérico da RFB e adapters para joi, yup e zod.
220 lines (216 loc) • 6.25 kB
JavaScript
// src/core/modulo11.ts
function cpfDigit(digits) {
let sum = 0;
const n = digits.length;
for (let i = n - 1; i >= 0; i--) {
sum += (digits.charCodeAt(i) - 48) * (n - i + 1);
}
const mod = sum % 11;
return mod < 2 ? 0 : 11 - mod;
}
function cnpjDigit(digits) {
let sum = 0;
let weight = 2;
for (let i = digits.length - 1; i >= 0; i--) {
sum += (digits.charCodeAt(i) - 48) * weight;
weight = weight === 9 ? 2 : weight + 1;
}
const mod = sum % 11;
return mod < 2 ? 0 : 11 - mod;
}
// src/cnpj.ts
var BLACKLIST = /* @__PURE__ */ new Set([
"00000000000000",
"11111111111111",
"22222222222222",
"33333333333333",
"44444444444444",
"55555555555555",
"66666666666666",
"77777777777777",
"88888888888888",
"99999999999999"
]);
var STRICT_STRIP_REGEX = /[./-]/g;
var LOOSE_STRIP_REGEX = /[^\dA-Z]/g;
var STRICT_MASK_REGEX = /^(?:[\dA-Z]{2}\.[\dA-Z]{3}\.[\dA-Z]{3}\/[\dA-Z]{4}-\d{2}|[\dA-Z]{12}\d{2})$/;
var VALID_CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
function strip(value, strict) {
if (typeof value !== "string") return "";
const normalized = strict ? value : value.toUpperCase();
const regex = strict ? STRICT_STRIP_REGEX : LOOSE_STRIP_REGEX;
return normalized.replace(regex, "");
}
function format(value) {
return strip(value).replace(
/^([\dA-Z]{2})([\dA-Z]{3})([\dA-Z]{3})([\dA-Z]{4})(\d{2})$/,
"$1.$2.$3/$4-$5"
);
}
function isValid(value, strict) {
if (strict && (typeof value !== "string" || !STRICT_MASK_REGEX.test(value))) {
return false;
}
const stripped = strip(value, strict);
if (!stripped || stripped.length !== 14 || BLACKLIST.has(stripped)) {
return false;
}
if (!/^\d{2}$/.test(stripped.slice(-2))) {
return false;
}
let numbers = stripped.slice(0, 12);
numbers += cnpjDigit(numbers);
numbers += cnpjDigit(numbers);
return numbers.slice(-2) === stripped.slice(-2);
}
function generate(options) {
const opts = typeof options === "boolean" ? { formatted: options } : options ?? {};
let numbers = "";
for (let i = 0; i < 12; i++) {
numbers += VALID_CHARS.charAt(Math.floor(Math.random() * VALID_CHARS.length));
}
numbers += cnpjDigit(numbers);
numbers += cnpjDigit(numbers);
return opts.formatted ? format(numbers) : numbers;
}
var cnpj_default = { verifierDigit: cnpjDigit, strip, format, isValid, generate };
// src/cpf.ts
var BLACKLIST2 = /* @__PURE__ */ new Set([
"00000000000",
"11111111111",
"22222222222",
"33333333333",
"44444444444",
"55555555555",
"66666666666",
"77777777777",
"88888888888",
"99999999999",
"12345678909"
]);
var STRICT_STRIP_REGEX2 = /[.-]/g;
var LOOSE_STRIP_REGEX2 = /[^\d]/g;
var STRICT_MASK_REGEX2 = /^(?:\d{3}\.\d{3}\.\d{3}-\d{2}|\d{11})$/;
var FISCAL_REGION_BY_UF = {
DF: 1,
GO: 1,
MS: 1,
MT: 1,
TO: 1,
AC: 2,
AM: 2,
AP: 2,
PA: 2,
RO: 2,
RR: 2,
CE: 3,
MA: 3,
PI: 3,
AL: 4,
PB: 4,
PE: 4,
RN: 4,
BA: 5,
SE: 5,
MG: 6,
ES: 7,
RJ: 7,
SP: 8,
PR: 9,
SC: 9,
RS: 0
};
function strip2(value, strict) {
if (typeof value !== "string") return "";
const regex = strict ? STRICT_STRIP_REGEX2 : LOOSE_STRIP_REGEX2;
return value.replace(regex, "");
}
function format2(value) {
return strip2(value).replace(/^(\d{3})(\d{3})(\d{3})(\d{2})$/, "$1.$2.$3-$4");
}
function isValid2(value, strict) {
if (strict && (typeof value !== "string" || !STRICT_MASK_REGEX2.test(value))) {
return false;
}
const stripped = strip2(value, strict);
if (!stripped || stripped.length !== 11 || BLACKLIST2.has(stripped)) {
return false;
}
let numbers = stripped.slice(0, 9);
numbers += cpfDigit(numbers);
numbers += cpfDigit(numbers);
return numbers.slice(-2) === stripped.slice(-2);
}
function generate2(options) {
const opts = typeof options === "boolean" ? { formatted: options } : options ?? {};
let numbers = "";
for (let i = 0; i < 8; i++) {
numbers += Math.floor(Math.random() * 10);
}
if (opts.state !== void 0) {
if (!Object.hasOwn(FISCAL_REGION_BY_UF, opts.state)) {
throw new TypeError(
`UF '${opts.state}' desconhecida \u2014 use uma das: ${Object.keys(FISCAL_REGION_BY_UF).join(", ")}`
);
}
numbers += FISCAL_REGION_BY_UF[opts.state];
} else {
numbers += Math.floor(Math.random() * 10);
}
numbers += cpfDigit(numbers);
numbers += cpfDigit(numbers);
return opts.formatted ? format2(numbers) : numbers;
}
var cpf_default = { verifierDigit: cpfDigit, strip: strip2, format: format2, isValid: isValid2, generate: generate2, FISCAL_REGION_BY_UF };
// src/adapters/joi.ts
var joiValidator = (joi) => ({
type: "document",
base: joi.string(),
messages: {
"document.cpf": "CPF inv\xE1lido",
"document.cpf.custom": "{{#message}}",
"document.cnpj": "CNPJ inv\xE1lido",
"document.cnpj.custom": "{{#message}}"
},
rules: {
cpf: {
method(message) {
return this.$_addRule({ name: "cpf", args: { message } });
},
args: [
{
name: "message",
ref: false,
assert: (value) => value === void 0 || typeof value === "string",
message: "must be a string"
}
],
// biome-ignore lint/suspicious/noExplicitAny: helpers/args are untyped in joi extensions
validate(value, helpers, args) {
if (cpf_default.isValid(value)) return value;
return args.message ? helpers.error("document.cpf.custom", { message: args.message }) : helpers.error("document.cpf");
}
},
cnpj: {
method(message) {
return this.$_addRule({ name: "cnpj", args: { message } });
},
args: [
{
name: "message",
ref: false,
assert: (value) => value === void 0 || typeof value === "string",
message: "must be a string"
}
],
// biome-ignore lint/suspicious/noExplicitAny: helpers/args are untyped in joi extensions
validate(value, helpers, args) {
if (cnpj_default.isValid(value)) return value;
return args.message ? helpers.error("document.cnpj.custom", { message: args.message }) : helpers.error("document.cnpj");
}
}
}
});
export { joiValidator };
//# sourceMappingURL=joi.js.map
//# sourceMappingURL=joi.js.map