@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/dist/client.js ADDED
@@ -0,0 +1,358 @@
1
+ import { ApiError, request, requestRaw } from "./http.js";
2
+ const sdkVersion = "0.1.0";
3
+ const withDefaults = (client, options = {}) => {
4
+ return {
5
+ ...options,
6
+ timeoutMs: options.timeoutMs ?? client.timeoutMs,
7
+ allowInsecure: options.allowInsecure ?? client.allowInsecure,
8
+ retry: options.retry ?? client.retry,
9
+ auth: options.auth ?? client.authOptions,
10
+ onAuthError: options.onAuthError ?? client.onAuthError,
11
+ sdkHeaders: {
12
+ "x-sdk-name": "@halal-trace/sdk",
13
+ "x-sdk-version": sdkVersion,
14
+ ...(client.clientVersion ? { "x-client-version": client.clientVersion } : {}),
15
+ ...(client.userAgent ? { "User-Agent": client.userAgent } : {}),
16
+ },
17
+ hooks: client.hooks,
18
+ signingHook: client.signingHook,
19
+ };
20
+ };
21
+ const unwrap = (response) => response.data ?? undefined;
22
+ async function* listPaginated(client, path, query) {
23
+ const limit = query?.limit ?? 50;
24
+ let offset = 0;
25
+ while (true) {
26
+ const response = await client.request("GET", path, { query: { ...query, limit, offset }, canRetry: true });
27
+ const items = response.body.data ?? [];
28
+ for (const item of items) {
29
+ yield item;
30
+ }
31
+ if (items.length < limit) {
32
+ break;
33
+ }
34
+ offset += limit;
35
+ }
36
+ }
37
+ async function* listSingle(client, path, query) {
38
+ const response = await client.request("GET", path, { query, canRetry: true });
39
+ const items = response.body.data ?? [];
40
+ for (const item of items) {
41
+ yield item;
42
+ }
43
+ }
44
+ async function* listRelayerJobsAll(client, orgId, query) {
45
+ const limit = query?.limit ?? 100;
46
+ let offset = 0;
47
+ while (true) {
48
+ const response = await client.orgs.listRelayerJobs(orgId, { ...query, limit, offset });
49
+ const items = response.data ?? [];
50
+ for (const item of items) {
51
+ yield item;
52
+ }
53
+ if (items.length < limit) {
54
+ break;
55
+ }
56
+ offset += limit;
57
+ }
58
+ }
59
+ export class ApiClient {
60
+ baseUrl;
61
+ timeoutMs;
62
+ headers;
63
+ authOptions;
64
+ retry;
65
+ allowInsecure;
66
+ onAuthError;
67
+ hooks;
68
+ userAgent;
69
+ clientVersion;
70
+ signingHook;
71
+ constructor(options) {
72
+ if (!options.baseUrl) {
73
+ throw new Error("baseUrl is required");
74
+ }
75
+ this.baseUrl = options.baseUrl;
76
+ this.timeoutMs = options.timeoutMs;
77
+ this.headers = options.headers;
78
+ this.authOptions = options.auth;
79
+ this.retry = options.retry;
80
+ this.allowInsecure = options.allowInsecure;
81
+ this.onAuthError = options.onAuthError;
82
+ this.hooks = options.hooks;
83
+ this.userAgent = options.userAgent;
84
+ this.clientVersion = options.clientVersion;
85
+ this.signingHook = options.signingHook;
86
+ }
87
+ async request(method, path, options = {}) {
88
+ const response = await request(this.baseUrl, path, method, {
89
+ ...withDefaults(this, options),
90
+ headers: {
91
+ ...(this.headers ?? {}),
92
+ ...(options.headers ?? {}),
93
+ },
94
+ });
95
+ return {
96
+ body: {
97
+ ...response.body,
98
+ requestId: response.requestId,
99
+ },
100
+ requestId: response.requestId,
101
+ };
102
+ }
103
+ health = {
104
+ get: async () => unwrap((await this.request("GET", "/health", { canRetry: true })).body),
105
+ live: async () => unwrap((await this.request("GET", "/live", { canRetry: true })).body),
106
+ ready: async () => unwrap((await this.request("GET", "/ready", { canRetry: true })).body),
107
+ };
108
+ system = {
109
+ docs: async () => {
110
+ const response = await requestRaw(this.baseUrl, "/docs", "GET", { ...withDefaults(this, {}), canRetry: true });
111
+ return response.body;
112
+ },
113
+ openApi: async () => {
114
+ const response = await requestRaw(this.baseUrl, "/docs/openapi.json", "GET", { ...withDefaults(this, {}), canRetry: true });
115
+ return response.body;
116
+ },
117
+ metrics: async () => {
118
+ const response = await requestRaw(this.baseUrl, "/metrics", "GET", { ...withDefaults(this, {}), canRetry: true });
119
+ return response.body;
120
+ },
121
+ inviteLanding: async (orgId, token) => {
122
+ const response = await requestRaw(this.baseUrl, "/invites/accept", "GET", {
123
+ ...withDefaults(this, {}),
124
+ query: { orgId, token },
125
+ canRetry: true,
126
+ });
127
+ return response.body;
128
+ },
129
+ };
130
+ auth = {
131
+ signUpEmail: async (payload, options) => {
132
+ const response = await requestRaw(this.baseUrl, "/api/auth/sign-up/email", "POST", {
133
+ ...withDefaults(this, options ?? {}),
134
+ body: payload,
135
+ canRetry: false,
136
+ });
137
+ return { data: response.body, token: response.headers.get("set-auth-token"), requestId: response.requestId };
138
+ },
139
+ signInEmail: async (payload, options) => {
140
+ const response = await requestRaw(this.baseUrl, "/api/auth/sign-in/email", "POST", {
141
+ ...withDefaults(this, options ?? {}),
142
+ body: payload,
143
+ canRetry: false,
144
+ });
145
+ return { data: response.body, token: response.headers.get("set-auth-token"), requestId: response.requestId };
146
+ },
147
+ verifyEmail: async (token, callbackURL) => {
148
+ const response = await requestRaw(this.baseUrl, "/api/auth/verify-email", "GET", {
149
+ ...withDefaults(this, {}),
150
+ query: { token, callbackURL },
151
+ canRetry: true,
152
+ });
153
+ return { data: response.body, token: response.headers.get("set-auth-token"), requestId: response.requestId };
154
+ },
155
+ requestPasswordReset: async (payload) => {
156
+ const response = await requestRaw(this.baseUrl, "/api/auth/request-password-reset", "POST", {
157
+ ...withDefaults(this, {}),
158
+ body: payload,
159
+ canRetry: false,
160
+ });
161
+ return { data: response.body, token: response.headers.get("set-auth-token"), requestId: response.requestId };
162
+ },
163
+ resetPassword: async (payload) => {
164
+ const response = await requestRaw(this.baseUrl, "/api/auth/reset-password", "POST", {
165
+ ...withDefaults(this, {}),
166
+ body: payload,
167
+ canRetry: false,
168
+ });
169
+ return { data: response.body, token: response.headers.get("set-auth-token"), requestId: response.requestId };
170
+ },
171
+ };
172
+ orgs = {
173
+ create: async (payload, options) => await this.request("POST", "/orgs", {
174
+ ...options,
175
+ body: payload,
176
+ canRetry: true,
177
+ idempotencyKey: options?.idempotencyKey,
178
+ }).then((res) => res.body),
179
+ addMember: async (orgId, payload, options) => await this.request("POST", `/orgs/${orgId}/members`, {
180
+ ...options,
181
+ body: payload,
182
+ canRetry: true,
183
+ idempotencyKey: options?.idempotencyKey,
184
+ }).then((res) => res.body),
185
+ updateMember: async (orgId, userId, payload) => await this.request("PATCH", `/orgs/${orgId}/members/${userId}`, { body: payload, canRetry: false }).then((res) => res.body),
186
+ removeMember: async (orgId, userId) => await this.request("DELETE", `/orgs/${orgId}/members/${userId}`, { canRetry: false }).then((res) => res.body),
187
+ listMembers: async (orgId) => await this.request("GET", `/orgs/${orgId}/members`, { canRetry: true }).then((res) => res.body),
188
+ listMembersAll: (orgId) => listSingle(this, `/orgs/${orgId}/members`),
189
+ listAuditLogs: async (orgId, query) => await this.request("GET", `/orgs/${orgId}/audit-logs`, { query, canRetry: true }).then((res) => res.body),
190
+ listAuditLogsAll: (orgId, query) => listPaginated(this, `/orgs/${orgId}/audit-logs`, query),
191
+ listRelayerJobs: async (orgId, query) => await this.request("GET", `/orgs/${orgId}/relayer-jobs`, { query, canRetry: true }).then((res) => res.body),
192
+ getRelayerJob: async (orgId, jobId) => await this.request("GET", `/orgs/${orgId}/relayer-jobs/${jobId}`, { canRetry: true }).then((res) => res.body),
193
+ listDeadRelayerJobs: async (orgId) => await this.request("GET", `/orgs/${orgId}/relayer-jobs/dead`, { canRetry: true }).then((res) => res.body),
194
+ retryRelayerJob: async (orgId, jobId) => await this.request("POST", `/orgs/${orgId}/relayer-jobs/${jobId}/retry`, { canRetry: false }).then((res) => res.body),
195
+ backfillOrg: async (orgId) => await this.request("POST", `/orgs/${orgId}/relayer-jobs/backfill`, { canRetry: false }).then((res) => res.body),
196
+ listInvites: async (orgId, query) => await this.request("GET", `/orgs/${orgId}/invites`, { query, canRetry: true }).then((res) => res.body),
197
+ listInvitesAll: (orgId, query) => listSingle(this, `/orgs/${orgId}/invites`, query),
198
+ inviteMember: async (orgId, payload, options) => await this.request("POST", `/orgs/${orgId}/invites`, {
199
+ ...options,
200
+ body: payload,
201
+ canRetry: true,
202
+ idempotencyKey: options?.idempotencyKey,
203
+ }).then((res) => res.body),
204
+ acceptInvite: async (orgId, token) => await this.request("POST", `/orgs/${orgId}/invites/${token}/accept`, { canRetry: false }).then((res) => res.body),
205
+ lookupUserByEmail: async (orgId, email) => await this.request("GET", `/orgs/${orgId}/users`, { query: { email }, canRetry: true }).then((res) => res.body),
206
+ listPermissions: async (orgId) => await this.request("GET", `/orgs/${orgId}/permissions`, { canRetry: true }).then((res) => res.body),
207
+ addPermission: async (orgId, payload) => await this.request("POST", `/orgs/${orgId}/permissions`, { body: payload, canRetry: false }).then((res) => res.body),
208
+ removePermission: async (orgId, payload) => await this.request("DELETE", `/orgs/${orgId}/permissions`, {
209
+ body: payload,
210
+ canRetry: false,
211
+ }).then((res) => res.body),
212
+ setEventDepartment: async (orgId, payload) => await this.request("POST", `/orgs/${orgId}/event-departments`, { body: payload, canRetry: false }).then((res) => res.body),
213
+ };
214
+ relayerJobs = {
215
+ listAll: (orgId, query) => listRelayerJobsAll(this, orgId, query),
216
+ get: async (orgId, jobId) => this.orgs.getRelayerJob(orgId, jobId).then((res) => res.data),
217
+ waitFor: async (orgId, jobId, options) => {
218
+ const timeoutMs = options?.timeoutMs ?? 60000;
219
+ const intervalMs = options?.intervalMs ?? 2000;
220
+ const start = Date.now();
221
+ while (Date.now() - start < timeoutMs) {
222
+ const job = await this.relayerJobs.get(orgId, jobId);
223
+ if (job && (job.status === "completed" || job.status === "dead" || job.status === "failed")) {
224
+ return job;
225
+ }
226
+ await new Promise((resolve) => setTimeout(resolve, intervalMs));
227
+ }
228
+ throw new ApiError("Timeout waiting for relayer job");
229
+ },
230
+ waitForRequest: async (orgId, requestId, options) => {
231
+ const timeoutMs = options?.timeoutMs ?? 60000;
232
+ const intervalMs = options?.intervalMs ?? 2000;
233
+ const start = Date.now();
234
+ while (Date.now() - start < timeoutMs) {
235
+ const jobs = await this.orgs.listRelayerJobs(orgId, { requestId, limit: 10, offset: 0 });
236
+ const job = jobs.data?.[0];
237
+ if (job) {
238
+ return job;
239
+ }
240
+ await new Promise((resolve) => setTimeout(resolve, intervalMs));
241
+ }
242
+ throw new ApiError("Timeout waiting for request relayer job");
243
+ },
244
+ };
245
+ batches = {
246
+ create: async (payload, options) => await this.request("POST", "/batches", {
247
+ ...options,
248
+ body: payload,
249
+ canRetry: true,
250
+ idempotencyKey: options?.idempotencyKey,
251
+ }).then((res) => res.body),
252
+ addPublicAttribute: async (batchId, payload, options) => await this.request("POST", `/batches/${batchId}/public-attributes`, {
253
+ ...options,
254
+ body: payload,
255
+ canRetry: true,
256
+ idempotencyKey: options?.idempotencyKey,
257
+ }).then((res) => res.body),
258
+ addEvent: async (batchId, payload, options) => await this.request("POST", `/batches/${batchId}/events`, {
259
+ ...options,
260
+ body: payload,
261
+ canRetry: Boolean(options?.idempotencyKey),
262
+ idempotencyKey: options?.idempotencyKey,
263
+ }).then((res) => res.body),
264
+ addDocument: async (batchId, payload, options) => await this.request("POST", `/batches/${batchId}/documents`, {
265
+ ...options,
266
+ body: payload,
267
+ canRetry: true,
268
+ idempotencyKey: options?.idempotencyKey,
269
+ }).then((res) => res.body),
270
+ addSignature: async (batchId, payload, options) => await this.request("POST", `/batches/${batchId}/signatures`, {
271
+ ...options,
272
+ body: payload,
273
+ canRetry: Boolean(options?.idempotencyKey),
274
+ idempotencyKey: options?.idempotencyKey,
275
+ }).then((res) => res.body),
276
+ addCertification: async (batchId, payload, options) => await this.request("POST", `/batches/${batchId}/certifications`, {
277
+ ...options,
278
+ body: payload,
279
+ canRetry: Boolean(options?.idempotencyKey),
280
+ idempotencyKey: options?.idempotencyKey,
281
+ }).then((res) => res.body),
282
+ getAuditPackManifest: async (batchId) => await this.request("GET", `/batches/${batchId}/audit-pack/manifest`, { canRetry: true }).then((res) => res.body),
283
+ listRelayerJobs: async (batchId, query) => await this.request("GET", `/batches/${batchId}/relayer-jobs`, { query, canRetry: true }).then((res) => res.body),
284
+ listRelayerJobsAll: (batchId, query) => listPaginated(this, `/batches/${batchId}/relayer-jobs`, query),
285
+ backfillBatch: async (batchId) => await this.request("POST", `/batches/${batchId}/relayer-jobs/backfill`, { canRetry: false }).then((res) => res.body),
286
+ };
287
+ devices = {
288
+ register: async (payload, options) => await this.request("POST", "/devices", {
289
+ ...options,
290
+ body: payload,
291
+ canRetry: true,
292
+ idempotencyKey: options?.idempotencyKey,
293
+ }).then((res) => res.body),
294
+ update: async (deviceId, payload, options) => await this.request("PATCH", `/devices/${deviceId}`, {
295
+ ...options,
296
+ body: payload,
297
+ canRetry: Boolean(options?.idempotencyKey),
298
+ idempotencyKey: options?.idempotencyKey,
299
+ }).then((res) => res.body),
300
+ setStatus: async (deviceId, payload, options) => await this.request("PATCH", `/devices/${deviceId}/status`, {
301
+ ...options,
302
+ body: payload,
303
+ canRetry: Boolean(options?.idempotencyKey),
304
+ idempotencyKey: options?.idempotencyKey,
305
+ }).then((res) => res.body),
306
+ };
307
+ anchors = {
308
+ create: async (payload, options) => await this.request("POST", "/anchors", {
309
+ ...options,
310
+ body: payload,
311
+ canRetry: true,
312
+ idempotencyKey: options?.idempotencyKey,
313
+ }).then((res) => res.body),
314
+ };
315
+ auditPacks = {
316
+ create: async (payload, options) => await this.request("POST", "/audit-packs", {
317
+ ...options,
318
+ body: payload,
319
+ canRetry: true,
320
+ idempotencyKey: options?.idempotencyKey,
321
+ }).then((res) => res.body),
322
+ downloadPdf: async (pdfUri) => {
323
+ const response = await fetch(pdfUri);
324
+ if (!response.ok) {
325
+ throw new ApiError(`Failed to download PDF: ${response.status}`, response.status);
326
+ }
327
+ return response.arrayBuffer();
328
+ },
329
+ downloadJson: async (jsonUri) => {
330
+ const response = await fetch(jsonUri);
331
+ if (!response.ok) {
332
+ throw new ApiError(`Failed to download JSON: ${response.status}`, response.status);
333
+ }
334
+ return response.text();
335
+ },
336
+ };
337
+ holograms = {
338
+ issue: async (payload, options) => await this.request("POST", "/holograms", {
339
+ ...options,
340
+ body: payload,
341
+ canRetry: true,
342
+ idempotencyKey: options?.idempotencyKey,
343
+ }).then((res) => res.body),
344
+ revoke: async (payload, options) => await this.request("POST", "/holograms/revoke", {
345
+ ...options,
346
+ body: payload,
347
+ canRetry: true,
348
+ idempotencyKey: options?.idempotencyKey,
349
+ }).then((res) => res.body),
350
+ verify: async (publicCode) => {
351
+ const response = await this.request("GET", `/verify/holograms/${publicCode}`, { canRetry: true });
352
+ if (response.body.status !== "ok") {
353
+ return null;
354
+ }
355
+ return response.body.data ?? null;
356
+ },
357
+ };
358
+ }
package/dist/http.d.ts ADDED
@@ -0,0 +1,70 @@
1
+ export type AuthOptions = {
2
+ token: string;
3
+ } | {
4
+ getToken: () => Promise<string> | string;
5
+ } | undefined;
6
+ export type RetryOptions = {
7
+ enabled?: boolean;
8
+ maxRetries?: number;
9
+ baseDelayMs?: number;
10
+ maxDelayMs?: number;
11
+ jitterMs?: number;
12
+ };
13
+ export type ClientHooks = {
14
+ onRequest?: (context: RequestContext) => void | Promise<void>;
15
+ onResponse?: (context: ResponseContext) => void | Promise<void>;
16
+ onError?: (context: ErrorContext) => void | Promise<void>;
17
+ };
18
+ export type RequestContext = {
19
+ method: string;
20
+ url: string;
21
+ headers: Record<string, string>;
22
+ body?: unknown;
23
+ requestId: string;
24
+ };
25
+ export type ResponseContext = {
26
+ method: string;
27
+ url: string;
28
+ status: number;
29
+ requestId: string;
30
+ durationMs: number;
31
+ };
32
+ export type ErrorContext = {
33
+ method: string;
34
+ url: string;
35
+ status?: number;
36
+ requestId: string;
37
+ durationMs: number;
38
+ error: Error;
39
+ };
40
+ export declare class ApiError extends Error {
41
+ status?: number;
42
+ body?: unknown;
43
+ requestId?: string;
44
+ constructor(message: string, status?: number, body?: unknown, requestId?: string);
45
+ }
46
+ export type RequestOptions = {
47
+ headers?: Record<string, string>;
48
+ body?: unknown;
49
+ query?: Record<string, string | number | boolean | undefined | null>;
50
+ timeoutMs?: number;
51
+ idempotencyKey?: string;
52
+ allowInsecure?: boolean;
53
+ retry?: RetryOptions;
54
+ auth?: AuthOptions;
55
+ onAuthError?: (error: ApiError) => void | Promise<void>;
56
+ requestId?: string;
57
+ sdkHeaders?: Record<string, string>;
58
+ hooks?: ClientHooks;
59
+ signingHook?: (context: RequestContext) => Promise<Record<string, string> | void> | Record<string, string> | void;
60
+ canRetry?: boolean;
61
+ };
62
+ export declare const request: <T>(baseUrl: string, path: string, method: string, options: RequestOptions) => Promise<{
63
+ body: T;
64
+ requestId: string;
65
+ }>;
66
+ export declare const requestRaw: (baseUrl: string, path: string, method: string, options: RequestOptions) => Promise<{
67
+ body: unknown;
68
+ requestId: string;
69
+ headers: Headers;
70
+ }>;