@neofaceid/web-sdk 1.36.0 → 1.36.2
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.ts +62 -17
- package/dist/neoface-id-sdk.es.js +33 -42
- package/dist/neoface-id-sdk.umd.js +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -88,7 +88,12 @@ export declare interface AuthorizeOptions {
|
|
|
88
88
|
amount?: number;
|
|
89
89
|
onChallengeStart?: (gesture: ChallengeGesture, index: number, total: number) => void;
|
|
90
90
|
onChallengeComplete?: (gesture: ChallengeGesture, index: number, total: number) => void;
|
|
91
|
-
|
|
91
|
+
/**
|
|
92
|
+
* NEO-427: agora recebe o resultado da COLETA (sessionId + challenge + gestures
|
|
93
|
+
* capturadas). O SDK não decide aprovação — envie os frames via seu próprio
|
|
94
|
+
* pipeline de recognition (ex.: `POST /auth/recognition/face/`).
|
|
95
|
+
*/
|
|
96
|
+
onSuccess?: (result: RunLivenessChallengeCollectResult) => void;
|
|
92
97
|
onError?: (error: NeoFaceError) => void;
|
|
93
98
|
onCancel?: () => void;
|
|
94
99
|
/**
|
|
@@ -329,6 +334,11 @@ export declare interface CaptureSession {
|
|
|
329
334
|
export declare interface ChallengeGesture {
|
|
330
335
|
index: number;
|
|
331
336
|
gesture_key: GestureKey | string;
|
|
337
|
+
/**
|
|
338
|
+
* Instrução pronta pelo backend (v1.4.x, respeita `Accept-Language`).
|
|
339
|
+
* Nos payloads legados vinha via `instruction_i18n`.
|
|
340
|
+
*/
|
|
341
|
+
instruction?: string;
|
|
332
342
|
instruction_i18n?: Record<string, string>;
|
|
333
343
|
}
|
|
334
344
|
|
|
@@ -637,7 +647,12 @@ export declare class ForgotPasswordModal {
|
|
|
637
647
|
private addStyles;
|
|
638
648
|
}
|
|
639
649
|
|
|
640
|
-
|
|
650
|
+
/**
|
|
651
|
+
* Chaves de gesto reconhecidas pelo backend real (case-insensitive; SDK
|
|
652
|
+
* normaliza para maiúsculo). Novos gestos podem aparecer sem impacto no SDK
|
|
653
|
+
* — o campo é `string` na runtime.
|
|
654
|
+
*/
|
|
655
|
+
export declare type GestureKey = 'LOOK_LEFT' | 'LOOK_RIGHT' | 'LOOK_UP' | 'LOOK_DOWN' | 'BLINK' | 'SMILE' | 'OPEN_MOUTH' | 'turn_left' | 'turn_right' | 'look_up' | 'look_down' | 'blink' | 'smile' | 'open_mouth';
|
|
641
656
|
|
|
642
657
|
export declare interface GestureResponse {
|
|
643
658
|
gesture_key: string;
|
|
@@ -769,9 +784,18 @@ export declare function isAdvancedDetectionAvailable(): boolean;
|
|
|
769
784
|
export declare function isInitialized(): boolean;
|
|
770
785
|
|
|
771
786
|
export declare interface LivenessChallenge {
|
|
772
|
-
|
|
787
|
+
/**
|
|
788
|
+
* ID do desafio. **Opcional no contrato v1.4.x** — o backend real rastreia
|
|
789
|
+
* o desafio pelo `session_id` e não devolve `challenge_id` no payload.
|
|
790
|
+
* Mantido opcional para consumidores que ainda dependem do valor.
|
|
791
|
+
*/
|
|
792
|
+
challenge_id?: string;
|
|
773
793
|
gestures: ChallengeGesture[];
|
|
794
|
+
/** Data ISO derivada de `expires_in` (segundos) ou legado `expires_at`. */
|
|
774
795
|
expires_at?: string;
|
|
796
|
+
/** Segundos até expirar (contrato v1.4.x). */
|
|
797
|
+
expires_in?: number;
|
|
798
|
+
language?: string;
|
|
775
799
|
}
|
|
776
800
|
|
|
777
801
|
export declare interface LivenessChallengeResult {
|
|
@@ -1310,7 +1334,7 @@ export declare const registerPersonWithoutFace: (personData: {
|
|
|
1310
1334
|
/**
|
|
1311
1335
|
* Data de lançamento da versão atual
|
|
1312
1336
|
*/
|
|
1313
|
-
export declare const RELEASE_DATE = "2026-09-
|
|
1337
|
+
export declare const RELEASE_DATE = "2026-09-03";
|
|
1314
1338
|
|
|
1315
1339
|
declare interface RequestChallengeWithSessionParams {
|
|
1316
1340
|
applicationToken: string;
|
|
@@ -1367,16 +1391,33 @@ declare interface ResolvedTheme {
|
|
|
1367
1391
|
}
|
|
1368
1392
|
|
|
1369
1393
|
/**
|
|
1370
|
-
* Orquestra
|
|
1394
|
+
* Orquestra:
|
|
1371
1395
|
* 1. Abre `capture/session`
|
|
1372
|
-
* 2. Pede `liveness/challenge` (
|
|
1396
|
+
* 2. Pede `liveness/challenge` (backend sorteia)
|
|
1373
1397
|
* 3. Delega a coleta de frames ao consumidor gesto a gesto
|
|
1374
|
-
* 4.
|
|
1398
|
+
* 4. **Retorna a coleta** para o consumidor submeter no pipeline dele.
|
|
1375
1399
|
*
|
|
1376
|
-
* Retry: em 410
|
|
1377
|
-
*
|
|
1400
|
+
* Retry: em 410 ao abrir sessão/pedir desafio, reabre + refaz 1x. Segunda falha propaga.
|
|
1401
|
+
*
|
|
1402
|
+
* Nota: até 1.36.1 esta função chamava `submitLivenessChallenge` no fim, mas o
|
|
1403
|
+
* endpoint não existe no backend real. NEO-427 removeu essa chamada. Quando
|
|
1404
|
+
* NEO-428 identificar o endpoint real de submit, o consumidor pode usá-lo, ou
|
|
1405
|
+
* uma variante `runLivenessChallengeAndSubmit` pode ser reintroduzida.
|
|
1378
1406
|
*/
|
|
1379
|
-
export declare function runLivenessChallenge({ applicationToken, purpose, collectFramesForGesture, retryOnSessionExpired, }: RunLivenessChallengeParams): Promise<
|
|
1407
|
+
export declare function runLivenessChallenge({ applicationToken, purpose, collectFramesForGesture, retryOnSessionExpired, }: RunLivenessChallengeParams): Promise<RunLivenessChallengeCollectResult>;
|
|
1408
|
+
|
|
1409
|
+
/**
|
|
1410
|
+
* Resultado da coleta orquestrada. NEO-427: NÃO submete os frames automaticamente
|
|
1411
|
+
* (o endpoint de submit `/challenge/{id}/submit/` não existe no backend v1.4.x —
|
|
1412
|
+
* devolve 404). O consumidor recebe as gestures coletadas e envia via seu próprio
|
|
1413
|
+
* pipeline (ex.: `POST /auth/recognition/face/` com `challenge_response` no body).
|
|
1414
|
+
*/
|
|
1415
|
+
export declare interface RunLivenessChallengeCollectResult {
|
|
1416
|
+
sessionId: string;
|
|
1417
|
+
challenge: LivenessChallenge;
|
|
1418
|
+
/** Frames coletados por gesto, na ordem sorteada pelo servidor. */
|
|
1419
|
+
capturedGestures: GestureResponse[];
|
|
1420
|
+
}
|
|
1380
1421
|
|
|
1381
1422
|
export declare interface RunLivenessChallengeParams {
|
|
1382
1423
|
applicationToken: string;
|
|
@@ -1384,10 +1425,10 @@ export declare interface RunLivenessChallengeParams {
|
|
|
1384
1425
|
/**
|
|
1385
1426
|
* Callback fornecido pelo consumidor: para cada gesto na sequência sorteada
|
|
1386
1427
|
* pelo servidor, retorna os frames coletados. SDK NÃO decide se o gesto foi
|
|
1387
|
-
* cumprido — só coleta
|
|
1428
|
+
* cumprido — só coleta; validação é 100% servidor.
|
|
1388
1429
|
*/
|
|
1389
1430
|
collectFramesForGesture: (gesture: ChallengeGesture, index: number, total: number) => Promise<string[]>;
|
|
1390
|
-
/** Se true, quando o servidor devolver 410
|
|
1431
|
+
/** Se true, quando o servidor devolver 410 na abertura do desafio, reabre sessão + refaz 1x. */
|
|
1391
1432
|
retryOnSessionExpired?: boolean;
|
|
1392
1433
|
}
|
|
1393
1434
|
|
|
@@ -1583,14 +1624,18 @@ export declare interface StartOnboardingOptions {
|
|
|
1583
1624
|
}
|
|
1584
1625
|
|
|
1585
1626
|
/**
|
|
1586
|
-
*
|
|
1587
|
-
*
|
|
1588
|
-
*
|
|
1627
|
+
* @deprecated NEO-427: o endpoint `POST /liveness/challenge/{id}/submit/` NÃO
|
|
1628
|
+
* EXISTE no backend v1.4.x — devolve 404. `runLivenessChallenge` não chama mais
|
|
1629
|
+
* esta função. Mantida apenas para consumidores que ainda dependem do símbolo;
|
|
1630
|
+
* será removida quando o endpoint real de submit for identificado (NEO-428).
|
|
1631
|
+
*
|
|
1632
|
+
* Continua tentando o path histórico e propaga o erro do servidor.
|
|
1589
1633
|
*/
|
|
1590
1634
|
export declare function submitLivenessChallenge({ challengeId, sessionId, applicationToken, gestures, }: SubmitLivenessChallengeParams): Promise<LivenessChallengeResult>;
|
|
1591
1635
|
|
|
1592
1636
|
export declare interface SubmitLivenessChallengeParams {
|
|
1593
|
-
|
|
1637
|
+
/** Opcional no v1.4.x — backend rastreia via session_id. */
|
|
1638
|
+
challengeId?: string;
|
|
1594
1639
|
sessionId: string;
|
|
1595
1640
|
applicationToken: string;
|
|
1596
1641
|
gestures: GestureResponse[];
|
|
@@ -1650,7 +1695,7 @@ export declare const validateToken: (applicationToken: string) => Promise<boolea
|
|
|
1650
1695
|
* MINOR: Incrementado quando adicionamos funcionalidades mantendo compatibilidade
|
|
1651
1696
|
* PATCH: Incrementado quando corrigimos bugs mantendo compatibilidade
|
|
1652
1697
|
*/
|
|
1653
|
-
export declare const VERSION = "1.36.
|
|
1698
|
+
export declare const VERSION = "1.36.2";
|
|
1654
1699
|
|
|
1655
1700
|
/**
|
|
1656
1701
|
* Executa `fn(sessionId)`. Se o servidor devolver 410 (sessão consumida/expirada),
|
|
@@ -6560,10 +6560,8 @@ function FaceCaptureModal({
|
|
|
6560
6560
|
const canvasRef = useRef(null);
|
|
6561
6561
|
const streamRef = useRef(null);
|
|
6562
6562
|
const faceDetectionTimeoutRef = useRef(null);
|
|
6563
|
-
const livenessCheckRef = useRef(null);
|
|
6564
6563
|
const [state, dispatch] = useReducer(modalReducer$1, { status: "requestingPermission" });
|
|
6565
6564
|
const [faceDetected, setFaceDetected] = useState(false);
|
|
6566
|
-
const [livenessAttempts, setLivenessAttempts] = useState(0);
|
|
6567
6565
|
const { startCamera, stopCamera } = useCamera();
|
|
6568
6566
|
const isFaceCentered = (detection) => {
|
|
6569
6567
|
if (!videoRef.current) return false;
|
|
@@ -6575,14 +6573,6 @@ function FaceCaptureModal({
|
|
|
6575
6573
|
const centerHeight = videoHeight * 0.4;
|
|
6576
6574
|
return box.x + box.width / 2 > centerX - centerWidth / 2 && box.x + box.width / 2 < centerX + centerWidth / 2 && box.y + box.height / 2 > centerY - centerHeight / 2 && box.y + box.height / 2 < centerY + centerHeight / 2;
|
|
6577
6575
|
};
|
|
6578
|
-
const startLivenessCheck = () => {
|
|
6579
|
-
livenessCheckRef.current = window.setTimeout(() => {
|
|
6580
|
-
if (state.status === "ready") {
|
|
6581
|
-
setLivenessAttempts((prev) => prev + 1);
|
|
6582
|
-
dispatch({ type: "START_CAPTURE" });
|
|
6583
|
-
}
|
|
6584
|
-
}, 3e3);
|
|
6585
|
-
};
|
|
6586
6576
|
const startFaceDetection = () => {
|
|
6587
6577
|
if (!videoRef.current) return;
|
|
6588
6578
|
const detectFaces = async () => {
|
|
@@ -6595,9 +6585,6 @@ function FaceCaptureModal({
|
|
|
6595
6585
|
const isOneFace = detections.length === 1;
|
|
6596
6586
|
const isCentered = isOneFace && isFaceCentered(detections[0]);
|
|
6597
6587
|
setFaceDetected(isOneFace && isCentered);
|
|
6598
|
-
if (isOneFace && isCentered && livenessAttempts === 0) {
|
|
6599
|
-
startLivenessCheck();
|
|
6600
|
-
}
|
|
6601
6588
|
} catch (error) {
|
|
6602
6589
|
console.error("Face detection error:", error);
|
|
6603
6590
|
}
|
|
@@ -6626,9 +6613,6 @@ function FaceCaptureModal({
|
|
|
6626
6613
|
if (faceDetectionTimeoutRef.current) {
|
|
6627
6614
|
window.clearTimeout(faceDetectionTimeoutRef.current);
|
|
6628
6615
|
}
|
|
6629
|
-
if (livenessCheckRef.current) {
|
|
6630
|
-
window.clearInterval(livenessCheckRef.current);
|
|
6631
|
-
}
|
|
6632
6616
|
};
|
|
6633
6617
|
}, []);
|
|
6634
6618
|
useEffect(() => {
|
|
@@ -6695,7 +6679,6 @@ function FaceCaptureModal({
|
|
|
6695
6679
|
};
|
|
6696
6680
|
const handleRetry = () => {
|
|
6697
6681
|
dispatch({ type: "RESET" });
|
|
6698
|
-
setLivenessAttempts(0);
|
|
6699
6682
|
};
|
|
6700
6683
|
return /* @__PURE__ */ jsxs("div", { className: "neofaceid-root", style: styles.overlay, children: [
|
|
6701
6684
|
/* @__PURE__ */ jsx(ScreenFlashOverlay, { active: flashOn }),
|
|
@@ -11194,8 +11177,8 @@ const biometricDetection = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.
|
|
|
11194
11177
|
initializeBiometricDetection,
|
|
11195
11178
|
isAdvancedDetectionAvailable
|
|
11196
11179
|
}, Symbol.toStringTag, { value: "Module" }));
|
|
11197
|
-
const VERSION = "1.36.
|
|
11198
|
-
const RELEASE_DATE = "2026-09-
|
|
11180
|
+
const VERSION = "1.36.2";
|
|
11181
|
+
const RELEASE_DATE = "2026-09-03";
|
|
11199
11182
|
let cachedSession = null;
|
|
11200
11183
|
function getCachedCaptureSession() {
|
|
11201
11184
|
return cachedSession;
|
|
@@ -11322,18 +11305,33 @@ async function requestLivenessChallenge({
|
|
|
11322
11305
|
}
|
|
11323
11306
|
const raw = await response.json().catch(() => null);
|
|
11324
11307
|
const payload = (raw == null ? void 0 : raw.data) ?? raw;
|
|
11325
|
-
const
|
|
11326
|
-
const
|
|
11327
|
-
|
|
11308
|
+
const rawSteps = payload == null ? void 0 : payload.steps;
|
|
11309
|
+
const rawGestures = payload == null ? void 0 : payload.gestures;
|
|
11310
|
+
const source = Array.isArray(rawSteps) ? rawSteps : Array.isArray(rawGestures) ? rawGestures : void 0;
|
|
11311
|
+
if (!source || source.length === 0) {
|
|
11328
11312
|
throw new NeoFaceError(
|
|
11329
|
-
"Resposta inválida ao solicitar desafio (
|
|
11313
|
+
"Resposta inválida ao solicitar desafio (steps/gestures ausentes)",
|
|
11330
11314
|
ErrorType.API_ERROR
|
|
11331
11315
|
);
|
|
11332
11316
|
}
|
|
11317
|
+
const gestures = source.map((s, i) => {
|
|
11318
|
+
const item = s;
|
|
11319
|
+
const gestureRaw = item.gesture ?? item.gesture_key;
|
|
11320
|
+
return {
|
|
11321
|
+
index: typeof item.order === "number" ? item.order : typeof item.index === "number" ? item.index : i + 1,
|
|
11322
|
+
gesture_key: gestureRaw ?? "",
|
|
11323
|
+
instruction: typeof item.instruction === "string" ? item.instruction : void 0,
|
|
11324
|
+
instruction_i18n: item.instruction_i18n && typeof item.instruction_i18n === "object" ? item.instruction_i18n : void 0
|
|
11325
|
+
};
|
|
11326
|
+
});
|
|
11327
|
+
const expiresIn = typeof (payload == null ? void 0 : payload.expires_in) === "number" ? payload.expires_in : void 0;
|
|
11328
|
+
const expiresAt = typeof (payload == null ? void 0 : payload.expires_at) === "string" && payload.expires_at.length > 0 ? payload.expires_at : expiresIn !== void 0 ? new Date(Date.now() + expiresIn * 1e3).toISOString() : void 0;
|
|
11333
11329
|
return {
|
|
11334
|
-
challenge_id:
|
|
11330
|
+
challenge_id: typeof (payload == null ? void 0 : payload.challenge_id) === "string" ? payload.challenge_id : void 0,
|
|
11335
11331
|
gestures,
|
|
11336
|
-
expires_at:
|
|
11332
|
+
expires_at: expiresAt,
|
|
11333
|
+
expires_in: expiresIn,
|
|
11334
|
+
language: typeof (payload == null ? void 0 : payload.language) === "string" ? payload.language : void 0
|
|
11337
11335
|
};
|
|
11338
11336
|
}
|
|
11339
11337
|
async function requestLivenessChallengeWithSession({
|
|
@@ -11359,22 +11357,26 @@ async function submitLivenessChallenge({
|
|
|
11359
11357
|
applicationToken,
|
|
11360
11358
|
gestures
|
|
11361
11359
|
}) {
|
|
11360
|
+
console.warn(
|
|
11361
|
+
"[NeoFaceID SDK] submitLivenessChallenge está deprecated (NEO-427): o endpoint /liveness/challenge/{id}/submit/ não existe no backend v1.4.x. Coleta os frames via runLivenessChallenge e envie via seu próprio pipeline até que NEO-428 defina o endpoint real."
|
|
11362
|
+
);
|
|
11362
11363
|
if (!applicationToken) {
|
|
11363
11364
|
throw new NeoFaceError(
|
|
11364
11365
|
"applicationToken é obrigatório para submeter desafio",
|
|
11365
11366
|
ErrorType.INVALID_TOKEN
|
|
11366
11367
|
);
|
|
11367
11368
|
}
|
|
11368
|
-
if (!
|
|
11369
|
+
if (!sessionId) {
|
|
11369
11370
|
throw new NeoFaceError(
|
|
11370
|
-
"
|
|
11371
|
+
"session_id é obrigatório para submeter desafio",
|
|
11371
11372
|
ErrorType.VALIDATION_ERROR
|
|
11372
11373
|
);
|
|
11373
11374
|
}
|
|
11374
11375
|
if (!Array.isArray(gestures) || gestures.length === 0) {
|
|
11375
11376
|
throw new NeoFaceError("gestures[] vazio — nada para submeter", ErrorType.VALIDATION_ERROR);
|
|
11376
11377
|
}
|
|
11377
|
-
const
|
|
11378
|
+
const idSuffix = challengeId ? `${encodeURIComponent(challengeId)}/submit/` : "submit/";
|
|
11379
|
+
const url = `${getBaseUrl()}/api/v1/auth/liveness/challenge/${idSuffix}`;
|
|
11378
11380
|
let response;
|
|
11379
11381
|
try {
|
|
11380
11382
|
response = await fetch(url, {
|
|
@@ -11425,18 +11427,13 @@ async function runLivenessChallenge({
|
|
|
11425
11427
|
sessionId: session.session_id,
|
|
11426
11428
|
applicationToken
|
|
11427
11429
|
});
|
|
11428
|
-
const
|
|
11430
|
+
const capturedGestures = [];
|
|
11429
11431
|
for (let i = 0; i < challenge.gestures.length; i += 1) {
|
|
11430
11432
|
const g = challenge.gestures[i];
|
|
11431
11433
|
const images = await collectFramesForGesture(g, i, challenge.gestures.length);
|
|
11432
|
-
|
|
11434
|
+
capturedGestures.push({ gesture_key: String(g.gesture_key), images });
|
|
11433
11435
|
}
|
|
11434
|
-
return
|
|
11435
|
-
challengeId: challenge.challenge_id,
|
|
11436
|
-
sessionId: session.session_id,
|
|
11437
|
-
applicationToken,
|
|
11438
|
-
gestures
|
|
11439
|
-
});
|
|
11436
|
+
return { sessionId: session.session_id, challenge, capturedGestures };
|
|
11440
11437
|
};
|
|
11441
11438
|
try {
|
|
11442
11439
|
return await attempt();
|
|
@@ -12941,12 +12938,6 @@ function AuthorizeModalComponent({
|
|
|
12941
12938
|
});
|
|
12942
12939
|
if (stream) stream.getTracks().forEach((t) => t.stop());
|
|
12943
12940
|
stream = null;
|
|
12944
|
-
if (result.success === false || result.liveness_passed === false) {
|
|
12945
|
-
setFailureCode("LIVENESS_LOW");
|
|
12946
|
-
setFailureAt(/* @__PURE__ */ new Date());
|
|
12947
|
-
setPhase("failure");
|
|
12948
|
-
return;
|
|
12949
|
-
}
|
|
12950
12941
|
setPhase("success");
|
|
12951
12942
|
if (onSuccess) onSuccess(result);
|
|
12952
12943
|
} catch (err) {
|