@neofaceid/web-sdk 2.0.0 → 2.1.0
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 +114 -16
- 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.0";
|
|
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,9 +6098,22 @@ 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
|
|
6099
|
-
setTimeout(resolve,
|
|
6100
|
-
});
|
|
6101
|
+
const wait2 = (ms = intervalMs) => new Promise((resolve) => {
|
|
6102
|
+
setTimeout(resolve, ms);
|
|
6103
|
+
});
|
|
6104
|
+
const retryAfterMs = (response) => {
|
|
6105
|
+
var _a3, _b2;
|
|
6106
|
+
const raw = (_b2 = (_a3 = response.headers) == null ? void 0 : _a3.get) == null ? void 0 : _b2.call(_a3, "Retry-After");
|
|
6107
|
+
if (!raw) return intervalMs;
|
|
6108
|
+
const seconds = Number(raw);
|
|
6109
|
+
if (Number.isFinite(seconds) && seconds > 0) return seconds * 1e3;
|
|
6110
|
+
const date2 = Date.parse(raw);
|
|
6111
|
+
if (Number.isFinite(date2)) {
|
|
6112
|
+
const delta = date2 - Date.now();
|
|
6113
|
+
if (delta > 0) return delta;
|
|
6114
|
+
}
|
|
6115
|
+
return intervalMs;
|
|
6116
|
+
};
|
|
6101
6117
|
let attempts = 0;
|
|
6102
6118
|
let lastTransient = "";
|
|
6103
6119
|
while (attempts < maxAttempts) {
|
|
@@ -6118,7 +6134,12 @@ const pollTaskStatus = async (taskId, applicationToken, options = {}) => {
|
|
|
6118
6134
|
}
|
|
6119
6135
|
if (response.status >= 500) {
|
|
6120
6136
|
lastTransient = `HTTP ${response.status}`;
|
|
6121
|
-
await
|
|
6137
|
+
await wait2();
|
|
6138
|
+
continue;
|
|
6139
|
+
}
|
|
6140
|
+
if (response.status === 429) {
|
|
6141
|
+
lastTransient = "HTTP 429";
|
|
6142
|
+
await wait2(retryAfterMs(response));
|
|
6122
6143
|
continue;
|
|
6123
6144
|
}
|
|
6124
6145
|
if (response.status === 401 || response.status === 403) {
|
|
@@ -6147,13 +6168,13 @@ const pollTaskStatus = async (taskId, applicationToken, options = {}) => {
|
|
|
6147
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";
|
|
6148
6169
|
throw new NeoFaceError(reason, ErrorType.RECOGNITION_FAILED);
|
|
6149
6170
|
}
|
|
6150
|
-
await
|
|
6171
|
+
await wait2();
|
|
6151
6172
|
} catch (error) {
|
|
6152
6173
|
if (error instanceof NeoFaceError) {
|
|
6153
6174
|
throw error;
|
|
6154
6175
|
}
|
|
6155
6176
|
lastTransient = error instanceof Error ? error.message : "erro desconhecido";
|
|
6156
|
-
await
|
|
6177
|
+
await wait2();
|
|
6157
6178
|
}
|
|
6158
6179
|
}
|
|
6159
6180
|
throw new NeoFaceError(
|
|
@@ -6615,9 +6636,9 @@ function ScreenFlashOverlay({
|
|
|
6615
6636
|
] })
|
|
6616
6637
|
] });
|
|
6617
6638
|
}
|
|
6618
|
-
const POLL_INTERVAL_MS = 50;
|
|
6639
|
+
const POLL_INTERVAL_MS$1 = 50;
|
|
6619
6640
|
const MIN_HORIZONTAL_DISTANCE = 1e-6;
|
|
6620
|
-
function distance(a, b) {
|
|
6641
|
+
function distance$1(a, b) {
|
|
6621
6642
|
return Math.hypot(a.x - b.x, a.y - b.y);
|
|
6622
6643
|
}
|
|
6623
6644
|
function calculateEAR(eyePoints) {
|
|
@@ -6625,9 +6646,9 @@ function calculateEAR(eyePoints) {
|
|
|
6625
6646
|
throw new Error(`calculateEAR espera exatamente 6 pontos do olho, recebeu ${eyePoints.length}`);
|
|
6626
6647
|
}
|
|
6627
6648
|
const [p1, p2, p3, p4, p5, p6] = eyePoints;
|
|
6628
|
-
const verticalA = distance(p2, p6);
|
|
6629
|
-
const verticalB = distance(p3, p5);
|
|
6630
|
-
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);
|
|
6631
6652
|
if (horizontal < MIN_HORIZONTAL_DISTANCE) return 0;
|
|
6632
6653
|
return (verticalA + verticalB) / (2 * horizontal);
|
|
6633
6654
|
}
|
|
@@ -6664,9 +6685,9 @@ function detectBlinkInWindow(faceapi2, videoEl, windowMs = 3e3, threshold = 0.2,
|
|
|
6664
6685
|
return;
|
|
6665
6686
|
}
|
|
6666
6687
|
}
|
|
6667
|
-
setTimeout(checkFrame, POLL_INTERVAL_MS);
|
|
6688
|
+
setTimeout(checkFrame, POLL_INTERVAL_MS$1);
|
|
6668
6689
|
}).catch(() => {
|
|
6669
|
-
setTimeout(checkFrame, POLL_INTERVAL_MS);
|
|
6690
|
+
setTimeout(checkFrame, POLL_INTERVAL_MS$1);
|
|
6670
6691
|
});
|
|
6671
6692
|
};
|
|
6672
6693
|
checkFrame();
|
|
@@ -8898,8 +8919,8 @@ function BiometricRegistrationModal({
|
|
|
8898
8919
|
/* @__PURE__ */ jsx("canvas", { ref: canvasRef })
|
|
8899
8920
|
] });
|
|
8900
8921
|
}
|
|
8901
|
-
const VERSION = "2.
|
|
8902
|
-
const RELEASE_DATE = "2026-09-
|
|
8922
|
+
const VERSION = "2.1.0";
|
|
8923
|
+
const RELEASE_DATE = "2026-09-23";
|
|
8903
8924
|
const CONSENT_TRAIL_STORAGE_KEY = "neoface_consent_trail_v1";
|
|
8904
8925
|
const CONSENT_TRAIL_MAX_LIMIT = 100;
|
|
8905
8926
|
async function hashString(inputStr) {
|
|
@@ -13307,6 +13328,70 @@ async function registerDocumentByImage(personId, jwtToken, options = {}) {
|
|
|
13307
13328
|
if (captured.backImage) images.push(captured.backImage);
|
|
13308
13329
|
return registerDocumentByImage$1(personId, jwtToken, images);
|
|
13309
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
|
+
async function createDetector() {
|
|
13361
|
+
const [tf, handPoseDetection] = await Promise.all([
|
|
13362
|
+
import("@tensorflow/tfjs-core"),
|
|
13363
|
+
import("@tensorflow-models/hand-pose-detection")
|
|
13364
|
+
]);
|
|
13365
|
+
await import("@tensorflow/tfjs-backend-webgl");
|
|
13366
|
+
await tf.setBackend("webgl");
|
|
13367
|
+
await tf.ready();
|
|
13368
|
+
return handPoseDetection.createDetector(handPoseDetection.SupportedModels.MediaPipeHands, {
|
|
13369
|
+
runtime: "tfjs",
|
|
13370
|
+
modelType: "lite",
|
|
13371
|
+
maxHands: 1
|
|
13372
|
+
});
|
|
13373
|
+
}
|
|
13374
|
+
function getDetector() {
|
|
13375
|
+
if (!detectorPromise) {
|
|
13376
|
+
detectorPromise = createDetector();
|
|
13377
|
+
}
|
|
13378
|
+
return detectorPromise;
|
|
13379
|
+
}
|
|
13380
|
+
const wait = (ms) => new Promise((resolve) => {
|
|
13381
|
+
setTimeout(resolve, ms);
|
|
13382
|
+
});
|
|
13383
|
+
async function detectThumbsUp(videoEl, timeoutMs) {
|
|
13384
|
+
const detector = await getDetector();
|
|
13385
|
+
const deadline = Date.now() + timeoutMs;
|
|
13386
|
+
while (Date.now() < deadline) {
|
|
13387
|
+
const hands = await detector.estimateHands(videoEl, { flipHorizontal: true });
|
|
13388
|
+
if (hands.length > 0 && isThumbsUp(hands[0].keypoints)) {
|
|
13389
|
+
return true;
|
|
13390
|
+
}
|
|
13391
|
+
await wait(POLL_INTERVAL_MS);
|
|
13392
|
+
}
|
|
13393
|
+
return false;
|
|
13394
|
+
}
|
|
13310
13395
|
const PHOTO_INSTRUCTIONS = [
|
|
13311
13396
|
"Olhe diretamente para a câmera",
|
|
13312
13397
|
"Vire levemente a cabeça para a esquerda",
|
|
@@ -13314,6 +13399,7 @@ const PHOTO_INSTRUCTIONS = [
|
|
|
13314
13399
|
"Mostre o polegar para cima (👍) e olhe para a câmera"
|
|
13315
13400
|
];
|
|
13316
13401
|
const TOTAL_PHOTOS = 4;
|
|
13402
|
+
const GESTURE_TIMEOUT_MS = 5e3;
|
|
13317
13403
|
const authorizeOperation = async (applicationToken, cpf, options) => {
|
|
13318
13404
|
if (!applicationToken) {
|
|
13319
13405
|
throw new NeoFaceError("Application token is required", ErrorType.VALIDATION_ERROR);
|
|
@@ -13375,7 +13461,19 @@ const authorizeOperation = async (applicationToken, cpf, options) => {
|
|
|
13375
13461
|
if (options == null ? void 0 : options.onProgress) {
|
|
13376
13462
|
options.onProgress(i + 1, TOTAL_PHOTOS, instruction);
|
|
13377
13463
|
}
|
|
13378
|
-
|
|
13464
|
+
const isGesturePhoto = i === TOTAL_PHOTOS - 1;
|
|
13465
|
+
if (isGesturePhoto) {
|
|
13466
|
+
const detected = await detectThumbsUp(video, GESTURE_TIMEOUT_MS);
|
|
13467
|
+
if (!detected) {
|
|
13468
|
+
guideModal.close();
|
|
13469
|
+
stream.getTracks().forEach((track) => track.stop());
|
|
13470
|
+
capturedPhotos.length = 0;
|
|
13471
|
+
throw new NeoFaceError(
|
|
13472
|
+
"Polegar para cima não detectado dentro do tempo limite.",
|
|
13473
|
+
ErrorType.GESTURE_NOT_DETECTED
|
|
13474
|
+
);
|
|
13475
|
+
}
|
|
13476
|
+
} else if (i > 0) {
|
|
13379
13477
|
await new Promise((resolve) => setTimeout(resolve, captureDelay));
|
|
13380
13478
|
}
|
|
13381
13479
|
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
|