@neofaceid/web-sdk 1.31.0 → 1.32.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 +1 -1
- package/dist/neoface-id-sdk.es.js +388 -10
- package/dist/neoface-id-sdk.umd.js +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -1626,7 +1626,7 @@ export declare const validateToken: (applicationToken: string) => Promise<boolea
|
|
|
1626
1626
|
* MINOR: Incrementado quando adicionamos funcionalidades mantendo compatibilidade
|
|
1627
1627
|
* PATCH: Incrementado quando corrigimos bugs mantendo compatibilidade
|
|
1628
1628
|
*/
|
|
1629
|
-
export declare const VERSION = "1.
|
|
1629
|
+
export declare const VERSION = "1.32.0";
|
|
1630
1630
|
|
|
1631
1631
|
/**
|
|
1632
1632
|
* Executa `fn(sessionId)`. Se o servidor devolver 410 (sessão consumida/expirada),
|
|
@@ -4362,6 +4362,12 @@ const INK = {
|
|
|
4362
4362
|
900: "#111C2B",
|
|
4363
4363
|
950: "#0A1320"
|
|
4364
4364
|
};
|
|
4365
|
+
const SEMANTIC = {
|
|
4366
|
+
success: { fg: "#046C4E", bg: "#E7F7F0", solid: "#0E9F6E" },
|
|
4367
|
+
attention: { fg: "#6E5600", bg: "#FFF6D9", solid: "#F1BE00" },
|
|
4368
|
+
critical: { fg: "#9B1C1C", bg: "#FDEAEA", solid: "#E02424" },
|
|
4369
|
+
info: { fg: "#00449A", bg: "#EBF6FE", solid: "#0073F4" }
|
|
4370
|
+
};
|
|
4365
4371
|
const LIGHT_TOKENS = {
|
|
4366
4372
|
ground: INK[25],
|
|
4367
4373
|
surface: "#FFFFFF",
|
|
@@ -10059,7 +10065,7 @@ class EmailPasswordModal {
|
|
|
10059
10065
|
}
|
|
10060
10066
|
}
|
|
10061
10067
|
}
|
|
10062
|
-
const MAX_ATTEMPTS = 2;
|
|
10068
|
+
const MAX_ATTEMPTS$1 = 2;
|
|
10063
10069
|
const RETRY_DELAY = 500;
|
|
10064
10070
|
const CAMERA_READY_DELAY = 200;
|
|
10065
10071
|
const CAMERA_STABILITY_DELAY = 100;
|
|
@@ -10477,7 +10483,7 @@ async function executeBiometricLoginFlow(options) {
|
|
|
10477
10483
|
ErrorType.UNKNOWN
|
|
10478
10484
|
);
|
|
10479
10485
|
overlay.updateStatus("error");
|
|
10480
|
-
if (attempts < MAX_ATTEMPTS) {
|
|
10486
|
+
if (attempts < MAX_ATTEMPTS$1) {
|
|
10481
10487
|
setTimeout(() => {
|
|
10482
10488
|
tryLogin();
|
|
10483
10489
|
}, RETRY_DELAY);
|
|
@@ -10860,7 +10866,7 @@ const biometricDetection = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.
|
|
|
10860
10866
|
initializeBiometricDetection,
|
|
10861
10867
|
isAdvancedDetectionAvailable
|
|
10862
10868
|
}, Symbol.toStringTag, { value: "Module" }));
|
|
10863
|
-
const VERSION = "1.
|
|
10869
|
+
const VERSION = "1.32.0";
|
|
10864
10870
|
const RELEASE_DATE = "2026-09-02";
|
|
10865
10871
|
let cachedSession = null;
|
|
10866
10872
|
function getCachedCaptureSession() {
|
|
@@ -12153,6 +12159,306 @@ function pointsFor(g) {
|
|
|
12153
12159
|
return ["24 24", "24 24", "24 24"];
|
|
12154
12160
|
}
|
|
12155
12161
|
}
|
|
12162
|
+
function StatusPill({ tone, children, icon, className }) {
|
|
12163
|
+
const palette = SEMANTIC[tone];
|
|
12164
|
+
return /* @__PURE__ */ jsxs(
|
|
12165
|
+
"span",
|
|
12166
|
+
{
|
|
12167
|
+
className,
|
|
12168
|
+
style: {
|
|
12169
|
+
display: "inline-flex",
|
|
12170
|
+
alignItems: "center",
|
|
12171
|
+
gap: 6,
|
|
12172
|
+
padding: "4px 10px",
|
|
12173
|
+
borderRadius: 9999,
|
|
12174
|
+
background: palette.bg,
|
|
12175
|
+
color: palette.fg,
|
|
12176
|
+
fontSize: 13,
|
|
12177
|
+
fontWeight: 600,
|
|
12178
|
+
fontFamily: "inherit",
|
|
12179
|
+
lineHeight: 1.3
|
|
12180
|
+
},
|
|
12181
|
+
children: [
|
|
12182
|
+
icon,
|
|
12183
|
+
children
|
|
12184
|
+
]
|
|
12185
|
+
}
|
|
12186
|
+
);
|
|
12187
|
+
}
|
|
12188
|
+
const DEFAULT = {
|
|
12189
|
+
title: "Não deu certo desta vez",
|
|
12190
|
+
hint: "Tente se posicionar em um ambiente mais estável.",
|
|
12191
|
+
actions: [
|
|
12192
|
+
"Segure o dispositivo firme",
|
|
12193
|
+
"Fique de frente para uma luz suave"
|
|
12194
|
+
]
|
|
12195
|
+
};
|
|
12196
|
+
const MAP = {
|
|
12197
|
+
LIVENESS_LOW: {
|
|
12198
|
+
title: "Precisamos ver com mais nitidez",
|
|
12199
|
+
hint: "A prova de vida veio abaixo do esperado.",
|
|
12200
|
+
actions: [
|
|
12201
|
+
"Faça o movimento com calma até o fim",
|
|
12202
|
+
"Aproxime o rosto sem cortar as bordas"
|
|
12203
|
+
]
|
|
12204
|
+
},
|
|
12205
|
+
NO_FACE_DETECTED: {
|
|
12206
|
+
title: "Não vi seu rosto",
|
|
12207
|
+
hint: "A câmera não conseguiu identificar um rosto na moldura.",
|
|
12208
|
+
actions: [
|
|
12209
|
+
"Centralize o rosto no oval",
|
|
12210
|
+
"Afaste-se um pouco se estiver muito próximo"
|
|
12211
|
+
]
|
|
12212
|
+
},
|
|
12213
|
+
LOW_LIGHT: {
|
|
12214
|
+
title: "A luz está baixa demais",
|
|
12215
|
+
hint: "Falta contraste para o algoritmo distinguir seu rosto.",
|
|
12216
|
+
actions: [
|
|
12217
|
+
"Fique de frente para uma janela ou lâmpada",
|
|
12218
|
+
"Evite luz vindo por trás — deixa o rosto em sombra"
|
|
12219
|
+
]
|
|
12220
|
+
},
|
|
12221
|
+
LOOK_STRAIGHT: {
|
|
12222
|
+
title: "Olhe direto para a câmera",
|
|
12223
|
+
hint: "O rosto está um pouco de lado para reconhecimento facial.",
|
|
12224
|
+
actions: [
|
|
12225
|
+
"Fique de frente para a câmera",
|
|
12226
|
+
"Mantenha o queixo paralelo ao chão"
|
|
12227
|
+
]
|
|
12228
|
+
},
|
|
12229
|
+
RECOGNITION_FAILED: {
|
|
12230
|
+
title: "Não consegui confirmar",
|
|
12231
|
+
hint: "A imagem não bate com o cadastro que temos.",
|
|
12232
|
+
actions: [
|
|
12233
|
+
"Retire óculos ou máscara temporariamente",
|
|
12234
|
+
"Faça em um lugar mais iluminado"
|
|
12235
|
+
]
|
|
12236
|
+
},
|
|
12237
|
+
INVALID_TOKEN: {
|
|
12238
|
+
title: "Sessão expirada",
|
|
12239
|
+
hint: "A conexão com o serviço perdeu validade.",
|
|
12240
|
+
actions: [
|
|
12241
|
+
"Volte à tela anterior e tente novamente",
|
|
12242
|
+
"Se persistir, entre em contato com o suporte"
|
|
12243
|
+
]
|
|
12244
|
+
},
|
|
12245
|
+
NETWORK: {
|
|
12246
|
+
title: "A conexão está oscilando",
|
|
12247
|
+
hint: "A imagem não chegou até nosso servidor.",
|
|
12248
|
+
actions: [
|
|
12249
|
+
"Verifique a rede do dispositivo",
|
|
12250
|
+
"Repita a captura em alguns segundos"
|
|
12251
|
+
]
|
|
12252
|
+
}
|
|
12253
|
+
};
|
|
12254
|
+
function copyForErrorCode(code) {
|
|
12255
|
+
if (!code) return DEFAULT;
|
|
12256
|
+
const key = code.toUpperCase();
|
|
12257
|
+
return MAP[key] ?? DEFAULT;
|
|
12258
|
+
}
|
|
12259
|
+
function detectBrowser(ua = navigator.userAgent) {
|
|
12260
|
+
const s = ua.toLowerCase();
|
|
12261
|
+
if (s.includes("edg/")) return "edge";
|
|
12262
|
+
if (s.includes("firefox")) return "firefox";
|
|
12263
|
+
if (s.includes("chrome") && !s.includes("edg/")) return "chrome";
|
|
12264
|
+
if (s.includes("safari") && !s.includes("chrome")) return "safari";
|
|
12265
|
+
return "unknown";
|
|
12266
|
+
}
|
|
12267
|
+
const CAMERA_INSTRUCTIONS = {
|
|
12268
|
+
chrome: "Clique no cadeado da barra de endereço → Configurações do site → Câmera → Permitir. Recarregue a página.",
|
|
12269
|
+
edge: "Clique no cadeado da barra de endereço → Permissões do site → Câmera → Permitir. Recarregue a página.",
|
|
12270
|
+
firefox: 'Clique no ícone da câmera bloqueada na barra de endereço e escolha "Permitir". Recarregue a página.',
|
|
12271
|
+
safari: "Menu Safari → Configurações → Sites → Câmera → Permitir para este site. Recarregue a página.",
|
|
12272
|
+
unknown: 'Abra as configurações do navegador, procure por "câmera" e permita o acesso para este site. Depois recarregue a página.'
|
|
12273
|
+
};
|
|
12274
|
+
function cameraInstructionFor(browser) {
|
|
12275
|
+
return CAMERA_INSTRUCTIONS[browser];
|
|
12276
|
+
}
|
|
12277
|
+
function classifyMediaError(err) {
|
|
12278
|
+
if (!err || typeof err !== "object") return "UNKNOWN";
|
|
12279
|
+
const name = (err.name ?? "").toString();
|
|
12280
|
+
if (name === "NotAllowedError" || name === "SecurityError") return "PERMISSION_DENIED";
|
|
12281
|
+
if (name === "NotFoundError" || name === "OverconstrainedError") return "NO_CAMERA";
|
|
12282
|
+
if (name === "NotReadableError" || name === "TrackStartError") return "IN_USE";
|
|
12283
|
+
return "UNKNOWN";
|
|
12284
|
+
}
|
|
12285
|
+
const STYLE_ID$1 = "neofaceid-state-views-styles";
|
|
12286
|
+
const CSS = `
|
|
12287
|
+
.neofaceid-root .nfid-state {
|
|
12288
|
+
display: flex; flex-direction: column; align-items: center; text-align: center; gap: 10px;
|
|
12289
|
+
padding: 8px 8px 4px;
|
|
12290
|
+
}
|
|
12291
|
+
.neofaceid-root .nfid-state h2 {
|
|
12292
|
+
margin: 6px 0 0; font-size: 20px; font-weight: 700; color: var(--nfid-text); line-height: 1.25;
|
|
12293
|
+
letter-spacing: -0.015em;
|
|
12294
|
+
}
|
|
12295
|
+
.neofaceid-root .nfid-state p {
|
|
12296
|
+
margin: 0; font-size: 14px; color: var(--nfid-text-muted); line-height: 1.5;
|
|
12297
|
+
}
|
|
12298
|
+
.neofaceid-root .nfid-state-actions {
|
|
12299
|
+
display: flex; flex-direction: column; gap: 10px; width: 100%; margin-top: 12px;
|
|
12300
|
+
}
|
|
12301
|
+
.neofaceid-root .nfid-state-actions--row {
|
|
12302
|
+
flex-direction: row; justify-content: center;
|
|
12303
|
+
}
|
|
12304
|
+
.neofaceid-root .nfid-state-icon-success {
|
|
12305
|
+
width: 64px; height: 64px; color: #0E9F6E;
|
|
12306
|
+
animation: nfidCelebrate 500ms cubic-bezier(.2,1.4,.4,1) both;
|
|
12307
|
+
}
|
|
12308
|
+
@keyframes nfidCelebrate {
|
|
12309
|
+
0% { transform: scale(0.6); opacity: 0; }
|
|
12310
|
+
60% { transform: scale(1.08); opacity: 1; }
|
|
12311
|
+
100% { transform: scale(1); }
|
|
12312
|
+
}
|
|
12313
|
+
.neofaceid-root .nfid-state-warn-icon { width: 56px; height: 56px; color: #F1BE00; }
|
|
12314
|
+
.neofaceid-root .nfid-state-cameraoff-icon { width: 56px; height: 56px; color: var(--nfid-text-muted); }
|
|
12315
|
+
.neofaceid-root .nfid-state-hints {
|
|
12316
|
+
display: flex; flex-direction: column; gap: 6px; margin-top: 8px; text-align: left; width: 100%;
|
|
12317
|
+
}
|
|
12318
|
+
.neofaceid-root .nfid-state-hint {
|
|
12319
|
+
display: flex; gap: 8px; align-items: flex-start; font-size: 13px; color: var(--nfid-text);
|
|
12320
|
+
padding: 8px 12px; background: var(--nfid-surface-muted); border-radius: 12px;
|
|
12321
|
+
}
|
|
12322
|
+
.neofaceid-root .nfid-state-hint::before { content: "•"; color: var(--nfid-action); font-weight: 700; }
|
|
12323
|
+
.neofaceid-root .nfid-state-attempt {
|
|
12324
|
+
font-size: 12px; font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase;
|
|
12325
|
+
color: var(--nfid-text-subtle);
|
|
12326
|
+
}
|
|
12327
|
+
.neofaceid-root .nfid-state-techcode {
|
|
12328
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
12329
|
+
font-size: 11px; color: var(--nfid-text-subtle); margin-top: 8px;
|
|
12330
|
+
}
|
|
12331
|
+
`;
|
|
12332
|
+
function useStateStyles() {
|
|
12333
|
+
useEffect(() => {
|
|
12334
|
+
injectThemeStyles$1();
|
|
12335
|
+
injectScopedStyles$1(STYLE_ID$1, CSS);
|
|
12336
|
+
}, []);
|
|
12337
|
+
}
|
|
12338
|
+
function AuthorizeSuccess({ appName, onContinue }) {
|
|
12339
|
+
useStateStyles();
|
|
12340
|
+
return /* @__PURE__ */ jsxs("div", { className: "nfid-state", role: "status", "aria-live": "polite", children: [
|
|
12341
|
+
/* @__PURE__ */ jsxs(
|
|
12342
|
+
"svg",
|
|
12343
|
+
{
|
|
12344
|
+
className: "nfid-state-icon-success",
|
|
12345
|
+
viewBox: "0 0 64 64",
|
|
12346
|
+
fill: "none",
|
|
12347
|
+
stroke: "currentColor",
|
|
12348
|
+
strokeWidth: "4",
|
|
12349
|
+
strokeLinecap: "round",
|
|
12350
|
+
strokeLinejoin: "round",
|
|
12351
|
+
"aria-hidden": "true",
|
|
12352
|
+
children: [
|
|
12353
|
+
/* @__PURE__ */ jsx("circle", { cx: "32", cy: "32", r: "28" }),
|
|
12354
|
+
/* @__PURE__ */ jsx("path", { d: "M20 33 L28 41 L44 24" })
|
|
12355
|
+
]
|
|
12356
|
+
}
|
|
12357
|
+
),
|
|
12358
|
+
/* @__PURE__ */ jsx("h2", { children: "Identidade confirmada" }),
|
|
12359
|
+
/* @__PURE__ */ jsxs("p", { children: [
|
|
12360
|
+
"Você já pode continuar no ",
|
|
12361
|
+
appName,
|
|
12362
|
+
"."
|
|
12363
|
+
] }),
|
|
12364
|
+
/* @__PURE__ */ jsx("div", { className: "nfid-state-actions", children: /* @__PURE__ */ jsx(Button, { variant: "primary", onClick: onContinue, children: "Continuar" }) })
|
|
12365
|
+
] });
|
|
12366
|
+
}
|
|
12367
|
+
function formatHms(d) {
|
|
12368
|
+
const hh = String(d.getHours()).padStart(2, "0");
|
|
12369
|
+
const mm = String(d.getMinutes()).padStart(2, "0");
|
|
12370
|
+
const ss = String(d.getSeconds()).padStart(2, "0");
|
|
12371
|
+
return `${hh}:${mm}:${ss}`;
|
|
12372
|
+
}
|
|
12373
|
+
function AuthorizeFailure({
|
|
12374
|
+
errorCode,
|
|
12375
|
+
attempt,
|
|
12376
|
+
maxAttempts,
|
|
12377
|
+
timestamp,
|
|
12378
|
+
onRetry,
|
|
12379
|
+
onFallback
|
|
12380
|
+
}) {
|
|
12381
|
+
useStateStyles();
|
|
12382
|
+
const copy = copyForErrorCode(errorCode);
|
|
12383
|
+
const stamp = timestamp ?? /* @__PURE__ */ new Date();
|
|
12384
|
+
return /* @__PURE__ */ jsxs("div", { className: "nfid-state", role: "alert", "aria-live": "assertive", children: [
|
|
12385
|
+
/* @__PURE__ */ jsxs(StatusPill, { tone: "attention", children: [
|
|
12386
|
+
"Tentativa ",
|
|
12387
|
+
attempt,
|
|
12388
|
+
" de ",
|
|
12389
|
+
maxAttempts
|
|
12390
|
+
] }),
|
|
12391
|
+
/* @__PURE__ */ jsxs(
|
|
12392
|
+
"svg",
|
|
12393
|
+
{
|
|
12394
|
+
className: "nfid-state-warn-icon",
|
|
12395
|
+
viewBox: "0 0 64 64",
|
|
12396
|
+
fill: "none",
|
|
12397
|
+
stroke: "currentColor",
|
|
12398
|
+
strokeWidth: "3",
|
|
12399
|
+
strokeLinecap: "round",
|
|
12400
|
+
strokeLinejoin: "round",
|
|
12401
|
+
"aria-hidden": "true",
|
|
12402
|
+
children: [
|
|
12403
|
+
/* @__PURE__ */ jsx("path", { d: "M32 8 L58 54 H6 Z" }),
|
|
12404
|
+
/* @__PURE__ */ jsx("path", { d: "M32 26 V38" }),
|
|
12405
|
+
/* @__PURE__ */ jsx("circle", { cx: "32", cy: "45", r: "2", fill: "currentColor" })
|
|
12406
|
+
]
|
|
12407
|
+
}
|
|
12408
|
+
),
|
|
12409
|
+
/* @__PURE__ */ jsx("h2", { children: copy.title }),
|
|
12410
|
+
/* @__PURE__ */ jsx("p", { children: copy.hint }),
|
|
12411
|
+
/* @__PURE__ */ jsxs("div", { className: "nfid-state-hints", "aria-label": "O que fazer", children: [
|
|
12412
|
+
/* @__PURE__ */ jsx("div", { className: "nfid-state-hint", children: copy.actions[0] }),
|
|
12413
|
+
/* @__PURE__ */ jsx("div", { className: "nfid-state-hint", children: copy.actions[1] })
|
|
12414
|
+
] }),
|
|
12415
|
+
/* @__PURE__ */ jsxs("div", { className: "nfid-state-actions", children: [
|
|
12416
|
+
/* @__PURE__ */ jsx(Button, { variant: "primary", onClick: onRetry, children: "Tentar de novo" }),
|
|
12417
|
+
/* @__PURE__ */ jsx(Button, { variant: "ghost", onClick: onFallback, children: "Verificar de outro jeito" })
|
|
12418
|
+
] }),
|
|
12419
|
+
/* @__PURE__ */ jsxs("p", { className: "nfid-state-techcode", children: [
|
|
12420
|
+
(errorCode ?? "UNKNOWN").toUpperCase(),
|
|
12421
|
+
" · ",
|
|
12422
|
+
formatHms(stamp)
|
|
12423
|
+
] })
|
|
12424
|
+
] });
|
|
12425
|
+
}
|
|
12426
|
+
function AuthorizePermissionDenied({
|
|
12427
|
+
browser,
|
|
12428
|
+
onFallback,
|
|
12429
|
+
onCancel
|
|
12430
|
+
}) {
|
|
12431
|
+
useStateStyles();
|
|
12432
|
+
const detected = browser ?? detectBrowser();
|
|
12433
|
+
const instruction = cameraInstructionFor(detected);
|
|
12434
|
+
return /* @__PURE__ */ jsxs("div", { className: "nfid-state", role: "alert", "aria-live": "assertive", children: [
|
|
12435
|
+
/* @__PURE__ */ jsxs(
|
|
12436
|
+
"svg",
|
|
12437
|
+
{
|
|
12438
|
+
className: "nfid-state-cameraoff-icon",
|
|
12439
|
+
viewBox: "0 0 24 24",
|
|
12440
|
+
fill: "none",
|
|
12441
|
+
stroke: "currentColor",
|
|
12442
|
+
strokeWidth: "1.75",
|
|
12443
|
+
strokeLinecap: "round",
|
|
12444
|
+
strokeLinejoin: "round",
|
|
12445
|
+
"aria-hidden": "true",
|
|
12446
|
+
children: [
|
|
12447
|
+
/* @__PURE__ */ jsx("path", { d: "m3 3 18 18" }),
|
|
12448
|
+
/* @__PURE__ */ jsx("path", { d: "M20.66 15.34A2 2 0 0 0 22 13.5V7.5a2 2 0 0 0-3.13-1.65L15 8.5" }),
|
|
12449
|
+
/* @__PURE__ */ jsx("path", { d: "M9 6H4a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2v-1" })
|
|
12450
|
+
]
|
|
12451
|
+
}
|
|
12452
|
+
),
|
|
12453
|
+
/* @__PURE__ */ jsx("h2", { children: "Câmera bloqueada" }),
|
|
12454
|
+
/* @__PURE__ */ jsx("p", { children: "Precisamos da câmera para confirmar que é você." }),
|
|
12455
|
+
/* @__PURE__ */ jsx("p", { style: { marginTop: 8 }, children: instruction }),
|
|
12456
|
+
/* @__PURE__ */ jsxs("div", { className: "nfid-state-actions", children: [
|
|
12457
|
+
/* @__PURE__ */ jsx(Button, { variant: "primary", onClick: onFallback, children: "Verificar de outro jeito" }),
|
|
12458
|
+
/* @__PURE__ */ jsx(Button, { variant: "ghost", onClick: onCancel, children: "Agora não" })
|
|
12459
|
+
] })
|
|
12460
|
+
] });
|
|
12461
|
+
}
|
|
12156
12462
|
const STYLE_ID = "neofaceid-authorize-styles";
|
|
12157
12463
|
const GESTURE_TO_GLYPH = {
|
|
12158
12464
|
center: "center",
|
|
@@ -12186,6 +12492,7 @@ function formatCurrency(amount, locale) {
|
|
|
12186
12492
|
return `R$ ${amount.toFixed(2)}`;
|
|
12187
12493
|
}
|
|
12188
12494
|
}
|
|
12495
|
+
const MAX_ATTEMPTS = 3;
|
|
12189
12496
|
function AuthorizeModalComponent({
|
|
12190
12497
|
applicationToken,
|
|
12191
12498
|
appName,
|
|
@@ -12201,6 +12508,10 @@ function AuthorizeModalComponent({
|
|
|
12201
12508
|
const [progress, setProgress] = useState(0);
|
|
12202
12509
|
const [currentGesture, setCurrentGesture] = useState(null);
|
|
12203
12510
|
const [phase, setPhase] = useState("preparing");
|
|
12511
|
+
const [attempt, setAttempt] = useState(1);
|
|
12512
|
+
const [failureCode, setFailureCode] = useState(void 0);
|
|
12513
|
+
const [failureAt, setFailureAt] = useState(void 0);
|
|
12514
|
+
const [runCounter, setRunCounter] = useState(0);
|
|
12204
12515
|
const cancelRef = useRef(false);
|
|
12205
12516
|
const locale = getLocale();
|
|
12206
12517
|
const headerAppName = appName ?? getAppName() ?? "sua aplicação";
|
|
@@ -12237,11 +12548,27 @@ function AuthorizeModalComponent({
|
|
|
12237
12548
|
}, []);
|
|
12238
12549
|
useEffect(() => {
|
|
12239
12550
|
let stream = null;
|
|
12551
|
+
cancelRef.current = false;
|
|
12552
|
+
setPhase("preparing");
|
|
12553
|
+
setProgress(0);
|
|
12554
|
+
setCurrentGesture(null);
|
|
12240
12555
|
async function run() {
|
|
12241
12556
|
try {
|
|
12242
|
-
|
|
12243
|
-
|
|
12244
|
-
|
|
12557
|
+
try {
|
|
12558
|
+
stream = await navigator.mediaDevices.getUserMedia({
|
|
12559
|
+
video: { facingMode: "user", width: { ideal: 1280 }, height: { ideal: 720 } }
|
|
12560
|
+
});
|
|
12561
|
+
} catch (mediaErr) {
|
|
12562
|
+
const kind = classifyMediaError(mediaErr);
|
|
12563
|
+
if (kind === "PERMISSION_DENIED") {
|
|
12564
|
+
setPhase("permission-denied");
|
|
12565
|
+
return;
|
|
12566
|
+
}
|
|
12567
|
+
throw new NeoFaceError(
|
|
12568
|
+
kind === "NO_CAMERA" ? "Câmera não encontrada" : kind === "IN_USE" ? "Câmera em uso por outro app" : "Falha ao acessar a câmera",
|
|
12569
|
+
ErrorType.CAMERA_ERROR
|
|
12570
|
+
);
|
|
12571
|
+
}
|
|
12245
12572
|
if (videoRef.current) {
|
|
12246
12573
|
videoRef.current.srcObject = stream;
|
|
12247
12574
|
videoRef.current.play().catch(() => {
|
|
@@ -12262,13 +12589,23 @@ function AuthorizeModalComponent({
|
|
|
12262
12589
|
return frame ? [frame] : [];
|
|
12263
12590
|
}
|
|
12264
12591
|
});
|
|
12265
|
-
|
|
12592
|
+
if (stream) stream.getTracks().forEach((t) => t.stop());
|
|
12593
|
+
stream = null;
|
|
12594
|
+
if (result.success === false || result.liveness_passed === false) {
|
|
12595
|
+
setFailureCode("LIVENESS_LOW");
|
|
12596
|
+
setFailureAt(/* @__PURE__ */ new Date());
|
|
12597
|
+
setPhase("failure");
|
|
12598
|
+
return;
|
|
12599
|
+
}
|
|
12600
|
+
setPhase("success");
|
|
12266
12601
|
if (onSuccess) onSuccess(result);
|
|
12267
|
-
onClose();
|
|
12268
12602
|
} catch (err) {
|
|
12603
|
+
if (cancelRef.current) return;
|
|
12269
12604
|
const nfe = err instanceof NeoFaceError ? err : new NeoFaceError(err instanceof Error ? err.message : "Falha", ErrorType.UNKNOWN);
|
|
12605
|
+
setFailureCode(nfe.type);
|
|
12606
|
+
setFailureAt(/* @__PURE__ */ new Date());
|
|
12607
|
+
setPhase("failure");
|
|
12270
12608
|
if (onError) onError(nfe);
|
|
12271
|
-
onClose();
|
|
12272
12609
|
} finally {
|
|
12273
12610
|
if (stream) stream.getTracks().forEach((t) => t.stop());
|
|
12274
12611
|
}
|
|
@@ -12278,14 +12615,55 @@ function AuthorizeModalComponent({
|
|
|
12278
12615
|
cancelRef.current = true;
|
|
12279
12616
|
if (stream) stream.getTracks().forEach((t) => t.stop());
|
|
12280
12617
|
};
|
|
12281
|
-
}, []);
|
|
12618
|
+
}, [runCounter]);
|
|
12282
12619
|
const handleClose = () => {
|
|
12283
12620
|
cancelRef.current = true;
|
|
12284
12621
|
if (onCancel) onCancel();
|
|
12285
12622
|
onClose();
|
|
12286
12623
|
};
|
|
12624
|
+
const handleRetry = () => {
|
|
12625
|
+
if (attempt >= MAX_ATTEMPTS) {
|
|
12626
|
+
handleClose();
|
|
12627
|
+
return;
|
|
12628
|
+
}
|
|
12629
|
+
setAttempt((a) => a + 1);
|
|
12630
|
+
setFailureCode(void 0);
|
|
12631
|
+
setFailureAt(void 0);
|
|
12632
|
+
setRunCounter((c) => c + 1);
|
|
12633
|
+
};
|
|
12634
|
+
const handleFallback = () => {
|
|
12635
|
+
cancelRef.current = true;
|
|
12636
|
+
if (onError) {
|
|
12637
|
+
onError(
|
|
12638
|
+
new NeoFaceError(
|
|
12639
|
+
"Fluxo alternativo solicitado pelo usuário",
|
|
12640
|
+
ErrorType.CONSENT_DENIED
|
|
12641
|
+
)
|
|
12642
|
+
);
|
|
12643
|
+
}
|
|
12644
|
+
onClose();
|
|
12645
|
+
};
|
|
12287
12646
|
const gestureKey = currentGesture ? GESTURE_TO_GLYPH[currentGesture.gesture_key] ?? "center" : "center";
|
|
12288
12647
|
const instruction = currentGesture ? GESTURE_TO_INSTRUCTION[currentGesture.gesture_key] ?? "Siga a instrução" : "Preparando a câmera…";
|
|
12648
|
+
if (phase === "success") {
|
|
12649
|
+
return /* @__PURE__ */ jsx(Sheet, { open: true, dismissOnOverlay: false, ariaLabel: "Identidade confirmada", children: /* @__PURE__ */ jsx(AuthorizeSuccess, { appName: headerAppName, onContinue: onClose }) });
|
|
12650
|
+
}
|
|
12651
|
+
if (phase === "permission-denied") {
|
|
12652
|
+
return /* @__PURE__ */ jsx(Sheet, { open: true, dismissOnOverlay: false, ariaLabel: "Câmera bloqueada", children: /* @__PURE__ */ jsx(AuthorizePermissionDenied, { onFallback: handleFallback, onCancel: handleClose }) });
|
|
12653
|
+
}
|
|
12654
|
+
if (phase === "failure") {
|
|
12655
|
+
return /* @__PURE__ */ jsx(Sheet, { open: true, dismissOnOverlay: false, ariaLabel: "Não foi possível confirmar", children: /* @__PURE__ */ jsx(
|
|
12656
|
+
AuthorizeFailure,
|
|
12657
|
+
{
|
|
12658
|
+
errorCode: failureCode,
|
|
12659
|
+
attempt,
|
|
12660
|
+
maxAttempts: MAX_ATTEMPTS,
|
|
12661
|
+
timestamp: failureAt,
|
|
12662
|
+
onRetry: handleRetry,
|
|
12663
|
+
onFallback: handleFallback
|
|
12664
|
+
}
|
|
12665
|
+
) });
|
|
12666
|
+
}
|
|
12289
12667
|
return /* @__PURE__ */ jsx(Sheet, { open: true, onDismiss: void 0, dismissOnOverlay: false, ariaLabel: "Autorização por biometria", children: /* @__PURE__ */ jsxs("div", { style: { position: "relative" }, children: [
|
|
12290
12668
|
/* @__PURE__ */ jsx(
|
|
12291
12669
|
"button",
|