vesant-sdk 1.2.0 → 1.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.
Files changed (71) hide show
  1. package/dist/{client-BWp5FI3x.d.ts → client-B6fUFAUM.d.mts} +2 -1
  2. package/dist/{client-BIfLMfuC.d.mts → client-DoczGA6L.d.ts} +2 -1
  3. package/dist/client-DzElM7u-.d.mts +238 -0
  4. package/dist/client-DzElM7u-.d.ts +238 -0
  5. package/dist/compliance/index.d.mts +5 -4
  6. package/dist/compliance/index.d.ts +5 -4
  7. package/dist/compliance/index.js +306 -98
  8. package/dist/compliance/index.js.map +1 -1
  9. package/dist/compliance/index.mjs +306 -98
  10. package/dist/compliance/index.mjs.map +1 -1
  11. package/dist/decisions/index.d.mts +100 -0
  12. package/dist/decisions/index.d.ts +100 -0
  13. package/dist/decisions/index.js +607 -0
  14. package/dist/decisions/index.js.map +1 -0
  15. package/dist/decisions/index.mjs +605 -0
  16. package/dist/decisions/index.mjs.map +1 -0
  17. package/dist/geolocation/index.d.mts +4 -3
  18. package/dist/geolocation/index.d.ts +4 -3
  19. package/dist/geolocation/index.js +306 -98
  20. package/dist/geolocation/index.js.map +1 -1
  21. package/dist/geolocation/index.mjs +306 -98
  22. package/dist/geolocation/index.mjs.map +1 -1
  23. package/dist/index.d.mts +15 -7
  24. package/dist/index.d.ts +15 -7
  25. package/dist/index.js +676 -90
  26. package/dist/index.js.map +1 -1
  27. package/dist/index.mjs +667 -91
  28. package/dist/index.mjs.map +1 -1
  29. package/dist/kyc/core.d.mts +4 -3
  30. package/dist/kyc/core.d.ts +4 -3
  31. package/dist/kyc/core.js +284 -29
  32. package/dist/kyc/core.js.map +1 -1
  33. package/dist/kyc/core.mjs +284 -29
  34. package/dist/kyc/core.mjs.map +1 -1
  35. package/dist/kyc/index.d.mts +46 -3
  36. package/dist/kyc/index.d.ts +46 -3
  37. package/dist/kyc/index.js +284 -29
  38. package/dist/kyc/index.js.map +1 -1
  39. package/dist/kyc/index.mjs +284 -29
  40. package/dist/kyc/index.mjs.map +1 -1
  41. package/dist/react.d.mts +9 -5
  42. package/dist/react.d.ts +9 -5
  43. package/dist/react.js +422 -99
  44. package/dist/react.js.map +1 -1
  45. package/dist/react.mjs +322 -4
  46. package/dist/react.mjs.map +1 -1
  47. package/dist/risk-profile/index.d.mts +4 -4
  48. package/dist/risk-profile/index.d.ts +4 -4
  49. package/dist/risk-profile/index.js +249 -29
  50. package/dist/risk-profile/index.js.map +1 -1
  51. package/dist/risk-profile/index.mjs +249 -29
  52. package/dist/risk-profile/index.mjs.map +1 -1
  53. package/dist/scores/index.d.mts +96 -0
  54. package/dist/scores/index.d.ts +96 -0
  55. package/dist/scores/index.js +594 -0
  56. package/dist/scores/index.js.map +1 -0
  57. package/dist/scores/index.mjs +591 -0
  58. package/dist/scores/index.mjs.map +1 -0
  59. package/dist/{types-DfHLp_tz.d.ts → types-DLC7Sfy5.d.ts} +1 -1
  60. package/dist/types-DZHongaK.d.mts +61 -0
  61. package/dist/types-DZHongaK.d.ts +61 -0
  62. package/dist/{types-DKCQN4C5.d.mts → types-jaLuzruy.d.mts} +1 -1
  63. package/dist/webhooks/index.d.mts +176 -0
  64. package/dist/webhooks/index.d.ts +176 -0
  65. package/dist/webhooks/index.js +193 -0
  66. package/dist/webhooks/index.js.map +1 -0
  67. package/dist/webhooks/index.mjs +188 -0
  68. package/dist/webhooks/index.mjs.map +1 -0
  69. package/package.json +25 -2
  70. package/dist/types-BpKxSXGF.d.mts +0 -177
  71. package/dist/types-BpKxSXGF.d.ts +0 -177
package/dist/index.mjs CHANGED
@@ -69,6 +69,146 @@ var ComplianceError = class _ComplianceError extends CGSError {
69
69
  Object.setPrototypeOf(this, _ComplianceError.prototype);
70
70
  }
71
71
  };
72
+ var CircuitBreakerOpenError = class _CircuitBreakerOpenError extends CGSError {
73
+ constructor() {
74
+ super("Circuit breaker is open \u2014 requests are temporarily blocked", "CIRCUIT_BREAKER_OPEN", 503);
75
+ this.name = "CircuitBreakerOpenError";
76
+ Object.setPrototypeOf(this, _CircuitBreakerOpenError.prototype);
77
+ }
78
+ };
79
+
80
+ // src/core/circuit-breaker.ts
81
+ var CircuitBreaker = class {
82
+ constructor(config = {}) {
83
+ this.state = "closed";
84
+ this.failures = 0;
85
+ this.successes = 0;
86
+ this.lastFailureTime = null;
87
+ this.failureThreshold = config.failureThreshold ?? 5;
88
+ this.resetTimeout = config.resetTimeout ?? 3e4;
89
+ this.successThreshold = config.successThreshold ?? 1;
90
+ }
91
+ /**
92
+ * Check if a request can proceed through the circuit breaker.
93
+ */
94
+ canExecute() {
95
+ if (this.state === "closed") {
96
+ return true;
97
+ }
98
+ if (this.state === "open") {
99
+ const now = Date.now();
100
+ if (this.lastFailureTime && now - this.lastFailureTime >= this.resetTimeout) {
101
+ this.state = "half-open";
102
+ this.successes = 0;
103
+ return true;
104
+ }
105
+ return false;
106
+ }
107
+ return true;
108
+ }
109
+ /**
110
+ * Record a successful request.
111
+ */
112
+ onSuccess() {
113
+ if (this.state === "half-open") {
114
+ this.successes++;
115
+ if (this.successes >= this.successThreshold) {
116
+ this.state = "closed";
117
+ this.failures = 0;
118
+ this.successes = 0;
119
+ this.lastFailureTime = null;
120
+ }
121
+ } else if (this.state === "closed") {
122
+ this.failures = 0;
123
+ }
124
+ }
125
+ /**
126
+ * Record a failed request.
127
+ */
128
+ onFailure() {
129
+ this.failures++;
130
+ this.lastFailureTime = Date.now();
131
+ if (this.state === "half-open") {
132
+ this.state = "open";
133
+ this.successes = 0;
134
+ } else if (this.state === "closed" && this.failures >= this.failureThreshold) {
135
+ this.state = "open";
136
+ }
137
+ }
138
+ /**
139
+ * Get current circuit breaker status.
140
+ */
141
+ getStatus() {
142
+ return {
143
+ state: this.state,
144
+ failures: this.failures,
145
+ successes: this.successes,
146
+ lastFailureTime: this.lastFailureTime,
147
+ nextRetryTime: this.state === "open" && this.lastFailureTime ? this.lastFailureTime + this.resetTimeout : null
148
+ };
149
+ }
150
+ /**
151
+ * Reset the circuit breaker to its initial closed state.
152
+ */
153
+ reset() {
154
+ this.state = "closed";
155
+ this.failures = 0;
156
+ this.successes = 0;
157
+ this.lastFailureTime = null;
158
+ }
159
+ };
160
+
161
+ // src/core/rate-limiter.ts
162
+ var RateLimitTracker = class {
163
+ constructor() {
164
+ this.limit = null;
165
+ this.remaining = null;
166
+ this.reset = null;
167
+ this.retryAfter = null;
168
+ }
169
+ /**
170
+ * Extract rate limit information from response headers.
171
+ */
172
+ updateFromHeaders(headers) {
173
+ const limit = headers.get("x-ratelimit-limit");
174
+ const remaining = headers.get("x-ratelimit-remaining");
175
+ const reset = headers.get("x-ratelimit-reset");
176
+ const retryAfter = headers.get("retry-after");
177
+ if (limit !== null) this.limit = parseInt(limit, 10);
178
+ if (remaining !== null) this.remaining = parseInt(remaining, 10);
179
+ if (reset !== null) this.reset = parseInt(reset, 10);
180
+ if (retryAfter !== null) this.retryAfter = parseInt(retryAfter, 10);
181
+ }
182
+ /**
183
+ * Check if the rate limit has been exceeded based on tracked headers.
184
+ */
185
+ isLimitExceeded() {
186
+ if (this.remaining !== null && this.remaining <= 0) {
187
+ if (this.reset !== null) {
188
+ const now = Math.floor(Date.now() / 1e3);
189
+ if (now >= this.reset) {
190
+ this.remaining = null;
191
+ this.reset = null;
192
+ this.retryAfter = null;
193
+ return false;
194
+ }
195
+ }
196
+ return true;
197
+ }
198
+ return false;
199
+ }
200
+ /**
201
+ * Get current rate limit status.
202
+ */
203
+ getStatus() {
204
+ return {
205
+ limit: this.limit,
206
+ remaining: this.remaining,
207
+ reset: this.reset,
208
+ retryAfter: this.retryAfter
209
+ };
210
+ }
211
+ };
72
212
 
73
213
  // src/core/logger.ts
74
214
  function createConsoleLogger() {
@@ -99,11 +239,82 @@ var noopLogger = {
99
239
  };
100
240
 
101
241
  // src/core/version.ts
102
- var SDK_VERSION = "1.2.0";
242
+ var SDK_VERSION = "1.3.0";
243
+
244
+ // src/shared/browser-utils.ts
245
+ function generateUUID() {
246
+ if (typeof crypto !== "undefined" && crypto.randomUUID) {
247
+ return crypto.randomUUID();
248
+ }
249
+ return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
250
+ const r = Math.random() * 16 | 0;
251
+ const v = c === "x" ? r : r & 3 | 8;
252
+ return v.toString(16);
253
+ });
254
+ }
255
+ function generateDeviceId() {
256
+ if (typeof window === "undefined" || typeof localStorage === "undefined") {
257
+ return generateUUID();
258
+ }
259
+ const storageKey = "cgs_device_id";
260
+ let deviceId = localStorage.getItem(storageKey);
261
+ if (!deviceId) {
262
+ deviceId = generateUUID();
263
+ localStorage.setItem(storageKey, deviceId);
264
+ }
265
+ return deviceId;
266
+ }
267
+ function getBrowserInfo() {
268
+ if (typeof navigator === "undefined") {
269
+ return { browser: "unknown", browser_version: "", os: "unknown", os_version: "" };
270
+ }
271
+ const ua = navigator.userAgent;
272
+ let browser = "unknown";
273
+ let browserVersion = "";
274
+ let os = "unknown";
275
+ let osVersion = "";
276
+ if (ua.includes("Firefox/")) {
277
+ browser = "Firefox";
278
+ browserVersion = ua.match(/Firefox\/([\d.]+)/)?.[1] || "";
279
+ } else if (ua.includes("Edg/")) {
280
+ browser = "Edge";
281
+ browserVersion = ua.match(/Edg\/([\d.]+)/)?.[1] || "";
282
+ } else if (ua.includes("Chrome/")) {
283
+ browser = "Chrome";
284
+ browserVersion = ua.match(/Chrome\/([\d.]+)/)?.[1] || "";
285
+ } else if (ua.includes("Safari/") && !ua.includes("Chrome")) {
286
+ browser = "Safari";
287
+ browserVersion = ua.match(/Version\/([\d.]+)/)?.[1] || "";
288
+ } else if (ua.includes("Opera") || ua.includes("OPR/")) {
289
+ browser = "Opera";
290
+ browserVersion = ua.match(/(?:Opera|OPR)\/([\d.]+)/)?.[1] || "";
291
+ }
292
+ if (ua.includes("Windows")) {
293
+ os = "Windows";
294
+ if (ua.includes("Windows NT 10.0")) osVersion = "10";
295
+ else if (ua.includes("Windows NT 6.3")) osVersion = "8.1";
296
+ else if (ua.includes("Windows NT 6.2")) osVersion = "8";
297
+ else if (ua.includes("Windows NT 6.1")) osVersion = "7";
298
+ } else if (ua.includes("Mac OS X")) {
299
+ os = "macOS";
300
+ osVersion = ua.match(/Mac OS X ([\d_]+)/)?.[1]?.replace(/_/g, ".") || "";
301
+ } else if (ua.includes("Linux")) {
302
+ os = "Linux";
303
+ } else if (ua.includes("Android")) {
304
+ os = "Android";
305
+ osVersion = ua.match(/Android ([\d.]+)/)?.[1] || "";
306
+ } else if (ua.includes("iOS") || ua.includes("iPhone") || ua.includes("iPad")) {
307
+ os = "iOS";
308
+ osVersion = ua.match(/OS ([\d_]+)/)?.[1]?.replace(/_/g, ".") || "";
309
+ }
310
+ return { browser, browser_version: browserVersion, os, os_version: osVersion };
311
+ }
103
312
 
104
313
  // src/core/client.ts
105
314
  var BaseClient = class {
106
315
  constructor(config) {
316
+ this.circuitBreaker = null;
317
+ this.rateLimitTracker = null;
107
318
  if (!config.baseURL?.trim()) {
108
319
  throw new ValidationError("baseURL is required and must be a non-empty string", ["baseURL"]);
109
320
  }
@@ -113,8 +324,7 @@ var BaseClient = class {
113
324
  this.interceptors = config.interceptors || [];
114
325
  this.logger = config.logger || createConsoleLogger();
115
326
  this.config = {
116
- baseURL: config.baseURL,
117
- tenantId: config.tenantId,
327
+ ...config,
118
328
  apiKey: config.apiKey || "",
119
329
  headers: config.headers || {},
120
330
  timeout: config.timeout || 1e4,
@@ -123,22 +333,49 @@ var BaseClient = class {
123
333
  interceptors: this.interceptors,
124
334
  logger: this.logger
125
335
  };
336
+ if (config.circuitBreaker) {
337
+ this.circuitBreaker = new CircuitBreaker(config.circuitBreaker);
338
+ }
339
+ if (config.enableRateLimitTracking) {
340
+ this.rateLimitTracker = new RateLimitTracker();
341
+ }
126
342
  }
127
343
  /**
128
344
  * Make an HTTP request with timeout and error handling
129
345
  */
130
346
  async request(endpoint, options = {}, serviceURL, requestOptions) {
131
- const url = `${serviceURL || this.config.baseURL}${endpoint}`;
347
+ const requestId = generateUUID();
348
+ if (this.circuitBreaker && !this.circuitBreaker.canExecute()) {
349
+ const error = new CircuitBreakerOpenError();
350
+ error.requestId = requestId;
351
+ throw error;
352
+ }
353
+ if (this.rateLimitTracker && this.rateLimitTracker.isLimitExceeded()) {
354
+ const status = this.rateLimitTracker.getStatus();
355
+ const error = new RateLimitError(status.retryAfter ?? void 0);
356
+ error.requestId = requestId;
357
+ throw error;
358
+ }
359
+ const baseURL = this.config.environment === "sandbox" && this.config.sandboxBaseURL ? this.config.sandboxBaseURL : serviceURL || this.config.baseURL;
360
+ const url = `${serviceURL || baseURL}${endpoint}`;
132
361
  const headers = {
133
362
  "Content-Type": "application/json",
134
363
  "X-Tenant-ID": this.config.tenantId,
135
364
  "X-SDK-Version": `vesant-sdk-ts/${SDK_VERSION}`,
365
+ "X-Request-ID": requestId,
136
366
  ...this.config.headers,
137
367
  ...options.headers || {}
138
368
  };
139
369
  if (this.config.apiKey) {
140
370
  headers["Authorization"] = `Bearer ${this.config.apiKey}`;
141
371
  }
372
+ if (this.config.environment === "sandbox") {
373
+ headers["X-Sandbox"] = "true";
374
+ }
375
+ const method = (options.method || "GET").toUpperCase();
376
+ if (["POST", "PUT", "PATCH"].includes(method)) {
377
+ headers["Idempotency-Key"] = requestOptions?.idempotencyKey || generateUUID();
378
+ }
142
379
  const controller = new AbortController();
143
380
  const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);
144
381
  if (requestOptions?.signal) {
@@ -166,6 +403,9 @@ var BaseClient = class {
166
403
  signal: controller.signal
167
404
  });
168
405
  clearTimeout(timeoutId);
406
+ if (this.rateLimitTracker) {
407
+ this.rateLimitTracker.updateFromHeaders(response.headers);
408
+ }
169
409
  let data;
170
410
  try {
171
411
  data = await response.json();
@@ -174,13 +414,15 @@ var BaseClient = class {
174
414
  this.handleErrorResponse(response.status, {
175
415
  error: `HTTP ${response.status}`,
176
416
  message: response.statusText
177
- });
417
+ }, requestId);
178
418
  }
419
+ this.circuitBreaker?.onSuccess();
179
420
  return void 0;
180
421
  }
181
422
  if (!response.ok) {
182
- this.handleErrorResponse(response.status, data || {});
423
+ this.handleErrorResponse(response.status, data || {}, requestId);
183
424
  }
425
+ this.circuitBreaker?.onSuccess();
184
426
  let result = data;
185
427
  for (const interceptor of this.interceptors) {
186
428
  if (interceptor.onResponse) {
@@ -193,6 +435,14 @@ var BaseClient = class {
193
435
  return result;
194
436
  } catch (error) {
195
437
  clearTimeout(timeoutId);
438
+ if (error instanceof CGSError && error.statusCode && error.statusCode >= 500) {
439
+ this.circuitBreaker?.onFailure();
440
+ } else if (error instanceof NetworkError || error instanceof TimeoutError) {
441
+ this.circuitBreaker?.onFailure();
442
+ }
443
+ if (error instanceof CGSError && !error.requestId) {
444
+ error.requestId = requestId;
445
+ }
196
446
  if (error instanceof Error) {
197
447
  for (const interceptor of this.interceptors) {
198
448
  if (interceptor.onError) {
@@ -203,15 +453,23 @@ var BaseClient = class {
203
453
  if (error instanceof Error) {
204
454
  if (error.name === "AbortError") {
205
455
  if (requestOptions?.signal?.aborted) {
206
- throw new CGSError("Request aborted", "REQUEST_ABORTED");
456
+ const abortError = new CGSError("Request aborted", "REQUEST_ABORTED");
457
+ abortError.requestId = requestId;
458
+ throw abortError;
207
459
  }
208
- throw new TimeoutError(this.config.timeout);
460
+ this.circuitBreaker?.onFailure();
461
+ const timeoutError = new TimeoutError(this.config.timeout);
462
+ timeoutError.requestId = requestId;
463
+ throw timeoutError;
209
464
  }
210
465
  if (error instanceof CGSError) {
211
466
  throw error;
212
467
  }
213
468
  }
214
- throw new NetworkError("Network request failed", error);
469
+ const networkError = new NetworkError("Network request failed", error);
470
+ networkError.requestId = requestId;
471
+ this.circuitBreaker?.onFailure();
472
+ throw networkError;
215
473
  }
216
474
  }
217
475
  /**
@@ -250,29 +508,36 @@ var BaseClient = class {
250
508
  /**
251
509
  * Handle error responses from API
252
510
  */
253
- handleErrorResponse(status, data) {
511
+ handleErrorResponse(status, data, requestId) {
254
512
  const message = data.error || data.message || `HTTP ${status}`;
255
- switch (status) {
256
- case 400:
257
- throw new CGSError(message, "BAD_REQUEST", 400, data);
258
- case 401:
259
- throw new AuthenticationError(message);
260
- case 403:
261
- throw new CGSError(message, "FORBIDDEN", 403, data);
262
- case 404:
263
- throw new CGSError(message, "NOT_FOUND", 404, data);
264
- case 429: {
265
- const retryAfter = data.retry_after || data.retryAfter;
266
- throw new RateLimitError(retryAfter);
513
+ const createError = () => {
514
+ switch (status) {
515
+ case 400:
516
+ return new CGSError(message, "BAD_REQUEST", 400, data);
517
+ case 401:
518
+ return new AuthenticationError(message);
519
+ case 403:
520
+ return new CGSError(message, "FORBIDDEN", 403, data);
521
+ case 404:
522
+ return new CGSError(message, "NOT_FOUND", 404, data);
523
+ case 429: {
524
+ const retryAfter = data.retry_after || data.retryAfter;
525
+ return new RateLimitError(retryAfter);
526
+ }
527
+ case 500:
528
+ case 502:
529
+ case 503:
530
+ case 504:
531
+ return new ServiceUnavailableError(message);
532
+ default:
533
+ return new CGSError(message, "UNKNOWN_ERROR", status, data);
267
534
  }
268
- case 500:
269
- case 502:
270
- case 503:
271
- case 504:
272
- throw new ServiceUnavailableError(message);
273
- default:
274
- throw new CGSError(message, "UNKNOWN_ERROR", status, data);
535
+ };
536
+ const error = createError();
537
+ if (requestId) {
538
+ error.requestId = requestId;
275
539
  }
540
+ throw error;
276
541
  }
277
542
  /**
278
543
  * Build query string from parameters
@@ -316,6 +581,18 @@ var BaseClient = class {
316
581
  getConfig() {
317
582
  return { ...this.config };
318
583
  }
584
+ /**
585
+ * Get rate limit status from tracked response headers.
586
+ */
587
+ getRateLimitStatus() {
588
+ return this.rateLimitTracker?.getStatus() ?? null;
589
+ }
590
+ /**
591
+ * Get circuit breaker status.
592
+ */
593
+ getCircuitBreakerStatus() {
594
+ return this.circuitBreaker?.getStatus() ?? null;
595
+ }
319
596
  /**
320
597
  * Health check endpoint
321
598
  */
@@ -324,73 +601,46 @@ var BaseClient = class {
324
601
  }
325
602
  };
326
603
 
327
- // src/shared/browser-utils.ts
328
- function generateUUID() {
329
- if (typeof crypto !== "undefined" && crypto.randomUUID) {
330
- return crypto.randomUUID();
331
- }
332
- return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
333
- const r = Math.random() * 16 | 0;
334
- const v = c === "x" ? r : r & 3 | 8;
335
- return v.toString(16);
336
- });
604
+ // src/core/webhook-utils.ts
605
+ async function verifyWebhookSignature(payload, signature, secret) {
606
+ const hexDigest = await computeHmacSha256(payload, secret);
607
+ const expectedPrefixed = `sha256=${hexDigest}`;
608
+ if (signature.startsWith("sha256=")) {
609
+ return constantTimeEqual(signature, expectedPrefixed);
610
+ }
611
+ return constantTimeEqual(signature, hexDigest);
337
612
  }
338
- function generateDeviceId() {
339
- if (typeof window === "undefined" || typeof localStorage === "undefined") {
340
- return generateUUID();
613
+ async function computeHmacSha256(message, secret) {
614
+ if (typeof globalThis.crypto !== "undefined" && globalThis.crypto.subtle) {
615
+ const encoder = new TextEncoder();
616
+ const key = await globalThis.crypto.subtle.importKey(
617
+ "raw",
618
+ encoder.encode(secret),
619
+ { name: "HMAC", hash: "SHA-256" },
620
+ false,
621
+ ["sign"]
622
+ );
623
+ const sig = await globalThis.crypto.subtle.sign("HMAC", key, encoder.encode(message));
624
+ return Array.from(new Uint8Array(sig)).map((b) => b.toString(16).padStart(2, "0")).join("");
341
625
  }
342
- const storageKey = "cgs_device_id";
343
- let deviceId = localStorage.getItem(storageKey);
344
- if (!deviceId) {
345
- deviceId = generateUUID();
346
- localStorage.setItem(storageKey, deviceId);
626
+ try {
627
+ const { createHmac } = await import('crypto');
628
+ return createHmac("sha256", secret).update(message).digest("hex");
629
+ } catch {
630
+ throw new Error(
631
+ "No crypto implementation available. Requires Web Crypto API or Node.js crypto module."
632
+ );
347
633
  }
348
- return deviceId;
349
634
  }
350
- function getBrowserInfo() {
351
- if (typeof navigator === "undefined") {
352
- return { browser: "unknown", browser_version: "", os: "unknown", os_version: "" };
635
+ function constantTimeEqual(a, b) {
636
+ if (a.length !== b.length) {
637
+ return false;
353
638
  }
354
- const ua = navigator.userAgent;
355
- let browser = "unknown";
356
- let browserVersion = "";
357
- let os = "unknown";
358
- let osVersion = "";
359
- if (ua.includes("Firefox/")) {
360
- browser = "Firefox";
361
- browserVersion = ua.match(/Firefox\/([\d.]+)/)?.[1] || "";
362
- } else if (ua.includes("Edg/")) {
363
- browser = "Edge";
364
- browserVersion = ua.match(/Edg\/([\d.]+)/)?.[1] || "";
365
- } else if (ua.includes("Chrome/")) {
366
- browser = "Chrome";
367
- browserVersion = ua.match(/Chrome\/([\d.]+)/)?.[1] || "";
368
- } else if (ua.includes("Safari/") && !ua.includes("Chrome")) {
369
- browser = "Safari";
370
- browserVersion = ua.match(/Version\/([\d.]+)/)?.[1] || "";
371
- } else if (ua.includes("Opera") || ua.includes("OPR/")) {
372
- browser = "Opera";
373
- browserVersion = ua.match(/(?:Opera|OPR)\/([\d.]+)/)?.[1] || "";
374
- }
375
- if (ua.includes("Windows")) {
376
- os = "Windows";
377
- if (ua.includes("Windows NT 10.0")) osVersion = "10";
378
- else if (ua.includes("Windows NT 6.3")) osVersion = "8.1";
379
- else if (ua.includes("Windows NT 6.2")) osVersion = "8";
380
- else if (ua.includes("Windows NT 6.1")) osVersion = "7";
381
- } else if (ua.includes("Mac OS X")) {
382
- os = "macOS";
383
- osVersion = ua.match(/Mac OS X ([\d_]+)/)?.[1]?.replace(/_/g, ".") || "";
384
- } else if (ua.includes("Linux")) {
385
- os = "Linux";
386
- } else if (ua.includes("Android")) {
387
- os = "Android";
388
- osVersion = ua.match(/Android ([\d.]+)/)?.[1] || "";
389
- } else if (ua.includes("iOS") || ua.includes("iPhone") || ua.includes("iPad")) {
390
- os = "iOS";
391
- osVersion = ua.match(/OS ([\d_]+)/)?.[1]?.replace(/_/g, ".") || "";
639
+ let result = 0;
640
+ for (let i = 0; i < a.length; i++) {
641
+ result |= a.charCodeAt(i) ^ b.charCodeAt(i);
392
642
  }
393
- return { browser, browser_version: browserVersion, os, os_version: osVersion };
643
+ return result === 0;
394
644
  }
395
645
 
396
646
  // src/geolocation/ciphertext.ts
@@ -2043,6 +2293,41 @@ var KycClient = class extends BaseClient {
2043
2293
  headers: this.getUserHeaders()
2044
2294
  });
2045
2295
  }
2296
+ /**
2297
+ * Create a reuse KYC session for validate a user with existing KYC verification
2298
+ *
2299
+ * @param request - Request containing the reference, customer_id, optional redirect URL, and callback URL (receives POST requests)
2300
+ */
2301
+ async createReuseKycSession(request) {
2302
+ return this.requestWithRetry("/api/v1/kyc/face/session", {
2303
+ method: "POST",
2304
+ body: JSON.stringify(request),
2305
+ headers: this.getUserHeaders()
2306
+ });
2307
+ }
2308
+ /**
2309
+ * Submit a reuse KYC session for validate a user with existing KYC verification
2310
+ *
2311
+ * @param request - Request containing the reference, token and proof (receives POST requests)
2312
+ */
2313
+ async submitReuseKycSession(request) {
2314
+ return this.requestWithRetry("/api/v1/kyc/face/submit", {
2315
+ method: "POST",
2316
+ body: JSON.stringify(request),
2317
+ headers: this.getUserHeaders()
2318
+ });
2319
+ }
2320
+ /**
2321
+ * Check reuse KYC session status for a reference
2322
+ * @param reference - The unique reference used for the reuse KYC session (e.g., customer ID or transaction ID)
2323
+ * @returns Response with kyc_status and message (reason)
2324
+ * **/
2325
+ async getReuseKycSessionStatus(reference) {
2326
+ return this.requestWithRetry(`/api/v1/kyc/face/verify/${encodeURIComponent(reference)}`, {
2327
+ method: "GET",
2328
+ headers: this.getUserHeaders()
2329
+ });
2330
+ }
2046
2331
  /**
2047
2332
  * Check KYC status for a user
2048
2333
  *
@@ -2514,6 +2799,297 @@ var KycClient = class extends BaseClient {
2514
2799
  // ============================================================================
2515
2800
  };
2516
2801
 
2517
- export { AuthenticationError, BaseClient, CGSError, ComplianceBlockedError, ComplianceClient, ComplianceError, DEFAULT_CURRENCY_RATES, GeolocationClient, KycClient, NetworkError, RateLimitError, RiskProfileClient, SDK_VERSION, ServiceUnavailableError, TimeoutError, ValidationError, createConsoleLogger, decodeCipherText, generateCipherText, isCipherTextExpired, noopLogger };
2802
+ // src/decisions/client.ts
2803
+ var DecisionsClient = class extends BaseClient {
2804
+ /**
2805
+ * Record a new decision for a customer.
2806
+ */
2807
+ async recordDecision(request, requestOptions) {
2808
+ return this.requestWithRetry(
2809
+ "/api/v1/decisions",
2810
+ { method: "POST", body: JSON.stringify(request) },
2811
+ void 0,
2812
+ void 0,
2813
+ requestOptions
2814
+ );
2815
+ }
2816
+ /**
2817
+ * Get decisions for a customer.
2818
+ */
2819
+ async getDecisions(customerId, filters, requestOptions) {
2820
+ const queryString = this.buildQueryString({
2821
+ customer_id: customerId,
2822
+ ...filters
2823
+ });
2824
+ return this.requestWithRetry(
2825
+ `/api/v1/decisions${queryString}`,
2826
+ { method: "GET" },
2827
+ void 0,
2828
+ void 0,
2829
+ requestOptions
2830
+ );
2831
+ }
2832
+ /**
2833
+ * Get a specific decision by ID.
2834
+ */
2835
+ async getDecision(decisionId, requestOptions) {
2836
+ return this.requestWithRetry(
2837
+ `/api/v1/decisions/${encodeURIComponent(decisionId)}`,
2838
+ { method: "GET" },
2839
+ void 0,
2840
+ void 0,
2841
+ requestOptions
2842
+ );
2843
+ }
2844
+ /**
2845
+ * Apply a label to a customer.
2846
+ */
2847
+ async applyLabel(request, requestOptions) {
2848
+ return this.requestWithRetry(
2849
+ "/api/v1/labels",
2850
+ { method: "POST", body: JSON.stringify(request) },
2851
+ void 0,
2852
+ void 0,
2853
+ requestOptions
2854
+ );
2855
+ }
2856
+ /**
2857
+ * Remove a label from a customer.
2858
+ */
2859
+ async removeLabel(customerId, requestOptions) {
2860
+ return this.requestWithRetry(
2861
+ `/api/v1/labels/${encodeURIComponent(customerId)}`,
2862
+ { method: "DELETE" },
2863
+ void 0,
2864
+ void 0,
2865
+ requestOptions
2866
+ );
2867
+ }
2868
+ /**
2869
+ * Get labels for a customer.
2870
+ */
2871
+ async getLabels(customerId, requestOptions) {
2872
+ const queryString = this.buildQueryString({ customer_id: customerId });
2873
+ return this.requestWithRetry(
2874
+ `/api/v1/labels${queryString}`,
2875
+ { method: "GET" },
2876
+ void 0,
2877
+ void 0,
2878
+ requestOptions
2879
+ );
2880
+ }
2881
+ };
2882
+
2883
+ // src/scores/client.ts
2884
+ var ScoresClient = class extends BaseClient {
2885
+ /**
2886
+ * Get the current risk score for a customer.
2887
+ */
2888
+ async getScore(customerId, requestOptions) {
2889
+ return this.requestWithRetry(
2890
+ `/api/v1/scores/${encodeURIComponent(customerId)}`,
2891
+ { method: "GET" },
2892
+ void 0,
2893
+ void 0,
2894
+ requestOptions
2895
+ );
2896
+ }
2897
+ /**
2898
+ * Get a detailed score breakdown for a customer.
2899
+ */
2900
+ async getScoreBreakdown(customerId, requestOptions) {
2901
+ return this.requestWithRetry(
2902
+ `/api/v1/scores/${encodeURIComponent(customerId)}/breakdown`,
2903
+ { method: "GET" },
2904
+ void 0,
2905
+ void 0,
2906
+ requestOptions
2907
+ );
2908
+ }
2909
+ };
2910
+ var WorkflowClient = class extends BaseClient {
2911
+ /**
2912
+ * Get the current workflow status for a customer.
2913
+ */
2914
+ async getWorkflowStatus(customerId, requestOptions) {
2915
+ return this.requestWithRetry(
2916
+ `/api/v1/workflows/${encodeURIComponent(customerId)}/status`,
2917
+ { method: "GET" },
2918
+ void 0,
2919
+ void 0,
2920
+ requestOptions
2921
+ );
2922
+ }
2923
+ /**
2924
+ * List workflows with optional filters.
2925
+ */
2926
+ async listWorkflows(filters, requestOptions) {
2927
+ const queryString = this.buildQueryString(filters || {});
2928
+ return this.requestWithRetry(
2929
+ `/api/v1/workflows${queryString}`,
2930
+ { method: "GET" },
2931
+ void 0,
2932
+ void 0,
2933
+ requestOptions
2934
+ );
2935
+ }
2936
+ /**
2937
+ * Get a specific workflow by ID.
2938
+ */
2939
+ async getWorkflow(workflowId, requestOptions) {
2940
+ return this.requestWithRetry(
2941
+ `/api/v1/workflows/${encodeURIComponent(workflowId)}`,
2942
+ { method: "GET" },
2943
+ void 0,
2944
+ void 0,
2945
+ requestOptions
2946
+ );
2947
+ }
2948
+ };
2949
+
2950
+ // src/webhooks/handler.ts
2951
+ var WebhookHandler = class {
2952
+ constructor(config) {
2953
+ this.handlers = /* @__PURE__ */ new Map();
2954
+ this.anyHandlers = [];
2955
+ this.secret = config.secret;
2956
+ this.tolerance = config.tolerance ?? 3e5;
2957
+ }
2958
+ /**
2959
+ * Register a handler for a specific event type.
2960
+ */
2961
+ on(eventType, handler) {
2962
+ const existing = this.handlers.get(eventType) || [];
2963
+ existing.push(handler);
2964
+ this.handlers.set(eventType, existing);
2965
+ return this;
2966
+ }
2967
+ /**
2968
+ * Register a catch-all handler for all event types.
2969
+ */
2970
+ onAny(handler) {
2971
+ this.anyHandlers.push(handler);
2972
+ return this;
2973
+ }
2974
+ /**
2975
+ * Verify signature and parse the webhook body.
2976
+ */
2977
+ async verifyAndParse(body, signature) {
2978
+ const isValid = await verifyWebhookSignature(body, signature, this.secret);
2979
+ if (!isValid) {
2980
+ throw new Error("Invalid webhook signature");
2981
+ }
2982
+ return this.parseAndValidate(body);
2983
+ }
2984
+ /**
2985
+ * Verify signature, parse, and dispatch to registered handlers.
2986
+ */
2987
+ async handle(body, signature) {
2988
+ const event = await this.verifyAndParse(body, signature);
2989
+ await this.dispatch(event);
2990
+ }
2991
+ /**
2992
+ * Parse an event without signature verification (for testing).
2993
+ */
2994
+ parseEvent(body) {
2995
+ return this.parseAndValidate(body);
2996
+ }
2997
+ parseAndValidate(body) {
2998
+ const event = JSON.parse(body);
2999
+ if (!event.type || !event.id || !event.timestamp) {
3000
+ throw new Error("Invalid webhook event: missing required fields (type, id, timestamp)");
3001
+ }
3002
+ if (this.tolerance > 0) {
3003
+ const eventTime = new Date(event.timestamp).getTime();
3004
+ const now = Date.now();
3005
+ if (Math.abs(now - eventTime) > this.tolerance) {
3006
+ throw new Error(
3007
+ `Webhook event timestamp is outside tolerance window (${this.tolerance}ms)`
3008
+ );
3009
+ }
3010
+ }
3011
+ return event;
3012
+ }
3013
+ async dispatch(event) {
3014
+ const typeHandlers = this.handlers.get(event.type) || [];
3015
+ const allHandlers = [...typeHandlers, ...this.anyHandlers];
3016
+ for (const handler of allHandlers) {
3017
+ await handler(event);
3018
+ }
3019
+ }
3020
+ };
3021
+
3022
+ // src/webhooks/middleware.ts
3023
+ function createWebhookMiddleware(options) {
3024
+ const handler = buildHandler(options);
3025
+ const signatureHeader = options.signatureHeader || "x-webhook-signature";
3026
+ return async (req, res, next) => {
3027
+ try {
3028
+ const body = typeof req.body === "string" ? req.body : req.body.toString("utf-8");
3029
+ const signature = req.headers[signatureHeader];
3030
+ if (!signature || typeof signature !== "string") {
3031
+ res.status(401).json({ error: "Missing webhook signature" });
3032
+ return;
3033
+ }
3034
+ await handler.handle(body, signature);
3035
+ res.status(200).json({ received: true });
3036
+ } catch (error) {
3037
+ const message = error instanceof Error ? error.message : "Webhook processing failed";
3038
+ if (message.includes("signature")) {
3039
+ res.status(401).json({ error: message });
3040
+ } else if (message.includes("tolerance") || message.includes("timestamp")) {
3041
+ res.status(400).json({ error: message });
3042
+ } else if (next) {
3043
+ next(error);
3044
+ } else {
3045
+ res.status(500).json({ error: message });
3046
+ }
3047
+ }
3048
+ };
3049
+ }
3050
+ function createNextWebhookHandler(options) {
3051
+ const handler = buildHandler(options);
3052
+ const signatureHeader = options.signatureHeader || "x-webhook-signature";
3053
+ return async (request) => {
3054
+ try {
3055
+ const body = await request.text();
3056
+ const signature = request.headers.get(signatureHeader);
3057
+ if (!signature) {
3058
+ return new Response(JSON.stringify({ error: "Missing webhook signature" }), {
3059
+ status: 401,
3060
+ headers: { "Content-Type": "application/json" }
3061
+ });
3062
+ }
3063
+ await handler.handle(body, signature);
3064
+ return new Response(JSON.stringify({ received: true }), {
3065
+ status: 200,
3066
+ headers: { "Content-Type": "application/json" }
3067
+ });
3068
+ } catch (error) {
3069
+ const message = error instanceof Error ? error.message : "Webhook processing failed";
3070
+ const status = message.includes("signature") ? 401 : message.includes("tolerance") || message.includes("timestamp") ? 400 : 500;
3071
+ return new Response(JSON.stringify({ error: message }), {
3072
+ status,
3073
+ headers: { "Content-Type": "application/json" }
3074
+ });
3075
+ }
3076
+ };
3077
+ }
3078
+ function buildHandler(options) {
3079
+ const handler = new WebhookHandler(options);
3080
+ if (options.handlers) {
3081
+ for (const [eventType, eventHandler] of Object.entries(options.handlers)) {
3082
+ if (eventHandler) {
3083
+ handler.on(eventType, eventHandler);
3084
+ }
3085
+ }
3086
+ }
3087
+ if (options.onAny) {
3088
+ handler.onAny(options.onAny);
3089
+ }
3090
+ return handler;
3091
+ }
3092
+
3093
+ export { AuthenticationError, BaseClient, CGSError, CircuitBreaker, CircuitBreakerOpenError, ComplianceBlockedError, ComplianceClient, ComplianceError, DEFAULT_CURRENCY_RATES, DecisionsClient, GeolocationClient, KycClient, NetworkError, RateLimitError, RateLimitTracker, RiskProfileClient, SDK_VERSION, ScoresClient, ServiceUnavailableError, TimeoutError, ValidationError, WebhookHandler, WorkflowClient, createConsoleLogger, createNextWebhookHandler, createWebhookMiddleware, decodeCipherText, generateCipherText, isCipherTextExpired, noopLogger, verifyWebhookSignature };
2518
3094
  //# sourceMappingURL=index.mjs.map
2519
3095
  //# sourceMappingURL=index.mjs.map