@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/dist/index.d.cts
ADDED
|
@@ -0,0 +1,1012 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Environment-variable resolution helpers.
|
|
3
|
+
*
|
|
4
|
+
* Contract: see `sdk/ENVIRONMENT.md`. Summary:
|
|
5
|
+
* - `LEGALIZE_API_KEY` — required (unless `apiKey` passed explicitly).
|
|
6
|
+
* - `LEGALIZE_BASE_URL` — default "https://legalize.dev".
|
|
7
|
+
* - `LEGALIZE_API_VERSION` — default "v1".
|
|
8
|
+
*
|
|
9
|
+
* Precedence: explicit arg > env var > default. Empty string = unset.
|
|
10
|
+
* Prefix `LEGALIZE_` is mandatory; no aliases.
|
|
11
|
+
*/
|
|
12
|
+
declare const DEFAULT_BASE_URL = "https://legalize.dev";
|
|
13
|
+
declare const DEFAULT_API_VERSION = "v1";
|
|
14
|
+
declare const DEFAULT_TIMEOUT = 30000;
|
|
15
|
+
declare const KEY_PREFIX = "leg_";
|
|
16
|
+
/**
|
|
17
|
+
* Resolve the API key from the explicit argument or the environment.
|
|
18
|
+
*
|
|
19
|
+
* Raises AuthenticationError synchronously if no key is available or the
|
|
20
|
+
* key prefix is unrecognized. Catching obviously-bad inputs before the
|
|
21
|
+
* first network call saves operators from debugging 401s later.
|
|
22
|
+
*/
|
|
23
|
+
declare function resolveApiKey(apiKey: string | undefined): string;
|
|
24
|
+
/**
|
|
25
|
+
* Resolve the base URL from the explicit argument, env, or default.
|
|
26
|
+
* Empty strings are treated as unset.
|
|
27
|
+
*/
|
|
28
|
+
declare function resolveBaseUrl(baseUrl: string | undefined): string;
|
|
29
|
+
/**
|
|
30
|
+
* Resolve the API version from the explicit argument, env, or default.
|
|
31
|
+
* Empty strings are treated as unset.
|
|
32
|
+
*/
|
|
33
|
+
declare function resolveApiVersion(apiVersion: string | undefined): string;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Retry policy for transient failures.
|
|
37
|
+
*
|
|
38
|
+
* Retries happen on:
|
|
39
|
+
* - Network errors (DNS, connect, read timeout, TLS)
|
|
40
|
+
* - HTTP 429 (rate limit)
|
|
41
|
+
* - HTTP 500, 502, 503, 504 (transient server issues)
|
|
42
|
+
*
|
|
43
|
+
* Retries do NOT happen on:
|
|
44
|
+
* - 4xx other than 429 (caller error, retrying won't help)
|
|
45
|
+
* - Non-idempotent methods by default (POST, PATCH): mutations are
|
|
46
|
+
* never auto-retried. Only GET/HEAD/OPTIONS/PUT/DELETE are retried.
|
|
47
|
+
* This prevents duplicate webhook creation and duplicate retry calls.
|
|
48
|
+
*
|
|
49
|
+
* The `Retry-After` header wins when present and non-negative. Otherwise
|
|
50
|
+
* exponential backoff with full jitter, capped at `maxDelay`.
|
|
51
|
+
*/
|
|
52
|
+
declare const DEFAULT_MAX_RETRIES = 3;
|
|
53
|
+
declare const DEFAULT_INITIAL_DELAY = 0.5;
|
|
54
|
+
declare const DEFAULT_MAX_DELAY = 30;
|
|
55
|
+
declare const DEFAULT_BACKOFF_FACTOR = 2;
|
|
56
|
+
declare const RETRY_STATUSES: ReadonlySet<number>;
|
|
57
|
+
/** HTTP methods considered safe to auto-retry (idempotent). */
|
|
58
|
+
declare const IDEMPOTENT_METHODS: ReadonlySet<string>;
|
|
59
|
+
interface RetryPolicyOptions {
|
|
60
|
+
maxRetries?: number;
|
|
61
|
+
initialDelay?: number;
|
|
62
|
+
maxDelay?: number;
|
|
63
|
+
backoffFactor?: number;
|
|
64
|
+
/**
|
|
65
|
+
* When true, retry POST/PATCH too. Default false — the SDK never
|
|
66
|
+
* auto-retries mutations unless the caller opts in explicitly.
|
|
67
|
+
*/
|
|
68
|
+
retryNonIdempotent?: boolean;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Configuration for automatic retries.
|
|
72
|
+
*
|
|
73
|
+
* Immutable — construct a new instance to tweak. Matches the Python
|
|
74
|
+
* `RetryPolicy` contract:
|
|
75
|
+
* - `maxRetries`: total attempts is at most `maxRetries + 1`. 0 disables retries.
|
|
76
|
+
* - `initialDelay`: seconds before the first retry (pre-jitter).
|
|
77
|
+
* - `maxDelay`: cap on any single sleep in seconds.
|
|
78
|
+
* - `backoffFactor`: multiplier applied to delay each attempt.
|
|
79
|
+
*/
|
|
80
|
+
declare class RetryPolicy {
|
|
81
|
+
readonly maxRetries: number;
|
|
82
|
+
readonly initialDelay: number;
|
|
83
|
+
readonly maxDelay: number;
|
|
84
|
+
readonly backoffFactor: number;
|
|
85
|
+
readonly retryNonIdempotent: boolean;
|
|
86
|
+
constructor(options?: RetryPolicyOptions);
|
|
87
|
+
/**
|
|
88
|
+
* Decide whether to retry given attempt index, HTTP status, and method.
|
|
89
|
+
*
|
|
90
|
+
* `status` is undefined when the failure was a transport error before
|
|
91
|
+
* the server returned a status. Transport errors are retried for any
|
|
92
|
+
* method (the request never hit the server, so the "don't duplicate
|
|
93
|
+
* mutations" concern doesn't apply).
|
|
94
|
+
*/
|
|
95
|
+
shouldRetry(attempt: number, options: {
|
|
96
|
+
status?: number;
|
|
97
|
+
method?: string;
|
|
98
|
+
}): boolean;
|
|
99
|
+
/**
|
|
100
|
+
* Seconds to wait before retry `attempt` (0-indexed).
|
|
101
|
+
*
|
|
102
|
+
* `Retry-After` wins unambiguously when present and non-negative: the
|
|
103
|
+
* server is telling us exactly how long to wait. Otherwise we use
|
|
104
|
+
* exponential backoff with full jitter:
|
|
105
|
+
*
|
|
106
|
+
* delay = random.uniform(0, min(maxDelay, initial * factor^attempt))
|
|
107
|
+
*
|
|
108
|
+
* Full jitter beats "equal jitter" and "decorrelated jitter" for
|
|
109
|
+
* preventing thundering-herd recovery spikes.
|
|
110
|
+
*/
|
|
111
|
+
computeDelay(attempt: number, options: {
|
|
112
|
+
retryAfter?: number;
|
|
113
|
+
}): number;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Parse the `Retry-After` header into seconds.
|
|
117
|
+
*
|
|
118
|
+
* RFC 9110 allows two forms:
|
|
119
|
+
* - A non-negative integer (delta-seconds): `Retry-After: 120`
|
|
120
|
+
* - An HTTP-date: `Retry-After: Wed, 21 Oct 2025 07:28:00 GMT`
|
|
121
|
+
*
|
|
122
|
+
* Unparseable input returns `undefined` so the caller can fall back to
|
|
123
|
+
* its own backoff. HTTP-date values in the past clamp to `0`.
|
|
124
|
+
*/
|
|
125
|
+
declare function parseRetryAfter(header: string | null | undefined): number | undefined;
|
|
126
|
+
/** Promise-based sleep — used by the client's retry loop. */
|
|
127
|
+
declare function sleep(seconds: number): Promise<void>;
|
|
128
|
+
|
|
129
|
+
interface components {
|
|
130
|
+
schemas: {
|
|
131
|
+
/** Commit */
|
|
132
|
+
Commit: {
|
|
133
|
+
/** Date */
|
|
134
|
+
date: string;
|
|
135
|
+
/** Message */
|
|
136
|
+
message: string;
|
|
137
|
+
/** Sha */
|
|
138
|
+
sha: string;
|
|
139
|
+
};
|
|
140
|
+
/** CommitsResponse */
|
|
141
|
+
CommitsResponse: {
|
|
142
|
+
/** Commits */
|
|
143
|
+
commits: components["schemas"]["Commit"][];
|
|
144
|
+
/** Law Id */
|
|
145
|
+
law_id: string;
|
|
146
|
+
};
|
|
147
|
+
/** CountryInfo */
|
|
148
|
+
CountryInfo: {
|
|
149
|
+
/** Count */
|
|
150
|
+
count: number;
|
|
151
|
+
/** Country */
|
|
152
|
+
country: string;
|
|
153
|
+
};
|
|
154
|
+
/** HTTPValidationError */
|
|
155
|
+
HTTPValidationError: {
|
|
156
|
+
/** Detail */
|
|
157
|
+
detail?: components["schemas"]["ValidationError"][];
|
|
158
|
+
};
|
|
159
|
+
/** JurisdictionInfo */
|
|
160
|
+
JurisdictionInfo: {
|
|
161
|
+
/** Count */
|
|
162
|
+
count: number;
|
|
163
|
+
/** Jurisdiction */
|
|
164
|
+
jurisdiction?: string | null;
|
|
165
|
+
};
|
|
166
|
+
/** LawAtCommitResponse */
|
|
167
|
+
LawAtCommitResponse: {
|
|
168
|
+
/** Content Md */
|
|
169
|
+
content_md: string;
|
|
170
|
+
/** Law Id */
|
|
171
|
+
law_id: string;
|
|
172
|
+
/** Sha */
|
|
173
|
+
sha: string;
|
|
174
|
+
};
|
|
175
|
+
/**
|
|
176
|
+
* LawDetail
|
|
177
|
+
* @description Full law with Markdown content (fetched from GitHub).
|
|
178
|
+
*/
|
|
179
|
+
LawDetail: {
|
|
180
|
+
/** Article Count */
|
|
181
|
+
article_count?: number | null;
|
|
182
|
+
/** Content Md */
|
|
183
|
+
content_md?: string | null;
|
|
184
|
+
/** Country */
|
|
185
|
+
country: string;
|
|
186
|
+
/** Department */
|
|
187
|
+
department?: string | null;
|
|
188
|
+
/** Extra */
|
|
189
|
+
extra?: {
|
|
190
|
+
[key: string]: unknown;
|
|
191
|
+
} | null;
|
|
192
|
+
/** Frontmatter */
|
|
193
|
+
frontmatter?: {
|
|
194
|
+
[key: string]: unknown;
|
|
195
|
+
} | null;
|
|
196
|
+
/** Id */
|
|
197
|
+
id: string;
|
|
198
|
+
/** Jurisdiction */
|
|
199
|
+
jurisdiction?: string | null;
|
|
200
|
+
/** Last Updated */
|
|
201
|
+
last_updated?: string | null;
|
|
202
|
+
/** Law Type */
|
|
203
|
+
law_type: string;
|
|
204
|
+
/** Publication Date */
|
|
205
|
+
publication_date?: string | null;
|
|
206
|
+
/** Short Title */
|
|
207
|
+
short_title?: string | null;
|
|
208
|
+
/** Source */
|
|
209
|
+
source?: string | null;
|
|
210
|
+
/** Status */
|
|
211
|
+
status?: string | null;
|
|
212
|
+
/** Title */
|
|
213
|
+
title: string;
|
|
214
|
+
};
|
|
215
|
+
/**
|
|
216
|
+
* LawMeta
|
|
217
|
+
* @description Law metadata without content. Lightweight — no GitHub fetch.
|
|
218
|
+
*/
|
|
219
|
+
LawMeta: {
|
|
220
|
+
/** Article Count */
|
|
221
|
+
article_count?: number | null;
|
|
222
|
+
/** Country */
|
|
223
|
+
country: string;
|
|
224
|
+
/** Department */
|
|
225
|
+
department?: string | null;
|
|
226
|
+
/** Extra */
|
|
227
|
+
extra?: {
|
|
228
|
+
[key: string]: unknown;
|
|
229
|
+
} | null;
|
|
230
|
+
/** Id */
|
|
231
|
+
id: string;
|
|
232
|
+
/** Jurisdiction */
|
|
233
|
+
jurisdiction?: string | null;
|
|
234
|
+
/** Last Updated */
|
|
235
|
+
last_updated?: string | null;
|
|
236
|
+
/** Law Type */
|
|
237
|
+
law_type: string;
|
|
238
|
+
/** Publication Date */
|
|
239
|
+
publication_date?: string | null;
|
|
240
|
+
/** Short Title */
|
|
241
|
+
short_title?: string | null;
|
|
242
|
+
/** Source */
|
|
243
|
+
source?: string | null;
|
|
244
|
+
/** Status */
|
|
245
|
+
status?: string | null;
|
|
246
|
+
/** Title */
|
|
247
|
+
title: string;
|
|
248
|
+
};
|
|
249
|
+
/**
|
|
250
|
+
* LawSearchResult
|
|
251
|
+
* @description Law result with search snippet.
|
|
252
|
+
*/
|
|
253
|
+
LawSearchResult: {
|
|
254
|
+
/** Article Count */
|
|
255
|
+
article_count?: number | null;
|
|
256
|
+
/** Country */
|
|
257
|
+
country: string;
|
|
258
|
+
/** Id */
|
|
259
|
+
id: string;
|
|
260
|
+
/** Jurisdiction */
|
|
261
|
+
jurisdiction?: string | null;
|
|
262
|
+
/** Law Type */
|
|
263
|
+
law_type: string;
|
|
264
|
+
/** Publication Date */
|
|
265
|
+
publication_date?: string | null;
|
|
266
|
+
/** Short Title */
|
|
267
|
+
short_title?: string | null;
|
|
268
|
+
/** Status */
|
|
269
|
+
status?: string | null;
|
|
270
|
+
/** Title */
|
|
271
|
+
title: string;
|
|
272
|
+
/** Title Snippet */
|
|
273
|
+
title_snippet?: string | null;
|
|
274
|
+
};
|
|
275
|
+
/**
|
|
276
|
+
* PaginatedLaws
|
|
277
|
+
* @description Unified response for law listing and search.
|
|
278
|
+
*
|
|
279
|
+
* When searching (query is not None), both ``total`` and ``count``
|
|
280
|
+
* are populated with the same value for backward compatibility.
|
|
281
|
+
* When listing, ``query`` and ``count`` are None.
|
|
282
|
+
*/
|
|
283
|
+
PaginatedLaws: {
|
|
284
|
+
/** Count */
|
|
285
|
+
count?: number | null;
|
|
286
|
+
/** Country */
|
|
287
|
+
country: string;
|
|
288
|
+
/** From Date */
|
|
289
|
+
from_date?: string | null;
|
|
290
|
+
/** Jurisdiction */
|
|
291
|
+
jurisdiction?: string | null;
|
|
292
|
+
/** Page */
|
|
293
|
+
page: number;
|
|
294
|
+
/** Per Page */
|
|
295
|
+
per_page: number;
|
|
296
|
+
/** Query */
|
|
297
|
+
query?: string | null;
|
|
298
|
+
/** Results */
|
|
299
|
+
results: components["schemas"]["LawSearchResult"][];
|
|
300
|
+
/** Sort */
|
|
301
|
+
sort?: string | null;
|
|
302
|
+
/** To Date */
|
|
303
|
+
to_date?: string | null;
|
|
304
|
+
/** Total */
|
|
305
|
+
total: number;
|
|
306
|
+
};
|
|
307
|
+
/** Reform */
|
|
308
|
+
Reform: {
|
|
309
|
+
/** Articles Affected */
|
|
310
|
+
articles_affected?: string | null;
|
|
311
|
+
/** Date */
|
|
312
|
+
date: string;
|
|
313
|
+
/** Source Id */
|
|
314
|
+
source_id?: string | null;
|
|
315
|
+
};
|
|
316
|
+
/** ReformsResponse */
|
|
317
|
+
ReformsResponse: {
|
|
318
|
+
/** Law Id */
|
|
319
|
+
law_id: string;
|
|
320
|
+
/** Limit */
|
|
321
|
+
limit: number;
|
|
322
|
+
/** Offset */
|
|
323
|
+
offset: number;
|
|
324
|
+
/** Reforms */
|
|
325
|
+
reforms: components["schemas"]["Reform"][];
|
|
326
|
+
/** Total */
|
|
327
|
+
total: number;
|
|
328
|
+
};
|
|
329
|
+
/** StatsResponse */
|
|
330
|
+
StatsResponse: {
|
|
331
|
+
/** Country */
|
|
332
|
+
country: string;
|
|
333
|
+
/** Jurisdiction */
|
|
334
|
+
jurisdiction?: string | null;
|
|
335
|
+
/** Law Types */
|
|
336
|
+
law_types: string[];
|
|
337
|
+
/** Most Reformed Laws */
|
|
338
|
+
most_reformed_laws: {
|
|
339
|
+
[key: string]: unknown;
|
|
340
|
+
}[];
|
|
341
|
+
/** Reform Activity By Year */
|
|
342
|
+
reform_activity_by_year: {
|
|
343
|
+
[key: string]: unknown;
|
|
344
|
+
}[];
|
|
345
|
+
};
|
|
346
|
+
/** ValidationError */
|
|
347
|
+
ValidationError: {
|
|
348
|
+
/** Context */
|
|
349
|
+
ctx?: Record<string, never>;
|
|
350
|
+
/** Input */
|
|
351
|
+
input?: unknown;
|
|
352
|
+
/** Location */
|
|
353
|
+
loc: (string | number)[];
|
|
354
|
+
/** Message */
|
|
355
|
+
msg: string;
|
|
356
|
+
/** Error Type */
|
|
357
|
+
type: string;
|
|
358
|
+
};
|
|
359
|
+
/** WebhookEndpointCreate */
|
|
360
|
+
WebhookEndpointCreate: {
|
|
361
|
+
/** Countries */
|
|
362
|
+
countries?: string[] | null;
|
|
363
|
+
/**
|
|
364
|
+
* Description
|
|
365
|
+
* @default
|
|
366
|
+
*/
|
|
367
|
+
description: string;
|
|
368
|
+
/** Event Types */
|
|
369
|
+
event_types: string[];
|
|
370
|
+
/** Url */
|
|
371
|
+
url: string;
|
|
372
|
+
};
|
|
373
|
+
/** WebhookEndpointUpdate */
|
|
374
|
+
WebhookEndpointUpdate: {
|
|
375
|
+
/** Countries */
|
|
376
|
+
countries?: string[] | null;
|
|
377
|
+
/** Description */
|
|
378
|
+
description?: string | null;
|
|
379
|
+
/** Enabled */
|
|
380
|
+
enabled?: boolean | null;
|
|
381
|
+
/** Event Types */
|
|
382
|
+
event_types?: string[] | null;
|
|
383
|
+
/** Url */
|
|
384
|
+
url?: string | null;
|
|
385
|
+
};
|
|
386
|
+
};
|
|
387
|
+
responses: never;
|
|
388
|
+
parameters: never;
|
|
389
|
+
requestBodies: never;
|
|
390
|
+
headers: never;
|
|
391
|
+
pathItems: never;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
* Public response and request types.
|
|
396
|
+
*
|
|
397
|
+
* The underlying schemas live in `generated.ts` (produced by
|
|
398
|
+
* `openapi-typescript` from `../openapi-sdk.json`). This file re-exports
|
|
399
|
+
* the curated subset the SDK user interacts with, plus a few hand-written
|
|
400
|
+
* helper types where OpenAPI generation is awkward.
|
|
401
|
+
*
|
|
402
|
+
* We preserve the server's `snake_case` field names on response objects
|
|
403
|
+
* to match the wire format byte-for-byte — same philosophy as the Python
|
|
404
|
+
* SDK, which keeps Pydantic aliases and populates fields from the raw
|
|
405
|
+
* JSON directly.
|
|
406
|
+
*/
|
|
407
|
+
|
|
408
|
+
type CountryInfo = components["schemas"]["CountryInfo"];
|
|
409
|
+
type JurisdictionInfo = components["schemas"]["JurisdictionInfo"];
|
|
410
|
+
type Commit = components["schemas"]["Commit"];
|
|
411
|
+
type CommitsResponse = components["schemas"]["CommitsResponse"];
|
|
412
|
+
type LawAtCommitResponse = components["schemas"]["LawAtCommitResponse"];
|
|
413
|
+
type LawDetail = components["schemas"]["LawDetail"];
|
|
414
|
+
type LawMeta = components["schemas"]["LawMeta"];
|
|
415
|
+
type LawSearchResult = components["schemas"]["LawSearchResult"];
|
|
416
|
+
type PaginatedLaws = components["schemas"]["PaginatedLaws"];
|
|
417
|
+
type Reform = components["schemas"]["Reform"];
|
|
418
|
+
type ReformsResponse = components["schemas"]["ReformsResponse"];
|
|
419
|
+
type StatsResponse = components["schemas"]["StatsResponse"];
|
|
420
|
+
type ApiValidationError = components["schemas"]["ValidationError"];
|
|
421
|
+
type HTTPValidationError = components["schemas"]["HTTPValidationError"];
|
|
422
|
+
type WebhookEndpointCreate = components["schemas"]["WebhookEndpointCreate"];
|
|
423
|
+
type WebhookEndpointUpdate = components["schemas"]["WebhookEndpointUpdate"];
|
|
424
|
+
/**
|
|
425
|
+
* Law listing sort options.
|
|
426
|
+
*
|
|
427
|
+
* The OpenAPI schema types this as `string` but the server enforces a
|
|
428
|
+
* specific allowed set. We keep it open (`string`) to tolerate server
|
|
429
|
+
* additions without bumping the SDK, but document the current values.
|
|
430
|
+
*/
|
|
431
|
+
type LawSort = "publication_date" | "-publication_date" | "last_updated" | "-last_updated" | "title" | "-title" | (string & {});
|
|
432
|
+
/** Options shared by `laws.list` and `laws.iter` filter surfaces. */
|
|
433
|
+
interface LawFilterOptions {
|
|
434
|
+
lawType?: string | string[];
|
|
435
|
+
year?: number;
|
|
436
|
+
status?: string;
|
|
437
|
+
jurisdiction?: string;
|
|
438
|
+
fromDate?: string;
|
|
439
|
+
toDate?: string;
|
|
440
|
+
sort?: LawSort;
|
|
441
|
+
}
|
|
442
|
+
interface LawListOptions extends LawFilterOptions {
|
|
443
|
+
page?: number;
|
|
444
|
+
perPage?: number;
|
|
445
|
+
}
|
|
446
|
+
interface LawSearchOptions extends LawFilterOptions {
|
|
447
|
+
page?: number;
|
|
448
|
+
perPage?: number;
|
|
449
|
+
}
|
|
450
|
+
interface LawIterOptions extends LawFilterOptions {
|
|
451
|
+
perPage?: number;
|
|
452
|
+
limit?: number;
|
|
453
|
+
}
|
|
454
|
+
interface ReformListOptions {
|
|
455
|
+
limit?: number;
|
|
456
|
+
offset?: number;
|
|
457
|
+
}
|
|
458
|
+
interface ReformIterOptions {
|
|
459
|
+
batch?: number;
|
|
460
|
+
limit?: number;
|
|
461
|
+
}
|
|
462
|
+
interface StatsOptions {
|
|
463
|
+
jurisdiction?: string;
|
|
464
|
+
}
|
|
465
|
+
/** Webhook endpoint CRUD types — server-side response. */
|
|
466
|
+
interface WebhookEndpoint {
|
|
467
|
+
id: number;
|
|
468
|
+
url: string;
|
|
469
|
+
event_types: string[];
|
|
470
|
+
countries?: string[] | null;
|
|
471
|
+
description?: string;
|
|
472
|
+
enabled?: boolean;
|
|
473
|
+
created_at?: string;
|
|
474
|
+
/** Only populated on creation, never on list/retrieve. */
|
|
475
|
+
secret?: string;
|
|
476
|
+
[key: string]: unknown;
|
|
477
|
+
}
|
|
478
|
+
interface WebhookDeliveriesPage {
|
|
479
|
+
total: number;
|
|
480
|
+
deliveries: Array<Record<string, unknown>>;
|
|
481
|
+
[key: string]: unknown;
|
|
482
|
+
}
|
|
483
|
+
interface WebhookDelivery {
|
|
484
|
+
id: number;
|
|
485
|
+
status: string;
|
|
486
|
+
[key: string]: unknown;
|
|
487
|
+
}
|
|
488
|
+
type WebhookDeliveryStatus = "failed" | "success" | "pending";
|
|
489
|
+
interface WebhookCreateOptions {
|
|
490
|
+
url: string;
|
|
491
|
+
eventTypes: string[];
|
|
492
|
+
countries?: string[];
|
|
493
|
+
description?: string;
|
|
494
|
+
}
|
|
495
|
+
interface WebhookUpdateOptions {
|
|
496
|
+
url?: string;
|
|
497
|
+
eventTypes?: string[];
|
|
498
|
+
countries?: string[];
|
|
499
|
+
description?: string;
|
|
500
|
+
enabled?: boolean;
|
|
501
|
+
}
|
|
502
|
+
interface WebhookDeliveriesOptions {
|
|
503
|
+
page?: number;
|
|
504
|
+
status?: WebhookDeliveryStatus;
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
/** `/api/v1/countries` — list every country the API serves. */
|
|
508
|
+
|
|
509
|
+
declare class Countries {
|
|
510
|
+
private readonly client;
|
|
511
|
+
constructor(client: Legalize);
|
|
512
|
+
/** Return every country the API serves, with law counts. */
|
|
513
|
+
list(options?: {
|
|
514
|
+
signal?: AbortSignal;
|
|
515
|
+
}): Promise<CountryInfo[]>;
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
/** `/api/v1/{country}/jurisdictions` — regions/states within a country. */
|
|
519
|
+
|
|
520
|
+
declare class Jurisdictions {
|
|
521
|
+
private readonly client;
|
|
522
|
+
constructor(client: Legalize);
|
|
523
|
+
/** List jurisdictions for a country (e.g. Spain's comunidades). */
|
|
524
|
+
list(country: string, options?: {
|
|
525
|
+
signal?: AbortSignal;
|
|
526
|
+
}): Promise<JurisdictionInfo[]>;
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
/**
|
|
530
|
+
* `/api/v1/{country}/laws` and sub-resources.
|
|
531
|
+
*
|
|
532
|
+
* Covers:
|
|
533
|
+
* - `list` / `search` — listing vs. full-text search
|
|
534
|
+
* - `iter` / `searchIter` — auto-paginated async iterators
|
|
535
|
+
* - `retrieve` — full law with Markdown content
|
|
536
|
+
* - `meta` — metadata only (fast, no GitHub fetch)
|
|
537
|
+
* - `commits` — git commit history
|
|
538
|
+
* - `atCommit` — time-travel to a specific SHA
|
|
539
|
+
*/
|
|
540
|
+
|
|
541
|
+
declare class Laws {
|
|
542
|
+
private readonly client;
|
|
543
|
+
constructor(client: Legalize);
|
|
544
|
+
/** Return a single page of laws for a country. */
|
|
545
|
+
list(country: string, options?: LawListOptions & {
|
|
546
|
+
signal?: AbortSignal;
|
|
547
|
+
}): Promise<PaginatedLaws>;
|
|
548
|
+
/** Full-text search for laws. `q` is required. */
|
|
549
|
+
search(country: string, q: string, options?: LawSearchOptions & {
|
|
550
|
+
signal?: AbortSignal;
|
|
551
|
+
}): Promise<PaginatedLaws>;
|
|
552
|
+
/** Auto-paginate across every matching law. */
|
|
553
|
+
iter(country: string, options?: LawIterOptions & {
|
|
554
|
+
signal?: AbortSignal;
|
|
555
|
+
}): AsyncIterableIterator<LawSearchResult>;
|
|
556
|
+
/** Auto-paginate across every match of a full-text search. */
|
|
557
|
+
searchIter(country: string, q: string, options?: LawIterOptions & {
|
|
558
|
+
signal?: AbortSignal;
|
|
559
|
+
}): AsyncIterableIterator<LawSearchResult>;
|
|
560
|
+
/** Fetch the full law including Markdown content. */
|
|
561
|
+
retrieve(country: string, lawId: string, options?: {
|
|
562
|
+
signal?: AbortSignal;
|
|
563
|
+
}): Promise<LawDetail>;
|
|
564
|
+
/** Fetch only the law metadata (no content). */
|
|
565
|
+
meta(country: string, lawId: string, options?: {
|
|
566
|
+
signal?: AbortSignal;
|
|
567
|
+
}): Promise<LawMeta>;
|
|
568
|
+
/** Git commit history for the law. */
|
|
569
|
+
commits(country: string, lawId: string, options?: {
|
|
570
|
+
signal?: AbortSignal;
|
|
571
|
+
}): Promise<CommitsResponse>;
|
|
572
|
+
/** Return the law's full text at a specific historical version. */
|
|
573
|
+
atCommit(country: string, lawId: string, sha: string, options?: {
|
|
574
|
+
signal?: AbortSignal;
|
|
575
|
+
}): Promise<LawAtCommitResponse>;
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
/** `/api/v1/{country}/law-types` — law types per country. */
|
|
579
|
+
|
|
580
|
+
declare class LawTypes {
|
|
581
|
+
private readonly client;
|
|
582
|
+
constructor(client: Legalize);
|
|
583
|
+
/** List law type identifiers (e.g. `["constitucion", "ley", "real_decreto"]`). */
|
|
584
|
+
list(country: string, options?: {
|
|
585
|
+
signal?: AbortSignal;
|
|
586
|
+
}): Promise<string[]>;
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
/** `/api/v1/{country}/laws/{law_id}/reforms` — reform history. */
|
|
590
|
+
|
|
591
|
+
declare class Reforms {
|
|
592
|
+
private readonly client;
|
|
593
|
+
constructor(client: Legalize);
|
|
594
|
+
/** Return a single page of reforms for a law. */
|
|
595
|
+
list(country: string, lawId: string, options?: ReformListOptions & {
|
|
596
|
+
signal?: AbortSignal;
|
|
597
|
+
}): Promise<ReformsResponse>;
|
|
598
|
+
/** Auto-paginate across every reform for a law. */
|
|
599
|
+
iter(country: string, lawId: string, options?: ReformIterOptions & {
|
|
600
|
+
signal?: AbortSignal;
|
|
601
|
+
}): AsyncIterableIterator<Reform>;
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
/** `/api/v1/{country}/stats` — aggregate statistics for a country. */
|
|
605
|
+
|
|
606
|
+
declare class Stats {
|
|
607
|
+
private readonly client;
|
|
608
|
+
constructor(client: Legalize);
|
|
609
|
+
/** Return aggregate stats for a country (and optionally a jurisdiction). */
|
|
610
|
+
retrieve(country: string, options?: StatsOptions & {
|
|
611
|
+
signal?: AbortSignal;
|
|
612
|
+
}): Promise<StatsResponse>;
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
/**
|
|
616
|
+
* `/api/v1/webhooks` — endpoint management + delivery history + test ping.
|
|
617
|
+
*
|
|
618
|
+
* The management endpoints (this file) require Pro+ tier. The webhook
|
|
619
|
+
* signature verification utility lives in `webhooks.ts` at the package
|
|
620
|
+
* root — it runs on the recipient's server and doesn't touch this API.
|
|
621
|
+
*/
|
|
622
|
+
|
|
623
|
+
declare class Webhooks {
|
|
624
|
+
private readonly client;
|
|
625
|
+
constructor(client: Legalize);
|
|
626
|
+
/** Create a webhook endpoint. Returns the signing secret ONCE. */
|
|
627
|
+
create(options: WebhookCreateOptions & {
|
|
628
|
+
signal?: AbortSignal;
|
|
629
|
+
}): Promise<WebhookEndpoint>;
|
|
630
|
+
/** List all webhook endpoints for the authenticated org. */
|
|
631
|
+
list(options?: {
|
|
632
|
+
signal?: AbortSignal;
|
|
633
|
+
}): Promise<WebhookEndpoint[]>;
|
|
634
|
+
/** Fetch a single endpoint by id. */
|
|
635
|
+
retrieve(endpointId: number, options?: {
|
|
636
|
+
signal?: AbortSignal;
|
|
637
|
+
}): Promise<WebhookEndpoint>;
|
|
638
|
+
/** Patch mutable fields on a webhook endpoint. */
|
|
639
|
+
update(endpointId: number, options: WebhookUpdateOptions & {
|
|
640
|
+
signal?: AbortSignal;
|
|
641
|
+
}): Promise<WebhookEndpoint>;
|
|
642
|
+
/** Delete a webhook endpoint. */
|
|
643
|
+
delete(endpointId: number, options?: {
|
|
644
|
+
signal?: AbortSignal;
|
|
645
|
+
}): Promise<Record<string, unknown>>;
|
|
646
|
+
/** List delivery attempts for an endpoint, optionally filtered by status. */
|
|
647
|
+
deliveries(endpointId: number, options?: WebhookDeliveriesOptions & {
|
|
648
|
+
signal?: AbortSignal;
|
|
649
|
+
}): Promise<WebhookDeliveriesPage>;
|
|
650
|
+
/** Retry a failed delivery. */
|
|
651
|
+
retry(endpointId: number, deliveryId: number, options?: {
|
|
652
|
+
signal?: AbortSignal;
|
|
653
|
+
}): Promise<WebhookDelivery>;
|
|
654
|
+
/** Send a `test.ping` event to verify the endpoint is reachable. */
|
|
655
|
+
test(endpointId: number, options?: {
|
|
656
|
+
signal?: AbortSignal;
|
|
657
|
+
}): Promise<WebhookDelivery>;
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
/**
|
|
661
|
+
* HTTP client core — the `Legalize` class.
|
|
662
|
+
*
|
|
663
|
+
* Wraps the built-in `fetch` with:
|
|
664
|
+
* - auth + identifying headers on every request
|
|
665
|
+
* - query-param cleanup matching the Python reference
|
|
666
|
+
* - retry policy with exponential backoff (see `retry.ts`)
|
|
667
|
+
* - error hierarchy mapping (see `errors.ts`)
|
|
668
|
+
* - per-request AbortSignal support
|
|
669
|
+
* - `lastResponse` populated on success AND error, for inspecting
|
|
670
|
+
* rate-limit headers and X-Request-Id after failures
|
|
671
|
+
*
|
|
672
|
+
* Design notes:
|
|
673
|
+
* - No runtime deps. Node 20+ ships `fetch`, `AbortController`, and
|
|
674
|
+
* `crypto.webcrypto` — we use only those.
|
|
675
|
+
* - `follow_redirects` is effectively false: the server doesn't
|
|
676
|
+
* redirect, and silently following one would hide misconfigurations.
|
|
677
|
+
* `fetch` supports "manual" redirect mode so we use it.
|
|
678
|
+
*/
|
|
679
|
+
|
|
680
|
+
/** Compose the User-Agent the SDK sends with every request. */
|
|
681
|
+
declare function defaultUserAgent(): string;
|
|
682
|
+
/** The fetch implementation the client uses. Global by default; overridable. */
|
|
683
|
+
type FetchImpl = typeof fetch;
|
|
684
|
+
interface LegalizeOptions {
|
|
685
|
+
apiKey?: string;
|
|
686
|
+
baseUrl?: string;
|
|
687
|
+
apiVersion?: string;
|
|
688
|
+
/** Timeout in milliseconds. Default 30000. */
|
|
689
|
+
timeout?: number;
|
|
690
|
+
maxRetries?: number;
|
|
691
|
+
retry?: RetryPolicy;
|
|
692
|
+
defaultHeaders?: Record<string, string>;
|
|
693
|
+
/** Override fetch — primarily for tests. */
|
|
694
|
+
fetch?: FetchImpl;
|
|
695
|
+
}
|
|
696
|
+
interface RequestOptions {
|
|
697
|
+
params?: Record<string, unknown>;
|
|
698
|
+
json?: unknown;
|
|
699
|
+
extraHeaders?: Record<string, string>;
|
|
700
|
+
signal?: AbortSignal;
|
|
701
|
+
/**
|
|
702
|
+
* Override the retry policy's idempotency check for this call.
|
|
703
|
+
* POST/PATCH are NOT retried by default.
|
|
704
|
+
*/
|
|
705
|
+
idempotencyKey?: string;
|
|
706
|
+
}
|
|
707
|
+
/**
|
|
708
|
+
* Synchronous-API, promise-based client for the Legalize API.
|
|
709
|
+
*
|
|
710
|
+
* Example:
|
|
711
|
+
*
|
|
712
|
+
* import { Legalize } from "@legalize-dev/sdk";
|
|
713
|
+
*
|
|
714
|
+
* const client = new Legalize({ apiKey: "leg_..." });
|
|
715
|
+
* const countries = await client.countries.list();
|
|
716
|
+
*
|
|
717
|
+
* Use `await using` (TS 5.2+) for deterministic cleanup:
|
|
718
|
+
*
|
|
719
|
+
* await using client = new Legalize({ apiKey: "leg_..." });
|
|
720
|
+
*/
|
|
721
|
+
declare class Legalize {
|
|
722
|
+
readonly countries: Countries;
|
|
723
|
+
readonly jurisdictions: Jurisdictions;
|
|
724
|
+
readonly lawTypes: LawTypes;
|
|
725
|
+
readonly laws: Laws;
|
|
726
|
+
readonly reforms: Reforms;
|
|
727
|
+
readonly stats: Stats;
|
|
728
|
+
readonly webhooks: Webhooks;
|
|
729
|
+
/** Exposed for tests; treat as private otherwise. */
|
|
730
|
+
readonly _apiKey: string;
|
|
731
|
+
readonly _baseUrl: string;
|
|
732
|
+
readonly _apiVersion: string;
|
|
733
|
+
readonly _headers: Record<string, string>;
|
|
734
|
+
private readonly _timeout;
|
|
735
|
+
private readonly _retry;
|
|
736
|
+
private readonly _fetch;
|
|
737
|
+
private _lastResponse;
|
|
738
|
+
constructor(options?: LegalizeOptions);
|
|
739
|
+
/** The raw HTTP response from the most recent request, or null. */
|
|
740
|
+
get lastResponse(): Response | null;
|
|
741
|
+
/**
|
|
742
|
+
* Execute a request and return the parsed JSON body (or null for 204).
|
|
743
|
+
*
|
|
744
|
+
* Throws an APIError subclass on non-2xx responses (after retries),
|
|
745
|
+
* APITimeoutError on timeout, or APIConnectionError on transport
|
|
746
|
+
* failure.
|
|
747
|
+
*/
|
|
748
|
+
request<T = unknown>(method: string, path: string, options?: RequestOptions): Promise<T>;
|
|
749
|
+
/** Release any resources held by the client. Kept for API symmetry. */
|
|
750
|
+
close(): Promise<void>;
|
|
751
|
+
/** TS 5.2+ `using` / `await using` support. */
|
|
752
|
+
[Symbol.asyncDispose](): Promise<void>;
|
|
753
|
+
private buildUrl;
|
|
754
|
+
private sendOnce;
|
|
755
|
+
}
|
|
756
|
+
/**
|
|
757
|
+
* Serialize query params matching the Python reference:
|
|
758
|
+
* - undefined/null → dropped.
|
|
759
|
+
* - booleans → "true" / "false".
|
|
760
|
+
* - arrays → comma-joined; empty arrays dropped.
|
|
761
|
+
* - everything else → String(value).
|
|
762
|
+
*/
|
|
763
|
+
declare function buildQueryString(params: Record<string, unknown>): string;
|
|
764
|
+
|
|
765
|
+
/**
|
|
766
|
+
* Error hierarchy for the Legalize SDK.
|
|
767
|
+
*
|
|
768
|
+
* The server returns three response shapes:
|
|
769
|
+
*
|
|
770
|
+
* 1. Structured dict from the API layer:
|
|
771
|
+
* { "detail": { "error": "quota_exceeded", "message": "...", "retry_after": 3600, ... } }
|
|
772
|
+
*
|
|
773
|
+
* 2. FastAPI validation errors (422):
|
|
774
|
+
* { "detail": [{ "loc": [...], "msg": "...", "type": "..." }, ...] }
|
|
775
|
+
*
|
|
776
|
+
* 3. Plain string detail for simple 404/400:
|
|
777
|
+
* { "detail": "Law not found: xyz" }
|
|
778
|
+
*
|
|
779
|
+
* `APIError.fromResponse` normalizes all three into an instance of the
|
|
780
|
+
* most specific subclass, keeping the raw body on `.body` and the parsed
|
|
781
|
+
* payload on `.data`.
|
|
782
|
+
*/
|
|
783
|
+
/** Base error for everything the SDK raises. */
|
|
784
|
+
declare class LegalizeError extends Error {
|
|
785
|
+
readonly name: string;
|
|
786
|
+
constructor(message: string, options?: {
|
|
787
|
+
cause?: unknown;
|
|
788
|
+
});
|
|
789
|
+
}
|
|
790
|
+
interface APIErrorOptions {
|
|
791
|
+
message: string;
|
|
792
|
+
statusCode?: number;
|
|
793
|
+
code?: string | undefined;
|
|
794
|
+
body?: unknown;
|
|
795
|
+
data?: unknown;
|
|
796
|
+
requestId?: string | undefined;
|
|
797
|
+
response?: Response | undefined;
|
|
798
|
+
cause?: unknown;
|
|
799
|
+
}
|
|
800
|
+
/** Any non-2xx HTTP response. */
|
|
801
|
+
declare class APIError extends LegalizeError {
|
|
802
|
+
readonly name: string;
|
|
803
|
+
readonly statusCode: number | undefined;
|
|
804
|
+
readonly code: string | undefined;
|
|
805
|
+
readonly body: unknown;
|
|
806
|
+
readonly data: unknown;
|
|
807
|
+
readonly requestId: string | undefined;
|
|
808
|
+
readonly response: Response | undefined;
|
|
809
|
+
constructor(options: APIErrorOptions);
|
|
810
|
+
toString(): string;
|
|
811
|
+
/**
|
|
812
|
+
* Build the most specific APIError subclass for a response.
|
|
813
|
+
*
|
|
814
|
+
* `body` is the raw bytes of the response body (or the parsed JSON
|
|
815
|
+
* object if already consumed). `data` is the parsed JSON, which
|
|
816
|
+
* drives error-code dispatch. Reads `X-Request-Id` for support.
|
|
817
|
+
*/
|
|
818
|
+
static fromResponse(response: Response, body: unknown, data?: unknown): APIError;
|
|
819
|
+
}
|
|
820
|
+
declare class AuthenticationError extends APIError {
|
|
821
|
+
readonly name = "AuthenticationError";
|
|
822
|
+
}
|
|
823
|
+
declare class ForbiddenError extends APIError {
|
|
824
|
+
readonly name = "ForbiddenError";
|
|
825
|
+
}
|
|
826
|
+
declare class NotFoundError extends APIError {
|
|
827
|
+
readonly name = "NotFoundError";
|
|
828
|
+
}
|
|
829
|
+
declare class InvalidRequestError extends APIError {
|
|
830
|
+
readonly name = "InvalidRequestError";
|
|
831
|
+
}
|
|
832
|
+
declare class ValidationError extends APIError {
|
|
833
|
+
readonly name = "ValidationError";
|
|
834
|
+
readonly errors: Array<Record<string, unknown>>;
|
|
835
|
+
}
|
|
836
|
+
declare class RateLimitError extends APIError {
|
|
837
|
+
readonly name = "RateLimitError";
|
|
838
|
+
readonly retryAfter: number | undefined;
|
|
839
|
+
readonly limit: number | undefined;
|
|
840
|
+
}
|
|
841
|
+
declare class ServerError extends APIError {
|
|
842
|
+
readonly name: string;
|
|
843
|
+
}
|
|
844
|
+
declare class ServiceUnavailableError extends ServerError {
|
|
845
|
+
readonly name: string;
|
|
846
|
+
}
|
|
847
|
+
/** Transport failure, no response. */
|
|
848
|
+
declare class APIConnectionError extends LegalizeError {
|
|
849
|
+
readonly name: string;
|
|
850
|
+
}
|
|
851
|
+
/** Request exceeded timeout. */
|
|
852
|
+
declare class APITimeoutError extends APIConnectionError {
|
|
853
|
+
readonly name: string;
|
|
854
|
+
}
|
|
855
|
+
type WebhookVerificationReason = "missing_header" | "bad_timestamp" | "timestamp_outside_tolerance" | "no_valid_signature" | "bad_signature";
|
|
856
|
+
/**
|
|
857
|
+
* Raised by Webhook.verify on any signature or timestamp failure.
|
|
858
|
+
*
|
|
859
|
+
* The message is intentionally generic so that an attacker probing the
|
|
860
|
+
* endpoint can't distinguish "bad signature" from "stale timestamp". The
|
|
861
|
+
* specific reason is available on `.reason` for server-side logging.
|
|
862
|
+
*/
|
|
863
|
+
declare class WebhookVerificationError extends LegalizeError {
|
|
864
|
+
readonly name: string;
|
|
865
|
+
readonly reason: WebhookVerificationReason;
|
|
866
|
+
constructor(reason: WebhookVerificationReason);
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
/**
|
|
870
|
+
* Pagination helpers — async iterators over page- and offset-based endpoints.
|
|
871
|
+
*
|
|
872
|
+
* The Legalize API uses two pagination styles:
|
|
873
|
+
*
|
|
874
|
+
* - page + perPage — laws list, webhook deliveries
|
|
875
|
+
* - limit + offset — reforms
|
|
876
|
+
*
|
|
877
|
+
* Each paginated response carries `total` (true match count). That lets
|
|
878
|
+
* iterators terminate without inferring end-of-stream.
|
|
879
|
+
*
|
|
880
|
+
* The iterators here are transport-agnostic: they delegate fetching to
|
|
881
|
+
* a caller-supplied async callback.
|
|
882
|
+
*/
|
|
883
|
+
declare const PAGE_MAX = 100;
|
|
884
|
+
/** Async iterator over a page-based endpoint (page + perPage). */
|
|
885
|
+
declare class PageIterator<T> implements AsyncIterableIterator<T> {
|
|
886
|
+
private readonly fetchPage;
|
|
887
|
+
private readonly perPage;
|
|
888
|
+
private readonly limit;
|
|
889
|
+
private page;
|
|
890
|
+
private yielded;
|
|
891
|
+
private buffer;
|
|
892
|
+
private bufferIdx;
|
|
893
|
+
private total;
|
|
894
|
+
private shortPage;
|
|
895
|
+
constructor(fetchPage: (page: number, perPage: number) => Promise<[T[], number]>, options?: {
|
|
896
|
+
perPage?: number;
|
|
897
|
+
limit?: number;
|
|
898
|
+
startPage?: number;
|
|
899
|
+
});
|
|
900
|
+
[Symbol.asyncIterator](): AsyncIterableIterator<T>;
|
|
901
|
+
next(): Promise<IteratorResult<T>>;
|
|
902
|
+
}
|
|
903
|
+
/** Async iterator over a limit/offset-based endpoint (reforms). */
|
|
904
|
+
declare class OffsetIterator<T> implements AsyncIterableIterator<T> {
|
|
905
|
+
private readonly fetchPage;
|
|
906
|
+
private readonly batch;
|
|
907
|
+
private readonly limit;
|
|
908
|
+
private offset;
|
|
909
|
+
private yielded;
|
|
910
|
+
private buffer;
|
|
911
|
+
private bufferIdx;
|
|
912
|
+
private done;
|
|
913
|
+
constructor(fetchPage: (limit: number, offset: number) => Promise<[T[], number]>, options?: {
|
|
914
|
+
batch?: number;
|
|
915
|
+
limit?: number;
|
|
916
|
+
startOffset?: number;
|
|
917
|
+
});
|
|
918
|
+
[Symbol.asyncIterator](): AsyncIterableIterator<T>;
|
|
919
|
+
next(): Promise<IteratorResult<T>>;
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
/**
|
|
923
|
+
* Webhook signature verification.
|
|
924
|
+
*
|
|
925
|
+
* Mirrors the server-side signer in the Legalize web backend. The scheme
|
|
926
|
+
* is Stripe-shaped:
|
|
927
|
+
*
|
|
928
|
+
* - Signed content: `${timestamp}.${rawJsonBody}` — RAW bytes, never
|
|
929
|
+
* a re-serialized JSON string. Reserialization will break the signature.
|
|
930
|
+
* - Algorithm: HMAC-SHA256, hex-encoded.
|
|
931
|
+
* - Header format: `v1=<hex>`. A future v2 scheme can coexist:
|
|
932
|
+
* `X-Legalize-Signature: v1=<sig1>,v2=<sig2>`.
|
|
933
|
+
* - Replay protection: reject if the header timestamp is more than
|
|
934
|
+
* `tolerance` seconds away from `now`. Default 300 seconds.
|
|
935
|
+
*
|
|
936
|
+
* Usage (Express):
|
|
937
|
+
*
|
|
938
|
+
* import express from "express";
|
|
939
|
+
* import { Webhook, WebhookVerificationError } from "@legalize-dev/sdk";
|
|
940
|
+
*
|
|
941
|
+
* app.post("/webhooks/legalize",
|
|
942
|
+
* express.raw({ type: "application/json" }),
|
|
943
|
+
* (req, res) => {
|
|
944
|
+
* try {
|
|
945
|
+
* const event = Webhook.verify({
|
|
946
|
+
* payload: req.body, // Buffer
|
|
947
|
+
* sigHeader: req.header("X-Legalize-Signature") ?? "",
|
|
948
|
+
* timestamp: req.header("X-Legalize-Timestamp") ?? "",
|
|
949
|
+
* secret: process.env.LEGALIZE_WHSEC!,
|
|
950
|
+
* });
|
|
951
|
+
* if (event.type === "law.updated") { ... }
|
|
952
|
+
* res.status(204).send();
|
|
953
|
+
* } catch (err) {
|
|
954
|
+
* res.status(400).send();
|
|
955
|
+
* }
|
|
956
|
+
* });
|
|
957
|
+
*/
|
|
958
|
+
declare const DEFAULT_TOLERANCE_SECONDS = 300;
|
|
959
|
+
interface WebhookEvent {
|
|
960
|
+
/** Server-assigned event id (e.g. `evt_...`). */
|
|
961
|
+
readonly id: string;
|
|
962
|
+
/** Event type (`law.created`, `law.updated`, `law.repealed`, ...). */
|
|
963
|
+
readonly type: string;
|
|
964
|
+
/** Server-side timestamp (ISO-8601 string). */
|
|
965
|
+
readonly createdAt: string;
|
|
966
|
+
/** Event-specific payload body. */
|
|
967
|
+
readonly data: Record<string, unknown>;
|
|
968
|
+
/** The full decoded JSON body — useful for fields the SDK doesn't type. */
|
|
969
|
+
readonly raw: Record<string, unknown>;
|
|
970
|
+
}
|
|
971
|
+
interface WebhookVerifyOptions {
|
|
972
|
+
/** The raw request body bytes. Do NOT pass a re-serialized JSON string. */
|
|
973
|
+
payload: Buffer | Uint8Array | string;
|
|
974
|
+
/** The `X-Legalize-Signature` header value. May contain several `vN=<hex>` pairs. */
|
|
975
|
+
sigHeader: string;
|
|
976
|
+
/** The `X-Legalize-Timestamp` header value (Unix seconds, decimal string). */
|
|
977
|
+
timestamp: string;
|
|
978
|
+
/** The endpoint's signing secret. */
|
|
979
|
+
secret: string;
|
|
980
|
+
/** Seconds of clock skew accepted. Default 300. */
|
|
981
|
+
tolerance?: number;
|
|
982
|
+
/** Unit-test hook: override the reference wall clock (Unix seconds). */
|
|
983
|
+
now?: number;
|
|
984
|
+
}
|
|
985
|
+
declare class Webhook {
|
|
986
|
+
/** Default anti-replay tolerance. */
|
|
987
|
+
static readonly TOLERANCE = 300;
|
|
988
|
+
/** Compute the canonical `v1=<hex>` signature for (payload, timestamp). */
|
|
989
|
+
static computeSignature(secret: string, payload: Buffer | Uint8Array | string, timestamp: string): string;
|
|
990
|
+
/**
|
|
991
|
+
* Verify a webhook delivery and return the parsed event.
|
|
992
|
+
*
|
|
993
|
+
* Signature verification happens BEFORE JSON parsing, to protect the
|
|
994
|
+
* process from resource-exhaustion on unauthenticated bodies.
|
|
995
|
+
*
|
|
996
|
+
* Throws WebhookVerificationError on any failure. The `.reason`
|
|
997
|
+
* field identifies which check tripped for server-side logging,
|
|
998
|
+
* while the user-facing message stays generic.
|
|
999
|
+
*/
|
|
1000
|
+
static verify(options: WebhookVerifyOptions): WebhookEvent;
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
/**
|
|
1004
|
+
* SDK version — MUST be kept in sync with the "version" field in package.json.
|
|
1005
|
+
*
|
|
1006
|
+
* The Node runtime doesn't resolve JSON imports cleanly across ESM/CJS dual
|
|
1007
|
+
* output without extra wiring, so we duplicate the literal here. The test
|
|
1008
|
+
* suite asserts they match so drift is caught immediately.
|
|
1009
|
+
*/
|
|
1010
|
+
declare const SDK_VERSION = "0.1.0";
|
|
1011
|
+
|
|
1012
|
+
export { APIConnectionError, APIError, type APIErrorOptions, APITimeoutError, type ApiValidationError, AuthenticationError, type Commit, type CommitsResponse, Countries, type CountryInfo, DEFAULT_API_VERSION, DEFAULT_BACKOFF_FACTOR, DEFAULT_BASE_URL, DEFAULT_INITIAL_DELAY, DEFAULT_MAX_DELAY, DEFAULT_MAX_RETRIES, DEFAULT_TIMEOUT, DEFAULT_TOLERANCE_SECONDS, type FetchImpl, ForbiddenError, type HTTPValidationError, IDEMPOTENT_METHODS, InvalidRequestError, type JurisdictionInfo, Jurisdictions, KEY_PREFIX, type LawAtCommitResponse, type LawDetail, type LawFilterOptions, type LawIterOptions, type LawListOptions, type LawMeta, type LawSearchOptions, type LawSearchResult, type LawSort, LawTypes, Laws, Legalize, LegalizeError, type LegalizeOptions, NotFoundError, OffsetIterator, PAGE_MAX, PageIterator, type PaginatedLaws, RETRY_STATUSES, RateLimitError, type Reform, type ReformIterOptions, type ReformListOptions, Reforms, type ReformsResponse, type RequestOptions, RetryPolicy, type RetryPolicyOptions, SDK_VERSION, ServerError, ServiceUnavailableError, Stats, type StatsOptions, type StatsResponse, ValidationError, Webhook, type WebhookCreateOptions, type WebhookDeliveriesOptions, type WebhookDeliveriesPage, type WebhookDelivery, type WebhookDeliveryStatus, type WebhookEndpoint, type WebhookEndpointCreate, type WebhookEndpointUpdate, type WebhookEvent, type WebhookUpdateOptions, WebhookVerificationError, type WebhookVerificationReason, type WebhookVerifyOptions, Webhooks, buildQueryString, defaultUserAgent, parseRetryAfter, resolveApiKey, resolveApiVersion, resolveBaseUrl, sleep };
|