@lexnode/legal-data-sdk 1.30.0 → 1.32.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.
package/dist/webhooks.js CHANGED
@@ -1,29 +1,11 @@
1
1
  import { createHmac, timingSafeEqual } from "node:crypto";
2
- /**
3
- * Verificação e tipagem das entregas assinadas do motor.
4
- *
5
- * O motor assina cada entrega como `x-lexnode-signature: t=<unix>,v1=<hex>`,
6
- * onde o HMAC-SHA256 cobre `${t}.${corpoCru}` com o segredo do endpoint. A
7
- * conferência é sobre os BYTES CRUS recebidos: reserializar o JSON muda a
8
- * ordem das chaves e o digest deixa de bater.
9
- */
10
2
  export const LEXNODE_WEBHOOK_HEADERS = {
11
3
  event: "x-lexnode-event",
12
4
  timestamp: "x-lexnode-timestamp",
13
5
  signature: "x-lexnode-signature",
14
6
  deliveryAttempt: "x-lexnode-delivery-attempt",
15
7
  };
16
- /** Janela de replay do motor. */
17
8
  export const DEFAULT_WEBHOOK_TOLERANCE_SECONDS = 300;
18
- /**
19
- * A forma exata do digest que o motor emite: 64 dígitos hex minúsculos.
20
- *
21
- * Conferir a forma ANTES de decodificar é o que barra o cabeçalho maleável:
22
- * `Buffer.from(v1, "hex")` para no primeiro caractere inválido, então um
23
- * digest válido com lixo colado no fim decodifica nos mesmos bytes e passaria
24
- * pela guarda de tamanho — a mesma entrega verificaria quantas vezes o
25
- * remetente quisesse, com cabeçalhos diferentes a cada vez.
26
- */
27
9
  const V1_DIGEST = /^[0-9a-f]{64}$/u;
28
10
  function isRawBody(value) {
29
11
  return typeof value === "string" || value instanceof Uint8Array;
@@ -55,13 +37,6 @@ function parseSignatureHeader(header) {
55
37
  }
56
38
  return out;
57
39
  }
58
- /**
59
- * Confere a assinatura de uma entrega e diz POR QUE recusou.
60
- *
61
- * O veredito é motivado de propósito: um `false` nu manda o receptor devolver
62
- * o mesmo erro para segredo trocado, relógio fora de hora e corpo adulterado —
63
- * três incidentes com conduta diferente.
64
- */
65
40
  export function verifyWebhookSignature(rawBody, headers, secret, options = {}) {
66
41
  if (!secret)
67
42
  return { ok: false, reason: "MISSING_SECRET" };
@@ -78,8 +53,6 @@ export function verifyWebhookSignature(rawBody, headers, secret, options = {}) {
78
53
  if (!Number.isFinite(timestamp)) {
79
54
  return { ok: false, reason: "MALFORMED_SIGNATURE" };
80
55
  }
81
- // O cabeçalho de timestamp é redundante com o `t` da assinatura — e é por
82
- // isso que ele vale: divergir os dois é sinal de corpo remontado no caminho.
83
56
  const timestampHeader = readHeader(headers, LEXNODE_WEBHOOK_HEADERS.timestamp);
84
57
  if (timestampHeader !== null && timestampHeader.trim() !== t) {
85
58
  return { ok: false, reason: "TIMESTAMP_MISMATCH" };
@@ -89,8 +62,6 @@ export function verifyWebhookSignature(rawBody, headers, secret, options = {}) {
89
62
  if (Math.abs(now - timestamp) > tolerance) {
90
63
  return { ok: false, reason: "TIMESTAMP_OUT_OF_TOLERANCE" };
91
64
  }
92
- // O prefixo e o corpo entram separados: o corpo em bytes é assinado como
93
- // veio, sem passar por uma conversão de texto que possa alterá-lo.
94
65
  const mac = createHmac("sha256", secret).update(`${t}.`, "utf8");
95
66
  const expected = (typeof rawBody === "string"
96
67
  ? mac.update(rawBody, "utf8")
@@ -104,17 +75,7 @@ export function verifyWebhookSignature(rawBody, headers, secret, options = {}) {
104
75
  }
105
76
  return { ok: true, timestamp };
106
77
  }
107
- /**
108
- * A entrega de teste, que o endpoint recebe sem assinar.
109
- *
110
- * Ela é o PRIMEIRO corpo que um receptor novo vê — o botão de testar
111
- * conectividade existe para ser apertado antes do primeiro evento real. Por
112
- * isso está na união (quem faz `switch (event.type)` precisa enxergá-la) e
113
- * fora do catálogo assinável (não se pede, não se recusa, chega a qualquer
114
- * endpoint).
115
- */
116
78
  export const WEBHOOK_TEST_EVENT_TYPE = "WEBHOOK_TEST";
117
- /** Os tipos que se pode ASSINAR ao cadastrar um endpoint. */
118
79
  export const WEBHOOK_EVENT_TYPES = [
119
80
  "PROCESS_MOVEMENT_CREATED",
120
81
  "PROCESS_INSTANCE_DISCOVERED",
@@ -133,26 +94,12 @@ const KNOWN_EVENT_TYPES = new Set([
133
94
  ...WEBHOOK_EVENT_TYPES,
134
95
  WEBHOOK_TEST_EVENT_TYPE,
135
96
  ]);
136
- /** Se o tipo está no catálogo que este pacote descreve. */
137
97
  export function isKnownWebhookEventType(type) {
138
98
  return KNOWN_EVENT_TYPES.has(type);
139
99
  }
140
- /** Se este evento é a entrega de teste, e não um fato do acervo. */
141
100
  export function isWebhookTest(event) {
142
101
  return event.type === WEBHOOK_TEST_EVENT_TYPE;
143
102
  }
144
- /**
145
- * O status HTTP que cada recusa merece.
146
- *
147
- * A distinção que decide tudo é: o remetente deve voltar? Um corpo que não se
148
- * autentica nunca passará a se autenticar, e insistir só queima a curva de
149
- * reentrega — recusa definitiva. Já o segredo ausente é indisponibilidade
150
- * NOSSA: responder 4xx ali faz o motor desistir de uma entrega que a nossa
151
- * própria configuração derrubou, e o evento se perde para sempre.
152
- *
153
- * A regra é a mesma em todo receptor, então mora aqui em vez de ser reinventada
154
- * — com esse erro — em cada integração.
155
- */
156
103
  const REJECTION_STATUS = {
157
104
  MISSING_SECRET: 503,
158
105
  RAW_BODY_REQUIRED: 500,
@@ -167,17 +114,9 @@ const REJECTION_STATUS = {
167
114
  export function webhookRejectionStatus(reason) {
168
115
  return REJECTION_STATUS[reason];
169
116
  }
170
- /** Se a recusa é de responsabilidade do receptor, e o remetente deve voltar. */
171
117
  export function isReceiverFault(reason) {
172
118
  return REJECTION_STATUS[reason] >= 500;
173
119
  }
174
- /**
175
- * Confere a assinatura e devolve o evento tipado.
176
- *
177
- * A assinatura prova a ORIGEM; a conferência de forma que vem depois prova que
178
- * o corpo é um envelope de evento — sem ela, um corpo assinado mas malformado
179
- * viraria um `as` silencioso dentro do consumidor.
180
- */
181
120
  export function parseWebhookEvent(rawBody, headers, secret, options = {}) {
182
121
  const verification = verifyWebhookSignature(rawBody, headers, secret, options);
183
122
  if (!verification.ok)
@@ -201,10 +140,6 @@ export function parseWebhookEvent(rawBody, headers, secret, options = {}) {
201
140
  if (!body.payload || typeof body.payload !== "object") {
202
141
  return { ok: false, reason: "INVALID_ENVELOPE" };
203
142
  }
204
- // O id é a chave de idempotência de todo receptor: uma entrega repetida é
205
- // normal, e é por ele que ela se reconhece. Aceitá-lo vazio faria a primeira
206
- // entrega sem id ocupar a única vaga de deduplicação e todas as seguintes
207
- // parecerem repetição dela — um evento por conta, e o resto em silêncio.
208
143
  if (typeof body.id !== "string" || !body.id) {
209
144
  return { ok: false, reason: "INVALID_ENVELOPE" };
210
145
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@lexnode/legal-data-sdk",
3
- "version": "1.30.0",
4
- "description": "Cliente TypeScript do LexNode, o motor de dados jur\u00eddicos do Brasil: transporte tipado, erros classificados, verifica\u00e7\u00e3o de webhook e os tipos gerados do contrato OpenAPI.",
3
+ "version": "1.32.0",
4
+ "description": "Cliente TypeScript do LexNode, o motor de dados jurídicos do Brasil: transporte tipado, erros classificados, verificação de webhook e os tipos gerados do contrato OpenAPI.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://www.lexnode.com.br/docs",
7
7
  "keywords": [
@@ -39,10 +39,13 @@
39
39
  ],
40
40
  "sideEffects": false,
41
41
  "scripts": {
42
- "build": "tsc -p tsconfig.json",
43
- "prepack": "tsc -p tsconfig.json"
42
+ "clean": "rm -rf dist",
43
+ "build": "npm run clean && tsc -p tsconfig.json && tsc -p tsconfig.declarations.json && npm run test:package",
44
+ "test:package": "node --import ../test-isolation.mjs --test package.test.mjs",
45
+ "prepack": "npm run build"
44
46
  },
45
47
  "devDependencies": {
48
+ "typescript": "^5.9.3",
46
49
  "vitest": "^4.1.9"
47
50
  },
48
51
  "publishConfig": {