@rebound-dlq/node 0.2.4 → 0.2.6

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.
package/README.md CHANGED
@@ -1,87 +1,84 @@
1
- # Rebound DLQ for Node.js
1
+ # Rebound DLQ Node.js SDK
2
2
 
3
3
  [![NPM Version](https://img.shields.io/npm/v/@rebound-dlq/node)](https://www.npmjs.com/package/@rebound-dlq/node)
4
4
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
5
 
6
- O **@rebound-dlq/node** e o SDK oficial em Node.js para integrar sua aplicacao com a plataforma **[Rebound DLQ](https://rebound-dlq.com)**.
6
+ SDK oficial em Node.js para integrar sua aplicação com a plataforma **[Rebound DLQ](https://rebound-dlq.com)**.
7
7
 
8
- Ele captura falhas da sua aplicacao, criptografa o payload localmente e envia o evento para a sua conta em background, sem travar a regra de negocio. A fila e mantida em memoria e o SDK ja entende os cenarios de rede instavel, limite de plano, overage e retomada automatica apos upgrade ou mudanca de configuracao da conta.
8
+ Capture falhas, registre eventos de erro e garanta rastreabilidade das suas filas mortas tudo sem impactar a performance da sua aplicação.
9
9
 
10
- ## Por que usar o Rebound DLQ?
10
+ ---
11
+
12
+ ## Por que usar?
13
+
14
+ - **Zero latência no fluxo principal** — o evento é enfileirado em memória e a aplicação segue sem esperar resposta de rede.
15
+ - **Criptografia local AES-256** — o payload é criptografado antes de sair do seu processo.
16
+ - **Entrega resiliente** — retry automático com backoff exponencial em caso de falha de rede.
17
+ - **Sem config extra** — o comportamento de retry, buffer e retomada é totalmente automático, determinado pela configuração da conta na plataforma.
11
18
 
12
- - **Zero latencia no fluxo principal**: o evento e enfileirado em memoria e a sua aplicacao segue executando sem esperar round-trip de rede.
13
- - **Criptografia local AES-256**: o payload e criptografado antes de sair do seu processo.
14
- - **Entrega resiliente**: falhas de rede e indisponibilidade temporaria continuam usando retry com backoff exponencial.
15
- - **Comportamento orientado ao plano da conta**: o SDK sabe quando deve continuar, quando deve aguardar e quando deve descartar eventos acima do limite.
16
- - **Retomada automatica**: quando a conta faz upgrade ou libera overage, o SDK volta a enviar sozinho.
19
+ > **Pré-requisito:** uma conta e um projeto ativos em [rebound-dlq.com](https://rebound-dlq.com).
17
20
 
18
- > Importante: este SDK requer uma conta e um projeto ativos no Rebound DLQ.
21
+ ---
19
22
 
20
- ## Instalacao
23
+ ## Instalação
21
24
 
22
25
  ```bash
23
26
  npm install @rebound-dlq/node
24
27
  ```
25
28
 
26
- ## Requisitos
27
-
28
- Para usar o SDK com o comportamento completo desta versao:
29
-
30
- - uma chave valida do projeto
31
- - uma API Rebound atualizada, com suporte a:
32
- - `POST /dlq-injestion`
33
- - `POST /dlq-injestion/runtime-state`
34
- - autenticacao HMAC com `x-api-key`, `x-sdk-timestamp` e `x-sdk-signature`
35
- - resposta estruturada de bloqueio por limite de plano
29
+ ---
36
30
 
37
- Se voce usa a plataforma oficial atualizada da Rebound, nao precisa configurar nada extra no cliente para esse fluxo. O comportamento de buffer, descarte e retomada e automatico.
31
+ ## Configuração
38
32
 
39
- ## Configuracao segura
33
+ A única configuração obrigatória é o `projectSecret`, obtido no painel da plataforma Rebound DLQ.
40
34
 
41
- Nunca exponha a chave diretamente no codigo-fonte. Use variaveis de ambiente:
35
+ Nunca exponha a chave no código-fonte. Use variáveis de ambiente:
42
36
 
43
37
  ```env
44
38
  REBOUND_PROJECT_SECRET="seu_project_secret_aqui"
45
39
  ```
46
40
 
47
- ## Executando os examples do repositorio
41
+ ---
48
42
 
49
- Os arquivos em `examples/` foram deixados simples para teste local. Cada example tem duas variaveis no topo do arquivo:
43
+ ## Agrupamento por fluxo
50
44
 
51
- - `PROJECT_SECRET`
52
- - `API_ENDPOINT`
45
+ Agora o SDK também pode ajudar a agrupar eventos DLQ por **fluxo de integração** na plataforma.
53
46
 
54
- Edite esses valores diretamente no arquivo que voce quer testar e depois rode o script.
47
+ Isso é útil quando o seu cliente quer enxergar, por exemplo, apenas os erros do fluxo `Pagarme`, `ERP`, `Webhook de pedidos` ou qualquer outro processo operacional.
55
48
 
56
- ```bash
57
- npm run example:wrapper
58
- npm run example:http
59
- npm run example:kafka
60
- npm run example:internal
61
- ```
49
+ Os campos usados pela plataforma são:
62
50
 
63
- Se preferir, voce tambem pode chamar o runner diretamente:
51
+ | Campo | Obrigatório | Descrição |
52
+ |---|---|---|
53
+ | `flowKey` | ✅ Sim | Identificador técnico e estável do fluxo. Ex.: `pagarme-payments`. |
54
+ | `flowLabel` | Não | Nome amigável exibido no painel. Ex.: `Integração Pagarme`. |
55
+ | `flowStep` | Não | Etapa específica do fluxo. Ex.: `create_charge`, `payment_webhook`, `capture_receivables`. |
64
56
 
65
- ```bash
66
- node scripts/run-example.cjs wrapper
67
- ```
57
+ ### Regra de agrupamento
68
58
 
69
- Os exemplos encerram sozinhos apos uma pequena janela de observacao para nao ficarem presos em retries de background quando a API estiver offline. Se quiser alterar isso, use `REBOUND_EXAMPLE_GRACE_MS`.
59
+ - Se chegar um evento com um `flowKey` existente naquele projeto, ele é agrupado no mesmo fluxo.
60
+ - Se o `flowKey` ainda não existir, a plataforma cria o fluxo automaticamente.
61
+ - Se o evento não enviar dados de fluxo, ele continua funcionando normalmente, como já funciona hoje.
70
62
 
71
- ### Fluxo unico de configuracao
63
+ ### Exemplo recomendado de nomenclatura
72
64
 
73
- O SDK agora trabalha com um fluxo unico:
65
+ ```ts
66
+ {
67
+ flowKey: 'pagarme-payments',
68
+ flowLabel: 'Integração Pagarme',
69
+ flowStep: 'payment_webhook'
70
+ }
71
+ ```
74
72
 
75
- - `projectSecret` para autenticar e derivar a criptografia local
76
- - `endpoint` para definir para qual API Rebound os eventos devem ser enviados
73
+ > Dica: trate `flowKey` como identificador estável. Evite usar textos que mudam com frequência.
77
74
 
78
- Nao existe mais diferenca publica entre ambiente de teste e producao dentro do SDK. Se voce quiser apontar para outra API, basta informar `endpoint` explicitamente.
75
+ ---
79
76
 
80
- ## Interfaces disponiveis
77
+ ## Interfaces disponíveis
81
78
 
82
- ### 1. `DlqWrapper`
79
+ ### `DlqWrapper` — recomendado
83
80
 
84
- Melhor opcao para encapsular uma funcao que pode falhar.
81
+ Encapsula qualquer função assíncrona. Se ela lançar uma exceção, o evento de erro é capturado, criptografado e enviado automaticamente em background.
85
82
 
86
83
  ```ts
87
84
  import { DlqWrapper } from '@rebound-dlq/node';
@@ -91,38 +88,21 @@ const dlq = new DlqWrapper({
91
88
  });
92
89
  ```
93
90
 
94
- #### Parametros
95
-
96
- | Campo | Obrigatorio | Descricao |
97
- | --- | --- | --- |
98
- | `projectSecret` | Sim | Chave secreta do projeto usada para criptografia local e autenticacao do SDK. |
99
- | `endpoint` | Nao | URL base da sua API Rebound, por exemplo `https://api.suaempresa.com/api/v1`. O SDK monta `/dlq-injestion` automaticamente, mas tambem aceita a URL completa por compatibilidade. |
100
- | `maxFailures` | Nao | Mantido apenas por compatibilidade retroativa. Atualmente nao altera o comportamento da fila. |
91
+ **Parâmetros:**
101
92
 
102
- ### 2. `ReboundClient`
93
+ | Campo | Obrigatório | Descrição |
94
+ |---|---|---|
95
+ | `projectSecret` | ✅ Sim | Chave secreta do projeto, usada para autenticação e criptografia local. |
103
96
 
104
- Melhor opcao para enviar payloads DLQ manualmente ou usar os adapters.
97
+ **Uso:**
105
98
 
106
99
  ```ts
107
- import { ReboundClient } from '@rebound-dlq/node';
108
-
109
- const client = new ReboundClient({
110
- projectSecret: process.env.REBOUND_PROJECT_SECRET!,
111
- endpoint: process.env.REBOUND_API_ENDPOINT,
112
- });
100
+ const resultado = await dlq.execute(() => minhaFuncaoQuePodefFalhar(dados), dados);
113
101
  ```
114
102
 
115
- #### Parametros
103
+ O erro original é sempre relançado para o seu tratamento normal. O SDK apenas captura, criptografa e envia o evento em background.
116
104
 
117
- | Campo | Obrigatorio | Descricao |
118
- | --- | --- | --- |
119
- | `projectSecret` | Sim | Secret do projeto criado no painel, usado para autenticar as chamadas do SDK. |
120
- | `endpoint` | Nao | URL base da sua API Rebound, por exemplo `https://api.suaempresa.com/api/v1`. O SDK monta `/dlq-injestion` automaticamente, mas tambem aceita a URL completa por compatibilidade. |
121
- | `timeout` | Nao | Campo reservado no tipo de configuracao. Atualmente nao altera o transporte. |
122
-
123
- Quando voce usa `ReboundClient` ou um adapter, o SDK converte o payload manual para o contrato de ingestao da API, gera `fingerprint`, criptografa o envelope localmente e envia no mesmo formato aceito pelo backend.
124
-
125
- ## Uso recomendado com `DlqWrapper`
105
+ **Exemplo completo:**
126
106
 
127
107
  ```ts
128
108
  import { DlqWrapper } from '@rebound-dlq/node';
@@ -131,47 +111,56 @@ const dlq = new DlqWrapper({
131
111
  projectSecret: process.env.REBOUND_PROJECT_SECRET!,
132
112
  });
133
113
 
134
- async function processarPagamento(dados: {
135
- userId: string;
136
- valor: number;
137
- tentativa: number;
138
- modulo: string;
139
- }) {
140
- if (dados.valor > 1000) {
141
- throw new Error('Saldo insuficiente na operadora do cartao');
114
+ async function processarPagamento(dados: { userId: string; valor: number }) {
115
+ if (dados.valor > 10000) {
116
+ throw new Error('Limite excedido na operadora');
142
117
  }
143
-
144
- return 'Sucesso';
118
+ return 'aprovado';
145
119
  }
146
120
 
147
- async function main() {
148
- const payload = {
149
- userId: '123',
150
- valor: 5000,
151
- tentativa: 1,
152
- modulo: 'Checkout',
153
- };
154
-
155
- try {
156
- const resultado = await dlq.execute(() => processarPagamento(payload), payload);
157
- console.log('Resultado:', resultado);
158
- } catch (erro) {
159
- console.error('Erro de negocio capturado pela aplicacao:', (erro as Error).message);
160
- }
121
+ try {
122
+ const resultado = await dlq.execute(
123
+ () => processarPagamento({ userId: 'u-123', valor: 15000 }),
124
+ { userId: 'u-123', valor: 15000 },
125
+ );
126
+ } catch (err) {
127
+ // erro relançado normalmente — o SDK já registrou o evento
128
+ console.error((err as Error).message);
161
129
  }
130
+ ```
131
+
132
+ ### `DlqWrapper` com fluxo
133
+
134
+ Se você quiser agrupar o evento por fluxo usando o `DlqWrapper`, passe um terceiro parâmetro opcional com o contexto do fluxo.
135
+
136
+ ```ts
137
+ import { DlqWrapper } from '@rebound-dlq/node';
138
+
139
+ const dlq = new DlqWrapper({
140
+ projectSecret: process.env.REBOUND_PROJECT_SECRET!,
141
+ });
162
142
 
163
- main();
143
+ await dlq.execute(
144
+ () => processarPagamento({ userId: 'u-123', valor: 15000 }),
145
+ {
146
+ userId: 'u-123',
147
+ valor: 15000,
148
+ },
149
+ {
150
+ flowKey: 'pagarme-payments',
151
+ flowLabel: 'Integração Pagarme',
152
+ flowStep: 'create_charge',
153
+ },
154
+ );
164
155
  ```
165
156
 
166
- ### O que acontece nesse fluxo
157
+ > Se você não passar o terceiro parâmetro, o evento continua sendo enviado normalmente, sem vínculo com fluxo.
158
+
159
+ ---
167
160
 
168
- 1. Sua funcao falha e a excecao e capturada.
169
- 2. O payload e criptografado localmente com AES-256.
170
- 3. O evento e colocado em uma fila em memoria.
171
- 4. O SDK envia o evento em background com autenticacao HMAC.
172
- 5. O erro original continua sendo relancado para o seu tratamento normal.
161
+ ### `ReboundClient` + adapters uso avançado
173
162
 
174
- ## Uso manual com `ReboundClient`
163
+ Para enviar eventos manualmente ou usar com adapters (HTTP, Kafka etc.).
175
164
 
176
165
  ```ts
177
166
  import { ReboundClient, HTTPAdapter } from '@rebound-dlq/node';
@@ -183,123 +172,120 @@ const client = new ReboundClient({
183
172
  const httpDlq = new HTTPAdapter(client);
184
173
 
185
174
  await httpDlq.sendToDLQ({
186
- payload: {
187
- orderId: 'ORD-1001',
188
- amount: 99.9,
189
- },
175
+ payload: { orderId: 'ORD-1001', amount: 99.9 },
190
176
  error: new Error('Payment gateway timeout'),
177
+ flowKey: 'checkout-payments',
178
+ flowLabel: 'Checkout de pagamentos',
179
+ flowStep: 'gateway_authorization',
191
180
  metadata: {
192
181
  endpoint: '/api/payments',
193
182
  method: 'POST',
194
183
  tenantId: 'acme',
195
- },
184
+ }
196
185
  });
197
186
  ```
198
187
 
199
- ## Comportamento de entrega
200
-
201
- ### Falha de rede ou API indisponivel
202
-
203
- Para erros transientes, o SDK continua aplicando retry em background com backoff exponencial.
188
+ **Parâmetros do `ReboundClient`:**
204
189
 
205
- ### Conta liberada para overage
190
+ | Campo | Obrigatório | Descrição |
191
+ |---|---|---|
192
+ | `projectSecret` | ✅ Sim | Chave secreta do projeto, usada para autenticação e criptografia local. |
206
193
 
207
- Quando a conta pode cobrar uso extra, a ingestao continua normalmente e o excedente e faturado pela plataforma na fatura Stripe da conta.
194
+ ### Exemplo com fluxo usando `HTTPAdapter`
208
195
 
209
- ### Conta bloqueada por limite com `bill_extra` desativado
210
-
211
- Quando a conta opta por **nao exceder o limite**:
212
-
213
- - o SDK nao insiste em retry infinito para esse caso
214
- - o evento atual e descartado
215
- - novos eventos tambem sao descartados enquanto a conta continuar bloqueada
216
- - o SDK consulta o estado da conta periodicamente e volta a enviar sozinho quando ela se tornar elegivel de novo
217
-
218
- Esse comportamento existe para respeitar a decisao explicita da conta de nao gerar custo extra.
219
-
220
- ### Conta bloqueada por limite, mas com escolha de `bill_extra`
221
-
222
- Quando a conta escolheu **aceitar cobranca extra** e a plataforma ainda responde com bloqueio temporario, o SDK assume que o cliente prefere **nao perder eventos** e usa um buffer temporario:
223
-
224
- - os eventos ficam em um `blocked buffer` em memoria por ate **15 minutos**
225
- - o buffer tem limite de **250 eventos por processo e por chave**
226
- - o SDK faz polling autenticado a cada **60 segundos**
227
- - quando a conta volta a ficar apta, os eventos buffered sao reenfileirados e enviados em ordem FIFO
228
-
229
- Se a conta continuar bloqueada alem da janela de buffer, os eventos buffered expiram e sao descartados com log explicito.
196
+ ```ts
197
+ import { ReboundClient, HTTPAdapter } from '@rebound-dlq/node';
230
198
 
231
- ## Observacao sobre `endpoint`
199
+ const client = new ReboundClient({
200
+ projectSecret: process.env.REBOUND_PROJECT_SECRET!,
201
+ });
232
202
 
233
- Prefira informar o `endpoint` como **URL base da API**. Exemplo recomendado:
203
+ const httpDlq = new HTTPAdapter(client);
234
204
 
235
- ```ts
236
- endpoint: 'https://sua-api.com/api/v1'
205
+ await httpDlq.sendToDLQ({
206
+ payload: {
207
+ orderId: 'ORD-1001',
208
+ amount: 99.9,
209
+ customerId: 'cus_123',
210
+ },
211
+ error: new Error('Payment gateway timeout'),
212
+ flowKey: 'pagarme-payments',
213
+ flowLabel: 'Integração Pagarme',
214
+ flowStep: 'create_charge',
215
+ metadata: {
216
+ endpoint: '/api/payments',
217
+ method: 'POST',
218
+ tenantId: 'acme',
219
+ },
220
+ });
237
221
  ```
238
222
 
239
- Tambem funciona por compatibilidade:
223
+ ### Exemplo com fluxo usando `KafkaAdapter`
240
224
 
241
225
  ```ts
242
- endpoint: 'https://sua-api.com/api/v1/dlq-injestion'
243
- ```
244
-
245
- O SDK adiciona internamente:
246
-
247
- - `/dlq-injestion`
248
- - `/dlq-injestion/runtime-state`
249
-
250
- ## Matriz de comportamento
226
+ import { ReboundClient, KafkaAdapter } from '@rebound-dlq/node';
251
227
 
252
- | Situacao | Comportamento do SDK |
253
- | --- | --- |
254
- | `2xx` da API | remove o item da fila |
255
- | falha de rede / `5xx` / erro transiente | retry com backoff exponencial |
256
- | limite de plano com politica `block` | descarta enquanto bloqueado e reconsulta estado |
257
- | limite de plano com escolha de `bill_extra`, mas bloqueio temporario | usa blocked buffer e reconsulta estado |
258
- | conta liberada novamente | retoma automaticamente sem reiniciar o processo |
259
-
260
- ## O que o cliente precisa saber sobre limites e cobranca extra
261
-
262
- Nao existe parametro novo no SDK para habilitar esse comportamento. Ele depende da configuracao da conta no Rebound DLQ:
263
-
264
- - se a conta habilitar cobranca extra para eventos, a plataforma tenta continuar a ingestao e cobrar o excedente
265
- - se a conta desabilitar cobranca extra, o SDK entende que pode haver perda de dados acima do limite e passa a descartar enquanto durar o bloqueio
266
- - se a conta fizer upgrade ou alterar essa politica, o SDK detecta a mudanca automaticamente na proxima sincronizacao
267
-
268
- Em outras palavras: **a decisao de produto e da conta; o SDK apenas executa essa politica automaticamente**.
228
+ const client = new ReboundClient({
229
+ projectSecret: process.env.REBOUND_PROJECT_SECRET!,
230
+ });
269
231
 
270
- ## Observacoes operacionais importantes
232
+ const kafkaDlq = new KafkaAdapter(client);
271
233
 
272
- - a fila do SDK e **in-memory**
273
- - eventos pendentes ou buffered **nao sobrevivem** a restart do processo
274
- - workloads de curta duracao ou ambientes muito efemeros podem perder eventos se o processo encerrar logo apos o enqueue
275
- - para maior seguranca operacional, prefira rodar o SDK em processos com shutdown gracioso
276
- - o SDK registra logs no console quando entra em modo de retry, descarte, buffer e retomada
234
+ await kafkaDlq.sendToDLQ({
235
+ payload: {
236
+ topic: 'payments.events',
237
+ partition: 2,
238
+ key: 'order-1001',
239
+ message: { orderId: 'ORD-1001', status: 'failed' },
240
+ },
241
+ error: new Error('Kafka publish timeout'),
242
+ flowKey: 'pagarme-payments',
243
+ flowLabel: 'Integração Pagarme',
244
+ flowStep: 'payment_webhook',
245
+ metadata: {
246
+ topic: 'payments.events',
247
+ consumerGroup: 'payments-worker',
248
+ },
249
+ });
250
+ ```
277
251
 
278
- ## Compatibilidade com APIs antigas
252
+ ### Resumo prático de uso
279
253
 
280
- Se a API de destino ainda nao suportar o contrato estruturado de bloqueio por limite, o SDK continua tratando respostas nao `2xx` como erros retryaveis. Ou seja: o fluxo novo de `buffer` e `discard` depende da API estar atualizada.
254
+ - Use `DlqWrapper` quando quiser a integração mais simples possível.
255
+ - Use `ReboundClient` + adapters quando quiser separar melhor `payload` e `metadata`.
256
+ - Para agrupamento por fluxo, envie `flowKey`, `flowLabel` e `flowStep` como campos opcionais separados do payload.
257
+ - `metadata` continua existindo para contexto técnico adicional, sem misturar regra de agrupamento com o payload do usuário.
281
258
 
282
- ## FAQ rapido
259
+ ---
283
260
 
284
- ### Preciso alterar meu codigo para usar o buffer ou o descarte?
261
+ ## Comportamento automático
285
262
 
286
- Nao. Esse comportamento e automatico nesta versao do SDK.
263
+ O SDK gerencia entrega, retry e limites de plano de forma totalmente automática, sem configuração adicional.
287
264
 
288
- ### Preciso informar um parametro novo no construtor?
265
+ | Situação | O que o SDK faz |
266
+ |---|---|
267
+ | Sucesso na entrega | Remove o evento da fila |
268
+ | Falha de rede / erro transiente | Retry com backoff exponencial |
269
+ | Conta no limite (sem cobrança extra) | Descarta eventos e reconsulta o estado periodicamente |
270
+ | Conta no limite (com cobrança extra ativa) | Mantém eventos em buffer por até 15 min e retenta quando liberado |
271
+ | Conta desbloqueada / upgrade | Retoma o envio automaticamente, sem reiniciar o processo |
289
272
 
290
- Nao. Basta manter `projectSecret` e, se necessario, `endpoint`.
273
+ > A política de limite e cobrança extra é definida na sua conta na plataforma, não no SDK.
291
274
 
292
- ### O SDK vai me avisar quando a conta for desbloqueada?
275
+ ---
293
276
 
294
- Sim. Ele faz polling em background e retoma sozinho quando a API indicar que a conta voltou a poder ingerir eventos.
277
+ ## Observações operacionais
295
278
 
296
- ### O buffer guarda tudo indefinidamente?
279
+ - A fila é **in-memory**: eventos não sobrevivem a restart do processo.
280
+ - Para workloads efêmeros, prefira processos com **shutdown gracioso**.
281
+ - O SDK emite logs no console ao entrar em modo de retry, buffer ou descarte.
297
282
 
298
- Nao. Ele e temporario, fica apenas em memoria, dura ate 15 minutos e respeita o limite de 250 eventos por processo/chave.
283
+ ---
299
284
 
300
285
  ## Suporte
301
286
 
302
- Se voce tiver duvidas de integracao, comportamento de limites ou operacao do SDK, abra um chamado pela plataforma oficial da [Rebound DLQ](https://rebound-dlq.com).
287
+ Dúvidas de integração ou comportamento? Abra um chamado pela plataforma oficial em [rebound-dlq.com](https://rebound-dlq.com).
303
288
 
304
289
  ---
290
+
305
291
  Feito por **Codify Labs / Rebound DLQ**.
@@ -6,13 +6,16 @@ class BaseAdapter {
6
6
  this.client = client;
7
7
  }
8
8
  async sendToDLQ(options) {
9
- const { payload, error, source = 'unknown', metadata = {} } = options;
9
+ const { payload, error, source = 'unknown', metadata = {}, flowKey, flowLabel, flowStep } = options;
10
10
  const dlqPayload = {
11
11
  payload,
12
12
  source,
13
13
  error: this.normalizeError(error),
14
14
  metadata,
15
- timestamp: Date.now()
15
+ timestamp: Date.now(),
16
+ flowKey,
17
+ flowLabel,
18
+ flowStep,
16
19
  };
17
20
  return this.client.send(dlqPayload);
18
21
  }
@@ -1,4 +1,4 @@
1
- import { DLQPayload, ReboundConfig } from "../models/payload";
1
+ import { DLQPayload, ReboundConfig } from '../models/payload';
2
2
  export declare class ReboundClient {
3
3
  private config;
4
4
  private readonly baseUrl;
@@ -7,5 +7,7 @@ export declare class ReboundClient {
7
7
  private buildIngestionPayload;
8
8
  private normalizeError;
9
9
  private generateFingerprint;
10
+ private normalizeFlowContext;
11
+ private normalizeOptionalText;
10
12
  private encrypt;
11
13
  }
@@ -28,10 +28,12 @@ class ReboundClient {
28
28
  metadata: payload.metadata ?? {},
29
29
  timestamp: payload.timestamp || Date.now(),
30
30
  };
31
+ const flowContext = this.normalizeFlowContext(payload);
31
32
  return {
32
33
  fingerprint: this.generateFingerprint(normalizedError, payload.source),
33
34
  error: normalizedError,
34
35
  payload: this.encrypt(envelope),
36
+ ...flowContext,
35
37
  };
36
38
  }
37
39
  normalizeError(error) {
@@ -50,6 +52,24 @@ class ReboundClient {
50
52
  const seed = `${error?.name || 'UnknownError'}${error?.message || ''}${stack}${source}`;
51
53
  return crypto_1.default.createHash('sha256').update(seed).digest('hex');
52
54
  }
55
+ normalizeFlowContext(payload) {
56
+ const flowKey = this.normalizeOptionalText(payload.flowKey);
57
+ if (!flowKey) {
58
+ return {};
59
+ }
60
+ return {
61
+ flowKey,
62
+ flowLabel: this.normalizeOptionalText(payload.flowLabel),
63
+ flowStep: this.normalizeOptionalText(payload.flowStep),
64
+ };
65
+ }
66
+ normalizeOptionalText(value) {
67
+ if (typeof value !== 'string') {
68
+ return undefined;
69
+ }
70
+ const trimmed = value.trim();
71
+ return trimmed.length > 0 ? trimmed : undefined;
72
+ }
53
73
  encrypt(payload) {
54
74
  const algorithm = 'aes-256-cbc';
55
75
  const key = crypto_1.default
@@ -1 +1 @@
1
- export declare const DEFAULT_REBOUND_API_ENDPOINT = "http://localhost:3001/api/v1";
1
+ export declare const DEFAULT_REBOUND_API_ENDPOINT = "https://api.rebound-dlq.com/api/v1";
@@ -1,4 +1,4 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.DEFAULT_REBOUND_API_ENDPOINT = void 0;
4
- exports.DEFAULT_REBOUND_API_ENDPOINT = 'http://localhost:3001/api/v1';
4
+ exports.DEFAULT_REBOUND_API_ENDPOINT = 'https://api.rebound-dlq.com/api/v1';
@@ -1,3 +1,4 @@
1
+ import type { DlqFlowContext } from '../models/payload';
1
2
  export interface DlqServiceConfig {
2
3
  projectSecret: string;
3
4
  endpoint?: string;
@@ -7,9 +8,11 @@ export declare class DlqWrapper {
7
8
  private projectSecret;
8
9
  private readonly endpoint;
9
10
  constructor(config: DlqServiceConfig);
10
- execute<T>(fn: () => Promise<T>, payload: any): Promise<T>;
11
+ execute<T>(fn: () => Promise<T>, payload: any, flow?: DlqFlowContext): Promise<T>;
11
12
  private reportError;
12
13
  private generateFingerprint;
14
+ private normalizeFlowContext;
15
+ private normalizeOptionalText;
13
16
  private encrypt;
14
17
  private sendToApi;
15
18
  }
@@ -14,22 +14,23 @@ class DlqWrapper {
14
14
  const configuredEndpoint = config.endpoint || constants_1.DEFAULT_REBOUND_API_ENDPOINT;
15
15
  this.endpoint = (0, endpoint_1.resolveDlqIngestionEndpoint)(configuredEndpoint);
16
16
  }
17
- async execute(fn, payload) {
17
+ async execute(fn, payload, flow) {
18
18
  try {
19
19
  return await fn();
20
20
  }
21
21
  catch (error) {
22
22
  // MemoryQueue handles retries in the background, making this instant
23
- this.reportError(error, payload).catch(e => {
23
+ this.reportError(error, payload, flow).catch(e => {
24
24
  // Silently fail if reportError fails, so we don't block the client logic
25
25
  console.error("[Rebound SDK] Error building payload to DLQ", e);
26
26
  });
27
27
  throw error;
28
28
  }
29
29
  }
30
- async reportError(error, payload) {
30
+ async reportError(error, payload, flow) {
31
31
  const fingerprint = this.generateFingerprint(error);
32
32
  const encryptedPayload = this.encrypt(payload);
33
+ const normalizedFlow = this.normalizeFlowContext(flow);
33
34
  const data = {
34
35
  fingerprint,
35
36
  error: {
@@ -37,7 +38,8 @@ class DlqWrapper {
37
38
  message: error.message,
38
39
  stack: error.stack
39
40
  },
40
- payload: encryptedPayload
41
+ payload: encryptedPayload,
42
+ ...normalizedFlow,
41
43
  };
42
44
  return this.sendToApi(data);
43
45
  }
@@ -47,6 +49,24 @@ class DlqWrapper {
47
49
  .update(`${error.name}${stack}`)
48
50
  .digest('hex');
49
51
  }
52
+ normalizeFlowContext(flow) {
53
+ const flowKey = this.normalizeOptionalText(flow?.flowKey);
54
+ if (!flowKey) {
55
+ return {};
56
+ }
57
+ return {
58
+ flowKey,
59
+ flowLabel: this.normalizeOptionalText(flow?.flowLabel),
60
+ flowStep: this.normalizeOptionalText(flow?.flowStep),
61
+ };
62
+ }
63
+ normalizeOptionalText(value) {
64
+ if (typeof value !== 'string') {
65
+ return undefined;
66
+ }
67
+ const trimmed = value.trim();
68
+ return trimmed.length > 0 ? trimmed : undefined;
69
+ }
50
70
  encrypt(payload) {
51
71
  const algorithm = 'aes-256-cbc';
52
72
  // Use projectSecret to derive a 32-byte key.
package/dist/index.d.ts CHANGED
@@ -4,4 +4,4 @@ export type { DlqServiceConfig } from './core/wrapper';
4
4
  export { HTTPAdapter } from './adapters/http.adapter';
5
5
  export { KafkaAdapter } from './adapters/kafka.adapter';
6
6
  export { ReboundError, ReboundAPIError } from './core/errors';
7
- export type { DLQPayload, ReboundConfig, SendToDLQOptions } from './models/payload';
7
+ export type { DLQPayload, DlqFlowContext, ReboundConfig, SendToDLQOptions } from './models/payload';
@@ -1,3 +1,8 @@
1
+ export interface DlqFlowContext {
2
+ flowKey?: string;
3
+ flowLabel?: string;
4
+ flowStep?: string;
5
+ }
1
6
  export interface DLQPayload {
2
7
  payload: any;
3
8
  source: string;
@@ -9,13 +14,16 @@ export interface DLQPayload {
9
14
  };
10
15
  metadata?: Record<string, any>;
11
16
  timestamp?: number;
17
+ flowKey?: string;
18
+ flowLabel?: string;
19
+ flowStep?: string;
12
20
  }
13
21
  export interface ReboundConfig {
14
22
  projectSecret: string;
15
23
  endpoint?: string;
16
24
  timeout?: number;
17
25
  }
18
- export interface SendToDLQOptions {
26
+ export interface SendToDLQOptions extends DlqFlowContext {
19
27
  payload: any;
20
28
  error?: Error | string;
21
29
  source?: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rebound-dlq/node",
3
- "version": "0.2.4",
3
+ "version": "0.2.6",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "files": ["dist"],