vesant-sdk 1.7.0-dev.ac305d8 → 1.7.0-dev.b4284ca

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 (75) hide show
  1. package/dist/{client-DoMSYMMR.d.ts → client-0NOPDnT0.d.ts} +29 -26
  2. package/dist/{client-DMIRx7Tu.d.mts → client-CvWNRwwg.d.mts} +29 -26
  3. package/dist/{client-ZNdnpWe7.d.mts → client-Dps40EtF.d.mts} +2 -2
  4. package/dist/{client-C3DCmGe9.d.ts → client-SMxqod4j.d.ts} +2 -2
  5. package/dist/{client-BolQlL5e.d.mts → client-q9fN8Hhf.d.mts} +103 -1
  6. package/dist/{client-BolQlL5e.d.ts → client-q9fN8Hhf.d.ts} +103 -1
  7. package/dist/compliance/index.d.mts +3 -3
  8. package/dist/compliance/index.d.ts +3 -3
  9. package/dist/compliance/index.js +133 -22
  10. package/dist/compliance/index.js.map +1 -1
  11. package/dist/compliance/index.mjs +133 -22
  12. package/dist/compliance/index.mjs.map +1 -1
  13. package/dist/decisions/index.d.mts +1 -1
  14. package/dist/decisions/index.d.ts +1 -1
  15. package/dist/decisions/index.js +68 -7
  16. package/dist/decisions/index.js.map +1 -1
  17. package/dist/decisions/index.mjs +68 -7
  18. package/dist/decisions/index.mjs.map +1 -1
  19. package/dist/geolocation/index.d.mts +59 -4
  20. package/dist/geolocation/index.d.ts +59 -4
  21. package/dist/geolocation/index.js +136 -19
  22. package/dist/geolocation/index.js.map +1 -1
  23. package/dist/geolocation/index.mjs +134 -20
  24. package/dist/geolocation/index.mjs.map +1 -1
  25. package/dist/index-CPOrMRaH.d.ts +264 -0
  26. package/dist/index-CimDmgZb.d.mts +264 -0
  27. package/dist/index.d.mts +27 -10
  28. package/dist/index.d.ts +27 -10
  29. package/dist/index.js +325 -67
  30. package/dist/index.js.map +1 -1
  31. package/dist/index.mjs +318 -68
  32. package/dist/index.mjs.map +1 -1
  33. package/dist/kyc/core.d.mts +2 -2
  34. package/dist/kyc/core.d.ts +2 -2
  35. package/dist/kyc/core.js +94 -19
  36. package/dist/kyc/core.js.map +1 -1
  37. package/dist/kyc/core.mjs +94 -19
  38. package/dist/kyc/core.mjs.map +1 -1
  39. package/dist/kyc/index.d.mts +110 -13
  40. package/dist/kyc/index.d.ts +110 -13
  41. package/dist/kyc/index.js +94 -19
  42. package/dist/kyc/index.js.map +1 -1
  43. package/dist/kyc/index.mjs +94 -19
  44. package/dist/kyc/index.mjs.map +1 -1
  45. package/dist/react.d.mts +9 -3
  46. package/dist/react.d.ts +9 -3
  47. package/dist/react.js +180 -77
  48. package/dist/react.js.map +1 -1
  49. package/dist/react.mjs +180 -77
  50. package/dist/react.mjs.map +1 -1
  51. package/dist/risk-profile/index.d.mts +1 -1
  52. package/dist/risk-profile/index.d.ts +1 -1
  53. package/dist/risk-profile/index.js +68 -7
  54. package/dist/risk-profile/index.js.map +1 -1
  55. package/dist/risk-profile/index.mjs +68 -7
  56. package/dist/risk-profile/index.mjs.map +1 -1
  57. package/dist/scores/index.d.mts +1 -1
  58. package/dist/scores/index.d.ts +1 -1
  59. package/dist/scores/index.js +68 -7
  60. package/dist/scores/index.js.map +1 -1
  61. package/dist/scores/index.mjs +68 -7
  62. package/dist/scores/index.mjs.map +1 -1
  63. package/dist/tax/index.d.mts +3 -26
  64. package/dist/tax/index.d.ts +3 -26
  65. package/dist/tax/index.js +74 -19
  66. package/dist/tax/index.js.map +1 -1
  67. package/dist/tax/index.mjs +74 -19
  68. package/dist/tax/index.mjs.map +1 -1
  69. package/dist/webhooks/index.d.mts +2 -189
  70. package/dist/webhooks/index.d.ts +2 -189
  71. package/dist/webhooks/index.js +85 -21
  72. package/dist/webhooks/index.js.map +1 -1
  73. package/dist/webhooks/index.mjs +85 -22
  74. package/dist/webhooks/index.mjs.map +1 -1
  75. package/package.json +4 -1
package/dist/index.mjs CHANGED
@@ -1,3 +1,59 @@
1
+ // src/core/auth.ts
2
+ var StaticApiKeyProvider = class {
3
+ constructor(apiKey) {
4
+ this.apiKey = apiKey;
5
+ }
6
+ async getCredential() {
7
+ return this.apiKey ? { scheme: "Bearer", token: this.apiKey } : null;
8
+ }
9
+ canRefresh() {
10
+ return false;
11
+ }
12
+ };
13
+ var RefreshingTokenProvider = class {
14
+ constructor(opts) {
15
+ this.opts = opts;
16
+ this.cached = null;
17
+ this.inFlight = null;
18
+ this.skewMs = opts.skewMs ?? 3e4;
19
+ this.scheme = opts.scheme ?? "Bearer";
20
+ this.now = opts.now ?? (() => Date.now());
21
+ }
22
+ canRefresh() {
23
+ return true;
24
+ }
25
+ async getCredential(ctx) {
26
+ const forceRefresh = ctx?.forceRefresh ?? false;
27
+ if (!forceRefresh && this.cached && !this.isExpiring(this.cached)) {
28
+ return { scheme: this.scheme, token: this.cached.token };
29
+ }
30
+ const fresh = await this.refresh();
31
+ return { scheme: this.scheme, token: fresh.token };
32
+ }
33
+ isExpiring(token) {
34
+ if (token.expiresAt === void 0) {
35
+ return false;
36
+ }
37
+ return this.now() >= token.expiresAt - this.skewMs;
38
+ }
39
+ refresh() {
40
+ if (this.inFlight) {
41
+ return this.inFlight;
42
+ }
43
+ this.inFlight = (async () => {
44
+ try {
45
+ const fresh = await this.opts.fetchToken();
46
+ this.cached = fresh;
47
+ this.opts.onRotate?.({ expiresAt: fresh.expiresAt });
48
+ return fresh;
49
+ } finally {
50
+ this.inFlight = null;
51
+ }
52
+ })();
53
+ return this.inFlight;
54
+ }
55
+ };
56
+
1
57
  // src/core/errors.ts
2
58
  var VesantError = class _VesantError extends Error {
3
59
  constructor(message, code, statusCode, details) {
@@ -322,6 +378,12 @@ var BaseClient = class {
322
378
  if (!config.tenantId?.trim()) {
323
379
  throw new ValidationError("tenantId is required and must be a non-empty string", ["tenantId"]);
324
380
  }
381
+ if (typeof fetch === "undefined") {
382
+ throw new VesantError(
383
+ "The Vesant SDK requires a global fetch (Node.js >= 18). Upgrade Node or provide a fetch polyfill.",
384
+ "RUNTIME_UNSUPPORTED"
385
+ );
386
+ }
325
387
  this.interceptors = config.interceptors || [];
326
388
  this.logger = config.logger || createConsoleLogger();
327
389
  let environment = config.environment;
@@ -340,11 +402,12 @@ var BaseClient = class {
340
402
  environment,
341
403
  headers: config.headers || {},
342
404
  timeout: config.timeout || 1e4,
343
- retries: config.retries || 3,
405
+ retries: config.retries ?? 3,
344
406
  debug: config.debug || false,
345
407
  interceptors: this.interceptors,
346
408
  logger: this.logger
347
409
  };
410
+ this.authProvider = config.authProvider ?? (apiKey ? new StaticApiKeyProvider(apiKey) : void 0);
348
411
  if (config.circuitBreaker) {
349
412
  this.circuitBreaker = new CircuitBreaker(config.circuitBreaker);
350
413
  }
@@ -352,6 +415,19 @@ var BaseClient = class {
352
415
  this.rateLimitTracker = new RateLimitTracker();
353
416
  }
354
417
  }
418
+ /**
419
+ * Resolve the Idempotency-Key for a request. Mutating methods (POST/PUT/PATCH)
420
+ * get the caller-supplied key or a freshly minted UUID; non-mutating methods get
421
+ * none. requestWithRetry resolves this once and injects it so every retry reuses
422
+ * one key (letting the server dedup replays); a direct request() resolves per call.
423
+ */
424
+ resolveIdempotencyKey(method, requestOptions) {
425
+ const isMutating = ["POST", "PUT", "PATCH"].includes((method || "GET").toUpperCase());
426
+ if (!isMutating) {
427
+ return void 0;
428
+ }
429
+ return requestOptions?.idempotencyKey ?? generateUUID();
430
+ }
355
431
  /**
356
432
  * Make an HTTP request with timeout and error handling
357
433
  */
@@ -377,15 +453,20 @@ var BaseClient = class {
377
453
  ...this.config.headers,
378
454
  ...options.headers || {}
379
455
  };
380
- if (this.config.apiKey) {
381
- headers["Authorization"] = `Bearer ${this.config.apiKey}`;
456
+ const credential = await this.authProvider?.getCredential({
457
+ tenantId: this.config.tenantId,
458
+ requestId
459
+ });
460
+ if (credential) {
461
+ headers["Authorization"] = `${credential.scheme ?? "Bearer"} ${credential.token}`;
382
462
  }
383
463
  if (this.config.environment === "sandbox") {
384
464
  headers["X-Sandbox"] = "true";
385
465
  }
386
466
  const method = (options.method || "GET").toUpperCase();
387
- if (["POST", "PUT", "PATCH"].includes(method)) {
388
- headers["Idempotency-Key"] = requestOptions?.idempotencyKey || generateUUID();
467
+ const idempotencyKey = this.resolveIdempotencyKey(method, requestOptions);
468
+ if (idempotencyKey) {
469
+ headers["Idempotency-Key"] = idempotencyKey;
389
470
  }
390
471
  const controller = new AbortController();
391
472
  const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);
@@ -406,10 +487,28 @@ var BaseClient = class {
406
487
  if (this.config.debug) {
407
488
  this.logger.debug(`${finalOptions.method || "GET"} ${endpoint}`);
408
489
  }
409
- const response = await fetch(url, {
490
+ let response = await fetch(url, {
410
491
  ...finalOptions,
411
492
  signal: controller.signal
412
493
  });
494
+ if (response.status === 401 && this.authProvider?.canRefresh?.()) {
495
+ const refreshed = await this.authProvider.getCredential({
496
+ tenantId: this.config.tenantId,
497
+ requestId,
498
+ forceRefresh: true
499
+ });
500
+ if (refreshed) {
501
+ this.config.onCredentialRotated?.({ reason: "unauthorized" });
502
+ finalOptions = {
503
+ ...finalOptions,
504
+ headers: {
505
+ ...finalOptions.headers,
506
+ Authorization: `${refreshed.scheme ?? "Bearer"} ${refreshed.token}`
507
+ }
508
+ };
509
+ response = await fetch(url, { ...finalOptions, signal: controller.signal });
510
+ }
511
+ }
413
512
  clearTimeout(timeoutId);
414
513
  if (this.rateLimitTracker) {
415
514
  this.rateLimitTracker.updateFromHeaders(response.headers);
@@ -498,9 +597,11 @@ var BaseClient = class {
498
597
  */
499
598
  async requestWithRetry(endpoint, options = {}, serviceURL, retries = this.config.retries, requestOptions) {
500
599
  let lastError;
600
+ const idempotencyKey = this.resolveIdempotencyKey(options.method || "GET", requestOptions);
601
+ const attemptOptions = idempotencyKey ? { ...requestOptions, idempotencyKey } : requestOptions;
501
602
  for (let attempt = 0; attempt <= retries; attempt++) {
502
603
  try {
503
- return await this.request(endpoint, options, serviceURL, requestOptions);
604
+ return await this.request(endpoint, options, serviceURL, attemptOptions);
504
605
  } catch (error) {
505
606
  lastError = error instanceof Error ? error : new Error("Unknown error");
506
607
  if (requestOptions?.signal?.aborted) {
@@ -597,6 +698,9 @@ var BaseClient = class {
597
698
  if (config.interceptors) {
598
699
  this.interceptors = config.interceptors;
599
700
  }
701
+ if (config.authProvider !== void 0 || config.apiKey !== void 0) {
702
+ this.authProvider = this.config.authProvider ?? (this.config.apiKey ? new StaticApiKeyProvider(this.config.apiKey) : void 0);
703
+ }
600
704
  }
601
705
  /**
602
706
  * Get current configuration (readonly)
@@ -624,8 +728,46 @@ var BaseClient = class {
624
728
  }
625
729
  };
626
730
 
731
+ // src/core/pagination.ts
732
+ async function* paginate(fetchPage) {
733
+ let page = 1;
734
+ let totalPages;
735
+ do {
736
+ const response = await fetchPage(page);
737
+ const items = response.data ?? [];
738
+ for (const item of items) {
739
+ yield item;
740
+ }
741
+ totalPages = response.total_pages ?? page;
742
+ if (items.length === 0) {
743
+ break;
744
+ }
745
+ page += 1;
746
+ } while (page <= totalPages);
747
+ }
748
+ async function collectAll(fetchPage) {
749
+ const items = [];
750
+ for await (const item of paginate(fetchPage)) {
751
+ items.push(item);
752
+ }
753
+ return items;
754
+ }
755
+
627
756
  // src/core/webhook-utils.ts
628
- async function verifyWebhookSignature(payload, signature, secret) {
757
+ async function verifyWebhookSignature(payload, signature, secret, opts = {}) {
758
+ const signed = parseSignedTimestamp(signature);
759
+ if (signed) {
760
+ const tolerance = opts.tolerance ?? 3e5;
761
+ const now = opts.now?.() ?? Date.now();
762
+ if (tolerance > 0 && Math.abs(now - signed.t * 1e3) > tolerance) {
763
+ return false;
764
+ }
765
+ const expected = await computeHmacSha256(`${signed.t}.${payload}`, secret);
766
+ return constantTimeEqual(signed.v1, expected);
767
+ }
768
+ if (opts.allowLegacy === false) {
769
+ return false;
770
+ }
629
771
  const hexDigest = await computeHmacSha256(payload, secret);
630
772
  const expectedPrefixed = `sha256=${hexDigest}`;
631
773
  if (signature.startsWith("sha256=")) {
@@ -633,6 +775,25 @@ async function verifyWebhookSignature(payload, signature, secret) {
633
775
  }
634
776
  return constantTimeEqual(signature, hexDigest);
635
777
  }
778
+ function parseSignedTimestamp(signature) {
779
+ if (!signature.includes("t=") || !signature.includes("v1=")) {
780
+ return null;
781
+ }
782
+ let t;
783
+ let v1;
784
+ for (const part of signature.split(",")) {
785
+ const eq = part.indexOf("=");
786
+ if (eq === -1) continue;
787
+ const key = part.slice(0, eq).trim();
788
+ const value = part.slice(eq + 1).trim();
789
+ if (key === "t") t = Number(value);
790
+ else if (key === "v1") v1 = value;
791
+ }
792
+ if (t === void 0 || Number.isNaN(t) || !v1) {
793
+ return null;
794
+ }
795
+ return { t, v1 };
796
+ }
636
797
  async function computeHmacSha256(message, secret) {
637
798
  if (typeof globalThis.crypto !== "undefined" && globalThis.crypto.subtle) {
638
799
  const encoder = new TextEncoder();
@@ -667,6 +828,61 @@ function constantTimeEqual(a, b) {
667
828
  return result === 0;
668
829
  }
669
830
 
831
+ // src/geolocation/integrity.ts
832
+ var isNil = (v) => v === null || v === void 0;
833
+ function assessGpsIntegrity(input, opts = {}) {
834
+ const flags = [];
835
+ const now = opts.now ?? (() => Date.now());
836
+ const maxAgeMs = opts.maxAgeMs ?? 6e4;
837
+ const minAccuracy = opts.minAccuracyMeters ?? 1;
838
+ const { accuracy, altitude, altitudeAccuracy, heading, speed, timestamp } = input;
839
+ if (isNil(accuracy)) {
840
+ flags.push("accuracy_missing");
841
+ } else if (accuracy === 0) {
842
+ flags.push("accuracy_zero");
843
+ } else if (accuracy < minAccuracy) {
844
+ flags.push("accuracy_implausibly_small");
845
+ }
846
+ const hasMotion = !isNil(heading) || !isNil(speed);
847
+ if (hasMotion && (isNil(accuracy) || accuracy === 0)) {
848
+ flags.push("motion_without_accuracy");
849
+ }
850
+ if (!isNil(altitude) && isNil(altitudeAccuracy)) {
851
+ flags.push("altitude_without_accuracy");
852
+ }
853
+ if (timestamp !== void 0 && now() - timestamp > maxAgeMs) {
854
+ flags.push("stale_timestamp");
855
+ }
856
+ if (opts.permissionState === "denied" || opts.permissionState === "prompt") {
857
+ flags.push(`permission_${opts.permissionState}`);
858
+ }
859
+ return { suspicious: flags.length > 0, flags };
860
+ }
861
+ function gpsIntegrityInputFromPosition(position) {
862
+ const c = position.coords;
863
+ return {
864
+ latitude: c.latitude,
865
+ longitude: c.longitude,
866
+ accuracy: c.accuracy,
867
+ altitude: c.altitude,
868
+ altitudeAccuracy: c.altitudeAccuracy,
869
+ heading: c.heading,
870
+ speed: c.speed,
871
+ timestamp: position.timestamp
872
+ };
873
+ }
874
+ async function probeGeolocationPermission() {
875
+ try {
876
+ if (typeof navigator === "undefined" || !navigator.permissions?.query) {
877
+ return "unavailable";
878
+ }
879
+ const status = await navigator.permissions.query({ name: "geolocation" });
880
+ return status.state;
881
+ } catch {
882
+ return "unavailable";
883
+ }
884
+ }
885
+
670
886
  // src/geolocation/ciphertext.ts
671
887
  var CIPHER_TEXT_EXPIRY_MINUTES = 5;
672
888
  async function computeHMAC(key, message) {
@@ -719,6 +935,7 @@ async function requestGPSLocation(timeout = 1e4, highAccuracy = true) {
719
935
  if (typeof navigator === "undefined" || !navigator.geolocation) {
720
936
  return null;
721
937
  }
938
+ const permissionState = await probeGeolocationPermission();
722
939
  return new Promise((resolve) => {
723
940
  navigator.geolocation.getCurrentPosition(
724
941
  (position) => {
@@ -735,6 +952,7 @@ async function requestGPSLocation(timeout = 1e4, highAccuracy = true) {
735
952
  resolve(null);
736
953
  return;
737
954
  }
955
+ const { flags } = assessGpsIntegrity(gpsIntegrityInputFromPosition(position), { permissionState });
738
956
  resolve({
739
957
  latitude,
740
958
  longitude,
@@ -743,7 +961,8 @@ async function requestGPSLocation(timeout = 1e4, highAccuracy = true) {
743
961
  altitude_accuracy: position.coords.altitudeAccuracy ?? void 0,
744
962
  heading: position.coords.heading ?? void 0,
745
963
  speed: position.coords.speed ?? void 0,
746
- timestamp: position.timestamp
964
+ timestamp: position.timestamp,
965
+ integrity_flags: flags.length ? flags : void 0
747
966
  });
748
967
  },
749
968
  () => {
@@ -810,6 +1029,9 @@ async function generateCipherText(options, config) {
810
1029
  );
811
1030
  if (location) {
812
1031
  locationData = location;
1032
+ if (location.integrity_flags?.length) {
1033
+ warnings.push(`GPS integrity flags (advisory): ${location.integrity_flags.join(", ")}`);
1034
+ }
813
1035
  } else if (gpsRequiredByConfig) {
814
1036
  throw new VesantError(
815
1037
  `GPS location is required for ${options.reason} by tenant configuration, but GPS was not available or permission was denied`,
@@ -929,8 +1151,8 @@ var GeolocationClient = class extends BaseClient {
929
1151
  * ```
930
1152
  */
931
1153
  async verifyIP(request, requestOptions) {
932
- if (!request.ip_address?.trim()) {
933
- throw new ValidationError("ip_address is required and must be a non-empty string", ["ip_address"]);
1154
+ if (request.ip_address !== void 0 && !request.ip_address.trim()) {
1155
+ throw new ValidationError("ip_address, when provided, must be a non-empty string", ["ip_address"]);
934
1156
  }
935
1157
  return this.requestWithRetry("/api/v1/geo/verify", {
936
1158
  method: "POST",
@@ -1280,20 +1502,12 @@ var GeolocationClient = class extends BaseClient {
1280
1502
  *
1281
1503
  * @example
1282
1504
  * ```typescript
1283
- * // Using GPS (preferred)
1505
+ * // GPS (the web SDK is GPS + IP only; the server handles the IP fallback)
1284
1506
  * const result = await client.captureLocation(token, {
1285
1507
  * latitude: 37.7749,
1286
1508
  * longitude: -122.4194,
1287
1509
  * accuracy: 10
1288
1510
  * });
1289
- *
1290
- * // Using WiFi positioning (fallback)
1291
- * const result = await client.captureLocation(token, {
1292
- * wifi_networks: [
1293
- * { macAddress: "00:11:22:33:44:55", signalStrength: -50 },
1294
- * { macAddress: "AA:BB:CC:DD:EE:FF", signalStrength: -70 }
1295
- * ]
1296
- * });
1297
1511
  * ```
1298
1512
  */
1299
1513
  async captureLocation(token, capture, requestOptions) {
@@ -1798,9 +2012,6 @@ var ComplianceClient = class {
1798
2012
  if (cipherTextResult.risk?.is_blocked) {
1799
2013
  blockReasons.push(...cipherTextResult.risk.block_reasons_structured ?? []);
1800
2014
  }
1801
- if (cipherTextResult.risk?.location_mismatch) {
1802
- blockReasons.push(sdkReasons.gpsIPMismatch());
1803
- }
1804
2015
  }
1805
2016
  if (geoVerification.gps_required && !cipherTextResult) {
1806
2017
  blockReasons.push(sdkReasons.gpsRequired());
@@ -2710,7 +2921,13 @@ var KycClient = class extends BaseClient {
2710
2921
  *
2711
2922
  * Generates a link that the user can visit to submit their KYC documents.
2712
2923
  *
2713
- * @param request - Request containing the user ID, optional redirect URL, and optional callback URL (receives POST requests)
2924
+ * Optionally pass the customer's registered identity data as
2925
+ * `customer_data` (same shape as the geolocation `customer_data` block).
2926
+ * It seeds the customer's risk profile so document verification can
2927
+ * cross-check the submitted document against trusted reference data;
2928
+ * fields never overwrite data already on the profile.
2929
+ *
2930
+ * @param request - Request containing the user ID, optional redirect URL, optional callback URL (receives POST requests), and optional customer identity data
2714
2931
  * @returns Response containing the redirect link and KYC ID
2715
2932
  *
2716
2933
  * @example
@@ -2718,19 +2935,27 @@ var KycClient = class extends BaseClient {
2718
2935
  * const result = await client.requestKycSubmitLink({
2719
2936
  * user_id: "user_123",
2720
2937
  * redirect_url: "https://merchant.com/kyc-complete", // optional
2721
- * callback_url: "https://merchant.com/api/kyc-webhook" // optional - receives POST requests on status change
2938
+ * callback_url: "https://merchant.com/api/kyc-webhook", // optional - receives POST requests on status change
2939
+ * customer_data: { // optional - seeds the risk profile for document cross-checks
2940
+ * full_name: "John Doe",
2941
+ * date_of_birth: "1999-06-02", // ISO 8601
2942
+ * email: "john@example.com",
2943
+ * phone: "+94771234567",
2944
+ * address: "12 Main St, Colombo", // used for address verification
2945
+ * country: "LK"
2946
+ * }
2722
2947
  * });
2723
2948
  *
2724
2949
  * console.log(`Redirect user to: ${result.link}`);
2725
2950
  * console.log(`KYC ID: ${result.kyc_id}`);
2726
2951
  * ```
2727
2952
  */
2728
- async requestKycSubmitLink(request) {
2953
+ async requestKycSubmitLink(request, requestOptions) {
2729
2954
  return this.requestWithRetry("/api/v1/kyc/request", {
2730
2955
  method: "POST",
2731
2956
  body: JSON.stringify(request),
2732
2957
  headers: this.getUserHeaders()
2733
- });
2958
+ }, void 0, void 0, requestOptions);
2734
2959
  }
2735
2960
  /**
2736
2961
  * Create a Event-Based Face Verification session.
@@ -2763,12 +2988,12 @@ var KycClient = class extends BaseClient {
2763
2988
  *
2764
2989
  * @param request - Token from `createEventBasedFaceVerificationSession`, plus the base64 selfie.
2765
2990
  */
2766
- async submitEventBasedFaceVerificationSession(request) {
2991
+ async submitEventBasedFaceVerificationSession(request, requestOptions) {
2767
2992
  return this.request("/api/v1/kyc/face/submit", {
2768
2993
  method: "POST",
2769
2994
  body: JSON.stringify(request),
2770
2995
  headers: this.getUserHeaders()
2771
- });
2996
+ }, void 0, requestOptions);
2772
2997
  }
2773
2998
  /**
2774
2999
  * Look up the current state of a Event-Based Face Verification session by its
@@ -2867,7 +3092,7 @@ var KycClient = class extends BaseClient {
2867
3092
  * @example
2868
3093
  * ```typescript
2869
3094
  * const result = await client.submitVerification({
2870
- * reference: "customer_123",
3095
+ * reference: "4d2aff34-ab86-422d-992e-50b1234a5b67",
2871
3096
  * email: "customer@example.com",
2872
3097
  * country: "US",
2873
3098
  * document: {
@@ -2884,12 +3109,12 @@ var KycClient = class extends BaseClient {
2884
3109
  * console.log(`Verification submitted: ${result.reference}`);
2885
3110
  * ```
2886
3111
  */
2887
- async submitVerification(request) {
3112
+ async submitVerification(request, requestOptions) {
2888
3113
  return this.requestWithRetry("/api/v1/kyc/submit", {
2889
3114
  method: "POST",
2890
3115
  body: JSON.stringify(request),
2891
3116
  headers: this.getUserHeaders()
2892
- });
3117
+ }, void 0, void 0, requestOptions);
2893
3118
  }
2894
3119
  /**
2895
3120
  * Get a KYC request by ID
@@ -2899,7 +3124,7 @@ var KycClient = class extends BaseClient {
2899
3124
  *
2900
3125
  * @example
2901
3126
  * ```typescript
2902
- * const kyc = await client.getKycRequest("kyc_abc123");
3127
+ * const kyc = await client.getKycRequest("550e8400-e29b-41d4-a716-446655440000");
2903
3128
  * console.log(`Status: ${kyc.status}, ID Verified: ${kyc.id_verified}`);
2904
3129
  * ```
2905
3130
  */
@@ -2974,7 +3199,7 @@ var KycClient = class extends BaseClient {
2974
3199
  * @example
2975
3200
  * ```typescript
2976
3201
  * await client.requestAdditionalDocuments({
2977
- * id: "kyc_abc123",
3202
+ * id: "550e8400-e29b-41d4-a716-446655440000",
2978
3203
  * document_types: ["address", "document_two"],
2979
3204
  * message: "Please provide proof of address and secondary ID"
2980
3205
  * });
@@ -2995,7 +3220,7 @@ var KycClient = class extends BaseClient {
2995
3220
  *
2996
3221
  * @example
2997
3222
  * ```typescript
2998
- * const proofs = await client.getProofDownloadURLs("kyc_abc123");
3223
+ * const proofs = await client.getProofDownloadURLs("550e8400-e29b-41d4-a716-446655440000");
2999
3224
  * proofs.forEach(proof => {
3000
3225
  * console.log(`${proof.type}: ${proof.url} (expires: ${proof.expires_at})`);
3001
3226
  * });
@@ -3329,19 +3554,13 @@ var TaxClient = class extends BaseClient {
3329
3554
  { method: "POST" }
3330
3555
  );
3331
3556
  }
3332
- async requestTaxFormWithProfile(input) {
3333
- return this.request(
3334
- "/api/v1/tax/customer-tax-profiles/request-form-with-profile",
3335
- {
3336
- method: "POST",
3337
- body: JSON.stringify(input)
3338
- }
3339
- );
3340
- }
3341
- async checkTINStatus(customerID) {
3557
+ async checkTINStatus(customerID, requestOptions) {
3342
3558
  return this.requestWithRetry(
3343
3559
  `/api/v1/tax/customer-tax-profiles/${customerID}/check-tin`,
3344
- { method: "POST" }
3560
+ { method: "POST" },
3561
+ void 0,
3562
+ void 0,
3563
+ requestOptions
3345
3564
  );
3346
3565
  }
3347
3566
  async reRequestTaxForm(customerID, input) {
@@ -3364,7 +3583,7 @@ var TaxClient = class extends BaseClient {
3364
3583
  }
3365
3584
  async getCustomerDocuments(customerID) {
3366
3585
  return this.request(
3367
- `/api/v1/tax/customer-tax-profiles/${customerID}/documents`
3586
+ `/api/v1/tm/customer-tax-profiles/${customerID}/documents`
3368
3587
  );
3369
3588
  }
3370
3589
  async downloadTaxForm(customerID, requestOptions) {
@@ -3421,15 +3640,48 @@ var TaxClient = class extends BaseClient {
3421
3640
  }
3422
3641
  };
3423
3642
 
3643
+ // src/webhooks/dedup.ts
3644
+ var InMemoryDedupStore = class {
3645
+ constructor(opts = {}) {
3646
+ this.expiryById = /* @__PURE__ */ new Map();
3647
+ this.maxEntries = opts.maxEntries ?? 1e3;
3648
+ this.now = opts.now ?? (() => Date.now());
3649
+ }
3650
+ async seen(id) {
3651
+ return this.expiryById.has(id);
3652
+ }
3653
+ async mark(id, ttlMs) {
3654
+ const now = this.now();
3655
+ this.expiryById.set(id, now + ttlMs);
3656
+ if (this.expiryById.size > this.maxEntries) {
3657
+ for (const [key, expiresAt] of this.expiryById) {
3658
+ if (now > expiresAt) {
3659
+ this.expiryById.delete(key);
3660
+ }
3661
+ }
3662
+ }
3663
+ }
3664
+ };
3665
+
3424
3666
  // src/webhooks/handler.ts
3667
+ function resolveEventId(event) {
3668
+ if (event.id) {
3669
+ return event.id;
3670
+ }
3671
+ if (event.event_type === "notification.created") {
3672
+ return event.notification?.id;
3673
+ }
3674
+ return void 0;
3675
+ }
3425
3676
  var WebhookHandler = class {
3426
3677
  constructor(config) {
3427
3678
  this.handlers = /* @__PURE__ */ new Map();
3428
3679
  this.anyHandlers = [];
3429
- this.seenEventIds = /* @__PURE__ */ new Map();
3430
3680
  this.secret = config.secret;
3431
3681
  this.tolerance = config.tolerance ?? 3e5;
3432
3682
  this.replayProtection = config.replayProtection ?? true;
3683
+ this.allowLegacySignature = config.allowLegacySignature ?? true;
3684
+ this.dedupStore = config.dedupStore ?? new InMemoryDedupStore();
3433
3685
  }
3434
3686
  /**
3435
3687
  * Register a handler for a specific event type.
@@ -3451,7 +3703,10 @@ var WebhookHandler = class {
3451
3703
  * Verify signature and parse the webhook body.
3452
3704
  */
3453
3705
  async verifyAndParse(body, signature) {
3454
- const isValid = await verifyWebhookSignature(body, signature, this.secret);
3706
+ const isValid = await verifyWebhookSignature(body, signature, this.secret, {
3707
+ tolerance: this.tolerance,
3708
+ allowLegacy: this.allowLegacySignature
3709
+ });
3455
3710
  if (!isValid) {
3456
3711
  throw new ValidationError("Invalid webhook signature", ["signature"]);
3457
3712
  }
@@ -3467,18 +3722,21 @@ var WebhookHandler = class {
3467
3722
  /**
3468
3723
  * Parse an event without signature verification (for testing).
3469
3724
  */
3470
- parseEvent(body) {
3725
+ async parseEvent(body) {
3471
3726
  return this.parseAndValidate(body);
3472
3727
  }
3473
- parseAndValidate(body) {
3728
+ async parseAndValidate(body) {
3474
3729
  const event = JSON.parse(body);
3475
- if (!event.type || !event.id || !event.timestamp) {
3476
- throw new ValidationError("Invalid webhook event: missing required fields (type, id, timestamp)", ["type", "id", "timestamp"]);
3730
+ const eventId = resolveEventId(event);
3731
+ if (!event.event_type || !eventId || !event.timestamp) {
3732
+ throw new ValidationError(
3733
+ "Invalid webhook event: missing required fields (event_type, id, timestamp)",
3734
+ ["event_type", "id", "timestamp"]
3735
+ );
3477
3736
  }
3478
3737
  if (this.tolerance > 0) {
3479
3738
  const eventTime = new Date(event.timestamp).getTime();
3480
- const now = Date.now();
3481
- if (Math.abs(now - eventTime) > this.tolerance) {
3739
+ if (Math.abs(Date.now() - eventTime) > this.tolerance) {
3482
3740
  throw new ValidationError(
3483
3741
  `Webhook event timestamp is outside tolerance window (${this.tolerance}ms)`,
3484
3742
  ["timestamp"]
@@ -3486,26 +3744,18 @@ var WebhookHandler = class {
3486
3744
  }
3487
3745
  }
3488
3746
  if (this.replayProtection) {
3489
- if (this.seenEventIds.has(event.id)) {
3747
+ if (await this.dedupStore.seen(eventId)) {
3490
3748
  throw new ValidationError(
3491
- `Duplicate webhook event: ${event.id} has already been processed`,
3749
+ `Duplicate webhook event: ${eventId} has already been processed`,
3492
3750
  ["id"]
3493
3751
  );
3494
3752
  }
3495
- const now = Date.now();
3496
- this.seenEventIds.set(event.id, now);
3497
- if (this.seenEventIds.size > 1e3) {
3498
- for (const [id, seenAt] of this.seenEventIds) {
3499
- if (now - seenAt > this.tolerance) {
3500
- this.seenEventIds.delete(id);
3501
- }
3502
- }
3503
- }
3753
+ await this.dedupStore.mark(eventId, this.tolerance);
3504
3754
  }
3505
3755
  return event;
3506
3756
  }
3507
3757
  async dispatch(event) {
3508
- const typeHandlers = this.handlers.get(event.type) || [];
3758
+ const typeHandlers = this.handlers.get(event.event_type) || [];
3509
3759
  const allHandlers = [...typeHandlers, ...this.anyHandlers];
3510
3760
  for (const handler of allHandlers) {
3511
3761
  await handler(event);
@@ -3586,6 +3836,6 @@ function buildHandler(options) {
3586
3836
  return handler;
3587
3837
  }
3588
3838
 
3589
- export { AuthenticationError, BaseClient, CGSError, CircuitBreaker, CircuitBreakerOpenError, ComplianceBlockedError, ComplianceClient, ComplianceError, DEFAULT_CURRENCY_RATES, GeolocationClient, KYC_DECLINED_DESCRIPTIONS, KycClient, NetworkError, RateLimitError, RateLimitTracker, RiskProfileClient, SDK_VERSION, ServiceUnavailableError, TaxClient, TimeoutError, ValidationError, VesantError, WebhookHandler, createConsoleLogger, createNextWebhookHandler, createWebhookMiddleware, decodeCipherText, generateCipherText, isCipherTextExpired, noopLogger, sdkReasons, verifyWebhookSignature };
3839
+ export { AuthenticationError, BaseClient, CGSError, CircuitBreaker, CircuitBreakerOpenError, ComplianceBlockedError, ComplianceClient, ComplianceError, DEFAULT_CURRENCY_RATES, GeolocationClient, InMemoryDedupStore, KYC_DECLINED_DESCRIPTIONS, KycClient, NetworkError, RateLimitError, RateLimitTracker, RefreshingTokenProvider, RiskProfileClient, SDK_VERSION, ServiceUnavailableError, StaticApiKeyProvider, TaxClient, TimeoutError, ValidationError, VesantError, WebhookHandler, assessGpsIntegrity, collectAll, createConsoleLogger, createNextWebhookHandler, createWebhookMiddleware, decodeCipherText, generateCipherText, gpsIntegrityInputFromPosition, isCipherTextExpired, noopLogger, paginate, probeGeolocationPermission, sdkReasons, verifyWebhookSignature };
3590
3840
  //# sourceMappingURL=index.mjs.map
3591
3841
  //# sourceMappingURL=index.mjs.map