gerar 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,51 @@
1
+ name: Publish to NPM Registry
2
+
3
+ on:
4
+ workflow_dispatch:
5
+
6
+ jobs:
7
+ publish:
8
+ runs-on: ubuntu-latest
9
+
10
+ steps:
11
+ - name: Checkout repository
12
+ uses: actions/checkout@v4
13
+ with:
14
+ ref: ${{ github.ref }}
15
+
16
+ - name: Enable Corepack & setup pnpm v10.20.0
17
+ run: |
18
+ corepack enable
19
+ corepack prepare pnpm@10.20.0 --activate
20
+
21
+ - name: Setup Node.js
22
+ uses: actions/setup-node@v4
23
+ with:
24
+ node-version: "22.12.0"
25
+ registry-url: "https://registry.npmjs.org/"
26
+
27
+ - name: Install dependencies
28
+ run: pnpm install
29
+
30
+ - name: Test project
31
+ run: pnpm test
32
+
33
+ - name: Build project
34
+ run: pnpm build
35
+
36
+ - name: Detect tag and bump version in package.json
37
+ run: |
38
+ if [[ "${GITHUB_REF}" == refs/tags/* ]]; then
39
+ TAG_VERSION="${GITHUB_REF##*/}"
40
+ NEW_VERSION="${TAG_VERSION#v}"
41
+ jq --arg version "$NEW_VERSION" '.version = $version' package.json > package.tmp.json
42
+ mv package.tmp.json package.json
43
+ else
44
+ echo "::error::Este workflow só pode ser disparado por uma tag!"
45
+ exit 1
46
+ fi
47
+
48
+ - name: Publish to NPM
49
+ run: npm publish --access public
50
+ env:
51
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
package/Makefile ADDED
@@ -0,0 +1,22 @@
1
+ .PHONY: build install check-node-version check-pnpm-version check-installation unit-tests watch-unit-tests
2
+
3
+ check-node-version:
4
+ @node -v | grep -q 'v22' || (echo "Node.js LTS version is required." && exit 1)
5
+
6
+ check-pnpm-version: check-node-version
7
+ @pnpm -v | grep -q '10.' || (echo "pnpm version 10.x is required." && exit 1)
8
+
9
+ check-installation: check-node-version check-pnpm-version
10
+ @if [ ! -d "node_modules" ]; then echo "Dependencies are not installed. Run make install." && exit 1; fi
11
+
12
+ install: check-node-version check-pnpm-version
13
+ @pnpm install;
14
+
15
+ build: check-node-version check-pnpm-version check-installation
16
+ @pnpm build;
17
+
18
+ unit-tests: check-node-version check-pnpm-version check-installation
19
+ @pnpm test;
20
+
21
+ watch-unit-tests: check-node-version check-pnpm-version check-installation
22
+ @pnpm test:watch;
@@ -0,0 +1,2 @@
1
+ import { CommandStructure } from "../../shared/utils/command-structure";
2
+ export declare const cpfCommand: CommandStructure;
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.cpfCommand = void 0;
4
+ const utils_1 = require("./utils");
5
+ exports.cpfCommand = {
6
+ name: "cpf",
7
+ description: "Gera um CPF fictício válido.",
8
+ action: (options) => {
9
+ const generatedCpfs = Array.from({ length: options.amount ?? 1 }, utils_1.generateCpf);
10
+ generatedCpfs.forEach((generatedCpf, index) => {
11
+ console.log(`CPF ${index + 1}) Com Máscara: ${generatedCpf.withMask} - Sem Máscara: ${generatedCpf.withoutMask}`);
12
+ });
13
+ },
14
+ };
15
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/commands/cpf/index.ts"],"names":[],"mappings":";;;AACA,mCAAsC;AAEzB,QAAA,UAAU,GAAqB;IAC1C,IAAI,EAAE,KAAK;IACX,WAAW,EAAE,8BAA8B;IAC3C,MAAM,EAAE,CAAC,OAA4B,EAAE,EAAE;QACvC,MAAM,aAAa,GAAG,KAAK,CAAC,IAAI,CAC9B,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,IAAI,CAAC,EAAE,EAC/B,mBAAW,CACZ,CAAC;QAEF,aAAa,CAAC,OAAO,CAAC,CAAC,YAAY,EAAE,KAAK,EAAE,EAAE;YAC5C,OAAO,CAAC,GAAG,CACT,OAAO,KAAK,GAAG,CAAC,kBACd,YAAY,CAAC,QACf,mBAAmB,YAAY,CAAC,WAAW,EAAE,CAC9C,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC;CACF,CAAC"}
@@ -0,0 +1,7 @@
1
+ type GenerateCpfResponse = {
2
+ withMask: string;
3
+ withoutMask: string;
4
+ };
5
+ export declare const calculateCpfDigit: (cpf: string) => number;
6
+ export declare const generateCpf: () => GenerateCpfResponse;
7
+ export {};
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generateCpf = exports.calculateCpfDigit = void 0;
4
+ const calculateCpfDigit = (cpf) => {
5
+ const length = cpf.length + 1;
6
+ const sum = cpf
7
+ .split("")
8
+ .reduce((acc, num, index) => acc + Number(num) * (length - index), 0);
9
+ const remainder = sum % 11;
10
+ return remainder < 2 ? 0 : 11 - remainder;
11
+ };
12
+ exports.calculateCpfDigit = calculateCpfDigit;
13
+ const generateCpf = () => {
14
+ const baseCpf = Array.from({ length: 9 }, () => Math.floor(Math.random() * 10)).join("");
15
+ const firstDigit = (0, exports.calculateCpfDigit)(baseCpf);
16
+ const lastDigit = (0, exports.calculateCpfDigit)(baseCpf + firstDigit);
17
+ const fullCpf = baseCpf + firstDigit.toString() + lastDigit.toString();
18
+ return {
19
+ // TODO: Improve this as soon as possible.
20
+ withMask: `${fullCpf.slice(0, 3)}.${fullCpf.slice(3, 6)}.${fullCpf.slice(6, 9)}-${fullCpf.slice(9, 11)}`,
21
+ withoutMask: fullCpf,
22
+ };
23
+ };
24
+ exports.generateCpf = generateCpf;
25
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../../../src/commands/cpf/utils.ts"],"names":[],"mappings":";;;AAKO,MAAM,iBAAiB,GAAG,CAAC,GAAW,EAAU,EAAE;IACvD,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC;IAC9B,MAAM,GAAG,GAAG,GAAG;SACZ,KAAK,CAAC,EAAE,CAAC;SACT,MAAM,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE,CAAC,GAAG,GAAG,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,GAAG,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC;IAExE,MAAM,SAAS,GAAG,GAAG,GAAG,EAAE,CAAC;IAE3B,OAAO,SAAS,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,SAAS,CAAC;AAC5C,CAAC,CAAC;AATW,QAAA,iBAAiB,qBAS5B;AAEK,MAAM,WAAW,GAAG,GAAwB,EAAE;IACnD,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,EAAE,GAAG,EAAE,CAC7C,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,CAC/B,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAEX,MAAM,UAAU,GAAG,IAAA,yBAAiB,EAAC,OAAO,CAAC,CAAC;IAC9C,MAAM,SAAS,GAAG,IAAA,yBAAiB,EAAC,OAAO,GAAG,UAAU,CAAC,CAAC;IAE1D,MAAM,OAAO,GAAG,OAAO,GAAG,UAAU,CAAC,QAAQ,EAAE,GAAG,SAAS,CAAC,QAAQ,EAAE,CAAC;IAEvE,OAAO;QACL,0CAA0C;QAC1C,QAAQ,EAAE,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,OAAO,CAAC,KAAK,CACtE,CAAC,EACD,CAAC,CACF,IAAI,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE;QAC3B,WAAW,EAAE,OAAO;KACrB,CAAC;AACJ,CAAC,CAAC;AAlBW,QAAA,WAAW,eAkBtB"}
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ const commander_1 = require("commander");
5
+ const cpf_1 = require("./commands/cpf");
6
+ const program = new commander_1.Command();
7
+ program
8
+ .name("gerar")
9
+ .description("CLI brasileiro para gerar dados fictícios para testes.")
10
+ .version("0.0.1");
11
+ program.helpCommand("help", "Exibe informações de ajuda do CLI.");
12
+ // TODO: Create automatic registration for commands.
13
+ program
14
+ .command(cpf_1.cpfCommand.name)
15
+ .description(cpf_1.cpfCommand.description)
16
+ .option("-a, --amount <amount>", "Quantidade a ser gerado.", (value) => Number(value))
17
+ .action(cpf_1.cpfCommand.action);
18
+ program.parse();
19
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AACA,yCAAoC;AACpC,wCAA4C;AAE5C,MAAM,OAAO,GAAG,IAAI,mBAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,OAAO,CAAC;KACb,WAAW,CAAC,wDAAwD,CAAC;KACrE,OAAO,CAAC,OAAO,CAAC,CAAC;AAEpB,OAAO,CAAC,WAAW,CAAC,MAAM,EAAE,oCAAoC,CAAC,CAAC;AAElE,oDAAoD;AACpD,OAAO;KACJ,OAAO,CAAC,gBAAU,CAAC,IAAI,CAAC;KACxB,WAAW,CAAC,gBAAU,CAAC,WAAW,CAAC;KACnC,MAAM,CACL,uBAAuB,EACvB,0BAA0B,EAC1B,CAAC,KAAa,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CACjC;KACA,MAAM,CAAC,gBAAU,CAAC,MAAM,CAAC,CAAC;AAE7B,OAAO,CAAC,KAAK,EAAE,CAAC"}
@@ -0,0 +1,6 @@
1
+ import { Command } from "commander";
2
+ export type CommandStructure = {
3
+ name: string;
4
+ description: string;
5
+ action: (this: Command, ...args: any[]) => void | Promise<void>;
6
+ };
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=command-structure.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"command-structure.js","sourceRoot":"","sources":["../../../src/shared/utils/command-structure.ts"],"names":[],"mappings":""}
package/package.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "gerar",
3
+ "version": "0.0.2",
4
+ "description": "CLI brasileiro para gerar dados fictícios para testes.",
5
+ "main": "dist/index.js",
6
+ "author": "Victor Ribeiro Boechat",
7
+ "bin": {
8
+ "gerar": "dist/index.js"
9
+ },
10
+ "scripts": {
11
+ "build": "tsc -p .",
12
+ "dev": "tsx src/index.ts",
13
+ "start": "node dist/index.js",
14
+ "test": "vitest run",
15
+ "test:watch": "vitest"
16
+ },
17
+ "keywords": [],
18
+ "license": "ISC",
19
+ "dependencies": {
20
+ "commander": "^14.0.2"
21
+ },
22
+ "devDependencies": {
23
+ "@types/node": "^24.10.1",
24
+ "tsx": "^4.19.2",
25
+ "typescript": "^5.9.3",
26
+ "vitest": "^4.0.13"
27
+ },
28
+ "packageManager": "pnpm@10.20.0"
29
+ }
@@ -0,0 +1,21 @@
1
+ import { CommandStructure } from "../../shared/utils/command-structure";
2
+ import { generateCpf } from "./utils";
3
+
4
+ export const cpfCommand: CommandStructure = {
5
+ name: "cpf",
6
+ description: "Gera um CPF fictício válido.",
7
+ action: (options: { amount?: number }) => {
8
+ const generatedCpfs = Array.from(
9
+ { length: options.amount ?? 1 },
10
+ generateCpf
11
+ );
12
+
13
+ generatedCpfs.forEach((generatedCpf, index) => {
14
+ console.log(
15
+ `CPF ${index + 1}) Com Máscara: ${
16
+ generatedCpf.withMask
17
+ } - Sem Máscara: ${generatedCpf.withoutMask}`
18
+ );
19
+ });
20
+ },
21
+ };
@@ -0,0 +1,114 @@
1
+ import { describe, it, expect, expectTypeOf } from "vitest";
2
+ import { calculateCpfDigit, generateCpf } from "../utils";
3
+
4
+ describe("calculateCpfDigit", () => {
5
+ it("should calculate the first check digit correctly for known CPF", () => {
6
+ const firstDigit = calculateCpfDigit("111444777");
7
+
8
+ expect(firstDigit).toBe(3);
9
+ });
10
+
11
+ it("should calculate the second check digit correctly for known CPF", () => {
12
+ const secondDigit = calculateCpfDigit("1114447773");
13
+
14
+ expect(secondDigit).toBe(5);
15
+ });
16
+
17
+ it("should return 0 when remainder is less than 2", () => {
18
+ const digit = calculateCpfDigit("000000000");
19
+
20
+ expect(digit).toBe(0);
21
+ });
22
+
23
+ it("should return correct digit when remainder is 2 or greater", () => {
24
+ const digit = calculateCpfDigit("123456789");
25
+
26
+ expect(digit).toBeGreaterThanOrEqual(0);
27
+ expect(digit).toBeLessThanOrEqual(9);
28
+ });
29
+
30
+ it("should handle 9-digit input correctly", () => {
31
+ const digit = calculateCpfDigit("123456789");
32
+
33
+ expect(digit).toBeTypeOf("number");
34
+ expect(digit).toBeGreaterThanOrEqual(0);
35
+ expect(digit).toBeLessThanOrEqual(9);
36
+ });
37
+
38
+ it("should handle 10-digit input correctly (for second digit calculation)", () => {
39
+ const digit = calculateCpfDigit("1234567890");
40
+
41
+ expect(digit).toBeTypeOf("number");
42
+ expect(digit).toBeGreaterThanOrEqual(0);
43
+ expect(digit).toBeLessThanOrEqual(9);
44
+ });
45
+ });
46
+
47
+ describe("generateCpf", () => {
48
+ it("should generate a CPF with correct structure", () => {
49
+ const result = generateCpf();
50
+
51
+ expect(result).toHaveProperty("withMask");
52
+ expect(result).toHaveProperty("withoutMask");
53
+ });
54
+
55
+ it("should generate CPF withoutMask with exactly 11 digits", () => {
56
+ const result = generateCpf();
57
+
58
+ expect(result.withoutMask).toMatch(/^\d{11}$/);
59
+ expect(result.withoutMask.length).toBe(11);
60
+ });
61
+
62
+ it("should generate CPF withMask in correct format (XXX.XXX.XXX-XX)", () => {
63
+ const result = generateCpf();
64
+
65
+ expect(result.withMask).toMatch(/^\d{3}\.\d{3}\.\d{3}-\d{2}$/);
66
+ });
67
+
68
+ it("should generate valid CPF digits (check digits are correct)", () => {
69
+ const result = generateCpf();
70
+ const cpf = result.withoutMask;
71
+
72
+ const base = cpf.slice(0, 9);
73
+ const firstCheckDigit = Number(cpf[9]);
74
+ const secondCheckDigit = Number(cpf[10]);
75
+
76
+ const calculatedFirstDigit = calculateCpfDigit(base);
77
+ expect(firstCheckDigit).toBe(calculatedFirstDigit);
78
+
79
+ const calculatedSecondDigit = calculateCpfDigit(base + firstCheckDigit);
80
+ expect(secondCheckDigit).toBe(calculatedSecondDigit);
81
+ });
82
+
83
+ it("should generate different CPFs on multiple calls", () => {
84
+ const results = Array.from({ length: 10 }, () => generateCpf());
85
+ const uniqueCpfs = new Set(results.map((r) => r.withoutMask));
86
+
87
+ expect(uniqueCpfs.size).toBeGreaterThan(1);
88
+ });
89
+
90
+ it("should generate CPF where withMask and withoutMask represent the same number", () => {
91
+ const result = generateCpf();
92
+
93
+ const unmasked = result.withMask.replace(/[.-]/g, "");
94
+
95
+ expect(unmasked).toBe(result.withoutMask);
96
+ });
97
+
98
+ it("should generate multiple valid CPFs", () => {
99
+ const results = Array.from({ length: 100 }, () => generateCpf());
100
+
101
+ results.forEach((result) => {
102
+ const cpf = result.withoutMask;
103
+ const base = cpf.slice(0, 9);
104
+ const firstCheckDigit = Number(cpf[9]);
105
+ const secondCheckDigit = Number(cpf[10]);
106
+
107
+ const calculatedFirstDigit = calculateCpfDigit(base);
108
+ const calculatedSecondDigit = calculateCpfDigit(base + firstCheckDigit);
109
+
110
+ expect(firstCheckDigit).toBe(calculatedFirstDigit);
111
+ expect(secondCheckDigit).toBe(calculatedSecondDigit);
112
+ });
113
+ });
114
+ });
@@ -0,0 +1,35 @@
1
+ type GenerateCpfResponse = {
2
+ withMask: string;
3
+ withoutMask: string;
4
+ };
5
+
6
+ export const calculateCpfDigit = (cpf: string): number => {
7
+ const length = cpf.length + 1;
8
+ const sum = cpf
9
+ .split("")
10
+ .reduce((acc, num, index) => acc + Number(num) * (length - index), 0);
11
+
12
+ const remainder = sum % 11;
13
+
14
+ return remainder < 2 ? 0 : 11 - remainder;
15
+ };
16
+
17
+ export const generateCpf = (): GenerateCpfResponse => {
18
+ const baseCpf = Array.from({ length: 9 }, () =>
19
+ Math.floor(Math.random() * 10)
20
+ ).join("");
21
+
22
+ const firstDigit = calculateCpfDigit(baseCpf);
23
+ const lastDigit = calculateCpfDigit(baseCpf + firstDigit);
24
+
25
+ const fullCpf = baseCpf + firstDigit.toString() + lastDigit.toString();
26
+
27
+ return {
28
+ // TODO: Improve this as soon as possible.
29
+ withMask: `${fullCpf.slice(0, 3)}.${fullCpf.slice(3, 6)}.${fullCpf.slice(
30
+ 6,
31
+ 9
32
+ )}-${fullCpf.slice(9, 11)}`,
33
+ withoutMask: fullCpf,
34
+ };
35
+ };
package/src/index.ts ADDED
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from "commander";
3
+ import { cpfCommand } from "./commands/cpf";
4
+
5
+ const program = new Command();
6
+
7
+ program
8
+ .name("gerar")
9
+ .description("CLI brasileiro para gerar dados fictícios para testes.")
10
+ .version("0.0.1");
11
+
12
+ program.helpCommand("help", "Exibe informações de ajuda do CLI.");
13
+
14
+ // TODO: Create automatic registration for commands.
15
+ program
16
+ .command(cpfCommand.name)
17
+ .description(cpfCommand.description)
18
+ .option(
19
+ "-a, --amount <amount>",
20
+ "Quantidade a ser gerado.",
21
+ (value: string) => Number(value)
22
+ )
23
+ .action(cpfCommand.action);
24
+
25
+ program.parse();
@@ -0,0 +1,7 @@
1
+ import { Command } from "commander";
2
+
3
+ export type CommandStructure = {
4
+ name: string;
5
+ description: string;
6
+ action: (this: Command, ...args: any[]) => void | Promise<void>;
7
+ };
package/tsconfig.json ADDED
@@ -0,0 +1,17 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "es2022",
4
+ "module": "commonjs",
5
+ "lib": ["es2022"],
6
+ "outDir": "dist",
7
+ "rootDir": "src",
8
+ "strict": true,
9
+ "esModuleInterop": true,
10
+ "skipLibCheck": true,
11
+ "forceConsistentCasingInFileNames": true,
12
+ "declaration": true,
13
+ "sourceMap": true
14
+ },
15
+ "include": ["src/**/*"],
16
+ "exclude": ["node_modules", "dist", "src/**/tests/**"]
17
+ }