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.js CHANGED
@@ -888,10 +888,91 @@ function useCustomerProfile(client, customerId, options = {}) {
888
888
  var Camera = "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCg0KPCFET0NUWVBFIHN2ZyBQVUJMSUMgIi0vL1czQy8vRFREIFNWRyAxLjAvL0VOIiAiaHR0cDovL3d3dy53My5vcmcvVFIvMjAwMS9SRUMtU1ZHLTIwMDEwOTA0L0RURC9zdmcxMC5kdGQiPg0KPCEtLSBVcGxvYWRlZCB0bzogU1ZHIFJlcG8sIHd3dy5zdmdyZXBvLmNvbSwgR2VuZXJhdG9yOiBTVkcgUmVwbyBNaXhlciBUb29scyAtLT4NCjxzdmcgdmVyc2lvbj0iMS4wIiBpZD0iTGF5ZXJfMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgDQoJIHdpZHRoPSI4MDBweCIgaGVpZ2h0PSI4MDBweCIgdmlld0JveD0iMCAwIDY0IDY0IiBlbmFibGUtYmFja2dyb3VuZD0ibmV3IDAgMCA2NCA2NCIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+DQo8Zz4NCgk8cGF0aCBmaWxsPSIjMjMxRjIwIiBkPSJNNjAsMTBINDkuNjU2bC02LjgyOC02LjgyOEM0Mi4wNzgsMi40MjIsNDEuMDYyLDIsNDAsMkgyNGMtMS4wNjIsMC0yLjA3OCwwLjQyMi0yLjgyOCwxLjE3MkwxNC4zNDQsMTBINA0KCQljLTIuMjExLDAtNCwxLjc4OS00LDR2NDRjMCwyLjIxMSwxLjc4OSw0LDQsNGg1NmMyLjIxMSwwLDQtMS43ODksNC00VjE0QzY0LDExLjc4OSw2Mi4yMTEsMTAsNjAsMTB6IE0zMiw1MA0KCQljLTguODM2LDAtMTYtNy4xNjQtMTYtMTZzNy4xNjQtMTYsMTYtMTZzMTYsNy4xNjQsMTYsMTZTNDAuODM2LDUwLDMyLDUweiIvPg0KCTxjaXJjbGUgZmlsbD0iIzIzMUYyMCIgY3g9IjMyIiBjeT0iMzQiIHI9IjgiLz4NCjwvZz4NCjwvc3ZnPg==";
889
889
  var Done = "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz48IS0tIFVwbG9hZGVkIHRvOiBTVkcgUmVwbywgd3d3LnN2Z3JlcG8uY29tLCBHZW5lcmF0b3I6IFNWRyBSZXBvIE1peGVyIFRvb2xzIC0tPg0KPHN2ZyB3aWR0aD0iODAwcHgiIGhlaWdodD0iODAwcHgiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4NCjxwYXRoIGQ9Ik04LjUgMTIuNUwxMC41IDE0LjVMMTUuNSA5LjUiIHN0cm9rZT0iIzFDMjc0QyIgc3Ryb2tlLXdpZHRoPSIxLjUiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPg0KPHBhdGggZD0iTTcgMy4zMzc4MkM4LjQ3MDg3IDIuNDg2OTcgMTAuMTc4NiAyIDEyIDJDMTcuNTIyOCAyIDIyIDYuNDc3MTUgMjIgMTJDMjIgMTcuNTIyOCAxNy41MjI4IDIyIDEyIDIyQzYuNDc3MTUgMjIgMiAxNy41MjI4IDIgMTJDMiAxMC4xNzg2IDIuNDg2OTcgOC40NzA4NyAzLjMzNzgyIDciIHN0cm9rZT0iIzFDMjc0QyIgc3Ryb2tlLXdpZHRoPSIxLjUiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIvPg0KPC9zdmc+";
890
890
  var Close = "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz48IS0tIFVwbG9hZGVkIHRvOiBTVkcgUmVwbywgd3d3LnN2Z3JlcG8uY29tLCBHZW5lcmF0b3I6IFNWRyBSZXBvIE1peGVyIFRvb2xzIC0tPg0KPHN2ZyB3aWR0aD0iODAwcHgiIGhlaWdodD0iODAwcHgiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4NCjxwYXRoIGQ9Ik0xNC41IDkuNTAwMDJMOS41IDE0LjVNOS40OTk5OCA5LjVMMTQuNSAxNC41IiBzdHJva2U9IiMxQzI3NEMiIHN0cm9rZS13aWR0aD0iMS41IiBzdHJva2UtbGluZWNhcD0icm91bmQiLz4NCjxwYXRoIGQ9Ik03IDMuMzM3ODJDOC40NzA4NyAyLjQ4Njk3IDEwLjE3ODYgMiAxMiAyQzE3LjUyMjggMiAyMiA2LjQ3NzE1IDIyIDEyQzIyIDE3LjUyMjggMTcuNTIyOCAyMiAxMiAyMkM2LjQ3NzE1IDIyIDIgMTcuNTIyOCAyIDEyQzIgMTAuMTc4NiAyLjQ4Njk3IDguNDcwODcgMy4zMzc4MiA3IiBzdHJva2U9IiMxQzI3NEMiIHN0cm9rZS13aWR0aD0iMS41IiBzdHJva2UtbGluZWNhcD0icm91bmQiLz4NCjwvc3ZnPg==";
891
- var Upload = "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz48IS0tIFVwbG9hZGVkIHRvOiBTVkcgUmVwbywgd3d3LnN2Z3JlcG8uY29tLCBHZW5lcmF0b3I6IFNWRyBSZXBvIE1peGVyIFRvb2xzIC0tPg0KPHN2ZyB3aWR0aD0iODAwcHgiIGhlaWdodD0iODAwcHgiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4NCjxwYXRoIGQ9Ik0xNyAxN0gxNy4wMU0xNS42IDE0SDE4QzE4LjkzMTkgMTQgMTkuMzk3OCAxNCAxOS43NjU0IDE0LjE1MjJDMjAuMjU1NCAxNC4zNTUyIDIwLjY0NDggMTQuNzQ0NiAyMC44NDc4IDE1LjIzNDZDMjEgMTUuNjAyMiAyMSAxNi4wNjgxIDIxIDE3QzIxIDE3LjkzMTkgMjEgMTguMzk3OCAyMC44NDc4IDE4Ljc2NTRDMjAuNjQ0OCAxOS4yNTU0IDIwLjI1NTQgMTkuNjQ0OCAxOS43NjU0IDE5Ljg0NzhDMTkuMzk3OCAyMCAxOC45MzE5IDIwIDE4IDIwSDZDNS4wNjgxMiAyMCA0LjYwMjE4IDIwIDQuMjM0NjMgMTkuODQ3OEMzLjc0NDU4IDE5LjY0NDggMy4zNTUyMyAxOS4yNTU0IDMuMTUyMjQgMTguNzY1NEMzIDE4LjM5NzggMyAxNy45MzE5IDMgMTdDMyAxNi4wNjgxIDMgMTUuNjAyMiAzLjE1MjI0IDE1LjIzNDZDMy4zNTUyMyAxNC43NDQ2IDMuNzQ0NTggMTQuMzU1MiA0LjIzNDYzIDE0LjE1MjJDNC42MDIxOCAxNCA1LjA2ODEyIDE0IDYgMTRIOC40TTEyIDE1VjRNMTIgNEwxNSA3TTEyIDRMOSA3IiBzdHJva2U9IiMwMDAwMDAiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+DQo8L3N2Zz4=";
891
+
892
+ // src/kyc/faceDetection.ts
893
+ var FACE_MESSAGES = {
894
+ idle: "Initializing camera...",
895
+ loading: "Loading face detection...",
896
+ scanning: "Position your face in the circle",
897
+ too_far: "Face too far. Please move closer to the camera",
898
+ too_close: "Too close. Please move back a little",
899
+ off_center: "Center your face in the circle",
900
+ ok: "Looking great! Tap the button to capture"
901
+ };
902
+ var FACE_COLORS = {
903
+ idle: "#9ca3af",
904
+ loading: "#3b82f6",
905
+ scanning: "#3b82f6",
906
+ too_far: "#ef4444",
907
+ too_close: "#f59e0b",
908
+ off_center: "#f59e0b",
909
+ ok: "#10b981"
910
+ };
911
+ var MEDIAPIPE_BASE = "https://cdn.jsdelivr.net/npm/@mediapipe/face_detection";
912
+ function mapDetectionsToFaceStatus(detections) {
913
+ if (detections.length === 0) return "scanning";
914
+ const bb = detections[0].boundingBox;
915
+ if (bb.width < 0.18) return "too_far";
916
+ if (bb.width > 0.72) return "too_close";
917
+ if (bb.xCenter < 0.28 || bb.xCenter > 0.72 || bb.yCenter < 0.28 || bb.yCenter > 0.72) {
918
+ return "off_center";
919
+ }
920
+ return "ok";
921
+ }
922
+ function isCaptureDisabled(ready, detectionActive, faceStatus) {
923
+ return !ready || detectionActive && faceStatus !== "ok";
924
+ }
925
+ function resolveMediaPipeBase(base) {
926
+ if (!base) return MEDIAPIPE_BASE;
927
+ return base.endsWith("/") ? base.slice(0, -1) : base;
928
+ }
929
+ var LIVENESS_SAMPLE_LIMIT = 20;
930
+ function buildLivenessArtifact(samples) {
931
+ if (samples.length === 0) return void 0;
932
+ return JSON.stringify({
933
+ version: 1,
934
+ source: "mediapipe-face-detection",
935
+ samples: samples.slice(-LIVENESS_SAMPLE_LIMIT)
936
+ });
937
+ }
938
+ var mediapipeLoaderPromises = /* @__PURE__ */ new Map();
939
+ function loadMediaPipeFaceDetection(assetBasePath) {
940
+ if (typeof window === "undefined" || typeof document === "undefined") {
941
+ return Promise.reject(new Error("MediaPipe requires a browser environment"));
942
+ }
943
+ const w = window;
944
+ if (w.FaceDetection) {
945
+ return Promise.resolve(w.FaceDetection);
946
+ }
947
+ const src = `${resolveMediaPipeBase(assetBasePath)}/face_detection.js`;
948
+ const existing = mediapipeLoaderPromises.get(src);
949
+ if (existing) return existing;
950
+ const loader = new Promise((resolve, reject) => {
951
+ const script = document.createElement("script");
952
+ script.src = src;
953
+ script.async = true;
954
+ script.crossOrigin = "anonymous";
955
+ script.onload = () => {
956
+ if (w.FaceDetection) {
957
+ resolve(w.FaceDetection);
958
+ } else {
959
+ mediapipeLoaderPromises.delete(src);
960
+ reject(new Error("MediaPipe loaded but FaceDetection global is missing"));
961
+ }
962
+ };
963
+ script.onerror = () => {
964
+ mediapipeLoaderPromises.delete(src);
965
+ reject(new Error("Failed to load MediaPipe face_detection script"));
966
+ };
967
+ document.head.appendChild(script);
968
+ });
969
+ mediapipeLoaderPromises.set(src, loader);
970
+ return loader;
971
+ }
892
972
 
893
973
  // src/kyc/FaceCaptureModal.tsx
894
974
  var MOBILE_UA = /Mobi|Android|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i;
975
+ var SESSION_EXPIRY_MS = 15 * 60 * 1e3;
895
976
  var LIVENESS_MESSAGES = [
896
977
  "Position your face inside the circle",
897
978
  "Make sure your face is well lit",
@@ -905,9 +986,9 @@ function headerSubtitle(stageKind) {
905
986
  case "accepted":
906
987
  return "All set";
907
988
  case "declined":
908
- return "Verification didn't match, try once more";
989
+ return "Verification Declined";
909
990
  case "max_attempts":
910
- return "We couldn't verify you";
991
+ return "We couldn't verify your identity";
911
992
  default:
912
993
  return "Please capture a clear photo of your face";
913
994
  }
@@ -920,16 +1001,19 @@ function FaceCaptureModal({
920
1001
  client,
921
1002
  session,
922
1003
  onComplete,
1004
+ onCancel,
923
1005
  defaultDevice,
924
- renderQR
1006
+ renderQR,
1007
+ mediapipeAssetBasePath
925
1008
  }) {
926
1009
  const isMobile = typeof navigator !== "undefined" && MOBILE_UA.test(navigator.userAgent);
1010
+ const initialChoice = defaultDevice ?? (isMobile ? "this" : "ask");
927
1011
  const initialStage = (() => {
928
- const choice = defaultDevice ?? (isMobile ? "this" : "ask");
929
- if (choice === "this") return { kind: "capture" };
930
- if (choice === "mobile") return { kind: "qr", mobileConnected: false };
1012
+ if (initialChoice === "this") return { kind: "capture" };
1013
+ if (initialChoice === "mobile") return { kind: "qr", mobileConnected: false };
931
1014
  return { kind: "choose" };
932
1015
  })();
1016
+ const hasMethodChoice = initialChoice === "ask";
933
1017
  const [stage, setStage] = React.useState(initialStage);
934
1018
  const [attempts, setAttempts] = React.useState(session.attempts);
935
1019
  const maxAttempts = session.max_attempts || 1;
@@ -937,21 +1021,30 @@ function FaceCaptureModal({
937
1021
  const [captureMode, setCaptureMode] = React.useState("idle");
938
1022
  const [capturedPreview, setCapturedPreview] = React.useState(null);
939
1023
  const [capturedBase64, setCapturedBase64] = React.useState(null);
1024
+ const [capturedArtifact, setCapturedArtifact] = React.useState(null);
940
1025
  const [livenessMessage, setLivenessMessage] = React.useState(LIVENESS_MESSAGES[0]);
941
1026
  const qrPayload = session.link || `vesant://reuse-kyc?token=${encodeURIComponent(session.token)}`;
942
1027
  const cancelledRef = React.useRef(false);
943
1028
  const submittingRef = React.useRef(false);
1029
+ React.useEffect(() => {
1030
+ const timer = setTimeout(() => {
1031
+ setStage(
1032
+ (prev) => prev.kind === "choose" || prev.kind === "qr" || prev.kind === "capture" ? { kind: "error", message: "Session expired" } : prev
1033
+ );
1034
+ }, SESSION_EXPIRY_MS);
1035
+ return () => clearTimeout(timer);
1036
+ }, [session.token]);
944
1037
  React.useEffect(() => {
945
1038
  if (stage.kind !== "qr") return;
946
1039
  let stopped = false;
947
1040
  const intervalMs = 2e3;
948
- const deadline = Date.now() + 15 * 60 * 1e3;
1041
+ const deadline = Date.now() + SESSION_EXPIRY_MS;
949
1042
  let mobileSeen = stage.mobileConnected;
950
1043
  const tick = async () => {
951
1044
  if (stopped || cancelledRef.current) return;
952
1045
  try {
953
1046
  if (mobileSeen) {
954
- const result = await client.getReuseKycSessionStatus(session.reference);
1047
+ const result = await client.getEventBasedFaceVerificationSessionStatus(session.token);
955
1048
  if (result.status === "accepted") {
956
1049
  setStage({ kind: "accepted", result });
957
1050
  stopped = true;
@@ -1023,10 +1116,13 @@ function FaceCaptureModal({
1023
1116
  try {
1024
1117
  const postPromise = (async () => {
1025
1118
  try {
1026
- const data = await client.submitReuseKycSession({
1119
+ const data = await client.submitEventBasedFaceVerificationSession({
1027
1120
  token: session.token,
1028
1121
  reference: ref,
1029
- proof
1122
+ proof,
1123
+ // undefined when detection wasn't running for this capture —
1124
+ // JSON.stringify drops it, so the field is absent on the wire.
1125
+ liveness_artifact: capturedArtifact ?? void 0
1030
1126
  });
1031
1127
  return { ok: true, data };
1032
1128
  } catch (err2) {
@@ -1039,7 +1135,7 @@ function FaceCaptureModal({
1039
1135
  while (Date.now() < deadline) {
1040
1136
  if (settled || cancelledRef.current) return null;
1041
1137
  try {
1042
- const r = await client.getReuseKycSessionStatus(ref);
1138
+ const r = await client.getEventBasedFaceVerificationSessionStatus(session.token);
1043
1139
  if (isFreshPollResult(r)) return r;
1044
1140
  } catch {
1045
1141
  }
@@ -1072,16 +1168,18 @@ function FaceCaptureModal({
1072
1168
  submittingRef.current = false;
1073
1169
  }
1074
1170
  };
1075
- const handleCameraCapture = (dataUrl) => {
1171
+ const handleCameraCapture = (dataUrl, livenessArtifact) => {
1076
1172
  const raw = dataUrl.split(",")[1] ?? "";
1077
1173
  setCapturedPreview(dataUrl);
1078
1174
  setCapturedBase64(raw);
1175
+ setCapturedArtifact(livenessArtifact ?? null);
1079
1176
  setCaptureMode("preview");
1080
1177
  };
1081
1178
  const handleCameraCancel = () => setCaptureMode("idle");
1082
1179
  const handleRetake = () => {
1083
1180
  setCapturedPreview(null);
1084
1181
  setCapturedBase64(null);
1182
+ setCapturedArtifact(null);
1085
1183
  setCaptureMode("live");
1086
1184
  };
1087
1185
  const handleConfirmSubmit = () => {
@@ -1110,6 +1208,9 @@ function FaceCaptureModal({
1110
1208
  }, [captureMode]);
1111
1209
  const close = (result) => {
1112
1210
  cancelledRef.current = true;
1211
+ if (result === null && onCancel) {
1212
+ onCancel();
1213
+ }
1113
1214
  onComplete(result);
1114
1215
  };
1115
1216
  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(
@@ -1118,7 +1219,7 @@ function FaceCaptureModal({
1118
1219
  style: primaryButtonStyle,
1119
1220
  onClick: () => setStage({ kind: "capture" })
1120
1221
  },
1121
- /* @__PURE__ */ React__default.default.createElement("img", { src: isMobile ? Camera : Upload, alt: "", width: 20, height: 20 }),
1222
+ /* @__PURE__ */ React__default.default.createElement("img", { src: Camera, alt: "", width: 20, height: 20 }),
1122
1223
  /* @__PURE__ */ React__default.default.createElement("span", null, "Continue on this device")
1123
1224
  ), /* @__PURE__ */ React__default.default.createElement(
1124
1225
  "button",
@@ -1135,6 +1236,13 @@ function FaceCaptureModal({
1135
1236
  onClick: () => setStage({ kind: "capture" })
1136
1237
  },
1137
1238
  "Use this device instead"
1239
+ ), hasMethodChoice && /* @__PURE__ */ React__default.default.createElement(
1240
+ "button",
1241
+ {
1242
+ style: secondaryButtonStyle,
1243
+ onClick: () => setStage({ kind: "choose" })
1244
+ },
1245
+ "Change verification method"
1138
1246
  ), /* @__PURE__ */ React__default.default.createElement("button", { style: cancelButtonStyle, onClick: () => close(null) }, "Cancel")));
1139
1247
  const renderCapture = (declinedReason) => {
1140
1248
  if (captureMode === "live") {
@@ -1143,20 +1251,31 @@ function FaceCaptureModal({
1143
1251
  {
1144
1252
  onCapture: handleCameraCapture,
1145
1253
  onCancel: handleCameraCancel,
1146
- message: livenessMessage
1254
+ message: livenessMessage,
1255
+ assetBasePath: mediapipeAssetBasePath
1147
1256
  }
1148
1257
  ), /* @__PURE__ */ React__default.default.createElement("p", { style: attemptsTextStyle }, "Attempt ", attempts + 1, " of ", maxAttempts));
1149
1258
  }
1150
1259
  if (captureMode === "preview" && capturedPreview) {
1151
1260
  return /* @__PURE__ */ React__default.default.createElement("div", { style: contentStyle }, declinedReason && /* @__PURE__ */ React__default.default.createElement("div", { style: alertBoxStyle }, /* @__PURE__ */ React__default.default.createElement("strong", null, "Verification declined."), /* @__PURE__ */ React__default.default.createElement("p", { style: alertTextStyle }, declinedReason)), /* @__PURE__ */ React__default.default.createElement("p", { style: subtitleStyle }, "Looks good? Submit this selfie or retake it."), /* @__PURE__ */ React__default.default.createElement("div", { style: previewBoxStyle }, /* @__PURE__ */ React__default.default.createElement("img", { src: capturedPreview, alt: "Captured selfie preview", style: previewImgStyle })), /* @__PURE__ */ React__default.default.createElement("div", { style: buttonsContainerStyle }, /* @__PURE__ */ React__default.default.createElement("button", { style: primaryButtonStyle, onClick: handleConfirmSubmit }, /* @__PURE__ */ React__default.default.createElement("img", { src: Done, alt: "", width: 20, height: 20 }), /* @__PURE__ */ React__default.default.createElement("span", null, "Submit")), /* @__PURE__ */ React__default.default.createElement("button", { style: secondaryButtonStyle, onClick: handleRetake }, "Retake"), /* @__PURE__ */ React__default.default.createElement("button", { style: cancelButtonStyle, onClick: () => close(null) }, "Cancel")), /* @__PURE__ */ React__default.default.createElement("p", { style: attemptsTextStyle }, "Attempt ", attempts + 1, " of ", maxAttempts));
1152
1261
  }
1153
- return /* @__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("div", { style: badgeStyle }, /* @__PURE__ */ React__default.default.createElement("img", { src: Done, alt: "", width: 16, height: 16 })))), declinedReason && /* @__PURE__ */ React__default.default.createElement("div", { style: alertBoxStyle }, /* @__PURE__ */ React__default.default.createElement("strong", null, "Verification declined."), /* @__PURE__ */ React__default.default.createElement("p", { style: alertTextStyle }, declinedReason)), /* @__PURE__ */ React__default.default.createElement("div", { style: instructionsBoxStyle }, /* @__PURE__ */ React__default.default.createElement("h3", { style: instructionsTitleStyle }, "Tips for best results:"), /* @__PURE__ */ React__default.default.createElement("ul", { style: instructionsListStyle }, /* @__PURE__ */ React__default.default.createElement("li", { style: instructionItemStyle }, /* @__PURE__ */ React__default.default.createElement("span", { style: bulletStyle }, "\u2022"), /* @__PURE__ */ React__default.default.createElement("span", null, "Ensure good lighting on your face")), /* @__PURE__ */ React__default.default.createElement("li", { style: instructionItemStyle }, /* @__PURE__ */ React__default.default.createElement("span", { style: bulletStyle }, "\u2022"), /* @__PURE__ */ React__default.default.createElement("span", null, "Remove glasses or accessories if possible")), /* @__PURE__ */ React__default.default.createElement("li", { style: instructionItemStyle }, /* @__PURE__ */ React__default.default.createElement("span", { style: bulletStyle }, "\u2022"), /* @__PURE__ */ React__default.default.createElement("span", null, "Look directly at the camera")))), /* @__PURE__ */ React__default.default.createElement("div", { style: buttonsContainerStyle }, /* @__PURE__ */ React__default.default.createElement("button", { style: primaryButtonStyle, onClick: handleOpenCamera }, /* @__PURE__ */ React__default.default.createElement("img", { src: Camera, alt: "", width: 20, height: 20 }), /* @__PURE__ */ React__default.default.createElement("span", null, declinedReason ? "Try again" : "Open Camera")), /* @__PURE__ */ React__default.default.createElement("button", { style: cancelButtonStyle, onClick: () => close(null) }, "Cancel")), /* @__PURE__ */ React__default.default.createElement("p", { style: attemptsTextStyle }, "Attempt ", attempts + 1, " of ", maxAttempts));
1262
+ return /* @__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("div", { style: badgeStyle }, /* @__PURE__ */ React__default.default.createElement("img", { src: Done, alt: "", width: 16, height: 16 })))), declinedReason && /* @__PURE__ */ React__default.default.createElement("div", { style: alertBoxStyle }, /* @__PURE__ */ React__default.default.createElement("strong", null, "Verification declined."), /* @__PURE__ */ React__default.default.createElement("p", { style: alertTextStyle }, declinedReason)), /* @__PURE__ */ React__default.default.createElement("div", { style: instructionsBoxStyle }, /* @__PURE__ */ React__default.default.createElement("h3", { style: instructionsTitleStyle }, "Tips for best results:"), /* @__PURE__ */ React__default.default.createElement("ul", { style: instructionsListStyle }, /* @__PURE__ */ React__default.default.createElement("li", { style: instructionItemStyle }, /* @__PURE__ */ React__default.default.createElement("span", { style: bulletStyle }, "\u2022"), /* @__PURE__ */ React__default.default.createElement("span", null, "Ensure good lighting on your face")), /* @__PURE__ */ React__default.default.createElement("li", { style: instructionItemStyle }, /* @__PURE__ */ React__default.default.createElement("span", { style: bulletStyle }, "\u2022"), /* @__PURE__ */ React__default.default.createElement("span", null, "Remove glasses or accessories if possible")), /* @__PURE__ */ React__default.default.createElement("li", { style: instructionItemStyle }, /* @__PURE__ */ React__default.default.createElement("span", { style: bulletStyle }, "\u2022"), /* @__PURE__ */ React__default.default.createElement("span", null, "Look directly at the camera")))), /* @__PURE__ */ React__default.default.createElement("div", { style: buttonsContainerStyle }, /* @__PURE__ */ React__default.default.createElement("button", { style: primaryButtonStyle, onClick: handleOpenCamera }, /* @__PURE__ */ React__default.default.createElement("img", { src: Camera, alt: "", width: 20, height: 20 }), /* @__PURE__ */ React__default.default.createElement("span", null, declinedReason ? "Try Again" : "Open Camera")), hasMethodChoice && /* @__PURE__ */ React__default.default.createElement(
1263
+ "button",
1264
+ {
1265
+ style: secondaryButtonStyle,
1266
+ onClick: () => setStage({ kind: "choose" })
1267
+ },
1268
+ "Change verification method"
1269
+ ), /* @__PURE__ */ React__default.default.createElement("button", { style: cancelButtonStyle, onClick: () => close(null) }, "Cancel")), /* @__PURE__ */ React__default.default.createElement("p", { style: attemptsTextStyle }, "Attempt ", attempts + 1, " of ", maxAttempts));
1154
1270
  };
1155
1271
  const renderSubmitting = () => /* @__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("p", { style: subtitleStyle }, "Verifying your photo\u2026"), /* @__PURE__ */ React__default.default.createElement("p", { style: { ...attemptsTextStyle, marginTop: 8 } }, "This usually takes a few seconds."));
1156
1272
  const renderAccepted = (result) => /* @__PURE__ */ React__default.default.createElement("div", { style: { ...contentStyle, alignItems: "center", textAlign: "center" } }, /* @__PURE__ */ React__default.default.createElement("div", { style: successCircleStyle }, /* @__PURE__ */ React__default.default.createElement("img", { src: Done, alt: "", width: 48, height: 48 })), /* @__PURE__ */ React__default.default.createElement("h3", { style: titleStyle }, "Verified"), /* @__PURE__ */ React__default.default.createElement("p", { style: subtitleStyle }, "Your identity has been confirmed. You can continue."), /* @__PURE__ */ React__default.default.createElement("div", { style: buttonsContainerStyle }, /* @__PURE__ */ React__default.default.createElement("button", { style: primaryButtonStyle, onClick: () => close(result) }, "Continue")));
1157
- const renderDeclined = (result) => /* @__PURE__ */ React__default.default.createElement("div", { style: contentStyle }, /* @__PURE__ */ React__default.default.createElement("div", { style: alertBoxStyle }, /* @__PURE__ */ React__default.default.createElement("strong", null, "Verification declined."), /* @__PURE__ */ React__default.default.createElement("p", { style: alertTextStyle }, result.declined_reason ?? "We couldn't match your selfie. Please take a new one.")), /* @__PURE__ */ React__default.default.createElement("div", { style: buttonsContainerStyle }, /* @__PURE__ */ React__default.default.createElement("button", { style: primaryButtonStyle, onClick: handleRetakeAfterDecline }, /* @__PURE__ */ React__default.default.createElement("img", { src: Camera, alt: "", width: 20, height: 20 }), /* @__PURE__ */ React__default.default.createElement("span", null, "Retake")), /* @__PURE__ */ React__default.default.createElement("button", { style: cancelButtonStyle, onClick: () => close(result) }, "Cancel")), /* @__PURE__ */ React__default.default.createElement("p", { style: attemptsTextStyle }, "Attempt ", Math.min(attempts + 1, maxAttempts), " of ", maxAttempts));
1158
- const renderMaxAttempts = (result) => /* @__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 }, "Maximum attempts reached"), /* @__PURE__ */ React__default.default.createElement("p", { style: subtitleStyle }, result.declined_reason ?? "We couldn't verify your identity after several attempts."), result.data?.freeze_account && /* @__PURE__ */ React__default.default.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__default.default.createElement("p", { style: alertTextStyle }, "You will be signed out of your session."), /* @__PURE__ */ React__default.default.createElement("div", { style: buttonsContainerStyle }, /* @__PURE__ */ React__default.default.createElement("button", { style: primaryButtonStyle, onClick: () => close(result) }, "Close")));
1159
- const renderError = (message) => /* @__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 }, "Something went wrong"), /* @__PURE__ */ React__default.default.createElement("p", { style: alertTextStyle }, message), /* @__PURE__ */ React__default.default.createElement("div", { style: buttonsContainerStyle }, /* @__PURE__ */ React__default.default.createElement("button", { style: primaryButtonStyle, onClick: () => setStage({ kind: "capture" }) }, "Try again"), /* @__PURE__ */ React__default.default.createElement("button", { style: cancelButtonStyle, onClick: () => close(null) }, "Cancel")));
1273
+ const renderDeclined = (result) => /* @__PURE__ */ React__default.default.createElement("div", { style: contentStyle }, /* @__PURE__ */ React__default.default.createElement("div", { style: alertBoxStyle }, /* @__PURE__ */ React__default.default.createElement("p", { style: alertTextStyle }, result.declined_reason ?? "We couldn't match your selfie. Please take a new one.")), /* @__PURE__ */ React__default.default.createElement("div", { style: buttonsContainerStyle }, /* @__PURE__ */ React__default.default.createElement("button", { style: primaryButtonStyle, onClick: handleRetakeAfterDecline }, /* @__PURE__ */ React__default.default.createElement("img", { src: Camera, alt: "", width: 20, height: 20 }), /* @__PURE__ */ React__default.default.createElement("span", null, "Retake")), /* @__PURE__ */ React__default.default.createElement("button", { style: cancelButtonStyle, onClick: () => close(result) }, "Cancel")), /* @__PURE__ */ React__default.default.createElement("p", { style: attemptsTextStyle }, "Attempt ", Math.min(attempts + 1, maxAttempts), " of ", maxAttempts));
1274
+ const renderMaxAttempts = (result) => /* @__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 }, "Maximum Attempts Reached"), /* @__PURE__ */ React__default.default.createElement("p", { style: subtitleStyle }, result.declined_reason ?? "We couldn't verify your identity after several attempts."), result.data?.freeze_account && /* @__PURE__ */ React__default.default.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__default.default.createElement("p", { style: alertTextStyle }, "You will be signed out of your session."), result.data?.block && /* @__PURE__ */ React__default.default.createElement("p", { style: alertTextStyle }, "This action has been blocked for security reasons."), /* @__PURE__ */ React__default.default.createElement("div", { style: buttonsContainerStyle }, /* @__PURE__ */ React__default.default.createElement("button", { style: primaryButtonStyle, onClick: () => close(result) }, "Close")));
1275
+ const renderError = (message) => {
1276
+ const sessionExpired = /session\s+expired/i.test(message);
1277
+ return /* @__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 }, sessionExpired ? "Session expired" : "Something Went Wrong"), /* @__PURE__ */ React__default.default.createElement("p", { style: alertTextStyle }, sessionExpired ? "Your verification session is no longer valid. Please start a new verification from the beginning." : message), /* @__PURE__ */ React__default.default.createElement("div", { style: buttonsContainerStyle }, !sessionExpired && /* @__PURE__ */ React__default.default.createElement("button", { style: primaryButtonStyle, onClick: () => setStage({ kind: "capture" }) }, "Try Again"), /* @__PURE__ */ React__default.default.createElement("button", { style: cancelButtonStyle, onClick: () => close(null) }, sessionExpired ? "Close" : "Cancel")));
1278
+ };
1160
1279
  const body = (() => {
1161
1280
  switch (stage.kind) {
1162
1281
  case "choose":
@@ -1364,11 +1483,14 @@ var attemptsTextStyle = {
1364
1483
  };
1365
1484
  var footerStyle = { padding: "12px 24px 16px", borderTop: "1px solid #f3f4f6" };
1366
1485
  var footerTextStyle = { margin: 0, fontSize: 12, color: "#9ca3af", textAlign: "center" };
1367
- function LiveCamera({ onCapture, onCancel, message }) {
1486
+ function LiveCamera({ onCapture, onCancel, message, assetBasePath }) {
1368
1487
  const videoRef = React.useRef(null);
1369
1488
  const streamRef = React.useRef(null);
1489
+ const samplesRef = React.useRef([]);
1370
1490
  const [ready, setReady] = React.useState(false);
1371
1491
  const [error, setError] = React.useState(null);
1492
+ const [faceStatus, setFaceStatus] = React.useState("idle");
1493
+ const [faceDetectionFailed, setFaceDetectionFailed] = React.useState(false);
1372
1494
  React.useEffect(() => {
1373
1495
  let cancelled = false;
1374
1496
  async function init() {
@@ -1409,6 +1531,75 @@ function LiveCamera({ onCapture, onCancel, message }) {
1409
1531
  streamRef.current = null;
1410
1532
  };
1411
1533
  }, []);
1534
+ React.useEffect(() => {
1535
+ if (!ready) {
1536
+ setFaceStatus("idle");
1537
+ return;
1538
+ }
1539
+ let cancelled = false;
1540
+ let rafId = null;
1541
+ let detector = null;
1542
+ setFaceStatus("loading");
1543
+ setFaceDetectionFailed(false);
1544
+ samplesRef.current = [];
1545
+ (async () => {
1546
+ try {
1547
+ const FaceDetection = await loadMediaPipeFaceDetection(assetBasePath);
1548
+ if (cancelled) return;
1549
+ const assetBase = resolveMediaPipeBase(assetBasePath);
1550
+ const d = new FaceDetection({
1551
+ locateFile: (f) => `${assetBase}/${f}`
1552
+ });
1553
+ d.setOptions({ model: "short", minDetectionConfidence: 0.5 });
1554
+ d.onResults((results) => {
1555
+ if (cancelled) return;
1556
+ const detections = results.detections ?? [];
1557
+ const status = mapDetectionsToFaceStatus(detections);
1558
+ samplesRef.current.push({
1559
+ t: Date.now(),
1560
+ status,
1561
+ ...detections.length > 0 ? { boundingBox: detections[0].boundingBox } : {}
1562
+ });
1563
+ if (samplesRef.current.length > LIVENESS_SAMPLE_LIMIT) {
1564
+ samplesRef.current.shift();
1565
+ }
1566
+ setFaceStatus(status);
1567
+ });
1568
+ await d.initialize();
1569
+ if (cancelled) return;
1570
+ detector = d;
1571
+ setFaceStatus("scanning");
1572
+ const tick = async () => {
1573
+ if (cancelled) return;
1574
+ const v = videoRef.current;
1575
+ if (v?.readyState === 4 && detector) {
1576
+ try {
1577
+ await detector.send({ image: v });
1578
+ } catch {
1579
+ }
1580
+ }
1581
+ if (!cancelled) {
1582
+ rafId = requestAnimationFrame(tick);
1583
+ }
1584
+ };
1585
+ tick();
1586
+ } catch (err) {
1587
+ console.error("Face detection failed to initialize:", err);
1588
+ if (!cancelled) {
1589
+ setFaceDetectionFailed(true);
1590
+ setFaceStatus("scanning");
1591
+ }
1592
+ }
1593
+ })();
1594
+ return () => {
1595
+ cancelled = true;
1596
+ if (rafId !== null) cancelAnimationFrame(rafId);
1597
+ try {
1598
+ detector?.close?.();
1599
+ } catch {
1600
+ }
1601
+ };
1602
+ }, [ready, assetBasePath]);
1412
1603
  const capture = () => {
1413
1604
  const video = videoRef.current;
1414
1605
  if (!video || !video.videoWidth) return;
@@ -1425,12 +1616,22 @@ function LiveCamera({ onCapture, onCancel, message }) {
1425
1616
  ctx.drawImage(video, sx, sy, size, size, 0, 0, size, size);
1426
1617
  streamRef.current?.getTracks().forEach((t) => t.stop());
1427
1618
  streamRef.current = null;
1428
- onCapture(canvas.toDataURL("image/jpeg", 0.9));
1619
+ onCapture(canvas.toDataURL("image/jpeg", 0.9), buildLivenessArtifact(samplesRef.current));
1429
1620
  };
1430
1621
  if (error) {
1431
1622
  return /* @__PURE__ */ React__default.default.createElement("div", { style: { ...contentStyle, alignItems: "center", textAlign: "center", padding: 0 } }, /* @__PURE__ */ React__default.default.createElement("div", { style: errorCircleStyle }, "!"), /* @__PURE__ */ React__default.default.createElement("h3", { style: titleStyle }, "Camera unavailable"), /* @__PURE__ */ React__default.default.createElement("p", { style: alertTextStyle }, error), /* @__PURE__ */ React__default.default.createElement("div", { style: buttonsContainerStyle }, /* @__PURE__ */ React__default.default.createElement("button", { style: secondaryButtonStyle, onClick: onCancel }, "Back")));
1432
1623
  }
1433
- return /* @__PURE__ */ React__default.default.createElement("div", { style: { display: "flex", flexDirection: "column", gap: 12 } }, /* @__PURE__ */ React__default.default.createElement("div", { style: videoFrameStyle }, /* @__PURE__ */ React__default.default.createElement(
1624
+ const detectionActive = ready && !faceDetectionFailed;
1625
+ const liveMessage = detectionActive ? FACE_MESSAGES[faceStatus] : message;
1626
+ const liveFaceColor = detectionActive ? FACE_COLORS[faceStatus] : void 0;
1627
+ const captureDisabled = isCaptureDisabled(ready, detectionActive, faceStatus);
1628
+ const ringPulse = detectionActive && (faceStatus === "too_far" || faceStatus === "too_close") ? "pulse 1.5s ease-in-out infinite" : void 0;
1629
+ const dynamicCircleStyle = {
1630
+ ...faceCircleStyle,
1631
+ ...liveFaceColor ? { borderColor: liveFaceColor } : null,
1632
+ ...ringPulse ? { animation: ringPulse } : null
1633
+ };
1634
+ return /* @__PURE__ */ React__default.default.createElement("div", { style: videoFrameStyle }, /* @__PURE__ */ React__default.default.createElement(
1434
1635
  "video",
1435
1636
  {
1436
1637
  ref: videoRef,
@@ -1439,24 +1640,87 @@ function LiveCamera({ onCapture, onCancel, message }) {
1439
1640
  autoPlay: true,
1440
1641
  style: videoStyle
1441
1642
  }
1442
- ), /* @__PURE__ */ React__default.default.createElement("div", { style: faceOverlayContainerStyle, "aria-hidden": "true" }, /* @__PURE__ */ React__default.default.createElement("div", { style: faceRingWrapStyle }, /* @__PURE__ */ React__default.default.createElement("div", { style: faceDashedRingStyle }), /* @__PURE__ */ React__default.default.createElement("div", { style: faceCircleStyle }))), message && /* @__PURE__ */ React__default.default.createElement("div", { style: liveMessagePillContainerStyle }, /* @__PURE__ */ React__default.default.createElement("div", { style: liveMessagePillStyle }, /* @__PURE__ */ React__default.default.createElement("span", { style: liveMessagePillTextStyle }, message))), !ready && /* @__PURE__ */ React__default.default.createElement("div", { style: videoLoadingStyle }, "Starting camera\u2026")), /* @__PURE__ */ React__default.default.createElement("div", { style: buttonsContainerStyle }, /* @__PURE__ */ React__default.default.createElement(
1643
+ ), /* @__PURE__ */ React__default.default.createElement("div", { style: faceOverlayContainerStyle, "aria-hidden": "true" }, /* @__PURE__ */ React__default.default.createElement("div", { style: faceRingWrapStyle }, /* @__PURE__ */ React__default.default.createElement("div", { style: faceDashedRingStyle }), /* @__PURE__ */ React__default.default.createElement("div", { style: dynamicCircleStyle }))), liveMessage && /* @__PURE__ */ React__default.default.createElement("div", { style: liveMessagePillContainerStyle }, /* @__PURE__ */ React__default.default.createElement("div", { style: liveMessagePillStyle }, /* @__PURE__ */ React__default.default.createElement("span", { style: liveMessagePillTextStyle }, liveMessage))), /* @__PURE__ */ React__default.default.createElement(
1644
+ "button",
1645
+ {
1646
+ type: "button",
1647
+ onClick: onCancel,
1648
+ "aria-label": "Close camera",
1649
+ style: overlayCloseButtonStyle
1650
+ },
1651
+ /* @__PURE__ */ React__default.default.createElement("img", { src: Close, alt: "", width: 16, height: 16 })
1652
+ ), !ready && /* @__PURE__ */ React__default.default.createElement("div", { style: videoLoadingStyle }, "Starting camera\u2026"), /* @__PURE__ */ React__default.default.createElement("div", { style: bottomGradientStyle }, /* @__PURE__ */ React__default.default.createElement(
1443
1653
  "button",
1444
1654
  {
1445
- style: ready ? primaryButtonStyle : { ...primaryButtonStyle, opacity: 0.5, cursor: "not-allowed" },
1655
+ type: "button",
1446
1656
  onClick: capture,
1447
- disabled: !ready
1657
+ disabled: captureDisabled,
1658
+ "aria-label": "Capture",
1659
+ style: captureDisabled ? { ...shutterButtonStyle, opacity: 0.5, cursor: "not-allowed" } : shutterButtonStyle
1448
1660
  },
1449
- /* @__PURE__ */ React__default.default.createElement("img", { src: Camera, alt: "", width: 20, height: 20 }),
1450
- /* @__PURE__ */ React__default.default.createElement("span", null, "Capture")
1451
- ), /* @__PURE__ */ React__default.default.createElement("button", { style: secondaryButtonStyle, onClick: onCancel }, "Cancel")));
1661
+ /* @__PURE__ */ React__default.default.createElement("span", { style: shutterInnerStyle })
1662
+ )));
1452
1663
  }
1453
1664
  var videoFrameStyle = {
1454
1665
  position: "relative",
1455
1666
  width: "100%",
1456
- aspectRatio: "1 / 1",
1457
- borderRadius: 12,
1667
+ aspectRatio: "3 / 4",
1668
+ borderRadius: 24,
1458
1669
  overflow: "hidden",
1459
- background: "#0f172a"
1670
+ background: "#000000",
1671
+ border: "4px solid rgba(255, 255, 255, 0.08)",
1672
+ boxShadow: "0 25px 50px -12px rgba(0, 0, 0, 0.5)"
1673
+ };
1674
+ var overlayCloseButtonStyle = {
1675
+ position: "absolute",
1676
+ top: 12,
1677
+ right: 12,
1678
+ width: 32,
1679
+ height: 32,
1680
+ display: "inline-flex",
1681
+ alignItems: "center",
1682
+ justifyContent: "center",
1683
+ padding: 0,
1684
+ border: "none",
1685
+ borderRadius: "50%",
1686
+ background: "rgba(0, 0, 0, 0.4)",
1687
+ backdropFilter: "blur(6px)",
1688
+ color: "#ffffff",
1689
+ cursor: "pointer",
1690
+ zIndex: 10
1691
+ };
1692
+ var bottomGradientStyle = {
1693
+ position: "absolute",
1694
+ bottom: 0,
1695
+ left: 0,
1696
+ right: 0,
1697
+ padding: "16px",
1698
+ background: "linear-gradient(to top, rgba(0, 0, 0, 0.8) 0%, rgba(0, 0, 0, 0) 100%)",
1699
+ display: "flex",
1700
+ alignItems: "center",
1701
+ justifyContent: "center",
1702
+ zIndex: 10
1703
+ };
1704
+ var shutterButtonStyle = {
1705
+ width: 64,
1706
+ height: 64,
1707
+ borderRadius: "50%",
1708
+ background: "rgba(255, 255, 255, 0.15)",
1709
+ backdropFilter: "blur(6px)",
1710
+ border: "2px solid rgba(255, 255, 255, 0.7)",
1711
+ display: "inline-flex",
1712
+ alignItems: "center",
1713
+ justifyContent: "center",
1714
+ cursor: "pointer",
1715
+ padding: 0,
1716
+ transition: "transform 120ms ease-out, background 120ms"
1717
+ };
1718
+ var shutterInnerStyle = {
1719
+ display: "block",
1720
+ width: 44,
1721
+ height: 44,
1722
+ borderRadius: "50%",
1723
+ background: "rgba(255, 255, 255, 0.85)"
1460
1724
  };
1461
1725
  var videoStyle = {
1462
1726
  width: "100%",
@@ -1500,7 +1764,9 @@ var videoLoadingStyle = {
1500
1764
  justifyContent: "center",
1501
1765
  color: "#e5e7eb",
1502
1766
  fontSize: 14,
1503
- fontWeight: 500
1767
+ fontWeight: 500,
1768
+ background: "rgba(0, 0, 0, 0.4)",
1769
+ zIndex: 5
1504
1770
  };
1505
1771
  var liveMessagePillContainerStyle = {
1506
1772
  position: "absolute",
@@ -1865,9 +2131,9 @@ function getRiskColor(risk) {
1865
2131
  };
1866
2132
  return colorMap[risk] || "#6b7280";
1867
2133
  }
1868
- function useReuseKYCSubmission(client$1) {
2134
+ function useEventBasedFaceVerificationSubmission(client$1) {
1869
2135
  const startSession = React.useCallback(
1870
- (request) => client$1.createReuseKycSession(request),
2136
+ (request) => client$1.createEventBasedFaceVerificationSession(request),
1871
2137
  [client$1]
1872
2138
  );
1873
2139
  const verifyFace = React.useCallback(
@@ -1884,6 +2150,8 @@ function useReuseKYCSubmission(client$1) {
1884
2150
  session,
1885
2151
  defaultDevice: opts.defaultDevice,
1886
2152
  renderQR: opts.renderQR,
2153
+ onCancel: opts.onCancel,
2154
+ mediapipeAssetBasePath: opts.mediapipeAssetBasePath,
1887
2155
  onComplete: (result) => {
1888
2156
  cleanup();
1889
2157
  resolve(result);
@@ -1920,6 +2188,7 @@ exports.isValidFileSize = isValidFileSize;
1920
2188
  exports.isValidFileType = isValidFileType;
1921
2189
  exports.useCipherText = useCipherText;
1922
2190
  exports.useCustomerProfile = useCustomerProfile;
2191
+ exports.useEventBasedFaceVerificationSubmission = useEventBasedFaceVerificationSubmission;
1923
2192
  exports.useGeolocation = useGeolocation;
1924
2193
  exports.useKycAlerts = useKycAlerts;
1925
2194
  exports.useKycOverview = useKycOverview;
@@ -1930,7 +2199,6 @@ exports.useLocationCapture = useLocationCapture;
1930
2199
  exports.useLocationRequests = useLocationRequests;
1931
2200
  exports.useLoginVerification = useLoginVerification;
1932
2201
  exports.useRegistration = useRegistration;
1933
- exports.useReuseKYCSubmission = useReuseKYCSubmission;
1934
2202
  exports.useTransactionVerification = useTransactionVerification;
1935
2203
  //# sourceMappingURL=react.js.map
1936
2204
  //# sourceMappingURL=react.js.map