@avenlabs/halal-trace-sdk 0.1.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/README.md +118 -0
- package/dist/client.d.ts +442 -0
- package/dist/client.js +358 -0
- package/dist/http.d.ts +70 -0
- package/dist/http.js +348 -0
- package/dist/http.test.d.ts +1 -0
- package/dist/http.test.js +7 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +3 -0
- package/dist/types.d.ts +210 -0
- package/dist/types.js +1 -0
- package/dist/utils.d.ts +1 -0
- package/dist/utils.js +5 -0
- package/package.json +21 -0
- package/src/client.ts +491 -0
- package/src/http.test.ts +10 -0
- package/src/http.ts +449 -0
- package/src/index.ts +32 -0
- package/src/types.ts +228 -0
- package/src/utils.ts +6 -0
- package/tsconfig.json +16 -0
package/src/http.ts
ADDED
|
@@ -0,0 +1,449 @@
|
|
|
1
|
+
import type { ApiErrorResponse } from "./types.js";
|
|
2
|
+
|
|
3
|
+
export type AuthOptions =
|
|
4
|
+
| { token: string }
|
|
5
|
+
| { getToken: () => Promise<string> | string }
|
|
6
|
+
| undefined;
|
|
7
|
+
|
|
8
|
+
export type RetryOptions = {
|
|
9
|
+
enabled?: boolean;
|
|
10
|
+
maxRetries?: number;
|
|
11
|
+
baseDelayMs?: number;
|
|
12
|
+
maxDelayMs?: number;
|
|
13
|
+
jitterMs?: number;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export type ClientHooks = {
|
|
17
|
+
onRequest?: (context: RequestContext) => void | Promise<void>;
|
|
18
|
+
onResponse?: (context: ResponseContext) => void | Promise<void>;
|
|
19
|
+
onError?: (context: ErrorContext) => void | Promise<void>;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export type RequestContext = {
|
|
23
|
+
method: string;
|
|
24
|
+
url: string;
|
|
25
|
+
headers: Record<string, string>;
|
|
26
|
+
body?: unknown;
|
|
27
|
+
requestId: string;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export type ResponseContext = {
|
|
31
|
+
method: string;
|
|
32
|
+
url: string;
|
|
33
|
+
status: number;
|
|
34
|
+
requestId: string;
|
|
35
|
+
durationMs: number;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export type ErrorContext = {
|
|
39
|
+
method: string;
|
|
40
|
+
url: string;
|
|
41
|
+
status?: number;
|
|
42
|
+
requestId: string;
|
|
43
|
+
durationMs: number;
|
|
44
|
+
error: Error;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export class ApiError extends Error {
|
|
48
|
+
status?: number;
|
|
49
|
+
body?: unknown;
|
|
50
|
+
requestId?: string;
|
|
51
|
+
|
|
52
|
+
constructor(message: string, status?: number, body?: unknown, requestId?: string) {
|
|
53
|
+
super(message);
|
|
54
|
+
this.status = status;
|
|
55
|
+
this.body = body;
|
|
56
|
+
this.requestId = requestId;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export type RequestOptions = {
|
|
61
|
+
headers?: Record<string, string>;
|
|
62
|
+
body?: unknown;
|
|
63
|
+
query?: Record<string, string | number | boolean | undefined | null>;
|
|
64
|
+
timeoutMs?: number;
|
|
65
|
+
idempotencyKey?: string;
|
|
66
|
+
allowInsecure?: boolean;
|
|
67
|
+
retry?: RetryOptions;
|
|
68
|
+
auth?: AuthOptions;
|
|
69
|
+
onAuthError?: (error: ApiError) => void | Promise<void>;
|
|
70
|
+
requestId?: string;
|
|
71
|
+
sdkHeaders?: Record<string, string>;
|
|
72
|
+
hooks?: ClientHooks;
|
|
73
|
+
signingHook?: (context: RequestContext) => Promise<Record<string, string> | void> | Record<string, string> | void;
|
|
74
|
+
canRetry?: boolean;
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const defaultRetry: Required<RetryOptions> = {
|
|
78
|
+
enabled: true,
|
|
79
|
+
maxRetries: 2,
|
|
80
|
+
baseDelayMs: 250,
|
|
81
|
+
maxDelayMs: 2000,
|
|
82
|
+
jitterMs: 200,
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const transientStatuses = new Set([408, 429, 500, 502, 503, 504]);
|
|
86
|
+
|
|
87
|
+
const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
88
|
+
|
|
89
|
+
const buildQuery = (query?: RequestOptions["query"]) => {
|
|
90
|
+
if (!query) {
|
|
91
|
+
return "";
|
|
92
|
+
}
|
|
93
|
+
const entries = Object.entries(query)
|
|
94
|
+
.filter(([, value]) => value !== undefined && value !== null)
|
|
95
|
+
.map(([key, value]) => [key, String(value)]);
|
|
96
|
+
if (entries.length === 0) {
|
|
97
|
+
return "";
|
|
98
|
+
}
|
|
99
|
+
const params = new URLSearchParams(entries);
|
|
100
|
+
return `?${params.toString()}`;
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
const computeBackoff = (attempt: number, baseDelayMs: number, maxDelayMs: number, jitterMs: number) => {
|
|
104
|
+
const delay = Math.min(baseDelayMs * Math.pow(2, Math.max(attempt - 1, 0)), maxDelayMs);
|
|
105
|
+
const jitter = Math.floor(Math.random() * jitterMs);
|
|
106
|
+
return delay + jitter;
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
const parseRetryAfter = (value: string | null): number | null => {
|
|
110
|
+
if (!value) {
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
const seconds = Number(value);
|
|
114
|
+
if (!Number.isNaN(seconds)) {
|
|
115
|
+
return seconds * 1000;
|
|
116
|
+
}
|
|
117
|
+
const date = Date.parse(value);
|
|
118
|
+
if (!Number.isNaN(date)) {
|
|
119
|
+
const delta = date - Date.now();
|
|
120
|
+
return delta > 0 ? delta : null;
|
|
121
|
+
}
|
|
122
|
+
return null;
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
const ensureHttps = (url: string, allowInsecure?: boolean) => {
|
|
126
|
+
if (allowInsecure) {
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
if (url.startsWith("http://")) {
|
|
130
|
+
throw new Error("Insecure baseUrl blocked. Set allowInsecure=true for http://");
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
const now = () => (globalThis.performance?.now ? globalThis.performance.now() : Date.now());
|
|
135
|
+
|
|
136
|
+
const generateRequestId = () => {
|
|
137
|
+
if (globalThis.crypto?.randomUUID) {
|
|
138
|
+
return globalThis.crypto.randomUUID();
|
|
139
|
+
}
|
|
140
|
+
return `${Date.now()}-${Math.random().toString(16).slice(2)}`;
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
export const request = async <T>(
|
|
144
|
+
baseUrl: string,
|
|
145
|
+
path: string,
|
|
146
|
+
method: string,
|
|
147
|
+
options: RequestOptions,
|
|
148
|
+
) => {
|
|
149
|
+
if (!baseUrl) {
|
|
150
|
+
throw new Error("baseUrl is required");
|
|
151
|
+
}
|
|
152
|
+
if (options.timeoutMs !== undefined && options.timeoutMs <= 0) {
|
|
153
|
+
throw new Error("timeoutMs must be greater than 0");
|
|
154
|
+
}
|
|
155
|
+
ensureHttps(baseUrl, options.allowInsecure);
|
|
156
|
+
|
|
157
|
+
const retryConfig = { ...defaultRetry, ...options.retry };
|
|
158
|
+
const url = `${baseUrl.replace(/\/$/, "")}${path}${buildQuery(options.query)}`;
|
|
159
|
+
const requestId = options.requestId ?? generateRequestId();
|
|
160
|
+
const headers: Record<string, string> = {
|
|
161
|
+
"Content-Type": "application/json",
|
|
162
|
+
"x-request-id": requestId,
|
|
163
|
+
...(options.sdkHeaders ?? {}),
|
|
164
|
+
...(options.headers ?? {}),
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
if (options.idempotencyKey) {
|
|
168
|
+
headers["Idempotency-Key"] = options.idempotencyKey;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (options.auth) {
|
|
172
|
+
if ("token" in options.auth) {
|
|
173
|
+
headers.Authorization = `Bearer ${options.auth.token}`;
|
|
174
|
+
} else if ("getToken" in options.auth) {
|
|
175
|
+
const token = await options.auth.getToken();
|
|
176
|
+
if (token) {
|
|
177
|
+
headers.Authorization = `Bearer ${token}`;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const body = options.body ? JSON.stringify(options.body) : undefined;
|
|
183
|
+
const context: RequestContext = { method, url, headers, body: options.body, requestId };
|
|
184
|
+
if (options.signingHook) {
|
|
185
|
+
const extraHeaders = await options.signingHook(context);
|
|
186
|
+
if (extraHeaders) {
|
|
187
|
+
Object.assign(headers, extraHeaders);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
if (options.hooks?.onRequest) {
|
|
191
|
+
await options.hooks.onRequest(context);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const start = now();
|
|
195
|
+
let lastError: ApiError | undefined;
|
|
196
|
+
|
|
197
|
+
for (let attempt = 0; attempt <= retryConfig.maxRetries; attempt += 1) {
|
|
198
|
+
try {
|
|
199
|
+
const controller = options.timeoutMs ? new AbortController() : undefined;
|
|
200
|
+
const timeoutHandle = options.timeoutMs
|
|
201
|
+
? setTimeout(() => controller?.abort(), options.timeoutMs)
|
|
202
|
+
: undefined;
|
|
203
|
+
const response = await fetch(url, {
|
|
204
|
+
method,
|
|
205
|
+
headers,
|
|
206
|
+
body,
|
|
207
|
+
signal: controller?.signal,
|
|
208
|
+
});
|
|
209
|
+
if (timeoutHandle) {
|
|
210
|
+
clearTimeout(timeoutHandle);
|
|
211
|
+
}
|
|
212
|
+
const durationMs = now() - start;
|
|
213
|
+
const responseRequestId = response.headers.get("x-request-id") ?? requestId;
|
|
214
|
+
const responseText = await response.text();
|
|
215
|
+
let responseBody: ApiErrorResponse | T | undefined;
|
|
216
|
+
if (responseText) {
|
|
217
|
+
try {
|
|
218
|
+
responseBody = JSON.parse(responseText) as ApiErrorResponse | T;
|
|
219
|
+
} catch {
|
|
220
|
+
responseBody = undefined;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
if (!response.ok) {
|
|
224
|
+
const message =
|
|
225
|
+
typeof responseBody === "object" && responseBody && "message" in responseBody
|
|
226
|
+
? String((responseBody as ApiErrorResponse).message)
|
|
227
|
+
: `Request failed: ${response.status}`;
|
|
228
|
+
const error = new ApiError(message, response.status, responseBody, responseRequestId);
|
|
229
|
+
if (response.status === 401 && options.onAuthError) {
|
|
230
|
+
await options.onAuthError(error);
|
|
231
|
+
}
|
|
232
|
+
const retryAfter = parseRetryAfter(response.headers.get("Retry-After"));
|
|
233
|
+
const shouldRetry =
|
|
234
|
+
retryConfig.enabled &&
|
|
235
|
+
options.canRetry &&
|
|
236
|
+
transientStatuses.has(response.status) &&
|
|
237
|
+
attempt < retryConfig.maxRetries;
|
|
238
|
+
if (shouldRetry) {
|
|
239
|
+
const delay = retryAfter ?? computeBackoff(attempt + 1, retryConfig.baseDelayMs, retryConfig.maxDelayMs, retryConfig.jitterMs);
|
|
240
|
+
await sleep(delay);
|
|
241
|
+
continue;
|
|
242
|
+
}
|
|
243
|
+
if (options.hooks?.onError) {
|
|
244
|
+
await options.hooks.onError({
|
|
245
|
+
method,
|
|
246
|
+
url,
|
|
247
|
+
status: response.status,
|
|
248
|
+
requestId: responseRequestId,
|
|
249
|
+
durationMs,
|
|
250
|
+
error,
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
throw error;
|
|
254
|
+
}
|
|
255
|
+
if (options.hooks?.onResponse) {
|
|
256
|
+
await options.hooks.onResponse({
|
|
257
|
+
method,
|
|
258
|
+
url,
|
|
259
|
+
status: response.status,
|
|
260
|
+
requestId: responseRequestId,
|
|
261
|
+
durationMs,
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
return { body: responseBody as T, requestId: responseRequestId };
|
|
265
|
+
} catch (error) {
|
|
266
|
+
const durationMs = now() - start;
|
|
267
|
+
const apiError = error instanceof ApiError ? error : new ApiError("Network error", undefined, undefined, requestId);
|
|
268
|
+
lastError = apiError;
|
|
269
|
+
if (
|
|
270
|
+
retryConfig.enabled &&
|
|
271
|
+
options.canRetry &&
|
|
272
|
+
attempt < retryConfig.maxRetries
|
|
273
|
+
) {
|
|
274
|
+
const delay = computeBackoff(attempt + 1, retryConfig.baseDelayMs, retryConfig.maxDelayMs, retryConfig.jitterMs);
|
|
275
|
+
await sleep(delay);
|
|
276
|
+
continue;
|
|
277
|
+
}
|
|
278
|
+
if (options.hooks?.onError) {
|
|
279
|
+
await options.hooks.onError({
|
|
280
|
+
method,
|
|
281
|
+
url,
|
|
282
|
+
requestId,
|
|
283
|
+
durationMs,
|
|
284
|
+
error: apiError,
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
throw apiError;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
if (lastError) {
|
|
292
|
+
throw lastError;
|
|
293
|
+
}
|
|
294
|
+
throw new ApiError("Request failed", undefined, undefined, requestId);
|
|
295
|
+
};
|
|
296
|
+
|
|
297
|
+
export const requestRaw = async (
|
|
298
|
+
baseUrl: string,
|
|
299
|
+
path: string,
|
|
300
|
+
method: string,
|
|
301
|
+
options: RequestOptions,
|
|
302
|
+
) => {
|
|
303
|
+
if (!baseUrl) {
|
|
304
|
+
throw new Error("baseUrl is required");
|
|
305
|
+
}
|
|
306
|
+
if (options.timeoutMs !== undefined && options.timeoutMs <= 0) {
|
|
307
|
+
throw new Error("timeoutMs must be greater than 0");
|
|
308
|
+
}
|
|
309
|
+
ensureHttps(baseUrl, options.allowInsecure);
|
|
310
|
+
|
|
311
|
+
const retryConfig = { ...defaultRetry, ...options.retry };
|
|
312
|
+
const url = `${baseUrl.replace(/\/$/, "")}${path}${buildQuery(options.query)}`;
|
|
313
|
+
const requestId = options.requestId ?? generateRequestId();
|
|
314
|
+
const headers: Record<string, string> = {
|
|
315
|
+
"Content-Type": "application/json",
|
|
316
|
+
"x-request-id": requestId,
|
|
317
|
+
...(options.sdkHeaders ?? {}),
|
|
318
|
+
...(options.headers ?? {}),
|
|
319
|
+
};
|
|
320
|
+
|
|
321
|
+
if (options.idempotencyKey) {
|
|
322
|
+
headers["Idempotency-Key"] = options.idempotencyKey;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
if (options.auth) {
|
|
326
|
+
if ("token" in options.auth) {
|
|
327
|
+
headers.Authorization = `Bearer ${options.auth.token}`;
|
|
328
|
+
} else if ("getToken" in options.auth) {
|
|
329
|
+
const token = await options.auth.getToken();
|
|
330
|
+
if (token) {
|
|
331
|
+
headers.Authorization = `Bearer ${token}`;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
const body = options.body ? JSON.stringify(options.body) : undefined;
|
|
337
|
+
const context: RequestContext = { method, url, headers, body: options.body, requestId };
|
|
338
|
+
if (options.signingHook) {
|
|
339
|
+
const extraHeaders = await options.signingHook(context);
|
|
340
|
+
if (extraHeaders) {
|
|
341
|
+
Object.assign(headers, extraHeaders);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
if (options.hooks?.onRequest) {
|
|
345
|
+
await options.hooks.onRequest(context);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
const start = now();
|
|
349
|
+
let lastError: ApiError | undefined;
|
|
350
|
+
|
|
351
|
+
for (let attempt = 0; attempt <= retryConfig.maxRetries; attempt += 1) {
|
|
352
|
+
try {
|
|
353
|
+
const controller = options.timeoutMs ? new AbortController() : undefined;
|
|
354
|
+
const timeoutHandle = options.timeoutMs
|
|
355
|
+
? setTimeout(() => controller?.abort(), options.timeoutMs)
|
|
356
|
+
: undefined;
|
|
357
|
+
const response = await fetch(url, {
|
|
358
|
+
method,
|
|
359
|
+
headers,
|
|
360
|
+
body,
|
|
361
|
+
signal: controller?.signal,
|
|
362
|
+
});
|
|
363
|
+
if (timeoutHandle) {
|
|
364
|
+
clearTimeout(timeoutHandle);
|
|
365
|
+
}
|
|
366
|
+
const durationMs = now() - start;
|
|
367
|
+
const responseRequestId = response.headers.get("x-request-id") ?? requestId;
|
|
368
|
+
const responseText = await response.text();
|
|
369
|
+
let responseBody: unknown = responseText;
|
|
370
|
+
if (responseText) {
|
|
371
|
+
try {
|
|
372
|
+
responseBody = JSON.parse(responseText);
|
|
373
|
+
} catch {
|
|
374
|
+
responseBody = responseText;
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
if (!response.ok) {
|
|
378
|
+
const message =
|
|
379
|
+
typeof responseBody === "object" && responseBody && "message" in (responseBody as ApiErrorResponse)
|
|
380
|
+
? String((responseBody as ApiErrorResponse).message)
|
|
381
|
+
: `Request failed: ${response.status}`;
|
|
382
|
+
const error = new ApiError(message, response.status, responseBody, responseRequestId);
|
|
383
|
+
if (response.status === 401 && options.onAuthError) {
|
|
384
|
+
await options.onAuthError(error);
|
|
385
|
+
}
|
|
386
|
+
const retryAfter = parseRetryAfter(response.headers.get("Retry-After"));
|
|
387
|
+
const shouldRetry =
|
|
388
|
+
retryConfig.enabled &&
|
|
389
|
+
options.canRetry &&
|
|
390
|
+
transientStatuses.has(response.status) &&
|
|
391
|
+
attempt < retryConfig.maxRetries;
|
|
392
|
+
if (shouldRetry) {
|
|
393
|
+
const delay = retryAfter ?? computeBackoff(attempt + 1, retryConfig.baseDelayMs, retryConfig.maxDelayMs, retryConfig.jitterMs);
|
|
394
|
+
await sleep(delay);
|
|
395
|
+
continue;
|
|
396
|
+
}
|
|
397
|
+
if (options.hooks?.onError) {
|
|
398
|
+
await options.hooks.onError({
|
|
399
|
+
method,
|
|
400
|
+
url,
|
|
401
|
+
status: response.status,
|
|
402
|
+
requestId: responseRequestId,
|
|
403
|
+
durationMs,
|
|
404
|
+
error,
|
|
405
|
+
});
|
|
406
|
+
}
|
|
407
|
+
throw error;
|
|
408
|
+
}
|
|
409
|
+
if (options.hooks?.onResponse) {
|
|
410
|
+
await options.hooks.onResponse({
|
|
411
|
+
method,
|
|
412
|
+
url,
|
|
413
|
+
status: response.status,
|
|
414
|
+
requestId: responseRequestId,
|
|
415
|
+
durationMs,
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
return { body: responseBody, requestId: responseRequestId, headers: response.headers };
|
|
419
|
+
} catch (error) {
|
|
420
|
+
const durationMs = now() - start;
|
|
421
|
+
const apiError = error instanceof ApiError ? error : new ApiError("Network error", undefined, undefined, requestId);
|
|
422
|
+
lastError = apiError;
|
|
423
|
+
if (
|
|
424
|
+
retryConfig.enabled &&
|
|
425
|
+
options.canRetry &&
|
|
426
|
+
attempt < retryConfig.maxRetries
|
|
427
|
+
) {
|
|
428
|
+
const delay = computeBackoff(attempt + 1, retryConfig.baseDelayMs, retryConfig.maxDelayMs, retryConfig.jitterMs);
|
|
429
|
+
await sleep(delay);
|
|
430
|
+
continue;
|
|
431
|
+
}
|
|
432
|
+
if (options.hooks?.onError) {
|
|
433
|
+
await options.hooks.onError({
|
|
434
|
+
method,
|
|
435
|
+
url,
|
|
436
|
+
requestId,
|
|
437
|
+
durationMs,
|
|
438
|
+
error: apiError,
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
throw apiError;
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
if (lastError) {
|
|
446
|
+
throw lastError;
|
|
447
|
+
}
|
|
448
|
+
throw new ApiError("Request failed", undefined, undefined, requestId);
|
|
449
|
+
};
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export { ApiClient } from "./client.js";
|
|
2
|
+
export type {
|
|
3
|
+
Anchor,
|
|
4
|
+
ApiErrorResponse,
|
|
5
|
+
ApiResponse,
|
|
6
|
+
AuthResult,
|
|
7
|
+
AuthSession,
|
|
8
|
+
AuditLog,
|
|
9
|
+
AuditPack,
|
|
10
|
+
AuditPackManifest,
|
|
11
|
+
Batch,
|
|
12
|
+
CertificationRecord,
|
|
13
|
+
Device,
|
|
14
|
+
DocumentAnchor,
|
|
15
|
+
EventDepartment,
|
|
16
|
+
HealthResponse,
|
|
17
|
+
Hologram,
|
|
18
|
+
HologramVerification,
|
|
19
|
+
Invite,
|
|
20
|
+
Member,
|
|
21
|
+
Org,
|
|
22
|
+
OrgInviteResult,
|
|
23
|
+
OrgUserLookup,
|
|
24
|
+
Permission,
|
|
25
|
+
PublicAttribute,
|
|
26
|
+
RelayerJobSummary,
|
|
27
|
+
SignatureRecord,
|
|
28
|
+
TraceEvent,
|
|
29
|
+
} from "./types.js";
|
|
30
|
+
export { ApiError } from "./http.js";
|
|
31
|
+
export type { AuthOptions, ClientHooks, RetryOptions } from "./http.js";
|
|
32
|
+
export { saveToFile } from "./utils.js";
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
export type ApiStatus = "ok" | "error" | "not_found";
|
|
2
|
+
|
|
3
|
+
export type ApiResponse<T> = {
|
|
4
|
+
status: ApiStatus;
|
|
5
|
+
data?: T;
|
|
6
|
+
relayerJob?: RelayerJobSummary | null;
|
|
7
|
+
requestId?: string;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export type ApiErrorResponse = {
|
|
11
|
+
status: "error";
|
|
12
|
+
message: string;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export type RelayerJobSummary = {
|
|
16
|
+
id: number;
|
|
17
|
+
status: string;
|
|
18
|
+
txHash: string | null;
|
|
19
|
+
jobType: string;
|
|
20
|
+
batchId: string | null;
|
|
21
|
+
requestId: string | null;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export type Org = {
|
|
25
|
+
orgId: string;
|
|
26
|
+
name?: string | null;
|
|
27
|
+
adminId: string;
|
|
28
|
+
active?: boolean;
|
|
29
|
+
createdAt?: string;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export type Batch = {
|
|
33
|
+
batchId: string;
|
|
34
|
+
orgId: string;
|
|
35
|
+
productId: string;
|
|
36
|
+
facilityId?: string | null;
|
|
37
|
+
createdAt: string;
|
|
38
|
+
active: boolean;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export type PublicAttribute = {
|
|
42
|
+
id: number;
|
|
43
|
+
batchId: string;
|
|
44
|
+
key: string;
|
|
45
|
+
value: string;
|
|
46
|
+
recordedAt: string;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export type TraceEvent = {
|
|
50
|
+
id: number;
|
|
51
|
+
batchId: string;
|
|
52
|
+
eventType: string;
|
|
53
|
+
metadataHash: string;
|
|
54
|
+
actorRole: string;
|
|
55
|
+
deviceId?: string | null;
|
|
56
|
+
recordedAt: string;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
export type DocumentAnchor = {
|
|
60
|
+
id: number;
|
|
61
|
+
batchId: string;
|
|
62
|
+
docHash: string;
|
|
63
|
+
docType: string;
|
|
64
|
+
uri: string;
|
|
65
|
+
recordedAt: string;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
export type SignatureRecord = {
|
|
69
|
+
id: number;
|
|
70
|
+
batchId: string;
|
|
71
|
+
signatureHash: string;
|
|
72
|
+
signerRole: string;
|
|
73
|
+
signerIdHash: string;
|
|
74
|
+
recordedAt: string;
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
export type CertificationRecord = {
|
|
78
|
+
id: number;
|
|
79
|
+
batchId: string;
|
|
80
|
+
status: "pending" | "approved" | "rejected";
|
|
81
|
+
decisionHash: string;
|
|
82
|
+
reviewerRole: string;
|
|
83
|
+
reviewerIdHash: string;
|
|
84
|
+
recordedAt: string;
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
export type Device = {
|
|
88
|
+
deviceId: string;
|
|
89
|
+
orgId: string;
|
|
90
|
+
deviceType: string;
|
|
91
|
+
label: string;
|
|
92
|
+
active: boolean;
|
|
93
|
+
registeredAt: string;
|
|
94
|
+
updatedAt: string;
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
export type Anchor = {
|
|
98
|
+
id: number;
|
|
99
|
+
orgId: string;
|
|
100
|
+
merkleRoot: string;
|
|
101
|
+
metadataHash: string;
|
|
102
|
+
periodStart: number;
|
|
103
|
+
periodEnd: number;
|
|
104
|
+
anchorChainId: number;
|
|
105
|
+
anchorTxHash: string;
|
|
106
|
+
recordedAt: string;
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
export type AuditPack = {
|
|
110
|
+
id: number;
|
|
111
|
+
batchId: string;
|
|
112
|
+
pdfHash: string;
|
|
113
|
+
jsonHash?: string | null;
|
|
114
|
+
packVersion: string;
|
|
115
|
+
pdfUri: string;
|
|
116
|
+
jsonUri?: string | null;
|
|
117
|
+
recordedAt: string;
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
export type Hologram = {
|
|
121
|
+
hologramId: string;
|
|
122
|
+
batchId: string;
|
|
123
|
+
orgId: string;
|
|
124
|
+
metadataHash: string;
|
|
125
|
+
uri: string;
|
|
126
|
+
publicCode?: string | null;
|
|
127
|
+
active: boolean;
|
|
128
|
+
issuedAt: string;
|
|
129
|
+
revokedAt?: string | null;
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
export type AuditPackManifest = {
|
|
133
|
+
batchId: string;
|
|
134
|
+
orgId: string;
|
|
135
|
+
auditPack: AuditPack;
|
|
136
|
+
publicAttributes: PublicAttribute[];
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
export type HologramVerification = {
|
|
140
|
+
hologramId: string;
|
|
141
|
+
publicCode: string;
|
|
142
|
+
active: boolean;
|
|
143
|
+
issuedAt: string;
|
|
144
|
+
revokedAt?: string | null;
|
|
145
|
+
metadataHash: string;
|
|
146
|
+
uri: string;
|
|
147
|
+
batch: Batch;
|
|
148
|
+
publicAttributes: PublicAttribute[];
|
|
149
|
+
auditPack?: AuditPack | null;
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
export type AuditLog = {
|
|
153
|
+
id: number;
|
|
154
|
+
orgId: string;
|
|
155
|
+
actorId?: string | null;
|
|
156
|
+
action: string;
|
|
157
|
+
targetType: string;
|
|
158
|
+
targetId?: string | null;
|
|
159
|
+
metadata?: string | null;
|
|
160
|
+
createdAt: string;
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
export type Member = {
|
|
164
|
+
orgId: string;
|
|
165
|
+
userId: string;
|
|
166
|
+
role: string;
|
|
167
|
+
department: string;
|
|
168
|
+
active: boolean;
|
|
169
|
+
createdAt: string;
|
|
170
|
+
updatedAt: string;
|
|
171
|
+
name?: string | null;
|
|
172
|
+
email?: string | null;
|
|
173
|
+
emailVerified?: boolean | null;
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
export type Invite = {
|
|
177
|
+
token: string;
|
|
178
|
+
orgId: string;
|
|
179
|
+
email: string;
|
|
180
|
+
role: string;
|
|
181
|
+
department: string;
|
|
182
|
+
status: string;
|
|
183
|
+
invitedBy: string;
|
|
184
|
+
createdAt: string;
|
|
185
|
+
expiresAt: string;
|
|
186
|
+
acceptedAt?: string | null;
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
export type Permission = {
|
|
190
|
+
orgId: string;
|
|
191
|
+
action: string;
|
|
192
|
+
role: string;
|
|
193
|
+
createdAt?: string;
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
export type OrgInviteResult =
|
|
197
|
+
| { mode: "member_added"; member: Member }
|
|
198
|
+
| { mode: "invite_pending"; invite: Invite }
|
|
199
|
+
| { mode: "invite_sent"; invite: Invite };
|
|
200
|
+
|
|
201
|
+
export type OrgUserLookup = {
|
|
202
|
+
id: string;
|
|
203
|
+
email: string;
|
|
204
|
+
name?: string | null;
|
|
205
|
+
emailVerified?: boolean | null;
|
|
206
|
+
} | null;
|
|
207
|
+
|
|
208
|
+
export type EventDepartment = {
|
|
209
|
+
orgId: string;
|
|
210
|
+
eventType: string;
|
|
211
|
+
department: string;
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
export type AuthSession = {
|
|
215
|
+
user: {
|
|
216
|
+
id: string;
|
|
217
|
+
email: string;
|
|
218
|
+
name?: string | null;
|
|
219
|
+
};
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
export type AuthResult<T = unknown> = {
|
|
223
|
+
data: T;
|
|
224
|
+
token?: string | null;
|
|
225
|
+
requestId: string;
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
export type HealthResponse = { status: "ok" };
|
package/src/utils.ts
ADDED