Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,14 @@ jobs:

strategy:
matrix:
node-version: [10.x, 12.x, 14.x]
node-version: [20.x, 22.x, 24.x]

steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- run: npm install
- run: npm ci
- run: npm test
36,909 changes: 20,940 additions & 15,969 deletions package-lock.json

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@
"@commitlint/cli": "^12.1.4",
"@commitlint/config-conventional": "^12.1.4",
"@release-it/conventional-changelog": "^1.1.4",
"@types/jest": "^26.0.23",
"@types/jest": "^30.0.0",
"cz-conventional-changelog": "^3.3.0",
"docsify-cli": "^4.4.2",
"husky": "^6.0.0",
Expand All @@ -70,6 +70,6 @@
"prismjs": "^1.23.0",
"release-it": "^14.8.0",
"tsdx": "^0.14.1",
"typescript": "^4.1.2"
"typescript": "^5.9.3"
}
}
51 changes: 51 additions & 0 deletions src/utilities/boleto/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
/**
* Constants and utility functions for validating and formatting Brazilian bank slip (boleto) numbers.
* @module boleto
*/
import { onlyNumbers, isLastChar } from '../../helpers';

export const PARTIALS = [
Expand Down Expand Up @@ -45,6 +49,11 @@ function isValidLength(digitableLine: string): boolean {
return digitableLine.length === LENGTH;
}

/**
* Calculates the modulus 10 check digit for a given partial string.
* @param partial - The partial string to calculate the check digit for.
* @returns The calculated check digit.
*/
function mod10(partial: string): number {
const sum = partial
.split('')
Expand All @@ -60,6 +69,11 @@ function mod10(partial: string): number {
return mod > 0 ? 10 - mod : 0;
}

/**
* Calculates the modulus 11 check digit for a given value.
* @param value - The value to calculate the check digit for.
* @returns The calculated check digit.
*/
function mod11(value: string): number {
const { initial, end } = MOD_11_WEIGHTS;

Expand All @@ -80,6 +94,11 @@ function mod11(value: string): number {
return mod === 0 || mod === 1 ? 1 : 11 - mod;
}

/**
* Validates if all partial sections of the digitable line have correct check digits.
* @param digitableLine - The digitable line to validate.
* @returns True if all partials are valid, false otherwise.
*/
function isValidPartials(digitableLine: string): boolean {
return PARTIALS.every(({ start, end, index }) => {
const mod = mod10(digitableLine.substring(start, end));
Expand All @@ -88,13 +107,23 @@ function isValidPartials(digitableLine: string): boolean {
});
}

/**
* Converts a digitable line to a boleto number format.
* @param digitableLine - The digitable line to parse.
* @returns The parsed boleto number.
*/
function parse(digitableLine: string): string {
return DIGITABLE_LINE_TO_BOLETO_CONVERT_POSITIONS.reduce(
(acc, pos) => acc + digitableLine.substring(pos.start, pos.end),
''
);
}

/**
* Validates if the check digit of the parsed digitable line is correct.
* @param parsedDigitableLine - The parsed digitable line to validate.
* @returns True if the check digit is valid, false otherwise.
*/
function isValidCheckDigit(parsedDigitableLine: string): boolean {
const mod = mod11(
parsedDigitableLine.slice(0, CHECK_DIGIT_POSITION) + parsedDigitableLine.slice(CHECK_DIGIT_POSITION + 1)
Expand All @@ -103,6 +132,16 @@ function isValidCheckDigit(parsedDigitableLine: string): boolean {
return +parsedDigitableLine[CHECK_DIGIT_POSITION] === mod;
}

/**
* Validates if a Brazilian bank slip (boleto) number is valid.
* Performs several validations including length check, partial validations,
* and check digit verification.
* @param digitableLine - The bank slip number to validate.
* @returns True if the bank slip number is valid, false otherwise.
* @example
* isValid('23790123016000000005325000456708') // returns true
* isValid('23790123016000000005325000456709') // returns false
*/
export function isValid(digitableLine: string): boolean {
if (!digitableLine || typeof digitableLine !== 'string') return false;

Expand All @@ -117,6 +156,18 @@ export function isValid(digitableLine: string): boolean {
return isValidCheckDigit(parsedDigits);
}

/**
* Formats a boleto (Brazilian bank slip) number string by adding dots and spaces at specific positions.
*
* @param boleto - The boleto number string to be formatted. Can contain numbers and other characters.
* @returns A formatted string containing only numbers with dots and spaces at standardized positions.
*
* @example
* ```typescript
* format('23790123016000000005325000456708')
* // returns '23790.12301 60000.000053 25000.456708'
* ```
*/
export function format(boleto: string) {
const digits = onlyNumbers(boleto);

Expand Down
13 changes: 13 additions & 0 deletions src/utilities/capitalize/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,19 @@ type CapitalizeOptions = {
upperCaseWords?: string[];
};

/**
* Capitalizes a given string according to specific rules for lower-case and upper-case words.
*
* - Words listed in `lowerCaseWords` (default: `PREPOSITIONS`) will be converted to lower case, except for the first word.
* - Words listed in `upperCaseWords` (default: `ACRONYMS`) will be converted to upper case.
* - All other words will be capitalized (first letter upper case, rest lower case).
*
* @param value - The input string to be capitalized.
* @param options - Optional configuration for capitalization.
* @param options.lowerCaseWords - Array of words to keep in lower case (default: `PREPOSITIONS`).
* @param options.upperCaseWords - Array of words to keep in upper case (default: `ACRONYMS`).
* @returns The capitalized string according to the specified rules.
*/
export function capitalize(
value: string,
{ lowerCaseWords = PREPOSITIONS, upperCaseWords = ACRONYMS }: CapitalizeOptions = {}
Expand Down
23 changes: 23 additions & 0 deletions src/utilities/cep/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,23 @@ export const LENGTH = 8;

export const HYPHEN_INDEXES = [4];

/**
* Checks if the provided CEP (postal code) has a valid length.
*
* @param cep - The CEP string to validate.
* @returns `true` if the CEP length matches the expected `LENGTH`, otherwise `false`.
*/
function isValidLength(cep: string) {
return cep.length === LENGTH;
}

/**
* Formats a Brazilian CEP (postal code) string by inserting hyphens at specific positions.
* Only numeric characters from the input are considered.
*
* @param cep - The input CEP string to be formatted.
* @returns The formatted CEP string with hyphens.
*/
export function format(cep: string): string {
const digits = onlyNumbers(cep);

Expand All @@ -25,6 +38,16 @@ export function format(cep: string): string {
}, '');
}

/**
* Checks if a given CEP (Brazilian postal code) is valid.
*
* The function validates the input by ensuring it is a string,
* extracts only the numeric digits, and verifies if the length
* matches the expected format for a CEP.
*
* @param cep - The CEP value to validate.
* @returns `true` if the CEP is valid, otherwise `false`.
*/
export function isValid(cep: string) {
if (!cep || typeof cep !== 'string') return false;

Expand Down
9 changes: 9 additions & 0 deletions src/utilities/cities/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,15 @@ import { StateCode, StateName, getStates } from '../states';

const sortAlphabetically = (cityA: string, cityB: string) => cityA.localeCompare(cityB);

/**
* Returns a list of city names for a given Brazilian state, or all cities if no state is specified.
*
* If a state name or code is provided, the function searches for the corresponding state and returns its cities sorted alphabetically.
* If no state is provided, it returns all cities from all states, sorted alphabetically.
*
* @param state - The name or code of the Brazilian state to filter cities by. Optional.
* @returns An array of city names, sorted alphabetically. Returns an empty array if the state is not found.
*/
export function getCities(state?: StateName | StateCode): string[] {
if (state) {
const states = getStates();
Expand Down
61 changes: 59 additions & 2 deletions src/utilities/cnpj/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,23 @@ export const CHECK_DIGITS_INDEXES = [12, 13];

export const FIRST_CHECK_DIGIT_WEIGHTS = [5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2];

export const SECOND_CHECK_DIGIT_WEIGHTS = [6, ...FIRST_CHECK_DIGIT_WEIGHTS];
export const SECOND_CHECK_DIGIT_WEIGHTS = [6].concat(FIRST_CHECK_DIGIT_WEIGHTS);

export interface FormatCnpjOptions {
pad?: boolean;
}

/**
* Formats a CNPJ (Cadastro Nacional da Pessoa Jurídica) number according to Brazilian standards.
*
* The function removes all non-numeric characters from the input, optionally pads the number to the required length,
* and inserts formatting characters (dots, slashes, hyphens) at the appropriate positions.
*
* @param cnpj - The CNPJ number to format, as a string or number.
* @param options - Optional formatting options.
* @property pad - If true, pads the CNPJ with leading zeros to the required length.
* @returns The formatted CNPJ string.
*/
export function format(cnpj: string | number, options: FormatCnpjOptions = {}): string {
let digits = onlyNumbers(cnpj);

Expand All @@ -54,6 +65,12 @@ export function format(cnpj: string | number, options: FormatCnpjOptions = {}):
}, '');
}

/**
* Generates a valid CNPJ (Cadastro Nacional da Pessoa Jurídica) number as a string.
* The generated CNPJ includes both check digits calculated according to official rules.
*
* @returns {string} A valid CNPJ number as a string.
*/
export function generate(): string {
const baseCNPJ = generateRandomNumber(LENGTH - 2);

Expand All @@ -66,17 +83,45 @@ export function generate(): string {
return `${baseCNPJ}${firstCheckDigit}${secondCheckDigit}`;
}

/**
* Checks if the provided CNPJ string matches the valid format.
*
* The valid format is: `NN.NNN.NNN/NNNN-NN` where `N` is a digit.
* Dots, slashes, and hyphens are optional.
*
* @param cnpj - The CNPJ string to validate.
* @returns `true` if the CNPJ matches the expected format, otherwise `false`.
*/
export function isValidFormat(cnpj: string): boolean {
return /^\d{2}\.?\d{3}\.?\d{3}\/?\d{4}-?\d{2}$/.test(cnpj);
}

/**
* Checks if the given CPF number is a reserved number.
*
* Reserved numbers are typically sequences of repeated digits (e.g., "00000000000", "11111111111", etc.)
* that are not valid for real CPF registrations.
*
* @param cpf - The CPF number as a string to be checked.
* @returns `true` if the CPF is a reserved number, otherwise `false`.
*/
export function isReservedNumber(cpf: string): boolean {
return RESERVED_NUMBERS.indexOf(cpf) >= 0;
}

// TODO: move to checksum helper
/**
* Checks if the provided CNPJ string has a valid checksum according to Brazilian CNPJ rules.
*
* The function uses predefined weights and indexes to calculate and validate the check digits.
* It iterates through the check digit indexes, adjusts the weights as needed, and compares
* the calculated check digit with the corresponding digit in the CNPJ.
*
* @param cnpj - The CNPJ string to validate.
* @returns `true` if the CNPJ has a valid checksum, `false` otherwise.
*/
export function isValidChecksum(cnpj: string): boolean {
const weights = [...FIRST_CHECK_DIGIT_WEIGHTS];
const weights = FIRST_CHECK_DIGIT_WEIGHTS.slice();

return CHECK_DIGITS_INDEXES.every((i) => {
if (i === CHECK_DIGITS_INDEXES[CHECK_DIGITS_INDEXES.length - 1]) {
Expand All @@ -96,6 +141,18 @@ export function isValidChecksum(cnpj: string): boolean {
});
}

/**
* Checks if a given CNPJ (Cadastro Nacional da Pessoa Jurídica) string is valid.
*
* The validation includes:
* - Ensuring the input is a non-empty string.
* - Verifying the format of the CNPJ.
* - Checking that the CNPJ is not a reserved number.
* - Validating the checksum digits.
*
* @param cnpj - The CNPJ string to validate.
* @returns `true` if the CNPJ is valid, otherwise `false`.
*/
export function isValid(cnpj: string): boolean {
if (!cnpj || typeof cnpj !== 'string') return false;

Expand Down
Loading