burnledger 0.2.2 → 0.3.1
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/LICENSE +21 -0
- package/README.md +1 -1
- package/dist/cjs/index.browser.d.ts +2 -1
- package/dist/cjs/index.browser.d.ts.map +1 -1
- package/dist/cjs/index.browser.js +7 -1
- package/dist/cjs/index.browser.js.map +1 -1
- package/dist/cjs/index.d.ts +9 -1
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +17 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/models.d.ts +28 -1
- package/dist/cjs/models.d.ts.map +1 -1
- package/dist/cjs/models.js +8 -0
- package/dist/cjs/models.js.map +1 -1
- package/dist/cjs/verify.d.ts +55 -1
- package/dist/cjs/verify.d.ts.map +1 -1
- package/dist/cjs/verify.js +414 -104
- package/dist/cjs/verify.js.map +1 -1
- package/dist/cjs/web-verifier.d.ts +29 -0
- package/dist/cjs/web-verifier.d.ts.map +1 -0
- package/dist/cjs/web-verifier.js +70 -0
- package/dist/cjs/web-verifier.js.map +1 -0
- package/dist/esm/cli.d.ts.map +1 -1
- package/dist/esm/cli.js +12 -5
- package/dist/esm/cli.js.map +1 -1
- package/dist/esm/index.browser.d.ts +2 -1
- package/dist/esm/index.browser.d.ts.map +1 -1
- package/dist/esm/index.browser.js +3 -0
- package/dist/esm/index.browser.js.map +1 -1
- package/dist/esm/index.d.ts +9 -1
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +13 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/models.d.ts +28 -1
- package/dist/esm/models.d.ts.map +1 -1
- package/dist/esm/models.js +8 -0
- package/dist/esm/models.js.map +1 -1
- package/dist/esm/verify.d.ts +55 -1
- package/dist/esm/verify.d.ts.map +1 -1
- package/dist/esm/verify.js +411 -105
- package/dist/esm/verify.js.map +1 -1
- package/dist/esm/web-verifier.d.ts +29 -0
- package/dist/esm/web-verifier.d.ts.map +1 -0
- package/dist/esm/web-verifier.js +63 -0
- package/dist/esm/web-verifier.js.map +1 -0
- package/package.json +12 -11
- package/src/cli.ts +207 -0
- package/src/client.ts +555 -0
- package/src/crypto-browser.ts +49 -0
- package/src/crypto-node.ts +40 -0
- package/src/crypto.ts +10 -0
- package/src/errors.ts +154 -0
- package/src/http.ts +209 -0
- package/src/index.browser.ts +110 -0
- package/src/index.ts +134 -0
- package/src/keys.ts +18 -0
- package/src/models.ts +558 -0
- package/src/pagination.ts +64 -0
- package/src/verify.ts +956 -0
- package/src/web-verifier.ts +89 -0
- package/src/webhooks.ts +76 -0
package/src/errors.ts
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/** Exception hierarchy for the BurnLedger SDK.
|
|
2
|
+
*
|
|
3
|
+
* Maps HTTP status codes to typed errors. Every error includes
|
|
4
|
+
* enough context for log correlation.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export class BurnLedgerError extends Error {
|
|
8
|
+
constructor(message: string) {
|
|
9
|
+
super(message);
|
|
10
|
+
this.name = "BurnLedgerError";
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export class ApiError extends BurnLedgerError {
|
|
15
|
+
readonly code: string;
|
|
16
|
+
readonly statusCode: number;
|
|
17
|
+
readonly requestId: string;
|
|
18
|
+
|
|
19
|
+
constructor(opts: {
|
|
20
|
+
code: string;
|
|
21
|
+
message: string;
|
|
22
|
+
requestId: string;
|
|
23
|
+
statusCode: number;
|
|
24
|
+
}) {
|
|
25
|
+
super(
|
|
26
|
+
`[${opts.statusCode}] ${opts.code}: ${opts.message} (request_id=${opts.requestId})`,
|
|
27
|
+
);
|
|
28
|
+
this.name = "ApiError";
|
|
29
|
+
this.code = opts.code;
|
|
30
|
+
this.statusCode = opts.statusCode;
|
|
31
|
+
this.requestId = opts.requestId;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export class AuthenticationError extends ApiError {
|
|
36
|
+
constructor(opts: { code: string; message: string; requestId: string }) {
|
|
37
|
+
super({ ...opts, statusCode: 401 });
|
|
38
|
+
this.name = "AuthenticationError";
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export class NotFoundError extends ApiError {
|
|
43
|
+
constructor(opts: { code: string; message: string; requestId: string }) {
|
|
44
|
+
super({ ...opts, statusCode: 404 });
|
|
45
|
+
this.name = "NotFoundError";
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export class ConflictError extends ApiError {
|
|
50
|
+
constructor(opts: { code: string; message: string; requestId: string }) {
|
|
51
|
+
super({ ...opts, statusCode: 409 });
|
|
52
|
+
this.name = "ConflictError";
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export class ValidationError extends ApiError {
|
|
57
|
+
constructor(opts: { code: string; message: string; requestId: string }) {
|
|
58
|
+
super({ ...opts, statusCode: 422 });
|
|
59
|
+
this.name = "ValidationError";
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export class RateLimitError extends ApiError {
|
|
64
|
+
readonly retryAfter: number | undefined;
|
|
65
|
+
|
|
66
|
+
constructor(opts: {
|
|
67
|
+
code: string;
|
|
68
|
+
message: string;
|
|
69
|
+
requestId: string;
|
|
70
|
+
retryAfter?: number;
|
|
71
|
+
}) {
|
|
72
|
+
super({ ...opts, statusCode: 429 });
|
|
73
|
+
this.name = "RateLimitError";
|
|
74
|
+
this.retryAfter = opts.retryAfter;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export class ServerError extends ApiError {
|
|
79
|
+
constructor(opts: {
|
|
80
|
+
code: string;
|
|
81
|
+
message: string;
|
|
82
|
+
requestId: string;
|
|
83
|
+
statusCode: number;
|
|
84
|
+
}) {
|
|
85
|
+
super(opts);
|
|
86
|
+
this.name = "ServerError";
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export class TimeoutError extends BurnLedgerError {
|
|
91
|
+
readonly operation: string;
|
|
92
|
+
readonly elapsed: number;
|
|
93
|
+
|
|
94
|
+
constructor(operation: string, elapsed: number) {
|
|
95
|
+
super(`${operation}: timed out after ${elapsed.toFixed(1)}s`);
|
|
96
|
+
this.name = "TimeoutError";
|
|
97
|
+
this.operation = operation;
|
|
98
|
+
this.elapsed = elapsed;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export class VerificationError extends BurnLedgerError {
|
|
103
|
+
readonly reason: string;
|
|
104
|
+
|
|
105
|
+
constructor(reason: string) {
|
|
106
|
+
super(reason);
|
|
107
|
+
this.name = "VerificationError";
|
|
108
|
+
this.reason = reason;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const STATUS_TO_CLASS: Record<
|
|
113
|
+
number,
|
|
114
|
+
new (opts: {
|
|
115
|
+
code: string;
|
|
116
|
+
message: string;
|
|
117
|
+
requestId: string;
|
|
118
|
+
retryAfter?: number;
|
|
119
|
+
}) => ApiError
|
|
120
|
+
> = {
|
|
121
|
+
401: AuthenticationError,
|
|
122
|
+
404: NotFoundError,
|
|
123
|
+
409: ConflictError,
|
|
124
|
+
422: ValidationError,
|
|
125
|
+
429: RateLimitError,
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
/** Parse an API error envelope and throw the appropriate exception. */
|
|
129
|
+
export function raiseForError(
|
|
130
|
+
statusCode: number,
|
|
131
|
+
body: Record<string, unknown>,
|
|
132
|
+
retryAfter?: number,
|
|
133
|
+
): never {
|
|
134
|
+
const err = (body.error ?? {}) as Record<string, unknown>;
|
|
135
|
+
const code = (err.code as string) ?? "UNKNOWN";
|
|
136
|
+
const message = (err.message as string) ?? "";
|
|
137
|
+
const requestId = (err.request_id as string) ?? "";
|
|
138
|
+
|
|
139
|
+
const Cls = STATUS_TO_CLASS[statusCode];
|
|
140
|
+
|
|
141
|
+
if (Cls === RateLimitError) {
|
|
142
|
+
throw new RateLimitError({ code, message, requestId, retryAfter });
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (Cls) {
|
|
146
|
+
throw new Cls({ code, message, requestId });
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (statusCode >= 500) {
|
|
150
|
+
throw new ServerError({ code, message, requestId, statusCode });
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
throw new ApiError({ code, message, requestId, statusCode });
|
|
154
|
+
}
|
package/src/http.ts
ADDED
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
/** HTTP transport layer wrapping native fetch.
|
|
2
|
+
*
|
|
3
|
+
* Handles: Bearer auth, response envelope unwrapping, retry on 5xx/transport
|
|
4
|
+
* errors, Retry-After respect on 429, and raw byte streaming for PDFs.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { raiseForError } from "./errors.js";
|
|
8
|
+
|
|
9
|
+
export interface TransportOptions {
|
|
10
|
+
baseUrl: string;
|
|
11
|
+
apiKey: string;
|
|
12
|
+
timeout: number;
|
|
13
|
+
maxRetries: number;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export class Transport {
|
|
17
|
+
private readonly baseUrl: string;
|
|
18
|
+
private readonly apiKey: string;
|
|
19
|
+
private readonly timeout: number;
|
|
20
|
+
private readonly maxRetries: number;
|
|
21
|
+
|
|
22
|
+
constructor(opts: TransportOptions) {
|
|
23
|
+
this.baseUrl = opts.baseUrl.replace(/\/+$/, "");
|
|
24
|
+
this.apiKey = opts.apiKey;
|
|
25
|
+
this.timeout = opts.timeout;
|
|
26
|
+
this.maxRetries = opts.maxRetries;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Make a request and return the unwrapped `data` field. */
|
|
30
|
+
async request(
|
|
31
|
+
method: string,
|
|
32
|
+
path: string,
|
|
33
|
+
opts?: {
|
|
34
|
+
json?: unknown;
|
|
35
|
+
params?: Record<string, string | number>;
|
|
36
|
+
authenticated?: boolean;
|
|
37
|
+
},
|
|
38
|
+
): Promise<unknown> {
|
|
39
|
+
const envelope = await this.requestRaw(method, path, opts);
|
|
40
|
+
return (envelope as Record<string, unknown>).data;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Make a request and return the full response envelope (for pagination). */
|
|
44
|
+
async requestRaw(
|
|
45
|
+
method: string,
|
|
46
|
+
path: string,
|
|
47
|
+
opts?: {
|
|
48
|
+
json?: unknown;
|
|
49
|
+
params?: Record<string, string | number>;
|
|
50
|
+
authenticated?: boolean;
|
|
51
|
+
},
|
|
52
|
+
): Promise<Record<string, unknown>> {
|
|
53
|
+
const authenticated = opts?.authenticated ?? true;
|
|
54
|
+
const headers: Record<string, string> = {
|
|
55
|
+
Accept: "application/json",
|
|
56
|
+
};
|
|
57
|
+
if (authenticated) {
|
|
58
|
+
headers["Authorization"] = `Bearer ${this.apiKey}`;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
let url = `${this.baseUrl}${path}`;
|
|
62
|
+
if (opts?.params) {
|
|
63
|
+
const qs = new URLSearchParams();
|
|
64
|
+
for (const [k, v] of Object.entries(opts.params)) {
|
|
65
|
+
qs.set(k, String(v));
|
|
66
|
+
}
|
|
67
|
+
url += `?${qs.toString()}`;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const body =
|
|
71
|
+
opts?.json !== undefined ? JSON.stringify(opts.json) : undefined;
|
|
72
|
+
if (body !== undefined) {
|
|
73
|
+
headers["Content-Type"] = "application/json";
|
|
74
|
+
}
|
|
75
|
+
if (method.toUpperCase() === "POST") {
|
|
76
|
+
// One key per logical call, reused across all retries of this call, so a
|
|
77
|
+
// 5xx/transport retry is deduplicated by the server instead of issuing a
|
|
78
|
+
// second certificate.
|
|
79
|
+
headers["Idempotency-Key"] = randomIdempotencyKey();
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
let lastError: Error | undefined;
|
|
83
|
+
|
|
84
|
+
for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
|
|
85
|
+
if (attempt > 0) {
|
|
86
|
+
// Backoff with ±25% jitter so coordinated 5xx events don't produce
|
|
87
|
+
// a synchronized retry storm from every client at the same instant.
|
|
88
|
+
const base = 500 * 2 ** (attempt - 1);
|
|
89
|
+
const jitter = base * 0.25 * (Math.random() * 2 - 1);
|
|
90
|
+
await sleep(base + jitter);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const controller = new AbortController();
|
|
94
|
+
const timer = setTimeout(
|
|
95
|
+
() => controller.abort(),
|
|
96
|
+
this.timeout * 1000,
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
let resp: Response;
|
|
100
|
+
try {
|
|
101
|
+
resp = await fetch(url, {
|
|
102
|
+
method,
|
|
103
|
+
headers,
|
|
104
|
+
body,
|
|
105
|
+
signal: controller.signal,
|
|
106
|
+
});
|
|
107
|
+
} catch (err) {
|
|
108
|
+
clearTimeout(timer);
|
|
109
|
+
lastError = err instanceof Error ? err : new Error(String(err));
|
|
110
|
+
continue;
|
|
111
|
+
} finally {
|
|
112
|
+
clearTimeout(timer);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (resp.status === 204) {
|
|
116
|
+
return {};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (resp.status === 429) {
|
|
120
|
+
const retryAfter = parseRetryAfter(resp);
|
|
121
|
+
const errorBody = await safeJson(resp);
|
|
122
|
+
raiseForError(resp.status, errorBody, retryAfter);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (resp.status >= 500 && attempt < this.maxRetries) {
|
|
126
|
+
lastError = new Error(`Server error ${resp.status}`);
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (resp.status >= 400) {
|
|
131
|
+
const errorBody = await safeJson(resp);
|
|
132
|
+
raiseForError(resp.status, errorBody);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return (await resp.json()) as Record<string, unknown>;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
throw new Error(
|
|
139
|
+
`Request failed after ${this.maxRetries + 1} attempts: ${lastError?.message}`,
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/** Make a request and return raw bytes (for PDF downloads). */
|
|
144
|
+
async requestBytes(
|
|
145
|
+
method: string,
|
|
146
|
+
path: string,
|
|
147
|
+
opts?: { authenticated?: boolean },
|
|
148
|
+
): Promise<Uint8Array> {
|
|
149
|
+
const authenticated = opts?.authenticated ?? true;
|
|
150
|
+
const headers: Record<string, string> = {};
|
|
151
|
+
if (authenticated) {
|
|
152
|
+
headers["Authorization"] = `Bearer ${this.apiKey}`;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const url = `${this.baseUrl}${path}`;
|
|
156
|
+
const controller = new AbortController();
|
|
157
|
+
const timer = setTimeout(() => controller.abort(), this.timeout * 1000);
|
|
158
|
+
|
|
159
|
+
let resp: Response;
|
|
160
|
+
try {
|
|
161
|
+
resp = await fetch(url, { method, headers, signal: controller.signal });
|
|
162
|
+
} finally {
|
|
163
|
+
clearTimeout(timer);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (resp.status >= 400) {
|
|
167
|
+
const errorBody = await safeJson(resp);
|
|
168
|
+
raiseForError(resp.status, errorBody);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return new Uint8Array(await resp.arrayBuffer());
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function parseRetryAfter(resp: Response): number | undefined {
|
|
176
|
+
const raw = resp.headers.get("Retry-After");
|
|
177
|
+
if (raw == null) return undefined;
|
|
178
|
+
const n = Number(raw);
|
|
179
|
+
return Number.isFinite(n) ? n : undefined;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
async function safeJson(resp: Response): Promise<Record<string, unknown>> {
|
|
183
|
+
// Read once. resp.json() consumes the stream on failure too — calling
|
|
184
|
+
// resp.text() afterward would throw "body already used" and the real
|
|
185
|
+
// status code would be lost.
|
|
186
|
+
const text = await resp.text();
|
|
187
|
+
try {
|
|
188
|
+
return JSON.parse(text) as Record<string, unknown>;
|
|
189
|
+
} catch {
|
|
190
|
+
return {
|
|
191
|
+
error: { code: "UNKNOWN", message: text, request_id: "" },
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function sleep(ms: number): Promise<void> {
|
|
197
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// randomIdempotencyKey returns a RFC 4122 v4 UUID using the Web Crypto API,
|
|
201
|
+
// available in both browsers and Node 18+ (wherever fetch is present).
|
|
202
|
+
function randomIdempotencyKey(): string {
|
|
203
|
+
const b = new Uint8Array(16);
|
|
204
|
+
globalThis.crypto.getRandomValues(b);
|
|
205
|
+
b[6] = (b[6]! & 0x0f) | 0x40; // version 4
|
|
206
|
+
b[8] = (b[8]! & 0x3f) | 0x80; // variant 10
|
|
207
|
+
const h = Array.from(b, (x) => x.toString(16).padStart(2, "0")).join("");
|
|
208
|
+
return `${h.slice(0, 8)}-${h.slice(8, 12)}-${h.slice(12, 16)}-${h.slice(16, 20)}-${h.slice(20)}`;
|
|
209
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/** BurnLedger TypeScript SDK — browser entry point.
|
|
2
|
+
*
|
|
3
|
+
* Uses WebCrypto (crypto.subtle) for Ed25519 and SHA-256.
|
|
4
|
+
* Requires Chrome 113+, Firefox 128+, or Safari 17+.
|
|
5
|
+
*
|
|
6
|
+
* Does NOT export verifyWebhookSignature (server-side only, uses node:crypto).
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export { BurnLedger } from "./client.js";
|
|
10
|
+
export type { BurnLedgerOptions } from "./client.js";
|
|
11
|
+
|
|
12
|
+
export {
|
|
13
|
+
BurnLedgerError,
|
|
14
|
+
ApiError,
|
|
15
|
+
AuthenticationError,
|
|
16
|
+
NotFoundError,
|
|
17
|
+
ConflictError,
|
|
18
|
+
ValidationError,
|
|
19
|
+
RateLimitError,
|
|
20
|
+
ServerError,
|
|
21
|
+
TimeoutError,
|
|
22
|
+
VerificationError,
|
|
23
|
+
} from "./errors.js";
|
|
24
|
+
|
|
25
|
+
export { Transport } from "./http.js";
|
|
26
|
+
export type { TransportOptions } from "./http.js";
|
|
27
|
+
|
|
28
|
+
export type {
|
|
29
|
+
ProofMode,
|
|
30
|
+
AttestationStatus,
|
|
31
|
+
CertificateStatus,
|
|
32
|
+
TransparencyStatus,
|
|
33
|
+
HealthStatus,
|
|
34
|
+
TransportSecurity,
|
|
35
|
+
ReadOnlyEnforcement,
|
|
36
|
+
ConnectorType,
|
|
37
|
+
HashScope,
|
|
38
|
+
LogEntryType,
|
|
39
|
+
VerificationResult,
|
|
40
|
+
TransparencyResult,
|
|
41
|
+
System,
|
|
42
|
+
SystemAttestation,
|
|
43
|
+
Attestation,
|
|
44
|
+
VerificationSystem,
|
|
45
|
+
VerifyResult,
|
|
46
|
+
CertificateResponse,
|
|
47
|
+
RevocationStatus,
|
|
48
|
+
Webhook,
|
|
49
|
+
SignedTreeHead,
|
|
50
|
+
LogEntry,
|
|
51
|
+
InclusionProof,
|
|
52
|
+
ConsistencyProof,
|
|
53
|
+
} from "./models.js";
|
|
54
|
+
|
|
55
|
+
// Raw-JSON → model parsers, for consumers that fetch API responses outside
|
|
56
|
+
// the client (the dashboard's paginated list hooks) but render SDK types.
|
|
57
|
+
export { parseSystem, parseCertificateResponse, parseWebhook } from "./models.js";
|
|
58
|
+
|
|
59
|
+
export { Paginator } from "./pagination.js";
|
|
60
|
+
|
|
61
|
+
export type { CryptoOps } from "./crypto.js";
|
|
62
|
+
export type { PublicKeyInfo } from "./verify.js";
|
|
63
|
+
export { issuanceBytes, hexToBytes, bytesToHex } from "./verify.js";
|
|
64
|
+
|
|
65
|
+
import { browserCrypto } from "./crypto-browser.js";
|
|
66
|
+
import {
|
|
67
|
+
verifyCertificate as _verifyCertificate,
|
|
68
|
+
verifyTransparency as _verifyTransparency,
|
|
69
|
+
verifyConsistency as _verifyConsistency,
|
|
70
|
+
publicKeyFromHex as _publicKeyFromHex,
|
|
71
|
+
} from "./verify.js";
|
|
72
|
+
import type { PublicKeyInfo } from "./verify.js";
|
|
73
|
+
import type { VerificationResult, TransparencyResult } from "./models.js";
|
|
74
|
+
|
|
75
|
+
type Cert = Record<string, unknown>;
|
|
76
|
+
|
|
77
|
+
/** Verify all signatures on a deletion certificate offline. */
|
|
78
|
+
export function verifyCertificate(
|
|
79
|
+
certificate: Cert,
|
|
80
|
+
publicKeys: Map<string, PublicKeyInfo>,
|
|
81
|
+
): Promise<VerificationResult> {
|
|
82
|
+
return _verifyCertificate(browserCrypto, certificate, publicKeys);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** Verify the transparency proof embedded in a certificate. */
|
|
86
|
+
export function verifyTransparency(
|
|
87
|
+
certificate: Cert,
|
|
88
|
+
publicKeys: Map<string, PublicKeyInfo>,
|
|
89
|
+
): Promise<TransparencyResult> {
|
|
90
|
+
return _verifyTransparency(browserCrypto, certificate, publicKeys);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** Construct a PublicKeyInfo from a hex-encoded Ed25519 public key. */
|
|
94
|
+
export function publicKeyFromHex(
|
|
95
|
+
hexKey: string,
|
|
96
|
+
opts?: { revoked?: boolean },
|
|
97
|
+
): Promise<PublicKeyInfo> {
|
|
98
|
+
return _publicKeyFromHex(browserCrypto, hexKey, opts);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** Verify a Merkle consistency proof between two tree states (RFC 6962). */
|
|
102
|
+
export function verifyConsistency(
|
|
103
|
+
oldSize: number,
|
|
104
|
+
newSize: number,
|
|
105
|
+
oldRoot: Uint8Array,
|
|
106
|
+
newRoot: Uint8Array,
|
|
107
|
+
proof: Uint8Array[],
|
|
108
|
+
): Promise<boolean> {
|
|
109
|
+
return _verifyConsistency(browserCrypto, oldSize, newSize, oldRoot, newRoot, proof);
|
|
110
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/** BurnLedger TypeScript SDK — public API (Node.js entry point). */
|
|
2
|
+
|
|
3
|
+
export { BurnLedger } from "./client.js";
|
|
4
|
+
export type { BurnLedgerOptions } from "./client.js";
|
|
5
|
+
|
|
6
|
+
export {
|
|
7
|
+
BurnLedgerError,
|
|
8
|
+
ApiError,
|
|
9
|
+
AuthenticationError,
|
|
10
|
+
NotFoundError,
|
|
11
|
+
ConflictError,
|
|
12
|
+
ValidationError,
|
|
13
|
+
RateLimitError,
|
|
14
|
+
ServerError,
|
|
15
|
+
TimeoutError,
|
|
16
|
+
VerificationError,
|
|
17
|
+
} from "./errors.js";
|
|
18
|
+
|
|
19
|
+
export { Transport } from "./http.js";
|
|
20
|
+
export type { TransportOptions } from "./http.js";
|
|
21
|
+
|
|
22
|
+
export type {
|
|
23
|
+
ProofMode,
|
|
24
|
+
AttestationStatus,
|
|
25
|
+
CertificateStatus,
|
|
26
|
+
TransparencyStatus,
|
|
27
|
+
HealthStatus,
|
|
28
|
+
TransportSecurity,
|
|
29
|
+
ReadOnlyEnforcement,
|
|
30
|
+
ConnectorType,
|
|
31
|
+
HashScope,
|
|
32
|
+
LogEntryType,
|
|
33
|
+
VerificationResult,
|
|
34
|
+
TransparencyResult,
|
|
35
|
+
ApiKeyRole,
|
|
36
|
+
System,
|
|
37
|
+
SystemAttestation,
|
|
38
|
+
Attestation,
|
|
39
|
+
VerificationSystem,
|
|
40
|
+
VerifyResult,
|
|
41
|
+
CertificateResponse,
|
|
42
|
+
RevocationStatus,
|
|
43
|
+
Webhook,
|
|
44
|
+
SignedTreeHead,
|
|
45
|
+
LogEntry,
|
|
46
|
+
InclusionProof,
|
|
47
|
+
ConsistencyProof,
|
|
48
|
+
BatchAttestationResponse,
|
|
49
|
+
BatchAttestationError,
|
|
50
|
+
MonthlyCount,
|
|
51
|
+
CertificateStats,
|
|
52
|
+
WebhookRotateResponse,
|
|
53
|
+
WebhookDelivery,
|
|
54
|
+
ApiKeyListItem,
|
|
55
|
+
ApiKeyResponse,
|
|
56
|
+
PlanUsage,
|
|
57
|
+
Profile,
|
|
58
|
+
SystemHealth,
|
|
59
|
+
} from "./models.js";
|
|
60
|
+
|
|
61
|
+
// Raw-JSON → model parsers, for consumers that fetch API responses outside
|
|
62
|
+
// the client (the dashboard's paginated list hooks) but render SDK types.
|
|
63
|
+
export { parseSystem, parseCertificateResponse, parseWebhook } from "./models.js";
|
|
64
|
+
|
|
65
|
+
export { Paginator } from "./pagination.js";
|
|
66
|
+
|
|
67
|
+
export type { CryptoOps } from "./crypto.js";
|
|
68
|
+
export type { PublicKeyInfo } from "./verify.js";
|
|
69
|
+
export { issuanceBytes, hexToBytes, bytesToHex } from "./verify.js";
|
|
70
|
+
|
|
71
|
+
import { nodeCrypto } from "./crypto-node.js";
|
|
72
|
+
import {
|
|
73
|
+
verifyCertificate as _verifyCertificate,
|
|
74
|
+
verifyCertificateWithStatus as _verifyCertificateWithStatus,
|
|
75
|
+
verifyTransparency as _verifyTransparency,
|
|
76
|
+
verifyConsistency as _verifyConsistency,
|
|
77
|
+
publicKeyFromHex as _publicKeyFromHex,
|
|
78
|
+
} from "./verify.js";
|
|
79
|
+
import type { PublicKeyInfo } from "./verify.js";
|
|
80
|
+
import type { VerificationResult, TransparencyResult } from "./models.js";
|
|
81
|
+
|
|
82
|
+
type Cert = Record<string, unknown>;
|
|
83
|
+
|
|
84
|
+
/** Verify all signatures on a deletion certificate offline. */
|
|
85
|
+
export function verifyCertificate(
|
|
86
|
+
certificate: Cert,
|
|
87
|
+
publicKeys: Map<string, PublicKeyInfo>,
|
|
88
|
+
): Promise<VerificationResult> {
|
|
89
|
+
return _verifyCertificate(nodeCrypto, certificate, publicKeys);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Verify a certificate and, separately, what a signed statement says about its
|
|
94
|
+
* revocation. Returns "VALID_REVOCATION_UNKNOWN" when no fresh statement is
|
|
95
|
+
* supplied — never "VALID", because "not revoked" is not something an offline
|
|
96
|
+
* check can establish on its own.
|
|
97
|
+
*/
|
|
98
|
+
export function verifyCertificateWithStatus(
|
|
99
|
+
certificate: Cert,
|
|
100
|
+
publicKeys: Map<string, PublicKeyInfo>,
|
|
101
|
+
status?: Record<string, unknown> | null,
|
|
102
|
+
now?: Date,
|
|
103
|
+
): Promise<string> {
|
|
104
|
+
return _verifyCertificateWithStatus(nodeCrypto, certificate, publicKeys, status, now);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** Verify the transparency proof embedded in a certificate. */
|
|
108
|
+
export function verifyTransparency(
|
|
109
|
+
certificate: Cert,
|
|
110
|
+
publicKeys: Map<string, PublicKeyInfo>,
|
|
111
|
+
): Promise<TransparencyResult> {
|
|
112
|
+
return _verifyTransparency(nodeCrypto, certificate, publicKeys);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/** Construct a PublicKeyInfo from a hex-encoded Ed25519 public key. */
|
|
116
|
+
export function publicKeyFromHex(
|
|
117
|
+
hexKey: string,
|
|
118
|
+
opts?: { revoked?: boolean },
|
|
119
|
+
): Promise<PublicKeyInfo> {
|
|
120
|
+
return _publicKeyFromHex(nodeCrypto, hexKey, opts);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** Verify a Merkle consistency proof between two tree states (RFC 6962). */
|
|
124
|
+
export function verifyConsistency(
|
|
125
|
+
oldSize: number,
|
|
126
|
+
newSize: number,
|
|
127
|
+
oldRoot: Uint8Array,
|
|
128
|
+
newRoot: Uint8Array,
|
|
129
|
+
proof: Uint8Array[],
|
|
130
|
+
): Promise<boolean> {
|
|
131
|
+
return _verifyConsistency(nodeCrypto, oldSize, newSize, oldRoot, newRoot, proof);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export { verifyWebhookSignature } from "./webhooks.js";
|
package/src/keys.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// Public-key file parsing shared by the CLI (and unit-testable in isolation).
|
|
2
|
+
|
|
3
|
+
export type KeyEntry = { key_id: string; public_key: string; key_status: string };
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Normalize a parsed keys file to an array of key entries. Accepts a bare
|
|
7
|
+
* JSON array (the generated keys.json shape), a { "keys": [...] } envelope,
|
|
8
|
+
* or the { "data": { "keys": [...] } } envelope the /.well-known endpoint
|
|
9
|
+
* actually publishes. Throws on any other shape.
|
|
10
|
+
*/
|
|
11
|
+
export function parseKeyEntries(parsed: unknown): KeyEntry[] {
|
|
12
|
+
const root = (parsed as { data?: unknown })?.data ?? parsed;
|
|
13
|
+
const entries = Array.isArray(root) ? root : (root as { keys?: unknown })?.keys;
|
|
14
|
+
if (!Array.isArray(entries)) {
|
|
15
|
+
throw new Error('--keys file must be a JSON array of keys or a {"keys": [...]} object');
|
|
16
|
+
}
|
|
17
|
+
return entries as KeyEntry[];
|
|
18
|
+
}
|