vesant-sdk 1.7.0-dev.e0ee6d5 → 1.7.0-dev.e1da577

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 (43) hide show
  1. package/dist/{client-ZNdnpWe7.d.mts → client-B0qhE2kr.d.mts} +1 -1
  2. package/dist/{client-DoMSYMMR.d.ts → client-DF7hlMEz.d.ts} +13 -0
  3. package/dist/{client-DMIRx7Tu.d.mts → client-DrjgZoH_.d.mts} +13 -0
  4. package/dist/{client-C3DCmGe9.d.ts → client-DtH2RLuy.d.ts} +1 -1
  5. package/dist/compliance/index.d.mts +2 -2
  6. package/dist/compliance/index.d.ts +2 -2
  7. package/dist/compliance/index.js +0 -3
  8. package/dist/compliance/index.js.map +1 -1
  9. package/dist/compliance/index.mjs +0 -3
  10. package/dist/compliance/index.mjs.map +1 -1
  11. package/dist/geolocation/index.d.mts +2 -2
  12. package/dist/geolocation/index.d.ts +2 -2
  13. package/dist/index.d.mts +4 -4
  14. package/dist/index.d.ts +4 -4
  15. package/dist/index.js +40 -22
  16. package/dist/index.js.map +1 -1
  17. package/dist/index.mjs +40 -22
  18. package/dist/index.mjs.map +1 -1
  19. package/dist/kyc/core.d.mts +1 -1
  20. package/dist/kyc/core.d.ts +1 -1
  21. package/dist/kyc/core.js +36 -18
  22. package/dist/kyc/core.js.map +1 -1
  23. package/dist/kyc/core.mjs +36 -18
  24. package/dist/kyc/core.mjs.map +1 -1
  25. package/dist/kyc/index.d.mts +161 -58
  26. package/dist/kyc/index.d.ts +161 -58
  27. package/dist/kyc/index.js +36 -18
  28. package/dist/kyc/index.js.map +1 -1
  29. package/dist/kyc/index.mjs +36 -18
  30. package/dist/kyc/index.mjs.map +1 -1
  31. package/dist/react.d.mts +18 -14
  32. package/dist/react.d.ts +18 -14
  33. package/dist/react.js +303 -35
  34. package/dist/react.js.map +1 -1
  35. package/dist/react.mjs +303 -35
  36. package/dist/react.mjs.map +1 -1
  37. package/dist/tax/index.d.mts +18 -1
  38. package/dist/tax/index.d.ts +18 -1
  39. package/dist/tax/index.js +4 -1
  40. package/dist/tax/index.js.map +1 -1
  41. package/dist/tax/index.mjs +4 -1
  42. package/dist/tax/index.mjs.map +1 -1
  43. package/package.json +1 -1
package/dist/react.mjs CHANGED
@@ -882,10 +882,91 @@ function useCustomerProfile(client, customerId, options = {}) {
882
882
  var Camera = "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCg0KPCFET0NUWVBFIHN2ZyBQVUJMSUMgIi0vL1czQy8vRFREIFNWRyAxLjAvL0VOIiAiaHR0cDovL3d3dy53My5vcmcvVFIvMjAwMS9SRUMtU1ZHLTIwMDEwOTA0L0RURC9zdmcxMC5kdGQiPg0KPCEtLSBVcGxvYWRlZCB0bzogU1ZHIFJlcG8sIHd3dy5zdmdyZXBvLmNvbSwgR2VuZXJhdG9yOiBTVkcgUmVwbyBNaXhlciBUb29scyAtLT4NCjxzdmcgdmVyc2lvbj0iMS4wIiBpZD0iTGF5ZXJfMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgDQoJIHdpZHRoPSI4MDBweCIgaGVpZ2h0PSI4MDBweCIgdmlld0JveD0iMCAwIDY0IDY0IiBlbmFibGUtYmFja2dyb3VuZD0ibmV3IDAgMCA2NCA2NCIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+DQo8Zz4NCgk8cGF0aCBmaWxsPSIjMjMxRjIwIiBkPSJNNjAsMTBINDkuNjU2bC02LjgyOC02LjgyOEM0Mi4wNzgsMi40MjIsNDEuMDYyLDIsNDAsMkgyNGMtMS4wNjIsMC0yLjA3OCwwLjQyMi0yLjgyOCwxLjE3MkwxNC4zNDQsMTBINA0KCQljLTIuMjExLDAtNCwxLjc4OS00LDR2NDRjMCwyLjIxMSwxLjc4OSw0LDQsNGg1NmMyLjIxMSwwLDQtMS43ODksNC00VjE0QzY0LDExLjc4OSw2Mi4yMTEsMTAsNjAsMTB6IE0zMiw1MA0KCQljLTguODM2LDAtMTYtNy4xNjQtMTYtMTZzNy4xNjQtMTYsMTYtMTZzMTYsNy4xNjQsMTYsMTZTNDAuODM2LDUwLDMyLDUweiIvPg0KCTxjaXJjbGUgZmlsbD0iIzIzMUYyMCIgY3g9IjMyIiBjeT0iMzQiIHI9IjgiLz4NCjwvZz4NCjwvc3ZnPg==";
883
883
  var Done = "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz48IS0tIFVwbG9hZGVkIHRvOiBTVkcgUmVwbywgd3d3LnN2Z3JlcG8uY29tLCBHZW5lcmF0b3I6IFNWRyBSZXBvIE1peGVyIFRvb2xzIC0tPg0KPHN2ZyB3aWR0aD0iODAwcHgiIGhlaWdodD0iODAwcHgiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4NCjxwYXRoIGQ9Ik04LjUgMTIuNUwxMC41IDE0LjVMMTUuNSA5LjUiIHN0cm9rZT0iIzFDMjc0QyIgc3Ryb2tlLXdpZHRoPSIxLjUiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPg0KPHBhdGggZD0iTTcgMy4zMzc4MkM4LjQ3MDg3IDIuNDg2OTcgMTAuMTc4NiAyIDEyIDJDMTcuNTIyOCAyIDIyIDYuNDc3MTUgMjIgMTJDMjIgMTcuNTIyOCAxNy41MjI4IDIyIDEyIDIyQzYuNDc3MTUgMjIgMiAxNy41MjI4IDIgMTJDMiAxMC4xNzg2IDIuNDg2OTcgOC40NzA4NyAzLjMzNzgyIDciIHN0cm9rZT0iIzFDMjc0QyIgc3Ryb2tlLXdpZHRoPSIxLjUiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIvPg0KPC9zdmc+";
884
884
  var Close = "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz48IS0tIFVwbG9hZGVkIHRvOiBTVkcgUmVwbywgd3d3LnN2Z3JlcG8uY29tLCBHZW5lcmF0b3I6IFNWRyBSZXBvIE1peGVyIFRvb2xzIC0tPg0KPHN2ZyB3aWR0aD0iODAwcHgiIGhlaWdodD0iODAwcHgiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4NCjxwYXRoIGQ9Ik0xNC41IDkuNTAwMDJMOS41IDE0LjVNOS40OTk5OCA5LjVMMTQuNSAxNC41IiBzdHJva2U9IiMxQzI3NEMiIHN0cm9rZS13aWR0aD0iMS41IiBzdHJva2UtbGluZWNhcD0icm91bmQiLz4NCjxwYXRoIGQ9Ik03IDMuMzM3ODJDOC40NzA4NyAyLjQ4Njk3IDEwLjE3ODYgMiAxMiAyQzE3LjUyMjggMiAyMiA2LjQ3NzE1IDIyIDEyQzIyIDE3LjUyMjggMTcuNTIyOCAyMiAxMiAyMkM2LjQ3NzE1IDIyIDIgMTcuNTIyOCAyIDEyQzIgMTAuMTc4NiAyLjQ4Njk3IDguNDcwODcgMy4zMzc4MiA3IiBzdHJva2U9IiMxQzI3NEMiIHN0cm9rZS13aWR0aD0iMS41IiBzdHJva2UtbGluZWNhcD0icm91bmQiLz4NCjwvc3ZnPg==";
885
- var Upload = "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz48IS0tIFVwbG9hZGVkIHRvOiBTVkcgUmVwbywgd3d3LnN2Z3JlcG8uY29tLCBHZW5lcmF0b3I6IFNWRyBSZXBvIE1peGVyIFRvb2xzIC0tPg0KPHN2ZyB3aWR0aD0iODAwcHgiIGhlaWdodD0iODAwcHgiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4NCjxwYXRoIGQ9Ik0xNyAxN0gxNy4wMU0xNS42IDE0SDE4QzE4LjkzMTkgMTQgMTkuMzk3OCAxNCAxOS43NjU0IDE0LjE1MjJDMjAuMjU1NCAxNC4zNTUyIDIwLjY0NDggMTQuNzQ0NiAyMC44NDc4IDE1LjIzNDZDMjEgMTUuNjAyMiAyMSAxNi4wNjgxIDIxIDE3QzIxIDE3LjkzMTkgMjEgMTguMzk3OCAyMC44NDc4IDE4Ljc2NTRDMjAuNjQ0OCAxOS4yNTU0IDIwLjI1NTQgMTkuNjQ0OCAxOS43NjU0IDE5Ljg0NzhDMTkuMzk3OCAyMCAxOC45MzE5IDIwIDE4IDIwSDZDNS4wNjgxMiAyMCA0LjYwMjE4IDIwIDQuMjM0NjMgMTkuODQ3OEMzLjc0NDU4IDE5LjY0NDggMy4zNTUyMyAxOS4yNTU0IDMuMTUyMjQgMTguNzY1NEMzIDE4LjM5NzggMyAxNy45MzE5IDMgMTdDMyAxNi4wNjgxIDMgMTUuNjAyMiAzLjE1MjI0IDE1LjIzNDZDMy4zNTUyMyAxNC43NDQ2IDMuNzQ0NTggMTQuMzU1MiA0LjIzNDYzIDE0LjE1MjJDNC42MDIxOCAxNCA1LjA2ODEyIDE0IDYgMTRIOC40TTEyIDE1VjRNMTIgNEwxNSA3TTEyIDRMOSA3IiBzdHJva2U9IiMwMDAwMDAiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+DQo8L3N2Zz4=";
885
+
886
+ // src/kyc/faceDetection.ts
887
+ var FACE_MESSAGES = {
888
+ idle: "Initializing camera...",
889
+ loading: "Loading face detection...",
890
+ scanning: "Position your face in the circle",
891
+ too_far: "Face too far. Please move closer to the camera",
892
+ too_close: "Too close. Please move back a little",
893
+ off_center: "Center your face in the circle",
894
+ ok: "Looking great! Tap the button to capture"
895
+ };
896
+ var FACE_COLORS = {
897
+ idle: "#9ca3af",
898
+ loading: "#3b82f6",
899
+ scanning: "#3b82f6",
900
+ too_far: "#ef4444",
901
+ too_close: "#f59e0b",
902
+ off_center: "#f59e0b",
903
+ ok: "#10b981"
904
+ };
905
+ var MEDIAPIPE_BASE = "https://cdn.jsdelivr.net/npm/@mediapipe/face_detection";
906
+ function mapDetectionsToFaceStatus(detections) {
907
+ if (detections.length === 0) return "scanning";
908
+ const bb = detections[0].boundingBox;
909
+ if (bb.width < 0.18) return "too_far";
910
+ if (bb.width > 0.72) return "too_close";
911
+ if (bb.xCenter < 0.28 || bb.xCenter > 0.72 || bb.yCenter < 0.28 || bb.yCenter > 0.72) {
912
+ return "off_center";
913
+ }
914
+ return "ok";
915
+ }
916
+ function isCaptureDisabled(ready, detectionActive, faceStatus) {
917
+ return !ready || detectionActive && faceStatus !== "ok";
918
+ }
919
+ function resolveMediaPipeBase(base) {
920
+ if (!base) return MEDIAPIPE_BASE;
921
+ return base.endsWith("/") ? base.slice(0, -1) : base;
922
+ }
923
+ var LIVENESS_SAMPLE_LIMIT = 20;
924
+ function buildLivenessArtifact(samples) {
925
+ if (samples.length === 0) return void 0;
926
+ return JSON.stringify({
927
+ version: 1,
928
+ source: "mediapipe-face-detection",
929
+ samples: samples.slice(-LIVENESS_SAMPLE_LIMIT)
930
+ });
931
+ }
932
+ var mediapipeLoaderPromises = /* @__PURE__ */ new Map();
933
+ function loadMediaPipeFaceDetection(assetBasePath) {
934
+ if (typeof window === "undefined" || typeof document === "undefined") {
935
+ return Promise.reject(new Error("MediaPipe requires a browser environment"));
936
+ }
937
+ const w = window;
938
+ if (w.FaceDetection) {
939
+ return Promise.resolve(w.FaceDetection);
940
+ }
941
+ const src = `${resolveMediaPipeBase(assetBasePath)}/face_detection.js`;
942
+ const existing = mediapipeLoaderPromises.get(src);
943
+ if (existing) return existing;
944
+ const loader = new Promise((resolve, reject) => {
945
+ const script = document.createElement("script");
946
+ script.src = src;
947
+ script.async = true;
948
+ script.crossOrigin = "anonymous";
949
+ script.onload = () => {
950
+ if (w.FaceDetection) {
951
+ resolve(w.FaceDetection);
952
+ } else {
953
+ mediapipeLoaderPromises.delete(src);
954
+ reject(new Error("MediaPipe loaded but FaceDetection global is missing"));
955
+ }
956
+ };
957
+ script.onerror = () => {
958
+ mediapipeLoaderPromises.delete(src);
959
+ reject(new Error("Failed to load MediaPipe face_detection script"));
960
+ };
961
+ document.head.appendChild(script);
962
+ });
963
+ mediapipeLoaderPromises.set(src, loader);
964
+ return loader;
965
+ }
886
966
 
887
967
  // src/kyc/FaceCaptureModal.tsx
888
968
  var MOBILE_UA = /Mobi|Android|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i;
969
+ var SESSION_EXPIRY_MS = 15 * 60 * 1e3;
889
970
  var LIVENESS_MESSAGES = [
890
971
  "Position your face inside the circle",
891
972
  "Make sure your face is well lit",
@@ -899,9 +980,9 @@ function headerSubtitle(stageKind) {
899
980
  case "accepted":
900
981
  return "All set";
901
982
  case "declined":
902
- return "Verification didn't match, try once more";
983
+ return "Verification Declined";
903
984
  case "max_attempts":
904
- return "We couldn't verify you";
985
+ return "We couldn't verify your identity";
905
986
  default:
906
987
  return "Please capture a clear photo of your face";
907
988
  }
@@ -914,16 +995,19 @@ function FaceCaptureModal({
914
995
  client,
915
996
  session,
916
997
  onComplete,
998
+ onCancel,
917
999
  defaultDevice,
918
- renderQR
1000
+ renderQR,
1001
+ mediapipeAssetBasePath
919
1002
  }) {
920
1003
  const isMobile = typeof navigator !== "undefined" && MOBILE_UA.test(navigator.userAgent);
1004
+ const initialChoice = defaultDevice ?? (isMobile ? "this" : "ask");
921
1005
  const initialStage = (() => {
922
- const choice = defaultDevice ?? (isMobile ? "this" : "ask");
923
- if (choice === "this") return { kind: "capture" };
924
- if (choice === "mobile") return { kind: "qr", mobileConnected: false };
1006
+ if (initialChoice === "this") return { kind: "capture" };
1007
+ if (initialChoice === "mobile") return { kind: "qr", mobileConnected: false };
925
1008
  return { kind: "choose" };
926
1009
  })();
1010
+ const hasMethodChoice = initialChoice === "ask";
927
1011
  const [stage, setStage] = useState(initialStage);
928
1012
  const [attempts, setAttempts] = useState(session.attempts);
929
1013
  const maxAttempts = session.max_attempts || 1;
@@ -931,21 +1015,30 @@ function FaceCaptureModal({
931
1015
  const [captureMode, setCaptureMode] = useState("idle");
932
1016
  const [capturedPreview, setCapturedPreview] = useState(null);
933
1017
  const [capturedBase64, setCapturedBase64] = useState(null);
1018
+ const [capturedArtifact, setCapturedArtifact] = useState(null);
934
1019
  const [livenessMessage, setLivenessMessage] = useState(LIVENESS_MESSAGES[0]);
935
1020
  const qrPayload = session.link || `vesant://reuse-kyc?token=${encodeURIComponent(session.token)}`;
936
1021
  const cancelledRef = useRef(false);
937
1022
  const submittingRef = useRef(false);
1023
+ useEffect(() => {
1024
+ const timer = setTimeout(() => {
1025
+ setStage(
1026
+ (prev) => prev.kind === "choose" || prev.kind === "qr" || prev.kind === "capture" ? { kind: "error", message: "Session expired" } : prev
1027
+ );
1028
+ }, SESSION_EXPIRY_MS);
1029
+ return () => clearTimeout(timer);
1030
+ }, [session.token]);
938
1031
  useEffect(() => {
939
1032
  if (stage.kind !== "qr") return;
940
1033
  let stopped = false;
941
1034
  const intervalMs = 2e3;
942
- const deadline = Date.now() + 15 * 60 * 1e3;
1035
+ const deadline = Date.now() + SESSION_EXPIRY_MS;
943
1036
  let mobileSeen = stage.mobileConnected;
944
1037
  const tick = async () => {
945
1038
  if (stopped || cancelledRef.current) return;
946
1039
  try {
947
1040
  if (mobileSeen) {
948
- const result = await client.getReuseKycSessionStatus(session.reference);
1041
+ const result = await client.getEventBasedFaceVerificationSessionStatus(session.token);
949
1042
  if (result.status === "accepted") {
950
1043
  setStage({ kind: "accepted", result });
951
1044
  stopped = true;
@@ -1017,10 +1110,13 @@ function FaceCaptureModal({
1017
1110
  try {
1018
1111
  const postPromise = (async () => {
1019
1112
  try {
1020
- const data = await client.submitReuseKycSession({
1113
+ const data = await client.submitEventBasedFaceVerificationSession({
1021
1114
  token: session.token,
1022
1115
  reference: ref,
1023
- proof
1116
+ proof,
1117
+ // undefined when detection wasn't running for this capture —
1118
+ // JSON.stringify drops it, so the field is absent on the wire.
1119
+ liveness_artifact: capturedArtifact ?? void 0
1024
1120
  });
1025
1121
  return { ok: true, data };
1026
1122
  } catch (err2) {
@@ -1033,7 +1129,7 @@ function FaceCaptureModal({
1033
1129
  while (Date.now() < deadline) {
1034
1130
  if (settled || cancelledRef.current) return null;
1035
1131
  try {
1036
- const r = await client.getReuseKycSessionStatus(ref);
1132
+ const r = await client.getEventBasedFaceVerificationSessionStatus(session.token);
1037
1133
  if (isFreshPollResult(r)) return r;
1038
1134
  } catch {
1039
1135
  }
@@ -1066,16 +1162,18 @@ function FaceCaptureModal({
1066
1162
  submittingRef.current = false;
1067
1163
  }
1068
1164
  };
1069
- const handleCameraCapture = (dataUrl) => {
1165
+ const handleCameraCapture = (dataUrl, livenessArtifact) => {
1070
1166
  const raw = dataUrl.split(",")[1] ?? "";
1071
1167
  setCapturedPreview(dataUrl);
1072
1168
  setCapturedBase64(raw);
1169
+ setCapturedArtifact(livenessArtifact ?? null);
1073
1170
  setCaptureMode("preview");
1074
1171
  };
1075
1172
  const handleCameraCancel = () => setCaptureMode("idle");
1076
1173
  const handleRetake = () => {
1077
1174
  setCapturedPreview(null);
1078
1175
  setCapturedBase64(null);
1176
+ setCapturedArtifact(null);
1079
1177
  setCaptureMode("live");
1080
1178
  };
1081
1179
  const handleConfirmSubmit = () => {
@@ -1104,6 +1202,9 @@ function FaceCaptureModal({
1104
1202
  }, [captureMode]);
1105
1203
  const close = (result) => {
1106
1204
  cancelledRef.current = true;
1205
+ if (result === null && onCancel) {
1206
+ onCancel();
1207
+ }
1107
1208
  onComplete(result);
1108
1209
  };
1109
1210
  const renderChoose = () => /* @__PURE__ */ React.createElement("div", { style: contentStyle }, /* @__PURE__ */ React.createElement("div", { style: visualGuideContainerStyle }, /* @__PURE__ */ React.createElement("div", { style: visualGuideStyle }, /* @__PURE__ */ React.createElement("div", { style: circleStyle }, /* @__PURE__ */ React.createElement("img", { src: Camera, alt: "", width: 48, height: 48 })))), /* @__PURE__ */ React.createElement("p", { style: subtitleStyle }, "How would you like to verify?"), /* @__PURE__ */ React.createElement("div", { style: buttonsContainerStyle }, /* @__PURE__ */ React.createElement(
@@ -1112,7 +1213,7 @@ function FaceCaptureModal({
1112
1213
  style: primaryButtonStyle,
1113
1214
  onClick: () => setStage({ kind: "capture" })
1114
1215
  },
1115
- /* @__PURE__ */ React.createElement("img", { src: isMobile ? Camera : Upload, alt: "", width: 20, height: 20 }),
1216
+ /* @__PURE__ */ React.createElement("img", { src: Camera, alt: "", width: 20, height: 20 }),
1116
1217
  /* @__PURE__ */ React.createElement("span", null, "Continue on this device")
1117
1218
  ), /* @__PURE__ */ React.createElement(
1118
1219
  "button",
@@ -1129,6 +1230,13 @@ function FaceCaptureModal({
1129
1230
  onClick: () => setStage({ kind: "capture" })
1130
1231
  },
1131
1232
  "Use this device instead"
1233
+ ), hasMethodChoice && /* @__PURE__ */ React.createElement(
1234
+ "button",
1235
+ {
1236
+ style: secondaryButtonStyle,
1237
+ onClick: () => setStage({ kind: "choose" })
1238
+ },
1239
+ "Change verification method"
1132
1240
  ), /* @__PURE__ */ React.createElement("button", { style: cancelButtonStyle, onClick: () => close(null) }, "Cancel")));
1133
1241
  const renderCapture = (declinedReason) => {
1134
1242
  if (captureMode === "live") {
@@ -1137,20 +1245,31 @@ function FaceCaptureModal({
1137
1245
  {
1138
1246
  onCapture: handleCameraCapture,
1139
1247
  onCancel: handleCameraCancel,
1140
- message: livenessMessage
1248
+ message: livenessMessage,
1249
+ assetBasePath: mediapipeAssetBasePath
1141
1250
  }
1142
1251
  ), /* @__PURE__ */ React.createElement("p", { style: attemptsTextStyle }, "Attempt ", attempts + 1, " of ", maxAttempts));
1143
1252
  }
1144
1253
  if (captureMode === "preview" && capturedPreview) {
1145
1254
  return /* @__PURE__ */ React.createElement("div", { style: contentStyle }, declinedReason && /* @__PURE__ */ React.createElement("div", { style: alertBoxStyle }, /* @__PURE__ */ React.createElement("strong", null, "Verification declined."), /* @__PURE__ */ React.createElement("p", { style: alertTextStyle }, declinedReason)), /* @__PURE__ */ React.createElement("p", { style: subtitleStyle }, "Looks good? Submit this selfie or retake it."), /* @__PURE__ */ React.createElement("div", { style: previewBoxStyle }, /* @__PURE__ */ React.createElement("img", { src: capturedPreview, alt: "Captured selfie preview", style: previewImgStyle })), /* @__PURE__ */ React.createElement("div", { style: buttonsContainerStyle }, /* @__PURE__ */ React.createElement("button", { style: primaryButtonStyle, onClick: handleConfirmSubmit }, /* @__PURE__ */ React.createElement("img", { src: Done, alt: "", width: 20, height: 20 }), /* @__PURE__ */ React.createElement("span", null, "Submit")), /* @__PURE__ */ React.createElement("button", { style: secondaryButtonStyle, onClick: handleRetake }, "Retake"), /* @__PURE__ */ React.createElement("button", { style: cancelButtonStyle, onClick: () => close(null) }, "Cancel")), /* @__PURE__ */ React.createElement("p", { style: attemptsTextStyle }, "Attempt ", attempts + 1, " of ", maxAttempts));
1146
1255
  }
1147
- return /* @__PURE__ */ React.createElement("div", { style: contentStyle }, /* @__PURE__ */ React.createElement("div", { style: visualGuideContainerStyle }, /* @__PURE__ */ React.createElement("div", { style: visualGuideStyle }, /* @__PURE__ */ React.createElement("div", { style: circleStyle }, /* @__PURE__ */ React.createElement("img", { src: Camera, alt: "", width: 48, height: 48 })), /* @__PURE__ */ React.createElement("div", { style: badgeStyle }, /* @__PURE__ */ React.createElement("img", { src: Done, alt: "", width: 16, height: 16 })))), declinedReason && /* @__PURE__ */ React.createElement("div", { style: alertBoxStyle }, /* @__PURE__ */ React.createElement("strong", null, "Verification declined."), /* @__PURE__ */ React.createElement("p", { style: alertTextStyle }, declinedReason)), /* @__PURE__ */ React.createElement("div", { style: instructionsBoxStyle }, /* @__PURE__ */ React.createElement("h3", { style: instructionsTitleStyle }, "Tips for best results:"), /* @__PURE__ */ React.createElement("ul", { style: instructionsListStyle }, /* @__PURE__ */ React.createElement("li", { style: instructionItemStyle }, /* @__PURE__ */ React.createElement("span", { style: bulletStyle }, "\u2022"), /* @__PURE__ */ React.createElement("span", null, "Ensure good lighting on your face")), /* @__PURE__ */ React.createElement("li", { style: instructionItemStyle }, /* @__PURE__ */ React.createElement("span", { style: bulletStyle }, "\u2022"), /* @__PURE__ */ React.createElement("span", null, "Remove glasses or accessories if possible")), /* @__PURE__ */ React.createElement("li", { style: instructionItemStyle }, /* @__PURE__ */ React.createElement("span", { style: bulletStyle }, "\u2022"), /* @__PURE__ */ React.createElement("span", null, "Look directly at the camera")))), /* @__PURE__ */ React.createElement("div", { style: buttonsContainerStyle }, /* @__PURE__ */ React.createElement("button", { style: primaryButtonStyle, onClick: handleOpenCamera }, /* @__PURE__ */ React.createElement("img", { src: Camera, alt: "", width: 20, height: 20 }), /* @__PURE__ */ React.createElement("span", null, declinedReason ? "Try again" : "Open Camera")), /* @__PURE__ */ React.createElement("button", { style: cancelButtonStyle, onClick: () => close(null) }, "Cancel")), /* @__PURE__ */ React.createElement("p", { style: attemptsTextStyle }, "Attempt ", attempts + 1, " of ", maxAttempts));
1256
+ return /* @__PURE__ */ React.createElement("div", { style: contentStyle }, /* @__PURE__ */ React.createElement("div", { style: visualGuideContainerStyle }, /* @__PURE__ */ React.createElement("div", { style: visualGuideStyle }, /* @__PURE__ */ React.createElement("div", { style: circleStyle }, /* @__PURE__ */ React.createElement("img", { src: Camera, alt: "", width: 48, height: 48 })), /* @__PURE__ */ React.createElement("div", { style: badgeStyle }, /* @__PURE__ */ React.createElement("img", { src: Done, alt: "", width: 16, height: 16 })))), declinedReason && /* @__PURE__ */ React.createElement("div", { style: alertBoxStyle }, /* @__PURE__ */ React.createElement("strong", null, "Verification declined."), /* @__PURE__ */ React.createElement("p", { style: alertTextStyle }, declinedReason)), /* @__PURE__ */ React.createElement("div", { style: instructionsBoxStyle }, /* @__PURE__ */ React.createElement("h3", { style: instructionsTitleStyle }, "Tips for best results:"), /* @__PURE__ */ React.createElement("ul", { style: instructionsListStyle }, /* @__PURE__ */ React.createElement("li", { style: instructionItemStyle }, /* @__PURE__ */ React.createElement("span", { style: bulletStyle }, "\u2022"), /* @__PURE__ */ React.createElement("span", null, "Ensure good lighting on your face")), /* @__PURE__ */ React.createElement("li", { style: instructionItemStyle }, /* @__PURE__ */ React.createElement("span", { style: bulletStyle }, "\u2022"), /* @__PURE__ */ React.createElement("span", null, "Remove glasses or accessories if possible")), /* @__PURE__ */ React.createElement("li", { style: instructionItemStyle }, /* @__PURE__ */ React.createElement("span", { style: bulletStyle }, "\u2022"), /* @__PURE__ */ React.createElement("span", null, "Look directly at the camera")))), /* @__PURE__ */ React.createElement("div", { style: buttonsContainerStyle }, /* @__PURE__ */ React.createElement("button", { style: primaryButtonStyle, onClick: handleOpenCamera }, /* @__PURE__ */ React.createElement("img", { src: Camera, alt: "", width: 20, height: 20 }), /* @__PURE__ */ React.createElement("span", null, declinedReason ? "Try Again" : "Open Camera")), hasMethodChoice && /* @__PURE__ */ React.createElement(
1257
+ "button",
1258
+ {
1259
+ style: secondaryButtonStyle,
1260
+ onClick: () => setStage({ kind: "choose" })
1261
+ },
1262
+ "Change verification method"
1263
+ ), /* @__PURE__ */ React.createElement("button", { style: cancelButtonStyle, onClick: () => close(null) }, "Cancel")), /* @__PURE__ */ React.createElement("p", { style: attemptsTextStyle }, "Attempt ", attempts + 1, " of ", maxAttempts));
1148
1264
  };
1149
1265
  const renderSubmitting = () => /* @__PURE__ */ React.createElement("div", { style: { ...contentStyle, alignItems: "center", textAlign: "center" } }, /* @__PURE__ */ React.createElement("div", { style: { ...spinnerStyle, margin: "24px auto", width: 32, height: 32 } }), /* @__PURE__ */ React.createElement("p", { style: subtitleStyle }, "Verifying your photo\u2026"), /* @__PURE__ */ React.createElement("p", { style: { ...attemptsTextStyle, marginTop: 8 } }, "This usually takes a few seconds."));
1150
1266
  const renderAccepted = (result) => /* @__PURE__ */ React.createElement("div", { style: { ...contentStyle, alignItems: "center", textAlign: "center" } }, /* @__PURE__ */ React.createElement("div", { style: successCircleStyle }, /* @__PURE__ */ React.createElement("img", { src: Done, alt: "", width: 48, height: 48 })), /* @__PURE__ */ React.createElement("h3", { style: titleStyle }, "Verified"), /* @__PURE__ */ React.createElement("p", { style: subtitleStyle }, "Your identity has been confirmed. You can continue."), /* @__PURE__ */ React.createElement("div", { style: buttonsContainerStyle }, /* @__PURE__ */ React.createElement("button", { style: primaryButtonStyle, onClick: () => close(result) }, "Continue")));
1151
- const renderDeclined = (result) => /* @__PURE__ */ React.createElement("div", { style: contentStyle }, /* @__PURE__ */ React.createElement("div", { style: alertBoxStyle }, /* @__PURE__ */ React.createElement("strong", null, "Verification declined."), /* @__PURE__ */ React.createElement("p", { style: alertTextStyle }, result.declined_reason ?? "We couldn't match your selfie. Please take a new one.")), /* @__PURE__ */ React.createElement("div", { style: buttonsContainerStyle }, /* @__PURE__ */ React.createElement("button", { style: primaryButtonStyle, onClick: handleRetakeAfterDecline }, /* @__PURE__ */ React.createElement("img", { src: Camera, alt: "", width: 20, height: 20 }), /* @__PURE__ */ React.createElement("span", null, "Retake")), /* @__PURE__ */ React.createElement("button", { style: cancelButtonStyle, onClick: () => close(result) }, "Cancel")), /* @__PURE__ */ React.createElement("p", { style: attemptsTextStyle }, "Attempt ", Math.min(attempts + 1, maxAttempts), " of ", maxAttempts));
1152
- const renderMaxAttempts = (result) => /* @__PURE__ */ React.createElement("div", { style: { ...contentStyle, alignItems: "center", textAlign: "center" } }, /* @__PURE__ */ React.createElement("div", { style: errorCircleStyle }, "!"), /* @__PURE__ */ React.createElement("h3", { style: titleStyle }, "Maximum attempts reached"), /* @__PURE__ */ React.createElement("p", { style: subtitleStyle }, result.declined_reason ?? "We couldn't verify your identity after several attempts."), result.data?.freeze_account && /* @__PURE__ */ React.createElement("p", { style: alertTextStyle }, "For your security, your account has been temporarily restricted", result.data.freeze_duration_minutes ? ` for ${result.data.freeze_duration_minutes} minutes` : "", ". Please contact support if you need immediate help."), result.data?.enforce_logout && /* @__PURE__ */ React.createElement("p", { style: alertTextStyle }, "You will be signed out of your session."), /* @__PURE__ */ React.createElement("div", { style: buttonsContainerStyle }, /* @__PURE__ */ React.createElement("button", { style: primaryButtonStyle, onClick: () => close(result) }, "Close")));
1153
- const renderError = (message) => /* @__PURE__ */ React.createElement("div", { style: { ...contentStyle, alignItems: "center", textAlign: "center" } }, /* @__PURE__ */ React.createElement("div", { style: errorCircleStyle }, "!"), /* @__PURE__ */ React.createElement("h3", { style: titleStyle }, "Something went wrong"), /* @__PURE__ */ React.createElement("p", { style: alertTextStyle }, message), /* @__PURE__ */ React.createElement("div", { style: buttonsContainerStyle }, /* @__PURE__ */ React.createElement("button", { style: primaryButtonStyle, onClick: () => setStage({ kind: "capture" }) }, "Try again"), /* @__PURE__ */ React.createElement("button", { style: cancelButtonStyle, onClick: () => close(null) }, "Cancel")));
1267
+ const renderDeclined = (result) => /* @__PURE__ */ React.createElement("div", { style: contentStyle }, /* @__PURE__ */ React.createElement("div", { style: alertBoxStyle }, /* @__PURE__ */ React.createElement("p", { style: alertTextStyle }, result.declined_reason ?? "We couldn't match your selfie. Please take a new one.")), /* @__PURE__ */ React.createElement("div", { style: buttonsContainerStyle }, /* @__PURE__ */ React.createElement("button", { style: primaryButtonStyle, onClick: handleRetakeAfterDecline }, /* @__PURE__ */ React.createElement("img", { src: Camera, alt: "", width: 20, height: 20 }), /* @__PURE__ */ React.createElement("span", null, "Retake")), /* @__PURE__ */ React.createElement("button", { style: cancelButtonStyle, onClick: () => close(result) }, "Cancel")), /* @__PURE__ */ React.createElement("p", { style: attemptsTextStyle }, "Attempt ", Math.min(attempts + 1, maxAttempts), " of ", maxAttempts));
1268
+ const renderMaxAttempts = (result) => /* @__PURE__ */ React.createElement("div", { style: { ...contentStyle, alignItems: "center", textAlign: "center" } }, /* @__PURE__ */ React.createElement("div", { style: errorCircleStyle }, "!"), /* @__PURE__ */ React.createElement("h3", { style: titleStyle }, "Maximum Attempts Reached"), /* @__PURE__ */ React.createElement("p", { style: subtitleStyle }, result.declined_reason ?? "We couldn't verify your identity after several attempts."), result.data?.freeze_account && /* @__PURE__ */ React.createElement("p", { style: alertTextStyle }, "For your security, your account has been temporarily restricted", result.data.freeze_duration_minutes ? ` for ${result.data.freeze_duration_minutes} minutes` : "", ". Please contact support if you need immediate help."), result.data?.enforce_logout && /* @__PURE__ */ React.createElement("p", { style: alertTextStyle }, "You will be signed out of your session."), result.data?.block && /* @__PURE__ */ React.createElement("p", { style: alertTextStyle }, "This action has been blocked for security reasons."), /* @__PURE__ */ React.createElement("div", { style: buttonsContainerStyle }, /* @__PURE__ */ React.createElement("button", { style: primaryButtonStyle, onClick: () => close(result) }, "Close")));
1269
+ const renderError = (message) => {
1270
+ const sessionExpired = /session\s+expired/i.test(message);
1271
+ return /* @__PURE__ */ React.createElement("div", { style: { ...contentStyle, alignItems: "center", textAlign: "center" } }, /* @__PURE__ */ React.createElement("div", { style: errorCircleStyle }, "!"), /* @__PURE__ */ React.createElement("h3", { style: titleStyle }, sessionExpired ? "Session expired" : "Something Went Wrong"), /* @__PURE__ */ React.createElement("p", { style: alertTextStyle }, sessionExpired ? "Your verification session is no longer valid. Please start a new verification from the beginning." : message), /* @__PURE__ */ React.createElement("div", { style: buttonsContainerStyle }, !sessionExpired && /* @__PURE__ */ React.createElement("button", { style: primaryButtonStyle, onClick: () => setStage({ kind: "capture" }) }, "Try Again"), /* @__PURE__ */ React.createElement("button", { style: cancelButtonStyle, onClick: () => close(null) }, sessionExpired ? "Close" : "Cancel")));
1272
+ };
1154
1273
  const body = (() => {
1155
1274
  switch (stage.kind) {
1156
1275
  case "choose":
@@ -1358,11 +1477,14 @@ var attemptsTextStyle = {
1358
1477
  };
1359
1478
  var footerStyle = { padding: "12px 24px 16px", borderTop: "1px solid #f3f4f6" };
1360
1479
  var footerTextStyle = { margin: 0, fontSize: 12, color: "#9ca3af", textAlign: "center" };
1361
- function LiveCamera({ onCapture, onCancel, message }) {
1480
+ function LiveCamera({ onCapture, onCancel, message, assetBasePath }) {
1362
1481
  const videoRef = useRef(null);
1363
1482
  const streamRef = useRef(null);
1483
+ const samplesRef = useRef([]);
1364
1484
  const [ready, setReady] = useState(false);
1365
1485
  const [error, setError] = useState(null);
1486
+ const [faceStatus, setFaceStatus] = useState("idle");
1487
+ const [faceDetectionFailed, setFaceDetectionFailed] = useState(false);
1366
1488
  useEffect(() => {
1367
1489
  let cancelled = false;
1368
1490
  async function init() {
@@ -1403,6 +1525,75 @@ function LiveCamera({ onCapture, onCancel, message }) {
1403
1525
  streamRef.current = null;
1404
1526
  };
1405
1527
  }, []);
1528
+ useEffect(() => {
1529
+ if (!ready) {
1530
+ setFaceStatus("idle");
1531
+ return;
1532
+ }
1533
+ let cancelled = false;
1534
+ let rafId = null;
1535
+ let detector = null;
1536
+ setFaceStatus("loading");
1537
+ setFaceDetectionFailed(false);
1538
+ samplesRef.current = [];
1539
+ (async () => {
1540
+ try {
1541
+ const FaceDetection = await loadMediaPipeFaceDetection(assetBasePath);
1542
+ if (cancelled) return;
1543
+ const assetBase = resolveMediaPipeBase(assetBasePath);
1544
+ const d = new FaceDetection({
1545
+ locateFile: (f) => `${assetBase}/${f}`
1546
+ });
1547
+ d.setOptions({ model: "short", minDetectionConfidence: 0.5 });
1548
+ d.onResults((results) => {
1549
+ if (cancelled) return;
1550
+ const detections = results.detections ?? [];
1551
+ const status = mapDetectionsToFaceStatus(detections);
1552
+ samplesRef.current.push({
1553
+ t: Date.now(),
1554
+ status,
1555
+ ...detections.length > 0 ? { boundingBox: detections[0].boundingBox } : {}
1556
+ });
1557
+ if (samplesRef.current.length > LIVENESS_SAMPLE_LIMIT) {
1558
+ samplesRef.current.shift();
1559
+ }
1560
+ setFaceStatus(status);
1561
+ });
1562
+ await d.initialize();
1563
+ if (cancelled) return;
1564
+ detector = d;
1565
+ setFaceStatus("scanning");
1566
+ const tick = async () => {
1567
+ if (cancelled) return;
1568
+ const v = videoRef.current;
1569
+ if (v?.readyState === 4 && detector) {
1570
+ try {
1571
+ await detector.send({ image: v });
1572
+ } catch {
1573
+ }
1574
+ }
1575
+ if (!cancelled) {
1576
+ rafId = requestAnimationFrame(tick);
1577
+ }
1578
+ };
1579
+ tick();
1580
+ } catch (err) {
1581
+ console.error("Face detection failed to initialize:", err);
1582
+ if (!cancelled) {
1583
+ setFaceDetectionFailed(true);
1584
+ setFaceStatus("scanning");
1585
+ }
1586
+ }
1587
+ })();
1588
+ return () => {
1589
+ cancelled = true;
1590
+ if (rafId !== null) cancelAnimationFrame(rafId);
1591
+ try {
1592
+ detector?.close?.();
1593
+ } catch {
1594
+ }
1595
+ };
1596
+ }, [ready, assetBasePath]);
1406
1597
  const capture = () => {
1407
1598
  const video = videoRef.current;
1408
1599
  if (!video || !video.videoWidth) return;
@@ -1419,12 +1610,22 @@ function LiveCamera({ onCapture, onCancel, message }) {
1419
1610
  ctx.drawImage(video, sx, sy, size, size, 0, 0, size, size);
1420
1611
  streamRef.current?.getTracks().forEach((t) => t.stop());
1421
1612
  streamRef.current = null;
1422
- onCapture(canvas.toDataURL("image/jpeg", 0.9));
1613
+ onCapture(canvas.toDataURL("image/jpeg", 0.9), buildLivenessArtifact(samplesRef.current));
1423
1614
  };
1424
1615
  if (error) {
1425
1616
  return /* @__PURE__ */ React.createElement("div", { style: { ...contentStyle, alignItems: "center", textAlign: "center", padding: 0 } }, /* @__PURE__ */ React.createElement("div", { style: errorCircleStyle }, "!"), /* @__PURE__ */ React.createElement("h3", { style: titleStyle }, "Camera unavailable"), /* @__PURE__ */ React.createElement("p", { style: alertTextStyle }, error), /* @__PURE__ */ React.createElement("div", { style: buttonsContainerStyle }, /* @__PURE__ */ React.createElement("button", { style: secondaryButtonStyle, onClick: onCancel }, "Back")));
1426
1617
  }
1427
- return /* @__PURE__ */ React.createElement("div", { style: { display: "flex", flexDirection: "column", gap: 12 } }, /* @__PURE__ */ React.createElement("div", { style: videoFrameStyle }, /* @__PURE__ */ React.createElement(
1618
+ const detectionActive = ready && !faceDetectionFailed;
1619
+ const liveMessage = detectionActive ? FACE_MESSAGES[faceStatus] : message;
1620
+ const liveFaceColor = detectionActive ? FACE_COLORS[faceStatus] : void 0;
1621
+ const captureDisabled = isCaptureDisabled(ready, detectionActive, faceStatus);
1622
+ const ringPulse = detectionActive && (faceStatus === "too_far" || faceStatus === "too_close") ? "pulse 1.5s ease-in-out infinite" : void 0;
1623
+ const dynamicCircleStyle = {
1624
+ ...faceCircleStyle,
1625
+ ...liveFaceColor ? { borderColor: liveFaceColor } : null,
1626
+ ...ringPulse ? { animation: ringPulse } : null
1627
+ };
1628
+ return /* @__PURE__ */ React.createElement("div", { style: videoFrameStyle }, /* @__PURE__ */ React.createElement(
1428
1629
  "video",
1429
1630
  {
1430
1631
  ref: videoRef,
@@ -1433,24 +1634,87 @@ function LiveCamera({ onCapture, onCancel, message }) {
1433
1634
  autoPlay: true,
1434
1635
  style: videoStyle
1435
1636
  }
1436
- ), /* @__PURE__ */ React.createElement("div", { style: faceOverlayContainerStyle, "aria-hidden": "true" }, /* @__PURE__ */ React.createElement("div", { style: faceRingWrapStyle }, /* @__PURE__ */ React.createElement("div", { style: faceDashedRingStyle }), /* @__PURE__ */ React.createElement("div", { style: faceCircleStyle }))), message && /* @__PURE__ */ React.createElement("div", { style: liveMessagePillContainerStyle }, /* @__PURE__ */ React.createElement("div", { style: liveMessagePillStyle }, /* @__PURE__ */ React.createElement("span", { style: liveMessagePillTextStyle }, message))), !ready && /* @__PURE__ */ React.createElement("div", { style: videoLoadingStyle }, "Starting camera\u2026")), /* @__PURE__ */ React.createElement("div", { style: buttonsContainerStyle }, /* @__PURE__ */ React.createElement(
1637
+ ), /* @__PURE__ */ React.createElement("div", { style: faceOverlayContainerStyle, "aria-hidden": "true" }, /* @__PURE__ */ React.createElement("div", { style: faceRingWrapStyle }, /* @__PURE__ */ React.createElement("div", { style: faceDashedRingStyle }), /* @__PURE__ */ React.createElement("div", { style: dynamicCircleStyle }))), liveMessage && /* @__PURE__ */ React.createElement("div", { style: liveMessagePillContainerStyle }, /* @__PURE__ */ React.createElement("div", { style: liveMessagePillStyle }, /* @__PURE__ */ React.createElement("span", { style: liveMessagePillTextStyle }, liveMessage))), /* @__PURE__ */ React.createElement(
1638
+ "button",
1639
+ {
1640
+ type: "button",
1641
+ onClick: onCancel,
1642
+ "aria-label": "Close camera",
1643
+ style: overlayCloseButtonStyle
1644
+ },
1645
+ /* @__PURE__ */ React.createElement("img", { src: Close, alt: "", width: 16, height: 16 })
1646
+ ), !ready && /* @__PURE__ */ React.createElement("div", { style: videoLoadingStyle }, "Starting camera\u2026"), /* @__PURE__ */ React.createElement("div", { style: bottomGradientStyle }, /* @__PURE__ */ React.createElement(
1437
1647
  "button",
1438
1648
  {
1439
- style: ready ? primaryButtonStyle : { ...primaryButtonStyle, opacity: 0.5, cursor: "not-allowed" },
1649
+ type: "button",
1440
1650
  onClick: capture,
1441
- disabled: !ready
1651
+ disabled: captureDisabled,
1652
+ "aria-label": "Capture",
1653
+ style: captureDisabled ? { ...shutterButtonStyle, opacity: 0.5, cursor: "not-allowed" } : shutterButtonStyle
1442
1654
  },
1443
- /* @__PURE__ */ React.createElement("img", { src: Camera, alt: "", width: 20, height: 20 }),
1444
- /* @__PURE__ */ React.createElement("span", null, "Capture")
1445
- ), /* @__PURE__ */ React.createElement("button", { style: secondaryButtonStyle, onClick: onCancel }, "Cancel")));
1655
+ /* @__PURE__ */ React.createElement("span", { style: shutterInnerStyle })
1656
+ )));
1446
1657
  }
1447
1658
  var videoFrameStyle = {
1448
1659
  position: "relative",
1449
1660
  width: "100%",
1450
- aspectRatio: "1 / 1",
1451
- borderRadius: 12,
1661
+ aspectRatio: "3 / 4",
1662
+ borderRadius: 24,
1452
1663
  overflow: "hidden",
1453
- background: "#0f172a"
1664
+ background: "#000000",
1665
+ border: "4px solid rgba(255, 255, 255, 0.08)",
1666
+ boxShadow: "0 25px 50px -12px rgba(0, 0, 0, 0.5)"
1667
+ };
1668
+ var overlayCloseButtonStyle = {
1669
+ position: "absolute",
1670
+ top: 12,
1671
+ right: 12,
1672
+ width: 32,
1673
+ height: 32,
1674
+ display: "inline-flex",
1675
+ alignItems: "center",
1676
+ justifyContent: "center",
1677
+ padding: 0,
1678
+ border: "none",
1679
+ borderRadius: "50%",
1680
+ background: "rgba(0, 0, 0, 0.4)",
1681
+ backdropFilter: "blur(6px)",
1682
+ color: "#ffffff",
1683
+ cursor: "pointer",
1684
+ zIndex: 10
1685
+ };
1686
+ var bottomGradientStyle = {
1687
+ position: "absolute",
1688
+ bottom: 0,
1689
+ left: 0,
1690
+ right: 0,
1691
+ padding: "16px",
1692
+ background: "linear-gradient(to top, rgba(0, 0, 0, 0.8) 0%, rgba(0, 0, 0, 0) 100%)",
1693
+ display: "flex",
1694
+ alignItems: "center",
1695
+ justifyContent: "center",
1696
+ zIndex: 10
1697
+ };
1698
+ var shutterButtonStyle = {
1699
+ width: 64,
1700
+ height: 64,
1701
+ borderRadius: "50%",
1702
+ background: "rgba(255, 255, 255, 0.15)",
1703
+ backdropFilter: "blur(6px)",
1704
+ border: "2px solid rgba(255, 255, 255, 0.7)",
1705
+ display: "inline-flex",
1706
+ alignItems: "center",
1707
+ justifyContent: "center",
1708
+ cursor: "pointer",
1709
+ padding: 0,
1710
+ transition: "transform 120ms ease-out, background 120ms"
1711
+ };
1712
+ var shutterInnerStyle = {
1713
+ display: "block",
1714
+ width: 44,
1715
+ height: 44,
1716
+ borderRadius: "50%",
1717
+ background: "rgba(255, 255, 255, 0.85)"
1454
1718
  };
1455
1719
  var videoStyle = {
1456
1720
  width: "100%",
@@ -1494,7 +1758,9 @@ var videoLoadingStyle = {
1494
1758
  justifyContent: "center",
1495
1759
  color: "#e5e7eb",
1496
1760
  fontSize: 14,
1497
- fontWeight: 500
1761
+ fontWeight: 500,
1762
+ background: "rgba(0, 0, 0, 0.4)",
1763
+ zIndex: 5
1498
1764
  };
1499
1765
  var liveMessagePillContainerStyle = {
1500
1766
  position: "absolute",
@@ -1859,9 +2125,9 @@ function getRiskColor(risk) {
1859
2125
  };
1860
2126
  return colorMap[risk] || "#6b7280";
1861
2127
  }
1862
- function useReuseKYCSubmission(client) {
2128
+ function useEventBasedFaceVerificationSubmission(client) {
1863
2129
  const startSession = useCallback(
1864
- (request) => client.createReuseKycSession(request),
2130
+ (request) => client.createEventBasedFaceVerificationSession(request),
1865
2131
  [client]
1866
2132
  );
1867
2133
  const verifyFace = useCallback(
@@ -1878,6 +2144,8 @@ function useReuseKYCSubmission(client) {
1878
2144
  session,
1879
2145
  defaultDevice: opts.defaultDevice,
1880
2146
  renderQR: opts.renderQR,
2147
+ onCancel: opts.onCancel,
2148
+ mediapipeAssetBasePath: opts.mediapipeAssetBasePath,
1881
2149
  onComplete: (result) => {
1882
2150
  cleanup();
1883
2151
  resolve(result);
@@ -1904,6 +2172,6 @@ function useReuseKYCSubmission(client) {
1904
2172
  return { startSession, verifyFace, runFlow };
1905
2173
  }
1906
2174
 
1907
- export { createDeviceFingerprint, fileToBase64, formatKycStatus, getBrowserInfo, getRiskColor, getStatusColor, isValidFileSize, isValidFileType, useCipherText, useCustomerProfile, useGeolocation, useKycAlerts, useKycOverview, useKycPreferences, useKycRequests, useKycSubmission, useLocationCapture, useLocationRequests, useLoginVerification, useRegistration, useReuseKYCSubmission, useTransactionVerification };
2175
+ export { createDeviceFingerprint, fileToBase64, formatKycStatus, getBrowserInfo, getRiskColor, getStatusColor, isValidFileSize, isValidFileType, useCipherText, useCustomerProfile, useEventBasedFaceVerificationSubmission, useGeolocation, useKycAlerts, useKycOverview, useKycPreferences, useKycRequests, useKycSubmission, useLocationCapture, useLocationRequests, useLoginVerification, useRegistration, useTransactionVerification };
1908
2176
  //# sourceMappingURL=react.mjs.map
1909
2177
  //# sourceMappingURL=react.mjs.map