@neofaceid/web-sdk 2.0.1 → 2.1.1
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 +5 -3
- package/dist/neoface-id-sdk.es.js +100 -15
- package/dist/neoface-id-sdk.umd.js +1 -1
- package/package.json +5 -1
package/dist/index.d.ts
CHANGED
|
@@ -729,7 +729,9 @@ export declare enum ErrorType {
|
|
|
729
729
|
UNKNOWN = "UnknownError",
|
|
730
730
|
CONSENT_DENIED = "ConsentDeniedError",
|
|
731
731
|
/** NEO-224 · piscada real (EAR) não detectada dentro da janela de liveness. */
|
|
732
|
-
LIVENESS_BLINK_MISSING = "LivenessBlinkMissingError"
|
|
732
|
+
LIVENESS_BLINK_MISSING = "LivenessBlinkMissingError",
|
|
733
|
+
/** NEO-230 · polegar para cima não detectado na janela de authorizeOperation. */
|
|
734
|
+
GESTURE_NOT_DETECTED = "GestureNotDetectedError"
|
|
733
735
|
}
|
|
734
736
|
|
|
735
737
|
export declare function FaceCaptureModal({ accessToken, onClose, onSuccess, onCannotGesture, autoLighting, }: FaceCaptureModalProps): JSX_2.Element;
|
|
@@ -1391,7 +1393,7 @@ export declare const registerPersonWithoutFace: (personData: {
|
|
|
1391
1393
|
/**
|
|
1392
1394
|
* Data de lançamento da versão atual
|
|
1393
1395
|
*/
|
|
1394
|
-
export declare const RELEASE_DATE = "2026-09-
|
|
1396
|
+
export declare const RELEASE_DATE = "2026-09-23";
|
|
1395
1397
|
|
|
1396
1398
|
declare interface RequestChallengeWithSessionParams {
|
|
1397
1399
|
applicationToken: string;
|
|
@@ -1864,7 +1866,7 @@ export declare const validateToken: (applicationToken: string) => Promise<boolea
|
|
|
1864
1866
|
* MINOR: Incrementado quando adicionamos funcionalidades mantendo compatibilidade
|
|
1865
1867
|
* PATCH: Incrementado quando corrigimos bugs mantendo compatibilidade
|
|
1866
1868
|
*/
|
|
1867
|
-
export declare const VERSION = "2.
|
|
1869
|
+
export declare const VERSION = "2.1.1";
|
|
1868
1870
|
|
|
1869
1871
|
/**
|
|
1870
1872
|
* Executa `fn(sessionId)`. Se o servidor devolver 410 (sessão consumida/expirada),
|
|
@@ -162,6 +162,7 @@ var ErrorType = /* @__PURE__ */ ((ErrorType2) => {
|
|
|
162
162
|
ErrorType2["UNKNOWN"] = "UnknownError";
|
|
163
163
|
ErrorType2["CONSENT_DENIED"] = "ConsentDeniedError";
|
|
164
164
|
ErrorType2["LIVENESS_BLINK_MISSING"] = "LivenessBlinkMissingError";
|
|
165
|
+
ErrorType2["GESTURE_NOT_DETECTED"] = "GestureNotDetectedError";
|
|
165
166
|
return ErrorType2;
|
|
166
167
|
})(ErrorType || {});
|
|
167
168
|
class NeoFaceError extends Error {
|
|
@@ -201,6 +202,8 @@ class NeoFaceError extends Error {
|
|
|
201
202
|
return "É necessário aceitar o uso do seu rosto para continuar.";
|
|
202
203
|
case "LivenessBlinkMissingError":
|
|
203
204
|
return "Não detectamos uma piscada. Pisque os olhos naturalmente e tente novamente.";
|
|
205
|
+
case "GestureNotDetectedError":
|
|
206
|
+
return "Não detectamos o polegar para cima. Mostre o gesto claramente para a câmera e tente novamente.";
|
|
204
207
|
case "ApiError":
|
|
205
208
|
if (this.message.includes("400")) return "Requisição inválida. Tente novamente.";
|
|
206
209
|
if (this.message.includes("500"))
|
|
@@ -6095,7 +6098,7 @@ const recordFaceVideo = async (options = {}) => {
|
|
|
6095
6098
|
const pollTaskStatus = async (taskId, applicationToken, options = {}) => {
|
|
6096
6099
|
var _a2, _b, _c, _d, _e, _f, _g, _h, _i, _j;
|
|
6097
6100
|
const { maxAttempts = 60, intervalMs = 1e3, onStatusChange } = options;
|
|
6098
|
-
const
|
|
6101
|
+
const wait2 = (ms = intervalMs) => new Promise((resolve) => {
|
|
6099
6102
|
setTimeout(resolve, ms);
|
|
6100
6103
|
});
|
|
6101
6104
|
const retryAfterMs = (response) => {
|
|
@@ -6131,12 +6134,12 @@ const pollTaskStatus = async (taskId, applicationToken, options = {}) => {
|
|
|
6131
6134
|
}
|
|
6132
6135
|
if (response.status >= 500) {
|
|
6133
6136
|
lastTransient = `HTTP ${response.status}`;
|
|
6134
|
-
await
|
|
6137
|
+
await wait2();
|
|
6135
6138
|
continue;
|
|
6136
6139
|
}
|
|
6137
6140
|
if (response.status === 429) {
|
|
6138
6141
|
lastTransient = "HTTP 429";
|
|
6139
|
-
await
|
|
6142
|
+
await wait2(retryAfterMs(response));
|
|
6140
6143
|
continue;
|
|
6141
6144
|
}
|
|
6142
6145
|
if (response.status === 401 || response.status === 403) {
|
|
@@ -6165,13 +6168,13 @@ const pollTaskStatus = async (taskId, applicationToken, options = {}) => {
|
|
|
6165
6168
|
const reason = ((_h = (_g = data.data) == null ? void 0 : _g.result) == null ? void 0 : _h.message) || ((_i = data.data) == null ? void 0 : _i.error) || ((_j = data.data) == null ? void 0 : _j.message) || data.message || "Task failed";
|
|
6166
6169
|
throw new NeoFaceError(reason, ErrorType.RECOGNITION_FAILED);
|
|
6167
6170
|
}
|
|
6168
|
-
await
|
|
6171
|
+
await wait2();
|
|
6169
6172
|
} catch (error) {
|
|
6170
6173
|
if (error instanceof NeoFaceError) {
|
|
6171
6174
|
throw error;
|
|
6172
6175
|
}
|
|
6173
6176
|
lastTransient = error instanceof Error ? error.message : "erro desconhecido";
|
|
6174
|
-
await
|
|
6177
|
+
await wait2();
|
|
6175
6178
|
}
|
|
6176
6179
|
}
|
|
6177
6180
|
throw new NeoFaceError(
|
|
@@ -6633,9 +6636,9 @@ function ScreenFlashOverlay({
|
|
|
6633
6636
|
] })
|
|
6634
6637
|
] });
|
|
6635
6638
|
}
|
|
6636
|
-
const POLL_INTERVAL_MS = 50;
|
|
6639
|
+
const POLL_INTERVAL_MS$1 = 50;
|
|
6637
6640
|
const MIN_HORIZONTAL_DISTANCE = 1e-6;
|
|
6638
|
-
function distance(a, b) {
|
|
6641
|
+
function distance$1(a, b) {
|
|
6639
6642
|
return Math.hypot(a.x - b.x, a.y - b.y);
|
|
6640
6643
|
}
|
|
6641
6644
|
function calculateEAR(eyePoints) {
|
|
@@ -6643,9 +6646,9 @@ function calculateEAR(eyePoints) {
|
|
|
6643
6646
|
throw new Error(`calculateEAR espera exatamente 6 pontos do olho, recebeu ${eyePoints.length}`);
|
|
6644
6647
|
}
|
|
6645
6648
|
const [p1, p2, p3, p4, p5, p6] = eyePoints;
|
|
6646
|
-
const verticalA = distance(p2, p6);
|
|
6647
|
-
const verticalB = distance(p3, p5);
|
|
6648
|
-
const horizontal = distance(p1, p4);
|
|
6649
|
+
const verticalA = distance$1(p2, p6);
|
|
6650
|
+
const verticalB = distance$1(p3, p5);
|
|
6651
|
+
const horizontal = distance$1(p1, p4);
|
|
6649
6652
|
if (horizontal < MIN_HORIZONTAL_DISTANCE) return 0;
|
|
6650
6653
|
return (verticalA + verticalB) / (2 * horizontal);
|
|
6651
6654
|
}
|
|
@@ -6682,9 +6685,9 @@ function detectBlinkInWindow(faceapi2, videoEl, windowMs = 3e3, threshold = 0.2,
|
|
|
6682
6685
|
return;
|
|
6683
6686
|
}
|
|
6684
6687
|
}
|
|
6685
|
-
setTimeout(checkFrame, POLL_INTERVAL_MS);
|
|
6688
|
+
setTimeout(checkFrame, POLL_INTERVAL_MS$1);
|
|
6686
6689
|
}).catch(() => {
|
|
6687
|
-
setTimeout(checkFrame, POLL_INTERVAL_MS);
|
|
6690
|
+
setTimeout(checkFrame, POLL_INTERVAL_MS$1);
|
|
6688
6691
|
});
|
|
6689
6692
|
};
|
|
6690
6693
|
checkFrame();
|
|
@@ -8916,8 +8919,8 @@ function BiometricRegistrationModal({
|
|
|
8916
8919
|
/* @__PURE__ */ jsx("canvas", { ref: canvasRef })
|
|
8917
8920
|
] });
|
|
8918
8921
|
}
|
|
8919
|
-
const VERSION = "2.
|
|
8920
|
-
const RELEASE_DATE = "2026-09-
|
|
8922
|
+
const VERSION = "2.1.1";
|
|
8923
|
+
const RELEASE_DATE = "2026-09-23";
|
|
8921
8924
|
const CONSENT_TRAIL_STORAGE_KEY = "neoface_consent_trail_v1";
|
|
8922
8925
|
const CONSENT_TRAIL_MAX_LIMIT = 100;
|
|
8923
8926
|
async function hashString(inputStr) {
|
|
@@ -13325,6 +13328,73 @@ async function registerDocumentByImage(personId, jwtToken, options = {}) {
|
|
|
13325
13328
|
if (captured.backImage) images.push(captured.backImage);
|
|
13326
13329
|
return registerDocumentByImage$1(personId, jwtToken, images);
|
|
13327
13330
|
}
|
|
13331
|
+
const WRIST = 0;
|
|
13332
|
+
const THUMB_MCP = 2;
|
|
13333
|
+
const THUMB_TIP = 4;
|
|
13334
|
+
const OTHER_FINGERS = [
|
|
13335
|
+
{ tip: 8, pip: 6 },
|
|
13336
|
+
// indicador
|
|
13337
|
+
{ tip: 12, pip: 10 },
|
|
13338
|
+
// médio
|
|
13339
|
+
{ tip: 16, pip: 14 },
|
|
13340
|
+
// anelar
|
|
13341
|
+
{ tip: 20, pip: 18 }
|
|
13342
|
+
// mindinho
|
|
13343
|
+
];
|
|
13344
|
+
const distance = (a, b) => Math.hypot(a.x - b.x, a.y - b.y);
|
|
13345
|
+
function isThumbsUp(keypoints) {
|
|
13346
|
+
if (keypoints.length < 21) return false;
|
|
13347
|
+
const wrist = keypoints[WRIST];
|
|
13348
|
+
const thumbMcp = keypoints[THUMB_MCP];
|
|
13349
|
+
const thumbTip = keypoints[THUMB_TIP];
|
|
13350
|
+
const thumbExtended = distance(thumbTip, wrist) > distance(thumbMcp, wrist) * 1.3;
|
|
13351
|
+
if (!thumbExtended) return false;
|
|
13352
|
+
return OTHER_FINGERS.every(({ tip, pip }) => {
|
|
13353
|
+
const tipPoint = keypoints[tip];
|
|
13354
|
+
const pipPoint = keypoints[pip];
|
|
13355
|
+
return distance(tipPoint, wrist) < distance(pipPoint, wrist);
|
|
13356
|
+
});
|
|
13357
|
+
}
|
|
13358
|
+
const POLL_INTERVAL_MS = 150;
|
|
13359
|
+
let detectorPromise = null;
|
|
13360
|
+
const MEDIAPIPE_HANDS_VERSION = "0.4.1675469240";
|
|
13361
|
+
async function createDetector() {
|
|
13362
|
+
const handPoseDetection = await import("@tensorflow-models/hand-pose-detection");
|
|
13363
|
+
return handPoseDetection.createDetector(handPoseDetection.SupportedModels.MediaPipeHands, {
|
|
13364
|
+
runtime: "mediapipe",
|
|
13365
|
+
modelType: "lite",
|
|
13366
|
+
maxHands: 1,
|
|
13367
|
+
solutionPath: `https://cdn.jsdelivr.net/npm/@mediapipe/hands@${MEDIAPIPE_HANDS_VERSION}`
|
|
13368
|
+
});
|
|
13369
|
+
}
|
|
13370
|
+
function getDetector() {
|
|
13371
|
+
if (!detectorPromise) {
|
|
13372
|
+
detectorPromise = createDetector();
|
|
13373
|
+
}
|
|
13374
|
+
return detectorPromise;
|
|
13375
|
+
}
|
|
13376
|
+
const wait = (ms) => new Promise((resolve) => {
|
|
13377
|
+
setTimeout(resolve, ms);
|
|
13378
|
+
});
|
|
13379
|
+
async function preloadGestureDetector(videoEl) {
|
|
13380
|
+
const detector = await getDetector();
|
|
13381
|
+
try {
|
|
13382
|
+
await detector.estimateHands(videoEl, {});
|
|
13383
|
+
} catch {
|
|
13384
|
+
}
|
|
13385
|
+
}
|
|
13386
|
+
async function detectThumbsUp(videoEl, timeoutMs) {
|
|
13387
|
+
const detector = await getDetector();
|
|
13388
|
+
const deadline = Date.now() + timeoutMs;
|
|
13389
|
+
while (Date.now() < deadline) {
|
|
13390
|
+
const hands = await detector.estimateHands(videoEl, { flipHorizontal: true });
|
|
13391
|
+
if (hands.length > 0 && isThumbsUp(hands[0].keypoints)) {
|
|
13392
|
+
return true;
|
|
13393
|
+
}
|
|
13394
|
+
await wait(POLL_INTERVAL_MS);
|
|
13395
|
+
}
|
|
13396
|
+
return false;
|
|
13397
|
+
}
|
|
13328
13398
|
const PHOTO_INSTRUCTIONS = [
|
|
13329
13399
|
"Olhe diretamente para a câmera",
|
|
13330
13400
|
"Vire levemente a cabeça para a esquerda",
|
|
@@ -13332,6 +13402,7 @@ const PHOTO_INSTRUCTIONS = [
|
|
|
13332
13402
|
"Mostre o polegar para cima (👍) e olhe para a câmera"
|
|
13333
13403
|
];
|
|
13334
13404
|
const TOTAL_PHOTOS = 4;
|
|
13405
|
+
const GESTURE_TIMEOUT_MS = 5e3;
|
|
13335
13406
|
const authorizeOperation = async (applicationToken, cpf, options) => {
|
|
13336
13407
|
if (!applicationToken) {
|
|
13337
13408
|
throw new NeoFaceError("Application token is required", ErrorType.VALIDATION_ERROR);
|
|
@@ -13373,6 +13444,8 @@ const authorizeOperation = async (applicationToken, cpf, options) => {
|
|
|
13373
13444
|
resolve();
|
|
13374
13445
|
};
|
|
13375
13446
|
});
|
|
13447
|
+
preloadGestureDetector(video).catch(() => {
|
|
13448
|
+
});
|
|
13376
13449
|
const guideModal = showCameraOvalGuideModal({
|
|
13377
13450
|
stream,
|
|
13378
13451
|
faceState: "centered",
|
|
@@ -13393,7 +13466,19 @@ const authorizeOperation = async (applicationToken, cpf, options) => {
|
|
|
13393
13466
|
if (options == null ? void 0 : options.onProgress) {
|
|
13394
13467
|
options.onProgress(i + 1, TOTAL_PHOTOS, instruction);
|
|
13395
13468
|
}
|
|
13396
|
-
|
|
13469
|
+
const isGesturePhoto = i === TOTAL_PHOTOS - 1;
|
|
13470
|
+
if (isGesturePhoto) {
|
|
13471
|
+
const detected = await detectThumbsUp(video, GESTURE_TIMEOUT_MS);
|
|
13472
|
+
if (!detected) {
|
|
13473
|
+
guideModal.close();
|
|
13474
|
+
stream.getTracks().forEach((track) => track.stop());
|
|
13475
|
+
capturedPhotos.length = 0;
|
|
13476
|
+
throw new NeoFaceError(
|
|
13477
|
+
"Polegar para cima não detectado dentro do tempo limite.",
|
|
13478
|
+
ErrorType.GESTURE_NOT_DETECTED
|
|
13479
|
+
);
|
|
13480
|
+
}
|
|
13481
|
+
} else if (i > 0) {
|
|
13397
13482
|
await new Promise((resolve) => setTimeout(resolve, captureDelay));
|
|
13398
13483
|
}
|
|
13399
13484
|
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
|