@lexnode/legal-data-sdk 1.0.0 → 1.24.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 +385 -11
- package/dist/client.d.ts +100 -0
- package/dist/client.js +476 -0
- package/dist/errors.d.ts +94 -0
- package/dist/errors.js +217 -0
- package/dist/freshness.d.ts +48 -0
- package/dist/freshness.js +105 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.js +17 -0
- package/dist/paginate.d.ts +21 -0
- package/dist/paginate.js +106 -0
- package/dist/search.d.ts +64 -0
- package/dist/search.js +75 -0
- package/dist/types.d.ts +182 -0
- package/dist/types.js +1 -0
- package/dist/watch.d.ts +93 -0
- package/dist/watch.js +95 -0
- package/dist/webhooks.d.ts +330 -0
- package/dist/webhooks.js +223 -0
- package/package.json +28 -4
- package/schema.d.ts +972 -95
package/README.md
CHANGED
|
@@ -1,25 +1,381 @@
|
|
|
1
1
|
# @lexnode/legal-data-sdk
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
Cliente TypeScript do **LexNode**, o motor de dados jurídicos do Brasil:
|
|
4
|
+
processos e movimentações, publicações dos diários oficiais, jurisprudência,
|
|
5
|
+
monitores e webhooks.
|
|
6
|
+
|
|
7
|
+
O pacote traz **os tipos gerados do contrato OpenAPI** e um **cliente de
|
|
8
|
+
runtime** construído sobre eles: transporte com os cabeçalhos certos, erros
|
|
9
|
+
classificados, verificação de assinatura de webhook, espera de busca
|
|
10
|
+
assíncrona, leitura do alcance da fonte e a composição de vigília com vários
|
|
11
|
+
interessados.
|
|
12
|
+
|
|
13
|
+
- **Sem dependências de runtime.** `fetch` global e `node:crypto`.
|
|
14
|
+
- **Node >= 20**, ESM.
|
|
15
|
+
|
|
16
|
+
> ⚠️ **Servidor apenas, e só por TLS.** A credencial é uma chave de **conta**:
|
|
17
|
+
> no browser ela está publicada, e em `http://` ela é lida por quem estiver no
|
|
18
|
+
> caminho — uma `baseUrl` sem https é recusada no construtor (host local é a
|
|
19
|
+
> exceção, para desenvolvimento). Um app com usuários finais fala com o próprio
|
|
20
|
+
> backend, e é o backend que fala com o motor — atribuindo cada chamada ao seu
|
|
21
|
+
> sub-tenant pelo cabeçalho `X-Office-Id`.
|
|
8
22
|
|
|
9
23
|
## Instalação
|
|
10
24
|
|
|
11
25
|
```sh
|
|
12
|
-
npm install
|
|
26
|
+
npm install @lexnode/legal-data-sdk
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## O cliente
|
|
30
|
+
|
|
31
|
+
```ts
|
|
32
|
+
import { createLexNodeClient } from "@lexnode/legal-data-sdk";
|
|
33
|
+
|
|
34
|
+
const lexnode = createLexNodeClient({
|
|
35
|
+
baseUrl: "https://api.lexnode.com.br",
|
|
36
|
+
apiKey: process.env.LEXNODE_API_KEY!,
|
|
37
|
+
// Sub-tenant padrão; cada chamada pode trocá-lo.
|
|
38
|
+
officeId: "escritorio-01",
|
|
39
|
+
timeoutMs: 30_000,
|
|
40
|
+
userAgent: "meu-app/1.0",
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
const { data, meta } = await lexnode.processes.get("00012345620268260100", {
|
|
44
|
+
tribunalAlias: "api_publica_tjsp",
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
console.log(data.formattedNumber, meta.credits, meta.rateLimit?.remaining);
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Toda chamada manda `Authorization: Bearer`, `X-Request-Id`, `X-Trace-Id` e,
|
|
51
|
+
quando há um, `X-Office-Id`; respeita o prazo do cliente **combinado** com o
|
|
52
|
+
`signal` de quem chamou; e devolve `{ data, meta }` tipados pela rota. O `meta`
|
|
53
|
+
é o do contrato mais `status` (o código HTTP) e `rateLimit` (os cabeçalhos
|
|
54
|
+
`RateLimit-*` / `Retry-After`, lidos **inclusive no sucesso**).
|
|
55
|
+
|
|
56
|
+
Em `rateLimit`, use `remaining` para desacelerar: `resetSeconds` repete o que o
|
|
57
|
+
cabeçalho `RateLimit-Reset` trouxe, e o motor lhe dá dois sentidos — segundos
|
|
58
|
+
até reabrir na recusa, comprimento da janela (60 s) no sucesso.
|
|
59
|
+
|
|
60
|
+
O pacote **não valida a resposta em tempo de execução**: `data` e `meta` chegam
|
|
61
|
+
tipados pelo contrato, e o que garante a correspondência é a versão fixada, não
|
|
62
|
+
uma conferência a cada chamada. Quem precisa de barreira na borda — porque o
|
|
63
|
+
dado alimenta um banco, por exemplo — valida o que consome.
|
|
64
|
+
|
|
65
|
+
### Métodos nomeados e a chamada genérica
|
|
66
|
+
|
|
67
|
+
Os métodos nomeados cobrem o uso diário:
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
await lexnode.tribunals.list();
|
|
71
|
+
await lexnode.processes.movements(numero, { tribunalAlias });
|
|
72
|
+
await lexnode.publications.list({ oabNumber: "123456", oabState: "SP" });
|
|
73
|
+
await lexnode.monitors.create({ type: "OAB", oabNumber: "123456", oabState: "SP" });
|
|
74
|
+
await lexnode.subscriptions.create({ tribunalAlias, processNumber, externalRef });
|
|
75
|
+
await lexnode.searchJobs.get(jobId);
|
|
76
|
+
await lexnode.events.list({ limit: 50 });
|
|
77
|
+
await lexnode.webhooks.endpoints.list();
|
|
78
|
+
await lexnode.webhooks.unsubscribedEventTypes();
|
|
79
|
+
await lexnode.usage.get();
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Qualquer rota do contrato — inclusive uma que o pacote ainda não batizou — sai
|
|
83
|
+
pela chamada genérica, tipada por `paths`:
|
|
84
|
+
|
|
85
|
+
```ts
|
|
86
|
+
const { data } = await lexnode.request("/v1/precedents", "get", {
|
|
87
|
+
params: { query: { query: "dano moral", limit: 20 } },
|
|
88
|
+
});
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Páginas
|
|
92
|
+
|
|
93
|
+
O motor pagina de três jeitos (cursor no `meta`, cursor no `data`, e
|
|
94
|
+
página/tamanho ou deslocamento/teto). `paginate` percorre qualquer um deles, com
|
|
95
|
+
teto explícito de páginas — cada página é uma chamada medida:
|
|
96
|
+
|
|
97
|
+
```ts
|
|
98
|
+
import { paginate } from "@lexnode/legal-data-sdk";
|
|
99
|
+
|
|
100
|
+
for await (const page of paginate(lexnode, "/v1/monitors", "get", {})) {
|
|
101
|
+
for (const monitor of page) console.log(monitor.id);
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Erros
|
|
106
|
+
|
|
107
|
+
```ts
|
|
108
|
+
import { LexNodeError } from "@lexnode/legal-data-sdk";
|
|
109
|
+
|
|
110
|
+
try {
|
|
111
|
+
await lexnode.processes.search({ tribunalAlias, number });
|
|
112
|
+
} catch (error) {
|
|
113
|
+
if (error instanceof LexNodeError) {
|
|
114
|
+
error.code; // "RATE_LIMITED", "PROCESS_NOT_FOUND", …
|
|
115
|
+
error.class; // "RETRYABLE" | "TERMINAL"
|
|
116
|
+
error.retryable;
|
|
117
|
+
error.status;
|
|
118
|
+
error.upstreamStatus; // o status real da fonte, quando preservado
|
|
119
|
+
error.requestId; // o que o suporte pede
|
|
120
|
+
error.fieldErrors; // recusa de validação, por campo
|
|
121
|
+
error.retryAfterSeconds;
|
|
122
|
+
error.rateLimit;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Prazo esgotado vira `CLIENT_TIMEOUT` (504, retentável); falha de rede vira
|
|
128
|
+
`CLIENT_NETWORK_ERROR` (502, retentável); resposta fora do envelope vira
|
|
129
|
+
`INVALID_RESPONSE` (502) com o corpo cru truncado na mensagem; o que o
|
|
130
|
+
transporte recusa mandar — cabeçalho com quebra de linha, corpo que não
|
|
131
|
+
serializa — vira `CLIENT_INVALID_REQUEST` (400, **não** retentável), porque
|
|
132
|
+
repetir o que nunca saiu não cura.
|
|
133
|
+
|
|
134
|
+
`LEXNODE_ERROR_CODES` traz o catálogo para o `switch`, e `LexNodeErrorCode`
|
|
135
|
+
mantém o autocompletar sem fechar a porta a um código novo. Importa na leitura
|
|
136
|
+
de processo: o motor responde 404 tanto para `PROCESS_NOT_FOUND` (o processo não
|
|
137
|
+
existe na fonte) quanto para `MOVEMENTS_NOT_AVAILABLE` (existe, mas as
|
|
138
|
+
movimentações não vêm) — quem trata os dois igual conta a história errada.
|
|
139
|
+
|
|
140
|
+
### Retentativa (opcional, desligada por padrão)
|
|
141
|
+
|
|
142
|
+
```ts
|
|
143
|
+
const lexnode = createLexNodeClient({
|
|
144
|
+
baseUrl,
|
|
145
|
+
apiKey,
|
|
146
|
+
retry: { attempts: 3, baseDelayMs: 500 },
|
|
147
|
+
});
|
|
13
148
|
```
|
|
14
149
|
|
|
15
|
-
|
|
150
|
+
Repete só o que é `RETRYABLE`, respeitando `Retry-After`. `GET` e `DELETE` são
|
|
151
|
+
repetidos por definição do HTTP. Uma **escrita** só é repetida quando o contrato
|
|
152
|
+
declara a repetição segura — hoje `POST /v1/publications/text-searches`, que usa
|
|
153
|
+
o `requestId` que você fixar como chave de replay — ou quando você mesmo o
|
|
154
|
+
declara na chamada:
|
|
16
155
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
156
|
+
```ts
|
|
157
|
+
await lexnode.webhooks.endpoints.create(corpo, { idempotent: true });
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
A afirmação é sua: sem ela, um `POST` repetido cria um **segundo** recurso — dois
|
|
161
|
+
endpoints para a mesma URL entregam cada evento em dobro, e o segredo do
|
|
162
|
+
primeiro fica órfão. Em toda outra rota o `requestId` é só rastreio.
|
|
163
|
+
|
|
164
|
+
A exceção é o `UPSTREAM_TIMEOUT` da leitura de um processo, que a política não
|
|
165
|
+
repete — ver [a seção da fonte lenta](#ler-um-processo-quando-a-fonte-está-lenta).
|
|
166
|
+
|
|
167
|
+
## Webhooks
|
|
20
168
|
|
|
21
169
|
```ts
|
|
22
|
-
import
|
|
170
|
+
import {
|
|
171
|
+
parseWebhookEvent,
|
|
172
|
+
webhookRejectionStatus,
|
|
173
|
+
} from "@lexnode/legal-data-sdk";
|
|
174
|
+
|
|
175
|
+
// O corpo CRU, nunca um JSON reserializado: o digest é sobre os bytes.
|
|
176
|
+
const resultado = parseWebhookEvent(
|
|
177
|
+
rawBody,
|
|
178
|
+
request.headers,
|
|
179
|
+
process.env.LEXNODE_WEBHOOK_SIGNING_SECRET!,
|
|
180
|
+
);
|
|
181
|
+
|
|
182
|
+
// O status de cada recusa vem junto: um corpo que não se autentica é 401 e não
|
|
183
|
+
// volta; segredo ausente é 503, porque a falha é SUA e o motor precisa insistir.
|
|
184
|
+
if (!resultado.ok) {
|
|
185
|
+
return reply.status(webhookRejectionStatus(resultado.reason)).send();
|
|
186
|
+
}
|
|
187
|
+
// Tipo que o catálogo ainda não descreve: ignore e responda 2xx.
|
|
188
|
+
if (!resultado.known) return reply.status(200).send();
|
|
189
|
+
|
|
190
|
+
switch (resultado.event.type) {
|
|
191
|
+
case "PUBLICATION_CREATED":
|
|
192
|
+
resultado.event.payload.publicationKey;
|
|
193
|
+
resultado.event.payload.excerpt; // null quando o teor foi retido
|
|
194
|
+
break;
|
|
195
|
+
case "PROCESS_MOVEMENT_CREATED":
|
|
196
|
+
resultado.event.payload.externalRef;
|
|
197
|
+
break;
|
|
198
|
+
case "SEARCH_JOB_COMPLETED":
|
|
199
|
+
if (resultado.event.payload.status === "FAILED") {
|
|
200
|
+
resultado.event.payload.error.code;
|
|
201
|
+
}
|
|
202
|
+
break;
|
|
203
|
+
case "WEBHOOK_TEST":
|
|
204
|
+
// A entrega de teste do console. Chega a qualquer endpoint, não se assina
|
|
205
|
+
// e não se recusa — é ela que valida o receptor antes do primeiro evento.
|
|
206
|
+
break;
|
|
207
|
+
default:
|
|
208
|
+
break;
|
|
209
|
+
}
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
O envelope precisa ser endereçável: entrega sem `id` ou sem `occurredAt` é
|
|
213
|
+
recusada como `INVALID_ENVELOPE`. O `id` é a chave de idempotência de todo
|
|
214
|
+
receptor — aceitá-lo vazio faria a primeira entrega ocupar a única vaga de
|
|
215
|
+
deduplicação e todas as seguintes parecerem repetição dela.
|
|
216
|
+
|
|
217
|
+
`WEBHOOK_EVENT_TYPES` é o catálogo **assinável** (o que entra em `eventTypes`
|
|
218
|
+
ao cadastrar um endpoint) e não inclui `WEBHOOK_TEST`, que chega sem ser
|
|
219
|
+
pedido — use `isWebhookTest(event)` para separá-lo.
|
|
220
|
+
|
|
221
|
+
`verifyWebhookSignature(rawBody, headers, secret)` faz só a conferência e
|
|
222
|
+
devolve o motivo da recusa (`MISSING_SIGNATURE`, `MALFORMED_SIGNATURE`,
|
|
223
|
+
`TIMESTAMP_MISMATCH`, `TIMESTAMP_OUT_OF_TOLERANCE`, `SIGNATURE_MISMATCH`,
|
|
224
|
+
`RAW_BODY_REQUIRED`), em vez de um `false` que faria vários incidentes
|
|
225
|
+
diferentes virarem a mesma resposta.
|
|
226
|
+
|
|
227
|
+
O corpo entra como `string` ou `Buffer` — os bytes como chegaram. Passar o JSON
|
|
228
|
+
já parseado é recusado como `RAW_BODY_REQUIRED`, e não confundido com
|
|
229
|
+
adulteração: reserializar reordena as chaves e o digest deixa de bater.
|
|
230
|
+
|
|
231
|
+
A janela de replay e o digest saem do `t` que viaja **dentro** da assinatura; o
|
|
232
|
+
cabeçalho `x-lexnode-timestamp` é redundante e só é conferido quando vem. Um
|
|
233
|
+
receptor escrito à mão que o exigia continua correto — o SDK apenas não faz da
|
|
234
|
+
ausência dele um motivo de recusa, porque ela não prova nada.
|
|
235
|
+
|
|
236
|
+
## Busca síncrona ou assíncrona
|
|
237
|
+
|
|
238
|
+
`POST /v1/processes/search` responde **200** com o resultado pronto ou **202**
|
|
239
|
+
com um trabalho enfileirado. O SDK diz qual foi, no tipo:
|
|
240
|
+
|
|
241
|
+
```ts
|
|
242
|
+
import { searchProcesses, waitForSearchJob } from "@lexnode/legal-data-sdk";
|
|
243
|
+
|
|
244
|
+
const desfecho = await searchProcesses(lexnode, { tribunalAlias, number });
|
|
245
|
+
|
|
246
|
+
if (desfecho.kind === "sync") {
|
|
247
|
+
desfecho.result.items;
|
|
248
|
+
} else {
|
|
249
|
+
// O orçamento é explícito: num ambiente serverless, esperar sem teto gasta
|
|
250
|
+
// a requisição inteira. Quem integrou por webhook nem espera — recebe o
|
|
251
|
+
// SEARCH_JOB_COMPLETED.
|
|
252
|
+
const job = await waitForSearchJob(lexnode, desfecho.job.jobId, {
|
|
253
|
+
budgetMs: 20_000,
|
|
254
|
+
});
|
|
255
|
+
job.status; // "SUCCEEDED" | "FAILED" | "CANCELED"
|
|
256
|
+
}
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
## Ler um processo quando a fonte está lenta
|
|
260
|
+
|
|
261
|
+
A ficha (`processes.get`) vai à fonte processual quando o acervo não tem o
|
|
262
|
+
processo fresco; o retrato (`processes.snapshot`) vai sempre, e só recorre ao
|
|
263
|
+
acervo quando a fonte falha. A fonte às vezes leva perto de um minuto por
|
|
264
|
+
consulta. `waitSeconds` diz quanto o motor espera por ela: de 10 a 180
|
|
265
|
+
segundos, padrão 20 na API.
|
|
266
|
+
|
|
267
|
+
```ts
|
|
268
|
+
const { data, meta } = await lexnode.processes.get(numero, {
|
|
269
|
+
tribunalAlias: "api_publica_tjsp",
|
|
270
|
+
waitSeconds: 120,
|
|
271
|
+
});
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
**O prazo da chamada acompanha a espera.** Sem `timeoutMs` na chamada, o SDK
|
|
275
|
+
usa o maior entre o prazo do cliente e a espera mais 15 s: depois da fonte, a
|
|
276
|
+
ficha ainda espera até 10 s pelo resumo das publicações do processo, e o resto
|
|
277
|
+
é a rede. Com `waitSeconds: 120`, a chamada desiste aos 135 s, e não aos 30 s do
|
|
278
|
+
padrão; sem `waitSeconds`, o SDK conta a espera padrão da API, e a chamada
|
|
279
|
+
desiste aos 35 s. Um `timeoutMs` explícito sempre vence. O mesmo vale para a
|
|
280
|
+
chamada genérica (`request`) nessas duas rotas.
|
|
281
|
+
|
|
282
|
+
**O tempo esgotado não se repete sozinho.** `UPSTREAM_TIMEOUT` (504) nessas
|
|
283
|
+
duas leituras é lentidão da fonte, não ausência do processo, e a política de
|
|
284
|
+
retentativa não o repete: a espera já correu inteira, e repetir na hora a
|
|
285
|
+
dobraria diante da mesma fonte lenta. As outras recusas retentáveis seguem a
|
|
286
|
+
política. Para insistir, a decisão é sua — uma nova chamada com espera maior,
|
|
287
|
+
ou a vigília do processo (`watchProcess`), que consulta em segundo plano.
|
|
288
|
+
|
|
289
|
+
**`meta.partialIndex`: o processo veio de parte do índice.** Quando a fonte
|
|
290
|
+
responde com só parte do índice e o processo estava na parte que respondeu, ele
|
|
291
|
+
é servido, e `meta.partialIndex` diz quantas partes falharam (`shardsFailed`),
|
|
292
|
+
de quantas (`shardsTotal`, nulo quando a fonte não as contou) e se o prazo
|
|
293
|
+
interno dela estourou (`timedOut`). O processo servido é o que a fonte tem, mas
|
|
294
|
+
outra instância do mesmo número — outro grau — pode estar na parte que não
|
|
295
|
+
respondeu. A resposta parcial que não serve o processo — sem ele, ou com uma
|
|
296
|
+
instância diferente da que o acervo já guarda — é 502 `UPSTREAM_UNAVAILABLE`
|
|
297
|
+
retentável, nunca 404.
|
|
298
|
+
|
|
299
|
+
```ts
|
|
300
|
+
if (meta.partialIndex) {
|
|
301
|
+
// Outro grau deste número, se existir, pode não ter vindo.
|
|
302
|
+
}
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
**Agentes têm a espera deles.** No canal para agentes (MCP), as tools
|
|
306
|
+
`ler_processo` e `consultar_processo` escolhem a espera em `maxWaitSeconds`, na
|
|
307
|
+
mesma faixa e com padrão de 120 s; esse canal não passa pelo SDK.
|
|
308
|
+
|
|
309
|
+
## Frescor: o `total: 0` qualificado
|
|
310
|
+
|
|
311
|
+
Um resultado vazio não distingue sozinho "não existe" de "a fonte ainda não
|
|
312
|
+
indexou". O motor publica a data-limite do índice; o SDK a lê nos dois eixos
|
|
313
|
+
(processo e diário):
|
|
314
|
+
|
|
315
|
+
```ts
|
|
316
|
+
import {
|
|
317
|
+
describeFreshness,
|
|
318
|
+
isNotYetIndexed,
|
|
319
|
+
readThrough,
|
|
320
|
+
} from "@lexnode/legal-data-sdk";
|
|
321
|
+
|
|
322
|
+
const busca = await lexnode.processes.search({ tribunalAlias, number });
|
|
323
|
+
|
|
324
|
+
if (isNotYetIndexed(busca, { since: "2026-09-15" })) {
|
|
325
|
+
// Não diga "não existe": diga até quando a fonte foi lida.
|
|
326
|
+
mostrar(describeFreshness(busca)); // "fonte lida até 10/09/2026"
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
readThrough(busca); // Date | null — null é ESTADO indeterminado, não erro
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
## Vigília com vários interessados
|
|
333
|
+
|
|
334
|
+
O motor separa duas primitivas: o **sensor** (`Monitor`), único por alvo na
|
|
335
|
+
conta e com teto, e a **assinatura** (`ProcessSubscription`), única por
|
|
336
|
+
`externalRef`, sem teto e ecoada no aviso. N interessados no mesmo processo são
|
|
337
|
+
um sensor e N assinaturas.
|
|
338
|
+
|
|
339
|
+
```ts
|
|
340
|
+
import { unwatchProcess, watchProcess } from "@lexnode/legal-data-sdk";
|
|
341
|
+
|
|
342
|
+
const { monitorId, monitorAlreadyExisted, subscription } = await watchProcess(
|
|
343
|
+
lexnode,
|
|
344
|
+
{
|
|
345
|
+
processNumber: "00012345620268260100",
|
|
346
|
+
tribunalAlias: "api_publica_tjsp",
|
|
347
|
+
officeId: "usuario-4711",
|
|
348
|
+
externalRef: "caso-42",
|
|
349
|
+
},
|
|
350
|
+
);
|
|
351
|
+
|
|
352
|
+
// Um interessado desiste; o sensor continua varrendo para os demais.
|
|
353
|
+
await unwatchProcess(lexnode, {
|
|
354
|
+
externalRef: "caso-42",
|
|
355
|
+
officeId: "usuario-4711",
|
|
356
|
+
});
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
**Limitação conhecida:** uma assinatura criada **antes** de o processo entrar no
|
|
360
|
+
acervo não recebe `PROCESS_MOVEMENT_CREATED` até a primeira aparição do alvo na
|
|
361
|
+
fonte. O contorno é refazer a assinatura quando chegar o
|
|
362
|
+
`MONITOR_SYNC_SUCCEEDED` com `firstAppearance: true`:
|
|
363
|
+
|
|
364
|
+
```ts
|
|
365
|
+
import { resubscribeOnFirstAppearance } from "@lexnode/legal-data-sdk";
|
|
366
|
+
|
|
367
|
+
await resubscribeOnFirstAppearance(lexnode, evento, [
|
|
368
|
+
{ externalRef: "caso-42", officeId: "usuario-4711" },
|
|
369
|
+
]);
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
## Só os tipos
|
|
373
|
+
|
|
374
|
+
O uso antigo continua valendo — o pacote exporta `paths` e `operations` como
|
|
375
|
+
sempre:
|
|
376
|
+
|
|
377
|
+
```ts
|
|
378
|
+
import type { operations, paths } from "@lexnode/legal-data-sdk";
|
|
23
379
|
|
|
24
380
|
// Uma operação inteira, pela rota e pelo método:
|
|
25
381
|
type SearchProcesses = paths["/v1/processes/search"]["post"];
|
|
@@ -29,6 +385,24 @@ type Tribunals =
|
|
|
29
385
|
operations["listTribunals"]["responses"][200]["content"]["application/json"]["data"];
|
|
30
386
|
```
|
|
31
387
|
|
|
388
|
+
E os atalhos derivados deles, que o cliente usa internamente:
|
|
389
|
+
|
|
390
|
+
```ts
|
|
391
|
+
import type { Body, Data, Query } from "@lexnode/legal-data-sdk";
|
|
392
|
+
|
|
393
|
+
type CorpoDaBusca = Body<"/v1/processes/search", "post">;
|
|
394
|
+
type ConsultaDeProcesso = Query<"/v1/processes/{processNumber}", "get">;
|
|
395
|
+
type Tribunais = Data<"/v1/tribunals", "get">;
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
## A chave de teste
|
|
399
|
+
|
|
400
|
+
A chave `lapi_test_` lê o acervo já formado, mas **não consulta a fonte viva nem
|
|
401
|
+
cria trabalho de fundo**: monitor, vigília, colheita e busca assíncrona
|
|
402
|
+
respondem 403 `SANDBOX_UPSTREAM_DISABLED`. Na prática, `watchProcess` e o ramo
|
|
403
|
+
`async` de `searchProcesses` só fecham com chave viva — vale saber antes de
|
|
404
|
+
achar que a integração está errada.
|
|
405
|
+
|
|
32
406
|
## Referências
|
|
33
407
|
|
|
34
408
|
- Documentação: <https://www.lexnode.com.br/docs>
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import type { paths } from "../schema.js";
|
|
2
|
+
import type { Body, CallOptions, LexNodeResult, MethodOf, Query, RequestArgs, RetryPolicy } from "./types.js";
|
|
3
|
+
export interface LexNodeClientOptions {
|
|
4
|
+
/** Base da API, por exemplo `https://api.lexnode.com.br`. */
|
|
5
|
+
baseUrl: string;
|
|
6
|
+
/**
|
|
7
|
+
* Chave de CONTA (`Authorization: Bearer`). É credencial de servidor: ver o
|
|
8
|
+
* aviso de `createLexNodeClient`.
|
|
9
|
+
*/
|
|
10
|
+
apiKey: string;
|
|
11
|
+
/** Sub-tenant padrão (`X-Office-Id`). Cada chamada pode trocá-lo ou omiti-lo. */
|
|
12
|
+
officeId?: string;
|
|
13
|
+
/**
|
|
14
|
+
* Prazo de cada chamada, em ms. Padrão: 30 s. A ficha e o retrato de um
|
|
15
|
+
* processo levam o maior entre este prazo e a espera pela fonte mais 15 s —
|
|
16
|
+
* `waitSeconds`, ou o padrão de 20 s da API quando ele falta.
|
|
17
|
+
*/
|
|
18
|
+
timeoutMs?: number;
|
|
19
|
+
/** Transporte alternativo — o `fetch` global é o padrão. */
|
|
20
|
+
fetch?: typeof globalThis.fetch;
|
|
21
|
+
/** Identifica a sua integração no `User-Agent`. */
|
|
22
|
+
userAgent?: string;
|
|
23
|
+
/** Política de retentativa padrão. Ausente = desligada. */
|
|
24
|
+
retry?: RetryPolicy;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* O cliente de runtime do motor de dados jurídicos.
|
|
28
|
+
*
|
|
29
|
+
* **Servidor apenas, e só por TLS.** A credencial é de CONTA: uma chave no
|
|
30
|
+
* browser é uma chave publicada, e uma chave em `http://` é uma chave lida por
|
|
31
|
+
* quem estiver no caminho — `baseUrl` sem https é recusada aqui, exceto em
|
|
32
|
+
* host local. Um app com usuários finais fala com o próprio backend, e é o
|
|
33
|
+
* backend que fala com o motor — atribuindo cada chamada ao seu sub-tenant por
|
|
34
|
+
* `X-Office-Id`.
|
|
35
|
+
*
|
|
36
|
+
* Sem dependências de runtime: `fetch` global e `node:crypto`.
|
|
37
|
+
*/
|
|
38
|
+
export declare function createLexNodeClient(options: LexNodeClientOptions): {
|
|
39
|
+
request: <P extends keyof paths, M extends MethodOf<P>>(path: P, method: M, args: RequestArgs<P, M>) => Promise<LexNodeResult<P, M>>;
|
|
40
|
+
tribunals: {
|
|
41
|
+
list: (query?: Query<"/v1/tribunals", "get">, opts?: CallOptions) => Promise<LexNodeResult<"/v1/tribunals", "get">>;
|
|
42
|
+
};
|
|
43
|
+
processes: {
|
|
44
|
+
search: (body: Body<"/v1/processes/search", "post">, opts?: CallOptions) => Promise<LexNodeResult<"/v1/processes/search", "post">>;
|
|
45
|
+
get: (processNumber: string, query: Query<"/v1/processes/{processNumber}", "get">, opts?: CallOptions) => Promise<LexNodeResult<"/v1/processes/{processNumber}", "get">>;
|
|
46
|
+
movements: (processNumber: string, query: Query<"/v1/processes/{processNumber}/movements", "get">, opts?: CallOptions) => Promise<LexNodeResult<"/v1/processes/{processNumber}/movements", "get">>;
|
|
47
|
+
snapshot: (processNumber: string, query: Query<"/v1/processes/{processNumber}/snapshot", "get">, opts?: CallOptions) => Promise<LexNodeResult<"/v1/processes/{processNumber}/snapshot", "get">>;
|
|
48
|
+
related: (processNumber: string, query: Query<"/v1/processes/{processNumber}/related", "get">, opts?: CallOptions) => Promise<LexNodeResult<"/v1/processes/{processNumber}/related", "get">>;
|
|
49
|
+
snapshotBatch: (body: Body<"/v1/processes/snapshot-batch", "post">, opts?: CallOptions) => Promise<LexNodeResult<"/v1/processes/snapshot-batch", "post">>;
|
|
50
|
+
};
|
|
51
|
+
publications: {
|
|
52
|
+
list: (query?: Query<"/v1/publications", "get">, opts?: CallOptions) => Promise<LexNodeResult<"/v1/publications", "get">>;
|
|
53
|
+
read: (publicationKey: string, opts?: CallOptions) => Promise<LexNodeResult<"/v1/publications/{publicationKey}", "get">>;
|
|
54
|
+
textSearch: (body: Body<"/v1/publications/text-searches", "post">, opts?: CallOptions) => Promise<LexNodeResult<"/v1/publications/text-searches", "post">>;
|
|
55
|
+
textAlerts: {
|
|
56
|
+
create: (body: Body<"/v1/publications/text-alerts", "post">, opts?: CallOptions) => Promise<LexNodeResult<"/v1/publications/text-alerts", "post">>;
|
|
57
|
+
list: (query?: Query<"/v1/publications/text-alerts", "get">, opts?: CallOptions) => Promise<LexNodeResult<"/v1/publications/text-alerts", "get">>;
|
|
58
|
+
delete: (alertId: string, opts?: CallOptions) => Promise<LexNodeResult<"/v1/publications/text-alerts/{alertId}", "delete">>;
|
|
59
|
+
};
|
|
60
|
+
};
|
|
61
|
+
monitors: {
|
|
62
|
+
create: (body: Body<"/v1/monitors", "post">, opts?: CallOptions) => Promise<LexNodeResult<"/v1/monitors", "post">>;
|
|
63
|
+
list: (query?: Query<"/v1/monitors", "get">, opts?: CallOptions) => Promise<LexNodeResult<"/v1/monitors", "get">>;
|
|
64
|
+
get: (monitorId: string, opts?: CallOptions) => Promise<LexNodeResult<"/v1/monitors/{monitorId}", "get">>;
|
|
65
|
+
update: (monitorId: string, body: Body<"/v1/monitors/{monitorId}", "patch">, opts?: CallOptions) => Promise<LexNodeResult<"/v1/monitors/{monitorId}", "patch">>;
|
|
66
|
+
delete: (monitorId: string, opts?: CallOptions) => Promise<LexNodeResult<"/v1/monitors/{monitorId}", "delete">>;
|
|
67
|
+
sync: (monitorId: string, opts?: CallOptions) => Promise<LexNodeResult<"/v1/monitors/{monitorId}/sync", "post">>;
|
|
68
|
+
};
|
|
69
|
+
subscriptions: {
|
|
70
|
+
create: (body: Body<"/v1/subscriptions", "post">, opts?: CallOptions) => Promise<LexNodeResult<"/v1/subscriptions", "post">>;
|
|
71
|
+
list: (query?: Query<"/v1/subscriptions", "get">, opts?: CallOptions) => Promise<LexNodeResult<"/v1/subscriptions", "get">>;
|
|
72
|
+
delete: (body: Body<"/v1/subscriptions", "delete">, opts?: CallOptions) => Promise<LexNodeResult<"/v1/subscriptions", "delete">>;
|
|
73
|
+
};
|
|
74
|
+
searchJobs: {
|
|
75
|
+
get: (jobId: string, opts?: CallOptions) => Promise<LexNodeResult<"/v1/search-jobs/{jobId}", "get">>;
|
|
76
|
+
list: (query?: Query<"/v1/search-jobs", "get">, opts?: CallOptions) => Promise<LexNodeResult<"/v1/search-jobs", "get">>;
|
|
77
|
+
cancel: (jobId: string, opts?: CallOptions) => Promise<LexNodeResult<"/v1/search-jobs/{jobId}/cancel", "post">>;
|
|
78
|
+
};
|
|
79
|
+
events: {
|
|
80
|
+
list: (query?: Query<"/v1/events", "get">, opts?: CallOptions) => Promise<LexNodeResult<"/v1/events", "get">>;
|
|
81
|
+
get: (eventId: string, opts?: CallOptions) => Promise<LexNodeResult<"/v1/events/{eventId}", "get">>;
|
|
82
|
+
};
|
|
83
|
+
usage: {
|
|
84
|
+
get: (query?: Query<"/v1/usage", "get">, opts?: CallOptions) => Promise<LexNodeResult<"/v1/usage", "get">>;
|
|
85
|
+
};
|
|
86
|
+
webhooks: {
|
|
87
|
+
endpoints: {
|
|
88
|
+
list: (query?: Query<"/v1/webhooks/endpoints", "get">, opts?: CallOptions) => Promise<LexNodeResult<"/v1/webhooks/endpoints", "get">>;
|
|
89
|
+
create: (body: Body<"/v1/webhooks/endpoints", "post">, opts?: CallOptions) => Promise<LexNodeResult<"/v1/webhooks/endpoints", "post">>;
|
|
90
|
+
update: (endpointId: string, body: Body<"/v1/webhooks/endpoints/{endpointId}", "patch">, opts?: CallOptions) => Promise<LexNodeResult<"/v1/webhooks/endpoints/{endpointId}", "patch">>;
|
|
91
|
+
delete: (endpointId: string, opts?: CallOptions) => Promise<LexNodeResult<"/v1/webhooks/endpoints/{endpointId}", "delete">>;
|
|
92
|
+
test: (endpointId: string, opts?: CallOptions) => Promise<LexNodeResult<"/v1/webhooks/endpoints/{endpointId}/test", "post">>;
|
|
93
|
+
rotateSecret: (endpointId: string, opts?: CallOptions) => Promise<LexNodeResult<"/v1/webhooks/endpoints/{endpointId}/rotate-secret", "post">>;
|
|
94
|
+
};
|
|
95
|
+
deliveries: (query?: Query<"/v1/webhooks/deliveries", "get">, opts?: CallOptions) => Promise<LexNodeResult<"/v1/webhooks/deliveries", "get">>;
|
|
96
|
+
health: (opts?: CallOptions) => Promise<LexNodeResult<"/v1/webhooks/health", "get">>;
|
|
97
|
+
unsubscribedEventTypes: (opts?: CallOptions) => Promise<LexNodeResult<"/v1/webhooks/unsubscribed-event-types", "get">>;
|
|
98
|
+
};
|
|
99
|
+
};
|
|
100
|
+
export type LexNodeClient = ReturnType<typeof createLexNodeClient>;
|