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