@lexnode/legal-data-sdk 1.30.0 → 1.31.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/README.md +197 -191
- package/dist/client.d.ts +217 -24
- package/dist/client.js +6 -103
- package/dist/errors.d.ts +86 -41
- package/dist/errors.js +1 -49
- package/dist/freshness.d.ts +88 -41
- package/dist/freshness.js +0 -47
- package/dist/index.d.ts +10 -6
- package/dist/index.js +0 -10
- package/dist/lookup.d.ts +105 -39
- package/dist/lookup.js +0 -40
- package/dist/paginate.d.ts +15 -5
- package/dist/paginate.js +0 -30
- package/dist/search.d.ts +57 -23
- package/dist/search.js +0 -17
- package/dist/types.d.ts +77 -68
- package/dist/watch.d.ts +78 -43
- package/dist/watch.js +0 -35
- package/dist/webhooks.d.ts +184 -93
- package/dist/webhooks.js +0 -65
- package/package.json +6 -3
- package/schema.d.ts +558 -518
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,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lexnode/legal-data-sdk",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.31.0",
|
|
4
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.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://www.lexnode.com.br/docs",
|
|
@@ -39,10 +39,13 @@
|
|
|
39
39
|
],
|
|
40
40
|
"sideEffects": false,
|
|
41
41
|
"scripts": {
|
|
42
|
-
"
|
|
43
|
-
"
|
|
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": {
|