vesant-sdk 1.7.1-dev.afc5a32 → 1.7.1-dev.c2ac5a5

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 (42) hide show
  1. package/README.md +4 -0
  2. package/dist/{client-D23KhWeh.d.mts → client-5q4whhGu.d.mts} +1 -1
  3. package/dist/{client-fy3trF2K.d.ts → client-BaUekQT8.d.ts} +32 -10
  4. package/dist/{client-B3DCAHlg.d.ts → client-DUHC-68_.d.ts} +1 -1
  5. package/dist/{client-BwXMQA2a.d.mts → client-XDEhh9VN.d.mts} +32 -10
  6. package/dist/compliance/index.d.mts +2 -2
  7. package/dist/compliance/index.d.ts +2 -2
  8. package/dist/compliance/index.js +39 -1
  9. package/dist/compliance/index.js.map +1 -1
  10. package/dist/compliance/index.mjs +39 -1
  11. package/dist/compliance/index.mjs.map +1 -1
  12. package/dist/geolocation/index.d.mts +2 -2
  13. package/dist/geolocation/index.d.ts +2 -2
  14. package/dist/geolocation/index.js +39 -1
  15. package/dist/geolocation/index.js.map +1 -1
  16. package/dist/geolocation/index.mjs +39 -1
  17. package/dist/geolocation/index.mjs.map +1 -1
  18. package/dist/index.d.mts +3 -3
  19. package/dist/index.d.ts +3 -3
  20. package/dist/index.js +86 -23
  21. package/dist/index.js.map +1 -1
  22. package/dist/index.mjs +86 -23
  23. package/dist/index.mjs.map +1 -1
  24. package/dist/kyc/core.d.mts +1 -1
  25. package/dist/kyc/core.d.ts +1 -1
  26. package/dist/kyc/core.js +47 -22
  27. package/dist/kyc/core.js.map +1 -1
  28. package/dist/kyc/core.mjs +47 -22
  29. package/dist/kyc/core.mjs.map +1 -1
  30. package/dist/kyc/index.d.mts +178 -39
  31. package/dist/kyc/index.d.ts +178 -39
  32. package/dist/kyc/index.js +47 -22
  33. package/dist/kyc/index.js.map +1 -1
  34. package/dist/kyc/index.mjs +47 -22
  35. package/dist/kyc/index.mjs.map +1 -1
  36. package/dist/react.d.mts +126 -48
  37. package/dist/react.d.ts +126 -48
  38. package/dist/react.js +131 -20
  39. package/dist/react.js.map +1 -1
  40. package/dist/react.mjs +129 -21
  41. package/dist/react.mjs.map +1 -1
  42. package/package.json +1 -1
package/dist/react.js CHANGED
@@ -429,14 +429,11 @@ function useCipherText(client) {
429
429
  const [configLoading, setConfigLoading] = React.useState(!!client);
430
430
  const [configError, setConfigError] = React.useState(null);
431
431
  const configRef = React.useRef(void 0);
432
- const configPromiseRef = React.useRef(null);
433
432
  React.useEffect(() => {
434
433
  if (!client) return;
435
434
  let cancelled = false;
436
435
  setConfigLoading(true);
437
- const promise = client.getGPSConfig();
438
- configPromiseRef.current = promise;
439
- promise.then((cfg) => {
436
+ client.getGPSConfig().then((cfg) => {
440
437
  if (!cancelled) {
441
438
  configRef.current = cfg;
442
439
  setConfigLoading(false);
@@ -451,13 +448,21 @@ function useCipherText(client) {
451
448
  cancelled = true;
452
449
  };
453
450
  }, [client]);
451
+ const refreshConfig = React.useCallback(async () => {
452
+ if (!client) return;
453
+ try {
454
+ configRef.current = await client.refreshGPSConfig();
455
+ setConfigError(null);
456
+ } catch (error) {
457
+ setConfigError(error instanceof Error ? error : new Error("Failed to refresh GPS config"));
458
+ }
459
+ }, [client]);
454
460
  const generate = React.useCallback(
455
461
  async (options) => {
456
462
  try {
457
- if (configPromiseRef.current) {
463
+ if (client) {
458
464
  try {
459
- const cfg = await configPromiseRef.current;
460
- configRef.current = cfg;
465
+ configRef.current = await client.getGPSConfig();
461
466
  } catch {
462
467
  }
463
468
  }
@@ -467,9 +472,9 @@ function useCipherText(client) {
467
472
  return null;
468
473
  }
469
474
  },
470
- []
475
+ [client]
471
476
  );
472
- return { generate, configLoading, configError };
477
+ return { generate, refreshConfig, configLoading, configError };
473
478
  }
474
479
  function useLocationRequests(client, options = {}) {
475
480
  const [requests, setRequests] = React.useState([]);
@@ -1009,6 +1014,46 @@ function loadMediaPipeFaceDetection(assetBasePath) {
1009
1014
 
1010
1015
  // src/kyc/hostedJourney.ts
1011
1016
  var REUSE_KYC_SESSION_EXPIRY_MS = 10 * 60 * 1e3;
1017
+ var JOURNEY_FRAME_HEIGHT = "clamp(500px, 64vh, 680px)";
1018
+ var JOURNEY_CONFIRM_TIMEOUT_MS = 45e3;
1019
+ function journeyOutcomeFromMessage(data) {
1020
+ const status = data?.verification_status;
1021
+ if (typeof status !== "string") return null;
1022
+ switch (status) {
1023
+ case "verification.accepted":
1024
+ return "accepted";
1025
+ case "verification.declined":
1026
+ return "declined";
1027
+ case "verification.cancelled":
1028
+ case "request.timeout":
1029
+ case "request.invalid":
1030
+ case "request.unauthorized":
1031
+ case "request.deleted":
1032
+ return "cancelled";
1033
+ default:
1034
+ return null;
1035
+ }
1036
+ }
1037
+ function journeyPollIntervalMs(confirming) {
1038
+ return confirming ? 1e3 : 2500;
1039
+ }
1040
+ function pendingJourneyResult(reference) {
1041
+ return {
1042
+ event: "reuse_kyc",
1043
+ reference,
1044
+ resource_id: "",
1045
+ status: "pending",
1046
+ data: {
1047
+ retries_remaining: 0,
1048
+ retry_limit_exceeded: false,
1049
+ trigger_alert: false,
1050
+ enforce_logout: false,
1051
+ freeze_account: false,
1052
+ block: false,
1053
+ freeze_duration_minutes: 0
1054
+ }
1055
+ };
1056
+ }
1012
1057
  function tryStartOperation(guard) {
1013
1058
  if (guard.current) return false;
1014
1059
  guard.current = true;
@@ -1020,12 +1065,22 @@ function releaseOperation(guard) {
1020
1065
  function retryTarget(hasHostedJourney) {
1021
1066
  return hasHostedJourney ? "journey" : "capture";
1022
1067
  }
1068
+ var CAPTURE_MODAL_MAX_WIDTH = 448;
1023
1069
  function modalMaxWidth(hasHostedJourney) {
1024
- return hasHostedJourney ? 680 : 448;
1070
+ return hasHostedJourney ? CAPTURE_MODAL_MAX_WIDTH : CAPTURE_MODAL_MAX_WIDTH;
1071
+ }
1072
+ var JOURNEY_CONTENT_PADDING = 12;
1073
+ function resultForModalClose(stageKind, result, unconfirmed) {
1074
+ const terminal = stageKind === "accepted" || stageKind === "declined" || stageKind === "max_attempts";
1075
+ if (terminal && result !== void 0) return result;
1076
+ const awaitingConfirmation = stageKind === "confirming" || stageKind === "unconfirmed";
1077
+ if (awaitingConfirmation && unconfirmed !== void 0) return unconfirmed;
1078
+ return null;
1025
1079
  }
1026
1080
 
1027
1081
  // src/kyc/FaceCaptureModal.tsx
1028
1082
  var MOBILE_UA = /Mobi|Android|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i;
1083
+ var ACCEPTED_AUTO_CLOSE_MS = 1200;
1029
1084
  var LIVENESS_MESSAGES = [
1030
1085
  "Position your face inside the oval",
1031
1086
  "Make sure your face is well lit",
@@ -1038,6 +1093,10 @@ function headerSubtitle(stageKind) {
1038
1093
  return "Use your phone to continue";
1039
1094
  case "journey":
1040
1095
  return "Follow the steps to verify your identity";
1096
+ case "confirming":
1097
+ return "Confirming your result";
1098
+ case "unconfirmed":
1099
+ return "Still confirming";
1041
1100
  case "accepted":
1042
1101
  return "All set";
1043
1102
  case "declined":
@@ -1137,8 +1196,10 @@ function FaceCaptureModal({
1137
1196
  stopped = true;
1138
1197
  };
1139
1198
  }, [stage.kind, stage.kind === "qr" ? stage.mobileConnected : false]);
1199
+ const journeyUrl = stage.kind === "journey" || stage.kind === "confirming" ? stage.url : "";
1140
1200
  React.useEffect(() => {
1141
- if (stage.kind !== "journey" || !stage.url) return;
1201
+ if (stage.kind !== "journey" && stage.kind !== "confirming" || !journeyUrl) return;
1202
+ const intervalMs = journeyPollIntervalMs(stage.kind === "confirming");
1142
1203
  let stopped = false;
1143
1204
  const checkOnce = async () => {
1144
1205
  try {
@@ -1155,27 +1216,43 @@ function FaceCaptureModal({
1155
1216
  if (stopped || cancelledRef.current) return;
1156
1217
  await checkOnce();
1157
1218
  if (!stopped && !cancelledRef.current) {
1158
- setTimeout(tick, 2500);
1219
+ setTimeout(tick, intervalMs);
1159
1220
  }
1160
1221
  };
1161
1222
  void tick();
1162
1223
  let frameOrigin = "";
1163
1224
  try {
1164
- frameOrigin = new URL(stage.url).origin;
1225
+ frameOrigin = new URL(journeyUrl).origin;
1165
1226
  } catch {
1166
1227
  frameOrigin = "";
1167
1228
  }
1168
1229
  const onMessage = (e) => {
1169
1230
  if (!frameOrigin || e.origin !== frameOrigin) return;
1170
- const status = e.data?.verification_status;
1171
- if (status) void checkOnce();
1231
+ const outcome = journeyOutcomeFromMessage(e.data);
1232
+ if (!outcome) return;
1233
+ void checkOnce();
1234
+ setStage((prev) => {
1235
+ if (prev.kind !== "journey") return prev;
1236
+ if (outcome === "cancelled") {
1237
+ return { kind: "error", message: "The verification was closed before it finished. Please try again." };
1238
+ }
1239
+ return { kind: "confirming", outcome, url: prev.url };
1240
+ });
1172
1241
  };
1173
1242
  window.addEventListener("message", onMessage);
1174
1243
  return () => {
1175
1244
  stopped = true;
1176
1245
  window.removeEventListener("message", onMessage);
1177
1246
  };
1178
- }, [stage.kind, stage.kind === "journey" ? stage.url : ""]);
1247
+ }, [stage.kind, journeyUrl]);
1248
+ React.useEffect(() => {
1249
+ if (stage.kind !== "confirming") return;
1250
+ const outcome = stage.outcome;
1251
+ const timer = setTimeout(() => {
1252
+ setStage((prev) => prev.kind === "confirming" ? { kind: "unconfirmed", outcome } : prev);
1253
+ }, JOURNEY_CONFIRM_TIMEOUT_MS);
1254
+ return () => clearTimeout(timer);
1255
+ }, [stage.kind, stage.kind === "confirming" ? stage.outcome : ""]);
1179
1256
  const handleJourneyRetry = async () => {
1180
1257
  if (!tryStartOperation(journeyRetryingRef)) return;
1181
1258
  setStage({ kind: "journey", url: "" });
@@ -1322,12 +1399,23 @@ function FaceCaptureModal({
1322
1399
  }, 3e3);
1323
1400
  return () => clearInterval(interval);
1324
1401
  }, [captureMode]);
1325
- const close = (result) => {
1402
+ const close = React.useCallback((result) => {
1326
1403
  cancelledRef.current = true;
1327
1404
  if (result === null && onCancel) {
1328
1405
  onCancel();
1329
1406
  }
1330
1407
  onComplete(result);
1408
+ }, [onCancel, onComplete]);
1409
+ React.useEffect(() => {
1410
+ if (stage.kind !== "accepted") return;
1411
+ const result = stage.result;
1412
+ const timer = setTimeout(() => close(result), ACCEPTED_AUTO_CLOSE_MS);
1413
+ return () => clearTimeout(timer);
1414
+ }, [close, stage]);
1415
+ const handleHeaderClose = () => {
1416
+ const result = "result" in stage ? stage.result : void 0;
1417
+ const unconfirmed = stage.kind === "confirming" || stage.kind === "unconfirmed" ? pendingJourneyResult(session.reference) : void 0;
1418
+ close(resultForModalClose(stage.kind, result, unconfirmed));
1331
1419
  };
1332
1420
  const renderChoose = () => /* @__PURE__ */ React__default.default.createElement("div", { style: contentStyle }, /* @__PURE__ */ React__default.default.createElement("div", { style: visualGuideContainerStyle }, /* @__PURE__ */ React__default.default.createElement("div", { style: visualGuideStyle }, /* @__PURE__ */ React__default.default.createElement("div", { style: circleStyle }, /* @__PURE__ */ React__default.default.createElement("img", { src: Camera, alt: "", width: 48, height: 48 })))), /* @__PURE__ */ React__default.default.createElement("p", { style: subtitleStyle }, "How would you like to verify?"), /* @__PURE__ */ React__default.default.createElement("div", { style: buttonsContainerStyle }, /* @__PURE__ */ React__default.default.createElement(
1333
1421
  "button",
@@ -1399,7 +1487,16 @@ function FaceCaptureModal({
1399
1487
  "Try Again"
1400
1488
  ), /* @__PURE__ */ React__default.default.createElement("button", { style: cancelButtonStyle, onClick: () => close(null) }, sessionExpired ? "Close" : "Cancel")));
1401
1489
  };
1402
- const renderJourney = (url) => /* @__PURE__ */ React__default.default.createElement("div", { style: contentStyle }, url ? /* @__PURE__ */ React__default.default.createElement(
1490
+ const renderConfirming = () => /* @__PURE__ */ React__default.default.createElement("div", { style: { ...contentStyle, alignItems: "center", textAlign: "center" } }, /* @__PURE__ */ React__default.default.createElement("div", { style: { ...spinnerStyle, margin: "24px auto", width: 32, height: 32 } }), /* @__PURE__ */ React__default.default.createElement("h3", { style: titleStyle }, "Confirming your result\u2026"), /* @__PURE__ */ React__default.default.createElement("p", { style: { ...attemptsTextStyle, marginTop: 8 } }, "This usually takes a few seconds."));
1491
+ const renderUnconfirmed = (outcome) => /* @__PURE__ */ React__default.default.createElement("div", { style: { ...contentStyle, alignItems: "center", textAlign: "center" } }, /* @__PURE__ */ React__default.default.createElement("div", { style: errorCircleStyle }, "!"), /* @__PURE__ */ React__default.default.createElement("h3", { style: titleStyle }, "Still confirming"), /* @__PURE__ */ React__default.default.createElement("p", { style: alertTextStyle }, outcome === "accepted" ? "Your verification was submitted, but we haven't received the final result yet. You can close this window and we'll keep checking in the background." : "Your last attempt wasn't accepted, but we haven't received the details yet. You can try again or close this window."), /* @__PURE__ */ React__default.default.createElement("div", { style: buttonsContainerStyle }, outcome === "declined" && /* @__PURE__ */ React__default.default.createElement("button", { style: primaryButtonStyle, onClick: () => void handleJourneyRetry() }, "Try Again"), /* @__PURE__ */ React__default.default.createElement(
1492
+ "button",
1493
+ {
1494
+ style: outcome === "accepted" ? primaryButtonStyle : cancelButtonStyle,
1495
+ onClick: () => close(pendingJourneyResult(session.reference))
1496
+ },
1497
+ "Close"
1498
+ )));
1499
+ const renderJourney = (url) => /* @__PURE__ */ React__default.default.createElement("div", { style: { ...contentStyle, padding: JOURNEY_CONTENT_PADDING } }, url ? /* @__PURE__ */ React__default.default.createElement(
1403
1500
  "iframe",
1404
1501
  {
1405
1502
  src: url,
@@ -1418,6 +1515,10 @@ function FaceCaptureModal({
1418
1515
  return renderCapture(stage.declinedReason);
1419
1516
  case "journey":
1420
1517
  return renderJourney(stage.url);
1518
+ case "confirming":
1519
+ return renderConfirming();
1520
+ case "unconfirmed":
1521
+ return renderUnconfirmed(stage.outcome);
1421
1522
  case "submitting":
1422
1523
  return renderSubmitting();
1423
1524
  case "accepted":
@@ -1433,7 +1534,7 @@ function FaceCaptureModal({
1433
1534
  return /* @__PURE__ */ React__default.default.createElement(React__default.default.Fragment, null, /* @__PURE__ */ React__default.default.createElement("style", null, keyframes), /* @__PURE__ */ React__default.default.createElement("div", { style: overlayStyle }, /* @__PURE__ */ React__default.default.createElement("div", { style: { ...modalStyle, maxWidth: modalMaxWidth(journeyMode) } }, /* @__PURE__ */ React__default.default.createElement("div", { style: headerStyle }, /* @__PURE__ */ React__default.default.createElement(
1434
1535
  "button",
1435
1536
  {
1436
- onClick: () => close(null),
1537
+ onClick: handleHeaderClose,
1437
1538
  style: closeButtonStyle,
1438
1539
  "aria-label": "Close"
1439
1540
  },
@@ -1492,7 +1593,7 @@ var contentStyle = {
1492
1593
  };
1493
1594
  var journeyFrameStyle = {
1494
1595
  width: "100%",
1495
- height: "min(520px, 60vh)",
1596
+ height: JOURNEY_FRAME_HEIGHT,
1496
1597
  border: 0,
1497
1598
  borderRadius: 12,
1498
1599
  background: "#f9fafb"
@@ -2279,6 +2380,13 @@ function getRiskColor(risk) {
2279
2380
  };
2280
2381
  return colorMap[risk] || "#6b7280";
2281
2382
  }
2383
+ function isMobileDevice(userAgent) {
2384
+ const ua = userAgent ?? (typeof navigator === "undefined" ? "" : navigator.userAgent);
2385
+ return /Mobi|Android|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(ua);
2386
+ }
2387
+ function buildEventBasedFaceVerificationQrPayload(token) {
2388
+ return `vesant://reuse-kyc?token=${encodeURIComponent(token)}`;
2389
+ }
2282
2390
  function useEventBasedFaceVerificationSubmission(client$1) {
2283
2391
  const startSession = React.useCallback(
2284
2392
  (request) => client$1.createEventBasedFaceVerificationSession(request),
@@ -2326,12 +2434,15 @@ function useEventBasedFaceVerificationSubmission(client$1) {
2326
2434
  return { startSession, verifyFace, runFlow };
2327
2435
  }
2328
2436
 
2437
+ exports.FaceCaptureModal = FaceCaptureModal_default;
2438
+ exports.buildEventBasedFaceVerificationQrPayload = buildEventBasedFaceVerificationQrPayload;
2329
2439
  exports.createDeviceFingerprint = createDeviceFingerprint;
2330
2440
  exports.fileToBase64 = fileToBase64;
2331
2441
  exports.formatKycStatus = formatKycStatus;
2332
2442
  exports.getBrowserInfo = getBrowserInfo;
2333
2443
  exports.getRiskColor = getRiskColor;
2334
2444
  exports.getStatusColor = getStatusColor;
2445
+ exports.isMobileDevice = isMobileDevice;
2335
2446
  exports.isValidFileSize = isValidFileSize;
2336
2447
  exports.isValidFileType = isValidFileType;
2337
2448
  exports.useCipherText = useCipherText;