@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 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.31.0";
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.31.0";
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
- stream = await navigator.mediaDevices.getUserMedia({
12243
- video: { facingMode: "user", width: { ideal: 1280 }, height: { ideal: 720 } }
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
- setPhase("done");
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",