vesant-sdk 1.7.1-dev.301c6df → 1.7.1-dev.328baf7
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.
- package/dist/{client-D23KhWeh.d.mts → client-5q4whhGu.d.mts} +1 -1
- package/dist/{client-fy3trF2K.d.ts → client-BaUekQT8.d.ts} +32 -10
- package/dist/{client-B3DCAHlg.d.ts → client-DUHC-68_.d.ts} +1 -1
- package/dist/{client-BwXMQA2a.d.mts → client-XDEhh9VN.d.mts} +32 -10
- package/dist/compliance/index.d.mts +2 -2
- package/dist/compliance/index.d.ts +2 -2
- package/dist/compliance/index.js +39 -1
- package/dist/compliance/index.js.map +1 -1
- package/dist/compliance/index.mjs +39 -1
- package/dist/compliance/index.mjs.map +1 -1
- package/dist/geolocation/index.d.mts +2 -2
- package/dist/geolocation/index.d.ts +2 -2
- package/dist/geolocation/index.js +39 -1
- package/dist/geolocation/index.js.map +1 -1
- package/dist/geolocation/index.mjs +39 -1
- package/dist/geolocation/index.mjs.map +1 -1
- package/dist/index.d.mts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +66 -8
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +66 -9
- package/dist/index.mjs.map +1 -1
- package/dist/kyc/core.d.mts +1 -1
- package/dist/kyc/core.d.ts +1 -1
- package/dist/kyc/core.js +27 -7
- package/dist/kyc/core.js.map +1 -1
- package/dist/kyc/core.mjs +27 -8
- package/dist/kyc/core.mjs.map +1 -1
- package/dist/kyc/index.d.mts +150 -26
- package/dist/kyc/index.d.ts +150 -26
- package/dist/kyc/index.js +27 -7
- package/dist/kyc/index.js.map +1 -1
- package/dist/kyc/index.mjs +27 -8
- package/dist/kyc/index.mjs.map +1 -1
- package/dist/react.d.mts +9 -2
- package/dist/react.d.ts +9 -2
- package/dist/react.js +166 -35
- package/dist/react.js.map +1 -1
- package/dist/react.mjs +166 -35
- package/dist/react.mjs.map +1 -1
- package/package.json +1 -1
package/dist/react.mjs
CHANGED
|
@@ -423,14 +423,11 @@ function useCipherText(client) {
|
|
|
423
423
|
const [configLoading, setConfigLoading] = useState(!!client);
|
|
424
424
|
const [configError, setConfigError] = useState(null);
|
|
425
425
|
const configRef = useRef(void 0);
|
|
426
|
-
const configPromiseRef = useRef(null);
|
|
427
426
|
useEffect(() => {
|
|
428
427
|
if (!client) return;
|
|
429
428
|
let cancelled = false;
|
|
430
429
|
setConfigLoading(true);
|
|
431
|
-
|
|
432
|
-
configPromiseRef.current = promise;
|
|
433
|
-
promise.then((cfg) => {
|
|
430
|
+
client.getGPSConfig().then((cfg) => {
|
|
434
431
|
if (!cancelled) {
|
|
435
432
|
configRef.current = cfg;
|
|
436
433
|
setConfigLoading(false);
|
|
@@ -445,13 +442,21 @@ function useCipherText(client) {
|
|
|
445
442
|
cancelled = true;
|
|
446
443
|
};
|
|
447
444
|
}, [client]);
|
|
445
|
+
const refreshConfig = useCallback(async () => {
|
|
446
|
+
if (!client) return;
|
|
447
|
+
try {
|
|
448
|
+
configRef.current = await client.refreshGPSConfig();
|
|
449
|
+
setConfigError(null);
|
|
450
|
+
} catch (error) {
|
|
451
|
+
setConfigError(error instanceof Error ? error : new Error("Failed to refresh GPS config"));
|
|
452
|
+
}
|
|
453
|
+
}, [client]);
|
|
448
454
|
const generate = useCallback(
|
|
449
455
|
async (options) => {
|
|
450
456
|
try {
|
|
451
|
-
if (
|
|
457
|
+
if (client) {
|
|
452
458
|
try {
|
|
453
|
-
|
|
454
|
-
configRef.current = cfg;
|
|
459
|
+
configRef.current = await client.getGPSConfig();
|
|
455
460
|
} catch {
|
|
456
461
|
}
|
|
457
462
|
}
|
|
@@ -461,9 +466,9 @@ function useCipherText(client) {
|
|
|
461
466
|
return null;
|
|
462
467
|
}
|
|
463
468
|
},
|
|
464
|
-
[]
|
|
469
|
+
[client]
|
|
465
470
|
);
|
|
466
|
-
return { generate, configLoading, configError };
|
|
471
|
+
return { generate, refreshConfig, configLoading, configError };
|
|
467
472
|
}
|
|
468
473
|
function useLocationRequests(client, options = {}) {
|
|
469
474
|
const [requests, setRequests] = useState([]);
|
|
@@ -924,19 +929,19 @@ var Close = "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0id
|
|
|
924
929
|
var FACE_MESSAGES = {
|
|
925
930
|
idle: "Initializing camera...",
|
|
926
931
|
loading: "Loading face detection...",
|
|
927
|
-
scanning: "Position your face in the
|
|
932
|
+
scanning: "Position your face in the oval",
|
|
928
933
|
too_far: "Face too far. Please move closer to the camera",
|
|
929
934
|
too_close: "Too close. Please move back a little",
|
|
930
|
-
off_center: "Center your face in the
|
|
935
|
+
off_center: "Center your face in the oval",
|
|
931
936
|
ok: "Looking great! Tap the button to capture"
|
|
932
937
|
};
|
|
933
938
|
var FACE_COLORS = {
|
|
934
939
|
idle: "#9ca3af",
|
|
935
|
-
loading: "#
|
|
936
|
-
scanning: "#
|
|
940
|
+
loading: "#ef4444",
|
|
941
|
+
scanning: "#ef4444",
|
|
937
942
|
too_far: "#ef4444",
|
|
938
|
-
too_close: "#
|
|
939
|
-
off_center: "#
|
|
943
|
+
too_close: "#ef4444",
|
|
944
|
+
off_center: "#ef4444",
|
|
940
945
|
ok: "#10b981"
|
|
941
946
|
};
|
|
942
947
|
var MEDIAPIPE_BASE = "https://cdn.jsdelivr.net/npm/@mediapipe/face_detection";
|
|
@@ -1001,11 +1006,32 @@ function loadMediaPipeFaceDetection(assetBasePath) {
|
|
|
1001
1006
|
return loader;
|
|
1002
1007
|
}
|
|
1003
1008
|
|
|
1009
|
+
// src/kyc/hostedJourney.ts
|
|
1010
|
+
var REUSE_KYC_SESSION_EXPIRY_MS = 10 * 60 * 1e3;
|
|
1011
|
+
function tryStartOperation(guard) {
|
|
1012
|
+
if (guard.current) return false;
|
|
1013
|
+
guard.current = true;
|
|
1014
|
+
return true;
|
|
1015
|
+
}
|
|
1016
|
+
function releaseOperation(guard) {
|
|
1017
|
+
guard.current = false;
|
|
1018
|
+
}
|
|
1019
|
+
function retryTarget(hasHostedJourney) {
|
|
1020
|
+
return hasHostedJourney ? "journey" : "capture";
|
|
1021
|
+
}
|
|
1022
|
+
function modalMaxWidth(hasHostedJourney) {
|
|
1023
|
+
return hasHostedJourney ? 680 : 448;
|
|
1024
|
+
}
|
|
1025
|
+
function resultForModalClose(stageKind, result) {
|
|
1026
|
+
const terminal = stageKind === "accepted" || stageKind === "declined" || stageKind === "max_attempts";
|
|
1027
|
+
return terminal && result !== void 0 ? result : null;
|
|
1028
|
+
}
|
|
1029
|
+
|
|
1004
1030
|
// src/kyc/FaceCaptureModal.tsx
|
|
1005
1031
|
var MOBILE_UA = /Mobi|Android|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i;
|
|
1006
|
-
var
|
|
1032
|
+
var ACCEPTED_AUTO_CLOSE_MS = 1200;
|
|
1007
1033
|
var LIVENESS_MESSAGES = [
|
|
1008
|
-
"Position your face inside the
|
|
1034
|
+
"Position your face inside the oval",
|
|
1009
1035
|
"Make sure your face is well lit",
|
|
1010
1036
|
"Remove glasses or hats if possible",
|
|
1011
1037
|
"Look straight at the camera, then tap Capture"
|
|
@@ -1014,6 +1040,8 @@ function headerSubtitle(stageKind) {
|
|
|
1014
1040
|
switch (stageKind) {
|
|
1015
1041
|
case "qr":
|
|
1016
1042
|
return "Use your phone to continue";
|
|
1043
|
+
case "journey":
|
|
1044
|
+
return "Follow the steps to verify your identity";
|
|
1017
1045
|
case "accepted":
|
|
1018
1046
|
return "All set";
|
|
1019
1047
|
case "declined":
|
|
@@ -1039,7 +1067,9 @@ function FaceCaptureModal({
|
|
|
1039
1067
|
}) {
|
|
1040
1068
|
const isMobile = typeof navigator !== "undefined" && MOBILE_UA.test(navigator.userAgent);
|
|
1041
1069
|
const initialChoice = defaultDevice ?? (isMobile ? "this" : "ask");
|
|
1070
|
+
const journeyMode = Boolean(session.verification_url);
|
|
1042
1071
|
const initialStage = (() => {
|
|
1072
|
+
if (journeyMode) return { kind: "journey", url: session.verification_url ?? "" };
|
|
1043
1073
|
if (initialChoice === "this") return { kind: "capture" };
|
|
1044
1074
|
if (initialChoice === "mobile") return { kind: "qr", mobileConnected: false };
|
|
1045
1075
|
return { kind: "choose" };
|
|
@@ -1057,19 +1087,20 @@ function FaceCaptureModal({
|
|
|
1057
1087
|
const qrPayload = session.link || `vesant://reuse-kyc?token=${encodeURIComponent(session.token)}`;
|
|
1058
1088
|
const cancelledRef = useRef(false);
|
|
1059
1089
|
const submittingRef = useRef(false);
|
|
1090
|
+
const journeyRetryingRef = useRef(false);
|
|
1060
1091
|
useEffect(() => {
|
|
1061
1092
|
const timer = setTimeout(() => {
|
|
1062
1093
|
setStage(
|
|
1063
|
-
(prev) => prev.kind === "choose" || prev.kind === "qr" || prev.kind === "capture" ? { kind: "error", message: "Session expired" } : prev
|
|
1094
|
+
(prev) => prev.kind === "choose" || prev.kind === "qr" || prev.kind === "capture" || prev.kind === "journey" ? { kind: "error", message: "Session expired" } : prev
|
|
1064
1095
|
);
|
|
1065
|
-
},
|
|
1096
|
+
}, REUSE_KYC_SESSION_EXPIRY_MS);
|
|
1066
1097
|
return () => clearTimeout(timer);
|
|
1067
1098
|
}, [session.token]);
|
|
1068
1099
|
useEffect(() => {
|
|
1069
1100
|
if (stage.kind !== "qr") return;
|
|
1070
1101
|
let stopped = false;
|
|
1071
1102
|
const intervalMs = 2e3;
|
|
1072
|
-
const deadline = Date.now() +
|
|
1103
|
+
const deadline = Date.now() + REUSE_KYC_SESSION_EXPIRY_MS;
|
|
1073
1104
|
let mobileSeen = stage.mobileConnected;
|
|
1074
1105
|
const tick = async () => {
|
|
1075
1106
|
if (stopped || cancelledRef.current) return;
|
|
@@ -1110,6 +1141,64 @@ function FaceCaptureModal({
|
|
|
1110
1141
|
stopped = true;
|
|
1111
1142
|
};
|
|
1112
1143
|
}, [stage.kind, stage.kind === "qr" ? stage.mobileConnected : false]);
|
|
1144
|
+
useEffect(() => {
|
|
1145
|
+
if (stage.kind !== "journey" || !stage.url) return;
|
|
1146
|
+
let stopped = false;
|
|
1147
|
+
const checkOnce = async () => {
|
|
1148
|
+
try {
|
|
1149
|
+
const result = await client.getEventBasedFaceVerificationSessionStatus(session.token);
|
|
1150
|
+
if (applyResult(result)) {
|
|
1151
|
+
stopped = true;
|
|
1152
|
+
return true;
|
|
1153
|
+
}
|
|
1154
|
+
} catch {
|
|
1155
|
+
}
|
|
1156
|
+
return false;
|
|
1157
|
+
};
|
|
1158
|
+
const tick = async () => {
|
|
1159
|
+
if (stopped || cancelledRef.current) return;
|
|
1160
|
+
await checkOnce();
|
|
1161
|
+
if (!stopped && !cancelledRef.current) {
|
|
1162
|
+
setTimeout(tick, 2500);
|
|
1163
|
+
}
|
|
1164
|
+
};
|
|
1165
|
+
void tick();
|
|
1166
|
+
let frameOrigin = "";
|
|
1167
|
+
try {
|
|
1168
|
+
frameOrigin = new URL(stage.url).origin;
|
|
1169
|
+
} catch {
|
|
1170
|
+
frameOrigin = "";
|
|
1171
|
+
}
|
|
1172
|
+
const onMessage = (e) => {
|
|
1173
|
+
if (!frameOrigin || e.origin !== frameOrigin) return;
|
|
1174
|
+
const status = e.data?.verification_status;
|
|
1175
|
+
if (status) void checkOnce();
|
|
1176
|
+
};
|
|
1177
|
+
window.addEventListener("message", onMessage);
|
|
1178
|
+
return () => {
|
|
1179
|
+
stopped = true;
|
|
1180
|
+
window.removeEventListener("message", onMessage);
|
|
1181
|
+
};
|
|
1182
|
+
}, [stage.kind, stage.kind === "journey" ? stage.url : ""]);
|
|
1183
|
+
const handleJourneyRetry = async () => {
|
|
1184
|
+
if (!tryStartOperation(journeyRetryingRef)) return;
|
|
1185
|
+
setStage({ kind: "journey", url: "" });
|
|
1186
|
+
try {
|
|
1187
|
+
const refreshed = await client.retryEventBasedFaceVerificationJourney(session.token);
|
|
1188
|
+
if (refreshed.verification_url) {
|
|
1189
|
+
setStage({ kind: "journey", url: refreshed.verification_url });
|
|
1190
|
+
return;
|
|
1191
|
+
}
|
|
1192
|
+
setStage({ kind: "error", message: "Unable to start a new attempt. Please try again." });
|
|
1193
|
+
} catch (err) {
|
|
1194
|
+
setStage({
|
|
1195
|
+
kind: "error",
|
|
1196
|
+
message: err instanceof Error ? err.message : "Unable to start a new attempt. Please try again."
|
|
1197
|
+
});
|
|
1198
|
+
} finally {
|
|
1199
|
+
releaseOperation(journeyRetryingRef);
|
|
1200
|
+
}
|
|
1201
|
+
};
|
|
1113
1202
|
const applyResult = (result) => {
|
|
1114
1203
|
if (result.status === "accepted") {
|
|
1115
1204
|
setStage({ kind: "accepted", result });
|
|
@@ -1237,12 +1326,22 @@ function FaceCaptureModal({
|
|
|
1237
1326
|
}, 3e3);
|
|
1238
1327
|
return () => clearInterval(interval);
|
|
1239
1328
|
}, [captureMode]);
|
|
1240
|
-
const close = (result) => {
|
|
1329
|
+
const close = useCallback((result) => {
|
|
1241
1330
|
cancelledRef.current = true;
|
|
1242
1331
|
if (result === null && onCancel) {
|
|
1243
1332
|
onCancel();
|
|
1244
1333
|
}
|
|
1245
1334
|
onComplete(result);
|
|
1335
|
+
}, [onCancel, onComplete]);
|
|
1336
|
+
useEffect(() => {
|
|
1337
|
+
if (stage.kind !== "accepted") return;
|
|
1338
|
+
const result = stage.result;
|
|
1339
|
+
const timer = setTimeout(() => close(result), ACCEPTED_AUTO_CLOSE_MS);
|
|
1340
|
+
return () => clearTimeout(timer);
|
|
1341
|
+
}, [close, stage]);
|
|
1342
|
+
const handleHeaderClose = () => {
|
|
1343
|
+
const result = "result" in stage ? stage.result : void 0;
|
|
1344
|
+
close(resultForModalClose(stage.kind, result));
|
|
1246
1345
|
};
|
|
1247
1346
|
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(
|
|
1248
1347
|
"button",
|
|
@@ -1301,12 +1400,28 @@ function FaceCaptureModal({
|
|
|
1301
1400
|
};
|
|
1302
1401
|
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."));
|
|
1303
1402
|
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")));
|
|
1304
|
-
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));
|
|
1403
|
+
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 }, journeyMode ? /* @__PURE__ */ React.createElement("button", { style: primaryButtonStyle, onClick: () => void handleJourneyRetry() }, /* @__PURE__ */ React.createElement("span", null, "Try Again")) : /* @__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));
|
|
1305
1404
|
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")));
|
|
1306
1405
|
const renderError = (message) => {
|
|
1307
1406
|
const sessionExpired = /session\s+expired/i.test(message);
|
|
1308
|
-
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(
|
|
1407
|
+
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(
|
|
1408
|
+
"button",
|
|
1409
|
+
{
|
|
1410
|
+
style: primaryButtonStyle,
|
|
1411
|
+
onClick: () => retryTarget(journeyMode) === "journey" ? void handleJourneyRetry() : setStage({ kind: "capture" })
|
|
1412
|
+
},
|
|
1413
|
+
"Try Again"
|
|
1414
|
+
), /* @__PURE__ */ React.createElement("button", { style: cancelButtonStyle, onClick: () => close(null) }, sessionExpired ? "Close" : "Cancel")));
|
|
1309
1415
|
};
|
|
1416
|
+
const renderJourney = (url) => /* @__PURE__ */ React.createElement("div", { style: contentStyle }, url ? /* @__PURE__ */ React.createElement(
|
|
1417
|
+
"iframe",
|
|
1418
|
+
{
|
|
1419
|
+
src: url,
|
|
1420
|
+
title: "Identity verification",
|
|
1421
|
+
allow: "camera; microphone",
|
|
1422
|
+
style: journeyFrameStyle
|
|
1423
|
+
}
|
|
1424
|
+
) : /* @__PURE__ */ React.createElement("div", { style: { padding: 24, textAlign: "center" } }, /* @__PURE__ */ React.createElement("div", { style: { ...spinnerStyle, margin: "24px auto", width: 32, height: 32 } }), /* @__PURE__ */ React.createElement("p", { style: subtitleStyle }, "Preparing your next attempt\u2026")), /* @__PURE__ */ React.createElement("p", { style: { ...attemptsTextStyle, textAlign: "center" } }, "Attempt ", Math.min(attempts + 1, maxAttempts), " of ", maxAttempts));
|
|
1310
1425
|
const body = (() => {
|
|
1311
1426
|
switch (stage.kind) {
|
|
1312
1427
|
case "choose":
|
|
@@ -1315,6 +1430,8 @@ function FaceCaptureModal({
|
|
|
1315
1430
|
return renderQRStage(stage.mobileConnected);
|
|
1316
1431
|
case "capture":
|
|
1317
1432
|
return renderCapture(stage.declinedReason);
|
|
1433
|
+
case "journey":
|
|
1434
|
+
return renderJourney(stage.url);
|
|
1318
1435
|
case "submitting":
|
|
1319
1436
|
return renderSubmitting();
|
|
1320
1437
|
case "accepted":
|
|
@@ -1327,10 +1444,10 @@ function FaceCaptureModal({
|
|
|
1327
1444
|
return renderError(stage.message);
|
|
1328
1445
|
}
|
|
1329
1446
|
})();
|
|
1330
|
-
return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("style", null, keyframes), /* @__PURE__ */ React.createElement("div", { style: overlayStyle }, /* @__PURE__ */ React.createElement("div", { style: modalStyle }, /* @__PURE__ */ React.createElement("div", { style: headerStyle }, /* @__PURE__ */ React.createElement(
|
|
1447
|
+
return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("style", null, keyframes), /* @__PURE__ */ React.createElement("div", { style: overlayStyle }, /* @__PURE__ */ React.createElement("div", { style: { ...modalStyle, maxWidth: modalMaxWidth(journeyMode) } }, /* @__PURE__ */ React.createElement("div", { style: headerStyle }, /* @__PURE__ */ React.createElement(
|
|
1331
1448
|
"button",
|
|
1332
1449
|
{
|
|
1333
|
-
onClick:
|
|
1450
|
+
onClick: handleHeaderClose,
|
|
1334
1451
|
style: closeButtonStyle,
|
|
1335
1452
|
"aria-label": "Close"
|
|
1336
1453
|
},
|
|
@@ -1360,7 +1477,8 @@ var modalStyle = {
|
|
|
1360
1477
|
borderRadius: 16,
|
|
1361
1478
|
boxShadow: "0 25px 50px -12px rgba(0,0,0,.25)",
|
|
1362
1479
|
width: "100%",
|
|
1363
|
-
|
|
1480
|
+
maxHeight: "calc(100vh - 32px)",
|
|
1481
|
+
overflowY: "auto",
|
|
1364
1482
|
animation: "zoomIn .2s ease-out"
|
|
1365
1483
|
};
|
|
1366
1484
|
var headerStyle = {
|
|
@@ -1386,6 +1504,13 @@ var contentStyle = {
|
|
|
1386
1504
|
flexDirection: "column",
|
|
1387
1505
|
gap: 16
|
|
1388
1506
|
};
|
|
1507
|
+
var journeyFrameStyle = {
|
|
1508
|
+
width: "100%",
|
|
1509
|
+
height: "min(520px, 60vh)",
|
|
1510
|
+
border: 0,
|
|
1511
|
+
borderRadius: 12,
|
|
1512
|
+
background: "#f9fafb"
|
|
1513
|
+
};
|
|
1389
1514
|
var visualGuideContainerStyle = { display: "flex", justifyContent: "center" };
|
|
1390
1515
|
var visualGuideStyle = { position: "relative" };
|
|
1391
1516
|
var circleStyle = {
|
|
@@ -1652,9 +1777,10 @@ function LiveCamera({ onCapture, onCancel, message, assetBasePath }) {
|
|
|
1652
1777
|
if (error) {
|
|
1653
1778
|
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")));
|
|
1654
1779
|
}
|
|
1655
|
-
const
|
|
1656
|
-
const
|
|
1657
|
-
const
|
|
1780
|
+
const detectionAvailable = !faceDetectionFailed;
|
|
1781
|
+
const detectionActive = ready && detectionAvailable;
|
|
1782
|
+
const liveMessage = detectionAvailable ? FACE_MESSAGES[faceStatus] : message;
|
|
1783
|
+
const liveFaceColor = detectionAvailable ? FACE_COLORS[faceStatus] : void 0;
|
|
1658
1784
|
const captureDisabled = isCaptureDisabled(ready, detectionActive, faceStatus);
|
|
1659
1785
|
const ringPulse = detectionActive && (faceStatus === "too_far" || faceStatus === "too_close") ? "pulse 1.5s ease-in-out infinite" : void 0;
|
|
1660
1786
|
const dynamicCircleStyle = {
|
|
@@ -1770,20 +1896,22 @@ var faceOverlayContainerStyle = {
|
|
|
1770
1896
|
};
|
|
1771
1897
|
var faceRingWrapStyle = {
|
|
1772
1898
|
position: "relative",
|
|
1773
|
-
|
|
1774
|
-
aspectRatio: "
|
|
1899
|
+
height: "64%",
|
|
1900
|
+
aspectRatio: "3 / 4"
|
|
1775
1901
|
};
|
|
1776
1902
|
var faceDashedRingStyle = {
|
|
1777
1903
|
position: "absolute",
|
|
1778
1904
|
inset: -6,
|
|
1779
|
-
borderRadius:
|
|
1780
|
-
border: "6px dashed rgba(255, 255, 255, 0.4)"
|
|
1781
|
-
animation: "spin 10s linear infinite"
|
|
1905
|
+
borderRadius: 9999,
|
|
1906
|
+
border: "6px dashed rgba(255, 255, 255, 0.4)"
|
|
1782
1907
|
};
|
|
1783
1908
|
var faceCircleStyle = {
|
|
1784
1909
|
position: "absolute",
|
|
1785
1910
|
inset: 0,
|
|
1786
|
-
|
|
1911
|
+
// Tailwind's `rounded-full` resolves to a very large fixed radius. On a
|
|
1912
|
+
// 3:4 box that creates the normal KYC capsule/head guide with straighter
|
|
1913
|
+
// sides; 50% would instead produce a mathematically perfect ellipse.
|
|
1914
|
+
borderRadius: 9999,
|
|
1787
1915
|
border: "4px solid #00bc7d",
|
|
1788
1916
|
boxShadow: "0 0 0 1000px rgba(0, 0, 0, 0.5)"
|
|
1789
1917
|
};
|
|
@@ -2126,6 +2254,7 @@ function isValidFileSize(file, maxSizeBytes = 10 * 1024 * 1024) {
|
|
|
2126
2254
|
}
|
|
2127
2255
|
function formatKycStatus(status) {
|
|
2128
2256
|
const statusMap = {
|
|
2257
|
+
awaiting_kyc: "Awaiting KYC",
|
|
2129
2258
|
pending: "Pending Review",
|
|
2130
2259
|
accepted: "Approved",
|
|
2131
2260
|
declined: "Rejected",
|
|
@@ -2136,6 +2265,8 @@ function formatKycStatus(status) {
|
|
|
2136
2265
|
}
|
|
2137
2266
|
function getStatusColor(status) {
|
|
2138
2267
|
const colorMap = {
|
|
2268
|
+
awaiting_kyc: "#94a3b8",
|
|
2269
|
+
// slate — not started
|
|
2139
2270
|
pending: "#f59e0b",
|
|
2140
2271
|
// amber
|
|
2141
2272
|
accepted: "#10b981",
|