@benup/bensdk 1.11.16 → 1.12.0

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.
@@ -1,110 +1,88 @@
1
- # 📦 BenSDK App - Documentação Inicial
1
+ <img src="ICON.png" alt="BenefitID" width="120">
2
2
 
3
- Bem-vindo ao seu **BenSDK App**! Esta biblioteca, escrita em **TypeScript**, será executada dentro do nosso **integrador**, permitindo que seu benefício seja incluído, removido ou sincronizado com base em eventos.
3
+ # bensdk - BenefitID
4
4
 
5
- > Este arquivo foi gerado automaticamente após a criação do projeto. Aqui você encontra tudo o que precisa para começar a desenvolver seu benefício.
6
-
7
-
8
- ## 🔧 Estrutura do Projeto
5
+ ## Estrutura do Projeto
9
6
 
10
7
  ```
11
8
  ├── src/
12
- │ ├── handlers/ # Handlers chamados pelo integrador
13
- └── ... # Seu código principal vai aqui
14
- ├── lib/ # Types usados pelo integrador (não editar!)
15
- ├── .benSDK # Token de publicação
16
- ├── .benSDKIgnore # Ignora arquivos na publicação
17
- ├── benefit-definition.ts # Definição do benefício + máquina de estados
18
-
9
+ │ ├── benefit-definition.ts # Definição do benefício + máquina de estados
10
+ ├── handlers/ # Handlers executados pelo integrador
11
+ │ └── lib/ # Utilitários e mocks
12
+ ├── bin/
13
+ ├── cli/ # CLI para geração de handlers e testes
14
+ │ └── server/ # Servidor local para testes (npm run dev:start)
15
+ ├── ICON.png # Ícone do benefício
16
+ ├── .benSDKIgnore # Ignora arquivos no empacotamento
17
+ └── dev-test.example.json # Modelo de credenciais para testes locais
19
18
  ```
20
19
 
20
+ ## Conceitos Básicos
21
21
 
22
- ## ⚙️ Conceitos Básicos
23
-
24
- ### 📤 Actions
22
+ ### Actions
25
23
 
26
- As **Actions** são comandos enviados pelo integrador ao seu BenApp. Cada action representa um evento (ex: inclusão de um funcionário) e será processada de acordo com a **máquina de estados** definida no arquivo `benefit-definition.ts`.
24
+ As **Actions** são os eventos enviados pelo integrador ao seu BenSDK. Cada action representa uma operação sobre um benefício (ex: concessão, revogação, recarga) e é processada de acordo com a **máquina de estados** definida em `src/benefit-definition.ts`.
27
25
 
28
- ### 🧠 Handlers
26
+ ### Handlers
29
27
 
30
- Os **handlers** são os pontos de entrada do seu app. Cada um processa uma Action específica. Você pode usar o padrão de desenvolvimento que preferir, desde que:
28
+ Cada **estado** da máquina de estados corresponde a um arquivo de handler em `src/handlers/`. O handler recebe:
31
29
 
32
- - Mantenha os arquivos existentes
33
- - Mantenha o `default export` de cada handler
34
- #### Handlers disponíveis:
30
+ - `message`: contexto da mensagem recebida da fila
31
+ - `action`: dados da operação (funcionário, empresa, eligibilityOptions, ctx compartilhado)
32
+ - `ctx`: contexto de execução (APIs, logger, benefitDefinition)
35
33
 
36
- - `request_create_employee.handler.ts` Quando um funcionário for incluído no benefício
37
- - `request_remove_employee.handler.ts` → Quando um funcionário for removido
38
- - `sync_existing_grant.handler.ts` → Verifica se o funcionário **já possui** o benefício
39
- - `sync_existing_revoke.handler.ts` → Verifica se o funcionário **já foi removido**
34
+ ### ctx: Contexto compartilhado entre handlers
40
35
 
41
- **Exemplo de handler:**
36
+ O campo `ctx` dentro de `action` é um objeto de estado acumulado ao longo de um fluxo. Use-o para passar dados entre etapas, por exemplo, um ID retornado pela API do parceiro que será necessário em handlers subsequentes.
42
37
 
43
- ```ts
44
- const handler: StateHandler<
45
- ActionGrant,
46
- ActionLogGrant['REQUEST_CREATE_EMPLOYEE'],
47
- ActionCtx
48
- > = async (message, action, ctx) => {
49
- // Aqui você chama a API do seu benefício, por exemplo.
50
-
51
- return {
52
- handlerResponse: { response: 'ACK' },
53
- action: {
54
- state: stateMachine.next,
55
- ctx: {},
56
- logs: { date: new Date().toISOString() },
57
- },
58
- };
59
- };
60
-
61
- export default handler;
62
-
63
- ```
38
+ ## Fluxos
64
39
 
65
- ### 🔁 Máquina de Estados
40
+ ### Concessão de Benefício (GRANT)
66
41
 
67
- A **máquina de estados** define a ordem de execução das actions. Para a maioria dos casos, o modelo padrão já é suficiente — mas ele pode ser adaptado conforme sua necessidade, desde que preserve os **estados finais**.
68
-
69
- #### ✅ Concessão de Benefício (GRANT)
70
-
71
- Nesse fluxo, o integrador:
72
- 1. Verifica se o funcionário já está no benefício (`SYNC_EXISTING_GRANT` - Executando o handler `sync_existing_grant.handler.ts`).
73
- 2. Caso não esteja, solicita sua inclusão (`REQUEST_CREATE_EMPLOYEE` - Executando o handler `request_create_employee.handler.ts` ).
74
-
75
- ```mermaid
42
+ ```mermaid
76
43
  flowchart TD
77
- START([Start]) --> REQUESTED_GRANT
78
- REQUESTED_GRANT -->|next| SYNC_EXISTING_GRANT["SYNC_EXISTING_GRANT"]
44
+ START([Início]) --> REQUESTED_GRANT
45
+ REQUESTED_GRANT -->|next| SYNC_EXISTING_GRANT
79
46
  SYNC_EXISTING_GRANT -->|next| REQUEST_CREATE_EMPLOYEE
80
47
  SYNC_EXISTING_GRANT -->|skip| GRANTED
81
48
  REQUEST_CREATE_EMPLOYEE -->|next| GRANTED
82
49
  REQUEST_CREATE_EMPLOYEE -->|fail| FAILED_TO_GRANT
83
50
 
51
+ GRANTED([GRANTED ✅])
52
+ FAILED_TO_GRANT([FAILED_TO_GRANT ❌])
84
53
  ```
85
54
 
86
- #### Revogação de Benefício (REVOKE)
87
-
88
- Nesse fluxo, o integrador:
89
- 1. Verifica se o funcionário já foi removido do benefício (`SYNC_EXISTING_REVOKE`).
90
- 2. Caso não tenha sido, solicita sua revogação (`REQUEST_REMOVE_EMPLOYEE`).
55
+ ### Revogação de Benefício (REVOKE)
91
56
 
92
- ```mermaid
57
+ ```mermaid
93
58
  flowchart TD
94
- START([Start]) --> REQUESTED_REVOKE
95
- REQUESTED_REVOKE -->|next| SYNC_EXISTING_REVOKE["SYNC_EXISTING_REVOKE"]
59
+ START([Início]) --> REQUESTED_REVOKE
60
+ REQUESTED_REVOKE -->|next| SYNC_EXISTING_REVOKE
96
61
  SYNC_EXISTING_REVOKE -->|next| REQUEST_REMOVE_EMPLOYEE
97
62
  SYNC_EXISTING_REVOKE -->|skip| REVOKED
98
63
  REQUEST_REMOVE_EMPLOYEE -->|next| REVOKED
99
64
  REQUEST_REMOVE_EMPLOYEE -->|fail| FAILED_TO_REVOKE
100
65
 
66
+ REVOKED([REVOKED ✅])
67
+ FAILED_TO_REVOKE([FAILED_TO_REVOKE ❌])
101
68
  ```
102
69
 
103
- ## 🚀 Próximos Passos
70
+ ## Comandos
104
71
 
105
- 1. Edite os arquivos em `src/handlers/` para integrar com sua API de benefício.
106
- 2. Teste sua integração através do painel executando o comando `npm run start`
107
- 3. Preencha seu token `.benSDK` para publicar quando estiver pronto.
108
- 4. Rode o `ben deploy` dentro da pasta da sua aplicação, não se preocupe, vamos checar se tá tudo certo antes de efetivamente colocar em produção
72
+ | Comando | Descricao |
73
+ |---------|-----------|
74
+ | `npm run dev:start` | Inicia o servidor local com autenticacao real contra a API do parceiro. [Ver documentacao](docs/DEV_SERVER.md) |
75
+ | `npm run test` | Executa os testes unitarios com Vitest |
76
+ | `npm run generate` | Gera handlers e testes a partir do `benefit-definition.ts` |
77
+ | `npm run start` | Abre o painel visual da maquina de estados |
78
+ | `npm run lint` | Valida tipos TypeScript, formatacao e linting |
79
+ | `npm run build` | Compila o projeto para `dist/` |
109
80
 
110
- Se tiver dúvidas, consulte a documentação oficial do BenSDK ou fale com nosso time de suporte.
81
+ ## Publicação
82
+
83
+ 1. Instale o [maria-cli](https://github.com/benup-dev/maria) seguindo as instrucões do repositório.
84
+ 2. Execute o comando abaixo na raiz do projeto:
85
+
86
+ ```bash
87
+ maria benefit deploy -r <link-ssh-do-repositorio> -n <benefitID>
88
+ ```
@@ -1,8 +1,14 @@
1
- import axios from 'axios';
1
+ import type { StateHandlerCtx } from '@benup/bensdk/bin/lib/types/state-handler.types.d.ts';
2
+ import type { BenefitDefinition } from '@benup/bensdk/bin/lib/types/benefit-definition.types';
3
+ import axios, { AxiosInstance } from 'axios';
2
4
  import AxiosMockAdapter from 'axios-mock-adapter';
3
- import { StateHandlerCtx } from './src/lib/types/state.handler.type';
5
+ import pino, { Logger } from 'pino';
4
6
 
5
- export function createStateHandlerCtxMock(benefitDefinition, logger) {
7
+ export function createStateHandlerCtxMock(
8
+ benefitDefinition: BenefitDefinition,
9
+ logger: Logger = pino(),
10
+ disableMocks = false
11
+ ) {
6
12
  const benupAPI = axios.create({
7
13
  validateStatus: () => true,
8
14
  baseURL: 'http://localhost/benupAPI'
@@ -18,35 +24,105 @@ export function createStateHandlerCtxMock(benefitDefinition, logger) {
18
24
  baseURL: 'http://localhost/benefitsAPI'
19
25
  });
20
26
 
27
+ const benefitPartnerAPI = axios.create({
28
+ validateStatus: () => true,
29
+ baseURL: 'http://localhost/benefitPartnerAPI'
30
+ });
31
+
32
+ // issueHelper mock: prints issues to the logger instead of sending them to a real service
33
+ // This way you can see issues raised by handlers during local development
34
+ const issuesHelper = {
35
+ sendIssue: async ({
36
+ title,
37
+ description,
38
+ fieldPath
39
+ }: {
40
+ title: string;
41
+ description: string;
42
+ fieldPath?: string;
43
+ }) => {
44
+ logger.warn(`[Issue] [${title}] ${description}${fieldPath ? ` → ${fieldPath}` : ''}`);
45
+ }
46
+ };
47
+
48
+ // When disableMocks=true the dev server will inject a real authenticated axios instance
49
+ // into ctxMock.benefitPartnerAPI. The mock here is a no-op passthrough function that can
50
+ // be applied to the authenticated instance if you need to stub specific endpoints
51
+ // (e.g. presigned URLs, file uploads) while keeping the rest as real API calls.
52
+ if (disableMocks) {
53
+ const apisMocks = {
54
+ benupAPI: new AxiosMockAdapter(benupAPI, { onNoMatch: 'throwException' }),
55
+ lgProxyAPI: new AxiosMockAdapter(lgProxyAPI, { onNoMatch: 'throwException' }),
56
+ benefitsAPI: new AxiosMockAdapter(benefitsAPI, { onNoMatch: 'throwException' }),
57
+
58
+ // Return a function so the dev server can apply mocks to the real authenticated instance.
59
+ // Use onNoMatch: 'passthrough' so real API calls are not blocked.
60
+ // Add any endpoint stubs here that you want to intercept even in real-call mode.
61
+ benefitPartnerAPI: (axiosInstance: AxiosInstance) => {
62
+ const mock = new AxiosMockAdapter(axiosInstance, { onNoMatch: 'passthrough' });
63
+ // Example: mock.onGet('/some-endpoint').reply(200, { ... });
64
+ return mock;
65
+ }
66
+ };
67
+
68
+ const ctxMock: StateHandlerCtx = {
69
+ rawMessage: {},
70
+ logger,
71
+ correlationIDs: {},
72
+ benefitsAPI,
73
+ lgProxyAPI,
74
+ benupAPI,
75
+ benefitDefinition,
76
+ benefitPartnerAPI,
77
+ // @ts-expect-error: universAPI intentionally omitted in mock context
78
+ universAPI: undefined,
79
+ issuesHelper
80
+ };
81
+
82
+ return { ctxMock, apisMocks };
83
+ }
84
+
85
+ // Full mock setup for unit testing (vitest). All API calls must be explicitly mocked.
21
86
  const apisMocks = {
22
- benupAPI: new AxiosMockAdapter(benupAPI, {
23
- onNoMatch: 'throwException'
24
- }),
25
- lgProxyAPI: new AxiosMockAdapter(lgProxyAPI, {
26
- onNoMatch: 'throwException'
27
- }),
28
- benefitsAPI: new AxiosMockAdapter(benefitsAPI, {
29
- onNoMatch: 'throwException'
30
- })
87
+ benupAPI: new AxiosMockAdapter(benupAPI, { onNoMatch: 'throwException' }),
88
+ lgProxyAPI: new AxiosMockAdapter(lgProxyAPI, { onNoMatch: 'throwException' }),
89
+ benefitsAPI: new AxiosMockAdapter(benefitsAPI, { onNoMatch: 'throwException' }),
90
+ benefitPartnerAPI: new AxiosMockAdapter(benefitPartnerAPI, { onNoMatch: 'throwException' }),
91
+
92
+ // Reset all mock adapters between tests
93
+ resetMocks: function () {
94
+ Object.entries(this)
95
+ .filter(([_, value]) => value instanceof AxiosMockAdapter)
96
+ .forEach(([_, mockAdapter]) => {
97
+ (mockAdapter as AxiosMockAdapter).reset();
98
+ });
99
+ },
100
+
101
+ // Restore all mock adapters (call in afterAll to clean up)
102
+ restoreAll: function () {
103
+ Object.entries(this)
104
+ .filter(([_, value]) => value instanceof AxiosMockAdapter)
105
+ .forEach(([_, mockAdapter]) => {
106
+ (mockAdapter as AxiosMockAdapter).restore();
107
+ });
108
+ }
31
109
  };
32
110
 
33
- // Mockar o endpoint GET /some-path para retornar 200 OK
34
- apisMocks.benefitsAPI.onGet('/proxy-http/<Algum path>').replyOnce(200, {
35
- values: ['123']
36
- });
37
-
111
+ // Set up your mock responses here for unit tests.
112
+ // Example: apisMocks.benefitPartnerAPI.onGet('/some-path').replyOnce(200, { ... });
38
113
 
39
114
  const ctxMock: StateHandlerCtx = {
40
115
  rawMessage: {},
41
-
42
116
  logger,
43
-
44
117
  correlationIDs: {},
45
-
46
118
  benefitsAPI,
47
119
  lgProxyAPI,
48
120
  benupAPI,
49
- benefitDefinition
121
+ benefitDefinition,
122
+ benefitPartnerAPI,
123
+ // @ts-expect-error: universAPI intentionally omitted in mock context
124
+ universAPI: undefined,
125
+ issuesHelper
50
126
  };
51
127
 
52
128
  return { ctxMock, apisMocks };
@@ -0,0 +1,22 @@
1
+ {
2
+ "_comment": "Copy this file to dev-test.json and fill in your real values. dev-test.json is gitignored.",
3
+
4
+ "credentials": {
5
+ "accountID": "FILL_ME",
6
+ "companyID": "FILL_ME",
7
+ "benefitID": "FILL_ME",
8
+ "connection": {
9
+ "url": "https://api.your-partner.com"
10
+ },
11
+ "secret": {
12
+ "username": "FILL_ME",
13
+ "password": "FILL_ME"
14
+ }
15
+ },
16
+
17
+ "eligibilityOptions": {
18
+ "GRANT": {},
19
+ "RECHARGE": {},
20
+ "GRANT_DEPENDENT": {}
21
+ }
22
+ }
@@ -0,0 +1,201 @@
1
+ # Servidor de Desenvolvimento Local: `npm run dev:start`
2
+
3
+ O `dev:start` sobe um servidor HTTP local (porta 3000) que permite testar qualquer handler diretamente contra a API real do parceiro, com autenticacao verdadeira e sem precisar de mocks.
4
+
5
+ Isso e especialmente util porque:
6
+ - Os dados que chegam da folha LG variam por cliente e nem sempre sao previsiveis nos testes unitarios.
7
+ - As APIs dos parceiros tem comportamentos nao documentados que so aparecem em chamadas reais.
8
+ - E possivel executar cada estado da maquina em sequencia, acumulando o `ctx` entre chamadas, simulando exatamente o que o integrador faria.
9
+
10
+ ---
11
+
12
+ ## Pre-requisitos
13
+
14
+ Crie o arquivo `dev-test.json` na raiz do projeto a partir do modelo:
15
+
16
+ ```bash
17
+ cp dev-test.example.json dev-test.json
18
+ ```
19
+
20
+ Preencha com suas credenciais reais. A estrutura exata de `credentials` deve seguir o schema definido no `auth.handler` do seu `benefit-definition.ts`:
21
+
22
+ ```json
23
+ {
24
+ "credentials": {
25
+ "accountID": "...",
26
+ "companyID": "...",
27
+ "benefitID": "...",
28
+ "connection": {
29
+ "url": "https://api.seu-parceiro.com"
30
+ },
31
+ "secret": {
32
+ "username": "...",
33
+ "password": "..."
34
+ }
35
+ },
36
+ "eligibilityOptions": {
37
+ "GRANT": {},
38
+ "RECHARGE": {}
39
+ }
40
+ }
41
+ ```
42
+
43
+ > **Atencao:** `dev-test.json` esta no `.gitignore` e no `.benSDKIgnore`. **Nunca commite credenciais reais.**
44
+
45
+ ---
46
+
47
+ ## Iniciando o servidor
48
+
49
+ ```bash
50
+ npm run dev:start
51
+ ```
52
+
53
+ Ao iniciar, o servidor exibe:
54
+ - Se as credenciais foram carregadas com sucesso.
55
+ - Quais acoes tem presets de `eligibilityOptions` configurados.
56
+ - Os endpoints disponiveis.
57
+
58
+ ---
59
+
60
+ ## Endpoints
61
+
62
+ ### `POST /`: Executar um handler
63
+
64
+ ```json
65
+ {
66
+ "run": "SYNC_EXISTING_GRANT",
67
+ "payload": {
68
+ "target": {
69
+ "employee": { "name": "...", "cpf": "...", "birthdate": "..." },
70
+ "company": { "cnpj": "..." }
71
+ },
72
+ "employmentContract": {
73
+ "benefits": {
74
+ "BENEFIT_ID": { "options": {} }
75
+ }
76
+ }
77
+ },
78
+ "message": {},
79
+ "ctx": {}
80
+ }
81
+ ```
82
+
83
+ | Campo | Tipo | Descricao |
84
+ |-------|------|-----------|
85
+ | `run` | `string` | Nome do estado a executar (ex: `SYNC_EXISTING_GRANT` ou `sync_existing_grant`) |
86
+ | `payload` | `object` | Dados da action: target, employmentContract, eligibilityOptions opcionais |
87
+ | `message` | `object` | Contexto da mensagem, pode ser `{}` em testes locais |
88
+ | `ctx` | `object` | Contexto acumulado entre handlers (use o `ctx` retornado pelo handler anterior) |
89
+
90
+ ### `POST /run-action`: Formato legado
91
+
92
+ Mesmo comportamento, mas utilizando os campos `requestPayload` e `currentContext` no lugar de `payload` e `ctx`.
93
+
94
+ ### `GET /state-machine`
95
+
96
+ Retorna o objeto `stateMachine` definido em `benefit-definition.ts`.
97
+
98
+ ---
99
+
100
+ ## Como funciona por dentro
101
+
102
+ ```
103
+ Requisicao POST /
104
+ |
105
+ +- Carrega credenciais do dev-test.json
106
+ +- Chama benefitDefinition.auth.handler(credentials, { tokenHelper, logger })
107
+ | +- Token cacheado em memoria, so autentica uma vez por sessao
108
+ +- Injeta axios autenticado em ctx.benefitPartnerAPI
109
+ +- Mescla eligibilityOptions: dev-test.json < sobrescrito pelo corpo da requisicao
110
+ +- Valida eligibilityOptions e ctx contra os schemas do benefit-definition.ts
111
+ +- Executa o handler e retorna a resposta como JSON
112
+ ```
113
+
114
+ ---
115
+
116
+ ## eligibilityOptions: presets no `dev-test.json`
117
+
118
+ Para nao precisar repetir as `eligibilityOptions` em cada requisicao, defina-as no `dev-test.json`. O servidor identifica automaticamente a qual action type pertence o handler informado em `run` consultando a `stateMachine`:
119
+
120
+ ```json
121
+ {
122
+ "eligibilityOptions": {
123
+ "GRANT": {
124
+ "externalCompanyID": "12345",
125
+ "externalCardType": 1
126
+ },
127
+ "RECHARGE": {
128
+ "productCode": "VA"
129
+ }
130
+ }
131
+ }
132
+ ```
133
+
134
+ - As options do `dev-test.json` sao as **default**.
135
+ - Se voce enviar `eligibilityOptions` no corpo da requisicao, eles **sobrescrevem** os do `dev-test.json`.
136
+ - Erros de validacao do schema aparecem como `WARN` no terminal e nao bloqueiam a execucao.
137
+
138
+ ---
139
+
140
+ ## Testando um fluxo completo
141
+
142
+ Execute os handlers na ordem da maquina de estados, acumulando o `ctx`:
143
+
144
+ ```bash
145
+ # 1. Verificar se o beneficiario ja existe
146
+ curl -s -X POST http://localhost:3000 \
147
+ -H "Content-Type: application/json" \
148
+ -d '{
149
+ "run": "SYNC_EXISTING_GRANT",
150
+ "payload": { "target": { "employee": { ... } } },
151
+ "ctx": {}
152
+ }' | jq .
153
+
154
+ # Exemplo de resposta:
155
+ # {
156
+ # "handlerResponse": { "response": "ACK" },
157
+ # "action": {
158
+ # "state": "REQUEST_CREATE_EMPLOYEE",
159
+ # "ctx": { "someInternalID": "abc123" },
160
+ # "logs": { ... }
161
+ # }
162
+ # }
163
+
164
+ # 2. Criar o beneficiario, passando o ctx retornado acima
165
+ curl -s -X POST http://localhost:3000 \
166
+ -H "Content-Type: application/json" \
167
+ -d '{
168
+ "run": "REQUEST_CREATE_EMPLOYEE",
169
+ "payload": { "target": { "employee": { ... } } },
170
+ "ctx": { "someInternalID": "abc123" }
171
+ }' | jq .
172
+ ```
173
+
174
+ > **Dica:** Use `| jq .action.ctx` para extrair apenas o ctx da resposta e passar para o proximo handler.
175
+
176
+ ---
177
+
178
+ ## Variaveis de ambiente
179
+
180
+ | Variavel | Valor | Efeito |
181
+ |----------|-------|--------|
182
+ | `NODE_ENV` | `production` | Mantem a validacao TLS ativa (por padrao, desabilitada em dev para suportar certificados self-signed) |
183
+ | `VERBOSE` | `true` | Loga o payload completo de cada requisicao no terminal |
184
+
185
+ ---
186
+
187
+ ## Troubleshooting
188
+
189
+ **`dev-test.json not found`**
190
+ Crie o arquivo: `cp dev-test.example.json dev-test.json`
191
+
192
+ **`Authentication failed`**
193
+ Verifique se as credenciais em `dev-test.json` estao corretas e se a URL da API esta acessivel a partir da sua maquina.
194
+
195
+ **`Handler not found: sync_existing_grant.handler.ts`**
196
+ Verifique se:
197
+ 1. O nome em `run` corresponde a um estado da `stateMachine` em `benefit-definition.ts`.
198
+ 2. O arquivo `src/handlers/{estado}.handler.ts` existe (rode `npm run generate` para gera-lo).
199
+
200
+ **TLS / certificados self-signed**
201
+ Em ambientes com zero-trust ou proxies com certificados self-signed, o servidor ja desabilita a verificacao TLS automaticamente (exceto quando `NODE_ENV=production`).
@@ -0,0 +1,96 @@
1
+ // @ts-nocheck
2
+ /**
3
+ * npm run docs:update
4
+ *
5
+ * Reads the current benefit-definition.ts, parses the stateMachine and
6
+ * availableActions, and regenerates the ## Fluxos section in README.md
7
+ * with up-to-date Mermaid diagrams.
8
+ *
9
+ * Run manually or let the pre-commit hook handle it automatically.
10
+ */
11
+ import { readFileSync, writeFileSync } from 'fs';
12
+ import { join } from 'path';
13
+ import benefitDefinition from '../../src/benefit-definition';
14
+
15
+ const ACTION_LABELS: Record<string, string> = {
16
+ GRANT: 'Concessão de Benefício (GRANT)',
17
+ REVOKE: 'Revogação de Benefício (REVOKE)',
18
+ RECHARGE: 'Recarga de Benefício (RECHARGE)',
19
+ DEDUCTION: 'Dedução (DEDUCTION)',
20
+ GRANT_DEPENDENT: 'Concessão para Dependente (GRANT_DEPENDENT)',
21
+ REVOKE_DEPENDENT: 'Revogação de Dependente (REVOKE_DEPENDENT)',
22
+ BEFORE_ALL_RECHARGE: 'Pré-processamento de Recargas (BEFORE_ALL_RECHARGE)',
23
+ AFTER_ALL_RECHARGE: 'Pós-processamento de Recargas (AFTER_ALL_RECHARGE)',
24
+ DEDUCTION_FILE_INGESTION: 'Ingestão de Arquivo de Deduções (DEDUCTION_FILE_INGESTION)'
25
+ };
26
+
27
+ function generateMermaidDiagram(
28
+ states: Record<string, Record<string, string>>
29
+ ): string {
30
+ const lines: string[] = ['flowchart TD'];
31
+
32
+ // The starting state is usually REQUESTED_*, otherwise the first defined state
33
+ const firstState =
34
+ Object.keys(states).find((s) => s.startsWith('REQUESTED_')) ?? Object.keys(states)[0];
35
+
36
+ lines.push(` START([Início]) --> ${firstState}`);
37
+
38
+ // Track all states that appear as transition targets
39
+ const allTargets = new Set<string>();
40
+
41
+ for (const [state, transitions] of Object.entries(states)) {
42
+ for (const [transitionType, targetState] of Object.entries(
43
+ transitions as Record<string, string>
44
+ )) {
45
+ lines.push(` ${state} -->|${transitionType}| ${targetState}`);
46
+ allTargets.add(targetState);
47
+ }
48
+ }
49
+
50
+ // Terminal states = appear as targets but are not defined as source states
51
+ const terminalStates = [...allTargets].filter((s) => !(s in states));
52
+ for (const terminal of terminalStates) {
53
+ const isFailure = terminal.includes('FAILED') || terminal.includes('FAIL');
54
+ lines.push(` ${terminal}([${terminal} ${isFailure ? '❌' : '✅'}])`);
55
+ }
56
+
57
+ return '```mermaid\n' + lines.join('\n') + '\n```';
58
+ }
59
+
60
+ function generateActionSection(
61
+ action: string,
62
+ states: Record<string, Record<string, string>>
63
+ ): string {
64
+ const label = ACTION_LABELS[action] ?? action;
65
+ const diagram = generateMermaidDiagram(states);
66
+ return `### ${label}\n\n${diagram}`;
67
+ }
68
+
69
+ const stateMachine = benefitDefinition.stateMachine as Record<
70
+ string,
71
+ Record<string, Record<string, string>>
72
+ >;
73
+ const availableActions = benefitDefinition.availableActions as readonly string[];
74
+
75
+ // Only generate diagrams for actions that are:
76
+ // 1. Listed in availableActions
77
+ // 2. Defined in stateMachine
78
+ const sections = Object.entries(stateMachine)
79
+ .filter(([action]) => availableActions.includes(action))
80
+ .map(([action, states]) => generateActionSection(action, states))
81
+ .join('\n\n');
82
+
83
+ const newFluxosSection = `## Fluxos\n\n${sections}`;
84
+
85
+ const readmePath = join(process.cwd(), 'README.md');
86
+ const readme = readFileSync(readmePath, 'utf-8');
87
+
88
+ // Replace everything between ## Fluxos and the next ## heading
89
+ const updated = readme.replace(/## Fluxos[\s\S]*?(?=\n## )/, newFluxosSection + '\n');
90
+
91
+ if (updated === readme) {
92
+ console.log('[benSDK] README.md: Fluxos section is already up to date');
93
+ } else {
94
+ writeFileSync(readmePath, updated);
95
+ console.log('[benSDK] README.md updated with current state machine diagrams');
96
+ }