@koraidv/react 1.7.5 → 1.7.7
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/index.d.mts +31 -0
- package/dist/index.d.ts +31 -0
- package/dist/index.js +187 -53
- package/dist/index.mjs +221 -87
- package/package.json +2 -2
package/dist/index.d.mts
CHANGED
|
@@ -205,6 +205,37 @@ interface LivenessScreenProps {
|
|
|
205
205
|
onComplete: () => Promise<any>;
|
|
206
206
|
onCancel: () => void;
|
|
207
207
|
}
|
|
208
|
+
/**
|
|
209
|
+
* Web liveness screen with a real front-facing camera.
|
|
210
|
+
*
|
|
211
|
+
* Before v1.7.7 this was a stub: a static oval guide + a "Complete
|
|
212
|
+
* Challenge" button that submitted a blank 100×100 white canvas. The
|
|
213
|
+
* camera was never wired up — Web shipped without a real liveness
|
|
214
|
+
* implementation while iOS + Android had full liveness from day one.
|
|
215
|
+
* Found and called out by Luckycat's first end-to-end integration on
|
|
216
|
+
* 2026-05-29.
|
|
217
|
+
*
|
|
218
|
+
* This implementation pairs a real `getUserMedia` camera feed with the
|
|
219
|
+
* server's existing liveness pipeline (ml-service detects whether the
|
|
220
|
+
* submitted frame shows the requested gesture). Per-challenge flow:
|
|
221
|
+
*
|
|
222
|
+
* 1. Render the front camera in the oval guide (object-fit cover,
|
|
223
|
+
* circular clip — same visual shape as the previous stub).
|
|
224
|
+
* 2. Show the challenge instruction (Smile / Turn left / Blink / ...).
|
|
225
|
+
* 3. Run a 3-second countdown so the user has time to perform the
|
|
226
|
+
* gesture.
|
|
227
|
+
* 4. When the countdown hits zero, capture a frame from the video
|
|
228
|
+
* and post it to /verifications/{id}/liveness/challenge with the
|
|
229
|
+
* challenge type. The hook's submitChallenge advances state on
|
|
230
|
+
* pass; on fail the same challenge stays current and the
|
|
231
|
+
* countdown re-arms for retry.
|
|
232
|
+
*
|
|
233
|
+
* Client-side gesture detection (MediaPipe Face Mesh + auto-capture
|
|
234
|
+
* when the gesture is satisfied) is the planned follow-up — that would
|
|
235
|
+
* close the UX gap with iOS, where the user doesn't have to time
|
|
236
|
+
* themselves against a countdown. Today's ship is "real camera, real
|
|
237
|
+
* frames, server decides," which is the parity floor.
|
|
238
|
+
*/
|
|
208
239
|
declare function LivenessScreen({ session, currentChallenge, completedChallenges, onChallengeComplete, onStart, onComplete, onCancel, }: LivenessScreenProps): react_jsx_runtime.JSX.Element;
|
|
209
240
|
|
|
210
241
|
type ResultPageMode = 'detailed' | 'simplified';
|
package/dist/index.d.ts
CHANGED
|
@@ -205,6 +205,37 @@ interface LivenessScreenProps {
|
|
|
205
205
|
onComplete: () => Promise<any>;
|
|
206
206
|
onCancel: () => void;
|
|
207
207
|
}
|
|
208
|
+
/**
|
|
209
|
+
* Web liveness screen with a real front-facing camera.
|
|
210
|
+
*
|
|
211
|
+
* Before v1.7.7 this was a stub: a static oval guide + a "Complete
|
|
212
|
+
* Challenge" button that submitted a blank 100×100 white canvas. The
|
|
213
|
+
* camera was never wired up — Web shipped without a real liveness
|
|
214
|
+
* implementation while iOS + Android had full liveness from day one.
|
|
215
|
+
* Found and called out by Luckycat's first end-to-end integration on
|
|
216
|
+
* 2026-05-29.
|
|
217
|
+
*
|
|
218
|
+
* This implementation pairs a real `getUserMedia` camera feed with the
|
|
219
|
+
* server's existing liveness pipeline (ml-service detects whether the
|
|
220
|
+
* submitted frame shows the requested gesture). Per-challenge flow:
|
|
221
|
+
*
|
|
222
|
+
* 1. Render the front camera in the oval guide (object-fit cover,
|
|
223
|
+
* circular clip — same visual shape as the previous stub).
|
|
224
|
+
* 2. Show the challenge instruction (Smile / Turn left / Blink / ...).
|
|
225
|
+
* 3. Run a 3-second countdown so the user has time to perform the
|
|
226
|
+
* gesture.
|
|
227
|
+
* 4. When the countdown hits zero, capture a frame from the video
|
|
228
|
+
* and post it to /verifications/{id}/liveness/challenge with the
|
|
229
|
+
* challenge type. The hook's submitChallenge advances state on
|
|
230
|
+
* pass; on fail the same challenge stays current and the
|
|
231
|
+
* countdown re-arms for retry.
|
|
232
|
+
*
|
|
233
|
+
* Client-side gesture detection (MediaPipe Face Mesh + auto-capture
|
|
234
|
+
* when the gesture is satisfied) is the planned follow-up — that would
|
|
235
|
+
* close the UX gap with iOS, where the user doesn't have to time
|
|
236
|
+
* themselves against a countdown. Today's ship is "real camera, real
|
|
237
|
+
* frames, server decides," which is the parity floor.
|
|
238
|
+
*/
|
|
208
239
|
declare function LivenessScreen({ session, currentChallenge, completedChallenges, onChallengeComplete, onStart, onComplete, onCancel, }: LivenessScreenProps): react_jsx_runtime.JSX.Element;
|
|
209
240
|
|
|
210
241
|
type ResultPageMode = 'detailed' | 'simplified';
|
package/dist/index.js
CHANGED
|
@@ -330,6 +330,15 @@ function useKoraIDV() {
|
|
|
330
330
|
return null;
|
|
331
331
|
}
|
|
332
332
|
}, [sdk]);
|
|
333
|
+
const completionFiredRef = (0, import_react2.useRef)(false);
|
|
334
|
+
(0, import_react2.useEffect)(() => {
|
|
335
|
+
if (state.step === "processing" && !completionFiredRef.current) {
|
|
336
|
+
completionFiredRef.current = true;
|
|
337
|
+
complete();
|
|
338
|
+
} else if (state.step !== "processing" && state.step !== "complete") {
|
|
339
|
+
completionFiredRef.current = false;
|
|
340
|
+
}
|
|
341
|
+
}, [state.step, complete]);
|
|
333
342
|
const cancel = (0, import_react2.useCallback)(() => {
|
|
334
343
|
sdk.reset();
|
|
335
344
|
setState({
|
|
@@ -2341,7 +2350,12 @@ function LivenessScreen({
|
|
|
2341
2350
|
onComplete,
|
|
2342
2351
|
onCancel
|
|
2343
2352
|
}) {
|
|
2353
|
+
const videoRef = (0, import_react8.useRef)(null);
|
|
2354
|
+
const canvasRef = (0, import_react8.useRef)(null);
|
|
2355
|
+
const [stream, setStream] = (0, import_react8.useState)(null);
|
|
2356
|
+
const [cameraError, setCameraError] = (0, import_react8.useState)(null);
|
|
2344
2357
|
const [countdown, setCountdown] = (0, import_react8.useState)(3);
|
|
2358
|
+
const [capturing, setCapturing] = (0, import_react8.useState)(false);
|
|
2345
2359
|
(0, import_react8.useEffect)(() => {
|
|
2346
2360
|
injectKeyframes();
|
|
2347
2361
|
}, []);
|
|
@@ -2349,24 +2363,89 @@ function LivenessScreen({
|
|
|
2349
2363
|
if (!session) onStart();
|
|
2350
2364
|
}, [session, onStart]);
|
|
2351
2365
|
(0, import_react8.useEffect)(() => {
|
|
2352
|
-
|
|
2353
|
-
|
|
2366
|
+
let mounted = true;
|
|
2367
|
+
async function startCamera() {
|
|
2368
|
+
try {
|
|
2369
|
+
const mediaStream = await navigator.mediaDevices.getUserMedia({
|
|
2370
|
+
video: {
|
|
2371
|
+
facingMode: "user",
|
|
2372
|
+
width: { ideal: 720 },
|
|
2373
|
+
height: { ideal: 720 }
|
|
2374
|
+
}
|
|
2375
|
+
});
|
|
2376
|
+
if (!mounted) {
|
|
2377
|
+
mediaStream.getTracks().forEach((t) => t.stop());
|
|
2378
|
+
return;
|
|
2379
|
+
}
|
|
2380
|
+
setStream(mediaStream);
|
|
2381
|
+
if (videoRef.current) {
|
|
2382
|
+
videoRef.current.srcObject = mediaStream;
|
|
2383
|
+
}
|
|
2384
|
+
} catch {
|
|
2385
|
+
if (mounted) {
|
|
2386
|
+
setCameraError(
|
|
2387
|
+
"Camera access denied. Please enable camera permissions and try again."
|
|
2388
|
+
);
|
|
2389
|
+
}
|
|
2390
|
+
}
|
|
2354
2391
|
}
|
|
2355
|
-
|
|
2392
|
+
startCamera();
|
|
2393
|
+
return () => {
|
|
2394
|
+
mounted = false;
|
|
2395
|
+
};
|
|
2396
|
+
}, []);
|
|
2397
|
+
(0, import_react8.useEffect)(() => {
|
|
2398
|
+
return () => {
|
|
2399
|
+
stream?.getTracks().forEach((t) => t.stop());
|
|
2400
|
+
};
|
|
2401
|
+
}, [stream]);
|
|
2356
2402
|
(0, import_react8.useEffect)(() => {
|
|
2357
2403
|
if (!currentChallenge) return;
|
|
2358
2404
|
setCountdown(3);
|
|
2359
|
-
const interval = setInterval(() => {
|
|
2360
|
-
setCountdown((c) => {
|
|
2361
|
-
if (c <= 1) {
|
|
2362
|
-
clearInterval(interval);
|
|
2363
|
-
return 0;
|
|
2364
|
-
}
|
|
2365
|
-
return c - 1;
|
|
2366
|
-
});
|
|
2367
|
-
}, 1e3);
|
|
2368
|
-
return () => clearInterval(interval);
|
|
2369
2405
|
}, [currentChallenge?.id]);
|
|
2406
|
+
const captureFrame = (0, import_react8.useCallback)(async () => {
|
|
2407
|
+
if (!currentChallenge || !videoRef.current || !canvasRef.current || capturing) {
|
|
2408
|
+
return;
|
|
2409
|
+
}
|
|
2410
|
+
const video = videoRef.current;
|
|
2411
|
+
const canvas = canvasRef.current;
|
|
2412
|
+
const ctx = canvas.getContext("2d");
|
|
2413
|
+
if (!ctx || video.videoWidth === 0 || video.videoHeight === 0) return;
|
|
2414
|
+
setCapturing(true);
|
|
2415
|
+
canvas.width = video.videoWidth;
|
|
2416
|
+
canvas.height = video.videoHeight;
|
|
2417
|
+
ctx.drawImage(video, 0, 0);
|
|
2418
|
+
canvas.toBlob(
|
|
2419
|
+
async (blob) => {
|
|
2420
|
+
if (blob) {
|
|
2421
|
+
await onChallengeComplete(blob);
|
|
2422
|
+
}
|
|
2423
|
+
setCapturing(false);
|
|
2424
|
+
},
|
|
2425
|
+
"image/jpeg",
|
|
2426
|
+
0.85
|
|
2427
|
+
);
|
|
2428
|
+
}, [currentChallenge, capturing, onChallengeComplete]);
|
|
2429
|
+
(0, import_react8.useEffect)(() => {
|
|
2430
|
+
if (!currentChallenge || capturing) return;
|
|
2431
|
+
if (countdown === 0) {
|
|
2432
|
+
captureFrame();
|
|
2433
|
+
return;
|
|
2434
|
+
}
|
|
2435
|
+
const t = setTimeout(() => setCountdown((c) => c - 1), 1e3);
|
|
2436
|
+
return () => clearTimeout(t);
|
|
2437
|
+
}, [countdown, currentChallenge?.id, capturing, captureFrame]);
|
|
2438
|
+
(0, import_react8.useEffect)(() => {
|
|
2439
|
+
if (session && !currentChallenge && completedChallenges > 0) {
|
|
2440
|
+
onComplete();
|
|
2441
|
+
}
|
|
2442
|
+
}, [session, currentChallenge, completedChallenges, onComplete]);
|
|
2443
|
+
if (cameraError) {
|
|
2444
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { style: styles.darkContainer, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { style: styles.errorContainer, children: [
|
|
2445
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { style: styles.errorText, children: cameraError }),
|
|
2446
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("button", { style: styles.primaryButton, onClick: onCancel, children: "Go back" })
|
|
2447
|
+
] }) });
|
|
2448
|
+
}
|
|
2370
2449
|
if (!session) {
|
|
2371
2450
|
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { style: styles.darkContainer, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { style: styles.loadingContainer, children: [
|
|
2372
2451
|
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { style: styles.spinner }),
|
|
@@ -2382,33 +2461,91 @@ function LivenessScreen({
|
|
|
2382
2461
|
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("button", { style: styles.glassCloseButton, onClick: onCancel, children: "\u2715" })
|
|
2383
2462
|
] }),
|
|
2384
2463
|
currentChallenge && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { style: { padding: "16px 0" }, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("h2", { style: styles.challengeTitle, children: currentChallenge.instruction }) }),
|
|
2385
|
-
/* @__PURE__ */ (0, import_jsx_runtime9.
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
{
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2464
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
|
|
2465
|
+
"div",
|
|
2466
|
+
{
|
|
2467
|
+
style: {
|
|
2468
|
+
flex: 1,
|
|
2469
|
+
display: "flex",
|
|
2470
|
+
alignItems: "center",
|
|
2471
|
+
justifyContent: "center"
|
|
2472
|
+
},
|
|
2473
|
+
children: [
|
|
2474
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { style: { position: "relative" }, children: [
|
|
2475
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
2476
|
+
"div",
|
|
2477
|
+
{
|
|
2478
|
+
style: {
|
|
2479
|
+
width: "240px",
|
|
2480
|
+
height: "300px",
|
|
2481
|
+
borderRadius: "50%",
|
|
2482
|
+
overflow: "hidden",
|
|
2483
|
+
backgroundColor: "#000",
|
|
2484
|
+
border: "3px solid rgba(255,255,255,0.2)"
|
|
2485
|
+
},
|
|
2486
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
2487
|
+
"video",
|
|
2488
|
+
{
|
|
2489
|
+
ref: videoRef,
|
|
2490
|
+
autoPlay: true,
|
|
2491
|
+
playsInline: true,
|
|
2492
|
+
muted: true,
|
|
2493
|
+
style: {
|
|
2494
|
+
width: "100%",
|
|
2495
|
+
height: "100%",
|
|
2496
|
+
objectFit: "cover",
|
|
2497
|
+
transform: "scaleX(-1)"
|
|
2498
|
+
}
|
|
2499
|
+
}
|
|
2500
|
+
)
|
|
2501
|
+
}
|
|
2502
|
+
),
|
|
2503
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
2504
|
+
"svg",
|
|
2505
|
+
{
|
|
2506
|
+
style: {
|
|
2507
|
+
position: "absolute",
|
|
2508
|
+
top: "-8px",
|
|
2509
|
+
left: "-8px",
|
|
2510
|
+
pointerEvents: "none"
|
|
2511
|
+
},
|
|
2512
|
+
width: "256",
|
|
2513
|
+
height: "316",
|
|
2514
|
+
viewBox: "0 0 256 316",
|
|
2515
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
2516
|
+
"ellipse",
|
|
2517
|
+
{
|
|
2518
|
+
cx: "128",
|
|
2519
|
+
cy: "158",
|
|
2520
|
+
rx: "124",
|
|
2521
|
+
ry: "154",
|
|
2522
|
+
fill: "none",
|
|
2523
|
+
stroke: colors.teal,
|
|
2524
|
+
strokeWidth: "5",
|
|
2525
|
+
strokeDasharray: `${completedChallenges / totalChallenges * 880} 880`,
|
|
2526
|
+
transform: "rotate(-90 128 158)",
|
|
2527
|
+
strokeLinecap: "round"
|
|
2528
|
+
}
|
|
2529
|
+
)
|
|
2530
|
+
}
|
|
2531
|
+
),
|
|
2532
|
+
currentChallenge && countdown > 0 && !capturing && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { style: styles.countdownBadge, children: countdown }),
|
|
2533
|
+
capturing && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
2534
|
+
"div",
|
|
2535
|
+
{
|
|
2536
|
+
style: {
|
|
2537
|
+
...styles.countdownBadge,
|
|
2538
|
+
fontSize: "14px",
|
|
2539
|
+
padding: "8px 14px"
|
|
2540
|
+
},
|
|
2541
|
+
children: "Checking..."
|
|
2542
|
+
}
|
|
2543
|
+
)
|
|
2544
|
+
] }),
|
|
2545
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("canvas", { ref: canvasRef, style: { display: "none" } })
|
|
2546
|
+
]
|
|
2547
|
+
}
|
|
2548
|
+
),
|
|
2412
2549
|
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { style: { padding: "16px 0" }, children: [
|
|
2413
2550
|
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { style: styles.progressDots, children: session.challenges.map((_, index) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
2414
2551
|
"div",
|
|
@@ -2422,24 +2559,21 @@ function LivenessScreen({
|
|
|
2422
2559
|
)) }),
|
|
2423
2560
|
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("p", { style: styles.progressText, children: [
|
|
2424
2561
|
"Challenge ",
|
|
2425
|
-
completedChallenges + 1,
|
|
2426
|
-
" of
|
|
2562
|
+
Math.min(completedChallenges + 1, totalChallenges),
|
|
2563
|
+
" of",
|
|
2564
|
+
" ",
|
|
2427
2565
|
totalChallenges
|
|
2428
2566
|
] })
|
|
2429
2567
|
] }),
|
|
2430
|
-
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { style: { padding: "
|
|
2431
|
-
"
|
|
2568
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { style: { padding: "0 24px 24px", textAlign: "center" }, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
2569
|
+
"p",
|
|
2432
2570
|
{
|
|
2433
|
-
style:
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
canvas.height = 100;
|
|
2438
|
-
canvas.toBlob(async (blob) => {
|
|
2439
|
-
if (blob) await onChallengeComplete(blob);
|
|
2440
|
-
});
|
|
2571
|
+
style: {
|
|
2572
|
+
color: "rgba(255,255,255,0.5)",
|
|
2573
|
+
fontSize: "13px",
|
|
2574
|
+
margin: 0
|
|
2441
2575
|
},
|
|
2442
|
-
children: "
|
|
2576
|
+
children: "Position your face inside the oval and follow the prompt."
|
|
2443
2577
|
}
|
|
2444
2578
|
) })
|
|
2445
2579
|
] });
|
package/dist/index.mjs
CHANGED
|
@@ -34,7 +34,7 @@ function useKoraIDVContext() {
|
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
// src/hooks/useKoraIDV.ts
|
|
37
|
-
import { useState, useCallback } from "react";
|
|
37
|
+
import { useState, useCallback, useEffect, useRef } from "react";
|
|
38
38
|
import {
|
|
39
39
|
KoraError
|
|
40
40
|
} from "@koraidv/core";
|
|
@@ -281,6 +281,15 @@ function useKoraIDV() {
|
|
|
281
281
|
return null;
|
|
282
282
|
}
|
|
283
283
|
}, [sdk]);
|
|
284
|
+
const completionFiredRef = useRef(false);
|
|
285
|
+
useEffect(() => {
|
|
286
|
+
if (state.step === "processing" && !completionFiredRef.current) {
|
|
287
|
+
completionFiredRef.current = true;
|
|
288
|
+
complete();
|
|
289
|
+
} else if (state.step !== "processing" && state.step !== "complete") {
|
|
290
|
+
completionFiredRef.current = false;
|
|
291
|
+
}
|
|
292
|
+
}, [state.step, complete]);
|
|
284
293
|
const cancel = useCallback(() => {
|
|
285
294
|
sdk.reset();
|
|
286
295
|
setState({
|
|
@@ -319,7 +328,7 @@ function useKoraIDV() {
|
|
|
319
328
|
}
|
|
320
329
|
|
|
321
330
|
// src/components/VerificationFlow.tsx
|
|
322
|
-
import { useEffect as
|
|
331
|
+
import { useEffect as useEffect8, useState as useState6 } from "react";
|
|
323
332
|
import { KoraError as KoraError2, KoraErrorCode } from "@koraidv/core";
|
|
324
333
|
|
|
325
334
|
// src/components/styles.ts
|
|
@@ -1299,7 +1308,7 @@ var styles = {
|
|
|
1299
1308
|
};
|
|
1300
1309
|
|
|
1301
1310
|
// src/components/DesignSystem.tsx
|
|
1302
|
-
import { useEffect } from "react";
|
|
1311
|
+
import { useEffect as useEffect2 } from "react";
|
|
1303
1312
|
import { jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
1304
1313
|
function StepProgressBar({ total, current, isDark = false }) {
|
|
1305
1314
|
return /* @__PURE__ */ jsx2("div", { style: styles.progressBar, children: Array.from({ length: total }).map((_, i) => /* @__PURE__ */ jsx2(
|
|
@@ -1373,7 +1382,7 @@ function ScoreMetricRow({ label, score, icon, status, message }) {
|
|
|
1373
1382
|
);
|
|
1374
1383
|
}
|
|
1375
1384
|
function ProcessingScreen({ steps }) {
|
|
1376
|
-
|
|
1385
|
+
useEffect2(() => {
|
|
1377
1386
|
injectKeyframes();
|
|
1378
1387
|
}, []);
|
|
1379
1388
|
return /* @__PURE__ */ jsxs("div", { style: styles.processingContainer, children: [
|
|
@@ -1743,7 +1752,7 @@ function getIcon(type) {
|
|
|
1743
1752
|
}
|
|
1744
1753
|
|
|
1745
1754
|
// src/components/DocumentCaptureScreen.tsx
|
|
1746
|
-
import { useRef, useEffect as
|
|
1755
|
+
import { useRef as useRef2, useEffect as useEffect3, useState as useState3, useCallback as useCallback2 } from "react";
|
|
1747
1756
|
import { Fragment, jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1748
1757
|
var qualityIssueMessages = {
|
|
1749
1758
|
face_blurred: "Photo on document is blurry. Retake in better lighting.",
|
|
@@ -1763,9 +1772,9 @@ function DocumentCaptureScreen({
|
|
|
1763
1772
|
onCapture,
|
|
1764
1773
|
onCancel
|
|
1765
1774
|
}) {
|
|
1766
|
-
const videoRef =
|
|
1767
|
-
const canvasRef =
|
|
1768
|
-
const guideRef =
|
|
1775
|
+
const videoRef = useRef2(null);
|
|
1776
|
+
const canvasRef = useRef2(null);
|
|
1777
|
+
const guideRef = useRef2(null);
|
|
1769
1778
|
const [stream, setStream] = useState3(null);
|
|
1770
1779
|
const [isCapturing, setIsCapturing] = useState3(false);
|
|
1771
1780
|
const [error, setError] = useState3(null);
|
|
@@ -1774,10 +1783,10 @@ function DocumentCaptureScreen({
|
|
|
1774
1783
|
const [qualityResult, setQualityResult] = useState3(null);
|
|
1775
1784
|
const [isCheckingQuality, setIsCheckingQuality] = useState3(false);
|
|
1776
1785
|
const [retakeCount, setRetakeCount] = useState3(0);
|
|
1777
|
-
|
|
1786
|
+
useEffect3(() => {
|
|
1778
1787
|
injectKeyframes();
|
|
1779
1788
|
}, []);
|
|
1780
|
-
|
|
1789
|
+
useEffect3(() => {
|
|
1781
1790
|
let mounted = true;
|
|
1782
1791
|
async function startCamera() {
|
|
1783
1792
|
try {
|
|
@@ -1799,7 +1808,7 @@ function DocumentCaptureScreen({
|
|
|
1799
1808
|
mounted = false;
|
|
1800
1809
|
};
|
|
1801
1810
|
}, [capturedImage]);
|
|
1802
|
-
|
|
1811
|
+
useEffect3(() => {
|
|
1803
1812
|
return () => {
|
|
1804
1813
|
stream?.getTracks().forEach((t) => t.stop());
|
|
1805
1814
|
};
|
|
@@ -2042,10 +2051,10 @@ function QualityCheck({ label }) {
|
|
|
2042
2051
|
}
|
|
2043
2052
|
|
|
2044
2053
|
// src/components/FlipDocumentScreen.tsx
|
|
2045
|
-
import { useEffect as
|
|
2054
|
+
import { useEffect as useEffect4 } from "react";
|
|
2046
2055
|
import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
2047
2056
|
function FlipDocumentScreen({ onContinue, onCancel }) {
|
|
2048
|
-
|
|
2057
|
+
useEffect4(() => {
|
|
2049
2058
|
injectKeyframes();
|
|
2050
2059
|
}, []);
|
|
2051
2060
|
return /* @__PURE__ */ jsxs6("div", { style: styles.darkContainer, children: [
|
|
@@ -2109,20 +2118,20 @@ function FlipDocumentScreen({ onContinue, onCancel }) {
|
|
|
2109
2118
|
}
|
|
2110
2119
|
|
|
2111
2120
|
// src/components/SelfieCaptureScreen.tsx
|
|
2112
|
-
import { useRef as
|
|
2121
|
+
import { useRef as useRef3, useEffect as useEffect5, useState as useState4, useCallback as useCallback3 } from "react";
|
|
2113
2122
|
import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
2114
2123
|
function SelfieCaptureScreen({ onCapture, onCancel }) {
|
|
2115
|
-
const videoRef =
|
|
2116
|
-
const canvasRef =
|
|
2124
|
+
const videoRef = useRef3(null);
|
|
2125
|
+
const canvasRef = useRef3(null);
|
|
2117
2126
|
const [stream, setStream] = useState4(null);
|
|
2118
2127
|
const [isCapturing, setIsCapturing] = useState4(false);
|
|
2119
2128
|
const [error, setError] = useState4(null);
|
|
2120
2129
|
const [capturedImage, setCapturedImage] = useState4(null);
|
|
2121
2130
|
const [capturedBlob, setCapturedBlob] = useState4(null);
|
|
2122
|
-
|
|
2131
|
+
useEffect5(() => {
|
|
2123
2132
|
injectKeyframes();
|
|
2124
2133
|
}, []);
|
|
2125
|
-
|
|
2134
|
+
useEffect5(() => {
|
|
2126
2135
|
let mounted = true;
|
|
2127
2136
|
async function startCamera() {
|
|
2128
2137
|
try {
|
|
@@ -2144,7 +2153,7 @@ function SelfieCaptureScreen({ onCapture, onCancel }) {
|
|
|
2144
2153
|
mounted = false;
|
|
2145
2154
|
};
|
|
2146
2155
|
}, [capturedImage]);
|
|
2147
|
-
|
|
2156
|
+
useEffect5(() => {
|
|
2148
2157
|
return () => {
|
|
2149
2158
|
stream?.getTracks().forEach((t) => t.stop());
|
|
2150
2159
|
};
|
|
@@ -2281,7 +2290,7 @@ function QualityCheck2({ label }) {
|
|
|
2281
2290
|
}
|
|
2282
2291
|
|
|
2283
2292
|
// src/components/LivenessScreen.tsx
|
|
2284
|
-
import { useEffect as
|
|
2293
|
+
import { useEffect as useEffect6, useRef as useRef4, useState as useState5, useCallback as useCallback4 } from "react";
|
|
2285
2294
|
import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
2286
2295
|
function LivenessScreen({
|
|
2287
2296
|
session,
|
|
@@ -2292,32 +2301,102 @@ function LivenessScreen({
|
|
|
2292
2301
|
onComplete,
|
|
2293
2302
|
onCancel
|
|
2294
2303
|
}) {
|
|
2304
|
+
const videoRef = useRef4(null);
|
|
2305
|
+
const canvasRef = useRef4(null);
|
|
2306
|
+
const [stream, setStream] = useState5(null);
|
|
2307
|
+
const [cameraError, setCameraError] = useState5(null);
|
|
2295
2308
|
const [countdown, setCountdown] = useState5(3);
|
|
2296
|
-
|
|
2309
|
+
const [capturing, setCapturing] = useState5(false);
|
|
2310
|
+
useEffect6(() => {
|
|
2297
2311
|
injectKeyframes();
|
|
2298
2312
|
}, []);
|
|
2299
|
-
|
|
2313
|
+
useEffect6(() => {
|
|
2300
2314
|
if (!session) onStart();
|
|
2301
2315
|
}, [session, onStart]);
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2316
|
+
useEffect6(() => {
|
|
2317
|
+
let mounted = true;
|
|
2318
|
+
async function startCamera() {
|
|
2319
|
+
try {
|
|
2320
|
+
const mediaStream = await navigator.mediaDevices.getUserMedia({
|
|
2321
|
+
video: {
|
|
2322
|
+
facingMode: "user",
|
|
2323
|
+
width: { ideal: 720 },
|
|
2324
|
+
height: { ideal: 720 }
|
|
2325
|
+
}
|
|
2326
|
+
});
|
|
2327
|
+
if (!mounted) {
|
|
2328
|
+
mediaStream.getTracks().forEach((t) => t.stop());
|
|
2329
|
+
return;
|
|
2330
|
+
}
|
|
2331
|
+
setStream(mediaStream);
|
|
2332
|
+
if (videoRef.current) {
|
|
2333
|
+
videoRef.current.srcObject = mediaStream;
|
|
2334
|
+
}
|
|
2335
|
+
} catch {
|
|
2336
|
+
if (mounted) {
|
|
2337
|
+
setCameraError(
|
|
2338
|
+
"Camera access denied. Please enable camera permissions and try again."
|
|
2339
|
+
);
|
|
2340
|
+
}
|
|
2341
|
+
}
|
|
2305
2342
|
}
|
|
2306
|
-
|
|
2307
|
-
|
|
2343
|
+
startCamera();
|
|
2344
|
+
return () => {
|
|
2345
|
+
mounted = false;
|
|
2346
|
+
};
|
|
2347
|
+
}, []);
|
|
2348
|
+
useEffect6(() => {
|
|
2349
|
+
return () => {
|
|
2350
|
+
stream?.getTracks().forEach((t) => t.stop());
|
|
2351
|
+
};
|
|
2352
|
+
}, [stream]);
|
|
2353
|
+
useEffect6(() => {
|
|
2308
2354
|
if (!currentChallenge) return;
|
|
2309
2355
|
setCountdown(3);
|
|
2310
|
-
const interval = setInterval(() => {
|
|
2311
|
-
setCountdown((c) => {
|
|
2312
|
-
if (c <= 1) {
|
|
2313
|
-
clearInterval(interval);
|
|
2314
|
-
return 0;
|
|
2315
|
-
}
|
|
2316
|
-
return c - 1;
|
|
2317
|
-
});
|
|
2318
|
-
}, 1e3);
|
|
2319
|
-
return () => clearInterval(interval);
|
|
2320
2356
|
}, [currentChallenge?.id]);
|
|
2357
|
+
const captureFrame = useCallback4(async () => {
|
|
2358
|
+
if (!currentChallenge || !videoRef.current || !canvasRef.current || capturing) {
|
|
2359
|
+
return;
|
|
2360
|
+
}
|
|
2361
|
+
const video = videoRef.current;
|
|
2362
|
+
const canvas = canvasRef.current;
|
|
2363
|
+
const ctx = canvas.getContext("2d");
|
|
2364
|
+
if (!ctx || video.videoWidth === 0 || video.videoHeight === 0) return;
|
|
2365
|
+
setCapturing(true);
|
|
2366
|
+
canvas.width = video.videoWidth;
|
|
2367
|
+
canvas.height = video.videoHeight;
|
|
2368
|
+
ctx.drawImage(video, 0, 0);
|
|
2369
|
+
canvas.toBlob(
|
|
2370
|
+
async (blob) => {
|
|
2371
|
+
if (blob) {
|
|
2372
|
+
await onChallengeComplete(blob);
|
|
2373
|
+
}
|
|
2374
|
+
setCapturing(false);
|
|
2375
|
+
},
|
|
2376
|
+
"image/jpeg",
|
|
2377
|
+
0.85
|
|
2378
|
+
);
|
|
2379
|
+
}, [currentChallenge, capturing, onChallengeComplete]);
|
|
2380
|
+
useEffect6(() => {
|
|
2381
|
+
if (!currentChallenge || capturing) return;
|
|
2382
|
+
if (countdown === 0) {
|
|
2383
|
+
captureFrame();
|
|
2384
|
+
return;
|
|
2385
|
+
}
|
|
2386
|
+
const t = setTimeout(() => setCountdown((c) => c - 1), 1e3);
|
|
2387
|
+
return () => clearTimeout(t);
|
|
2388
|
+
}, [countdown, currentChallenge?.id, capturing, captureFrame]);
|
|
2389
|
+
useEffect6(() => {
|
|
2390
|
+
if (session && !currentChallenge && completedChallenges > 0) {
|
|
2391
|
+
onComplete();
|
|
2392
|
+
}
|
|
2393
|
+
}, [session, currentChallenge, completedChallenges, onComplete]);
|
|
2394
|
+
if (cameraError) {
|
|
2395
|
+
return /* @__PURE__ */ jsx9("div", { style: styles.darkContainer, children: /* @__PURE__ */ jsxs8("div", { style: styles.errorContainer, children: [
|
|
2396
|
+
/* @__PURE__ */ jsx9("p", { style: styles.errorText, children: cameraError }),
|
|
2397
|
+
/* @__PURE__ */ jsx9("button", { style: styles.primaryButton, onClick: onCancel, children: "Go back" })
|
|
2398
|
+
] }) });
|
|
2399
|
+
}
|
|
2321
2400
|
if (!session) {
|
|
2322
2401
|
return /* @__PURE__ */ jsx9("div", { style: styles.darkContainer, children: /* @__PURE__ */ jsxs8("div", { style: styles.loadingContainer, children: [
|
|
2323
2402
|
/* @__PURE__ */ jsx9("div", { style: styles.spinner }),
|
|
@@ -2333,33 +2412,91 @@ function LivenessScreen({
|
|
|
2333
2412
|
/* @__PURE__ */ jsx9("button", { style: styles.glassCloseButton, onClick: onCancel, children: "\u2715" })
|
|
2334
2413
|
] }),
|
|
2335
2414
|
currentChallenge && /* @__PURE__ */ jsx9("div", { style: { padding: "16px 0" }, children: /* @__PURE__ */ jsx9("h2", { style: styles.challengeTitle, children: currentChallenge.instruction }) }),
|
|
2336
|
-
/* @__PURE__ */
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
{
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2415
|
+
/* @__PURE__ */ jsxs8(
|
|
2416
|
+
"div",
|
|
2417
|
+
{
|
|
2418
|
+
style: {
|
|
2419
|
+
flex: 1,
|
|
2420
|
+
display: "flex",
|
|
2421
|
+
alignItems: "center",
|
|
2422
|
+
justifyContent: "center"
|
|
2423
|
+
},
|
|
2424
|
+
children: [
|
|
2425
|
+
/* @__PURE__ */ jsxs8("div", { style: { position: "relative" }, children: [
|
|
2426
|
+
/* @__PURE__ */ jsx9(
|
|
2427
|
+
"div",
|
|
2428
|
+
{
|
|
2429
|
+
style: {
|
|
2430
|
+
width: "240px",
|
|
2431
|
+
height: "300px",
|
|
2432
|
+
borderRadius: "50%",
|
|
2433
|
+
overflow: "hidden",
|
|
2434
|
+
backgroundColor: "#000",
|
|
2435
|
+
border: "3px solid rgba(255,255,255,0.2)"
|
|
2436
|
+
},
|
|
2437
|
+
children: /* @__PURE__ */ jsx9(
|
|
2438
|
+
"video",
|
|
2439
|
+
{
|
|
2440
|
+
ref: videoRef,
|
|
2441
|
+
autoPlay: true,
|
|
2442
|
+
playsInline: true,
|
|
2443
|
+
muted: true,
|
|
2444
|
+
style: {
|
|
2445
|
+
width: "100%",
|
|
2446
|
+
height: "100%",
|
|
2447
|
+
objectFit: "cover",
|
|
2448
|
+
transform: "scaleX(-1)"
|
|
2449
|
+
}
|
|
2450
|
+
}
|
|
2451
|
+
)
|
|
2452
|
+
}
|
|
2453
|
+
),
|
|
2454
|
+
/* @__PURE__ */ jsx9(
|
|
2455
|
+
"svg",
|
|
2456
|
+
{
|
|
2457
|
+
style: {
|
|
2458
|
+
position: "absolute",
|
|
2459
|
+
top: "-8px",
|
|
2460
|
+
left: "-8px",
|
|
2461
|
+
pointerEvents: "none"
|
|
2462
|
+
},
|
|
2463
|
+
width: "256",
|
|
2464
|
+
height: "316",
|
|
2465
|
+
viewBox: "0 0 256 316",
|
|
2466
|
+
children: /* @__PURE__ */ jsx9(
|
|
2467
|
+
"ellipse",
|
|
2468
|
+
{
|
|
2469
|
+
cx: "128",
|
|
2470
|
+
cy: "158",
|
|
2471
|
+
rx: "124",
|
|
2472
|
+
ry: "154",
|
|
2473
|
+
fill: "none",
|
|
2474
|
+
stroke: colors.teal,
|
|
2475
|
+
strokeWidth: "5",
|
|
2476
|
+
strokeDasharray: `${completedChallenges / totalChallenges * 880} 880`,
|
|
2477
|
+
transform: "rotate(-90 128 158)",
|
|
2478
|
+
strokeLinecap: "round"
|
|
2479
|
+
}
|
|
2480
|
+
)
|
|
2481
|
+
}
|
|
2482
|
+
),
|
|
2483
|
+
currentChallenge && countdown > 0 && !capturing && /* @__PURE__ */ jsx9("div", { style: styles.countdownBadge, children: countdown }),
|
|
2484
|
+
capturing && /* @__PURE__ */ jsx9(
|
|
2485
|
+
"div",
|
|
2486
|
+
{
|
|
2487
|
+
style: {
|
|
2488
|
+
...styles.countdownBadge,
|
|
2489
|
+
fontSize: "14px",
|
|
2490
|
+
padding: "8px 14px"
|
|
2491
|
+
},
|
|
2492
|
+
children: "Checking..."
|
|
2493
|
+
}
|
|
2494
|
+
)
|
|
2495
|
+
] }),
|
|
2496
|
+
/* @__PURE__ */ jsx9("canvas", { ref: canvasRef, style: { display: "none" } })
|
|
2497
|
+
]
|
|
2498
|
+
}
|
|
2499
|
+
),
|
|
2363
2500
|
/* @__PURE__ */ jsxs8("div", { style: { padding: "16px 0" }, children: [
|
|
2364
2501
|
/* @__PURE__ */ jsx9("div", { style: styles.progressDots, children: session.challenges.map((_, index) => /* @__PURE__ */ jsx9(
|
|
2365
2502
|
"div",
|
|
@@ -2373,24 +2510,21 @@ function LivenessScreen({
|
|
|
2373
2510
|
)) }),
|
|
2374
2511
|
/* @__PURE__ */ jsxs8("p", { style: styles.progressText, children: [
|
|
2375
2512
|
"Challenge ",
|
|
2376
|
-
completedChallenges + 1,
|
|
2377
|
-
" of
|
|
2513
|
+
Math.min(completedChallenges + 1, totalChallenges),
|
|
2514
|
+
" of",
|
|
2515
|
+
" ",
|
|
2378
2516
|
totalChallenges
|
|
2379
2517
|
] })
|
|
2380
2518
|
] }),
|
|
2381
|
-
/* @__PURE__ */ jsx9("div", { style: { padding: "
|
|
2382
|
-
"
|
|
2519
|
+
/* @__PURE__ */ jsx9("div", { style: { padding: "0 24px 24px", textAlign: "center" }, children: /* @__PURE__ */ jsx9(
|
|
2520
|
+
"p",
|
|
2383
2521
|
{
|
|
2384
|
-
style:
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
canvas.height = 100;
|
|
2389
|
-
canvas.toBlob(async (blob) => {
|
|
2390
|
-
if (blob) await onChallengeComplete(blob);
|
|
2391
|
-
});
|
|
2522
|
+
style: {
|
|
2523
|
+
color: "rgba(255,255,255,0.5)",
|
|
2524
|
+
fontSize: "13px",
|
|
2525
|
+
margin: 0
|
|
2392
2526
|
},
|
|
2393
|
-
children: "
|
|
2527
|
+
children: "Position your face inside the oval and follow the prompt."
|
|
2394
2528
|
}
|
|
2395
2529
|
) })
|
|
2396
2530
|
] });
|
|
@@ -2763,10 +2897,10 @@ function ErrorScreen({ error, onRetry, onCancel }) {
|
|
|
2763
2897
|
}
|
|
2764
2898
|
|
|
2765
2899
|
// src/components/LoadingScreen.tsx
|
|
2766
|
-
import { useEffect as
|
|
2900
|
+
import { useEffect as useEffect7 } from "react";
|
|
2767
2901
|
import { jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
2768
2902
|
function LoadingScreen({ message = "Loading..." }) {
|
|
2769
|
-
|
|
2903
|
+
useEffect7(() => {
|
|
2770
2904
|
injectKeyframes();
|
|
2771
2905
|
}, []);
|
|
2772
2906
|
return /* @__PURE__ */ jsx12("div", { style: styles.container, children: /* @__PURE__ */ jsxs11("div", { style: styles.loadingContainer, children: [
|
|
@@ -2823,20 +2957,20 @@ function VerificationFlow({
|
|
|
2823
2957
|
const [showFlipInstruction, setShowFlipInstruction] = useState6(true);
|
|
2824
2958
|
const [supportedCountries, setSupportedCountries] = useState6([]);
|
|
2825
2959
|
const [countriesLoading, setCountriesLoading] = useState6(false);
|
|
2826
|
-
|
|
2960
|
+
useEffect8(() => {
|
|
2827
2961
|
if (state.step === "document_front") {
|
|
2828
2962
|
setShowFlipInstruction(true);
|
|
2829
2963
|
}
|
|
2830
2964
|
}, [state.step]);
|
|
2831
|
-
|
|
2965
|
+
useEffect8(() => {
|
|
2832
2966
|
startVerification(externalId, tier);
|
|
2833
2967
|
}, [externalId, tier, startVerification]);
|
|
2834
|
-
|
|
2968
|
+
useEffect8(() => {
|
|
2835
2969
|
if (state.step === "complete" && state.verification && onComplete) {
|
|
2836
2970
|
onComplete(state.verification);
|
|
2837
2971
|
}
|
|
2838
2972
|
}, [state.step, state.verification, onComplete]);
|
|
2839
|
-
|
|
2973
|
+
useEffect8(() => {
|
|
2840
2974
|
if (state.error && onError) {
|
|
2841
2975
|
onError(state.error);
|
|
2842
2976
|
}
|
|
@@ -2972,7 +3106,7 @@ function VerificationFlow({
|
|
|
2972
3106
|
}
|
|
2973
3107
|
|
|
2974
3108
|
// src/components/QrHandoffScreen.tsx
|
|
2975
|
-
import { useEffect as
|
|
3109
|
+
import { useEffect as useEffect9, useState as useState7, useRef as useRef5 } from "react";
|
|
2976
3110
|
import { jsx as jsx14, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
2977
3111
|
function QrHandoffScreen({
|
|
2978
3112
|
session,
|
|
@@ -2985,8 +3119,8 @@ function QrHandoffScreen({
|
|
|
2985
3119
|
const [timeLeft, setTimeLeft] = useState7(session.expiresIn);
|
|
2986
3120
|
const [scanned, setScanned] = useState7(false);
|
|
2987
3121
|
const [expired, setExpired] = useState7(false);
|
|
2988
|
-
const timerRef =
|
|
2989
|
-
|
|
3122
|
+
const timerRef = useRef5();
|
|
3123
|
+
useEffect9(() => {
|
|
2990
3124
|
setTimeLeft(session.expiresIn);
|
|
2991
3125
|
setExpired(false);
|
|
2992
3126
|
setScanned(false);
|
|
@@ -3003,7 +3137,7 @@ function QrHandoffScreen({
|
|
|
3003
3137
|
}, 1e3);
|
|
3004
3138
|
return () => clearInterval(timerRef.current);
|
|
3005
3139
|
}, [session.token]);
|
|
3006
|
-
|
|
3140
|
+
useEffect9(() => {
|
|
3007
3141
|
if (!eventSource) return;
|
|
3008
3142
|
const handleStatus = (event) => {
|
|
3009
3143
|
try {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@koraidv/react",
|
|
3
|
-
"version": "1.7.
|
|
3
|
+
"version": "1.7.7",
|
|
4
4
|
"description": "Kora IDV React Components for Identity Verification",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"test": "vitest run"
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@koraidv/core": "^1.7.
|
|
24
|
+
"@koraidv/core": "^1.7.7"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
27
|
"@types/react": "^18.2.0",
|