@neofaceid/web-sdk 1.6.0 → 1.8.7

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.
@@ -1,29 +1,40 @@
1
1
  var __defProp = Object.defineProperty;
2
2
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
- var __publicField = (obj, key, value) => {
4
- __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
5
- return value;
6
- };
7
- var _a;
8
- import require$$0$1, { useRef, useState, useCallback, useReducer, useEffect } from "react";
3
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
+ import React, { useRef, useState, useCallback, useReducer, useEffect } from "react";
9
5
  import require$$0 from "react-dom";
10
6
  import * as faceapi from "face-api.js";
11
- var createRoot;
12
- var m = require$$0;
13
- if (process.env.NODE_ENV === "production") {
14
- createRoot = m.createRoot;
15
- m.hydrateRoot;
16
- } else {
17
- var i = m.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
18
- createRoot = function(c, o) {
19
- i.usingClientEntryPoint = true;
20
- try {
21
- return m.createRoot(c, o);
22
- } finally {
23
- i.usingClientEntryPoint = false;
24
- }
25
- };
7
+ var client = {};
8
+ var hasRequiredClient;
9
+ function requireClient() {
10
+ if (hasRequiredClient) return client;
11
+ hasRequiredClient = 1;
12
+ var m = require$$0;
13
+ if (process.env.NODE_ENV === "production") {
14
+ client.createRoot = m.createRoot;
15
+ client.hydrateRoot = m.hydrateRoot;
16
+ } else {
17
+ var i = m.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
18
+ client.createRoot = function(c, o) {
19
+ i.usingClientEntryPoint = true;
20
+ try {
21
+ return m.createRoot(c, o);
22
+ } finally {
23
+ i.usingClientEntryPoint = false;
24
+ }
25
+ };
26
+ client.hydrateRoot = function(c, h, o) {
27
+ i.usingClientEntryPoint = true;
28
+ try {
29
+ return m.hydrateRoot(c, h, o);
30
+ } finally {
31
+ i.usingClientEntryPoint = false;
32
+ }
33
+ };
34
+ }
35
+ return client;
26
36
  }
37
+ var clientExports = requireClient();
27
38
  var jsxRuntime = { exports: {} };
28
39
  var reactJsxRuntime_production_min = {};
29
40
  /**
@@ -37,20 +48,16 @@ var reactJsxRuntime_production_min = {};
37
48
  */
38
49
  var hasRequiredReactJsxRuntime_production_min;
39
50
  function requireReactJsxRuntime_production_min() {
40
- if (hasRequiredReactJsxRuntime_production_min)
41
- return reactJsxRuntime_production_min;
51
+ if (hasRequiredReactJsxRuntime_production_min) return reactJsxRuntime_production_min;
42
52
  hasRequiredReactJsxRuntime_production_min = 1;
43
- var f = require$$0$1, k = Symbol.for("react.element"), l = Symbol.for("react.fragment"), m2 = Object.prototype.hasOwnProperty, n = f.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner, p = { key: true, ref: true, __self: true, __source: true };
53
+ var f = React, k = Symbol.for("react.element"), l = Symbol.for("react.fragment"), m = Object.prototype.hasOwnProperty, n = f.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner, p = { key: true, ref: true, __self: true, __source: true };
44
54
  function q(c, a, g) {
45
55
  var b, d = {}, e = null, h = null;
46
56
  void 0 !== g && (e = "" + g);
47
57
  void 0 !== a.key && (e = "" + a.key);
48
58
  void 0 !== a.ref && (h = a.ref);
49
- for (b in a)
50
- m2.call(a, b) && !p.hasOwnProperty(b) && (d[b] = a[b]);
51
- if (c && c.defaultProps)
52
- for (b in a = c.defaultProps, a)
53
- void 0 === d[b] && (d[b] = a[b]);
59
+ for (b in a) m.call(a, b) && !p.hasOwnProperty(b) && (d[b] = a[b]);
60
+ if (c && c.defaultProps) for (b in a = c.defaultProps, a) void 0 === d[b] && (d[b] = a[b]);
54
61
  return { $$typeof: k, type: c, key: e, ref: h, props: d, _owner: n.current };
55
62
  }
56
63
  reactJsxRuntime_production_min.Fragment = l;
@@ -70,12 +77,11 @@ var reactJsxRuntime_development = {};
70
77
  */
71
78
  var hasRequiredReactJsxRuntime_development;
72
79
  function requireReactJsxRuntime_development() {
73
- if (hasRequiredReactJsxRuntime_development)
74
- return reactJsxRuntime_development;
80
+ if (hasRequiredReactJsxRuntime_development) return reactJsxRuntime_development;
75
81
  hasRequiredReactJsxRuntime_development = 1;
76
82
  if (process.env.NODE_ENV !== "production") {
77
83
  (function() {
78
- var React = require$$0$1;
84
+ var React$1 = React;
79
85
  var REACT_ELEMENT_TYPE = Symbol.for("react.element");
80
86
  var REACT_PORTAL_TYPE = Symbol.for("react.portal");
81
87
  var REACT_FRAGMENT_TYPE = Symbol.for("react.fragment");
@@ -101,7 +107,7 @@ function requireReactJsxRuntime_development() {
101
107
  }
102
108
  return null;
103
109
  }
104
- var ReactSharedInternals = React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
110
+ var ReactSharedInternals = React$1.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
105
111
  function error(format) {
106
112
  {
107
113
  {
@@ -559,10 +565,6 @@ function requireReactJsxRuntime_development() {
559
565
  };
560
566
  var specialPropKeyWarningShown;
561
567
  var specialPropRefWarningShown;
562
- var didWarnAboutStringRefs;
563
- {
564
- didWarnAboutStringRefs = {};
565
- }
566
568
  function hasValidRef(config) {
567
569
  {
568
570
  if (hasOwnProperty.call(config, "ref")) {
@@ -587,13 +589,7 @@ function requireReactJsxRuntime_development() {
587
589
  }
588
590
  function warnIfStringRefCannotBeAutoConverted(config, self) {
589
591
  {
590
- if (typeof config.ref === "string" && ReactCurrentOwner.current && self && ReactCurrentOwner.current.stateNode !== self) {
591
- var componentName = getComponentNameFromType(ReactCurrentOwner.current.type);
592
- if (!didWarnAboutStringRefs[componentName]) {
593
- error('Component "%s" contains the string ref "%s". Support for string refs will be removed in a future major release. This case cannot be automatically converted to an arrow function. We ask you to manually fix this case by using useRef() or createRef() instead. Learn more about using refs safely here: https://reactjs.org/link/strict-mode-string-ref', getComponentNameFromType(ReactCurrentOwner.current.type), config.ref);
594
- didWarnAboutStringRefs[componentName] = true;
595
- }
596
- }
592
+ if (typeof config.ref === "string" && ReactCurrentOwner.current && self) ;
597
593
  }
598
594
  }
599
595
  function defineKeyPropWarningGetter(props, displayName) {
@@ -747,11 +743,6 @@ function requireReactJsxRuntime_development() {
747
743
  }
748
744
  function getSourceInfoErrorAddendum(source) {
749
745
  {
750
- if (source !== void 0) {
751
- var fileName = source.fileName.replace(/^.*[\\\/]/, "");
752
- var lineNumber = source.lineNumber;
753
- return "\n\nCheck your code at " + fileName + ":" + lineNumber + ".";
754
- }
755
746
  return "";
756
747
  }
757
748
  }
@@ -877,7 +868,7 @@ function requireReactJsxRuntime_development() {
877
868
  if (type === void 0 || typeof type === "object" && type !== null && Object.keys(type).length === 0) {
878
869
  info += " You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.";
879
870
  }
880
- var sourceInfo = getSourceInfoErrorAddendum(source);
871
+ var sourceInfo = getSourceInfoErrorAddendum();
881
872
  if (sourceInfo) {
882
873
  info += sourceInfo;
883
874
  } else {
@@ -960,12 +951,18 @@ function requireReactJsxRuntime_development() {
960
951
  }
961
952
  return reactJsxRuntime_development;
962
953
  }
963
- if (process.env.NODE_ENV === "production") {
964
- jsxRuntime.exports = requireReactJsxRuntime_production_min();
965
- } else {
966
- jsxRuntime.exports = requireReactJsxRuntime_development();
954
+ var hasRequiredJsxRuntime;
955
+ function requireJsxRuntime() {
956
+ if (hasRequiredJsxRuntime) return jsxRuntime.exports;
957
+ hasRequiredJsxRuntime = 1;
958
+ if (process.env.NODE_ENV === "production") {
959
+ jsxRuntime.exports = requireReactJsxRuntime_production_min();
960
+ } else {
961
+ jsxRuntime.exports = requireReactJsxRuntime_development();
962
+ }
963
+ return jsxRuntime.exports;
967
964
  }
968
- var jsxRuntimeExports = jsxRuntime.exports;
965
+ var jsxRuntimeExports = requireJsxRuntime();
969
966
  class CameraPermissionDenied extends Error {
970
967
  constructor(message) {
971
968
  super(message);
@@ -1007,7 +1004,7 @@ const useCamera = () => {
1007
1004
  };
1008
1005
  const startCamera = useCallback(
1009
1006
  async (constraints) => {
1010
- var _a2;
1007
+ var _a;
1011
1008
  console.log("🎬 [useCamera] startCamera chamado", {
1012
1009
  hasExistingStream: !!streamRef.current,
1013
1010
  isInitializing: isInitializingRef.current
@@ -1049,7 +1046,7 @@ const useCamera = () => {
1049
1046
  console.log("📋 [useCamera] Detalhes do erro:", {
1050
1047
  name: error.name,
1051
1048
  message: error.message,
1052
- stack: (_a2 = error.stack) == null ? void 0 : _a2.substring(0, 200)
1049
+ stack: (_a = error.stack) == null ? void 0 : _a.substring(0, 200)
1053
1050
  });
1054
1051
  if (error.name === "NotAllowedError" || error.name === "PermissionDeniedError") {
1055
1052
  throw new CameraPermissionDenied(
@@ -1137,7 +1134,8 @@ class NeoFaceError extends Error {
1137
1134
  this.type = type;
1138
1135
  }
1139
1136
  }
1140
- const API_BASE_URL = ((_a = { "BASE_URL": "/", "MODE": "production", "DEV": false, "PROD": true, "SSR": false }) == null ? void 0 : _a.VITE_API_BASE_URL) || "http://localhost:8000";
1137
+ const __vite_import_meta_env__$1 = {};
1138
+ const API_BASE_URL = (__vite_import_meta_env__$1 == null ? void 0 : __vite_import_meta_env__$1.VITE_API_BASE_URL) || "http://localhost:8000";
1141
1139
  const REQUEST_TIMEOUT = 1e4;
1142
1140
  const ensureSecureContext = () => {
1143
1141
  const isLocalhost = window.location.hostname === "localhost" || window.location.hostname === "127.0.0.1" || window.location.hostname === "";
@@ -1183,6 +1181,46 @@ const compressImage = async (imageBlob) => new Promise((resolve, reject) => {
1183
1181
  };
1184
1182
  img.src = URL.createObjectURL(imageBlob);
1185
1183
  });
1184
+ const compressDocumentImage = async (imageBlob) => new Promise((resolve, reject) => {
1185
+ const img = new Image();
1186
+ img.onload = () => {
1187
+ const MAX_SIZE = 1280;
1188
+ let width = img.width;
1189
+ let height = img.height;
1190
+ if (width > height && width > MAX_SIZE) {
1191
+ height = height * MAX_SIZE / width;
1192
+ width = MAX_SIZE;
1193
+ } else if (height > MAX_SIZE) {
1194
+ width = width * MAX_SIZE / height;
1195
+ height = MAX_SIZE;
1196
+ }
1197
+ const canvas = document.createElement("canvas");
1198
+ canvas.width = width;
1199
+ canvas.height = height;
1200
+ const ctx = canvas.getContext("2d");
1201
+ if (!ctx) {
1202
+ reject(new Error("Failed to get canvas context"));
1203
+ return;
1204
+ }
1205
+ ctx.drawImage(img, 0, 0, width, height);
1206
+ canvas.toBlob(
1207
+ (blob) => {
1208
+ if (blob) {
1209
+ resolve(blob);
1210
+ } else {
1211
+ reject(new Error("Failed to compress document image"));
1212
+ }
1213
+ },
1214
+ "image/jpeg",
1215
+ 0.75
1216
+ // 75% quality to keep file size manageable
1217
+ );
1218
+ };
1219
+ img.onerror = () => {
1220
+ reject(new Error("Failed to load document image"));
1221
+ };
1222
+ img.src = URL.createObjectURL(imageBlob);
1223
+ });
1186
1224
  const validateToken = async (applicationToken) => {
1187
1225
  ensureSecureContext();
1188
1226
  const controller = createTimeoutController();
@@ -1211,13 +1249,16 @@ const getOnboardingDetails = async (applicationToken, onboardingToken) => {
1211
1249
  ensureSecureContext();
1212
1250
  const controller = createTimeoutController();
1213
1251
  try {
1214
- const response = await fetch(`${API_BASE_URL}/api/v1/onboardings/${encodeURIComponent(onboardingToken)}/`, {
1215
- method: "GET",
1216
- headers: {
1217
- "X-App-Token": applicationToken
1218
- },
1219
- signal: controller.signal
1220
- });
1252
+ const response = await fetch(
1253
+ `${API_BASE_URL}/api/v1/onboardings/${encodeURIComponent(onboardingToken)}/`,
1254
+ {
1255
+ method: "GET",
1256
+ headers: {
1257
+ "X-App-Token": applicationToken
1258
+ },
1259
+ signal: controller.signal
1260
+ }
1261
+ );
1221
1262
  if (!response.ok) {
1222
1263
  if (response.status === 401 || response.status === 403) {
1223
1264
  throw new NeoFaceError("Token de aplicação inválido ou expirado", ErrorType.INVALID_TOKEN);
@@ -1246,13 +1287,16 @@ const validateOnboardingToken = async (applicationToken, onboardingToken) => {
1246
1287
  ensureSecureContext();
1247
1288
  const controller = createTimeoutController();
1248
1289
  try {
1249
- const response = await fetch(`${API_BASE_URL}/api/v1/onboardings/${encodeURIComponent(onboardingToken)}/`, {
1250
- method: "GET",
1251
- headers: {
1252
- "X-App-Token": applicationToken
1253
- },
1254
- signal: controller.signal
1255
- });
1290
+ const response = await fetch(
1291
+ `${API_BASE_URL}/api/v1/onboardings/${encodeURIComponent(onboardingToken)}/`,
1292
+ {
1293
+ method: "GET",
1294
+ headers: {
1295
+ "X-App-Token": applicationToken
1296
+ },
1297
+ signal: controller.signal
1298
+ }
1299
+ );
1256
1300
  if (!response.ok) {
1257
1301
  if (response.status === 401 || response.status === 403) {
1258
1302
  throw new NeoFaceError("Token de aplicação inválido ou expirado", ErrorType.INVALID_TOKEN);
@@ -1305,7 +1349,10 @@ const completeOnboarding = async (applicationToken, onboardingToken, faceImage,
1305
1349
  if (response.status === 404) {
1306
1350
  throw new NeoFaceError("Link de onboarding não encontrado", ErrorType.NOT_FOUND);
1307
1351
  }
1308
- throw new NeoFaceError((data == null ? void 0 : data.error) || (data == null ? void 0 : data.message) || "Falha ao concluir onboarding", ErrorType.NETWORK);
1352
+ throw new NeoFaceError(
1353
+ (data == null ? void 0 : data.error) || (data == null ? void 0 : data.message) || "Falha ao concluir onboarding",
1354
+ ErrorType.NETWORK
1355
+ );
1309
1356
  }
1310
1357
  return {
1311
1358
  success: !!data.success,
@@ -1331,13 +1378,14 @@ const completeOnboarding = async (applicationToken, onboardingToken, faceImage,
1331
1378
  const completeOnboardingWithData = async (applicationToken, onboardingToken, personData, personImages, documentImage) => {
1332
1379
  ensureSecureContext();
1333
1380
  if (!personImages || personImages.length === 0) {
1334
- throw new NeoFaceError("Pelo menos uma imagem da pessoa é obrigatória", ErrorType.VALIDATION_ERROR);
1381
+ throw new NeoFaceError(
1382
+ "Pelo menos uma imagem da pessoa é obrigatória",
1383
+ ErrorType.VALIDATION_ERROR
1384
+ );
1335
1385
  }
1336
1386
  const controller = createTimeoutController();
1337
1387
  try {
1338
- const compressedPersonImages = await Promise.all(
1339
- personImages.map((img) => compressImage(img))
1340
- );
1388
+ const compressedPersonImages = await Promise.all(personImages.map((img) => compressImage(img)));
1341
1389
  const compressedDocument = await compressImage(documentImage);
1342
1390
  const formData = new FormData();
1343
1391
  compressedPersonImages.forEach((img, index) => {
@@ -1378,7 +1426,10 @@ const completeOnboardingWithData = async (applicationToken, onboardingToken, per
1378
1426
  if (response.status === 404) {
1379
1427
  throw new NeoFaceError("Link de onboarding não encontrado", ErrorType.NOT_FOUND);
1380
1428
  }
1381
- throw new NeoFaceError((data == null ? void 0 : data.error) || (data == null ? void 0 : data.message) || "Falha ao concluir onboarding", ErrorType.NETWORK);
1429
+ throw new NeoFaceError(
1430
+ (data == null ? void 0 : data.error) || (data == null ? void 0 : data.message) || "Falha ao concluir onboarding",
1431
+ ErrorType.NETWORK
1432
+ );
1382
1433
  }
1383
1434
  return {
1384
1435
  success: !!data.success,
@@ -1447,19 +1498,25 @@ const recognize = async (image, applicationToken) => {
1447
1498
  }
1448
1499
  };
1449
1500
  const loginWithBiometric$1 = async (image, applicationToken) => {
1450
- var _a2, _b, _c, _d, _e;
1501
+ var _a, _b, _c, _d, _e;
1451
1502
  ensureSecureContext();
1452
1503
  const controller = createTimeoutController();
1453
1504
  const compressedImage = await compressImage(image);
1454
1505
  try {
1455
- const formData = new FormData();
1456
- formData.append("face_frames", compressedImage);
1457
- const response = await fetch(`${API_BASE_URL}/api/v1/external/auth/login/`, {
1506
+ const arrayBuffer = await compressedImage.arrayBuffer();
1507
+ const base64Image = btoa(
1508
+ new Uint8Array(arrayBuffer).reduce((data2, byte) => data2 + String.fromCharCode(byte), "")
1509
+ );
1510
+ const response = await fetch(`${API_BASE_URL}/api/v1/login/donor/face/`, {
1458
1511
  method: "POST",
1459
1512
  headers: {
1513
+ "Content-Type": "application/json",
1460
1514
  "X-App-Token": applicationToken
1461
1515
  },
1462
- body: formData,
1516
+ body: JSON.stringify({
1517
+ image: base64Image,
1518
+ biometric_type: "FACE"
1519
+ }),
1463
1520
  signal: controller.signal
1464
1521
  });
1465
1522
  if (!response.ok) {
@@ -1477,7 +1534,7 @@ const loginWithBiometric$1 = async (image, applicationToken) => {
1477
1534
  accessToken: data.accessToken,
1478
1535
  alias: data.alias,
1479
1536
  user: {
1480
- id: ((_a2 = data.user) == null ? void 0 : _a2.id) || "",
1537
+ id: ((_a = data.user) == null ? void 0 : _a.id) || "",
1481
1538
  email: ((_b = data.user) == null ? void 0 : _b.email) || "",
1482
1539
  role: ((_c = data.user) == null ? void 0 : _c.role) || "",
1483
1540
  validated: ((_d = data.user) == null ? void 0 : _d.validated) || false,
@@ -1594,7 +1651,7 @@ const simpleIdentification = async (documentType, documentNumber, applicationTok
1594
1651
  }
1595
1652
  };
1596
1653
  const recognizeByPurpose = async (image, applicationToken, purpose, confidenceThreshold = 0.8, signature, sessionData) => {
1597
- var _a2;
1654
+ var _a;
1598
1655
  ensureSecureContext();
1599
1656
  const controller = createTimeoutController();
1600
1657
  const compressedImage = await compressImage(image);
@@ -1635,7 +1692,7 @@ const recognizeByPurpose = async (image, applicationToken, purpose, confidenceTh
1635
1692
  const data = await response.json();
1636
1693
  return {
1637
1694
  success: data.success,
1638
- personName: data.person_name || ((_a2 = data.person) == null ? void 0 : _a2.name),
1695
+ personName: data.person_name || ((_a = data.person) == null ? void 0 : _a.name),
1639
1696
  email: data.email || (sessionData == null ? void 0 : sessionData.email),
1640
1697
  cpf: data.cpf || (sessionData == null ? void 0 : sessionData.cpf),
1641
1698
  confidenceScore: data.confidence_score,
@@ -1659,24 +1716,52 @@ const recognizeByPurpose = async (image, applicationToken, purpose, confidenceTh
1659
1716
  throw new NeoFaceError("Unknown error occurred", ErrorType.NETWORK_ERROR);
1660
1717
  }
1661
1718
  };
1662
- const registerPersonWithoutFace = async (personData, applicationToken) => {
1719
+ const registerPersonWithoutFace = async (personData, applicationToken, options) => {
1663
1720
  ensureSecureContext();
1664
1721
  const controller = createTimeoutController();
1665
1722
  try {
1723
+ let documentFrontBase64;
1724
+ let documentBackBase64;
1725
+ if (options == null ? void 0 : options.documentFront) {
1726
+ const compressed = await compressDocumentImage(options.documentFront);
1727
+ documentFrontBase64 = await new Promise((resolve, reject) => {
1728
+ const reader = new FileReader();
1729
+ reader.onload = () => resolve(reader.result);
1730
+ reader.onerror = reject;
1731
+ reader.readAsDataURL(compressed);
1732
+ });
1733
+ }
1734
+ if (options == null ? void 0 : options.documentBack) {
1735
+ const compressed = await compressDocumentImage(options.documentBack);
1736
+ documentBackBase64 = await new Promise((resolve, reject) => {
1737
+ const reader = new FileReader();
1738
+ reader.onload = () => resolve(reader.result);
1739
+ reader.onerror = reject;
1740
+ reader.readAsDataURL(compressed);
1741
+ });
1742
+ }
1743
+ const body = {
1744
+ name: personData.name,
1745
+ birth_date: personData.birth_date,
1746
+ cpf: personData.cpf,
1747
+ email: personData.email,
1748
+ password: personData.password,
1749
+ password_confirm: personData.password
1750
+ };
1751
+ if (documentFrontBase64) {
1752
+ body.document_images = [{
1753
+ front: documentFrontBase64,
1754
+ back: documentBackBase64 || void 0,
1755
+ type: (options == null ? void 0 : options.documentType) || "RG"
1756
+ }];
1757
+ }
1666
1758
  const response = await fetch(`${API_BASE_URL}/api/v1/signup/donor/`, {
1667
1759
  method: "POST",
1668
1760
  headers: {
1669
1761
  "Content-Type": "application/json",
1670
1762
  "X-App-Token": applicationToken
1671
1763
  },
1672
- body: JSON.stringify({
1673
- name: personData.name,
1674
- birth_date: personData.birth_date,
1675
- cpf: personData.cpf,
1676
- email: personData.email,
1677
- password: personData.password,
1678
- password_confirm: personData.password
1679
- }),
1764
+ body: JSON.stringify(body),
1680
1765
  signal: controller.signal
1681
1766
  });
1682
1767
  if (!response.ok) {
@@ -1700,11 +1785,14 @@ const registerPersonWithoutFace = async (personData, applicationToken) => {
1700
1785
  throw new NeoFaceError("Unknown error occurred", ErrorType.NETWORK);
1701
1786
  }
1702
1787
  };
1703
- const registerPersonWithBiometric = async (personData, facePhotos, applicationToken) => {
1704
- var _a2, _b, _c, _d, _e, _f, _g, _h;
1788
+ const registerPersonWithBiometric = async (personData, facePhotos, applicationToken, options) => {
1789
+ var _a, _b, _c, _d, _e, _f, _g, _h;
1705
1790
  ensureSecureContext();
1706
1791
  if (!facePhotos || facePhotos.length === 0) {
1707
- throw new NeoFaceError("At least one face photo is required for biometric registration", ErrorType.VALIDATION_ERROR);
1792
+ throw new NeoFaceError(
1793
+ "At least one face photo is required for biometric registration",
1794
+ ErrorType.VALIDATION_ERROR
1795
+ );
1708
1796
  }
1709
1797
  if (facePhotos.length > 5) {
1710
1798
  throw new NeoFaceError("Maximum of 5 face photos allowed", ErrorType.VALIDATION_ERROR);
@@ -1725,21 +1813,49 @@ const registerPersonWithBiometric = async (personData, facePhotos, applicationTo
1725
1813
  });
1726
1814
  })
1727
1815
  );
1816
+ let documentFrontBase64;
1817
+ let documentBackBase64;
1818
+ if (options == null ? void 0 : options.documentFront) {
1819
+ const compressed = await compressDocumentImage(options.documentFront);
1820
+ documentFrontBase64 = await new Promise((resolve, reject) => {
1821
+ const reader = new FileReader();
1822
+ reader.onload = () => resolve(reader.result);
1823
+ reader.onerror = reject;
1824
+ reader.readAsDataURL(compressed);
1825
+ });
1826
+ }
1827
+ if (options == null ? void 0 : options.documentBack) {
1828
+ const compressed = await compressDocumentImage(options.documentBack);
1829
+ documentBackBase64 = await new Promise((resolve, reject) => {
1830
+ const reader = new FileReader();
1831
+ reader.onload = () => resolve(reader.result);
1832
+ reader.onerror = reject;
1833
+ reader.readAsDataURL(compressed);
1834
+ });
1835
+ }
1836
+ const body = {
1837
+ name: personData.name,
1838
+ birth_date: personData.birth_date,
1839
+ cpf: personData.cpf,
1840
+ email: personData.email,
1841
+ password: personData.password,
1842
+ password_confirm: personData.password,
1843
+ face_photos: facePhotosBase64
1844
+ };
1845
+ if (documentFrontBase64) {
1846
+ body.document_images = [{
1847
+ front: documentFrontBase64,
1848
+ back: documentBackBase64 || void 0,
1849
+ type: (options == null ? void 0 : options.documentType) || "RG"
1850
+ }];
1851
+ }
1728
1852
  const response = await fetch(`${API_BASE_URL}/api/v1/signup/donor/`, {
1729
1853
  method: "POST",
1730
1854
  headers: {
1731
1855
  "Content-Type": "application/json",
1732
1856
  "X-App-Token": applicationToken
1733
1857
  },
1734
- body: JSON.stringify({
1735
- name: personData.name,
1736
- birth_date: personData.birth_date,
1737
- cpf: personData.cpf,
1738
- email: personData.email,
1739
- password: personData.password,
1740
- password_confirm: personData.password,
1741
- face_photos: facePhotosBase64
1742
- }),
1858
+ body: JSON.stringify(body),
1743
1859
  signal: controller.signal
1744
1860
  });
1745
1861
  if (!response.ok) {
@@ -1757,7 +1873,10 @@ const registerPersonWithBiometric = async (personData, facePhotos, applicationTo
1757
1873
  throw new NeoFaceError(`Erro de validação: ${errorMessages}`, ErrorType.VALIDATION_ERROR);
1758
1874
  }
1759
1875
  if (errorData.error === "fraud_detected") {
1760
- throw new NeoFaceError("Fraud detected in biometric images. Registration blocked.", ErrorType.VALIDATION_ERROR);
1876
+ throw new NeoFaceError(
1877
+ "Fraud detected in biometric images. Registration blocked.",
1878
+ ErrorType.VALIDATION_ERROR
1879
+ );
1761
1880
  }
1762
1881
  if (errorData.error === "user_exists" || errorData.error_code === "ALREADY_EXISTS") {
1763
1882
  throw new NeoFaceError("User with this email already exists", ErrorType.VALIDATION_ERROR);
@@ -1775,7 +1894,7 @@ const registerPersonWithBiometric = async (personData, facePhotos, applicationTo
1775
1894
  }
1776
1895
  }
1777
1896
  const data = await response.json();
1778
- if (response.status === 202 && ((_a2 = data.data) == null ? void 0 : _a2.task_id)) {
1897
+ if (response.status === 202 && ((_a = data.data) == null ? void 0 : _a.task_id)) {
1779
1898
  return {
1780
1899
  success: data.success,
1781
1900
  message: data.message || "Registration is being processed. You will receive an email when completed.",
@@ -1859,7 +1978,7 @@ const loginWithEmail = async (email, password, applicationToken) => {
1859
1978
  }
1860
1979
  };
1861
1980
  const identifyPerson = async (image, applicationToken) => {
1862
- var _a2, _b;
1981
+ var _a, _b;
1863
1982
  ensureSecureContext();
1864
1983
  const controller = createTimeoutController();
1865
1984
  const compressedImage = await compressImage(image);
@@ -1893,9 +2012,12 @@ const identifyPerson = async (image, applicationToken) => {
1893
2012
  }
1894
2013
  const data = await response.json();
1895
2014
  if (!data.success) {
1896
- throw new NeoFaceError(data.message || "Person identification failed", ErrorType.RECOGNITION_FAILED);
2015
+ throw new NeoFaceError(
2016
+ data.message || "Person identification failed",
2017
+ ErrorType.RECOGNITION_FAILED
2018
+ );
1897
2019
  }
1898
- const personName = ((_a2 = data.person) == null ? void 0 : _a2.name) || "";
2020
+ const personName = ((_a = data.person) == null ? void 0 : _a.name) || "";
1899
2021
  const birthDateStr = (_b = data.person) == null ? void 0 : _b.birth_date;
1900
2022
  if (!personName) {
1901
2023
  throw new NeoFaceError("Person name not found in response", ErrorType.PERSON_NOT_FOUND);
@@ -1962,7 +2084,10 @@ const registerBiometric = async (personId, faceImage, applicationToken) => {
1962
2084
  });
1963
2085
  if (!response.ok) {
1964
2086
  if (response.status === 401 || response.status === 403) {
1965
- throw new NeoFaceError("Invalid or expired application token, or insufficient permissions", ErrorType.INVALID_TOKEN);
2087
+ throw new NeoFaceError(
2088
+ "Invalid or expired application token, or insufficient permissions",
2089
+ ErrorType.INVALID_TOKEN
2090
+ );
1966
2091
  }
1967
2092
  if (response.status === 400) {
1968
2093
  try {
@@ -2000,6 +2125,272 @@ const registerBiometric = async (personId, faceImage, applicationToken) => {
2000
2125
  throw new NeoFaceError("Unknown error occurred", ErrorType.NETWORK_ERROR);
2001
2126
  }
2002
2127
  };
2128
+ const registerApplication = async (jwtToken, consumerId, applicationData) => {
2129
+ ensureSecureContext();
2130
+ const controller = createTimeoutController();
2131
+ try {
2132
+ const response = await fetch(`${API_BASE_URL}/api/v1/applications/`, {
2133
+ method: "POST",
2134
+ headers: {
2135
+ "Content-Type": "application/json",
2136
+ Authorization: `Bearer ${jwtToken}`
2137
+ },
2138
+ body: JSON.stringify({
2139
+ consumer: consumerId,
2140
+ application_name: applicationData.applicationName,
2141
+ domain: applicationData.domain,
2142
+ accept_only_email_with_same_domain: applicationData.acceptOnlyEmailWithSameDomain
2143
+ }),
2144
+ signal: controller.signal
2145
+ });
2146
+ if (!response.ok) {
2147
+ if (response.status === 401) {
2148
+ throw new NeoFaceError("Invalid or expired JWT token", ErrorType.INVALID_TOKEN);
2149
+ }
2150
+ if (response.status === 403) {
2151
+ throw new NeoFaceError(
2152
+ "Insufficient permissions to create application",
2153
+ ErrorType.INVALID_TOKEN
2154
+ );
2155
+ }
2156
+ if (response.status === 400) {
2157
+ try {
2158
+ const errorData = await response.json();
2159
+ const errorMessage = errorData.message || errorData.error || "Invalid application data";
2160
+ throw new NeoFaceError(errorMessage, ErrorType.VALIDATION_ERROR);
2161
+ } catch (parseError) {
2162
+ if (parseError instanceof NeoFaceError) {
2163
+ throw parseError;
2164
+ }
2165
+ throw new NeoFaceError("Invalid application data", ErrorType.VALIDATION_ERROR);
2166
+ }
2167
+ }
2168
+ throw new NeoFaceError(`Server returned status ${response.status}`, ErrorType.NETWORK);
2169
+ }
2170
+ const data = await response.json();
2171
+ return {
2172
+ success: true,
2173
+ application: {
2174
+ id: data.id,
2175
+ application_name: data.application_name,
2176
+ domain: data.domain,
2177
+ accept_only_email_with_same_domain: data.accept_only_email_with_same_domain,
2178
+ app_token: data.app_token,
2179
+ is_active: data.is_active,
2180
+ created_at: data.created_at
2181
+ },
2182
+ message: "Application registered successfully"
2183
+ };
2184
+ } catch (error) {
2185
+ if (error instanceof Error) {
2186
+ if (error.name === "AbortError") {
2187
+ throw new NeoFaceError("Request timed out", ErrorType.NETWORK);
2188
+ }
2189
+ if (error instanceof NeoFaceError) {
2190
+ throw error;
2191
+ }
2192
+ throw new NeoFaceError(error.message, ErrorType.NETWORK);
2193
+ }
2194
+ throw new NeoFaceError("Unknown error occurred", ErrorType.NETWORK_ERROR);
2195
+ }
2196
+ };
2197
+ const recordFaceVideo = async (options = {}) => {
2198
+ ensureSecureContext();
2199
+ const { durationMs = 3e3, mimeType = "video/webm", onProgress } = options;
2200
+ return new Promise(async (resolve, reject) => {
2201
+ let stream = null;
2202
+ let mediaRecorder = null;
2203
+ const chunks = [];
2204
+ try {
2205
+ stream = await navigator.mediaDevices.getUserMedia({
2206
+ video: {
2207
+ facingMode: "user",
2208
+ width: { ideal: 640 },
2209
+ height: { ideal: 480 }
2210
+ },
2211
+ audio: false
2212
+ });
2213
+ let selectedMimeType = mimeType;
2214
+ const mimeTypes = ["video/webm;codecs=vp9", "video/webm;codecs=vp8", "video/webm", "video/mp4"];
2215
+ for (const type of mimeTypes) {
2216
+ if (MediaRecorder.isTypeSupported(type)) {
2217
+ selectedMimeType = type;
2218
+ break;
2219
+ }
2220
+ }
2221
+ mediaRecorder = new MediaRecorder(stream, {
2222
+ mimeType: selectedMimeType,
2223
+ videoBitsPerSecond: 1e6
2224
+ // 1 Mbps
2225
+ });
2226
+ mediaRecorder.ondataavailable = (event) => {
2227
+ if (event.data.size > 0) {
2228
+ chunks.push(event.data);
2229
+ }
2230
+ };
2231
+ mediaRecorder.onstop = () => {
2232
+ if (stream) {
2233
+ stream.getTracks().forEach((track) => track.stop());
2234
+ }
2235
+ const videoBlob = new Blob(chunks, { type: selectedMimeType });
2236
+ resolve(videoBlob);
2237
+ };
2238
+ mediaRecorder.onerror = (event) => {
2239
+ if (stream) {
2240
+ stream.getTracks().forEach((track) => track.stop());
2241
+ }
2242
+ reject(new NeoFaceError(`Recording error: ${event}`, ErrorType.CAPTURE_ERROR));
2243
+ };
2244
+ mediaRecorder.start(100);
2245
+ if (onProgress) {
2246
+ const progressInterval = 100;
2247
+ let elapsed = 0;
2248
+ const progressTimer = setInterval(() => {
2249
+ elapsed += progressInterval;
2250
+ const progress = Math.min(100, elapsed / durationMs * 100);
2251
+ onProgress(progress);
2252
+ if (elapsed >= durationMs) {
2253
+ clearInterval(progressTimer);
2254
+ }
2255
+ }, progressInterval);
2256
+ }
2257
+ setTimeout(() => {
2258
+ if (mediaRecorder && mediaRecorder.state === "recording") {
2259
+ mediaRecorder.stop();
2260
+ }
2261
+ }, durationMs);
2262
+ } catch (error) {
2263
+ if (stream) {
2264
+ stream.getTracks().forEach((track) => track.stop());
2265
+ }
2266
+ if (error instanceof Error) {
2267
+ if (error.name === "NotAllowedError") {
2268
+ reject(new NeoFaceError("Camera permission denied", ErrorType.CAMERA_ERROR));
2269
+ } else if (error.name === "NotFoundError") {
2270
+ reject(new NeoFaceError("No camera found", ErrorType.NO_CAMERA));
2271
+ } else {
2272
+ reject(new NeoFaceError(error.message, ErrorType.CAPTURE_ERROR));
2273
+ }
2274
+ } else {
2275
+ reject(new NeoFaceError("Failed to record video", ErrorType.CAPTURE_ERROR));
2276
+ }
2277
+ }
2278
+ });
2279
+ };
2280
+ const pollTaskStatus = async (taskId, applicationToken, options = {}) => {
2281
+ var _a, _b, _c, _d, _e, _f;
2282
+ const { maxAttempts = 60, intervalMs = 1e3, onStatusChange } = options;
2283
+ let attempts = 0;
2284
+ while (attempts < maxAttempts) {
2285
+ attempts++;
2286
+ try {
2287
+ const response = await fetch(`${API_BASE_URL}/api/v1/tasks/status/?task_id=${taskId}`, {
2288
+ method: "GET",
2289
+ headers: {
2290
+ Accept: "application/json",
2291
+ "X-App-Token": applicationToken
2292
+ }
2293
+ });
2294
+ if (!response.ok) {
2295
+ throw new NeoFaceError(`Task status check failed: ${response.status}`, ErrorType.NETWORK);
2296
+ }
2297
+ const data = await response.json();
2298
+ const status = ((_a = data.data) == null ? void 0 : _a.status) || "unknown";
2299
+ if (onStatusChange) {
2300
+ const progress = (_c = (_b = data.data) == null ? void 0 : _b.progress) == null ? void 0 : _c.percentage;
2301
+ onStatusChange(status, progress);
2302
+ }
2303
+ if (status === "success") {
2304
+ return data.data.result;
2305
+ }
2306
+ if (status === "failure") {
2307
+ const errorMessage = ((_e = (_d = data.data) == null ? void 0 : _d.result) == null ? void 0 : _e.message) || ((_f = data.data) == null ? void 0 : _f.error) || "Task failed";
2308
+ throw new NeoFaceError(errorMessage, ErrorType.RECOGNITION_FAILED);
2309
+ }
2310
+ if (status === "revoked") {
2311
+ throw new NeoFaceError("Task was cancelled", ErrorType.RECOGNITION_FAILED);
2312
+ }
2313
+ await new Promise((resolve) => setTimeout(resolve, intervalMs));
2314
+ } catch (error) {
2315
+ if (error instanceof NeoFaceError) {
2316
+ throw error;
2317
+ }
2318
+ throw new NeoFaceError(
2319
+ `Polling error: ${error instanceof Error ? error.message : "Unknown error"}`,
2320
+ ErrorType.NETWORK
2321
+ );
2322
+ }
2323
+ }
2324
+ throw new NeoFaceError("Task status polling timed out", ErrorType.NETWORK);
2325
+ };
2326
+ const startProofOfLife = async (videoBase64, applicationToken) => {
2327
+ var _a;
2328
+ ensureSecureContext();
2329
+ const controller = createTimeoutController();
2330
+ try {
2331
+ const response = await fetch(`${API_BASE_URL}/api/v1/auth/recognition/face/external/`, {
2332
+ method: "POST",
2333
+ headers: {
2334
+ "Content-Type": "application/json",
2335
+ "X-App-Token": applicationToken
2336
+ },
2337
+ body: JSON.stringify({
2338
+ video: videoBase64,
2339
+ purpose: "PROOF_OF_LIFE"
2340
+ }),
2341
+ signal: controller.signal
2342
+ });
2343
+ if (!response.ok) {
2344
+ if (response.status === 401 || response.status === 403) {
2345
+ throw new NeoFaceError("Invalid or expired application token", ErrorType.INVALID_TOKEN);
2346
+ }
2347
+ if (response.status === 400) {
2348
+ const errorData = await response.json();
2349
+ throw new NeoFaceError(
2350
+ errorData.message || "Invalid request data",
2351
+ ErrorType.VALIDATION_ERROR
2352
+ );
2353
+ }
2354
+ throw new NeoFaceError(`Server returned status ${response.status}`, ErrorType.NETWORK);
2355
+ }
2356
+ const data = await response.json();
2357
+ if (!data.success || !((_a = data.data) == null ? void 0 : _a.task_id)) {
2358
+ throw new NeoFaceError(
2359
+ data.message || "Failed to start proof of life",
2360
+ ErrorType.RECOGNITION_FAILED
2361
+ );
2362
+ }
2363
+ return {
2364
+ taskId: data.data.task_id,
2365
+ historyId: data.data.history_id,
2366
+ status: data.data.status,
2367
+ message: data.message || "Recognition started successfully"
2368
+ };
2369
+ } catch (error) {
2370
+ if (error instanceof Error) {
2371
+ if (error.name === "AbortError") {
2372
+ throw new NeoFaceError("Request timed out", ErrorType.NETWORK);
2373
+ }
2374
+ if (error instanceof NeoFaceError) {
2375
+ throw error;
2376
+ }
2377
+ throw new NeoFaceError(error.message, ErrorType.NETWORK);
2378
+ }
2379
+ throw new NeoFaceError("Unknown error occurred", ErrorType.NETWORK_ERROR);
2380
+ }
2381
+ };
2382
+ const videoToBase64 = async (videoBlob) => {
2383
+ return new Promise((resolve, reject) => {
2384
+ const reader = new FileReader();
2385
+ reader.onload = () => {
2386
+ const result = reader.result;
2387
+ const base64 = result.includes(",") ? result.split(",")[1] : result;
2388
+ resolve(base64);
2389
+ };
2390
+ reader.onerror = () => reject(new NeoFaceError("Failed to convert video to base64", ErrorType.CAPTURE_ERROR));
2391
+ reader.readAsDataURL(videoBlob);
2392
+ });
2393
+ };
2003
2394
  const modalReducer$1 = (state, action) => {
2004
2395
  switch (action.type) {
2005
2396
  case "PERMISSION_GRANTED":
@@ -2146,8 +2537,7 @@ function FaceCaptureModal({ accessToken, onClose, onSuccess }) {
2146
2537
  const [livenessAttempts, setLivenessAttempts] = useState(0);
2147
2538
  const { startCamera, stopCamera } = useCamera();
2148
2539
  const isFaceCentered = (detection) => {
2149
- if (!videoRef.current)
2150
- return false;
2540
+ if (!videoRef.current) return false;
2151
2541
  const { box } = detection;
2152
2542
  const { videoWidth, videoHeight } = videoRef.current;
2153
2543
  const centerX = videoWidth / 2;
@@ -2165,11 +2555,9 @@ function FaceCaptureModal({ accessToken, onClose, onSuccess }) {
2165
2555
  }, 3e3);
2166
2556
  };
2167
2557
  const startFaceDetection = () => {
2168
- if (!videoRef.current)
2169
- return;
2558
+ if (!videoRef.current) return;
2170
2559
  const detectFaces = async () => {
2171
- if (!videoRef.current || state.status !== "ready")
2172
- return;
2560
+ if (!videoRef.current || state.status !== "ready") return;
2173
2561
  try {
2174
2562
  const detections = await faceapi.detectAllFaces(
2175
2563
  videoRef.current,
@@ -2231,8 +2619,7 @@ function FaceCaptureModal({ accessToken, onClose, onSuccess }) {
2231
2619
  };
2232
2620
  }, [startCamera, stopCamera]);
2233
2621
  const handleCapture = async () => {
2234
- if (!canvasRef.current || !videoRef.current)
2235
- return;
2622
+ if (!canvasRef.current || !videoRef.current) return;
2236
2623
  try {
2237
2624
  dispatch({ type: "START_CAPTURE" });
2238
2625
  const context = canvasRef.current.getContext("2d");
@@ -2244,13 +2631,11 @@ function FaceCaptureModal({ accessToken, onClose, onSuccess }) {
2244
2631
  canvasRef.current.height = videoRef.current.videoHeight;
2245
2632
  context.drawImage(videoRef.current, 0, 0, canvasRef.current.width, canvasRef.current.height);
2246
2633
  const blob = await new Promise((resolve, reject) => {
2247
- var _a2;
2248
- (_a2 = canvasRef.current) == null ? void 0 : _a2.toBlob(
2634
+ var _a;
2635
+ (_a = canvasRef.current) == null ? void 0 : _a.toBlob(
2249
2636
  (b) => {
2250
- if (b)
2251
- resolve(b);
2252
- else
2253
- reject(new Error("Failed to convert canvas to blob"));
2637
+ if (b) resolve(b);
2638
+ else reject(new Error("Failed to convert canvas to blob"));
2254
2639
  },
2255
2640
  "image/jpeg",
2256
2641
  0.95
@@ -2381,6 +2766,7 @@ function BiometricRegistrationModal({
2381
2766
  applicationToken,
2382
2767
  onClose,
2383
2768
  onSuccess,
2769
+ onPhotosCaptured,
2384
2770
  onError,
2385
2771
  useRealApi = true
2386
2772
  // Sempre usa API real em produção
@@ -2485,8 +2871,7 @@ function BiometricRegistrationModal({
2485
2871
  clearInterval(progressIntervalRef.current);
2486
2872
  }
2487
2873
  progressIntervalRef.current = window.setInterval(() => {
2488
- if (!holdStartTimeRef.current)
2489
- return;
2874
+ if (!holdStartTimeRef.current) return;
2490
2875
  const elapsed = Date.now() - holdStartTimeRef.current;
2491
2876
  const progress = Math.min(elapsed / CALIBRATION.CAPTURE_HOLD_TIME * 100, 100);
2492
2877
  dispatch({ type: "UPDATE_PROGRESS", progress });
@@ -2525,8 +2910,7 @@ function BiometricRegistrationModal({
2525
2910
  useEffect(() => {
2526
2911
  let mounted = true;
2527
2912
  const initCamera = async () => {
2528
- if (state.status !== "liveness" || streamRef.current)
2529
- return;
2913
+ if (state.status !== "liveness" || streamRef.current) return;
2530
2914
  try {
2531
2915
  const stream = await navigator.mediaDevices.getUserMedia({
2532
2916
  video: {
@@ -2555,8 +2939,7 @@ function BiometricRegistrationModal({
2555
2939
  streamRef.current = stream;
2556
2940
  await video.play();
2557
2941
  } catch (error) {
2558
- if (!mounted)
2559
- return;
2942
+ if (!mounted) return;
2560
2943
  const errorMessage = error instanceof Error ? error.message : "Erro ao acessar câmera";
2561
2944
  dispatch({ type: "ERROR", message: errorMessage });
2562
2945
  }
@@ -2603,8 +2986,7 @@ function BiometricRegistrationModal({
2603
2986
  scoreThreshold: 0.4
2604
2987
  })
2605
2988
  ).withFaceLandmarks();
2606
- if (!isRunning)
2607
- return;
2989
+ if (!isRunning) return;
2608
2990
  const hasFace = !!detection;
2609
2991
  setFaceDetected(hasFace);
2610
2992
  if (hasFace && detection.detection) {
@@ -2645,8 +3027,8 @@ function BiometricRegistrationModal({
2645
3027
  }
2646
3028
  };
2647
3029
  const startTimer = setTimeout(() => {
2648
- var _a2;
2649
- if (isRunning && ((_a2 = videoRef.current) == null ? void 0 : _a2.readyState) === 4) {
3030
+ var _a;
3031
+ if (isRunning && ((_a = videoRef.current) == null ? void 0 : _a.readyState) === 4) {
2650
3032
  detectFace2();
2651
3033
  }
2652
3034
  }, 1e3);
@@ -2707,6 +3089,14 @@ function BiometricRegistrationModal({
2707
3089
  };
2708
3090
  const processRegistration = async (photos) => {
2709
3091
  try {
3092
+ if (onPhotosCaptured) {
3093
+ dispatch({ type: "SUCCESS" });
3094
+ onPhotosCaptured(photos);
3095
+ return;
3096
+ }
3097
+ if (!personData || !applicationToken) {
3098
+ throw new Error("Dados de registro não fornecidos");
3099
+ }
2710
3100
  console.log("🚀 Fazendo requisição ao backend...");
2711
3101
  const result = await registerPersonWithBiometric(personData, photos, applicationToken);
2712
3102
  const adaptedResult = {
@@ -2719,7 +3109,9 @@ function BiometricRegistrationModal({
2719
3109
  onboarding_link: result.onboarding_link
2720
3110
  };
2721
3111
  dispatch({ type: "SUCCESS" });
2722
- onSuccess(adaptedResult);
3112
+ if (onSuccess) {
3113
+ onSuccess(adaptedResult);
3114
+ }
2723
3115
  } catch (error) {
2724
3116
  const errorMessage = error instanceof Error ? error.message : "Erro desconhecido";
2725
3117
  dispatch({ type: "ERROR", message: errorMessage });
@@ -2727,8 +3119,7 @@ function BiometricRegistrationModal({
2727
3119
  }
2728
3120
  };
2729
3121
  const renderLivenessDetection = () => {
2730
- if (state.status !== "liveness")
2731
- return null;
3122
+ if (state.status !== "liveness") return null;
2732
3123
  const progress = `${state.currentStepIndex + 1}/${state.stepSequence.length - 1}`;
2733
3124
  const stepProgress = state.stepProgress;
2734
3125
  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "liveness-container", children: [
@@ -2790,8 +3181,7 @@ function BiometricRegistrationModal({
2790
3181
  )
2791
3182
  ] });
2792
3183
  const renderError = () => {
2793
- if (state.status !== "error")
2794
- return null;
3184
+ if (state.status !== "error") return null;
2795
3185
  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "result-container error", children: [
2796
3186
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "result-icon error-icon", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("svg", { width: "60", height: "60", viewBox: "0 0 60 60", fill: "none", children: [
2797
3187
  /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "30", cy: "30", r: "28", stroke: "currentColor", strokeWidth: "3" }),
@@ -2827,8 +3217,8 @@ function BiometricRegistrationModal({
2827
3217
  }
2828
3218
  )
2829
3219
  ] }) }),
2830
- /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { children: "Verificação concluída!" }),
2831
- /* @__PURE__ */ jsxRuntimeExports.jsx("p", { children: "Sua biometria foi registrada com sucesso" }),
3220
+ /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { children: onPhotosCaptured ? "Captura concluída!" : "Verificação concluída!" }),
3221
+ /* @__PURE__ */ jsxRuntimeExports.jsx("p", { children: onPhotosCaptured ? "Suas capturas faciais foram realizadas" : "Sua biometria foi registrada com sucesso" }),
2832
3222
  /* @__PURE__ */ jsxRuntimeExports.jsx("button", { className: "primary-button", onClick: onClose, children: "Continuar" })
2833
3223
  ] });
2834
3224
  return /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
@@ -3485,8 +3875,7 @@ let BiometricCaptureModal$1 = class BiometricCaptureModal2 {
3485
3875
  * Inicializa a câmera
3486
3876
  */
3487
3877
  async initializeCamera() {
3488
- if (!this.modal)
3489
- throw new Error("Modal não inicializado");
3878
+ if (!this.modal) throw new Error("Modal não inicializado");
3490
3879
  this.video = this.modal.querySelector(".neofaceid-video");
3491
3880
  this.canvas = this.modal.querySelector(".neofaceid-canvas");
3492
3881
  if (!this.video || !this.canvas) {
@@ -3513,8 +3902,7 @@ let BiometricCaptureModal$1 = class BiometricCaptureModal2 {
3513
3902
  * Inicia a contagem regressiva
3514
3903
  */
3515
3904
  startCountdown() {
3516
- if (!this.modal)
3517
- return;
3905
+ if (!this.modal) return;
3518
3906
  const countdownElement = this.modal.querySelector(".neofaceid-countdown-number");
3519
3907
  const captureBtn = this.modal.querySelector(".neofaceid-capture-btn");
3520
3908
  const statusText = this.modal.querySelector(".neofaceid-status-text");
@@ -3552,13 +3940,11 @@ let BiometricCaptureModal$1 = class BiometricCaptureModal2 {
3552
3940
  * Captura a imagem da câmera
3553
3941
  */
3554
3942
  async captureImage() {
3555
- if (this.isCapturing || !this.video || !this.canvas)
3556
- return;
3943
+ if (this.isCapturing || !this.video || !this.canvas) return;
3557
3944
  this.isCapturing = true;
3558
3945
  try {
3559
3946
  const context = this.canvas.getContext("2d");
3560
- if (!context)
3561
- throw new Error("Não foi possível obter contexto do canvas");
3947
+ if (!context) throw new Error("Não foi possível obter contexto do canvas");
3562
3948
  context.drawImage(this.video, 0, 0, this.canvas.width, this.canvas.height);
3563
3949
  const imageData = this.canvas.toDataURL("image/jpeg", 0.8);
3564
3950
  const base64Data = imageData.split(",")[1];
@@ -3597,27 +3983,26 @@ let BiometricCaptureModal$1 = class BiometricCaptureModal2 {
3597
3983
  * Adiciona event listeners aos elementos do modal
3598
3984
  */
3599
3985
  addEventListeners() {
3600
- if (!this.modal)
3601
- return;
3986
+ if (!this.modal) return;
3602
3987
  const closeBtn = this.modal.querySelector(".neofaceid-close-btn");
3603
3988
  const cancelBtn = this.modal.querySelector(".neofaceid-cancel-btn");
3604
3989
  const captureBtn = this.modal.querySelector(".neofaceid-capture-btn");
3605
3990
  closeBtn == null ? void 0 : closeBtn.addEventListener("click", () => {
3606
- var _a2, _b;
3991
+ var _a, _b;
3607
3992
  this.close();
3608
- (_b = (_a2 = this.options).onCancel) == null ? void 0 : _b.call(_a2);
3993
+ (_b = (_a = this.options).onCancel) == null ? void 0 : _b.call(_a);
3609
3994
  });
3610
3995
  cancelBtn == null ? void 0 : cancelBtn.addEventListener("click", () => {
3611
- var _a2, _b;
3996
+ var _a, _b;
3612
3997
  this.close();
3613
- (_b = (_a2 = this.options).onCancel) == null ? void 0 : _b.call(_a2);
3998
+ (_b = (_a = this.options).onCancel) == null ? void 0 : _b.call(_a);
3614
3999
  });
3615
4000
  captureBtn == null ? void 0 : captureBtn.addEventListener("click", () => {
3616
4001
  this.captureImage();
3617
4002
  });
3618
4003
  this.modal.addEventListener("click", (e) => {
3619
- var _a2, _b, _c;
3620
- if (e.target === ((_a2 = this.modal) == null ? void 0 : _a2.querySelector(".neofaceid-modal-overlay"))) {
4004
+ var _a, _b, _c;
4005
+ if (e.target === ((_a = this.modal) == null ? void 0 : _a.querySelector(".neofaceid-modal-overlay"))) {
3621
4006
  this.close();
3622
4007
  (_c = (_b = this.options).onCancel) == null ? void 0 : _c.call(_b);
3623
4008
  }
@@ -3901,266 +4286,190 @@ let BiometricCaptureModal$1 = class BiometricCaptureModal2 {
3901
4286
  document.head.appendChild(style);
3902
4287
  }
3903
4288
  };
3904
- function BiometricStatusOverlayComponent({ status, onClose }) {
3905
- const [isVisible, setIsVisible] = useState(true);
3906
- useEffect(() => {
3907
- if (status === "success") {
3908
- const timer = setTimeout(() => {
3909
- setIsVisible(false);
3910
- setTimeout(() => {
3911
- onClose == null ? void 0 : onClose();
3912
- }, 300);
3913
- }, 800);
3914
- return () => clearTimeout(timer);
3915
- }
3916
- }, [status, onClose]);
3917
- if (!isVisible)
3918
- return null;
3919
- return /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
3920
- /* @__PURE__ */ jsxRuntimeExports.jsx("style", { children: `
3921
- @keyframes fadeIn {
3922
- from { opacity: 0; transform: scale(0.9); }
3923
- to { opacity: 1; transform: scale(1); }
3924
- }
4289
+ class BiometricStatusOverlay {
4290
+ constructor() {
4291
+ __publicField(this, "container", null);
4292
+ __publicField(this, "currentStatus", "preparing");
4293
+ }
4294
+ getStyles() {
4295
+ return `
4296
+ @keyframes neofaceid-fadeIn {
4297
+ from { opacity: 0; transform: scale(0.9); }
4298
+ to { opacity: 1; transform: scale(1); }
4299
+ }
3925
4300
 
3926
- @keyframes fadeOut {
3927
- from { opacity: 1; transform: scale(1); }
3928
- to { opacity: 0; transform: scale(0.9); }
3929
- }
4301
+ @keyframes neofaceid-fadeOut {
4302
+ from { opacity: 1; transform: scale(1); }
4303
+ to { opacity: 0; transform: scale(0.9); }
4304
+ }
3930
4305
 
3931
- @keyframes pulse {
3932
- 0%, 100% {
3933
- transform: scale(1);
3934
- opacity: 1;
3935
- }
3936
- 50% {
3937
- transform: scale(1.1);
3938
- opacity: 0.9;
3939
- }
4306
+ @keyframes neofaceid-pulse {
4307
+ 0%, 100% {
4308
+ transform: scale(1);
4309
+ opacity: 1;
3940
4310
  }
3941
-
3942
- @keyframes spin {
3943
- from { transform: rotate(0deg); }
3944
- to { transform: rotate(360deg); }
4311
+ 50% {
4312
+ transform: scale(1.1);
4313
+ opacity: 0.9;
3945
4314
  }
4315
+ }
3946
4316
 
3947
- @keyframes checkmark {
3948
- 0% {
3949
- stroke-dashoffset: 100;
3950
- opacity: 0;
3951
- }
3952
- 50% {
3953
- opacity: 1;
3954
- }
3955
- 100% {
3956
- stroke-dashoffset: 0;
3957
- opacity: 1;
3958
- }
4317
+ @keyframes neofaceid-checkmark {
4318
+ 0% {
4319
+ stroke-dashoffset: 100;
4320
+ opacity: 0;
3959
4321
  }
3960
-
3961
- @keyframes errorShake {
3962
- 0%, 100% { transform: translateX(0) scale(1); }
3963
- 10%, 30%, 50%, 70%, 90% { transform: translateX(-4px) scale(1); }
3964
- 20%, 40%, 60%, 80% { transform: translateX(4px) scale(1); }
4322
+ 50% {
4323
+ opacity: 1;
3965
4324
  }
3966
-
3967
- @keyframes scaleIn {
3968
- 0% {
3969
- transform: scale(0);
3970
- opacity: 0;
3971
- }
3972
- 50% {
3973
- transform: scale(1.1);
3974
- }
3975
- 100% {
3976
- transform: scale(1);
3977
- opacity: 1;
3978
- }
4325
+ 100% {
4326
+ stroke-dashoffset: 0;
4327
+ opacity: 1;
3979
4328
  }
4329
+ }
3980
4330
 
3981
- .neofaceid-overlay-container {
3982
- position: fixed;
3983
- top: 0;
3984
- left: 0;
3985
- right: 0;
3986
- bottom: 0;
3987
- display: flex;
3988
- align-items: center;
3989
- justify-content: center;
3990
- z-index: 10000;
3991
- pointer-events: none;
3992
- animation: fadeIn 0.3s ease-out;
3993
- }
4331
+ @keyframes neofaceid-errorShake {
4332
+ 0%, 100% { transform: translateX(0) scale(1); }
4333
+ 10%, 30%, 50%, 70%, 90% { transform: translateX(-4px) scale(1); }
4334
+ 20%, 40%, 60%, 80% { transform: translateX(4px) scale(1); }
4335
+ }
3994
4336
 
3995
- .neofaceid-overlay-container.fade-out {
3996
- animation: fadeOut 0.3s ease-out;
4337
+ @keyframes neofaceid-scaleIn {
4338
+ 0% {
4339
+ transform: scale(0);
4340
+ opacity: 0;
3997
4341
  }
3998
-
3999
- .neofaceid-overlay-content {
4000
- display: flex;
4001
- flex-direction: column;
4002
- align-items: center;
4003
- justify-content: center;
4004
- gap: 24px;
4005
- pointer-events: auto;
4342
+ 50% {
4343
+ transform: scale(1.1);
4006
4344
  }
4007
-
4008
- .neofaceid-logo-wrapper {
4009
- position: relative;
4010
- width: 120px;
4011
- height: 120px;
4012
- display: flex;
4013
- align-items: center;
4014
- justify-content: center;
4345
+ 100% {
4346
+ transform: scale(1);
4347
+ opacity: 1;
4015
4348
  }
4349
+ }
4016
4350
 
4017
- .neofaceid-logo {
4018
- width: 80px;
4019
- height: 80px;
4020
- object-fit: contain;
4021
- filter: drop-shadow(0 4px 12px rgba(124, 58, 237, 0.3));
4022
- }
4351
+ .neofaceid-overlay-container {
4352
+ position: fixed;
4353
+ top: 0;
4354
+ left: 0;
4355
+ right: 0;
4356
+ bottom: 0;
4357
+ display: flex;
4358
+ align-items: center;
4359
+ justify-content: center;
4360
+ z-index: 10000;
4361
+ pointer-events: none;
4362
+ animation: neofaceid-fadeIn 0.3s ease-out;
4363
+ }
4023
4364
 
4024
- .neofaceid-logo.pulsing {
4025
- animation: pulse 2s ease-in-out infinite;
4026
- }
4365
+ .neofaceid-overlay-container.fade-out {
4366
+ animation: neofaceid-fadeOut 0.3s ease-out forwards;
4367
+ }
4027
4368
 
4028
- .neofaceid-status-icon {
4029
- position: absolute;
4030
- inset: 0;
4031
- display: flex;
4032
- align-items: center;
4033
- justify-content: center;
4034
- }
4369
+ .neofaceid-overlay-content {
4370
+ display: flex;
4371
+ flex-direction: column;
4372
+ align-items: center;
4373
+ justify-content: center;
4374
+ gap: 24px;
4375
+ pointer-events: auto;
4376
+ }
4035
4377
 
4036
- .neofaceid-success-icon {
4037
- width: 60px;
4038
- height: 60px;
4039
- color: #10b981;
4040
- animation: scaleIn 0.6s ease-out;
4041
- }
4378
+ .neofaceid-logo-wrapper {
4379
+ position: relative;
4380
+ width: 120px;
4381
+ height: 120px;
4382
+ display: flex;
4383
+ align-items: center;
4384
+ justify-content: center;
4385
+ }
4042
4386
 
4043
- .neofaceid-success-icon svg circle {
4044
- stroke-dasharray: 100;
4045
- stroke-dashoffset: 100;
4046
- animation: checkmark 0.6s ease-out 0.2s forwards;
4047
- }
4387
+ .neofaceid-logo {
4388
+ width: 80px;
4389
+ height: 80px;
4390
+ object-fit: contain;
4391
+ filter: drop-shadow(0 4px 12px rgba(124, 58, 237, 0.3));
4392
+ }
4048
4393
 
4049
- .neofaceid-error-icon {
4050
- width: 60px;
4051
- height: 60px;
4052
- color: #ef4444;
4053
- animation: errorShake 0.5s ease-out, scaleIn 0.4s ease-out;
4054
- }
4394
+ .neofaceid-logo.pulsing {
4395
+ animation: neofaceid-pulse 2s ease-in-out infinite;
4396
+ }
4055
4397
 
4056
- /* Responsive */
4057
- @media (max-width: 640px) {
4058
- .neofaceid-logo-wrapper {
4059
- width: 100px;
4060
- height: 100px;
4061
- }
4398
+ .neofaceid-status-icon {
4399
+ position: absolute;
4400
+ inset: 0;
4401
+ display: flex;
4402
+ align-items: center;
4403
+ justify-content: center;
4404
+ }
4062
4405
 
4063
- .neofaceid-logo {
4064
- width: 70px;
4065
- height: 70px;
4066
- }
4067
- }
4068
- ` }),
4069
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: `neofaceid-overlay-container ${!isVisible ? "fade-out" : ""}`, children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "neofaceid-overlay-content", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "neofaceid-logo-wrapper", children: [
4070
- (status === "preparing" || status === "verifying") && /* @__PURE__ */ jsxRuntimeExports.jsx(jsxRuntimeExports.Fragment, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(
4071
- "img",
4072
- {
4073
- src: "/public/logo-icone-no-backgorund.png",
4074
- alt: "NeoFaceId",
4075
- className: `neofaceid-logo ${status === "verifying" ? "pulsing" : ""}`,
4076
- onError: (e) => {
4077
- var _a2;
4078
- const img = e.target;
4079
- img.style.display = "none";
4080
- const fallback = document.createElement("div");
4081
- fallback.innerHTML = `
4082
- <svg width="80" height="80" viewBox="0 0 80 80" fill="none" xmlns="http://www.w3.org/2000/svg">
4083
- <circle cx="40" cy="40" r="35" fill="#7c3aed" opacity="0.2"/>
4084
- <path d="M40 20 L40 60 M20 40 L60 40" stroke="#7c3aed" stroke-width="4" stroke-linecap="round"/>
4085
- </svg>
4086
- `;
4087
- fallback.style.cssText = "width: 80px; height: 80px; display: flex; align-items: center; justify-content: center;";
4088
- (_a2 = img.parentElement) == null ? void 0 : _a2.appendChild(fallback);
4089
- }
4090
- }
4091
- ) }),
4092
- status === "success" && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "neofaceid-status-icon", children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
4093
- "svg",
4094
- {
4095
- className: "neofaceid-success-icon",
4096
- viewBox: "0 0 60 60",
4097
- fill: "none",
4098
- xmlns: "http://www.w3.org/2000/svg",
4099
- children: [
4100
- /* @__PURE__ */ jsxRuntimeExports.jsx(
4101
- "circle",
4102
- {
4103
- cx: "30",
4104
- cy: "30",
4105
- r: "28",
4106
- stroke: "currentColor",
4107
- strokeWidth: "3",
4108
- fill: "none"
4109
- }
4110
- ),
4111
- /* @__PURE__ */ jsxRuntimeExports.jsx(
4112
- "path",
4113
- {
4114
- d: "M15 30 L25 40 L45 20",
4115
- stroke: "currentColor",
4116
- strokeWidth: "3",
4117
- strokeLinecap: "round",
4118
- strokeLinejoin: "round",
4119
- fill: "none"
4120
- }
4121
- )
4122
- ]
4406
+ .neofaceid-success-icon {
4407
+ width: 60px;
4408
+ height: 60px;
4409
+ color: #10b981;
4410
+ animation: neofaceid-scaleIn 0.6s ease-out;
4411
+ }
4412
+
4413
+ .neofaceid-error-icon {
4414
+ width: 60px;
4415
+ height: 60px;
4416
+ color: #ef4444;
4417
+ animation: neofaceid-errorShake 0.5s ease-out, neofaceid-scaleIn 0.4s ease-out;
4418
+ }
4419
+
4420
+ @media (max-width: 640px) {
4421
+ .neofaceid-logo-wrapper {
4422
+ width: 100px;
4423
+ height: 100px;
4123
4424
  }
4124
- ) }),
4125
- status === "error" && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "neofaceid-status-icon", children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
4126
- "svg",
4127
- {
4128
- className: "neofaceid-error-icon",
4129
- viewBox: "0 0 60 60",
4130
- fill: "none",
4131
- xmlns: "http://www.w3.org/2000/svg",
4132
- children: [
4133
- /* @__PURE__ */ jsxRuntimeExports.jsx(
4134
- "circle",
4135
- {
4136
- cx: "30",
4137
- cy: "30",
4138
- r: "28",
4139
- stroke: "currentColor",
4140
- strokeWidth: "3",
4141
- fill: "none"
4142
- }
4143
- ),
4144
- /* @__PURE__ */ jsxRuntimeExports.jsx(
4145
- "path",
4146
- {
4147
- d: "M20 20 L40 40 M40 20 L20 40",
4148
- stroke: "currentColor",
4149
- strokeWidth: "3",
4150
- strokeLinecap: "round"
4151
- }
4152
- )
4153
- ]
4425
+
4426
+ .neofaceid-logo {
4427
+ width: 70px;
4428
+ height: 70px;
4154
4429
  }
4155
- ) })
4156
- ] }) }) })
4157
- ] });
4158
- }
4159
- class BiometricStatusOverlay {
4160
- constructor() {
4161
- __publicField(this, "container", null);
4162
- __publicField(this, "root", null);
4163
- __publicField(this, "currentStatus", "preparing");
4430
+ }
4431
+ `;
4432
+ }
4433
+ renderContent() {
4434
+ const isPulsing = this.currentStatus === "verifying";
4435
+ const showLogo = this.currentStatus === "preparing" || this.currentStatus === "verifying";
4436
+ const showSuccess = this.currentStatus === "success";
4437
+ const showError = this.currentStatus === "error";
4438
+ return `
4439
+ <style>${this.getStyles()}</style>
4440
+ <div class="neofaceid-overlay-container">
4441
+ <div class="neofaceid-overlay-content">
4442
+ <div class="neofaceid-logo-wrapper">
4443
+ ${showLogo ? `
4444
+ <img
4445
+ src="/logo-icone-no-backgorund.png"
4446
+ alt="NeoFaceId"
4447
+ class="neofaceid-logo ${isPulsing ? "pulsing" : ""}"
4448
+ onerror="this.style.display='none'"
4449
+ />
4450
+ ` : ""}
4451
+
4452
+ ${showSuccess ? `
4453
+ <div class="neofaceid-status-icon">
4454
+ <svg class="neofaceid-success-icon" viewBox="0 0 60 60" fill="none" xmlns="http://www.w3.org/2000/svg">
4455
+ <circle cx="30" cy="30" r="28" stroke="currentColor" stroke-width="3" fill="none"/>
4456
+ <path d="M15 30 L25 40 L45 20" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" fill="none"/>
4457
+ </svg>
4458
+ </div>
4459
+ ` : ""}
4460
+
4461
+ ${showError ? `
4462
+ <div class="neofaceid-status-icon">
4463
+ <svg class="neofaceid-error-icon" viewBox="0 0 60 60" fill="none" xmlns="http://www.w3.org/2000/svg">
4464
+ <circle cx="30" cy="30" r="28" stroke="currentColor" stroke-width="3" fill="none"/>
4465
+ <path d="M20 20 L40 40 M40 20 L20 40" stroke="currentColor" stroke-width="3" stroke-linecap="round"/>
4466
+ </svg>
4467
+ </div>
4468
+ ` : ""}
4469
+ </div>
4470
+ </div>
4471
+ </div>
4472
+ `;
4164
4473
  }
4165
4474
  /**
4166
4475
  * Cria e exibe o overlay
@@ -4172,18 +4481,17 @@ class BiometricStatusOverlay {
4172
4481
  }
4173
4482
  this.container = document.createElement("div");
4174
4483
  this.container.id = "neofaceid-status-overlay";
4175
- document.body.appendChild(this.container);
4176
- this.root = createRoot(this.container);
4177
4484
  this.currentStatus = status;
4178
- this.render();
4485
+ this.container.innerHTML = this.renderContent();
4486
+ document.body.appendChild(this.container);
4179
4487
  }
4180
4488
  /**
4181
4489
  * Atualiza o status do overlay
4182
4490
  */
4183
4491
  updateStatus(status) {
4184
4492
  this.currentStatus = status;
4185
- if (this.root) {
4186
- this.render();
4493
+ if (this.container) {
4494
+ this.container.innerHTML = this.renderContent();
4187
4495
  }
4188
4496
  }
4189
4497
  /**
@@ -4191,42 +4499,18 @@ class BiometricStatusOverlay {
4191
4499
  */
4192
4500
  close() {
4193
4501
  if (this.container) {
4194
- if (this.root) {
4195
- this.root.render(
4196
- /* @__PURE__ */ jsxRuntimeExports.jsx(
4197
- BiometricStatusOverlayComponent,
4198
- {
4199
- status: this.currentStatus,
4200
- onClose: () => {
4201
- if (this.container && document.body.contains(this.container)) {
4202
- document.body.removeChild(this.container);
4203
- }
4204
- this.container = null;
4205
- this.root = null;
4206
- }
4207
- }
4208
- )
4209
- );
4502
+ const overlayElement = this.container.querySelector(".neofaceid-overlay-container");
4503
+ if (overlayElement) {
4504
+ overlayElement.classList.add("fade-out");
4210
4505
  }
4211
4506
  setTimeout(() => {
4212
4507
  if (this.container && document.body.contains(this.container)) {
4213
4508
  document.body.removeChild(this.container);
4214
4509
  }
4215
4510
  this.container = null;
4216
- this.root = null;
4217
4511
  }, 300);
4218
4512
  }
4219
4513
  }
4220
- /**
4221
- * Renderiza o componente
4222
- */
4223
- render() {
4224
- if (!this.root || !this.container)
4225
- return;
4226
- this.root.render(
4227
- /* @__PURE__ */ jsxRuntimeExports.jsx(BiometricStatusOverlayComponent, { status: this.currentStatus, onClose: () => this.close() })
4228
- );
4229
- }
4230
4514
  }
4231
4515
  function FallbackPromptComponent({
4232
4516
  error,
@@ -4439,7 +4723,7 @@ class FallbackPrompt {
4439
4723
  this.container = document.createElement("div");
4440
4724
  this.container.id = "neofaceid-fallback-prompt";
4441
4725
  document.body.appendChild(this.container);
4442
- this.root = createRoot(this.container);
4726
+ this.root = clientExports.createRoot(this.container);
4443
4727
  this.render(error, onRetry, onCredentials, onCancel);
4444
4728
  }
4445
4729
  /**
@@ -4459,8 +4743,7 @@ class FallbackPrompt {
4459
4743
  * Renderiza o componente
4460
4744
  */
4461
4745
  render(error, onRetry, onCredentials, onCancel) {
4462
- if (!this.root || !this.container)
4463
- return;
4746
+ if (!this.root || !this.container) return;
4464
4747
  this.root.render(
4465
4748
  /* @__PURE__ */ jsxRuntimeExports.jsx(
4466
4749
  FallbackPromptComponent,
@@ -4752,8 +5035,30 @@ class EmailPasswordModal {
4752
5035
  }
4753
5036
  }
4754
5037
  }
4755
- const MAX_ATTEMPTS = 3;
5038
+ const MAX_ATTEMPTS = 2;
4756
5039
  const RETRY_DELAY = 500;
5040
+ const CAMERA_READY_DELAY = 200;
5041
+ const CAMERA_STABILITY_DELAY = 100;
5042
+ const FACE_DETECTION_TIME = 5e3;
5043
+ const DETECTION_INTERVAL = 100;
5044
+ const SUCCESS_DELAY = 200;
5045
+ let modelsLoaded = false;
5046
+ let modelsLoading = null;
5047
+ async function preloadFaceDetectionModels() {
5048
+ if (modelsLoaded) return;
5049
+ if (modelsLoading) return modelsLoading;
5050
+ modelsLoading = (async () => {
5051
+ try {
5052
+ const MODEL_URL = "https://cdn.jsdelivr.net/npm/@vladmandic/face-api/model";
5053
+ await faceapi.nets.tinyFaceDetector.loadFromUri(MODEL_URL);
5054
+ modelsLoaded = true;
5055
+ console.log("✅ Face detection models preloaded");
5056
+ } catch (error) {
5057
+ console.warn("⚠️ Face-api.js models not loaded:", error);
5058
+ }
5059
+ })();
5060
+ return modelsLoading;
5061
+ }
4757
5062
  async function captureFaceSilently() {
4758
5063
  return new Promise((resolve, reject) => {
4759
5064
  const video = document.createElement("video");
@@ -4815,20 +5120,21 @@ async function captureFaceSilently() {
4815
5120
  return;
4816
5121
  }
4817
5122
  const detectFace2 = async () => {
5123
+ if (!modelsLoaded) return true;
4818
5124
  try {
4819
5125
  const detection = await faceapi.detectSingleFace(
4820
5126
  video,
4821
- new faceapi.TinyFaceDetectorOptions({ inputSize: 160, scoreThreshold: 0.4 })
5127
+ new faceapi.TinyFaceDetectorOptions({ inputSize: 160, scoreThreshold: 0.3 })
4822
5128
  );
4823
5129
  return !!detection;
4824
5130
  } catch {
4825
- return false;
5131
+ return true;
4826
5132
  }
4827
5133
  };
4828
5134
  detectFace2().then((hasFace) => {
4829
5135
  if (!hasFace && faceDetectionAttempts < MAX_FACE_DETECTION_ATTEMPTS) {
4830
5136
  faceDetectionAttempts++;
4831
- setTimeout(tryCapture, 200);
5137
+ setTimeout(tryCapture, 100);
4832
5138
  return;
4833
5139
  }
4834
5140
  ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
@@ -4847,7 +5153,7 @@ async function captureFaceSilently() {
4847
5153
  });
4848
5154
  };
4849
5155
  video.onloadedmetadata = () => {
4850
- setTimeout(tryCapture, 800);
5156
+ setTimeout(tryCapture, CAMERA_READY_DELAY);
4851
5157
  };
4852
5158
  video.onerror = () => {
4853
5159
  cleanup();
@@ -4864,64 +5170,52 @@ async function captureFaceSilently() {
4864
5170
  });
4865
5171
  });
4866
5172
  }
4867
- async function detectFaceContinuously(video, minDetectionTime = 3e3, detectionInterval = 200) {
5173
+ async function detectFaceQuickly(video, maxTime = FACE_DETECTION_TIME, interval = DETECTION_INTERVAL) {
4868
5174
  return new Promise((resolve) => {
4869
- let faceDetectedCount = 0;
4870
- let totalChecks = 0;
4871
- const requiredChecks = Math.ceil(minDetectionTime / detectionInterval);
4872
- const minSuccessRate = 0.6;
5175
+ let attempts = 0;
5176
+ const maxAttempts = Math.ceil(maxTime / interval);
4873
5177
  let timeoutId = null;
4874
5178
  let isResolved = false;
4875
5179
  const finish = (success) => {
4876
- if (isResolved)
4877
- return;
5180
+ if (isResolved) return;
4878
5181
  isResolved = true;
4879
- if (timeoutId) {
4880
- clearTimeout(timeoutId);
4881
- }
5182
+ if (timeoutId) clearTimeout(timeoutId);
4882
5183
  resolve(success);
4883
5184
  };
4884
5185
  const checkFace = async () => {
4885
- if (isResolved)
5186
+ if (isResolved) return;
5187
+ attempts++;
5188
+ if (!modelsLoaded) {
5189
+ finish(true);
4886
5190
  return;
5191
+ }
4887
5192
  try {
4888
5193
  const detection = await faceapi.detectSingleFace(
4889
5194
  video,
4890
- new faceapi.TinyFaceDetectorOptions({ inputSize: 160, scoreThreshold: 0.4 })
5195
+ new faceapi.TinyFaceDetectorOptions({ inputSize: 160, scoreThreshold: 0.3 })
4891
5196
  );
4892
- totalChecks++;
4893
5197
  if (detection) {
4894
- faceDetectedCount++;
5198
+ finish(true);
5199
+ return;
4895
5200
  }
4896
- if (totalChecks >= requiredChecks) {
4897
- const successRate = faceDetectedCount / totalChecks;
4898
- finish(successRate >= minSuccessRate);
5201
+ if (attempts >= maxAttempts) {
5202
+ finish(false);
4899
5203
  return;
4900
5204
  }
4901
- timeoutId = window.setTimeout(checkFace, detectionInterval);
5205
+ timeoutId = window.setTimeout(checkFace, interval);
4902
5206
  } catch (error) {
4903
- totalChecks++;
4904
- if (totalChecks >= requiredChecks) {
4905
- const successRate = faceDetectedCount / totalChecks;
4906
- finish(successRate >= minSuccessRate);
4907
- } else {
4908
- timeoutId = window.setTimeout(checkFace, detectionInterval);
4909
- }
5207
+ finish(true);
4910
5208
  }
4911
5209
  };
4912
- const safetyTimeout = window.setTimeout(() => {
5210
+ window.setTimeout(() => {
4913
5211
  if (!isResolved) {
4914
- const successRate = totalChecks > 0 ? faceDetectedCount / totalChecks : 0;
4915
- finish(successRate >= minSuccessRate);
4916
- }
4917
- }, minDetectionTime + 1e3);
4918
- timeoutId = window.setTimeout(() => {
4919
- checkFace();
4920
- clearTimeout(safetyTimeout);
4921
- }, detectionInterval);
5212
+ finish(true);
5213
+ }
5214
+ }, maxTime + 500);
5215
+ checkFace();
4922
5216
  });
4923
5217
  }
4924
- async function attemptLogin(applicationToken, overlay) {
5218
+ async function attemptLogin(applicationToken, overlay, fastMode = false) {
4925
5219
  overlay.updateStatus("verifying");
4926
5220
  const video = document.createElement("video");
4927
5221
  video.style.position = "fixed";
@@ -4953,8 +5247,11 @@ async function attemptLogin(applicationToken, overlay) {
4953
5247
  video.onloadedmetadata = () => resolve(void 0);
4954
5248
  }
4955
5249
  });
4956
- await new Promise((resolve) => setTimeout(resolve, 500));
4957
- const faceDetected = await detectFaceContinuously(video, 3e3);
5250
+ await new Promise((resolve) => setTimeout(resolve, CAMERA_STABILITY_DELAY));
5251
+ let faceDetected = true;
5252
+ if (!fastMode) {
5253
+ faceDetected = await detectFaceQuickly(video);
5254
+ }
4958
5255
  if (!faceDetected) {
4959
5256
  if (stream) {
4960
5257
  stream.getTracks().forEach((track) => track.stop());
@@ -5020,30 +5317,25 @@ async function attemptLogin(applicationToken, overlay) {
5020
5317
  }
5021
5318
  }
5022
5319
  async function executeBiometricLoginFlow(options) {
5023
- const { applicationToken, onSuccess, onError, onFallbackRequest, onCancel } = options;
5320
+ const { applicationToken, onSuccess, onError, onFallbackRequest, onCancel, fastMode = false } = options;
5024
5321
  const overlay = new BiometricStatusOverlay();
5025
5322
  const fallbackPrompt = new FallbackPrompt();
5026
5323
  let attempts = 0;
5027
5324
  let lastError;
5028
- try {
5029
- const MODEL_URL = "https://cdn.jsdelivr.net/npm/@vladmandic/face-api/model";
5030
- await Promise.all([
5031
- faceapi.nets.tinyFaceDetector.loadFromUri(MODEL_URL),
5032
- faceapi.nets.faceLandmark68Net.loadFromUri(MODEL_URL)
5033
- ]);
5034
- } catch (error) {
5035
- console.warn("Face-api.js models not loaded, continuing without face detection");
5325
+ if (!modelsLoaded && !fastMode) {
5326
+ preloadFaceDetectionModels().catch(() => {
5327
+ });
5036
5328
  }
5037
5329
  overlay.show("preparing");
5038
5330
  const tryLogin = async () => {
5039
5331
  try {
5040
5332
  attempts++;
5041
- const result = await attemptLogin(applicationToken, overlay);
5333
+ const result = await attemptLogin(applicationToken, overlay, fastMode);
5042
5334
  overlay.updateStatus("success");
5043
5335
  setTimeout(() => {
5044
5336
  overlay.close();
5045
5337
  onSuccess(result);
5046
- }, 800);
5338
+ }, SUCCESS_DELAY);
5047
5339
  } catch (error) {
5048
5340
  lastError = error instanceof NeoFaceError ? error : new NeoFaceError(
5049
5341
  error instanceof Error ? error.message : "Erro desconhecido",
@@ -5062,7 +5354,7 @@ async function executeBiometricLoginFlow(options) {
5062
5354
  () => {
5063
5355
  attempts = 0;
5064
5356
  overlay.show("preparing");
5065
- setTimeout(() => tryLogin(), 500);
5357
+ setTimeout(() => tryLogin(), RETRY_DELAY);
5066
5358
  },
5067
5359
  () => {
5068
5360
  if (onFallbackRequest) {
@@ -5078,18 +5370,19 @@ async function executeBiometricLoginFlow(options) {
5078
5370
  },
5079
5371
  onCancel
5080
5372
  );
5081
- }, 1e3);
5373
+ }, 500);
5082
5374
  }
5083
5375
  }
5084
5376
  };
5085
- setTimeout(() => {
5377
+ {
5086
5378
  tryLogin();
5087
- }, 300);
5379
+ }
5088
5380
  }
5089
5381
  const biometricLoginFlow = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
5090
5382
  __proto__: null,
5091
5383
  captureFaceSilently,
5092
- executeBiometricLoginFlow
5384
+ executeBiometricLoginFlow,
5385
+ preloadFaceDetectionModels
5093
5386
  }, Symbol.toStringTag, { value: "Module" }));
5094
5387
  async function startFaceLogin(options) {
5095
5388
  try {
@@ -5418,8 +5711,8 @@ const biometricDetection = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.
5418
5711
  initializeBiometricDetection,
5419
5712
  isAdvancedDetectionAvailable
5420
5713
  }, Symbol.toStringTag, { value: "Module" }));
5421
- const VERSION = "1.6.0";
5422
- const RELEASE_DATE = "2025-12-01";
5714
+ const VERSION = "1.8.7";
5715
+ const RELEASE_DATE = "2025-12-30";
5423
5716
  class OnboardingCaptureModal {
5424
5717
  constructor(options) {
5425
5718
  __publicField(this, "overlay", null);
@@ -5526,8 +5819,7 @@ class OnboardingCaptureModal {
5526
5819
  audio: false
5527
5820
  };
5528
5821
  this.stream = await navigator.mediaDevices.getUserMedia(constraints);
5529
- if (!this.video)
5530
- throw new Error("Elemento de vídeo não encontrado");
5822
+ if (!this.video) throw new Error("Elemento de vídeo não encontrado");
5531
5823
  this.video.srcObject = this.stream;
5532
5824
  await this.video.play();
5533
5825
  }
@@ -5535,8 +5827,7 @@ class OnboardingCaptureModal {
5535
5827
  * Executa a contagem regressiva e atualiza textos para captura de rosto.
5536
5828
  */
5537
5829
  async runFaceCaptureCountdown() {
5538
- if (!this.overlay)
5539
- return;
5830
+ if (!this.overlay) return;
5540
5831
  const countdownEl = this.overlay.querySelector(".neofaceid-countdown");
5541
5832
  const countdownNumberEl = this.overlay.querySelector(".neofaceid-countdown-number");
5542
5833
  const statusText = this.overlay.querySelector(".neofaceid-status-text");
@@ -5560,8 +5851,7 @@ class OnboardingCaptureModal {
5560
5851
  * Aguarda o clique do usuário no botão "Capturar" (para documento).
5561
5852
  */
5562
5853
  async waitForUserCaptureClick() {
5563
- if (!this.overlay)
5564
- return;
5854
+ if (!this.overlay) return;
5565
5855
  const captureBtn = this.overlay.querySelector(".neofaceid-capture-btn");
5566
5856
  const statusText = this.overlay.querySelector(".neofaceid-status-text");
5567
5857
  captureBtn.disabled = false;
@@ -5579,11 +5869,9 @@ class OnboardingCaptureModal {
5579
5869
  * Captura o frame atual do vídeo e retorna como Blob JPEG.
5580
5870
  */
5581
5871
  async captureFrame() {
5582
- if (!this.video || !this.canvas)
5583
- throw new Error("Elementos de captura não encontrados");
5872
+ if (!this.video || !this.canvas) throw new Error("Elementos de captura não encontrados");
5584
5873
  const ctx = this.canvas.getContext("2d");
5585
- if (!ctx)
5586
- throw new Error("Falha ao obter contexto do canvas");
5874
+ if (!ctx) throw new Error("Falha ao obter contexto do canvas");
5587
5875
  this.canvas.width = this.video.videoWidth;
5588
5876
  this.canvas.height = this.video.videoHeight;
5589
5877
  ctx.drawImage(this.video, 0, 0, this.canvas.width, this.canvas.height);
@@ -5596,8 +5884,7 @@ class OnboardingCaptureModal {
5596
5884
  * Atualiza título e subtítulo do modal conforme o passo.
5597
5885
  */
5598
5886
  updateTexts(title, subtitle) {
5599
- if (!this.overlay)
5600
- return;
5887
+ if (!this.overlay) return;
5601
5888
  const titleEl = this.overlay.querySelector(".neofaceid-title");
5602
5889
  const subtitleEl = this.overlay.querySelector(".neofaceid-subtitle");
5603
5890
  const frameEl = this.overlay.querySelector(".neofaceid-frame");
@@ -5643,6 +5930,7 @@ const startOnboarding = async (options) => {
5643
5930
  onError(new NeoFaceError(message, ErrorType.UNKNOWN));
5644
5931
  }
5645
5932
  };
5933
+ const __vite_import_meta_env__ = {};
5646
5934
  class NeoFaceID {
5647
5935
  /**
5648
5936
  * Creates a new NeoFaceID instance
@@ -5654,9 +5942,8 @@ class NeoFaceID {
5654
5942
  __publicField(this, "baseUrl");
5655
5943
  __publicField(this, "signature");
5656
5944
  __publicField(this, "sessionData");
5657
- var _a2;
5658
5945
  this.appToken = config.appToken;
5659
- this.baseUrl = config.baseUrl || (((_a2 = { "BASE_URL": "/", "MODE": "production", "DEV": false, "PROD": true, "SSR": false }) == null ? void 0 : _a2.VITE_API_BASE_URL) || "http://localhost:8000");
5946
+ this.baseUrl = config.baseUrl || (__vite_import_meta_env__ == null ? void 0 : __vite_import_meta_env__.VITE_API_BASE_URL) || "http://localhost:8000";
5660
5947
  this.signature = config.signature;
5661
5948
  this.sessionData = config.sessionData;
5662
5949
  if (this.signature && !this.validateSignatureFormat(this.signature)) {
@@ -5720,14 +6007,14 @@ class NeoFaceID {
5720
6007
  }
5721
6008
  /**
5722
6009
  * Performs login recognition using biometric data
5723
- *
6010
+ *
5724
6011
  * This method is designed for external integrations that require
5725
6012
  * signature validation and session management.
5726
- *
6013
+ *
5727
6014
  * @param options Recognition options
5728
6015
  * @returns Promise that resolves to recognition result
5729
6016
  * @throws NeoFaceError if recognition fails
5730
- *
6017
+ *
5731
6018
  * @example
5732
6019
  * ```typescript
5733
6020
  * const result = await sdk.loginRecognition({
@@ -5739,18 +6026,13 @@ class NeoFaceID {
5739
6026
  * purpose: 'LOGIN',
5740
6027
  * confidenceThreshold: 0.8
5741
6028
  * });
5742
- *
6029
+ *
5743
6030
  * // Result includes signature and sessionId for callback validation
5744
6031
  * console.log(result.signature, result.sessionId);
5745
6032
  * ```
5746
6033
  */
5747
6034
  async loginRecognition(options) {
5748
- const {
5749
- biometricData,
5750
- typeOfIdentification,
5751
- purpose,
5752
- confidenceThreshold = 0.8
5753
- } = options;
6035
+ const { biometricData, typeOfIdentification, purpose, confidenceThreshold = 0.8 } = options;
5754
6036
  const imageBlobs = Array.isArray(biometricData) ? biometricData : [biometricData];
5755
6037
  if (imageBlobs.length === 0) {
5756
6038
  throw new NeoFaceError(
@@ -5769,10 +6051,7 @@ class NeoFaceID {
5769
6051
  this.sessionData
5770
6052
  );
5771
6053
  if (!result.success) {
5772
- throw new NeoFaceError(
5773
- "Recognition failed",
5774
- ErrorType.RECOGNITION_FAILED
5775
- );
6054
+ throw new NeoFaceError("Recognition failed", ErrorType.RECOGNITION_FAILED);
5776
6055
  }
5777
6056
  if (!result.personName || !result.email || !result.cpf) {
5778
6057
  throw new NeoFaceError(
@@ -5800,25 +6079,174 @@ class NeoFaceID {
5800
6079
  );
5801
6080
  }
5802
6081
  }
5803
- }
5804
- const PHOTO_INSTRUCTIONS = [
5805
- "Olhe diretamente para a câmera",
5806
- "Vire levemente a cabeça para a esquerda",
5807
- "Vire levemente a cabeça para a direita",
5808
- "Mostre o polegar para cima (👍) e olhe para a câmera"
5809
- ];
5810
- const TOTAL_PHOTOS = 4;
5811
- const authorizeOperation = async (applicationToken, cpf, options) => {
5812
- if (!applicationToken) {
5813
- throw new NeoFaceError("Application token is required", ErrorType.VALIDATION_ERROR);
5814
- }
5815
- if (!cpf) {
5816
- throw new NeoFaceError("CPF is required", ErrorType.VALIDATION_ERROR);
5817
- }
5818
- const cpfClean = cpf.replace(/\D/g, "");
5819
- if (cpfClean.length !== 11) {
5820
- throw new NeoFaceError("Invalid CPF format", ErrorType.VALIDATION_ERROR);
5821
- }
6082
+ /**
6083
+ * Register a new application for a consumer
6084
+ *
6085
+ * This method allows authenticated users to register new applications
6086
+ * that will receive their own app_token for API access.
6087
+ *
6088
+ * @param jwtToken JWT authentication token from logged user
6089
+ * @param consumerId Consumer ID (UUID) who will own the application
6090
+ * @param applicationData Application registration data
6091
+ * @returns Promise with registration result including app_token
6092
+ * @throws NeoFaceError if registration fails
6093
+ *
6094
+ * @example
6095
+ * ```typescript
6096
+ * const sdk = new NeoFaceID({
6097
+ * appToken: 'your-application-token'
6098
+ * });
6099
+ *
6100
+ * const result = await sdk.registerApplication(
6101
+ * userJwtToken,
6102
+ * consumerUuid,
6103
+ * {
6104
+ * applicationName: 'My New App',
6105
+ * domain: 'example.com',
6106
+ * acceptOnlyEmailWithSameDomain: true
6107
+ * }
6108
+ * );
6109
+ *
6110
+ * // Use the generated app_token for the new application
6111
+ * console.log('New App Token:', result.application.app_token);
6112
+ * ```
6113
+ */
6114
+ async registerApplication(jwtToken, consumerId, applicationData) {
6115
+ try {
6116
+ const result = await registerApplication(jwtToken, consumerId, applicationData);
6117
+ return result;
6118
+ } catch (error) {
6119
+ if (error instanceof NeoFaceError) {
6120
+ throw error;
6121
+ }
6122
+ throw new NeoFaceError(
6123
+ `Application registration failed: ${error instanceof Error ? error.message : "Unknown error"}`,
6124
+ ErrorType.VALIDATION_ERROR
6125
+ );
6126
+ }
6127
+ }
6128
+ /**
6129
+ * Performs Proof of Life verification
6130
+ *
6131
+ * This method records a video from the camera, sends it to the backend
6132
+ * for liveness detection and face recognition, and returns the result
6133
+ * with personal data filtered by purpose.
6134
+ *
6135
+ * The process is asynchronous:
6136
+ * 1. Records video from the camera (default 3 seconds)
6137
+ * 2. Converts video to base64 and sends to backend
6138
+ * 3. Backend processes liveness detection and face recognition
6139
+ * 4. Polls for task completion
6140
+ * 5. Returns personal data of the identified person
6141
+ *
6142
+ * @param options Configuration options for the proof of life process
6143
+ * @returns Promise that resolves to ProofOfLifeResult
6144
+ * @throws NeoFaceError if verification fails
6145
+ *
6146
+ * @example
6147
+ * ```typescript
6148
+ * const sdk = new NeoFaceID({
6149
+ * appToken: 'your-application-token'
6150
+ * });
6151
+ *
6152
+ * const result = await sdk.proofOfLife({
6153
+ * videoDurationMs: 3000, // 3 seconds
6154
+ * onRecordingProgress: (progress) => {
6155
+ * console.log(`Recording: ${progress}%`);
6156
+ * },
6157
+ * onTaskStatusChange: (status, progress) => {
6158
+ * console.log(`Status: ${status}, Progress: ${progress}%`);
6159
+ * }
6160
+ * });
6161
+ *
6162
+ * if (result.success && result.isLive) {
6163
+ * console.log('Person verified:', result.personalData);
6164
+ * console.log('Liveness score:', result.livenessScore);
6165
+ * console.log('Face recognition score:', result.faceRecognitionScore);
6166
+ * }
6167
+ * ```
6168
+ */
6169
+ async proofOfLife(options = {}) {
6170
+ const {
6171
+ videoDurationMs = 3e3,
6172
+ maxPollingAttempts = 60,
6173
+ pollingIntervalMs = 1e3,
6174
+ onRecordingProgress,
6175
+ onTaskStatusChange
6176
+ } = options;
6177
+ try {
6178
+ const videoBlob = await recordFaceVideo({
6179
+ durationMs: videoDurationMs,
6180
+ onProgress: onRecordingProgress
6181
+ });
6182
+ const videoBase64 = await videoToBase64(videoBlob);
6183
+ const startResult = await startProofOfLife(videoBase64, this.appToken);
6184
+ if (onTaskStatusChange) {
6185
+ onTaskStatusChange("processing", 0);
6186
+ }
6187
+ const taskResult = await pollTaskStatus(startResult.taskId, this.appToken, {
6188
+ maxAttempts: maxPollingAttempts,
6189
+ intervalMs: pollingIntervalMs,
6190
+ onStatusChange: onTaskStatusChange
6191
+ });
6192
+ if (!taskResult || !taskResult.success) {
6193
+ return {
6194
+ success: false,
6195
+ isLive: (taskResult == null ? void 0 : taskResult.is_live) || false,
6196
+ livenessScore: (taskResult == null ? void 0 : taskResult.liveness_score) || 0,
6197
+ faceRecognitionScore: (taskResult == null ? void 0 : taskResult.face_recognition_score) || 0,
6198
+ combinedConfidence: (taskResult == null ? void 0 : taskResult.combined_confidence) || 0,
6199
+ personId: taskResult == null ? void 0 : taskResult.person_id,
6200
+ personalData: (taskResult == null ? void 0 : taskResult.personal_data) || [],
6201
+ processingTime: (taskResult == null ? void 0 : taskResult.processing_time) || 0,
6202
+ historyId: startResult.historyId,
6203
+ taskId: startResult.taskId,
6204
+ error: (taskResult == null ? void 0 : taskResult.error) || "Verification failed",
6205
+ message: (taskResult == null ? void 0 : taskResult.message) || "Proof of life verification failed"
6206
+ };
6207
+ }
6208
+ return {
6209
+ success: true,
6210
+ isLive: taskResult.is_live || false,
6211
+ livenessScore: taskResult.liveness_score || 0,
6212
+ faceRecognitionScore: taskResult.face_recognition_score || 0,
6213
+ combinedConfidence: taskResult.combined_confidence || 0,
6214
+ personId: taskResult.person_id,
6215
+ personalData: taskResult.personal_data || [],
6216
+ processingTime: taskResult.processing_time || 0,
6217
+ historyId: startResult.historyId,
6218
+ taskId: startResult.taskId,
6219
+ message: "Proof of life verification successful"
6220
+ };
6221
+ } catch (error) {
6222
+ if (error instanceof NeoFaceError) {
6223
+ throw error;
6224
+ }
6225
+ throw new NeoFaceError(
6226
+ `Proof of life failed: ${error instanceof Error ? error.message : "Unknown error"}`,
6227
+ ErrorType.RECOGNITION_FAILED
6228
+ );
6229
+ }
6230
+ }
6231
+ }
6232
+ const PHOTO_INSTRUCTIONS = [
6233
+ "Olhe diretamente para a câmera",
6234
+ "Vire levemente a cabeça para a esquerda",
6235
+ "Vire levemente a cabeça para a direita",
6236
+ "Mostre o polegar para cima (👍) e olhe para a câmera"
6237
+ ];
6238
+ const TOTAL_PHOTOS = 4;
6239
+ const authorizeOperation = async (applicationToken, cpf, options) => {
6240
+ if (!applicationToken) {
6241
+ throw new NeoFaceError("Application token is required", ErrorType.VALIDATION_ERROR);
6242
+ }
6243
+ if (!cpf) {
6244
+ throw new NeoFaceError("CPF is required", ErrorType.VALIDATION_ERROR);
6245
+ }
6246
+ const cpfClean = cpf.replace(/\D/g, "");
6247
+ if (cpfClean.length !== 11) {
6248
+ throw new NeoFaceError("Invalid CPF format", ErrorType.VALIDATION_ERROR);
6249
+ }
5822
6250
  const capturedPhotos = [];
5823
6251
  const captureDelay = (options == null ? void 0 : options.captureDelay) || 2e3;
5824
6252
  try {
@@ -5921,6 +6349,999 @@ const authorizeOperation = async (applicationToken, cpf, options) => {
5921
6349
  throw new NeoFaceError("Unknown error during authorization", ErrorType.UNKNOWN);
5922
6350
  }
5923
6351
  };
6352
+ const DOCUMENT_INFO = {
6353
+ RG: {
6354
+ name: "RG / Carteira de Identidade",
6355
+ icon: '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="4" width="18" height="16" rx="2"/><circle cx="9" cy="10" r="2"/><path d="M15 8h2M15 12h2M7 16h10"/></svg>',
6356
+ hasBack: true,
6357
+ frontLabel: "Frente do RG",
6358
+ backLabel: "Verso do RG"
6359
+ },
6360
+ CNH: {
6361
+ name: "CNH / Carteira de Motorista",
6362
+ icon: '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="6" width="18" height="12" rx="2"/><circle cx="8" cy="12" r="2"/><path d="M13 10h5M13 14h3"/></svg>',
6363
+ hasBack: true,
6364
+ frontLabel: "Frente da CNH",
6365
+ backLabel: "Verso da CNH"
6366
+ },
6367
+ CPF: {
6368
+ name: "CPF",
6369
+ icon: '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><path d="M14 2v6h6M16 13H8M16 17H8M10 9H8"/></svg>',
6370
+ hasBack: false,
6371
+ frontLabel: "Cartão CPF"
6372
+ }
6373
+ };
6374
+ class DocumentCaptureModal {
6375
+ constructor(options) {
6376
+ __publicField(this, "overlay", null);
6377
+ __publicField(this, "video", null);
6378
+ __publicField(this, "canvas", null);
6379
+ __publicField(this, "stream", null);
6380
+ __publicField(this, "resolvePromise", null);
6381
+ __publicField(this, "options");
6382
+ __publicField(this, "selectedDocument", null);
6383
+ __publicField(this, "currentStep", "select");
6384
+ __publicField(this, "frontBlob", null);
6385
+ __publicField(this, "backBlob", null);
6386
+ this.options = {
6387
+ title: (options == null ? void 0 : options.title) ?? "Validação de Identidade",
6388
+ subtitle: (options == null ? void 0 : options.subtitle) ?? "Selecione o tipo de documento",
6389
+ useBackCamera: (options == null ? void 0 : options.useBackCamera) ?? true,
6390
+ onCancel: options == null ? void 0 : options.onCancel,
6391
+ preSelectedDocument: options == null ? void 0 : options.preSelectedDocument
6392
+ };
6393
+ }
6394
+ async open() {
6395
+ return new Promise((resolve) => {
6396
+ this.resolvePromise = resolve;
6397
+ if (this.options.preSelectedDocument) {
6398
+ this.selectedDocument = this.options.preSelectedDocument;
6399
+ this.currentStep = "front";
6400
+ }
6401
+ this.render();
6402
+ });
6403
+ }
6404
+ render() {
6405
+ var _a;
6406
+ if (!this.overlay) {
6407
+ this.createOverlay();
6408
+ }
6409
+ const content = (_a = this.overlay) == null ? void 0 : _a.querySelector(".neoface-doc-content");
6410
+ if (!content) return;
6411
+ switch (this.currentStep) {
6412
+ case "select":
6413
+ content.innerHTML = this.renderSelectStep();
6414
+ this.bindSelectEvents();
6415
+ break;
6416
+ case "front":
6417
+ case "back":
6418
+ content.innerHTML = this.renderCaptureStep();
6419
+ this.bindCaptureEvents();
6420
+ this.startCamera();
6421
+ break;
6422
+ case "preview-front":
6423
+ case "preview-back":
6424
+ content.innerHTML = this.renderPreviewStep();
6425
+ this.bindPreviewEvents();
6426
+ break;
6427
+ }
6428
+ }
6429
+ createOverlay() {
6430
+ const overlay = document.createElement("div");
6431
+ overlay.className = "neoface-doc-overlay";
6432
+ overlay.innerHTML = `
6433
+ <div class="neoface-doc-modal">
6434
+ <div class="neoface-doc-content"></div>
6435
+ </div>
6436
+ `;
6437
+ if (!document.getElementById("neoface-doc-styles-v2")) {
6438
+ const style = document.createElement("style");
6439
+ style.id = "neoface-doc-styles-v2";
6440
+ style.textContent = this.getStyles();
6441
+ document.head.appendChild(style);
6442
+ }
6443
+ document.body.appendChild(overlay);
6444
+ this.overlay = overlay;
6445
+ }
6446
+ renderSelectStep() {
6447
+ return `
6448
+ <div class="neoface-doc-header">
6449
+ <div class="neoface-doc-header-left">
6450
+ <h2 class="neoface-doc-title">${this.options.title}</h2>
6451
+ <p class="neoface-doc-subtitle">${this.options.subtitle}</p>
6452
+ </div>
6453
+ <button class="neoface-doc-close-btn" aria-label="Fechar">
6454
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
6455
+ <path d="M18 6L6 18M6 6l12 12"/>
6456
+ </svg>
6457
+ </button>
6458
+ </div>
6459
+
6460
+ <div class="neoface-doc-body">
6461
+ <div class="neoface-doc-select-grid">
6462
+ ${Object.entries(DOCUMENT_INFO).map(([type, info]) => `
6463
+ <button class="neoface-doc-select-option" data-type="${type}">
6464
+ <span class="neoface-doc-select-icon">${info.icon}</span>
6465
+ <span class="neoface-doc-select-name">${info.name}</span>
6466
+ <svg class="neoface-doc-select-arrow" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 18l6-6-6-6"/></svg>
6467
+ </button>
6468
+ `).join("")}
6469
+ </div>
6470
+
6471
+ <div class="neoface-doc-tips">
6472
+ <div class="neoface-doc-tip">
6473
+ <span>Garanta boa iluminação e que todos os dados estejam legíveis</span>
6474
+ </div>
6475
+ </div>
6476
+ </div>
6477
+
6478
+ ${this.renderBranding()}
6479
+ `;
6480
+ }
6481
+ renderCaptureStep() {
6482
+ const docInfo = this.selectedDocument ? DOCUMENT_INFO[this.selectedDocument] : null;
6483
+ if (!docInfo) return "";
6484
+ const isBack = this.currentStep === "back";
6485
+ const label = isBack ? docInfo.backLabel : docInfo.frontLabel;
6486
+ const stepNumber = isBack ? 2 : 1;
6487
+ const totalSteps = docInfo.hasBack ? 2 : 1;
6488
+ return `
6489
+ <div class="neoface-doc-header">
6490
+ <div class="neoface-doc-header-left">
6491
+ <div class="neoface-doc-step-indicator">
6492
+ <span class="neoface-doc-step-badge">${stepNumber}/${totalSteps}</span>
6493
+ <span class="neoface-doc-step-label">${label}</span>
6494
+ </div>
6495
+ </div>
6496
+ <button class="neoface-doc-close-btn" aria-label="Fechar">
6497
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
6498
+ <path d="M18 6L6 18M6 6l12 12"/>
6499
+ </svg>
6500
+ </button>
6501
+ </div>
6502
+
6503
+ <div class="neoface-doc-body">
6504
+ <div class="neoface-doc-camera-container">
6505
+ <video class="neoface-doc-video" autoplay playsinline></video>
6506
+ <canvas class="neoface-doc-canvas"></canvas>
6507
+
6508
+ <div class="neoface-doc-guide">
6509
+ <div class="neoface-doc-frame">
6510
+ <div class="neoface-doc-corner neoface-doc-corner-tl"></div>
6511
+ <div class="neoface-doc-corner neoface-doc-corner-tr"></div>
6512
+ <div class="neoface-doc-corner neoface-doc-corner-bl"></div>
6513
+ <div class="neoface-doc-corner neoface-doc-corner-br"></div>
6514
+ <div class="neoface-doc-frame-label">${label}</div>
6515
+ </div>
6516
+ </div>
6517
+
6518
+ <div class="neoface-doc-loading" style="display:none">
6519
+ <div class="neoface-doc-spinner"></div>
6520
+ </div>
6521
+ </div>
6522
+
6523
+ <div class="neoface-doc-instructions">
6524
+ <div class="neoface-doc-instruction">
6525
+ <svg class="neoface-doc-instruction-icon" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="18" height="18" rx="2"/><path d="M3 9h18M9 21V9"/></svg>
6526
+ <span>Posicione o documento dentro do quadro</span>
6527
+ </div>
6528
+ <div class="neoface-doc-instruction">
6529
+ <svg class="neoface-doc-instruction-icon" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="5"/><path d="M12 1v2M12 21v2M4.22 4.22l1.42 1.42M18.36 18.36l1.42 1.42M1 12h2M21 12h2M4.22 19.78l1.42-1.42M18.36 5.64l1.42-1.42"/></svg>
6530
+ <span>Evite reflexos e sombras</span>
6531
+ </div>
6532
+ </div>
6533
+ </div>
6534
+
6535
+ <div class="neoface-doc-footer">
6536
+ <button class="neoface-doc-btn neoface-doc-btn-secondary neoface-doc-back-btn">
6537
+ ← Voltar
6538
+ </button>
6539
+ <input type="file" accept="image/*" class="neoface-doc-file-input" style="display:none" />
6540
+ <button class="neoface-doc-btn neoface-doc-btn-secondary neoface-doc-upload-btn">
6541
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
6542
+ <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/>
6543
+ <polyline points="17,8 12,3 7,8"/>
6544
+ <line x1="12" y1="3" x2="12" y2="15"/>
6545
+ </svg>
6546
+ Anexar
6547
+ </button>
6548
+ <button class="neoface-doc-btn neoface-doc-btn-primary neoface-doc-capture-btn">
6549
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
6550
+ <circle cx="12" cy="12" r="10"/>
6551
+ <circle cx="12" cy="12" r="4"/>
6552
+ </svg>
6553
+ Capturar
6554
+ </button>
6555
+ </div>
6556
+
6557
+ ${this.renderBranding()}
6558
+ `;
6559
+ }
6560
+ renderPreviewStep() {
6561
+ const docInfo = this.selectedDocument ? DOCUMENT_INFO[this.selectedDocument] : null;
6562
+ if (!docInfo) return "";
6563
+ const isPreviewFront = this.currentStep === "preview-front";
6564
+ const label = isPreviewFront ? docInfo.frontLabel : docInfo.backLabel;
6565
+ const isLastStep = !docInfo.hasBack || this.currentStep === "preview-back";
6566
+ const stepNumber = isPreviewFront ? 1 : 2;
6567
+ const totalSteps = docInfo.hasBack ? 2 : 1;
6568
+ return `
6569
+ <div class="neoface-doc-header">
6570
+ <div class="neoface-doc-header-left">
6571
+ <div class="neoface-doc-step-indicator">
6572
+ <span class="neoface-doc-step-badge">${stepNumber}/${totalSteps}</span>
6573
+ <span class="neoface-doc-step-label">Confirmar ${label}</span>
6574
+ </div>
6575
+ </div>
6576
+ <button class="neoface-doc-close-btn" aria-label="Fechar">
6577
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
6578
+ <path d="M18 6L6 18M6 6l12 12"/>
6579
+ </svg>
6580
+ </button>
6581
+ </div>
6582
+
6583
+ <div class="neoface-doc-body">
6584
+ <div class="neoface-doc-preview-single">
6585
+ <span class="neoface-doc-preview-label">${label}</span>
6586
+ <img class="neoface-doc-preview-img neoface-doc-preview-current" />
6587
+ </div>
6588
+
6589
+ <div class="neoface-doc-preview-hint">
6590
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><path d="M12 16v-4M12 8h.01"/></svg>
6591
+ <span>Verifique se todos os dados estão legíveis</span>
6592
+ </div>
6593
+ </div>
6594
+
6595
+ <div class="neoface-doc-footer">
6596
+ <button class="neoface-doc-btn neoface-doc-btn-secondary neoface-doc-retake-btn">
6597
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
6598
+ <path d="M1 4v6h6M23 20v-6h-6"/>
6599
+ <path d="M20.49 9A9 9 0 0 0 5.64 5.64L1 10m22 4l-4.64 4.36A9 9 0 0 1 3.51 15"/>
6600
+ </svg>
6601
+ Refazer
6602
+ </button>
6603
+ <button class="neoface-doc-btn neoface-doc-btn-success neoface-doc-confirm-btn">
6604
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
6605
+ <path d="M20 6L9 17l-5-5"/>
6606
+ </svg>
6607
+ ${isLastStep ? "Confirmar e Enviar" : "Próximo"}
6608
+ </button>
6609
+ </div>
6610
+
6611
+ ${this.renderBranding()}
6612
+ `;
6613
+ }
6614
+ renderBranding() {
6615
+ return `
6616
+ <div class="neoface-doc-branding">
6617
+ <img src="/logo-icone-no-backgorund.png"
6618
+ alt="NeoFaceId" class="neoface-doc-brand-icon"
6619
+ onerror="this.style.display='none'" />
6620
+ <span class="neoface-doc-brand-name-text">NeoFaceId</span>
6621
+ <span class="neoface-doc-brand-text">by</span>
6622
+ <span class="neoface-doc-brand-name">OCTA</span>
6623
+ </div>
6624
+ `;
6625
+ }
6626
+ bindSelectEvents() {
6627
+ var _a;
6628
+ const options = (_a = this.overlay) == null ? void 0 : _a.querySelectorAll(".neoface-doc-select-option");
6629
+ options == null ? void 0 : options.forEach((option) => {
6630
+ option.addEventListener("click", () => {
6631
+ const type = option.getAttribute("data-type");
6632
+ this.selectedDocument = type;
6633
+ this.currentStep = "front";
6634
+ this.render();
6635
+ });
6636
+ });
6637
+ this.bindCloseEvent();
6638
+ }
6639
+ bindCaptureEvents() {
6640
+ var _a, _b, _c, _d;
6641
+ const captureBtn = (_a = this.overlay) == null ? void 0 : _a.querySelector(".neoface-doc-capture-btn");
6642
+ const backBtn = (_b = this.overlay) == null ? void 0 : _b.querySelector(".neoface-doc-back-btn");
6643
+ const uploadBtn = (_c = this.overlay) == null ? void 0 : _c.querySelector(".neoface-doc-upload-btn");
6644
+ const fileInput = (_d = this.overlay) == null ? void 0 : _d.querySelector(".neoface-doc-file-input");
6645
+ captureBtn == null ? void 0 : captureBtn.addEventListener("click", () => this.handleCapture());
6646
+ backBtn == null ? void 0 : backBtn.addEventListener("click", () => this.handleBack());
6647
+ uploadBtn == null ? void 0 : uploadBtn.addEventListener("click", () => fileInput == null ? void 0 : fileInput.click());
6648
+ fileInput == null ? void 0 : fileInput.addEventListener("change", (e) => this.handleFileUpload(e));
6649
+ this.bindCloseEvent();
6650
+ }
6651
+ async handleFileUpload(event) {
6652
+ var _a, _b;
6653
+ const input = event.target;
6654
+ const file = (_a = input.files) == null ? void 0 : _a[0];
6655
+ if (!file) return;
6656
+ const loadingEl = (_b = this.overlay) == null ? void 0 : _b.querySelector(".neoface-doc-loading");
6657
+ if (loadingEl) loadingEl.style.display = "flex";
6658
+ try {
6659
+ const compressedBlob = await this.compressUploadedImage(file);
6660
+ if (this.currentStep === "front") {
6661
+ this.frontBlob = compressedBlob;
6662
+ this.currentStep = "preview-front";
6663
+ } else if (this.currentStep === "back") {
6664
+ this.backBlob = compressedBlob;
6665
+ this.currentStep = "preview-back";
6666
+ }
6667
+ this.stopCamera();
6668
+ this.render();
6669
+ } catch (error) {
6670
+ console.error("[DocumentCapture] Error compressing uploaded image:", error);
6671
+ const blob = new Blob([file], { type: file.type });
6672
+ if (this.currentStep === "front") {
6673
+ this.frontBlob = blob;
6674
+ this.currentStep = "preview-front";
6675
+ } else if (this.currentStep === "back") {
6676
+ this.backBlob = blob;
6677
+ this.currentStep = "preview-back";
6678
+ }
6679
+ this.stopCamera();
6680
+ this.render();
6681
+ }
6682
+ }
6683
+ compressUploadedImage(file) {
6684
+ return new Promise((resolve, reject) => {
6685
+ const img = new Image();
6686
+ img.onload = () => {
6687
+ const MAX_SIZE = 1280;
6688
+ let width = img.width;
6689
+ let height = img.height;
6690
+ if (width > height && width > MAX_SIZE) {
6691
+ height = height * MAX_SIZE / width;
6692
+ width = MAX_SIZE;
6693
+ } else if (height > MAX_SIZE) {
6694
+ width = width * MAX_SIZE / height;
6695
+ height = MAX_SIZE;
6696
+ }
6697
+ const canvas = document.createElement("canvas");
6698
+ canvas.width = width;
6699
+ canvas.height = height;
6700
+ const ctx = canvas.getContext("2d");
6701
+ if (!ctx) {
6702
+ reject(new Error("Failed to get canvas context"));
6703
+ return;
6704
+ }
6705
+ ctx.drawImage(img, 0, 0, width, height);
6706
+ canvas.toBlob(
6707
+ (blob) => {
6708
+ if (blob) {
6709
+ console.log(`[DocumentCapture] Compressed image from ${(file.size / 1024).toFixed(0)}KB to ${(blob.size / 1024).toFixed(0)}KB`);
6710
+ resolve(blob);
6711
+ } else {
6712
+ reject(new Error("Failed to compress image"));
6713
+ }
6714
+ },
6715
+ "image/jpeg",
6716
+ 0.75
6717
+ // 75% quality - keeps file size manageable for server limits
6718
+ );
6719
+ };
6720
+ img.onerror = () => reject(new Error("Failed to load image"));
6721
+ img.src = URL.createObjectURL(file);
6722
+ });
6723
+ }
6724
+ bindPreviewEvents() {
6725
+ var _a, _b, _c;
6726
+ const confirmBtn = (_a = this.overlay) == null ? void 0 : _a.querySelector(".neoface-doc-confirm-btn");
6727
+ const retakeBtn = (_b = this.overlay) == null ? void 0 : _b.querySelector(".neoface-doc-retake-btn");
6728
+ confirmBtn == null ? void 0 : confirmBtn.addEventListener("click", () => this.handleConfirm());
6729
+ retakeBtn == null ? void 0 : retakeBtn.addEventListener("click", () => this.handleRetake());
6730
+ const previewImg = (_c = this.overlay) == null ? void 0 : _c.querySelector(".neoface-doc-preview-current");
6731
+ if (previewImg) {
6732
+ const blob = this.currentStep === "preview-front" ? this.frontBlob : this.backBlob;
6733
+ if (blob) previewImg.src = URL.createObjectURL(blob);
6734
+ }
6735
+ this.bindCloseEvent();
6736
+ }
6737
+ bindCloseEvent() {
6738
+ var _a;
6739
+ const closeBtn = (_a = this.overlay) == null ? void 0 : _a.querySelector(".neoface-doc-close-btn");
6740
+ closeBtn == null ? void 0 : closeBtn.addEventListener("click", () => this.handleCancel());
6741
+ }
6742
+ async startCamera() {
6743
+ var _a, _b, _c;
6744
+ try {
6745
+ const constraints = {
6746
+ video: {
6747
+ facingMode: this.options.useBackCamera ? "environment" : "user",
6748
+ width: { ideal: 1920 },
6749
+ height: { ideal: 1080 }
6750
+ },
6751
+ audio: false
6752
+ };
6753
+ this.stream = await navigator.mediaDevices.getUserMedia(constraints);
6754
+ this.video = (_a = this.overlay) == null ? void 0 : _a.querySelector(".neoface-doc-video");
6755
+ this.canvas = (_b = this.overlay) == null ? void 0 : _b.querySelector(".neoface-doc-canvas");
6756
+ if (this.video) {
6757
+ this.video.srcObject = this.stream;
6758
+ await this.video.play();
6759
+ }
6760
+ } catch (error) {
6761
+ console.error("[DocumentCapture] Camera error:", error);
6762
+ if (this.options.useBackCamera) {
6763
+ try {
6764
+ this.stream = await navigator.mediaDevices.getUserMedia({
6765
+ video: { facingMode: "user" },
6766
+ audio: false
6767
+ });
6768
+ if (this.video) {
6769
+ this.video.srcObject = this.stream;
6770
+ await this.video.play();
6771
+ }
6772
+ } catch (fallbackError) {
6773
+ (_c = this.resolvePromise) == null ? void 0 : _c.call(this, {
6774
+ success: false,
6775
+ frontImage: null,
6776
+ backImage: null,
6777
+ error: "Não foi possível acessar a câmera"
6778
+ });
6779
+ this.close();
6780
+ }
6781
+ }
6782
+ }
6783
+ }
6784
+ stopCamera() {
6785
+ if (this.stream) {
6786
+ this.stream.getTracks().forEach((track) => track.stop());
6787
+ this.stream = null;
6788
+ }
6789
+ }
6790
+ async handleCapture() {
6791
+ var _a;
6792
+ if (!this.video || !this.canvas) return;
6793
+ const loading = (_a = this.overlay) == null ? void 0 : _a.querySelector(".neoface-doc-loading");
6794
+ if (loading) loading.style.display = "flex";
6795
+ const ctx = this.canvas.getContext("2d");
6796
+ if (!ctx) return;
6797
+ const videoWidth = this.video.videoWidth;
6798
+ const videoHeight = this.video.videoHeight;
6799
+ const frameWidthPercent = 0.75;
6800
+ const frameHeightPercent = 0.65;
6801
+ const cropWidth = Math.floor(videoWidth * frameWidthPercent);
6802
+ const cropHeight = Math.floor(videoHeight * frameHeightPercent);
6803
+ const cropX = Math.floor((videoWidth - cropWidth) / 2);
6804
+ const cropY = Math.floor((videoHeight - cropHeight) / 2);
6805
+ this.canvas.width = cropWidth;
6806
+ this.canvas.height = cropHeight;
6807
+ ctx.drawImage(
6808
+ this.video,
6809
+ cropX,
6810
+ cropY,
6811
+ cropWidth,
6812
+ cropHeight,
6813
+ // Área de origem (crop)
6814
+ 0,
6815
+ 0,
6816
+ cropWidth,
6817
+ cropHeight
6818
+ // Área de destino (canvas inteiro)
6819
+ );
6820
+ try {
6821
+ const blob = await new Promise((resolve, reject) => {
6822
+ this.canvas.toBlob(
6823
+ (b) => b ? resolve(b) : reject(new Error("Capture failed")),
6824
+ "image/jpeg",
6825
+ 0.92
6826
+ );
6827
+ });
6828
+ if (this.currentStep === "front") {
6829
+ this.frontBlob = blob;
6830
+ this.stopCamera();
6831
+ this.currentStep = "preview-front";
6832
+ this.render();
6833
+ } else if (this.currentStep === "back") {
6834
+ this.backBlob = blob;
6835
+ this.stopCamera();
6836
+ this.currentStep = "preview-back";
6837
+ this.render();
6838
+ }
6839
+ } catch (error) {
6840
+ console.error("[DocumentCapture] Capture error:", error);
6841
+ } finally {
6842
+ if (loading) loading.style.display = "none";
6843
+ }
6844
+ }
6845
+ handleBack() {
6846
+ this.stopCamera();
6847
+ if (this.currentStep === "back") {
6848
+ this.currentStep = "front";
6849
+ this.frontBlob = null;
6850
+ } else if (this.currentStep === "front") {
6851
+ this.currentStep = "select";
6852
+ this.selectedDocument = null;
6853
+ }
6854
+ this.render();
6855
+ }
6856
+ handleRetake() {
6857
+ if (this.currentStep === "preview-front") {
6858
+ this.frontBlob = null;
6859
+ this.currentStep = "front";
6860
+ } else if (this.currentStep === "preview-back") {
6861
+ this.backBlob = null;
6862
+ this.currentStep = "back";
6863
+ }
6864
+ this.render();
6865
+ }
6866
+ handleConfirm() {
6867
+ var _a, _b;
6868
+ const docInfo = this.selectedDocument ? DOCUMENT_INFO[this.selectedDocument] : null;
6869
+ if (this.currentStep === "preview-front") {
6870
+ if (docInfo == null ? void 0 : docInfo.hasBack) {
6871
+ this.currentStep = "back";
6872
+ this.render();
6873
+ } else {
6874
+ (_a = this.resolvePromise) == null ? void 0 : _a.call(this, {
6875
+ success: true,
6876
+ documentType: this.selectedDocument || void 0,
6877
+ frontImage: this.frontBlob,
6878
+ backImage: null
6879
+ });
6880
+ this.close();
6881
+ }
6882
+ } else if (this.currentStep === "preview-back") {
6883
+ (_b = this.resolvePromise) == null ? void 0 : _b.call(this, {
6884
+ success: true,
6885
+ documentType: this.selectedDocument || void 0,
6886
+ frontImage: this.frontBlob,
6887
+ backImage: this.backBlob
6888
+ });
6889
+ this.close();
6890
+ }
6891
+ }
6892
+ handleCancel() {
6893
+ var _a, _b, _c;
6894
+ (_b = (_a = this.options).onCancel) == null ? void 0 : _b.call(_a);
6895
+ (_c = this.resolvePromise) == null ? void 0 : _c.call(this, {
6896
+ success: false,
6897
+ frontImage: null,
6898
+ backImage: null,
6899
+ error: "cancelled"
6900
+ });
6901
+ this.close();
6902
+ }
6903
+ close() {
6904
+ this.stopCamera();
6905
+ if (this.overlay) {
6906
+ this.overlay.remove();
6907
+ this.overlay = null;
6908
+ }
6909
+ }
6910
+ getStyles() {
6911
+ return `
6912
+ .neoface-doc-overlay {
6913
+ position: fixed;
6914
+ inset: 0;
6915
+ background: rgba(0, 0, 0, 0.85);
6916
+ backdrop-filter: blur(8px);
6917
+ display: flex;
6918
+ align-items: center;
6919
+ justify-content: center;
6920
+ z-index: 10000;
6921
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
6922
+ padding: 16px;
6923
+ }
6924
+
6925
+ .neoface-doc-modal {
6926
+ background: linear-gradient(145deg, #1a1a2e 0%, #16213e 100%);
6927
+ border-radius: 24px;
6928
+ width: 100%;
6929
+ max-width: 520px;
6930
+ box-shadow: 0 25px 80px rgba(0, 0, 0, 0.6), 0 0 0 1px rgba(255,255,255,0.1);
6931
+ overflow: hidden;
6932
+ animation: neoface-doc-slideIn 0.3s ease;
6933
+ }
6934
+
6935
+ @keyframes neoface-doc-slideIn {
6936
+ from { transform: translateY(20px) scale(0.98); opacity: 0; }
6937
+ to { transform: translateY(0) scale(1); opacity: 1; }
6938
+ }
6939
+
6940
+ .neoface-doc-header {
6941
+ padding: 20px 24px;
6942
+ display: flex;
6943
+ align-items: flex-start;
6944
+ justify-content: space-between;
6945
+ border-bottom: 1px solid rgba(255, 255, 255, 0.08);
6946
+ }
6947
+
6948
+ .neoface-doc-title {
6949
+ margin: 0 0 4px 0;
6950
+ font-size: 20px;
6951
+ font-weight: 600;
6952
+ color: #fff;
6953
+ }
6954
+
6955
+ .neoface-doc-subtitle {
6956
+ margin: 0;
6957
+ font-size: 14px;
6958
+ color: rgba(255, 255, 255, 0.6);
6959
+ }
6960
+
6961
+ .neoface-doc-step-indicator {
6962
+ display: flex;
6963
+ align-items: center;
6964
+ gap: 12px;
6965
+ }
6966
+
6967
+ .neoface-doc-step-badge {
6968
+ background: linear-gradient(135deg, #00c8ff, #0099cc);
6969
+ color: #fff;
6970
+ font-size: 12px;
6971
+ font-weight: 600;
6972
+ padding: 4px 10px;
6973
+ border-radius: 20px;
6974
+ }
6975
+
6976
+ .neoface-doc-step-label {
6977
+ font-size: 16px;
6978
+ font-weight: 500;
6979
+ color: #fff;
6980
+ }
6981
+
6982
+ .neoface-doc-close-btn {
6983
+ background: transparent;
6984
+ border: none;
6985
+ padding: 4px;
6986
+ cursor: pointer;
6987
+ color: rgba(255, 255, 255, 0.5);
6988
+ transition: all 0.2s;
6989
+ }
6990
+
6991
+ .neoface-doc-close-btn:hover {
6992
+ color: #fff;
6993
+ }
6994
+
6995
+ .neoface-doc-body {
6996
+ padding: 20px 24px;
6997
+ }
6998
+
6999
+ /* Select Step */
7000
+ .neoface-doc-select-grid {
7001
+ display: flex;
7002
+ flex-direction: column;
7003
+ gap: 12px;
7004
+ }
7005
+
7006
+ .neoface-doc-select-option {
7007
+ display: flex;
7008
+ align-items: center;
7009
+ gap: 16px;
7010
+ padding: 16px 20px;
7011
+ background: rgba(255, 255, 255, 0.05);
7012
+ border: 2px solid rgba(255, 255, 255, 0.1);
7013
+ border-radius: 14px;
7014
+ cursor: pointer;
7015
+ transition: all 0.2s;
7016
+ text-align: left;
7017
+ }
7018
+
7019
+ .neoface-doc-select-option:hover {
7020
+ background: rgba(0, 200, 255, 0.1);
7021
+ border-color: rgba(0, 200, 255, 0.4);
7022
+ transform: translateX(4px);
7023
+ }
7024
+
7025
+ .neoface-doc-select-icon {
7026
+ display: flex;
7027
+ align-items: center;
7028
+ justify-content: center;
7029
+ width: 44px;
7030
+ height: 44px;
7031
+ background: rgba(0, 200, 255, 0.15);
7032
+ border-radius: 12px;
7033
+ color: #00c8ff;
7034
+ }
7035
+
7036
+ .neoface-doc-select-icon svg {
7037
+ width: 24px;
7038
+ height: 24px;
7039
+ }
7040
+
7041
+ .neoface-doc-select-name {
7042
+ flex: 1;
7043
+ font-size: 15px;
7044
+ font-weight: 500;
7045
+ color: #fff;
7046
+ }
7047
+
7048
+ .neoface-doc-select-arrow {
7049
+ color: rgba(255, 255, 255, 0.4);
7050
+ transition: transform 0.2s;
7051
+ }
7052
+
7053
+ .neoface-doc-select-option:hover .neoface-doc-select-arrow {
7054
+ color: #00c8ff;
7055
+ transform: translateX(4px);
7056
+ }
7057
+
7058
+ .neoface-doc-tips {
7059
+ margin-top: 20px;
7060
+ }
7061
+
7062
+ .neoface-doc-tip {
7063
+ display: flex;
7064
+ align-items: center;
7065
+ justify-content: center;
7066
+ padding: 14px 16px;
7067
+ background: rgba(0, 200, 255, 0.08);
7068
+ border: 1px solid rgba(0, 200, 255, 0.15);
7069
+ border-radius: 10px;
7070
+ color: rgba(255, 255, 255, 0.8);
7071
+ font-size: 13px;
7072
+ line-height: 1;
7073
+ text-align: center;
7074
+ }
7075
+
7076
+ .neoface-doc-tip span {
7077
+ transform: translateY(-2px);
7078
+ }
7079
+
7080
+ .neoface-doc-tip-icon {
7081
+ color: #00c8ff;
7082
+ flex-shrink: 0;
7083
+ }
7084
+
7085
+ /* Camera */
7086
+ .neoface-doc-camera-container {
7087
+ position: relative;
7088
+ background: #000;
7089
+ border-radius: 16px;
7090
+ overflow: hidden;
7091
+ aspect-ratio: 4/3;
7092
+ }
7093
+
7094
+ .neoface-doc-video {
7095
+ width: 100%;
7096
+ height: 100%;
7097
+ object-fit: cover;
7098
+ }
7099
+
7100
+ .neoface-doc-canvas {
7101
+ display: none;
7102
+ }
7103
+
7104
+ .neoface-doc-guide {
7105
+ position: absolute;
7106
+ inset: 0;
7107
+ display: flex;
7108
+ align-items: center;
7109
+ justify-content: center;
7110
+ pointer-events: none;
7111
+ }
7112
+
7113
+ .neoface-doc-frame {
7114
+ width: 75%;
7115
+ height: 65%;
7116
+ position: relative;
7117
+ border: 2px dashed rgba(0, 200, 255, 0.5);
7118
+ border-radius: 12px;
7119
+ background: rgba(0, 200, 255, 0.03);
7120
+ }
7121
+
7122
+ .neoface-doc-corner {
7123
+ position: absolute;
7124
+ width: 28px;
7125
+ height: 28px;
7126
+ border-color: #00c8ff;
7127
+ border-style: solid;
7128
+ }
7129
+
7130
+ .neoface-doc-corner-tl { top: -2px; left: -2px; border-width: 4px 0 0 4px; border-radius: 10px 0 0 0; }
7131
+ .neoface-doc-corner-tr { top: -2px; right: -2px; border-width: 4px 4px 0 0; border-radius: 0 10px 0 0; }
7132
+ .neoface-doc-corner-bl { bottom: -2px; left: -2px; border-width: 0 0 4px 4px; border-radius: 0 0 0 10px; }
7133
+ .neoface-doc-corner-br { bottom: -2px; right: -2px; border-width: 0 4px 4px 0; border-radius: 0 0 10px 0; }
7134
+
7135
+ .neoface-doc-frame-label {
7136
+ position: absolute;
7137
+ bottom: -30px;
7138
+ left: 50%;
7139
+ transform: translateX(-50%);
7140
+ background: rgba(0, 0, 0, 0.7);
7141
+ color: #00c8ff;
7142
+ font-size: 12px;
7143
+ font-weight: 500;
7144
+ padding: 6px 14px;
7145
+ border-radius: 20px;
7146
+ white-space: nowrap;
7147
+ }
7148
+
7149
+ .neoface-doc-loading {
7150
+ position: absolute;
7151
+ inset: 0;
7152
+ background: rgba(0, 0, 0, 0.7);
7153
+ display: none;
7154
+ align-items: center;
7155
+ justify-content: center;
7156
+ }
7157
+
7158
+ .neoface-doc-spinner {
7159
+ width: 48px;
7160
+ height: 48px;
7161
+ border: 4px solid rgba(255, 255, 255, 0.2);
7162
+ border-top-color: #00c8ff;
7163
+ border-radius: 50%;
7164
+ animation: neoface-doc-spin 0.8s linear infinite;
7165
+ }
7166
+
7167
+ @keyframes neoface-doc-spin {
7168
+ to { transform: rotate(360deg); }
7169
+ }
7170
+
7171
+ .neoface-doc-instructions {
7172
+ display: flex;
7173
+ flex-direction: column;
7174
+ gap: 8px;
7175
+ margin-top: 16px;
7176
+ }
7177
+
7178
+ .neoface-doc-instruction {
7179
+ display: flex;
7180
+ align-items: center;
7181
+ gap: 10px;
7182
+ padding: 10px 14px;
7183
+ background: rgba(255, 255, 255, 0.04);
7184
+ border-radius: 10px;
7185
+ font-size: 13px;
7186
+ color: rgba(255, 255, 255, 0.75);
7187
+ }
7188
+
7189
+ .neoface-doc-instruction-icon {
7190
+ color: rgba(255, 255, 255, 0.5);
7191
+ flex-shrink: 0;
7192
+ }
7193
+
7194
+ /* Preview */
7195
+ .neoface-doc-preview-single {
7196
+ display: flex;
7197
+ flex-direction: column;
7198
+ align-items: center;
7199
+ gap: 12px;
7200
+ }
7201
+
7202
+ .neoface-doc-preview-label {
7203
+ font-size: 12px;
7204
+ font-weight: 500;
7205
+ color: rgba(255, 255, 255, 0.6);
7206
+ text-transform: uppercase;
7207
+ letter-spacing: 0.5px;
7208
+ text-align: center;
7209
+ }
7210
+
7211
+ .neoface-doc-preview-img {
7212
+ width: 75%;
7213
+ height: auto;
7214
+ border-radius: 12px;
7215
+ border: 2px solid rgba(0, 200, 255, 0.3);
7216
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
7217
+ }
7218
+
7219
+ .neoface-doc-preview-hint {
7220
+ display: flex;
7221
+ align-items: center;
7222
+ gap: 10px;
7223
+ padding: 12px 16px;
7224
+ background: rgba(0, 200, 255, 0.08);
7225
+ border: 1px solid rgba(0, 200, 255, 0.15);
7226
+ border-radius: 10px;
7227
+ font-size: 13px;
7228
+ color: rgba(255, 255, 255, 0.8);
7229
+ margin-top: 8px;
7230
+ }
7231
+
7232
+ .neoface-doc-preview-hint svg {
7233
+ color: #00c8ff;
7234
+ flex-shrink: 0;
7235
+ }
7236
+
7237
+ /* Footer */
7238
+ .neoface-doc-footer {
7239
+ padding: 16px 24px;
7240
+ display: flex;
7241
+ gap: 12px;
7242
+ justify-content: flex-end;
7243
+ border-top: 1px solid rgba(255, 255, 255, 0.08);
7244
+ }
7245
+
7246
+ .neoface-doc-btn {
7247
+ display: flex;
7248
+ align-items: center;
7249
+ gap: 8px;
7250
+ padding: 12px 20px;
7251
+ border-radius: 12px;
7252
+ font-size: 14px;
7253
+ font-weight: 500;
7254
+ cursor: pointer;
7255
+ transition: all 0.2s;
7256
+ border: none;
7257
+ }
7258
+
7259
+ .neoface-doc-btn-primary {
7260
+ background: linear-gradient(135deg, #00c8ff 0%, #0099cc 100%);
7261
+ color: #fff;
7262
+ }
7263
+
7264
+ .neoface-doc-btn-primary:hover {
7265
+ transform: translateY(-2px);
7266
+ box-shadow: 0 6px 20px rgba(0, 200, 255, 0.4);
7267
+ }
7268
+
7269
+ .neoface-doc-btn-success {
7270
+ background: linear-gradient(135deg, #10b981 0%, #059669 100%);
7271
+ color: #fff;
7272
+ }
7273
+
7274
+ .neoface-doc-btn-success:hover {
7275
+ transform: translateY(-2px);
7276
+ box-shadow: 0 6px 20px rgba(16, 185, 129, 0.4);
7277
+ }
7278
+
7279
+ .neoface-doc-btn-secondary {
7280
+ background: rgba(255, 255, 255, 0.08);
7281
+ color: rgba(255, 255, 255, 0.8);
7282
+ }
7283
+
7284
+ .neoface-doc-btn-secondary:hover {
7285
+ background: rgba(255, 255, 255, 0.12);
7286
+ color: #fff;
7287
+ }
7288
+
7289
+ /* Branding */
7290
+ .neoface-doc-branding {
7291
+ display: flex;
7292
+ align-items: center;
7293
+ justify-content: center;
7294
+ gap: 8px;
7295
+ padding: 16px 24px;
7296
+ border-top: 1px solid rgba(255, 255, 255, 0.06);
7297
+ }
7298
+
7299
+ .neoface-doc-brand-icon {
7300
+ height: 22px;
7301
+ width: auto;
7302
+ }
7303
+
7304
+ .neoface-doc-brand-name-text {
7305
+ font-size: 13px;
7306
+ font-weight: 600;
7307
+ color: rgba(255, 255, 255, 0.9);
7308
+ }
7309
+
7310
+ .neoface-doc-brand-text {
7311
+ font-size: 12px;
7312
+ color: rgba(255, 255, 255, 0.5);
7313
+ }
7314
+
7315
+ .neoface-doc-brand-name {
7316
+ font-size: 13px;
7317
+ font-weight: 700;
7318
+ color: #00c8ff;
7319
+ letter-spacing: 1px;
7320
+ }
7321
+
7322
+ /* Mobile */
7323
+ @media (max-width: 500px) {
7324
+ .neoface-doc-modal {
7325
+ border-radius: 20px;
7326
+ }
7327
+
7328
+ .neoface-doc-header,
7329
+ .neoface-doc-body,
7330
+ .neoface-doc-footer {
7331
+ padding: 16px 18px;
7332
+ }
7333
+
7334
+ .neoface-doc-preview-grid.has-back {
7335
+ grid-template-columns: 1fr;
7336
+ }
7337
+ }
7338
+ `;
7339
+ }
7340
+ }
7341
+ const startDocumentCapture = async (options) => {
7342
+ const modal = new DocumentCaptureModal(options);
7343
+ return modal.open();
7344
+ };
5924
7345
  function start(applicationToken, callbacks) {
5925
7346
  const container = document.createElement("div");
5926
7347
  container.id = "neoface-modal-container";
@@ -5931,7 +7352,7 @@ function start(applicationToken, callbacks) {
5931
7352
  }
5932
7353
  };
5933
7354
  validateToken(applicationToken).then(() => {
5934
- const modalElement = require$$0$1.createElement(FaceCaptureModal, {
7355
+ const modalElement = React.createElement(FaceCaptureModal, {
5935
7356
  accessToken: applicationToken,
5936
7357
  onClose: cleanup,
5937
7358
  onSuccess: (result) => {
@@ -5943,7 +7364,7 @@ function start(applicationToken, callbacks) {
5943
7364
  });
5944
7365
  }
5945
7366
  });
5946
- const root = createRoot(container);
7367
+ const root = clientExports.createRoot(container);
5947
7368
  root.render(modalElement);
5948
7369
  }).catch((error) => {
5949
7370
  cleanup();
@@ -5960,7 +7381,7 @@ function startBiometricRegistration(personData, applicationToken, callbacks, opt
5960
7381
  }
5961
7382
  };
5962
7383
  validateToken(applicationToken).then(() => {
5963
- const modalElement = require$$0$1.createElement(BiometricRegistrationModal, {
7384
+ const modalElement = React.createElement(BiometricRegistrationModal, {
5964
7385
  personData,
5965
7386
  applicationToken,
5966
7387
  useRealApi: (options == null ? void 0 : options.useRealApi) ?? true,
@@ -5975,7 +7396,35 @@ function startBiometricRegistration(personData, applicationToken, callbacks, opt
5975
7396
  callbacks.onError("BIOMETRIC_REGISTRATION_ERROR", error);
5976
7397
  }
5977
7398
  });
5978
- const root = createRoot(container);
7399
+ const root = clientExports.createRoot(container);
7400
+ root.render(modalElement);
7401
+ }).catch((error) => {
7402
+ cleanup();
7403
+ callbacks.onError("TOKEN_VALIDATION_ERROR", error.message || "Failed to validate token");
7404
+ });
7405
+ }
7406
+ function startLivenessCapture(applicationToken, callbacks) {
7407
+ const container = document.createElement("div");
7408
+ container.id = "neoface-liveness-capture-container";
7409
+ document.body.appendChild(container);
7410
+ const cleanup = () => {
7411
+ if (document.body.contains(container)) {
7412
+ document.body.removeChild(container);
7413
+ }
7414
+ };
7415
+ validateToken(applicationToken).then(() => {
7416
+ const modalElement = React.createElement(BiometricRegistrationModal, {
7417
+ applicationToken,
7418
+ onClose: cleanup,
7419
+ onPhotosCaptured: (photos) => {
7420
+ callbacks.onSuccess(photos);
7421
+ },
7422
+ onError: (error) => {
7423
+ cleanup();
7424
+ callbacks.onError("LIVENESS_CAPTURE_ERROR", error);
7425
+ }
7426
+ });
7427
+ const root = clientExports.createRoot(container);
5979
7428
  root.render(modalElement);
5980
7429
  }).catch((error) => {
5981
7430
  cleanup();
@@ -5986,6 +7435,7 @@ export {
5986
7435
  BiometricCaptureModal$1 as BiometricCaptureModal,
5987
7436
  BiometricRegistrationModal,
5988
7437
  BiometricStatusOverlay,
7438
+ DocumentCaptureModal,
5989
7439
  EmailPasswordModal,
5990
7440
  ErrorType,
5991
7441
  FaceCaptureModal,
@@ -6004,6 +7454,8 @@ export {
6004
7454
  initializeBiometricDetection,
6005
7455
  isAdvancedDetectionAvailable,
6006
7456
  loginWithBiometric$1 as loginWithBiometric,
7457
+ loginWithEmail,
7458
+ preloadFaceDetectionModels,
6007
7459
  recognize,
6008
7460
  recognizeBiometric,
6009
7461
  recognizeByPurpose,
@@ -6014,8 +7466,10 @@ export {
6014
7466
  start,
6015
7467
  startAutoLogin,
6016
7468
  startBiometricRegistration,
7469
+ startDocumentCapture,
6017
7470
  startFaceLogin,
6018
7471
  startHandLogin,
7472
+ startLivenessCapture,
6019
7473
  startOnboarding,
6020
7474
  validateOnboardingToken,
6021
7475
  validateToken