@legalize-dev/sdk 0.1.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/CHANGELOG.md +49 -0
- package/LICENSE +21 -0
- package/README.md +246 -0
- package/dist/index.cjs +1129 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +1012 -0
- package/dist/index.d.ts +1012 -0
- package/dist/index.js +1063 -0
- package/dist/index.js.map +1 -0
- package/package.json +67 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to the `@legalize-dev/sdk` Node SDK will be
|
|
4
|
+
documented in this file. The format is based on [Keep a Changelog][kac]
|
|
5
|
+
and this project adheres to [Semantic Versioning][semver].
|
|
6
|
+
|
|
7
|
+
[kac]: https://keepachangelog.com/en/1.1.0/
|
|
8
|
+
[semver]: https://semver.org/spec/v2.0.0.html
|
|
9
|
+
|
|
10
|
+
## [Unreleased]
|
|
11
|
+
|
|
12
|
+
## [0.1.0] — 2026-04-20
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
|
|
16
|
+
- First public release of the Node SDK for the Legalize API, published
|
|
17
|
+
as [`@legalize-dev/sdk`](https://www.npmjs.com/package/@legalize-dev/sdk)
|
|
18
|
+
on npm.
|
|
19
|
+
- `Legalize` client with configurable timeout, retry policy, default
|
|
20
|
+
headers, and `fetch` override for tests.
|
|
21
|
+
- Zero-config construction from `LEGALIZE_API_KEY`, `LEGALIZE_BASE_URL`,
|
|
22
|
+
`LEGALIZE_API_VERSION` — matches the cross-SDK `ENVIRONMENT.md`
|
|
23
|
+
contract byte-for-byte with the Python SDK.
|
|
24
|
+
- Full resource surface: `countries`, `jurisdictions`, `lawTypes`,
|
|
25
|
+
`laws`, `reforms`, `stats`, `webhooks` (create / list / retrieve /
|
|
26
|
+
update / delete / deliveries / retry / test).
|
|
27
|
+
- Auto-paginated async iterators `laws.iter`, `laws.searchIter`,
|
|
28
|
+
`reforms.iter`.
|
|
29
|
+
- Typed response models generated from `openapi-sdk.json`.
|
|
30
|
+
- Error hierarchy rooted at `LegalizeError`: `APIError`,
|
|
31
|
+
`AuthenticationError`, `ForbiddenError`, `NotFoundError`,
|
|
32
|
+
`InvalidRequestError`, `ValidationError`, `RateLimitError`,
|
|
33
|
+
`ServerError`, `ServiceUnavailableError`, `APIConnectionError`,
|
|
34
|
+
`APITimeoutError`, `WebhookVerificationError`.
|
|
35
|
+
- Retry policy with exponential backoff + full jitter. Honors
|
|
36
|
+
`Retry-After` in both delta-seconds and HTTP-date form. POST/PATCH
|
|
37
|
+
are not auto-retried unless `retryNonIdempotent: true`.
|
|
38
|
+
- `AbortSignal` support on every method.
|
|
39
|
+
- `Webhook.verify` and `Webhook.computeSignature` — constant-time HMAC
|
|
40
|
+
comparison, multi-signature header parsing, 300-second anti-replay
|
|
41
|
+
window.
|
|
42
|
+
- Dual ESM + CJS build via `tsup` with `.d.ts` for both variants.
|
|
43
|
+
- `await using` / `[Symbol.asyncDispose]` support.
|
|
44
|
+
- `lastResponse` populated on success AND error paths for inspecting
|
|
45
|
+
rate-limit headers and `X-Request-Id`.
|
|
46
|
+
- Examples for the four main use cases — list laws, search,
|
|
47
|
+
time-travel, Express webhook receiver, Fastify webhook receiver.
|
|
48
|
+
- Test suite with ≥ 95% line coverage and a contract test that asserts
|
|
49
|
+
the SDK exposes every `(method, path)` tuple in `openapi-sdk.json`.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Legalize
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
# @legalize-dev/sdk
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@legalize-dev/sdk)
|
|
4
|
+
[](https://www.npmjs.com/package/@legalize-dev/sdk)
|
|
5
|
+
[](https://github.com/legalize-dev/legalize-sdks/blob/main/LICENSE)
|
|
6
|
+
|
|
7
|
+
Official Node client for the [Legalize API](https://legalize.dev/api) — legal texts as structured, versioned data.
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @legalize-dev/sdk
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import { Legalize } from "@legalize-dev/sdk";
|
|
15
|
+
|
|
16
|
+
const client = new Legalize({ apiKey: "leg_..." });
|
|
17
|
+
|
|
18
|
+
for await (const law of client.laws.iter("es", { lawType: "ley_organica" })) {
|
|
19
|
+
console.log(law.id, law.title);
|
|
20
|
+
}
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Why this SDK
|
|
24
|
+
|
|
25
|
+
- **Typed end-to-end.** Types are generated from the canonical OpenAPI
|
|
26
|
+
spec and shipped in the package. `strict` TypeScript friendly.
|
|
27
|
+
- **Zero runtime dependencies.** Uses Node's built-in `fetch`,
|
|
28
|
+
`AbortController`, and `crypto`. No `undici`, `axios`, or `node-fetch`.
|
|
29
|
+
- **Retries with backoff built in.** Honors `Retry-After`, handles
|
|
30
|
+
429/5xx, exponential delay with full jitter, and never auto-retries
|
|
31
|
+
POST/PATCH by default (no duplicate mutations).
|
|
32
|
+
- **Webhook verification is a one-liner.** Constant-time HMAC compare,
|
|
33
|
+
5-minute anti-replay window, clock-skew tolerant.
|
|
34
|
+
- **Works in any Node 20+ environment.** Lambda, Cloud Functions, Fly,
|
|
35
|
+
Railway, plain `node`, `tsx`, `ts-node`, etc.
|
|
36
|
+
- **ESM and CJS dual build.** `"type": "module"` with a proper `exports`
|
|
37
|
+
map so `import` and `require` both resolve cleanly.
|
|
38
|
+
|
|
39
|
+
## Quick tour
|
|
40
|
+
|
|
41
|
+
### List, iterate, search
|
|
42
|
+
|
|
43
|
+
```ts
|
|
44
|
+
// One page
|
|
45
|
+
const page = await client.laws.list("es", { page: 1, perPage: 50 });
|
|
46
|
+
console.log(page.total, page.results.length);
|
|
47
|
+
|
|
48
|
+
// Auto-paginated async iterator (fetches pages as needed)
|
|
49
|
+
for await (const law of client.laws.iter("es", { status: "vigente" })) {
|
|
50
|
+
// ...
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Full-text search
|
|
54
|
+
const results = await client.laws.search("es", "protección de datos");
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Time-travel
|
|
58
|
+
|
|
59
|
+
Every law has a git-tracked history. Retrieve it at any past revision:
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
const commits = await client.laws.commits("es", "ley_organica_3_2018");
|
|
63
|
+
const oldest = commits.commits[commits.commits.length - 1]!.sha;
|
|
64
|
+
const past = await client.laws.atCommit("es", "ley_organica_3_2018", oldest);
|
|
65
|
+
console.log(past.content_md); // Markdown at that revision
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Abort + timeout
|
|
69
|
+
|
|
70
|
+
Every method accepts a standard `AbortSignal`:
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
const ac = new AbortController();
|
|
74
|
+
setTimeout(() => ac.abort(), 5000);
|
|
75
|
+
const out = await client.laws.list("es", { signal: ac.signal });
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Per-request timeouts are configured on the client:
|
|
79
|
+
|
|
80
|
+
```ts
|
|
81
|
+
const client = new Legalize({ apiKey: "leg_...", timeout: 10_000 });
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Webhooks
|
|
85
|
+
|
|
86
|
+
Verify a signed delivery in one call. **Pass the raw request bytes** —
|
|
87
|
+
re-serialized JSON will NOT verify:
|
|
88
|
+
|
|
89
|
+
```ts
|
|
90
|
+
import express from "express";
|
|
91
|
+
import { Webhook, WebhookVerificationError } from "@legalize-dev/sdk";
|
|
92
|
+
|
|
93
|
+
app.post(
|
|
94
|
+
"/webhooks/legalize",
|
|
95
|
+
express.raw({ type: "application/json" }),
|
|
96
|
+
(req, res) => {
|
|
97
|
+
try {
|
|
98
|
+
const event = Webhook.verify({
|
|
99
|
+
payload: req.body as Buffer,
|
|
100
|
+
sigHeader: req.header("X-Legalize-Signature") ?? "",
|
|
101
|
+
timestamp: req.header("X-Legalize-Timestamp") ?? "",
|
|
102
|
+
secret: process.env.LEGALIZE_WHSEC!,
|
|
103
|
+
});
|
|
104
|
+
if (event.type === "law.updated") {
|
|
105
|
+
// ...
|
|
106
|
+
}
|
|
107
|
+
res.status(204).send();
|
|
108
|
+
} catch (err) {
|
|
109
|
+
if (err instanceof WebhookVerificationError) {
|
|
110
|
+
res.status(400).send();
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
throw err;
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
);
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Working Express and Fastify receivers in
|
|
120
|
+
[`examples/`](https://github.com/legalize-dev/legalize-sdks/tree/main/node/examples).
|
|
121
|
+
|
|
122
|
+
## Configuration
|
|
123
|
+
|
|
124
|
+
### Zero-config (recommended for servers + Kubernetes)
|
|
125
|
+
|
|
126
|
+
Set the environment and just instantiate:
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
export LEGALIZE_API_KEY=leg_live_...
|
|
130
|
+
# Optional:
|
|
131
|
+
export LEGALIZE_BASE_URL=https://legalize.dev
|
|
132
|
+
export LEGALIZE_API_VERSION=v1
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
```ts
|
|
136
|
+
import { Legalize } from "@legalize-dev/sdk";
|
|
137
|
+
|
|
138
|
+
const client = new Legalize(); // picks everything up from the environment
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Explicit
|
|
142
|
+
|
|
143
|
+
```ts
|
|
144
|
+
import { Legalize, RetryPolicy } from "@legalize-dev/sdk";
|
|
145
|
+
|
|
146
|
+
const client = new Legalize({
|
|
147
|
+
apiKey: "leg_...",
|
|
148
|
+
baseUrl: "https://legalize.dev",
|
|
149
|
+
apiVersion: "v1", // negotiated via Legalize-API-Version header
|
|
150
|
+
timeout: 30_000, // milliseconds
|
|
151
|
+
retry: new RetryPolicy({
|
|
152
|
+
maxRetries: 5,
|
|
153
|
+
initialDelay: 0.5,
|
|
154
|
+
maxDelay: 10,
|
|
155
|
+
}),
|
|
156
|
+
defaultHeaders: { "X-Correlation-Id": "..." },
|
|
157
|
+
});
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Precedence: explicit argument > environment variable > built-in default.
|
|
161
|
+
The full cross-SDK contract is documented in
|
|
162
|
+
[`ENVIRONMENT.md`](https://github.com/legalize-dev/legalize-sdks/blob/main/ENVIRONMENT.md).
|
|
163
|
+
|
|
164
|
+
Read rate-limit headers from the last response:
|
|
165
|
+
|
|
166
|
+
```ts
|
|
167
|
+
await client.countries.list();
|
|
168
|
+
const resp = client.lastResponse;
|
|
169
|
+
console.log(resp?.headers.get("X-RateLimit-Remaining"));
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
The same `lastResponse` is populated when a call fails, so you can
|
|
173
|
+
inspect `X-Request-Id` and rate-limit headers after an error too:
|
|
174
|
+
|
|
175
|
+
```ts
|
|
176
|
+
try {
|
|
177
|
+
await client.laws.retrieve("es", "unknown");
|
|
178
|
+
} catch (err) {
|
|
179
|
+
console.error(client.lastResponse?.headers.get("X-Request-Id"));
|
|
180
|
+
throw err;
|
|
181
|
+
}
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### Cleanup
|
|
185
|
+
|
|
186
|
+
The client holds no persistent connection pool (Node's `fetch` manages
|
|
187
|
+
sockets globally), but `close()` is exported for API symmetry across
|
|
188
|
+
SDKs. TS 5.2+ `await using` is supported:
|
|
189
|
+
|
|
190
|
+
```ts
|
|
191
|
+
{
|
|
192
|
+
await using client = new Legalize({ apiKey: "leg_..." });
|
|
193
|
+
await client.countries.list();
|
|
194
|
+
} // client auto-disposed
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
## Retries
|
|
198
|
+
|
|
199
|
+
Auto-retries on 429 + 5xx + transport errors, with exponential backoff
|
|
200
|
+
and full jitter. `Retry-After` (integer seconds or HTTP-date form) is
|
|
201
|
+
honored when present, capped at `maxDelay`.
|
|
202
|
+
|
|
203
|
+
**POST and PATCH are NOT retried by default** — they may be
|
|
204
|
+
non-idempotent. Opt in per-policy with `retryNonIdempotent: true`, or
|
|
205
|
+
send an `Idempotency-Key` and wrap your own retry loop.
|
|
206
|
+
|
|
207
|
+
## Errors
|
|
208
|
+
|
|
209
|
+
All errors inherit from `LegalizeError`. Catch the specific one you
|
|
210
|
+
care about and let the rest bubble:
|
|
211
|
+
|
|
212
|
+
```ts
|
|
213
|
+
import {
|
|
214
|
+
AuthenticationError, // 401 — bad/missing key
|
|
215
|
+
ForbiddenError, // 403
|
|
216
|
+
NotFoundError, // 404
|
|
217
|
+
InvalidRequestError, // 400
|
|
218
|
+
ValidationError, // 422
|
|
219
|
+
RateLimitError, // 429 — retried automatically by default
|
|
220
|
+
ServerError, // 5xx
|
|
221
|
+
ServiceUnavailableError, // 503
|
|
222
|
+
APIConnectionError, // network failure
|
|
223
|
+
APITimeoutError, // timeout
|
|
224
|
+
WebhookVerificationError,
|
|
225
|
+
} from "@legalize-dev/sdk";
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
Every `APIError` exposes `.statusCode`, `.code`, `.body`, `.response`,
|
|
229
|
+
and `.requestId`.
|
|
230
|
+
|
|
231
|
+
## Compatibility
|
|
232
|
+
|
|
233
|
+
- Node 20, 22, 24
|
|
234
|
+
- Linux, macOS, Windows
|
|
235
|
+
- ESM and CommonJS consumers (dual package)
|
|
236
|
+
|
|
237
|
+
## Links
|
|
238
|
+
|
|
239
|
+
- [API reference](https://legalize.dev/api/docs)
|
|
240
|
+
- [Monorepo](https://github.com/legalize-dev/legalize-sdks) (all language SDKs)
|
|
241
|
+
- [Changelog](https://github.com/legalize-dev/legalize-sdks/blob/main/node/CHANGELOG.md)
|
|
242
|
+
- [Contributing](https://github.com/legalize-dev/legalize-sdks/blob/main/CONTRIBUTING.md)
|
|
243
|
+
|
|
244
|
+
## License
|
|
245
|
+
|
|
246
|
+
MIT
|