@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/README.md
CHANGED
|
@@ -5,20 +5,20 @@ processos e movimentações, publicações dos diários oficiais, jurisprudênci
|
|
|
5
5
|
monitores e webhooks.
|
|
6
6
|
|
|
7
7
|
O pacote traz **os tipos gerados do contrato OpenAPI** e um **cliente de
|
|
8
|
-
runtime** construído sobre eles:
|
|
8
|
+
runtime** construído sobre eles: chamadas com os cabeçalhos certos, erros
|
|
9
9
|
classificados, verificação de assinatura de webhook, espera de busca
|
|
10
|
-
assíncrona, leitura do
|
|
10
|
+
assíncrona, leitura do frescor da fonte e a vigília de um processo para vários
|
|
11
11
|
interessados.
|
|
12
12
|
|
|
13
13
|
- **Sem dependências de runtime.** `fetch` global e `node:crypto`.
|
|
14
14
|
- **Node >= 20**, ESM.
|
|
15
15
|
|
|
16
|
-
> ⚠️ **
|
|
17
|
-
> no browser ela
|
|
18
|
-
>
|
|
19
|
-
>
|
|
20
|
-
> backend
|
|
21
|
-
>
|
|
16
|
+
> ⚠️ **Só no servidor, e só por TLS.** A credencial é uma chave de **conta**:
|
|
17
|
+
> no browser ela fica exposta, e em `http://` qualquer um no caminho a lê —
|
|
18
|
+
> uma `baseUrl` sem https é recusada no construtor (exceto no host local, para
|
|
19
|
+
> desenvolvimento). Um app com usuários finais chama o próprio backend, e o
|
|
20
|
+
> backend chama a API, atribuindo cada chamada ao sub-tenant dela (um
|
|
21
|
+
> escritório, um usuário final) pelo cabeçalho `X-Office-Id`.
|
|
22
22
|
|
|
23
23
|
## Instalação
|
|
24
24
|
|
|
@@ -34,33 +34,33 @@ import { createLexNodeClient } from "@lexnode/legal-data-sdk";
|
|
|
34
34
|
const lexnode = createLexNodeClient({
|
|
35
35
|
baseUrl: "https://api.lexnode.com.br",
|
|
36
36
|
apiKey: process.env.LEXNODE_API_KEY!,
|
|
37
|
-
//
|
|
37
|
+
// Escritório padrão; cada chamada pode trocá-lo.
|
|
38
38
|
officeId: "escritorio-01",
|
|
39
39
|
timeoutMs: 30_000,
|
|
40
40
|
userAgent: "meu-app/1.0",
|
|
41
41
|
});
|
|
42
42
|
|
|
43
43
|
const { data, meta } = await lexnode.processes.get("00012345620268260100", {
|
|
44
|
-
tribunalAlias: "
|
|
44
|
+
tribunalAlias: "TJSP",
|
|
45
45
|
});
|
|
46
46
|
|
|
47
47
|
console.log(data.formattedNumber, meta.credits, meta.rateLimit?.remaining);
|
|
48
48
|
```
|
|
49
49
|
|
|
50
|
-
Toda chamada
|
|
51
|
-
quando
|
|
52
|
-
|
|
53
|
-
é o do contrato mais `status` (o código HTTP) e `rateLimit` (os
|
|
54
|
-
`RateLimit-*`, lidos **
|
|
50
|
+
Toda chamada envia `Authorization: Bearer`, `X-Request-Id`, `X-Trace-Id` e,
|
|
51
|
+
quando houver, `X-Office-Id`; respeita o prazo **e** o `signal` que você
|
|
52
|
+
passar, o que vier primeiro; e devolve `{ data, meta }` tipados pela rota. O
|
|
53
|
+
`meta` é o do contrato mais `status` (o código HTTP) e `rateLimit` (os
|
|
54
|
+
cabeçalhos `RateLimit-*`, lidos **também no sucesso**, e o `Retry-After` da
|
|
55
|
+
recusa).
|
|
55
56
|
|
|
56
|
-
Em `rateLimit`, use `remaining` para desacelerar
|
|
57
|
-
cabeçalho `RateLimit-Reset
|
|
58
|
-
|
|
57
|
+
Em `rateLimit`, use `remaining` para desacelerar. `resetSeconds` repete o
|
|
58
|
+
cabeçalho `RateLimit-Reset`, que tem dois sentidos: na recusa, os segundos até
|
|
59
|
+
a janela reabrir; no sucesso, a duração da janela (60 s).
|
|
59
60
|
|
|
60
61
|
O pacote **não valida a resposta em tempo de execução**: `data` e `meta` chegam
|
|
61
|
-
tipados pelo contrato
|
|
62
|
-
|
|
63
|
-
dado alimenta um banco, por exemplo — valida o que consome.
|
|
62
|
+
tipados pelo contrato da versão instalada. Se o dado alimenta um banco, por
|
|
63
|
+
exemplo, valide o que consumir.
|
|
64
64
|
|
|
65
65
|
### Métodos nomeados e a chamada genérica
|
|
66
66
|
|
|
@@ -80,8 +80,8 @@ await lexnode.webhooks.unsubscribedEventTypes();
|
|
|
80
80
|
await lexnode.usage.get();
|
|
81
81
|
```
|
|
82
82
|
|
|
83
|
-
Qualquer rota do contrato
|
|
84
|
-
|
|
83
|
+
Qualquer rota do contrato, inclusive as que não têm método nomeado, sai pela
|
|
84
|
+
chamada genérica, tipada por `paths`:
|
|
85
85
|
|
|
86
86
|
```ts
|
|
87
87
|
const { data } = await lexnode.request("/v1/precedents", "get", {
|
|
@@ -91,9 +91,9 @@ const { data } = await lexnode.request("/v1/precedents", "get", {
|
|
|
91
91
|
|
|
92
92
|
### Páginas
|
|
93
93
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
94
|
+
A API pagina de três jeitos: cursor no `meta`, cursor no `data`, e página e
|
|
95
|
+
tamanho ou deslocamento e limite. `paginate` percorre qualquer um deles, até
|
|
96
|
+
`maxPages` páginas (padrão 100) — cada página é uma chamada:
|
|
97
97
|
|
|
98
98
|
```ts
|
|
99
99
|
import { paginate } from "@lexnode/legal-data-sdk";
|
|
@@ -116,7 +116,7 @@ try {
|
|
|
116
116
|
error.class; // "RETRYABLE" | "TERMINAL"
|
|
117
117
|
error.retryable;
|
|
118
118
|
error.status;
|
|
119
|
-
error.upstreamStatus; // o status
|
|
119
|
+
error.upstreamStatus; // o status que a fonte respondeu, quando informado
|
|
120
120
|
error.requestId; // o que o suporte pede
|
|
121
121
|
error.fieldErrors; // recusa de validação, por campo
|
|
122
122
|
error.retryAfterSeconds;
|
|
@@ -126,17 +126,17 @@ try {
|
|
|
126
126
|
```
|
|
127
127
|
|
|
128
128
|
Prazo esgotado vira `CLIENT_TIMEOUT` (504, retentável); falha de rede vira
|
|
129
|
-
`CLIENT_NETWORK_ERROR` (502, retentável); resposta fora do
|
|
130
|
-
`INVALID_RESPONSE` (502) com o
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
129
|
+
`CLIENT_NETWORK_ERROR` (502, retentável); resposta fora do formato da API vira
|
|
130
|
+
`INVALID_RESPONSE` (502), com o início do corpo na mensagem; e o pedido que não
|
|
131
|
+
se monta — cabeçalho com quebra de linha, corpo que não vira JSON — vira
|
|
132
|
+
`CLIENT_INVALID_REQUEST` (400, **não** retentável), porque não chegou a ser
|
|
133
|
+
enviado.
|
|
134
134
|
|
|
135
135
|
`LEXNODE_ERROR_CODES` traz o catálogo para o `switch`, e `LexNodeErrorCode`
|
|
136
|
-
mantém o autocompletar
|
|
137
|
-
|
|
138
|
-
existe na fonte) quanto para `MOVEMENTS_NOT_AVAILABLE` (existe, mas as
|
|
139
|
-
movimentações não
|
|
136
|
+
mantém o autocompletar e aceita um código novo da API. Na leitura de processo
|
|
137
|
+
isso importa: a API responde 404 tanto para `PROCESS_NOT_FOUND` (o processo não
|
|
138
|
+
existe na fonte) quanto para `MOVEMENTS_NOT_AVAILABLE` (ele existe, mas as
|
|
139
|
+
movimentações não vieram) — trate os dois pelo `code`.
|
|
140
140
|
|
|
141
141
|
### Retentativa (opcional, desligada por padrão)
|
|
142
142
|
|
|
@@ -148,19 +148,22 @@ const lexnode = createLexNodeClient({
|
|
|
148
148
|
});
|
|
149
149
|
```
|
|
150
150
|
|
|
151
|
-
Repete só o que é `RETRYABLE`, respeitando `Retry-After
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
declara
|
|
151
|
+
Repete só o que é `RETRYABLE`, respeitando `Retry-After`: quando a recusa pede
|
|
152
|
+
uma espera maior que `maxDelayMs` (8 s por padrão), como a da manutenção
|
|
153
|
+
declarada pela fonte, ela é lançada sem repetição, e `retryAfterSeconds` diz
|
|
154
|
+
quando vale tentar de novo. `GET` e `DELETE` são repetidos por definição do
|
|
155
|
+
HTTP. Uma **escrita** só é repetida quando o contrato declara a repetição
|
|
156
|
+
segura — hoje `POST /v1/publications/text-searches`, que usa o `requestId` que
|
|
157
|
+
você fixar como chave de reenvio — ou quando você mesmo o declara na chamada:
|
|
156
158
|
|
|
157
159
|
```ts
|
|
158
160
|
await lexnode.webhooks.endpoints.create(corpo, { idempotent: true });
|
|
159
161
|
```
|
|
160
162
|
|
|
161
|
-
A
|
|
162
|
-
endpoints para a mesma URL entregam cada evento em dobro, e o segredo do
|
|
163
|
-
primeiro
|
|
163
|
+
A declaração é sua: sem ela, um `POST` repetido cria um **segundo** recurso —
|
|
164
|
+
dois endpoints para a mesma URL entregam cada evento em dobro, e o segredo do
|
|
165
|
+
primeiro se perde. Nas outras rotas, o `requestId` só serve para rastrear a
|
|
166
|
+
chamada.
|
|
164
167
|
|
|
165
168
|
A exceção é o `UPSTREAM_TIMEOUT` da leitura de um processo, que a política não
|
|
166
169
|
repete — ver [a seção da fonte lenta](#ler-um-processo-quando-a-fonte-está-lenta).
|
|
@@ -173,15 +176,16 @@ import {
|
|
|
173
176
|
webhookRejectionStatus,
|
|
174
177
|
} from "@lexnode/legal-data-sdk";
|
|
175
178
|
|
|
176
|
-
// O corpo
|
|
179
|
+
// O corpo como chegou, nunca um JSON serializado de novo: a assinatura vale
|
|
180
|
+
// para os bytes.
|
|
177
181
|
const resultado = parseWebhookEvent(
|
|
178
182
|
rawBody,
|
|
179
183
|
request.headers,
|
|
180
184
|
process.env.LEXNODE_WEBHOOK_SIGNING_SECRET!,
|
|
181
185
|
);
|
|
182
186
|
|
|
183
|
-
//
|
|
184
|
-
//
|
|
187
|
+
// Cada recusa tem o seu status: a assinatura que não confere é 401; o segredo
|
|
188
|
+
// ausente no seu receptor é 503.
|
|
185
189
|
if (!resultado.ok) {
|
|
186
190
|
return reply.status(webhookRejectionStatus(resultado.reason)).send();
|
|
187
191
|
}
|
|
@@ -210,22 +214,22 @@ switch (resultado.event.type) {
|
|
|
210
214
|
}
|
|
211
215
|
break;
|
|
212
216
|
case "PROCESS_LOOKUP_COMPLETED":
|
|
213
|
-
//
|
|
217
|
+
// Aponta a consulta: o processo se lê em payload.pollUrl, sem nova cobrança.
|
|
214
218
|
resultado.event.payload.lookupId;
|
|
215
219
|
break;
|
|
216
220
|
case "WEBHOOK_TEST":
|
|
217
|
-
// A entrega de teste
|
|
218
|
-
//
|
|
221
|
+
// A entrega de teste (`webhooks.endpoints.test` ou o portal). Chega a
|
|
222
|
+
// qualquer endpoint sem constar de `eventTypes`; use-a para validar o
|
|
223
|
+
// receptor antes do primeiro evento.
|
|
219
224
|
break;
|
|
220
225
|
default:
|
|
221
226
|
break;
|
|
222
227
|
}
|
|
223
228
|
```
|
|
224
229
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
deduplicação e todas as seguintes parecerem repetição dela.
|
|
230
|
+
Entrega sem `id` ou sem `occurredAt` é recusada como `INVALID_ENVELOPE`. A
|
|
231
|
+
mesma entrega pode chegar mais de uma vez: use o `id` para descartar a
|
|
232
|
+
repetida.
|
|
229
233
|
|
|
230
234
|
`WEBHOOK_EVENT_TYPES` é o catálogo **assinável** (o que entra em `eventTypes`
|
|
231
235
|
ao cadastrar um endpoint) e não inclui `WEBHOOK_TEST`, que chega sem ser
|
|
@@ -234,22 +238,20 @@ pedido — use `isWebhookTest(event)` para separá-lo.
|
|
|
234
238
|
`verifyWebhookSignature(rawBody, headers, secret)` faz só a conferência e
|
|
235
239
|
devolve o motivo da recusa (`MISSING_SIGNATURE`, `MALFORMED_SIGNATURE`,
|
|
236
240
|
`TIMESTAMP_MISMATCH`, `TIMESTAMP_OUT_OF_TOLERANCE`, `SIGNATURE_MISMATCH`,
|
|
237
|
-
`RAW_BODY_REQUIRED`),
|
|
238
|
-
diferentes virarem a mesma resposta.
|
|
241
|
+
`RAW_BODY_REQUIRED`), para você tratar cada caso.
|
|
239
242
|
|
|
240
|
-
O corpo entra como `string` ou `Buffer
|
|
241
|
-
|
|
242
|
-
|
|
243
|
+
O corpo entra como `string` ou `Buffer`, como chegou. Um objeto já convertido
|
|
244
|
+
de JSON é recusado como `RAW_BODY_REQUIRED`: serializar de novo muda a ordem
|
|
245
|
+
das chaves, e a assinatura deixa de conferir.
|
|
243
246
|
|
|
244
|
-
A
|
|
245
|
-
cabeçalho `x-lexnode-timestamp`
|
|
246
|
-
|
|
247
|
-
ausência dele um motivo de recusa, porque ela não prova nada.
|
|
247
|
+
A tolerância do instante e o digest usam o `t` que vem **dentro** da
|
|
248
|
+
assinatura. O cabeçalho `x-lexnode-timestamp` repete esse valor: quando vem,
|
|
249
|
+
precisa ser igual; quando falta, não é motivo de recusa.
|
|
248
250
|
|
|
249
251
|
## Busca síncrona ou assíncrona
|
|
250
252
|
|
|
251
253
|
`POST /v1/processes/search` responde **200** com o resultado pronto ou **202**
|
|
252
|
-
com
|
|
254
|
+
com uma busca enfileirada. O SDK diz qual foi, no tipo:
|
|
253
255
|
|
|
254
256
|
```ts
|
|
255
257
|
import { searchProcesses, waitForSearchJob } from "@lexnode/legal-data-sdk";
|
|
@@ -259,9 +261,8 @@ const desfecho = await searchProcesses(lexnode, { tribunalAlias, number });
|
|
|
259
261
|
if (desfecho.kind === "sync") {
|
|
260
262
|
desfecho.result.items;
|
|
261
263
|
} else {
|
|
262
|
-
//
|
|
263
|
-
//
|
|
264
|
-
// SEARCH_JOB_COMPLETED.
|
|
264
|
+
// Espere com orçamento — num ambiente serverless, dentro do prazo da
|
|
265
|
+
// função — ou receba o SEARCH_JOB_COMPLETED por webhook.
|
|
265
266
|
const job = await waitForSearchJob(lexnode, desfecho.job.jobId, {
|
|
266
267
|
budgetMs: 20_000,
|
|
267
268
|
});
|
|
@@ -272,30 +273,30 @@ if (desfecho.kind === "sync") {
|
|
|
272
273
|
## Um número, várias instâncias
|
|
273
274
|
|
|
274
275
|
A fonte processual guarda um documento por instância do processo — 1º grau, 2º
|
|
275
|
-
grau, turma recursal… —, às vezes dois do mesmo grau, e o
|
|
276
|
-
na sua instância.
|
|
276
|
+
grau, turma recursal… —, às vezes dois do mesmo grau, e o LexNode guarda cada
|
|
277
|
+
um na sua instância.
|
|
277
278
|
|
|
278
279
|
**O topo não troca.** O topo da ficha (`data`) e do retrato (`data.process` e
|
|
279
280
|
`data.movements`) é sempre a instância principal do número, com o grau em
|
|
280
281
|
`degree`: a mesma de uma leitura para a outra, qualquer que seja a ordem em que
|
|
281
282
|
a fonte devolve os documentos. Ela só muda quando uma resposta completa da
|
|
282
|
-
fonte pelo número deixa de trazê-la.
|
|
283
|
-
|
|
283
|
+
fonte pelo número deixa de trazê-la. Se você compara movimentos pela chave, o
|
|
284
|
+
histórico de outro grau não aparece como novo no topo — e o topo pode não ser o
|
|
284
285
|
grau que anda: o 2º grau guardado primeiro continua no topo enquanto o 1º se
|
|
285
286
|
move.
|
|
286
287
|
|
|
287
288
|
**Todos os graus em `instances`.** A ficha e o retrato listam em `instances`
|
|
288
|
-
todos os documentos
|
|
289
|
+
todos os documentos guardados do número, a principal primeiro
|
|
289
290
|
(`primary: true`). Cada um diz de onde veio nesta resposta (`origin`: `SOURCE`
|
|
290
|
-
quando a fonte o trouxe, `LAKE` quando
|
|
291
|
-
(`fetchedAt`) e desde quando a resposta completa da fonte vem sem ele
|
|
292
|
-
(`absentSince`). No retrato, cada instância traz os movimentos da última
|
|
293
|
-
dela, do mais recente ao mais antigo e sem o payload bruto da fonte; a
|
|
294
|
-
|
|
295
|
-
com lista vazia, que diria
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
lista.
|
|
291
|
+
quando a fonte o trouxe, `LAKE` quando veio da cópia guardada), quando foi
|
|
292
|
+
buscado (`fetchedAt`) e desde quando a resposta completa da fonte vem sem ele
|
|
293
|
+
(`absentSince`). No retrato, cada instância traz os movimentos da última
|
|
294
|
+
captura dela, do mais recente ao mais antigo e sem o payload bruto da fonte; a
|
|
295
|
+
instância cujos movimentos nunca foram capturados vem com
|
|
296
|
+
`movementsCaptured: false` e `movements` nulo, nunca com lista vazia, que diria
|
|
297
|
+
que o grau não andou. Todos os documentos levam a classificação do número
|
|
298
|
+
inteiro — basta um em segredo de justiça para todos saírem marcados —, e a
|
|
299
|
+
ficha retida pelo controle de confidencialidade sai sem a lista.
|
|
299
300
|
|
|
300
301
|
```ts
|
|
301
302
|
const { data } = await lexnode.processes.snapshot(numero, { tribunalAlias });
|
|
@@ -309,9 +310,9 @@ for (const instancia of data.instances) {
|
|
|
309
310
|
|
|
310
311
|
No lote (`processes.snapshotBatch`), `instances` traz só o cabeçalho de cada
|
|
311
312
|
documento; os movimentos de cada instância vêm no retrato de um processo. A
|
|
312
|
-
busca por número completo
|
|
313
|
-
fonte ainda traz do número, a principal primeiro; quando eles
|
|
314
|
-
`limit`, a busca vai à fonte, que pagina.
|
|
313
|
+
busca por número completo respondida com os dados guardados devolve um item por
|
|
314
|
+
documento que a fonte ainda traz do número, a principal primeiro; quando eles
|
|
315
|
+
não cabem em `limit`, a busca vai à fonte, que pagina.
|
|
315
316
|
|
|
316
317
|
**Recorte por grau.** A lista de movimentações aceita `degree` e devolve só os
|
|
317
318
|
movimentos que o documento daquele grau trouxe na última captura; cada item diz
|
|
@@ -327,21 +328,21 @@ data.items; // só o que o 1º grau trouxe
|
|
|
327
328
|
```
|
|
328
329
|
|
|
329
330
|
**O grau novo chega num evento só.** Quando outro grau de um processo assinado
|
|
330
|
-
aparece na fonte, o histórico dele
|
|
331
|
-
`PROCESS_MOVEMENT_CREATED
|
|
332
|
-
|
|
333
|
-
`
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
331
|
+
aparece na fonte, o histórico dele não sai movimento a movimento em
|
|
332
|
+
`PROCESS_MOVEMENT_CREATED`: chega um `PROCESS_INSTANCE_DISCOVERED`, com o grau,
|
|
333
|
+
o órgão julgador e as contagens — `historyMovementCount` não saiu em aviso
|
|
334
|
+
próprio, `announcedMovementCount` saiu também como movimento novo. O
|
|
335
|
+
`PROCESS_MOVEMENT_CREATED` diz o grau do movimento mais recente (`degree`,
|
|
336
|
+
`instanceSourceId`) e quantos movimentos novos vieram de cada grau
|
|
337
|
+
(`instances`). Assine os dois: só com o movimento, você não fica sabendo do
|
|
338
|
+
grau que apareceu.
|
|
338
339
|
|
|
339
340
|
**`meta.otherInstances`: o resumo do que veio.** Quando a resposta da fonte traz
|
|
340
341
|
outros documentos do número além da principal, `meta.otherInstances` resume cada
|
|
341
342
|
um com o grau (`grau`), a data em que a fonte o atualizou (`updatedAt`) e o
|
|
342
343
|
órgão julgador (`courtBody`, quando vem). A lista não sai para processo restrito
|
|
343
|
-
e não inclui documento
|
|
344
|
-
|
|
344
|
+
e não inclui documento em segredo de justiça; os documentos inteiros estão em
|
|
345
|
+
`instances`.
|
|
345
346
|
|
|
346
347
|
```ts
|
|
347
348
|
for (const outra of meta.otherInstances ?? []) {
|
|
@@ -350,24 +351,24 @@ for (const outra of meta.otherInstances ?? []) {
|
|
|
350
351
|
```
|
|
351
352
|
|
|
352
353
|
**Continuação.** Na API nada é cortado: o retrato traz os movimentos guardados
|
|
353
|
-
de cada instância, e a lista de movimentações lê
|
|
354
|
-
à fonte. No canal para agentes (MCP),
|
|
355
|
-
resposta: de cada grau vêm os movimentos
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
ou o acompanha.
|
|
354
|
+
de cada instância, e a lista de movimentações lê os movimentos guardados, grau a
|
|
355
|
+
grau, sem ir à fonte. No canal para agentes (MCP), que não passa por este
|
|
356
|
+
pacote, `consultar_processo` cabe numa resposta: de cada grau vêm os movimentos
|
|
357
|
+
mais recentes, o grau que se moveu por último primeiro, e a mesma tool com
|
|
358
|
+
`grau` e `pagina` traz os seguintes, por 1 crédito e sem ir à fonte — só para
|
|
359
|
+
quem já leu o processo ou o acompanha.
|
|
359
360
|
|
|
360
361
|
## Ler um processo quando a fonte está lenta
|
|
361
362
|
|
|
362
|
-
A ficha (`processes.get`) vai à fonte processual quando
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
perto de um minuto por consulta. `waitSeconds` diz quanto
|
|
366
|
-
|
|
363
|
+
A ficha (`processes.get`) vai à fonte processual quando não há uma cópia recente
|
|
364
|
+
guardada; o retrato (`processes.snapshot`) vai sempre. Os dois só respondem com
|
|
365
|
+
a cópia guardada quando a fonte falha (seção seguinte). A fonte às vezes leva
|
|
366
|
+
perto de um minuto por consulta. `waitSeconds` diz quanto a API espera por ela:
|
|
367
|
+
de 10 a 180 segundos, padrão 20.
|
|
367
368
|
|
|
368
369
|
```ts
|
|
369
370
|
const { data, meta } = await lexnode.processes.get(numero, {
|
|
370
|
-
tribunalAlias: "
|
|
371
|
+
tribunalAlias: "TJSP",
|
|
371
372
|
waitSeconds: 120,
|
|
372
373
|
});
|
|
373
374
|
```
|
|
@@ -384,27 +385,27 @@ chamada genérica (`request`) nessas duas rotas.
|
|
|
384
385
|
duas leituras é lentidão da fonte, não ausência do processo, e a política de
|
|
385
386
|
retentativa não o repete: a espera já correu inteira, e repetir na hora a
|
|
386
387
|
dobraria diante da mesma fonte lenta. As outras recusas retentáveis seguem a
|
|
387
|
-
política. Para insistir, a decisão é sua — uma nova chamada com espera maior,
|
|
388
|
-
|
|
388
|
+
política. Para insistir, a decisão é sua — uma nova chamada com espera maior, a
|
|
389
|
+
leitura que segue em segundo plano (`readProcessWithRecovery`, abaixo) ou a
|
|
390
|
+
vigília do processo (`watchProcess`).
|
|
389
391
|
|
|
390
392
|
**`meta.partialIndex`: o processo veio de parte do índice.** Quando a fonte
|
|
391
393
|
responde com só parte do índice e o processo estava na parte que respondeu, ele
|
|
392
394
|
é servido, e `meta.partialIndex` diz quantas partes falharam (`shardsFailed`),
|
|
393
395
|
de quantas (`shardsTotal`, nulo quando a fonte não as contou) e se o prazo
|
|
394
|
-
interno dela estourou (`timedOut`). O documento
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
retentável, nunca 404.
|
|
396
|
+
interno dela estourou (`timedOut`). O documento já guardado que não veio sai em
|
|
397
|
+
`instances` com `origin: "LAKE"` e a data dele, e outro grau ainda não guardado,
|
|
398
|
+
se existir, pode estar na parte que não respondeu. A resposta parcial sem o
|
|
399
|
+
processo é 502 `UPSTREAM_UNAVAILABLE` retentável, nunca 404.
|
|
399
400
|
|
|
400
401
|
```ts
|
|
401
402
|
if (meta.partialIndex) {
|
|
402
|
-
//
|
|
403
|
+
// Um grau deste número ainda não guardado pode não ter vindo.
|
|
403
404
|
}
|
|
404
405
|
```
|
|
405
406
|
|
|
406
407
|
Antes de recusar a resposta parcial que veio sem o processo porque partes do
|
|
407
|
-
índice ficaram fora,
|
|
408
|
+
índice ficaram fora, a API consulta a fonte de novo — até duas vezes, com uma
|
|
408
409
|
pausa curta, enquanto a espera couber.
|
|
409
410
|
|
|
410
411
|
**Agentes têm a espera deles.** No canal para agentes (MCP), as tools
|
|
@@ -415,7 +416,7 @@ mesma faixa e com padrão de 120 s; esse canal não passa pelo SDK.
|
|
|
415
416
|
|
|
416
417
|
Quando a fonte processual falha — não responde dentro de `waitSeconds`, fica
|
|
417
418
|
indisponível, responde com parte do índice sem poder servir o processo ou pede
|
|
418
|
-
para esperar por volume — e
|
|
419
|
+
para esperar por volume — e há uma cópia guardada do processo, a ficha
|
|
419
420
|
(`processes.get`) e o retrato (`processes.snapshot`) respondem com essa cópia em
|
|
420
421
|
vez do erro. A resposta diz que é cópia, e de quando:
|
|
421
422
|
|
|
@@ -423,38 +424,39 @@ vez do erro. A resposta diz que é cópia, e de quando:
|
|
|
423
424
|
`UPSTREAM_UNAVAILABLE` ou `UPSTREAM_RATE_LIMITED`;
|
|
424
425
|
- `meta.lakeRescue.retryAfterSeconds`, repetido no header `Retry-After`, diz na
|
|
425
426
|
recusa por volume quanto esperar antes de consultar a fonte de novo. É a
|
|
426
|
-
espera
|
|
427
|
-
fica `null` no sucesso;
|
|
427
|
+
espera pedida pela fonte, não pelo limite de vazão da API:
|
|
428
|
+
`meta.rateLimit.retryAfterSeconds` fica `null` no sucesso;
|
|
428
429
|
- `meta.sourceFetchedAt` diz quando a cópia foi buscada na fonte. Esse campo
|
|
429
|
-
acompanha toda resposta das duas leituras,
|
|
430
|
-
que o controle de confidencialidade entrega retida, sem o teor.
|
|
430
|
+
acompanha toda resposta das duas leituras, venha ela da fonte ou da cópia,
|
|
431
|
+
menos a ficha que o controle de confidencialidade entrega retida, sem o teor.
|
|
431
432
|
|
|
432
433
|
A cópia custa 1 crédito, na ficha e no retrato. O retrato confere o saldo pelo
|
|
433
434
|
preço cheio, 8, antes de ir à fonte; a ficha confere pelo preço do que
|
|
434
435
|
entregou, depois da leitura. No retrato guardado, cada instância traz os
|
|
435
|
-
movimentos da última vez que
|
|
436
|
+
movimentos da última vez que ela foi buscada, com a data em
|
|
436
437
|
`instances[].fetchedAt`, e `sourcePayload` é nulo: o que andou depois dessas
|
|
437
438
|
datas não está na resposta. O número que a resposta completa mais recente da
|
|
438
|
-
fonte já não trazia em documento nenhum não
|
|
439
|
-
data vale para os dados do processo, e o resumo das publicações
|
|
440
|
-
relido na hora.
|
|
439
|
+
fonte já não trazia em documento nenhum não tem cópia servida. Na ficha
|
|
440
|
+
guardada, a data vale para os dados do processo, e o resumo das publicações
|
|
441
|
+
(`djen`) é relido na hora.
|
|
441
442
|
|
|
442
|
-
**Até que idade.** Sem pedir nada, só a cópia de até 24 h
|
|
443
|
-
nessas duas rotas, uma resposta de sucesso já
|
|
444
|
-
desse prazo. A cópia de qualquer idade vem com
|
|
443
|
+
**Até que idade.** Sem pedir nada, só a cópia de até 24 h responde na falha da
|
|
444
|
+
fonte — nessas duas rotas, uma resposta de sucesso já pode ser uma cópia
|
|
445
|
+
guardada dentro desse prazo. A cópia de qualquer idade vem com
|
|
446
|
+
`staleOnFailure: true`:
|
|
445
447
|
|
|
446
448
|
```ts
|
|
447
449
|
import { describeFreshness, lakeRescueOf } from "@lexnode/legal-data-sdk";
|
|
448
450
|
|
|
449
451
|
const retrato = await lexnode.processes.snapshot(numero, {
|
|
450
|
-
tribunalAlias: "
|
|
452
|
+
tribunalAlias: "TJSP",
|
|
451
453
|
staleOnFailure: true,
|
|
452
454
|
});
|
|
453
455
|
|
|
454
|
-
const
|
|
455
|
-
if (
|
|
456
|
+
const copia = lakeRescueOf(retrato);
|
|
457
|
+
if (copia) {
|
|
456
458
|
// A fonte falhou e isto é a cópia guardada: o que andou depois de
|
|
457
|
-
//
|
|
459
|
+
// copia.sourceFetchedAt não está aqui.
|
|
458
460
|
mostrar(describeFreshness(retrato));
|
|
459
461
|
// "cópia guardada em 26/09/2026, servida porque a fonte não respondeu a tempo"
|
|
460
462
|
}
|
|
@@ -465,16 +467,17 @@ if (socorro) {
|
|
|
465
467
|
devolve a data de qualquer resposta dessas leituras. As duas aceitam o envelope
|
|
466
468
|
inteiro e o item do lote.
|
|
467
469
|
|
|
468
|
-
**
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
confidencialidade da leitura; além de 24 h, só a recebe
|
|
472
|
-
registrado no processo — de uma leitura anterior dele ou de uma
|
|
473
|
-
ativa —, e
|
|
470
|
+
**Quando a cópia não é servida.** A resposta da fonte que não traz o processo segue 404
|
|
471
|
+
`PROCESS_NOT_FOUND`, com cópia guardada ou sem ela: é a fonte dizendo que não
|
|
472
|
+
tem o número, e não uma falha dela. A cópia passa pelo mesmo controle de
|
|
473
|
+
confidencialidade da leitura; além de 24 h, só a recebe a conta que já tem
|
|
474
|
+
interesse registrado no processo — de uma leitura anterior dele ou de uma
|
|
475
|
+
assinatura ativa —, e as demais recebem a falha da fonte, como se não houvesse
|
|
476
|
+
cópia.
|
|
474
477
|
|
|
475
|
-
**No lote.** `processes.snapshotBatch`
|
|
476
|
-
|
|
477
|
-
de 8.
|
|
478
|
+
**No lote.** `processes.snapshotBatch` só usa a cópia de até 24 h. O item
|
|
479
|
+
respondido com ela traz `lakeRescue`, com a causa e a data, e custa 1 crédito
|
|
480
|
+
em vez de 8.
|
|
478
481
|
|
|
479
482
|
## Leitura que não coube na espera
|
|
480
483
|
|
|
@@ -488,23 +491,23 @@ em segundo plano em vez de virar erro:
|
|
|
488
491
|
`PROCESS`) ou `snapshot` (visão `SNAPSHOT`), com as instâncias do número em
|
|
489
492
|
`instances`, o mesmo `meta` e o mesmo preço da leitura direta;
|
|
490
493
|
- **202**: a fonte falhou — não respondeu a tempo, ficou indisponível ou pediu
|
|
491
|
-
para esperar —, e
|
|
492
|
-
tentativa, por até 6 h (`giveUpAt`). Nada é cobrado na hora: o preço
|
|
493
|
-
leitura fica retido em `creditsReserved`, o máximo que a entrega cobra, e
|
|
494
|
+
para esperar —, e a API abriu uma consulta que segue tentando sozinha, até
|
|
495
|
+
60 s por tentativa, por até 6 h (`giveUpAt`). Nada é cobrado na hora: o preço
|
|
496
|
+
da leitura fica retido em `creditsReserved`, o máximo que a entrega cobra, e
|
|
494
497
|
só é cobrado se o processo vier. Se não vier, a retenção volta;
|
|
495
498
|
- qualquer outra recusa é a da leitura direta: 404 quando a fonte diz não ter o
|
|
496
499
|
processo, 403 no segredo de justiça, 402 sem saldo.
|
|
497
500
|
|
|
498
501
|
Para uma integração que precisa continuar mesmo quando a fonte demora, use
|
|
499
|
-
`readProcessWithRecovery`. A aceitação da cópia antiga é explícita
|
|
500
|
-
total é limitada. O fim da espera devolve `pending`, com a mesma consulta e
|
|
501
|
-
|
|
502
|
+
`readProcessWithRecovery`. A aceitação da cópia antiga é explícita, e a espera
|
|
503
|
+
total é limitada. O fim da espera devolve `pending`, com a mesma consulta e a
|
|
504
|
+
próxima leitura útil, sem cancelar a consulta nem abrir outra:
|
|
502
505
|
|
|
503
506
|
```ts
|
|
504
507
|
import { readProcessWithRecovery } from "@lexnode/legal-data-sdk";
|
|
505
508
|
|
|
506
509
|
const leitura = await readProcessWithRecovery(lexnode, {
|
|
507
|
-
tribunalAlias: "
|
|
510
|
+
tribunalAlias: "TJSP",
|
|
508
511
|
processNumber: numeroDoProcesso,
|
|
509
512
|
view: "PROCESS",
|
|
510
513
|
waitSeconds: 5,
|
|
@@ -523,10 +526,10 @@ if (leitura.kind === "ready") {
|
|
|
523
526
|
}
|
|
524
527
|
```
|
|
525
528
|
|
|
526
|
-
Sem `completionBudgetMs`,
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
529
|
+
Sem `completionBudgetMs`, a função devolve na hora o desfecho da abertura (200
|
|
530
|
+
ou 202). O orçamento cobre só o acompanhamento, e cada chamada mantém o próprio
|
|
531
|
+
prazo. Abortar a espera não cancela a consulta. O custo, o controle de
|
|
532
|
+
confidencialidade e a cobrança única são os de `processLookups.create`.
|
|
530
533
|
|
|
531
534
|
`lookupProcess` diz, no tipo, qual dos dois desfechos aconteceu, e
|
|
532
535
|
`waitForProcessLookup` espera a consulta terminar:
|
|
@@ -535,7 +538,7 @@ O custo, o gate de confidencialidade e a cobrança única são os do endpoint ex
|
|
|
535
538
|
import { lookupProcess, waitForProcessLookup } from "@lexnode/legal-data-sdk";
|
|
536
539
|
|
|
537
540
|
const desfecho = await lookupProcess(lexnode, {
|
|
538
|
-
tribunalAlias: "
|
|
541
|
+
tribunalAlias: "TJSP",
|
|
539
542
|
processNumber: numero,
|
|
540
543
|
view: "SNAPSHOT",
|
|
541
544
|
});
|
|
@@ -543,9 +546,9 @@ const desfecho = await lookupProcess(lexnode, {
|
|
|
543
546
|
if (desfecho.kind === "ready") {
|
|
544
547
|
desfecho.data.movements; // o retrato, como processes.snapshot o devolveria
|
|
545
548
|
} else {
|
|
546
|
-
//
|
|
547
|
-
//
|
|
548
|
-
// uma tentativa em curso
|
|
549
|
+
// Espere com orçamento. A consulta é lida de novo na hora da próxima
|
|
550
|
+
// tentativa (nextAttemptAt), quando o estado pode mudar, e a cada 10 s
|
|
551
|
+
// enquanto uma tentativa está em curso.
|
|
549
552
|
const consulta = await waitForProcessLookup(
|
|
550
553
|
lexnode,
|
|
551
554
|
desfecho.lookup.lookupId!,
|
|
@@ -565,22 +568,23 @@ chamada repetida tenta a fonte na hora, dentro da espera dela. O tempo
|
|
|
565
568
|
esgotado da fonte continua sem repetição automática.
|
|
566
569
|
|
|
567
570
|
**A consulta se lê de graça, por 24 h.** `processLookups.get(lookupId)` custa 0
|
|
568
|
-
e, depois da entrega, traz o processo por 24 h (`resultExpiresAt`), relido
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
completam a superfície; o cancelamento devolve a
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
571
|
+
e, depois da entrega, traz o processo por 24 h (`resultExpiresAt`), relido e
|
|
572
|
+
passado de novo pelo controle de confidencialidade: o processo que passou a
|
|
573
|
+
segredo de justiça depois responde 403. O retrato lido da consulta traz a
|
|
574
|
+
instância principal no topo e cada instância com os movimentos da última
|
|
575
|
+
captura dela, e `sourcePayload` é nulo. `processLookups.list` e
|
|
576
|
+
`processLookups.cancel` completam a superfície; o cancelamento devolve a
|
|
577
|
+
retenção e não gera evento.
|
|
578
|
+
|
|
579
|
+
**Por webhook, sem esperar nada.** O fim sai em `PROCESS_LOOKUP_COMPLETED`, que
|
|
580
|
+
aponta a consulta e nunca traz o processo; o movimento novo que a entrega
|
|
581
|
+
trouxer chega no `PROCESS_MOVEMENT_CREATED` de sempre.
|
|
578
582
|
|
|
579
583
|
**A cópia guardada e a atualização, uma compra só.** Com `staleOnFailure: true`,
|
|
580
|
-
o 202 traz em `stale` a cópia
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
+
o 202 traz em `stale` a cópia guardada, com a data e a causa, por 1 crédito, e
|
|
585
|
+
a consulta segue buscando a versão atual. A entrega cobra só o resto do preço
|
|
586
|
+
(`alreadyCharged` diz quanto já foi): as duas juntas custam o mesmo que a
|
|
587
|
+
leitura direta, e a atualização que não vier não cobra mais nada.
|
|
584
588
|
|
|
585
589
|
**A leitura direta fecha a consulta.** A ficha ou o retrato do mesmo processo
|
|
586
590
|
lidos com sucesso por `processes.get` ou `processes.snapshot` fecham a consulta
|
|
@@ -593,8 +597,8 @@ consulta.
|
|
|
593
597
|
## Frescor: o `total: 0` qualificado
|
|
594
598
|
|
|
595
599
|
Um resultado vazio não distingue sozinho "não existe" de "a fonte ainda não
|
|
596
|
-
indexou".
|
|
597
|
-
(processo e diário):
|
|
600
|
+
indexou". A API publica a data-limite do índice da fonte; o SDK a lê nos dois
|
|
601
|
+
eixos (processo e diário):
|
|
598
602
|
|
|
599
603
|
```ts
|
|
600
604
|
import {
|
|
@@ -610,15 +614,15 @@ if (isNotYetIndexed(busca, { since: "2026-09-15" })) {
|
|
|
610
614
|
mostrar(describeFreshness(busca)); // "fonte lida até 10/09/2026"
|
|
611
615
|
}
|
|
612
616
|
|
|
613
|
-
readThrough(busca); // Date | null — null
|
|
617
|
+
readThrough(busca); // Date | null — null quando a resposta não declara a data-limite
|
|
614
618
|
```
|
|
615
619
|
|
|
616
620
|
## Vigília com vários interessados
|
|
617
621
|
|
|
618
|
-
|
|
619
|
-
conta e
|
|
620
|
-
`externalRef`, sem
|
|
621
|
-
um
|
|
622
|
+
A vigília de um processo tem duas partes: o **monitor**, que consulta a fonte —
|
|
623
|
+
um por processo na conta, e o plano limita quantos —, e a **assinatura**, que
|
|
624
|
+
recebe o aviso — uma por `externalRef`, sem limite, e o `externalRef` volta no
|
|
625
|
+
evento. N interessados no mesmo processo são um monitor e N assinaturas.
|
|
622
626
|
|
|
623
627
|
```ts
|
|
624
628
|
import { unwatchProcess, watchProcess } from "@lexnode/legal-data-sdk";
|
|
@@ -627,23 +631,26 @@ const { monitorId, monitorAlreadyExisted, subscription } = await watchProcess(
|
|
|
627
631
|
lexnode,
|
|
628
632
|
{
|
|
629
633
|
processNumber: "00012345620268260100",
|
|
630
|
-
tribunalAlias: "
|
|
634
|
+
tribunalAlias: "TJSP",
|
|
631
635
|
officeId: "usuario-4711",
|
|
632
636
|
externalRef: "caso-42",
|
|
633
637
|
},
|
|
634
638
|
);
|
|
635
639
|
|
|
636
|
-
// Um interessado
|
|
640
|
+
// Um interessado sai; o monitor continua ativo para os demais.
|
|
637
641
|
await unwatchProcess(lexnode, {
|
|
638
642
|
externalRef: "caso-42",
|
|
639
643
|
officeId: "usuario-4711",
|
|
640
644
|
});
|
|
641
645
|
```
|
|
642
646
|
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
+
Quando o último interessado sair, remova o monitor com
|
|
648
|
+
`lexnode.monitors.delete(monitorId)`.
|
|
649
|
+
|
|
650
|
+
**Limitação conhecida:** uma assinatura criada **antes** de o processo aparecer
|
|
651
|
+
na fonte não recebe `PROCESS_MOVEMENT_CREATED` até a primeira aparição dele. O
|
|
652
|
+
contorno é refazer a assinatura quando chegar o `MONITOR_SYNC_SUCCEEDED` com
|
|
653
|
+
`firstAppearance: true`:
|
|
647
654
|
|
|
648
655
|
```ts
|
|
649
656
|
import { resubscribeOnFirstAppearance } from "@lexnode/legal-data-sdk";
|
|
@@ -653,10 +660,9 @@ await resubscribeOnFirstAppearance(lexnode, evento, [
|
|
|
653
660
|
]);
|
|
654
661
|
```
|
|
655
662
|
|
|
656
|
-
##
|
|
663
|
+
## Os tipos do contrato
|
|
657
664
|
|
|
658
|
-
O
|
|
659
|
-
sempre:
|
|
665
|
+
O pacote exporta `paths` e `operations`, gerados do contrato OpenAPI:
|
|
660
666
|
|
|
661
667
|
```ts
|
|
662
668
|
import type { operations, paths } from "@lexnode/legal-data-sdk";
|
|
@@ -669,7 +675,7 @@ type Tribunals =
|
|
|
669
675
|
operations["listTribunals"]["responses"][200]["content"]["application/json"]["data"];
|
|
670
676
|
```
|
|
671
677
|
|
|
672
|
-
E
|
|
678
|
+
E atalhos derivados deles:
|
|
673
679
|
|
|
674
680
|
```ts
|
|
675
681
|
import type { Body, Data, Query } from "@lexnode/legal-data-sdk";
|
|
@@ -681,11 +687,11 @@ type Tribunais = Data<"/v1/tribunals", "get">;
|
|
|
681
687
|
|
|
682
688
|
## A chave de teste
|
|
683
689
|
|
|
684
|
-
A chave `lapi_test_`
|
|
685
|
-
cria trabalho
|
|
686
|
-
respondem 403 `SANDBOX_UPSTREAM_DISABLED`.
|
|
687
|
-
`async` de `searchProcesses` só
|
|
688
|
-
|
|
690
|
+
A chave `lapi_test_` relê o que a conta já buscou com a chave de produção, mas
|
|
691
|
+
**não consulta a fonte nem cria trabalho em segundo plano**: monitor, vigília,
|
|
692
|
+
colheita e busca assíncrona respondem 403 `SANDBOX_UPSTREAM_DISABLED`. Por isso
|
|
693
|
+
`watchProcess` e o ramo `async` de `searchProcesses` só funcionam com a chave
|
|
694
|
+
de produção.
|
|
689
695
|
|
|
690
696
|
## Referências
|
|
691
697
|
|