vesant-sdk 1.7.0-dev.362ac6b → 1.7.0-dev.4c91be9

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 +343 -69
  30. package/dist/index.js.map +1 -1
  31. package/dist/index.mjs +336 -70
  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 +109 -30
  36. package/dist/kyc/core.js.map +1 -1
  37. package/dist/kyc/core.mjs +109 -30
  38. package/dist/kyc/core.mjs.map +1 -1
  39. package/dist/kyc/index.d.mts +164 -61
  40. package/dist/kyc/index.d.ts +164 -61
  41. package/dist/kyc/index.js +109 -30
  42. package/dist/kyc/index.js.map +1 -1
  43. package/dist/kyc/index.mjs +109 -30
  44. package/dist/kyc/index.mjs.map +1 -1
  45. package/dist/react.d.mts +21 -15
  46. package/dist/react.d.ts +21 -15
  47. package/dist/react.js +199 -87
  48. package/dist/react.js.map +1 -1
  49. package/dist/react.mjs +199 -87
  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 +20 -3
  64. package/dist/tax/index.d.ts +20 -3
  65. package/dist/tax/index.js +77 -10
  66. package/dist/tax/index.js.map +1 -1
  67. package/dist/tax/index.mjs +77 -10
  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.js CHANGED
@@ -1,5 +1,61 @@
1
1
  'use strict';
2
2
 
3
+ // src/core/auth.ts
4
+ var StaticApiKeyProvider = class {
5
+ constructor(apiKey) {
6
+ this.apiKey = apiKey;
7
+ }
8
+ async getCredential() {
9
+ return this.apiKey ? { scheme: "Bearer", token: this.apiKey } : null;
10
+ }
11
+ canRefresh() {
12
+ return false;
13
+ }
14
+ };
15
+ var RefreshingTokenProvider = class {
16
+ constructor(opts) {
17
+ this.opts = opts;
18
+ this.cached = null;
19
+ this.inFlight = null;
20
+ this.skewMs = opts.skewMs ?? 3e4;
21
+ this.scheme = opts.scheme ?? "Bearer";
22
+ this.now = opts.now ?? (() => Date.now());
23
+ }
24
+ canRefresh() {
25
+ return true;
26
+ }
27
+ async getCredential(ctx) {
28
+ const forceRefresh = ctx?.forceRefresh ?? false;
29
+ if (!forceRefresh && this.cached && !this.isExpiring(this.cached)) {
30
+ return { scheme: this.scheme, token: this.cached.token };
31
+ }
32
+ const fresh = await this.refresh();
33
+ return { scheme: this.scheme, token: fresh.token };
34
+ }
35
+ isExpiring(token) {
36
+ if (token.expiresAt === void 0) {
37
+ return false;
38
+ }
39
+ return this.now() >= token.expiresAt - this.skewMs;
40
+ }
41
+ refresh() {
42
+ if (this.inFlight) {
43
+ return this.inFlight;
44
+ }
45
+ this.inFlight = (async () => {
46
+ try {
47
+ const fresh = await this.opts.fetchToken();
48
+ this.cached = fresh;
49
+ this.opts.onRotate?.({ expiresAt: fresh.expiresAt });
50
+ return fresh;
51
+ } finally {
52
+ this.inFlight = null;
53
+ }
54
+ })();
55
+ return this.inFlight;
56
+ }
57
+ };
58
+
3
59
  // src/core/errors.ts
4
60
  var VesantError = class _VesantError extends Error {
5
61
  constructor(message, code, statusCode, details) {
@@ -324,6 +380,12 @@ var BaseClient = class {
324
380
  if (!config.tenantId?.trim()) {
325
381
  throw new ValidationError("tenantId is required and must be a non-empty string", ["tenantId"]);
326
382
  }
383
+ if (typeof fetch === "undefined") {
384
+ throw new VesantError(
385
+ "The Vesant SDK requires a global fetch (Node.js >= 18). Upgrade Node or provide a fetch polyfill.",
386
+ "RUNTIME_UNSUPPORTED"
387
+ );
388
+ }
327
389
  this.interceptors = config.interceptors || [];
328
390
  this.logger = config.logger || createConsoleLogger();
329
391
  let environment = config.environment;
@@ -342,11 +404,12 @@ var BaseClient = class {
342
404
  environment,
343
405
  headers: config.headers || {},
344
406
  timeout: config.timeout || 1e4,
345
- retries: config.retries || 3,
407
+ retries: config.retries ?? 3,
346
408
  debug: config.debug || false,
347
409
  interceptors: this.interceptors,
348
410
  logger: this.logger
349
411
  };
412
+ this.authProvider = config.authProvider ?? (apiKey ? new StaticApiKeyProvider(apiKey) : void 0);
350
413
  if (config.circuitBreaker) {
351
414
  this.circuitBreaker = new CircuitBreaker(config.circuitBreaker);
352
415
  }
@@ -354,6 +417,19 @@ var BaseClient = class {
354
417
  this.rateLimitTracker = new RateLimitTracker();
355
418
  }
356
419
  }
420
+ /**
421
+ * Resolve the Idempotency-Key for a request. Mutating methods (POST/PUT/PATCH)
422
+ * get the caller-supplied key or a freshly minted UUID; non-mutating methods get
423
+ * none. requestWithRetry resolves this once and injects it so every retry reuses
424
+ * one key (letting the server dedup replays); a direct request() resolves per call.
425
+ */
426
+ resolveIdempotencyKey(method, requestOptions) {
427
+ const isMutating = ["POST", "PUT", "PATCH"].includes((method || "GET").toUpperCase());
428
+ if (!isMutating) {
429
+ return void 0;
430
+ }
431
+ return requestOptions?.idempotencyKey ?? generateUUID();
432
+ }
357
433
  /**
358
434
  * Make an HTTP request with timeout and error handling
359
435
  */
@@ -379,15 +455,20 @@ var BaseClient = class {
379
455
  ...this.config.headers,
380
456
  ...options.headers || {}
381
457
  };
382
- if (this.config.apiKey) {
383
- headers["Authorization"] = `Bearer ${this.config.apiKey}`;
458
+ const credential = await this.authProvider?.getCredential({
459
+ tenantId: this.config.tenantId,
460
+ requestId
461
+ });
462
+ if (credential) {
463
+ headers["Authorization"] = `${credential.scheme ?? "Bearer"} ${credential.token}`;
384
464
  }
385
465
  if (this.config.environment === "sandbox") {
386
466
  headers["X-Sandbox"] = "true";
387
467
  }
388
468
  const method = (options.method || "GET").toUpperCase();
389
- if (["POST", "PUT", "PATCH"].includes(method)) {
390
- headers["Idempotency-Key"] = requestOptions?.idempotencyKey || generateUUID();
469
+ const idempotencyKey = this.resolveIdempotencyKey(method, requestOptions);
470
+ if (idempotencyKey) {
471
+ headers["Idempotency-Key"] = idempotencyKey;
391
472
  }
392
473
  const controller = new AbortController();
393
474
  const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);
@@ -408,10 +489,28 @@ var BaseClient = class {
408
489
  if (this.config.debug) {
409
490
  this.logger.debug(`${finalOptions.method || "GET"} ${endpoint}`);
410
491
  }
411
- const response = await fetch(url, {
492
+ let response = await fetch(url, {
412
493
  ...finalOptions,
413
494
  signal: controller.signal
414
495
  });
496
+ if (response.status === 401 && this.authProvider?.canRefresh?.()) {
497
+ const refreshed = await this.authProvider.getCredential({
498
+ tenantId: this.config.tenantId,
499
+ requestId,
500
+ forceRefresh: true
501
+ });
502
+ if (refreshed) {
503
+ this.config.onCredentialRotated?.({ reason: "unauthorized" });
504
+ finalOptions = {
505
+ ...finalOptions,
506
+ headers: {
507
+ ...finalOptions.headers,
508
+ Authorization: `${refreshed.scheme ?? "Bearer"} ${refreshed.token}`
509
+ }
510
+ };
511
+ response = await fetch(url, { ...finalOptions, signal: controller.signal });
512
+ }
513
+ }
415
514
  clearTimeout(timeoutId);
416
515
  if (this.rateLimitTracker) {
417
516
  this.rateLimitTracker.updateFromHeaders(response.headers);
@@ -500,9 +599,11 @@ var BaseClient = class {
500
599
  */
501
600
  async requestWithRetry(endpoint, options = {}, serviceURL, retries = this.config.retries, requestOptions) {
502
601
  let lastError;
602
+ const idempotencyKey = this.resolveIdempotencyKey(options.method || "GET", requestOptions);
603
+ const attemptOptions = idempotencyKey ? { ...requestOptions, idempotencyKey } : requestOptions;
503
604
  for (let attempt = 0; attempt <= retries; attempt++) {
504
605
  try {
505
- return await this.request(endpoint, options, serviceURL, requestOptions);
606
+ return await this.request(endpoint, options, serviceURL, attemptOptions);
506
607
  } catch (error) {
507
608
  lastError = error instanceof Error ? error : new Error("Unknown error");
508
609
  if (requestOptions?.signal?.aborted) {
@@ -599,6 +700,9 @@ var BaseClient = class {
599
700
  if (config.interceptors) {
600
701
  this.interceptors = config.interceptors;
601
702
  }
703
+ if (config.authProvider !== void 0 || config.apiKey !== void 0) {
704
+ this.authProvider = this.config.authProvider ?? (this.config.apiKey ? new StaticApiKeyProvider(this.config.apiKey) : void 0);
705
+ }
602
706
  }
603
707
  /**
604
708
  * Get current configuration (readonly)
@@ -626,8 +730,46 @@ var BaseClient = class {
626
730
  }
627
731
  };
628
732
 
733
+ // src/core/pagination.ts
734
+ async function* paginate(fetchPage) {
735
+ let page = 1;
736
+ let totalPages;
737
+ do {
738
+ const response = await fetchPage(page);
739
+ const items = response.data ?? [];
740
+ for (const item of items) {
741
+ yield item;
742
+ }
743
+ totalPages = response.total_pages ?? page;
744
+ if (items.length === 0) {
745
+ break;
746
+ }
747
+ page += 1;
748
+ } while (page <= totalPages);
749
+ }
750
+ async function collectAll(fetchPage) {
751
+ const items = [];
752
+ for await (const item of paginate(fetchPage)) {
753
+ items.push(item);
754
+ }
755
+ return items;
756
+ }
757
+
629
758
  // src/core/webhook-utils.ts
630
- async function verifyWebhookSignature(payload, signature, secret) {
759
+ async function verifyWebhookSignature(payload, signature, secret, opts = {}) {
760
+ const signed = parseSignedTimestamp(signature);
761
+ if (signed) {
762
+ const tolerance = opts.tolerance ?? 3e5;
763
+ const now = opts.now?.() ?? Date.now();
764
+ if (tolerance > 0 && Math.abs(now - signed.t * 1e3) > tolerance) {
765
+ return false;
766
+ }
767
+ const expected = await computeHmacSha256(`${signed.t}.${payload}`, secret);
768
+ return constantTimeEqual(signed.v1, expected);
769
+ }
770
+ if (opts.allowLegacy === false) {
771
+ return false;
772
+ }
631
773
  const hexDigest = await computeHmacSha256(payload, secret);
632
774
  const expectedPrefixed = `sha256=${hexDigest}`;
633
775
  if (signature.startsWith("sha256=")) {
@@ -635,6 +777,25 @@ async function verifyWebhookSignature(payload, signature, secret) {
635
777
  }
636
778
  return constantTimeEqual(signature, hexDigest);
637
779
  }
780
+ function parseSignedTimestamp(signature) {
781
+ if (!signature.includes("t=") || !signature.includes("v1=")) {
782
+ return null;
783
+ }
784
+ let t;
785
+ let v1;
786
+ for (const part of signature.split(",")) {
787
+ const eq = part.indexOf("=");
788
+ if (eq === -1) continue;
789
+ const key = part.slice(0, eq).trim();
790
+ const value = part.slice(eq + 1).trim();
791
+ if (key === "t") t = Number(value);
792
+ else if (key === "v1") v1 = value;
793
+ }
794
+ if (t === void 0 || Number.isNaN(t) || !v1) {
795
+ return null;
796
+ }
797
+ return { t, v1 };
798
+ }
638
799
  async function computeHmacSha256(message, secret) {
639
800
  if (typeof globalThis.crypto !== "undefined" && globalThis.crypto.subtle) {
640
801
  const encoder = new TextEncoder();
@@ -669,6 +830,61 @@ function constantTimeEqual(a, b) {
669
830
  return result === 0;
670
831
  }
671
832
 
833
+ // src/geolocation/integrity.ts
834
+ var isNil = (v) => v === null || v === void 0;
835
+ function assessGpsIntegrity(input, opts = {}) {
836
+ const flags = [];
837
+ const now = opts.now ?? (() => Date.now());
838
+ const maxAgeMs = opts.maxAgeMs ?? 6e4;
839
+ const minAccuracy = opts.minAccuracyMeters ?? 1;
840
+ const { accuracy, altitude, altitudeAccuracy, heading, speed, timestamp } = input;
841
+ if (isNil(accuracy)) {
842
+ flags.push("accuracy_missing");
843
+ } else if (accuracy === 0) {
844
+ flags.push("accuracy_zero");
845
+ } else if (accuracy < minAccuracy) {
846
+ flags.push("accuracy_implausibly_small");
847
+ }
848
+ const hasMotion = !isNil(heading) || !isNil(speed);
849
+ if (hasMotion && (isNil(accuracy) || accuracy === 0)) {
850
+ flags.push("motion_without_accuracy");
851
+ }
852
+ if (!isNil(altitude) && isNil(altitudeAccuracy)) {
853
+ flags.push("altitude_without_accuracy");
854
+ }
855
+ if (timestamp !== void 0 && now() - timestamp > maxAgeMs) {
856
+ flags.push("stale_timestamp");
857
+ }
858
+ if (opts.permissionState === "denied" || opts.permissionState === "prompt") {
859
+ flags.push(`permission_${opts.permissionState}`);
860
+ }
861
+ return { suspicious: flags.length > 0, flags };
862
+ }
863
+ function gpsIntegrityInputFromPosition(position) {
864
+ const c = position.coords;
865
+ return {
866
+ latitude: c.latitude,
867
+ longitude: c.longitude,
868
+ accuracy: c.accuracy,
869
+ altitude: c.altitude,
870
+ altitudeAccuracy: c.altitudeAccuracy,
871
+ heading: c.heading,
872
+ speed: c.speed,
873
+ timestamp: position.timestamp
874
+ };
875
+ }
876
+ async function probeGeolocationPermission() {
877
+ try {
878
+ if (typeof navigator === "undefined" || !navigator.permissions?.query) {
879
+ return "unavailable";
880
+ }
881
+ const status = await navigator.permissions.query({ name: "geolocation" });
882
+ return status.state;
883
+ } catch {
884
+ return "unavailable";
885
+ }
886
+ }
887
+
672
888
  // src/geolocation/ciphertext.ts
673
889
  var CIPHER_TEXT_EXPIRY_MINUTES = 5;
674
890
  async function computeHMAC(key, message) {
@@ -721,6 +937,7 @@ async function requestGPSLocation(timeout = 1e4, highAccuracy = true) {
721
937
  if (typeof navigator === "undefined" || !navigator.geolocation) {
722
938
  return null;
723
939
  }
940
+ const permissionState = await probeGeolocationPermission();
724
941
  return new Promise((resolve) => {
725
942
  navigator.geolocation.getCurrentPosition(
726
943
  (position) => {
@@ -737,6 +954,7 @@ async function requestGPSLocation(timeout = 1e4, highAccuracy = true) {
737
954
  resolve(null);
738
955
  return;
739
956
  }
957
+ const { flags } = assessGpsIntegrity(gpsIntegrityInputFromPosition(position), { permissionState });
740
958
  resolve({
741
959
  latitude,
742
960
  longitude,
@@ -745,7 +963,8 @@ async function requestGPSLocation(timeout = 1e4, highAccuracy = true) {
745
963
  altitude_accuracy: position.coords.altitudeAccuracy ?? void 0,
746
964
  heading: position.coords.heading ?? void 0,
747
965
  speed: position.coords.speed ?? void 0,
748
- timestamp: position.timestamp
966
+ timestamp: position.timestamp,
967
+ integrity_flags: flags.length ? flags : void 0
749
968
  });
750
969
  },
751
970
  () => {
@@ -812,6 +1031,9 @@ async function generateCipherText(options, config) {
812
1031
  );
813
1032
  if (location) {
814
1033
  locationData = location;
1034
+ if (location.integrity_flags?.length) {
1035
+ warnings.push(`GPS integrity flags (advisory): ${location.integrity_flags.join(", ")}`);
1036
+ }
815
1037
  } else if (gpsRequiredByConfig) {
816
1038
  throw new VesantError(
817
1039
  `GPS location is required for ${options.reason} by tenant configuration, but GPS was not available or permission was denied`,
@@ -931,8 +1153,8 @@ var GeolocationClient = class extends BaseClient {
931
1153
  * ```
932
1154
  */
933
1155
  async verifyIP(request, requestOptions) {
934
- if (!request.ip_address?.trim()) {
935
- throw new ValidationError("ip_address is required and must be a non-empty string", ["ip_address"]);
1156
+ if (request.ip_address !== void 0 && !request.ip_address.trim()) {
1157
+ throw new ValidationError("ip_address, when provided, must be a non-empty string", ["ip_address"]);
936
1158
  }
937
1159
  return this.requestWithRetry("/api/v1/geo/verify", {
938
1160
  method: "POST",
@@ -1282,20 +1504,12 @@ var GeolocationClient = class extends BaseClient {
1282
1504
  *
1283
1505
  * @example
1284
1506
  * ```typescript
1285
- * // Using GPS (preferred)
1507
+ * // GPS (the web SDK is GPS + IP only; the server handles the IP fallback)
1286
1508
  * const result = await client.captureLocation(token, {
1287
1509
  * latitude: 37.7749,
1288
1510
  * longitude: -122.4194,
1289
1511
  * accuracy: 10
1290
1512
  * });
1291
- *
1292
- * // Using WiFi positioning (fallback)
1293
- * const result = await client.captureLocation(token, {
1294
- * wifi_networks: [
1295
- * { macAddress: "00:11:22:33:44:55", signalStrength: -50 },
1296
- * { macAddress: "AA:BB:CC:DD:EE:FF", signalStrength: -70 }
1297
- * ]
1298
- * });
1299
1513
  * ```
1300
1514
  */
1301
1515
  async captureLocation(token, capture, requestOptions) {
@@ -1800,9 +2014,6 @@ var ComplianceClient = class {
1800
2014
  if (cipherTextResult.risk?.is_blocked) {
1801
2015
  blockReasons.push(...cipherTextResult.risk.block_reasons_structured ?? []);
1802
2016
  }
1803
- if (cipherTextResult.risk?.location_mismatch) {
1804
- blockReasons.push(sdkReasons.gpsIPMismatch());
1805
- }
1806
2017
  }
1807
2018
  if (geoVerification.gps_required && !cipherTextResult) {
1808
2019
  blockReasons.push(sdkReasons.gpsRequired());
@@ -2712,7 +2923,13 @@ var KycClient = class extends BaseClient {
2712
2923
  *
2713
2924
  * Generates a link that the user can visit to submit their KYC documents.
2714
2925
  *
2715
- * @param request - Request containing the user ID, optional redirect URL, and optional callback URL (receives POST requests)
2926
+ * Optionally pass the customer's registered identity data as
2927
+ * `customer_data` (same shape as the geolocation `customer_data` block).
2928
+ * It seeds the customer's risk profile so document verification can
2929
+ * cross-check the submitted document against trusted reference data;
2930
+ * fields never overwrite data already on the profile.
2931
+ *
2932
+ * @param request - Request containing the user ID, optional redirect URL, optional callback URL (receives POST requests), and optional customer identity data
2716
2933
  * @returns Response containing the redirect link and KYC ID
2717
2934
  *
2718
2935
  * @example
@@ -2720,22 +2937,30 @@ var KycClient = class extends BaseClient {
2720
2937
  * const result = await client.requestKycSubmitLink({
2721
2938
  * user_id: "user_123",
2722
2939
  * redirect_url: "https://merchant.com/kyc-complete", // optional
2723
- * callback_url: "https://merchant.com/api/kyc-webhook" // optional - receives POST requests on status change
2940
+ * callback_url: "https://merchant.com/api/kyc-webhook", // optional - receives POST requests on status change
2941
+ * customer_data: { // optional - seeds the risk profile for document cross-checks
2942
+ * full_name: "John Doe",
2943
+ * date_of_birth: "1999-06-02", // ISO 8601
2944
+ * email: "john@example.com",
2945
+ * phone: "+94771234567",
2946
+ * address: "12 Main St, Colombo", // used for address verification
2947
+ * country: "LK"
2948
+ * }
2724
2949
  * });
2725
2950
  *
2726
2951
  * console.log(`Redirect user to: ${result.link}`);
2727
2952
  * console.log(`KYC ID: ${result.kyc_id}`);
2728
2953
  * ```
2729
2954
  */
2730
- async requestKycSubmitLink(request) {
2955
+ async requestKycSubmitLink(request, requestOptions) {
2731
2956
  return this.requestWithRetry("/api/v1/kyc/request", {
2732
2957
  method: "POST",
2733
2958
  body: JSON.stringify(request),
2734
2959
  headers: this.getUserHeaders()
2735
- });
2960
+ }, void 0, void 0, requestOptions);
2736
2961
  }
2737
2962
  /**
2738
- * Create a Re-Use KYC session.
2963
+ * Create a Event-Based Face Verification session.
2739
2964
  *
2740
2965
  * Inspect the response before showing UI:
2741
2966
  * - `is_required === false` → skip face capture; `reason` explains why.
@@ -2745,7 +2970,7 @@ var KycClient = class extends BaseClient {
2745
2970
  *
2746
2971
  * @param request - Reference, customer_id, event, amount (for threshold events), optional URLs.
2747
2972
  */
2748
- async createReuseKycSession(request) {
2973
+ async createEventBasedFaceVerificationSession(request) {
2749
2974
  return this.request("/api/v1/kyc/face/session", {
2750
2975
  method: "POST",
2751
2976
  body: JSON.stringify(request),
@@ -2753,33 +2978,37 @@ var KycClient = class extends BaseClient {
2753
2978
  });
2754
2979
  }
2755
2980
  /**
2756
- * Submit a real-time face capture for an active Re-Use KYC session.
2981
+ * Submit a real-time face capture for an active Event-Based Face Verification session.
2757
2982
  *
2758
2983
  * **Mobile-only.** The server rejects desktop User-Agents with HTTP
2759
2984
  * 400 (`face capture must be completed on a mobile device`). Use the
2760
- * QR handoff from `createReuseKycSession` for desktop callers.
2985
+ * QR handoff from `createEventBasedFaceVerificationSession` for desktop callers.
2761
2986
  *
2762
2987
  * The `data` field on the response carries `retries_remaining`,
2763
2988
  * `retry_limit_exceeded`, and the reaction flags (`enforce_logout`,
2764
2989
  * `freeze_account`, etc.) so the tenant app can act on a final failure.
2765
2990
  *
2766
- * @param request - Token from `createReuseKycSession`, plus the base64 selfie.
2991
+ * @param request - Token from `createEventBasedFaceVerificationSession`, plus the base64 selfie.
2767
2992
  */
2768
- async submitReuseKycSession(request) {
2993
+ async submitEventBasedFaceVerificationSession(request, requestOptions) {
2769
2994
  return this.request("/api/v1/kyc/face/submit", {
2770
2995
  method: "POST",
2771
2996
  body: JSON.stringify(request),
2772
2997
  headers: this.getUserHeaders()
2773
- });
2998
+ }, void 0, requestOptions);
2774
2999
  }
2775
3000
  /**
2776
- * Look up the current state of a Re-Use KYC session by its forward
2777
- * reference. Useful for desktop pollers waiting on the mobile handoff.
3001
+ * Look up the current state of a Event-Based Face Verification session by its
3002
+ * session token. Useful for desktop pollers waiting on the mobile handoff.
3003
+ *
3004
+ * The lookup is scoped by the unguessable, server-issued session token (not
3005
+ * the enumerable forward reference): the endpoint is public/unauthenticated,
3006
+ * and scoping by the token is what prevents cross-tenant status reads.
2778
3007
  *
2779
- * @param reference - The reference used when the session was created.
3008
+ * @param token - The session token returned by `createEventBasedFaceVerificationSession`.
2780
3009
  */
2781
- async getReuseKycSessionStatus(reference) {
2782
- return this.requestWithRetry(`/api/v1/kyc/face/verify/${encodeURIComponent(reference)}`, {
3010
+ async getEventBasedFaceVerificationSessionStatus(token) {
3011
+ return this.requestWithRetry(`/api/v1/kyc/face/verify/${encodeURIComponent(token)}`, {
2783
3012
  method: "GET",
2784
3013
  headers: this.getUserHeaders()
2785
3014
  });
@@ -2790,7 +3019,7 @@ var KycClient = class extends BaseClient {
2790
3019
  * callers poll `mobile_connected` to detect when a mobile device has
2791
3020
  * scanned the QR and attached.
2792
3021
  *
2793
- * @param token - The session token returned by `createReuseKycSession`.
3022
+ * @param token - The session token returned by `createEventBasedFaceVerificationSession`.
2794
3023
  */
2795
3024
  async getHandoffSession(token) {
2796
3025
  return this.request(
@@ -2865,7 +3094,7 @@ var KycClient = class extends BaseClient {
2865
3094
  * @example
2866
3095
  * ```typescript
2867
3096
  * const result = await client.submitVerification({
2868
- * reference: "customer_123",
3097
+ * reference: "4d2aff34-ab86-422d-992e-50b1234a5b67",
2869
3098
  * email: "customer@example.com",
2870
3099
  * country: "US",
2871
3100
  * document: {
@@ -2882,12 +3111,12 @@ var KycClient = class extends BaseClient {
2882
3111
  * console.log(`Verification submitted: ${result.reference}`);
2883
3112
  * ```
2884
3113
  */
2885
- async submitVerification(request) {
3114
+ async submitVerification(request, requestOptions) {
2886
3115
  return this.requestWithRetry("/api/v1/kyc/submit", {
2887
3116
  method: "POST",
2888
3117
  body: JSON.stringify(request),
2889
3118
  headers: this.getUserHeaders()
2890
- });
3119
+ }, void 0, void 0, requestOptions);
2891
3120
  }
2892
3121
  /**
2893
3122
  * Get a KYC request by ID
@@ -2897,7 +3126,7 @@ var KycClient = class extends BaseClient {
2897
3126
  *
2898
3127
  * @example
2899
3128
  * ```typescript
2900
- * const kyc = await client.getKycRequest("kyc_abc123");
3129
+ * const kyc = await client.getKycRequest("550e8400-e29b-41d4-a716-446655440000");
2901
3130
  * console.log(`Status: ${kyc.status}, ID Verified: ${kyc.id_verified}`);
2902
3131
  * ```
2903
3132
  */
@@ -2972,7 +3201,7 @@ var KycClient = class extends BaseClient {
2972
3201
  * @example
2973
3202
  * ```typescript
2974
3203
  * await client.requestAdditionalDocuments({
2975
- * id: "kyc_abc123",
3204
+ * id: "550e8400-e29b-41d4-a716-446655440000",
2976
3205
  * document_types: ["address", "document_two"],
2977
3206
  * message: "Please provide proof of address and secondary ID"
2978
3207
  * });
@@ -2993,7 +3222,7 @@ var KycClient = class extends BaseClient {
2993
3222
  *
2994
3223
  * @example
2995
3224
  * ```typescript
2996
- * const proofs = await client.getProofDownloadURLs("kyc_abc123");
3225
+ * const proofs = await client.getProofDownloadURLs("550e8400-e29b-41d4-a716-446655440000");
2997
3226
  * proofs.forEach(proof => {
2998
3227
  * console.log(`${proof.type}: ${proof.url} (expires: ${proof.expires_at})`);
2999
3228
  * });
@@ -3327,10 +3556,13 @@ var TaxClient = class extends BaseClient {
3327
3556
  { method: "POST" }
3328
3557
  );
3329
3558
  }
3330
- async checkTINStatus(customerID) {
3559
+ async checkTINStatus(customerID, requestOptions) {
3331
3560
  return this.requestWithRetry(
3332
3561
  `/api/v1/tax/customer-tax-profiles/${customerID}/check-tin`,
3333
- { method: "POST" }
3562
+ { method: "POST" },
3563
+ void 0,
3564
+ void 0,
3565
+ requestOptions
3334
3566
  );
3335
3567
  }
3336
3568
  async reRequestTaxForm(customerID, input) {
@@ -3353,7 +3585,7 @@ var TaxClient = class extends BaseClient {
3353
3585
  }
3354
3586
  async getCustomerDocuments(customerID) {
3355
3587
  return this.request(
3356
- `/api/v1/tax/customer-tax-profiles/${customerID}/documents`
3588
+ `/api/v1/tm/customer-tax-profiles/${customerID}/documents`
3357
3589
  );
3358
3590
  }
3359
3591
  async downloadTaxForm(customerID, requestOptions) {
@@ -3378,6 +3610,9 @@ var TaxClient = class extends BaseClient {
3378
3610
  });
3379
3611
  return res.version;
3380
3612
  }
3613
+ async runReminders() {
3614
+ return this.request("/api/v1/tax/reminders/run", { method: "POST" });
3615
+ }
3381
3616
  async getComplianceStats(filters) {
3382
3617
  const params = {};
3383
3618
  if (filters?.time_range) params.time_range = filters.time_range;
@@ -3407,15 +3642,48 @@ var TaxClient = class extends BaseClient {
3407
3642
  }
3408
3643
  };
3409
3644
 
3645
+ // src/webhooks/dedup.ts
3646
+ var InMemoryDedupStore = class {
3647
+ constructor(opts = {}) {
3648
+ this.expiryById = /* @__PURE__ */ new Map();
3649
+ this.maxEntries = opts.maxEntries ?? 1e3;
3650
+ this.now = opts.now ?? (() => Date.now());
3651
+ }
3652
+ async seen(id) {
3653
+ return this.expiryById.has(id);
3654
+ }
3655
+ async mark(id, ttlMs) {
3656
+ const now = this.now();
3657
+ this.expiryById.set(id, now + ttlMs);
3658
+ if (this.expiryById.size > this.maxEntries) {
3659
+ for (const [key, expiresAt] of this.expiryById) {
3660
+ if (now > expiresAt) {
3661
+ this.expiryById.delete(key);
3662
+ }
3663
+ }
3664
+ }
3665
+ }
3666
+ };
3667
+
3410
3668
  // src/webhooks/handler.ts
3669
+ function resolveEventId(event) {
3670
+ if (event.id) {
3671
+ return event.id;
3672
+ }
3673
+ if (event.event_type === "notification.created") {
3674
+ return event.notification?.id;
3675
+ }
3676
+ return void 0;
3677
+ }
3411
3678
  var WebhookHandler = class {
3412
3679
  constructor(config) {
3413
3680
  this.handlers = /* @__PURE__ */ new Map();
3414
3681
  this.anyHandlers = [];
3415
- this.seenEventIds = /* @__PURE__ */ new Map();
3416
3682
  this.secret = config.secret;
3417
3683
  this.tolerance = config.tolerance ?? 3e5;
3418
3684
  this.replayProtection = config.replayProtection ?? true;
3685
+ this.allowLegacySignature = config.allowLegacySignature ?? true;
3686
+ this.dedupStore = config.dedupStore ?? new InMemoryDedupStore();
3419
3687
  }
3420
3688
  /**
3421
3689
  * Register a handler for a specific event type.
@@ -3437,7 +3705,10 @@ var WebhookHandler = class {
3437
3705
  * Verify signature and parse the webhook body.
3438
3706
  */
3439
3707
  async verifyAndParse(body, signature) {
3440
- const isValid = await verifyWebhookSignature(body, signature, this.secret);
3708
+ const isValid = await verifyWebhookSignature(body, signature, this.secret, {
3709
+ tolerance: this.tolerance,
3710
+ allowLegacy: this.allowLegacySignature
3711
+ });
3441
3712
  if (!isValid) {
3442
3713
  throw new ValidationError("Invalid webhook signature", ["signature"]);
3443
3714
  }
@@ -3453,18 +3724,21 @@ var WebhookHandler = class {
3453
3724
  /**
3454
3725
  * Parse an event without signature verification (for testing).
3455
3726
  */
3456
- parseEvent(body) {
3727
+ async parseEvent(body) {
3457
3728
  return this.parseAndValidate(body);
3458
3729
  }
3459
- parseAndValidate(body) {
3730
+ async parseAndValidate(body) {
3460
3731
  const event = JSON.parse(body);
3461
- if (!event.type || !event.id || !event.timestamp) {
3462
- throw new ValidationError("Invalid webhook event: missing required fields (type, id, timestamp)", ["type", "id", "timestamp"]);
3732
+ const eventId = resolveEventId(event);
3733
+ if (!event.event_type || !eventId || !event.timestamp) {
3734
+ throw new ValidationError(
3735
+ "Invalid webhook event: missing required fields (event_type, id, timestamp)",
3736
+ ["event_type", "id", "timestamp"]
3737
+ );
3463
3738
  }
3464
3739
  if (this.tolerance > 0) {
3465
3740
  const eventTime = new Date(event.timestamp).getTime();
3466
- const now = Date.now();
3467
- if (Math.abs(now - eventTime) > this.tolerance) {
3741
+ if (Math.abs(Date.now() - eventTime) > this.tolerance) {
3468
3742
  throw new ValidationError(
3469
3743
  `Webhook event timestamp is outside tolerance window (${this.tolerance}ms)`,
3470
3744
  ["timestamp"]
@@ -3472,26 +3746,18 @@ var WebhookHandler = class {
3472
3746
  }
3473
3747
  }
3474
3748
  if (this.replayProtection) {
3475
- if (this.seenEventIds.has(event.id)) {
3749
+ if (await this.dedupStore.seen(eventId)) {
3476
3750
  throw new ValidationError(
3477
- `Duplicate webhook event: ${event.id} has already been processed`,
3751
+ `Duplicate webhook event: ${eventId} has already been processed`,
3478
3752
  ["id"]
3479
3753
  );
3480
3754
  }
3481
- const now = Date.now();
3482
- this.seenEventIds.set(event.id, now);
3483
- if (this.seenEventIds.size > 1e3) {
3484
- for (const [id, seenAt] of this.seenEventIds) {
3485
- if (now - seenAt > this.tolerance) {
3486
- this.seenEventIds.delete(id);
3487
- }
3488
- }
3489
- }
3755
+ await this.dedupStore.mark(eventId, this.tolerance);
3490
3756
  }
3491
3757
  return event;
3492
3758
  }
3493
3759
  async dispatch(event) {
3494
- const typeHandlers = this.handlers.get(event.type) || [];
3760
+ const typeHandlers = this.handlers.get(event.event_type) || [];
3495
3761
  const allHandlers = [...typeHandlers, ...this.anyHandlers];
3496
3762
  for (const handler of allHandlers) {
3497
3763
  await handler(event);
@@ -3582,26 +3848,34 @@ exports.ComplianceClient = ComplianceClient;
3582
3848
  exports.ComplianceError = ComplianceError;
3583
3849
  exports.DEFAULT_CURRENCY_RATES = DEFAULT_CURRENCY_RATES;
3584
3850
  exports.GeolocationClient = GeolocationClient;
3851
+ exports.InMemoryDedupStore = InMemoryDedupStore;
3585
3852
  exports.KYC_DECLINED_DESCRIPTIONS = KYC_DECLINED_DESCRIPTIONS;
3586
3853
  exports.KycClient = KycClient;
3587
3854
  exports.NetworkError = NetworkError;
3588
3855
  exports.RateLimitError = RateLimitError;
3589
3856
  exports.RateLimitTracker = RateLimitTracker;
3857
+ exports.RefreshingTokenProvider = RefreshingTokenProvider;
3590
3858
  exports.RiskProfileClient = RiskProfileClient;
3591
3859
  exports.SDK_VERSION = SDK_VERSION;
3592
3860
  exports.ServiceUnavailableError = ServiceUnavailableError;
3861
+ exports.StaticApiKeyProvider = StaticApiKeyProvider;
3593
3862
  exports.TaxClient = TaxClient;
3594
3863
  exports.TimeoutError = TimeoutError;
3595
3864
  exports.ValidationError = ValidationError;
3596
3865
  exports.VesantError = VesantError;
3597
3866
  exports.WebhookHandler = WebhookHandler;
3867
+ exports.assessGpsIntegrity = assessGpsIntegrity;
3868
+ exports.collectAll = collectAll;
3598
3869
  exports.createConsoleLogger = createConsoleLogger;
3599
3870
  exports.createNextWebhookHandler = createNextWebhookHandler;
3600
3871
  exports.createWebhookMiddleware = createWebhookMiddleware;
3601
3872
  exports.decodeCipherText = decodeCipherText;
3602
3873
  exports.generateCipherText = generateCipherText;
3874
+ exports.gpsIntegrityInputFromPosition = gpsIntegrityInputFromPosition;
3603
3875
  exports.isCipherTextExpired = isCipherTextExpired;
3604
3876
  exports.noopLogger = noopLogger;
3877
+ exports.paginate = paginate;
3878
+ exports.probeGeolocationPermission = probeGeolocationPermission;
3605
3879
  exports.sdkReasons = sdkReasons;
3606
3880
  exports.verifyWebhookSignature = verifyWebhookSignature;
3607
3881
  //# sourceMappingURL=index.js.map