@neofaceid/web-sdk 1.10.0 → 1.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"neoface-id-sdk.umd.js","sources":["../node_modules/react-dom/client.js","../node_modules/react/cjs/react-jsx-runtime.development.js","../node_modules/react/jsx-runtime.js","../node_modules/react/cjs/react-jsx-runtime.production.min.js","../src/hooks/useCamera.ts","../src/errors.ts","../src/config.ts","../src/api.ts","../src/components/FaceCaptureModal.tsx","../src/components/BiometricRegistrationModal.tsx","../src/components/BiometricStatusOverlay.ts","../src/components/FallbackPrompt.tsx","../src/EmailPasswordModal.ts","../src/utils/biometricLoginFlow.ts","../src/biometricLogin.ts","../src/biometricDetection.ts","../src/OnboardingCaptureModal.ts","../src/authorization.ts","../src/DocumentCaptureModal.ts","../src/BiometricCaptureModal.ts","../src/NeoFaceID.ts","../src/version.ts","../src/index.ts","../src/onboarding.ts"],"sourcesContent":["'use strict';\n\nvar m = require('react-dom');\nif (process.env.NODE_ENV === 'production') {\n exports.createRoot = m.createRoot;\n exports.hydrateRoot = m.hydrateRoot;\n} else {\n var i = m.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;\n exports.createRoot = function(c, o) {\n i.usingClientEntryPoint = true;\n try {\n return m.createRoot(c, o);\n } finally {\n i.usingClientEntryPoint = false;\n }\n };\n exports.hydrateRoot = function(c, h, o) {\n i.usingClientEntryPoint = true;\n try {\n return m.hydrateRoot(c, h, o);\n } finally {\n i.usingClientEntryPoint = false;\n }\n };\n}\n","/**\n * @license React\n * react-jsx-runtime.development.js\n *\n * Copyright (c) Facebook, Inc. and its affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n'use strict';\n\nif (process.env.NODE_ENV !== \"production\") {\n (function() {\n'use strict';\n\nvar React = require('react');\n\n// ATTENTION\n// When adding new symbols to this file,\n// Please consider also adding to 'react-devtools-shared/src/backend/ReactSymbols'\n// The Symbol used to tag the ReactElement-like types.\nvar REACT_ELEMENT_TYPE = Symbol.for('react.element');\nvar REACT_PORTAL_TYPE = Symbol.for('react.portal');\nvar REACT_FRAGMENT_TYPE = Symbol.for('react.fragment');\nvar REACT_STRICT_MODE_TYPE = Symbol.for('react.strict_mode');\nvar REACT_PROFILER_TYPE = Symbol.for('react.profiler');\nvar REACT_PROVIDER_TYPE = Symbol.for('react.provider');\nvar REACT_CONTEXT_TYPE = Symbol.for('react.context');\nvar REACT_FORWARD_REF_TYPE = Symbol.for('react.forward_ref');\nvar REACT_SUSPENSE_TYPE = Symbol.for('react.suspense');\nvar REACT_SUSPENSE_LIST_TYPE = Symbol.for('react.suspense_list');\nvar REACT_MEMO_TYPE = Symbol.for('react.memo');\nvar REACT_LAZY_TYPE = Symbol.for('react.lazy');\nvar REACT_OFFSCREEN_TYPE = Symbol.for('react.offscreen');\nvar MAYBE_ITERATOR_SYMBOL = Symbol.iterator;\nvar FAUX_ITERATOR_SYMBOL = '@@iterator';\nfunction getIteratorFn(maybeIterable) {\n if (maybeIterable === null || typeof maybeIterable !== 'object') {\n return null;\n }\n\n var maybeIterator = MAYBE_ITERATOR_SYMBOL && maybeIterable[MAYBE_ITERATOR_SYMBOL] || maybeIterable[FAUX_ITERATOR_SYMBOL];\n\n if (typeof maybeIterator === 'function') {\n return maybeIterator;\n }\n\n return null;\n}\n\nvar ReactSharedInternals = React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;\n\nfunction error(format) {\n {\n {\n for (var _len2 = arguments.length, args = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {\n args[_key2 - 1] = arguments[_key2];\n }\n\n printWarning('error', format, args);\n }\n }\n}\n\nfunction printWarning(level, format, args) {\n // When changing this logic, you might want to also\n // update consoleWithStackDev.www.js as well.\n {\n var ReactDebugCurrentFrame = ReactSharedInternals.ReactDebugCurrentFrame;\n var stack = ReactDebugCurrentFrame.getStackAddendum();\n\n if (stack !== '') {\n format += '%s';\n args = args.concat([stack]);\n } // eslint-disable-next-line react-internal/safe-string-coercion\n\n\n var argsWithFormat = args.map(function (item) {\n return String(item);\n }); // Careful: RN currently depends on this prefix\n\n argsWithFormat.unshift('Warning: ' + format); // We intentionally don't use spread (or .apply) directly because it\n // breaks IE9: https://github.com/facebook/react/issues/13610\n // eslint-disable-next-line react-internal/no-production-logging\n\n Function.prototype.apply.call(console[level], console, argsWithFormat);\n }\n}\n\n// -----------------------------------------------------------------------------\n\nvar enableScopeAPI = false; // Experimental Create Event Handle API.\nvar enableCacheElement = false;\nvar enableTransitionTracing = false; // No known bugs, but needs performance testing\n\nvar enableLegacyHidden = false; // Enables unstable_avoidThisFallback feature in Fiber\n// stuff. Intended to enable React core members to more easily debug scheduling\n// issues in DEV builds.\n\nvar enableDebugTracing = false; // Track which Fiber(s) schedule render work.\n\nvar REACT_MODULE_REFERENCE;\n\n{\n REACT_MODULE_REFERENCE = Symbol.for('react.module.reference');\n}\n\nfunction isValidElementType(type) {\n if (typeof type === 'string' || typeof type === 'function') {\n return true;\n } // Note: typeof might be other than 'symbol' or 'number' (e.g. if it's a polyfill).\n\n\n if (type === REACT_FRAGMENT_TYPE || type === REACT_PROFILER_TYPE || enableDebugTracing || type === REACT_STRICT_MODE_TYPE || type === REACT_SUSPENSE_TYPE || type === REACT_SUSPENSE_LIST_TYPE || enableLegacyHidden || type === REACT_OFFSCREEN_TYPE || enableScopeAPI || enableCacheElement || enableTransitionTracing ) {\n return true;\n }\n\n if (typeof type === 'object' && type !== null) {\n if (type.$$typeof === REACT_LAZY_TYPE || type.$$typeof === REACT_MEMO_TYPE || type.$$typeof === REACT_PROVIDER_TYPE || type.$$typeof === REACT_CONTEXT_TYPE || type.$$typeof === REACT_FORWARD_REF_TYPE || // This needs to include all possible module reference object\n // types supported by any Flight configuration anywhere since\n // we don't know which Flight build this will end up being used\n // with.\n type.$$typeof === REACT_MODULE_REFERENCE || type.getModuleId !== undefined) {\n return true;\n }\n }\n\n return false;\n}\n\nfunction getWrappedName(outerType, innerType, wrapperName) {\n var displayName = outerType.displayName;\n\n if (displayName) {\n return displayName;\n }\n\n var functionName = innerType.displayName || innerType.name || '';\n return functionName !== '' ? wrapperName + \"(\" + functionName + \")\" : wrapperName;\n} // Keep in sync with react-reconciler/getComponentNameFromFiber\n\n\nfunction getContextName(type) {\n return type.displayName || 'Context';\n} // Note that the reconciler package should generally prefer to use getComponentNameFromFiber() instead.\n\n\nfunction getComponentNameFromType(type) {\n if (type == null) {\n // Host root, text node or just invalid type.\n return null;\n }\n\n {\n if (typeof type.tag === 'number') {\n error('Received an unexpected object in getComponentNameFromType(). ' + 'This is likely a bug in React. Please file an issue.');\n }\n }\n\n if (typeof type === 'function') {\n return type.displayName || type.name || null;\n }\n\n if (typeof type === 'string') {\n return type;\n }\n\n switch (type) {\n case REACT_FRAGMENT_TYPE:\n return 'Fragment';\n\n case REACT_PORTAL_TYPE:\n return 'Portal';\n\n case REACT_PROFILER_TYPE:\n return 'Profiler';\n\n case REACT_STRICT_MODE_TYPE:\n return 'StrictMode';\n\n case REACT_SUSPENSE_TYPE:\n return 'Suspense';\n\n case REACT_SUSPENSE_LIST_TYPE:\n return 'SuspenseList';\n\n }\n\n if (typeof type === 'object') {\n switch (type.$$typeof) {\n case REACT_CONTEXT_TYPE:\n var context = type;\n return getContextName(context) + '.Consumer';\n\n case REACT_PROVIDER_TYPE:\n var provider = type;\n return getContextName(provider._context) + '.Provider';\n\n case REACT_FORWARD_REF_TYPE:\n return getWrappedName(type, type.render, 'ForwardRef');\n\n case REACT_MEMO_TYPE:\n var outerName = type.displayName || null;\n\n if (outerName !== null) {\n return outerName;\n }\n\n return getComponentNameFromType(type.type) || 'Memo';\n\n case REACT_LAZY_TYPE:\n {\n var lazyComponent = type;\n var payload = lazyComponent._payload;\n var init = lazyComponent._init;\n\n try {\n return getComponentNameFromType(init(payload));\n } catch (x) {\n return null;\n }\n }\n\n // eslint-disable-next-line no-fallthrough\n }\n }\n\n return null;\n}\n\nvar assign = Object.assign;\n\n// Helpers to patch console.logs to avoid logging during side-effect free\n// replaying on render function. This currently only patches the object\n// lazily which won't cover if the log function was extracted eagerly.\n// We could also eagerly patch the method.\nvar disabledDepth = 0;\nvar prevLog;\nvar prevInfo;\nvar prevWarn;\nvar prevError;\nvar prevGroup;\nvar prevGroupCollapsed;\nvar prevGroupEnd;\n\nfunction disabledLog() {}\n\ndisabledLog.__reactDisabledLog = true;\nfunction disableLogs() {\n {\n if (disabledDepth === 0) {\n /* eslint-disable react-internal/no-production-logging */\n prevLog = console.log;\n prevInfo = console.info;\n prevWarn = console.warn;\n prevError = console.error;\n prevGroup = console.group;\n prevGroupCollapsed = console.groupCollapsed;\n prevGroupEnd = console.groupEnd; // https://github.com/facebook/react/issues/19099\n\n var props = {\n configurable: true,\n enumerable: true,\n value: disabledLog,\n writable: true\n }; // $FlowFixMe Flow thinks console is immutable.\n\n Object.defineProperties(console, {\n info: props,\n log: props,\n warn: props,\n error: props,\n group: props,\n groupCollapsed: props,\n groupEnd: props\n });\n /* eslint-enable react-internal/no-production-logging */\n }\n\n disabledDepth++;\n }\n}\nfunction reenableLogs() {\n {\n disabledDepth--;\n\n if (disabledDepth === 0) {\n /* eslint-disable react-internal/no-production-logging */\n var props = {\n configurable: true,\n enumerable: true,\n writable: true\n }; // $FlowFixMe Flow thinks console is immutable.\n\n Object.defineProperties(console, {\n log: assign({}, props, {\n value: prevLog\n }),\n info: assign({}, props, {\n value: prevInfo\n }),\n warn: assign({}, props, {\n value: prevWarn\n }),\n error: assign({}, props, {\n value: prevError\n }),\n group: assign({}, props, {\n value: prevGroup\n }),\n groupCollapsed: assign({}, props, {\n value: prevGroupCollapsed\n }),\n groupEnd: assign({}, props, {\n value: prevGroupEnd\n })\n });\n /* eslint-enable react-internal/no-production-logging */\n }\n\n if (disabledDepth < 0) {\n error('disabledDepth fell below zero. ' + 'This is a bug in React. Please file an issue.');\n }\n }\n}\n\nvar ReactCurrentDispatcher = ReactSharedInternals.ReactCurrentDispatcher;\nvar prefix;\nfunction describeBuiltInComponentFrame(name, source, ownerFn) {\n {\n if (prefix === undefined) {\n // Extract the VM specific prefix used by each line.\n try {\n throw Error();\n } catch (x) {\n var match = x.stack.trim().match(/\\n( *(at )?)/);\n prefix = match && match[1] || '';\n }\n } // We use the prefix to ensure our stacks line up with native stack frames.\n\n\n return '\\n' + prefix + name;\n }\n}\nvar reentry = false;\nvar componentFrameCache;\n\n{\n var PossiblyWeakMap = typeof WeakMap === 'function' ? WeakMap : Map;\n componentFrameCache = new PossiblyWeakMap();\n}\n\nfunction describeNativeComponentFrame(fn, construct) {\n // If something asked for a stack inside a fake render, it should get ignored.\n if ( !fn || reentry) {\n return '';\n }\n\n {\n var frame = componentFrameCache.get(fn);\n\n if (frame !== undefined) {\n return frame;\n }\n }\n\n var control;\n reentry = true;\n var previousPrepareStackTrace = Error.prepareStackTrace; // $FlowFixMe It does accept undefined.\n\n Error.prepareStackTrace = undefined;\n var previousDispatcher;\n\n {\n previousDispatcher = ReactCurrentDispatcher.current; // Set the dispatcher in DEV because this might be call in the render function\n // for warnings.\n\n ReactCurrentDispatcher.current = null;\n disableLogs();\n }\n\n try {\n // This should throw.\n if (construct) {\n // Something should be setting the props in the constructor.\n var Fake = function () {\n throw Error();\n }; // $FlowFixMe\n\n\n Object.defineProperty(Fake.prototype, 'props', {\n set: function () {\n // We use a throwing setter instead of frozen or non-writable props\n // because that won't throw in a non-strict mode function.\n throw Error();\n }\n });\n\n if (typeof Reflect === 'object' && Reflect.construct) {\n // We construct a different control for this case to include any extra\n // frames added by the construct call.\n try {\n Reflect.construct(Fake, []);\n } catch (x) {\n control = x;\n }\n\n Reflect.construct(fn, [], Fake);\n } else {\n try {\n Fake.call();\n } catch (x) {\n control = x;\n }\n\n fn.call(Fake.prototype);\n }\n } else {\n try {\n throw Error();\n } catch (x) {\n control = x;\n }\n\n fn();\n }\n } catch (sample) {\n // This is inlined manually because closure doesn't do it for us.\n if (sample && control && typeof sample.stack === 'string') {\n // This extracts the first frame from the sample that isn't also in the control.\n // Skipping one frame that we assume is the frame that calls the two.\n var sampleLines = sample.stack.split('\\n');\n var controlLines = control.stack.split('\\n');\n var s = sampleLines.length - 1;\n var c = controlLines.length - 1;\n\n while (s >= 1 && c >= 0 && sampleLines[s] !== controlLines[c]) {\n // We expect at least one stack frame to be shared.\n // Typically this will be the root most one. However, stack frames may be\n // cut off due to maximum stack limits. In this case, one maybe cut off\n // earlier than the other. We assume that the sample is longer or the same\n // and there for cut off earlier. So we should find the root most frame in\n // the sample somewhere in the control.\n c--;\n }\n\n for (; s >= 1 && c >= 0; s--, c--) {\n // Next we find the first one that isn't the same which should be the\n // frame that called our sample function and the control.\n if (sampleLines[s] !== controlLines[c]) {\n // In V8, the first line is describing the message but other VMs don't.\n // If we're about to return the first line, and the control is also on the same\n // line, that's a pretty good indicator that our sample threw at same line as\n // the control. I.e. before we entered the sample frame. So we ignore this result.\n // This can happen if you passed a class to function component, or non-function.\n if (s !== 1 || c !== 1) {\n do {\n s--;\n c--; // We may still have similar intermediate frames from the construct call.\n // The next one that isn't the same should be our match though.\n\n if (c < 0 || sampleLines[s] !== controlLines[c]) {\n // V8 adds a \"new\" prefix for native classes. Let's remove it to make it prettier.\n var _frame = '\\n' + sampleLines[s].replace(' at new ', ' at '); // If our component frame is labeled \"<anonymous>\"\n // but we have a user-provided \"displayName\"\n // splice it in to make the stack more readable.\n\n\n if (fn.displayName && _frame.includes('<anonymous>')) {\n _frame = _frame.replace('<anonymous>', fn.displayName);\n }\n\n {\n if (typeof fn === 'function') {\n componentFrameCache.set(fn, _frame);\n }\n } // Return the line we found.\n\n\n return _frame;\n }\n } while (s >= 1 && c >= 0);\n }\n\n break;\n }\n }\n }\n } finally {\n reentry = false;\n\n {\n ReactCurrentDispatcher.current = previousDispatcher;\n reenableLogs();\n }\n\n Error.prepareStackTrace = previousPrepareStackTrace;\n } // Fallback to just using the name if we couldn't make it throw.\n\n\n var name = fn ? fn.displayName || fn.name : '';\n var syntheticFrame = name ? describeBuiltInComponentFrame(name) : '';\n\n {\n if (typeof fn === 'function') {\n componentFrameCache.set(fn, syntheticFrame);\n }\n }\n\n return syntheticFrame;\n}\nfunction describeFunctionComponentFrame(fn, source, ownerFn) {\n {\n return describeNativeComponentFrame(fn, false);\n }\n}\n\nfunction shouldConstruct(Component) {\n var prototype = Component.prototype;\n return !!(prototype && prototype.isReactComponent);\n}\n\nfunction describeUnknownElementTypeFrameInDEV(type, source, ownerFn) {\n\n if (type == null) {\n return '';\n }\n\n if (typeof type === 'function') {\n {\n return describeNativeComponentFrame(type, shouldConstruct(type));\n }\n }\n\n if (typeof type === 'string') {\n return describeBuiltInComponentFrame(type);\n }\n\n switch (type) {\n case REACT_SUSPENSE_TYPE:\n return describeBuiltInComponentFrame('Suspense');\n\n case REACT_SUSPENSE_LIST_TYPE:\n return describeBuiltInComponentFrame('SuspenseList');\n }\n\n if (typeof type === 'object') {\n switch (type.$$typeof) {\n case REACT_FORWARD_REF_TYPE:\n return describeFunctionComponentFrame(type.render);\n\n case REACT_MEMO_TYPE:\n // Memo may contain any component type so we recursively resolve it.\n return describeUnknownElementTypeFrameInDEV(type.type, source, ownerFn);\n\n case REACT_LAZY_TYPE:\n {\n var lazyComponent = type;\n var payload = lazyComponent._payload;\n var init = lazyComponent._init;\n\n try {\n // Lazy may contain any component type so we recursively resolve it.\n return describeUnknownElementTypeFrameInDEV(init(payload), source, ownerFn);\n } catch (x) {}\n }\n }\n }\n\n return '';\n}\n\nvar hasOwnProperty = Object.prototype.hasOwnProperty;\n\nvar loggedTypeFailures = {};\nvar ReactDebugCurrentFrame = ReactSharedInternals.ReactDebugCurrentFrame;\n\nfunction setCurrentlyValidatingElement(element) {\n {\n if (element) {\n var owner = element._owner;\n var stack = describeUnknownElementTypeFrameInDEV(element.type, element._source, owner ? owner.type : null);\n ReactDebugCurrentFrame.setExtraStackFrame(stack);\n } else {\n ReactDebugCurrentFrame.setExtraStackFrame(null);\n }\n }\n}\n\nfunction checkPropTypes(typeSpecs, values, location, componentName, element) {\n {\n // $FlowFixMe This is okay but Flow doesn't know it.\n var has = Function.call.bind(hasOwnProperty);\n\n for (var typeSpecName in typeSpecs) {\n if (has(typeSpecs, typeSpecName)) {\n var error$1 = void 0; // Prop type validation may throw. In case they do, we don't want to\n // fail the render phase where it didn't fail before. So we log it.\n // After these have been cleaned up, we'll let them throw.\n\n try {\n // This is intentionally an invariant that gets caught. It's the same\n // behavior as without this statement except with a better message.\n if (typeof typeSpecs[typeSpecName] !== 'function') {\n // eslint-disable-next-line react-internal/prod-error-codes\n var err = Error((componentName || 'React class') + ': ' + location + ' type `' + typeSpecName + '` is invalid; ' + 'it must be a function, usually from the `prop-types` package, but received `' + typeof typeSpecs[typeSpecName] + '`.' + 'This often happens because of typos such as `PropTypes.function` instead of `PropTypes.func`.');\n err.name = 'Invariant Violation';\n throw err;\n }\n\n error$1 = typeSpecs[typeSpecName](values, typeSpecName, componentName, location, null, 'SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED');\n } catch (ex) {\n error$1 = ex;\n }\n\n if (error$1 && !(error$1 instanceof Error)) {\n setCurrentlyValidatingElement(element);\n\n error('%s: type specification of %s' + ' `%s` is invalid; the type checker ' + 'function must return `null` or an `Error` but returned a %s. ' + 'You may have forgotten to pass an argument to the type checker ' + 'creator (arrayOf, instanceOf, objectOf, oneOf, oneOfType, and ' + 'shape all require an argument).', componentName || 'React class', location, typeSpecName, typeof error$1);\n\n setCurrentlyValidatingElement(null);\n }\n\n if (error$1 instanceof Error && !(error$1.message in loggedTypeFailures)) {\n // Only monitor this failure once because there tends to be a lot of the\n // same error.\n loggedTypeFailures[error$1.message] = true;\n setCurrentlyValidatingElement(element);\n\n error('Failed %s type: %s', location, error$1.message);\n\n setCurrentlyValidatingElement(null);\n }\n }\n }\n }\n}\n\nvar isArrayImpl = Array.isArray; // eslint-disable-next-line no-redeclare\n\nfunction isArray(a) {\n return isArrayImpl(a);\n}\n\n/*\n * The `'' + value` pattern (used in in perf-sensitive code) throws for Symbol\n * and Temporal.* types. See https://github.com/facebook/react/pull/22064.\n *\n * The functions in this module will throw an easier-to-understand,\n * easier-to-debug exception with a clear errors message message explaining the\n * problem. (Instead of a confusing exception thrown inside the implementation\n * of the `value` object).\n */\n// $FlowFixMe only called in DEV, so void return is not possible.\nfunction typeName(value) {\n {\n // toStringTag is needed for namespaced types like Temporal.Instant\n var hasToStringTag = typeof Symbol === 'function' && Symbol.toStringTag;\n var type = hasToStringTag && value[Symbol.toStringTag] || value.constructor.name || 'Object';\n return type;\n }\n} // $FlowFixMe only called in DEV, so void return is not possible.\n\n\nfunction willCoercionThrow(value) {\n {\n try {\n testStringCoercion(value);\n return false;\n } catch (e) {\n return true;\n }\n }\n}\n\nfunction testStringCoercion(value) {\n // If you ended up here by following an exception call stack, here's what's\n // happened: you supplied an object or symbol value to React (as a prop, key,\n // DOM attribute, CSS property, string ref, etc.) and when React tried to\n // coerce it to a string using `'' + value`, an exception was thrown.\n //\n // The most common types that will cause this exception are `Symbol` instances\n // and Temporal objects like `Temporal.Instant`. But any object that has a\n // `valueOf` or `[Symbol.toPrimitive]` method that throws will also cause this\n // exception. (Library authors do this to prevent users from using built-in\n // numeric operators like `+` or comparison operators like `>=` because custom\n // methods are needed to perform accurate arithmetic or comparison.)\n //\n // To fix the problem, coerce this object or symbol value to a string before\n // passing it to React. The most reliable way is usually `String(value)`.\n //\n // To find which value is throwing, check the browser or debugger console.\n // Before this exception was thrown, there should be `console.error` output\n // that shows the type (Symbol, Temporal.PlainDate, etc.) that caused the\n // problem and how that type was used: key, atrribute, input value prop, etc.\n // In most cases, this console output also shows the component and its\n // ancestor components where the exception happened.\n //\n // eslint-disable-next-line react-internal/safe-string-coercion\n return '' + value;\n}\nfunction checkKeyStringCoercion(value) {\n {\n if (willCoercionThrow(value)) {\n error('The provided key is an unsupported type %s.' + ' This value must be coerced to a string before before using it here.', typeName(value));\n\n return testStringCoercion(value); // throw (to help callers find troubleshooting comments)\n }\n }\n}\n\nvar ReactCurrentOwner = ReactSharedInternals.ReactCurrentOwner;\nvar RESERVED_PROPS = {\n key: true,\n ref: true,\n __self: true,\n __source: true\n};\nvar specialPropKeyWarningShown;\nvar specialPropRefWarningShown;\nvar didWarnAboutStringRefs;\n\n{\n didWarnAboutStringRefs = {};\n}\n\nfunction hasValidRef(config) {\n {\n if (hasOwnProperty.call(config, 'ref')) {\n var getter = Object.getOwnPropertyDescriptor(config, 'ref').get;\n\n if (getter && getter.isReactWarning) {\n return false;\n }\n }\n }\n\n return config.ref !== undefined;\n}\n\nfunction hasValidKey(config) {\n {\n if (hasOwnProperty.call(config, 'key')) {\n var getter = Object.getOwnPropertyDescriptor(config, 'key').get;\n\n if (getter && getter.isReactWarning) {\n return false;\n }\n }\n }\n\n return config.key !== undefined;\n}\n\nfunction warnIfStringRefCannotBeAutoConverted(config, self) {\n {\n if (typeof config.ref === 'string' && ReactCurrentOwner.current && self && ReactCurrentOwner.current.stateNode !== self) {\n var componentName = getComponentNameFromType(ReactCurrentOwner.current.type);\n\n if (!didWarnAboutStringRefs[componentName]) {\n 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);\n\n didWarnAboutStringRefs[componentName] = true;\n }\n }\n }\n}\n\nfunction defineKeyPropWarningGetter(props, displayName) {\n {\n var warnAboutAccessingKey = function () {\n if (!specialPropKeyWarningShown) {\n specialPropKeyWarningShown = true;\n\n error('%s: `key` is not a prop. Trying to access it will result ' + 'in `undefined` being returned. If you need to access the same ' + 'value within the child component, you should pass it as a different ' + 'prop. (https://reactjs.org/link/special-props)', displayName);\n }\n };\n\n warnAboutAccessingKey.isReactWarning = true;\n Object.defineProperty(props, 'key', {\n get: warnAboutAccessingKey,\n configurable: true\n });\n }\n}\n\nfunction defineRefPropWarningGetter(props, displayName) {\n {\n var warnAboutAccessingRef = function () {\n if (!specialPropRefWarningShown) {\n specialPropRefWarningShown = true;\n\n error('%s: `ref` is not a prop. Trying to access it will result ' + 'in `undefined` being returned. If you need to access the same ' + 'value within the child component, you should pass it as a different ' + 'prop. (https://reactjs.org/link/special-props)', displayName);\n }\n };\n\n warnAboutAccessingRef.isReactWarning = true;\n Object.defineProperty(props, 'ref', {\n get: warnAboutAccessingRef,\n configurable: true\n });\n }\n}\n/**\n * Factory method to create a new React element. This no longer adheres to\n * the class pattern, so do not use new to call it. Also, instanceof check\n * will not work. Instead test $$typeof field against Symbol.for('react.element') to check\n * if something is a React Element.\n *\n * @param {*} type\n * @param {*} props\n * @param {*} key\n * @param {string|object} ref\n * @param {*} owner\n * @param {*} self A *temporary* helper to detect places where `this` is\n * different from the `owner` when React.createElement is called, so that we\n * can warn. We want to get rid of owner and replace string `ref`s with arrow\n * functions, and as long as `this` and owner are the same, there will be no\n * change in behavior.\n * @param {*} source An annotation object (added by a transpiler or otherwise)\n * indicating filename, line number, and/or other information.\n * @internal\n */\n\n\nvar ReactElement = function (type, key, ref, self, source, owner, props) {\n var element = {\n // This tag allows us to uniquely identify this as a React Element\n $$typeof: REACT_ELEMENT_TYPE,\n // Built-in properties that belong on the element\n type: type,\n key: key,\n ref: ref,\n props: props,\n // Record the component responsible for creating this element.\n _owner: owner\n };\n\n {\n // The validation flag is currently mutative. We put it on\n // an external backing store so that we can freeze the whole object.\n // This can be replaced with a WeakMap once they are implemented in\n // commonly used development environments.\n element._store = {}; // To make comparing ReactElements easier for testing purposes, we make\n // the validation flag non-enumerable (where possible, which should\n // include every environment we run tests in), so the test framework\n // ignores it.\n\n Object.defineProperty(element._store, 'validated', {\n configurable: false,\n enumerable: false,\n writable: true,\n value: false\n }); // self and source are DEV only properties.\n\n Object.defineProperty(element, '_self', {\n configurable: false,\n enumerable: false,\n writable: false,\n value: self\n }); // Two elements created in two different places should be considered\n // equal for testing purposes and therefore we hide it from enumeration.\n\n Object.defineProperty(element, '_source', {\n configurable: false,\n enumerable: false,\n writable: false,\n value: source\n });\n\n if (Object.freeze) {\n Object.freeze(element.props);\n Object.freeze(element);\n }\n }\n\n return element;\n};\n/**\n * https://github.com/reactjs/rfcs/pull/107\n * @param {*} type\n * @param {object} props\n * @param {string} key\n */\n\nfunction jsxDEV(type, config, maybeKey, source, self) {\n {\n var propName; // Reserved names are extracted\n\n var props = {};\n var key = null;\n var ref = null; // Currently, key can be spread in as a prop. This causes a potential\n // issue if key is also explicitly declared (ie. <div {...props} key=\"Hi\" />\n // or <div key=\"Hi\" {...props} /> ). We want to deprecate key spread,\n // but as an intermediary step, we will use jsxDEV for everything except\n // <div {...props} key=\"Hi\" />, because we aren't currently able to tell if\n // key is explicitly declared to be undefined or not.\n\n if (maybeKey !== undefined) {\n {\n checkKeyStringCoercion(maybeKey);\n }\n\n key = '' + maybeKey;\n }\n\n if (hasValidKey(config)) {\n {\n checkKeyStringCoercion(config.key);\n }\n\n key = '' + config.key;\n }\n\n if (hasValidRef(config)) {\n ref = config.ref;\n warnIfStringRefCannotBeAutoConverted(config, self);\n } // Remaining properties are added to a new props object\n\n\n for (propName in config) {\n if (hasOwnProperty.call(config, propName) && !RESERVED_PROPS.hasOwnProperty(propName)) {\n props[propName] = config[propName];\n }\n } // Resolve default props\n\n\n if (type && type.defaultProps) {\n var defaultProps = type.defaultProps;\n\n for (propName in defaultProps) {\n if (props[propName] === undefined) {\n props[propName] = defaultProps[propName];\n }\n }\n }\n\n if (key || ref) {\n var displayName = typeof type === 'function' ? type.displayName || type.name || 'Unknown' : type;\n\n if (key) {\n defineKeyPropWarningGetter(props, displayName);\n }\n\n if (ref) {\n defineRefPropWarningGetter(props, displayName);\n }\n }\n\n return ReactElement(type, key, ref, self, source, ReactCurrentOwner.current, props);\n }\n}\n\nvar ReactCurrentOwner$1 = ReactSharedInternals.ReactCurrentOwner;\nvar ReactDebugCurrentFrame$1 = ReactSharedInternals.ReactDebugCurrentFrame;\n\nfunction setCurrentlyValidatingElement$1(element) {\n {\n if (element) {\n var owner = element._owner;\n var stack = describeUnknownElementTypeFrameInDEV(element.type, element._source, owner ? owner.type : null);\n ReactDebugCurrentFrame$1.setExtraStackFrame(stack);\n } else {\n ReactDebugCurrentFrame$1.setExtraStackFrame(null);\n }\n }\n}\n\nvar propTypesMisspellWarningShown;\n\n{\n propTypesMisspellWarningShown = false;\n}\n/**\n * Verifies the object is a ReactElement.\n * See https://reactjs.org/docs/react-api.html#isvalidelement\n * @param {?object} object\n * @return {boolean} True if `object` is a ReactElement.\n * @final\n */\n\n\nfunction isValidElement(object) {\n {\n return typeof object === 'object' && object !== null && object.$$typeof === REACT_ELEMENT_TYPE;\n }\n}\n\nfunction getDeclarationErrorAddendum() {\n {\n if (ReactCurrentOwner$1.current) {\n var name = getComponentNameFromType(ReactCurrentOwner$1.current.type);\n\n if (name) {\n return '\\n\\nCheck the render method of `' + name + '`.';\n }\n }\n\n return '';\n }\n}\n\nfunction getSourceInfoErrorAddendum(source) {\n {\n if (source !== undefined) {\n var fileName = source.fileName.replace(/^.*[\\\\\\/]/, '');\n var lineNumber = source.lineNumber;\n return '\\n\\nCheck your code at ' + fileName + ':' + lineNumber + '.';\n }\n\n return '';\n }\n}\n/**\n * Warn if there's no key explicitly set on dynamic arrays of children or\n * object keys are not valid. This allows us to keep track of children between\n * updates.\n */\n\n\nvar ownerHasKeyUseWarning = {};\n\nfunction getCurrentComponentErrorInfo(parentType) {\n {\n var info = getDeclarationErrorAddendum();\n\n if (!info) {\n var parentName = typeof parentType === 'string' ? parentType : parentType.displayName || parentType.name;\n\n if (parentName) {\n info = \"\\n\\nCheck the top-level render call using <\" + parentName + \">.\";\n }\n }\n\n return info;\n }\n}\n/**\n * Warn if the element doesn't have an explicit key assigned to it.\n * This element is in an array. The array could grow and shrink or be\n * reordered. All children that haven't already been validated are required to\n * have a \"key\" property assigned to it. Error statuses are cached so a warning\n * will only be shown once.\n *\n * @internal\n * @param {ReactElement} element Element that requires a key.\n * @param {*} parentType element's parent's type.\n */\n\n\nfunction validateExplicitKey(element, parentType) {\n {\n if (!element._store || element._store.validated || element.key != null) {\n return;\n }\n\n element._store.validated = true;\n var currentComponentErrorInfo = getCurrentComponentErrorInfo(parentType);\n\n if (ownerHasKeyUseWarning[currentComponentErrorInfo]) {\n return;\n }\n\n ownerHasKeyUseWarning[currentComponentErrorInfo] = true; // Usually the current owner is the offender, but if it accepts children as a\n // property, it may be the creator of the child that's responsible for\n // assigning it a key.\n\n var childOwner = '';\n\n if (element && element._owner && element._owner !== ReactCurrentOwner$1.current) {\n // Give the component that originally created this child.\n childOwner = \" It was passed a child from \" + getComponentNameFromType(element._owner.type) + \".\";\n }\n\n setCurrentlyValidatingElement$1(element);\n\n error('Each child in a list should have a unique \"key\" prop.' + '%s%s See https://reactjs.org/link/warning-keys for more information.', currentComponentErrorInfo, childOwner);\n\n setCurrentlyValidatingElement$1(null);\n }\n}\n/**\n * Ensure that every element either is passed in a static location, in an\n * array with an explicit keys property defined, or in an object literal\n * with valid key property.\n *\n * @internal\n * @param {ReactNode} node Statically passed child of any type.\n * @param {*} parentType node's parent's type.\n */\n\n\nfunction validateChildKeys(node, parentType) {\n {\n if (typeof node !== 'object') {\n return;\n }\n\n if (isArray(node)) {\n for (var i = 0; i < node.length; i++) {\n var child = node[i];\n\n if (isValidElement(child)) {\n validateExplicitKey(child, parentType);\n }\n }\n } else if (isValidElement(node)) {\n // This element was passed in a valid location.\n if (node._store) {\n node._store.validated = true;\n }\n } else if (node) {\n var iteratorFn = getIteratorFn(node);\n\n if (typeof iteratorFn === 'function') {\n // Entry iterators used to provide implicit keys,\n // but now we print a separate warning for them later.\n if (iteratorFn !== node.entries) {\n var iterator = iteratorFn.call(node);\n var step;\n\n while (!(step = iterator.next()).done) {\n if (isValidElement(step.value)) {\n validateExplicitKey(step.value, parentType);\n }\n }\n }\n }\n }\n }\n}\n/**\n * Given an element, validate that its props follow the propTypes definition,\n * provided by the type.\n *\n * @param {ReactElement} element\n */\n\n\nfunction validatePropTypes(element) {\n {\n var type = element.type;\n\n if (type === null || type === undefined || typeof type === 'string') {\n return;\n }\n\n var propTypes;\n\n if (typeof type === 'function') {\n propTypes = type.propTypes;\n } else if (typeof type === 'object' && (type.$$typeof === REACT_FORWARD_REF_TYPE || // Note: Memo only checks outer props here.\n // Inner props are checked in the reconciler.\n type.$$typeof === REACT_MEMO_TYPE)) {\n propTypes = type.propTypes;\n } else {\n return;\n }\n\n if (propTypes) {\n // Intentionally inside to avoid triggering lazy initializers:\n var name = getComponentNameFromType(type);\n checkPropTypes(propTypes, element.props, 'prop', name, element);\n } else if (type.PropTypes !== undefined && !propTypesMisspellWarningShown) {\n propTypesMisspellWarningShown = true; // Intentionally inside to avoid triggering lazy initializers:\n\n var _name = getComponentNameFromType(type);\n\n error('Component %s declared `PropTypes` instead of `propTypes`. Did you misspell the property assignment?', _name || 'Unknown');\n }\n\n if (typeof type.getDefaultProps === 'function' && !type.getDefaultProps.isReactClassApproved) {\n error('getDefaultProps is only used on classic React.createClass ' + 'definitions. Use a static property named `defaultProps` instead.');\n }\n }\n}\n/**\n * Given a fragment, validate that it can only be provided with fragment props\n * @param {ReactElement} fragment\n */\n\n\nfunction validateFragmentProps(fragment) {\n {\n var keys = Object.keys(fragment.props);\n\n for (var i = 0; i < keys.length; i++) {\n var key = keys[i];\n\n if (key !== 'children' && key !== 'key') {\n setCurrentlyValidatingElement$1(fragment);\n\n error('Invalid prop `%s` supplied to `React.Fragment`. ' + 'React.Fragment can only have `key` and `children` props.', key);\n\n setCurrentlyValidatingElement$1(null);\n break;\n }\n }\n\n if (fragment.ref !== null) {\n setCurrentlyValidatingElement$1(fragment);\n\n error('Invalid attribute `ref` supplied to `React.Fragment`.');\n\n setCurrentlyValidatingElement$1(null);\n }\n }\n}\n\nvar didWarnAboutKeySpread = {};\nfunction jsxWithValidation(type, props, key, isStaticChildren, source, self) {\n {\n var validType = isValidElementType(type); // We warn in this case but don't throw. We expect the element creation to\n // succeed and there will likely be errors in render.\n\n if (!validType) {\n var info = '';\n\n if (type === undefined || typeof type === 'object' && type !== null && Object.keys(type).length === 0) {\n 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.\";\n }\n\n var sourceInfo = getSourceInfoErrorAddendum(source);\n\n if (sourceInfo) {\n info += sourceInfo;\n } else {\n info += getDeclarationErrorAddendum();\n }\n\n var typeString;\n\n if (type === null) {\n typeString = 'null';\n } else if (isArray(type)) {\n typeString = 'array';\n } else if (type !== undefined && type.$$typeof === REACT_ELEMENT_TYPE) {\n typeString = \"<\" + (getComponentNameFromType(type.type) || 'Unknown') + \" />\";\n info = ' Did you accidentally export a JSX literal instead of a component?';\n } else {\n typeString = typeof type;\n }\n\n error('React.jsx: type is invalid -- expected a string (for ' + 'built-in components) or a class/function (for composite ' + 'components) but got: %s.%s', typeString, info);\n }\n\n var element = jsxDEV(type, props, key, source, self); // The result can be nullish if a mock or a custom function is used.\n // TODO: Drop this when these are no longer allowed as the type argument.\n\n if (element == null) {\n return element;\n } // Skip key warning if the type isn't valid since our key validation logic\n // doesn't expect a non-string/function type and can throw confusing errors.\n // We don't want exception behavior to differ between dev and prod.\n // (Rendering will throw with a helpful message and as soon as the type is\n // fixed, the key warnings will appear.)\n\n\n if (validType) {\n var children = props.children;\n\n if (children !== undefined) {\n if (isStaticChildren) {\n if (isArray(children)) {\n for (var i = 0; i < children.length; i++) {\n validateChildKeys(children[i], type);\n }\n\n if (Object.freeze) {\n Object.freeze(children);\n }\n } else {\n error('React.jsx: Static children should always be an array. ' + 'You are likely explicitly calling React.jsxs or React.jsxDEV. ' + 'Use the Babel transform instead.');\n }\n } else {\n validateChildKeys(children, type);\n }\n }\n }\n\n {\n if (hasOwnProperty.call(props, 'key')) {\n var componentName = getComponentNameFromType(type);\n var keys = Object.keys(props).filter(function (k) {\n return k !== 'key';\n });\n var beforeExample = keys.length > 0 ? '{key: someKey, ' + keys.join(': ..., ') + ': ...}' : '{key: someKey}';\n\n if (!didWarnAboutKeySpread[componentName + beforeExample]) {\n var afterExample = keys.length > 0 ? '{' + keys.join(': ..., ') + ': ...}' : '{}';\n\n error('A props object containing a \"key\" prop is being spread into JSX:\\n' + ' let props = %s;\\n' + ' <%s {...props} />\\n' + 'React keys must be passed directly to JSX without using spread:\\n' + ' let props = %s;\\n' + ' <%s key={someKey} {...props} />', beforeExample, componentName, afterExample, componentName);\n\n didWarnAboutKeySpread[componentName + beforeExample] = true;\n }\n }\n }\n\n if (type === REACT_FRAGMENT_TYPE) {\n validateFragmentProps(element);\n } else {\n validatePropTypes(element);\n }\n\n return element;\n }\n} // These two functions exist to still get child warnings in dev\n// even with the prod transform. This means that jsxDEV is purely\n// opt-in behavior for better messages but that we won't stop\n// giving you warnings if you use production apis.\n\nfunction jsxWithValidationStatic(type, props, key) {\n {\n return jsxWithValidation(type, props, key, true);\n }\n}\nfunction jsxWithValidationDynamic(type, props, key) {\n {\n return jsxWithValidation(type, props, key, false);\n }\n}\n\nvar jsx = jsxWithValidationDynamic ; // we may want to special case jsxs internally to take advantage of static children.\n// for now we can ship identical prod functions\n\nvar jsxs = jsxWithValidationStatic ;\n\nexports.Fragment = REACT_FRAGMENT_TYPE;\nexports.jsx = jsx;\nexports.jsxs = jsxs;\n })();\n}\n","'use strict';\n\nif (process.env.NODE_ENV === 'production') {\n module.exports = require('./cjs/react-jsx-runtime.production.min.js');\n} else {\n module.exports = require('./cjs/react-jsx-runtime.development.js');\n}\n","/**\n * @license React\n * react-jsx-runtime.production.min.js\n *\n * Copyright (c) Facebook, Inc. and its affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n'use strict';var f=require(\"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:!0,ref:!0,__self:!0,__source:!0};\nfunction q(c,a,g){var b,d={},e=null,h=null;void 0!==g&&(e=\"\"+g);void 0!==a.key&&(e=\"\"+a.key);void 0!==a.ref&&(h=a.ref);for(b in a)m.call(a,b)&&!p.hasOwnProperty(b)&&(d[b]=a[b]);if(c&&c.defaultProps)for(b in a=c.defaultProps,a)void 0===d[b]&&(d[b]=a[b]);return{$$typeof:k,type:c,key:e,ref:h,props:d,_owner:n.current}}exports.Fragment=l;exports.jsx=q;exports.jsxs=q;\n","import { useCallback, useRef, useState } from 'react';\n\n// Error class for camera permission denied\nexport class CameraPermissionDenied extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'CameraPermissionDenied';\n }\n}\n\n// Private MediaStream wrapper to prevent direct access to tracks\nclass CameraStream {\n private stream: MediaStream;\n\n constructor(stream: MediaStream) {\n this.stream = stream;\n }\n\n // Get the underlying MediaStream (for internal use only)\n getStream(): MediaStream {\n return this.stream;\n }\n\n // Stop all tracks\n stop(): void {\n this.stream.getTracks().forEach(track => track.stop());\n }\n}\n\n// Hook to manage camera access\nexport const useCamera = () => {\n // Store the camera stream reference\n const streamRef = useRef<CameraStream | null>(null);\n // Track if camera is active\n const [isActive, setIsActive] = useState<boolean>(false);\n // Flag to prevent duplicate initialization\n const isInitializingRef = useRef<boolean>(false);\n\n // Determine optimal camera constraints based on device\n const getOptimalConstraints = (): MediaStreamConstraints => {\n const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(\n navigator.userAgent\n );\n\n return {\n video: {\n facingMode: 'user',\n width: { ideal: isMobile ? 640 : 1280 },\n height: { ideal: isMobile ? 480 : 720 },\n aspectRatio: { ideal: 1.777777778 }, // 16:9\n },\n audio: false,\n };\n };\n\n // Start the camera\n // 🔥 CORREÇÃO CRÍTICA: Remover isActive das dependências para evitar loop infinito\n const startCamera = useCallback(\n async (constraints?: MediaStreamConstraints): Promise<MediaStream> => {\n console.log('🎬 [useCamera] startCamera chamado', {\n hasExistingStream: !!streamRef.current,\n isInitializing: isInitializingRef.current,\n });\n\n // 🔥 CORREÇÃO: Se já existe um stream ativo, retorná-lo ao invés de lançar erro\n const existingStream = streamRef.current;\n if (existingStream) {\n console.log('⚠️ [useCamera] Câmera já está ativa, retornando stream existente');\n return existingStream.getStream();\n }\n\n // 🔥 CORREÇÃO: Prevenir inicialização duplicada (React Strict Mode)\n if (isInitializingRef.current) {\n console.log('⏳ [useCamera] Inicialização já em andamento, aguardando...');\n // Aguardar um pouco e tentar novamente\n await new Promise(resolve => setTimeout(resolve, 100));\n const currentStream = streamRef.current;\n if (currentStream) {\n console.log('✅ [useCamera] Stream criado por outra chamada, usando-o');\n return currentStream.getStream();\n }\n }\n\n isInitializingRef.current = true;\n\n try {\n // Use provided constraints or get optimal ones\n const mediaConstraints = constraints || getOptimalConstraints();\n\n console.log('📹 [useCamera] Solicitando acesso à câmera...', mediaConstraints);\n\n // Request camera access\n const stream = await navigator.mediaDevices.getUserMedia(mediaConstraints);\n\n console.log('✅ [useCamera] Stream obtido com sucesso:', {\n tracks: stream.getTracks().length,\n videoTracks: stream.getVideoTracks().length,\n active: stream.active,\n });\n\n // Wrap the stream to prevent direct access to tracks\n const cameraStream = new CameraStream(stream);\n streamRef.current = cameraStream;\n setIsActive(true);\n isInitializingRef.current = false;\n\n console.log('✅ [useCamera] Câmera iniciada com sucesso');\n\n // Return the stream for the caller to use\n return stream;\n } catch (error) {\n isInitializingRef.current = false;\n\n console.error('❌ [useCamera] Erro ao acessar câmera:', error);\n\n // 🔥 CORREÇÃO: Tratamento mais específico de erros\n if (error instanceof Error) {\n console.log('📋 [useCamera] Detalhes do erro:', {\n name: error.name,\n message: error.message,\n stack: error.stack?.substring(0, 200),\n });\n\n // Handle permission denied error\n if (error.name === 'NotAllowedError' || error.name === 'PermissionDeniedError') {\n throw new CameraPermissionDenied(\n 'Permissão para acessar a câmera foi negada pelo usuário'\n );\n }\n\n // Handle device not found\n if (error.name === 'NotFoundError' || error.name === 'DevicesNotFoundError') {\n throw new Error('Nenhuma câmera foi encontrada no dispositivo');\n }\n\n // Handle device already in use\n if (error.name === 'NotReadableError' || error.name === 'TrackStartError') {\n throw new Error('A câmera já está sendo usada por outro aplicativo');\n }\n\n // Handle overconstrained\n if (\n error.name === 'OverconstrainedError' ||\n error.name === 'ConstraintNotSatisfiedError'\n ) {\n throw new Error('As configurações solicitadas não são suportadas pela câmera');\n }\n }\n\n // Re-throw other errors with more context\n throw new Error(\n `Erro ao inicializar câmera: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n },\n [] // 🔥 Array vazio - função é criada apenas uma vez\n );\n\n // Stop the camera\n const stopCamera = useCallback((stream: MediaStream): void => {\n console.log('🛑 [useCamera] Parando câmera...');\n\n // Find the corresponding CameraStream\n const cameraStream = streamRef.current;\n\n if (cameraStream && cameraStream.getStream() === stream) {\n // Stop all tracks\n cameraStream.stop();\n console.log('✅ [useCamera] Todas as tracks paradas');\n\n // Clear references\n streamRef.current = null;\n setIsActive(false);\n isInitializingRef.current = false;\n\n console.log('✅ [useCamera] Câmera desligada com sucesso');\n } else {\n console.warn(\n '⚠️ [useCamera] Tentativa de parar um stream que não foi iniciado por este hook'\n );\n }\n }, []);\n\n // Clean up on unmount\n const cleanup = useCallback(() => {\n console.log('🧹 [useCamera] Cleanup chamado');\n if (streamRef.current) {\n streamRef.current.stop();\n streamRef.current = null;\n setIsActive(false);\n isInitializingRef.current = false;\n console.log('✅ [useCamera] Cleanup concluído');\n }\n }, []);\n\n return {\n startCamera,\n stopCamera,\n cleanup,\n isActive,\n };\n};\n","/**\n * Tipos padronizados de erro do SDK NeoFace ID.\n * Utilize estes tipos para categorizar e tratar erros de forma consistente.\n */\nexport enum ErrorType {\n NETWORK = 'NetworkError',\n NETWORK_ERROR = 'NetworkError',\n INVALID_TOKEN = 'InvalidTokenError',\n RECOGNITION_FAILED = 'RecognitionFailedError',\n LOGIN_FAILED = 'LoginFailedError',\n VALIDATION_ERROR = 'ValidationError',\n API_ERROR = 'ApiError',\n PERSON_NOT_FOUND = 'PersonNotFoundError',\n INITIALIZATION_ERROR = 'InitializationError',\n CAMERA_ERROR = 'CameraError',\n NO_CAMERA = 'NoCameraError',\n CAPTURE_ERROR = 'CaptureError',\n NOT_FOUND = 'NotFoundError',\n UNKNOWN = 'UnknownError',\n}\n\n/**\n * Erro customizado do SDK que carrega um tipo semântico.\n * Permite aos consumidores diferenciar falhas por categoria com `error.type`.\n */\nexport class NeoFaceError extends Error {\n type: ErrorType;\n\n /**\n * Constrói um erro do SDK com mensagem e tipo categórico.\n * @param message Mensagem descritiva do erro\n * @param type Tipo categórico do erro\n */\n constructor(message: string, type: ErrorType) {\n super(message);\n this.name = type;\n this.type = type;\n }\n}\n","/**\n * SDK Global Configuration\n * \n * Este módulo gerencia a configuração global do SDK, permitindo\n * inicialização com detecção automática de ambiente ou override manual.\n */\n\nexport type Environment = 'development' | 'sandbox' | 'production';\n\n/**\n * Mapeamento de ambientes para URLs base da API\n */\nconst ENVIRONMENT_URLS: Record<Environment, string> = {\n development: 'http://localhost:8000',\n sandbox: 'https://sandbox-core.neofaceid.com',\n production: 'https://core.neofaceid.com.br',\n};\n\n/**\n * Opções de inicialização do SDK\n */\nexport interface SDKInitOptions {\n /**\n * Ambiente da aplicação. Se fornecido, a URL base será determinada automaticamente.\n * Pode ser 'development', 'sandbox' ou 'production'.\n */\n environment?: Environment;\n \n /**\n * URL base da API. Se fornecido, tem precedência sobre 'environment'.\n * Útil para ambientes customizados ou testes.\n */\n baseUrl?: string;\n \n /**\n * Token de aplicação para autenticação com a API.\n * Obtido através do painel administrativo do NeoFaceID.\n */\n applicationToken?: string;\n}\n\n/**\n * Configuração interna do SDK\n */\ninterface SDKConfig {\n baseUrl: string;\n applicationToken: string | null;\n environment: Environment | 'custom';\n initialized: boolean;\n}\n\n/**\n * Configuração global do SDK (singleton)\n */\nlet globalConfig: SDKConfig = {\n baseUrl: ENVIRONMENT_URLS.sandbox, // Default para sandbox\n applicationToken: null,\n environment: 'sandbox',\n initialized: false,\n};\n\n/**\n * Inicializa o SDK com as configurações fornecidas.\n * Deve ser chamado uma vez no início da aplicação, antes de usar qualquer função do SDK.\n * \n * @example\n * // Usando ambiente predefinido\n * NeoFaceSDK.init({ environment: 'production' });\n * \n * // Usando URL customizada\n * NeoFaceSDK.init({ baseUrl: 'https://my-custom-api.com' });\n * \n * // Com token de aplicação\n * NeoFaceSDK.init({ \n * environment: 'sandbox',\n * applicationToken: 'your-app-token'\n * });\n * \n * @param options Opções de inicialização\n */\nexport function init(options: SDKInitOptions = {}): void {\n const { environment, baseUrl, applicationToken } = options;\n \n // Determinar a URL base\n if (baseUrl) {\n // URL explícita tem precedência\n globalConfig.baseUrl = baseUrl;\n globalConfig.environment = 'custom';\n } else if (environment) {\n // Usar ambiente predefinido\n if (!ENVIRONMENT_URLS[environment]) {\n console.warn(`[NeoFaceID SDK] Ambiente '${environment}' não reconhecido. Usando 'sandbox'.`);\n globalConfig.baseUrl = ENVIRONMENT_URLS.sandbox;\n globalConfig.environment = 'sandbox';\n } else {\n globalConfig.baseUrl = ENVIRONMENT_URLS[environment];\n globalConfig.environment = environment;\n }\n } else {\n // Fallback: usar variável de ambiente ou sandbox\n const envUrl = (typeof import.meta !== 'undefined' && (import.meta as any).env?.VITE_API_BASE_URL);\n if (envUrl) {\n globalConfig.baseUrl = envUrl;\n globalConfig.environment = 'custom';\n }\n // Caso contrário, mantém o default (sandbox)\n }\n \n // Configurar token de aplicação\n if (applicationToken) {\n globalConfig.applicationToken = applicationToken;\n }\n \n globalConfig.initialized = true;\n \n console.log(`[NeoFaceID SDK] Inicializado - Ambiente: ${globalConfig.environment}, URL: ${globalConfig.baseUrl}`);\n}\n\n/**\n * Retorna a URL base configurada para a API.\n * Se o SDK não foi inicializado, retorna o fallback para sandbox.\n */\nexport function getBaseUrl(): string {\n if (!globalConfig.initialized) {\n console.warn('[NeoFaceID SDK] SDK não foi inicializado. Usando configuração padrão (sandbox).');\n }\n return globalConfig.baseUrl;\n}\n\n/**\n * Retorna o token de aplicação configurado, se houver.\n */\nexport function getApplicationToken(): string | null {\n return globalConfig.applicationToken;\n}\n\n/**\n * Retorna o ambiente atual configurado.\n */\nexport function getEnvironment(): Environment | 'custom' {\n return globalConfig.environment;\n}\n\n/**\n * Verifica se o SDK foi inicializado.\n */\nexport function isInitialized(): boolean {\n return globalConfig.initialized;\n}\n\n/**\n * Retorna toda a configuração atual (para debug).\n */\nexport function getConfig(): Readonly<SDKConfig> {\n return { ...globalConfig };\n}\n\n/**\n * Reseta a configuração do SDK (útil para testes).\n * @internal\n */\nexport function _resetConfig(): void {\n globalConfig = {\n baseUrl: ENVIRONMENT_URLS.sandbox,\n applicationToken: null,\n environment: 'sandbox',\n initialized: false,\n };\n}\n\n/**\n * Exporta as URLs de ambiente para uso externo se necessário.\n */\nexport { ENVIRONMENT_URLS };\n","import { NeoFaceError, ErrorType } from './errors';\n\nexport interface NeoFaceApiConfig {\n apiKey: string;\n baseUrl: string;\n}\n\nexport interface BiometricLoginResult {\n success: boolean;\n accessToken: string;\n alias: string;\n user: {\n id: string;\n email: string;\n role: string;\n validated: boolean;\n active: boolean;\n };\n person?: {\n name: string;\n birth_date: string;\n };\n confidence_score?: number;\n recognition_id?: number;\n}\n\n/**\n * Application registration data\n */\nexport interface ApplicationRegistrationData {\n applicationName: string;\n domain: string;\n acceptOnlyEmailWithSameDomain: boolean;\n}\n\n/**\n * Application registration result\n */\nexport interface ApplicationRegistrationResult {\n success: boolean;\n application: {\n id: string;\n application_name: string;\n domain: string;\n accept_only_email_with_same_domain: boolean;\n app_token: string;\n is_active: boolean;\n created_at: string;\n };\n message: string;\n}\n\n// Tipagem para detalhes do link de onboarding\nexport interface OnboardingLinkDetails {\n id: string;\n token: string;\n event: string;\n custom_flow_id?: string | null;\n expire: boolean;\n expires_at?: string | null;\n notification: boolean;\n callback_url?: string | null;\n callback_email?: string | null;\n person_name: string;\n person_birth_date: string;\n person_cpf: string;\n person_email: string;\n person_phone?: string | null;\n status: string;\n created_at: string;\n updated_at: string;\n completed_at?: string | null;\n is_valid: boolean;\n is_expired: boolean;\n onboarding_url: string;\n consumer_name?: string;\n}\n\n// API constants\nimport { getBaseUrl } from './config';\n\n// Getter para a URL base da API - usa a configuração global do SDK\nconst getApiBaseUrl = (): string => getBaseUrl();\n\nconst REQUEST_TIMEOUT = 10000; // 10 seconds\n\n// Helper function to check secure context\n// Permite localhost para desenvolvimento\nconst ensureSecureContext = (): void => {\n const isLocalhost =\n window.location.hostname === 'localhost' ||\n window.location.hostname === '127.0.0.1' ||\n window.location.hostname === '';\n\n if (!window.isSecureContext && !isLocalhost) {\n throw new NeoFaceError(\n 'Secure context (HTTPS) is required for this operation',\n ErrorType.NETWORK\n );\n }\n};\n\n// Helper function to create timeout abort controller\nconst createTimeoutController = (): AbortController => {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), REQUEST_TIMEOUT);\n\n // Clean up timeout when controller is aborted\n controller.signal.addEventListener('abort', () => clearTimeout(timeoutId));\n\n return controller;\n};\n\n// Helper function to compress image (for faces - 70% quality is fine)\nconst compressImage = async (imageBlob: Blob): Promise<Blob> =>\n new Promise((resolve, reject) => {\n const img = new Image();\n img.onload = () => {\n const canvas = document.createElement('canvas');\n canvas.width = img.width;\n canvas.height = img.height;\n\n const ctx = canvas.getContext('2d');\n if (!ctx) {\n reject(new Error('Failed to get canvas context'));\n return;\n }\n\n ctx.drawImage(img, 0, 0);\n\n canvas.toBlob(\n blob => {\n if (blob) {\n resolve(blob);\n } else {\n reject(new Error('Failed to compress image'));\n }\n },\n 'image/jpeg',\n 0.7\n );\n };\n\n img.onerror = () => {\n reject(new Error('Failed to load image'));\n };\n\n img.src = URL.createObjectURL(imageBlob);\n });\n\n// Helper function to compress document images (75% quality + resize to avoid 413 errors)\nconst compressDocumentImage = async (imageBlob: Blob): Promise<Blob> =>\n new Promise((resolve, reject) => {\n const img = new Image();\n img.onload = () => {\n // Resize to max 1280px on longest side\n const MAX_SIZE = 1280;\n let width = img.width;\n let height = img.height;\n\n if (width > height && width > MAX_SIZE) {\n height = (height * MAX_SIZE) / width;\n width = MAX_SIZE;\n } else if (height > MAX_SIZE) {\n width = (width * MAX_SIZE) / height;\n height = MAX_SIZE;\n }\n\n const canvas = document.createElement('canvas');\n canvas.width = width;\n canvas.height = height;\n\n const ctx = canvas.getContext('2d');\n if (!ctx) {\n reject(new Error('Failed to get canvas context'));\n return;\n }\n\n ctx.drawImage(img, 0, 0, width, height);\n\n canvas.toBlob(\n blob => {\n if (blob) {\n resolve(blob);\n } else {\n reject(new Error('Failed to compress document image'));\n }\n },\n 'image/jpeg',\n 0.75 // 75% quality to keep file size manageable\n );\n };\n\n img.onerror = () => {\n reject(new Error('Failed to load document image'));\n };\n\n img.src = URL.createObjectURL(imageBlob);\n });\n\n/**\n * Validates an application token\n * @param applicationToken The application token to validate\n * @returns Promise that resolves to true if token is valid, false otherwise\n * @throws NeoFaceError if request fails\n */\nexport const validateToken = async (applicationToken: string): Promise<boolean> => {\n ensureSecureContext();\n\n const controller = createTimeoutController();\n\n try {\n const response = await fetch(`${getApiBaseUrl()}/api/v1/auth/application/validate_token/`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'X-App-Token': applicationToken,\n },\n body: JSON.stringify({}),\n signal: controller.signal,\n });\n\n return response.ok;\n } catch (error) {\n if (error instanceof Error) {\n if (error.name === 'AbortError') {\n throw new NeoFaceError('Request timed out', ErrorType.NETWORK_ERROR);\n }\n throw new NeoFaceError(error.message, ErrorType.NETWORK_ERROR);\n }\n throw new NeoFaceError('Unknown error occurred', ErrorType.NETWORK_ERROR);\n }\n};\n\n/**\n * Obtém detalhes de um link de onboarding por token\n * @param applicationToken Token da aplicação (header `X-App-Token`)\n * @param onboardingToken Token do link de onboarding\n * @returns Detalhes do link de onboarding\n * @throws NeoFaceError em caso de falha de rede ou autorização\n */\nexport const getOnboardingDetails = async (\n applicationToken: string,\n onboardingToken: string\n): Promise<OnboardingLinkDetails> => {\n ensureSecureContext();\n\n const controller = createTimeoutController();\n\n try {\n const response = await fetch(\n `${getApiBaseUrl()}/api/v1/onboardings/${encodeURIComponent(onboardingToken)}/`,\n {\n method: 'GET',\n headers: {\n 'X-App-Token': applicationToken,\n },\n signal: controller.signal,\n }\n );\n\n if (!response.ok) {\n if (response.status === 401 || response.status === 403) {\n throw new NeoFaceError('Token de aplicação inválido ou expirado', ErrorType.INVALID_TOKEN);\n }\n if (response.status === 404) {\n throw new NeoFaceError('Link de onboarding não encontrado', ErrorType.NOT_FOUND);\n }\n throw new NeoFaceError(`Falha ao obter detalhes (${response.status})`, ErrorType.NETWORK);\n }\n\n const data = (await response.json()) as OnboardingLinkDetails;\n return data;\n } catch (error) {\n if (error instanceof Error) {\n if (error.name === 'AbortError') {\n throw new NeoFaceError('Tempo de requisição excedido', ErrorType.NETWORK);\n }\n if (error instanceof NeoFaceError) {\n throw error;\n }\n throw new NeoFaceError(error.message, ErrorType.NETWORK);\n }\n throw new NeoFaceError('Erro desconhecido', ErrorType.NETWORK_ERROR);\n }\n};\n\n/**\n * Valida se um token de onboarding é válido\n * @param applicationToken Token da aplicação (header `X-App-Token`)\n * @param onboardingToken Token do link de onboarding a ser validado\n * @returns Promise que resolve para true se o token é válido, false caso contrário\n * @throws NeoFaceError em caso de falha de rede ou autorização\n */\nexport const validateOnboardingToken = async (\n applicationToken: string,\n onboardingToken: string\n): Promise<boolean> => {\n ensureSecureContext();\n\n const controller = createTimeoutController();\n\n try {\n const response = await fetch(\n `${getApiBaseUrl()}/api/v1/onboardings/${encodeURIComponent(onboardingToken)}/`,\n {\n method: 'GET',\n headers: {\n 'X-App-Token': applicationToken,\n },\n signal: controller.signal,\n }\n );\n\n if (!response.ok) {\n if (response.status === 401 || response.status === 403) {\n throw new NeoFaceError('Token de aplicação inválido ou expirado', ErrorType.INVALID_TOKEN);\n }\n if (response.status === 404) {\n // Token não encontrado = inválido\n return false;\n }\n throw new NeoFaceError(`Falha ao validar token (${response.status})`, ErrorType.NETWORK);\n }\n\n const data = (await response.json()) as OnboardingLinkDetails;\n\n // Retorna true apenas se o link existe e está válido\n return data?.is_valid === true;\n } catch (error) {\n if (error instanceof Error) {\n if (error.name === 'AbortError') {\n throw new NeoFaceError('Tempo de requisição excedido', ErrorType.NETWORK);\n }\n if (error instanceof NeoFaceError) {\n throw error;\n }\n throw new NeoFaceError(error.message, ErrorType.NETWORK);\n }\n throw new NeoFaceError('Erro desconhecido', ErrorType.NETWORK_ERROR);\n }\n};\n\n/**\n * Interface para dados da pessoa no onboarding\n */\nexport interface OnboardingPersonData {\n name?: string;\n birth_date?: string; // Formato: YYYY-MM-DD\n cpf?: string;\n cnpj?: string;\n email?: string;\n}\n\n/**\n * Conclui o processo de onboarding enviando imagens de face e documento\n * @param applicationToken Token da aplicação (header `X-App-Token`)\n * @param onboardingToken Token do link de onboarding\n * @param faceImage Blob da imagem de rosto\n * @param documentImage Blob da imagem do documento\n * @returns Resposta da conclusão do onboarding\n * @throws NeoFaceError em caso de falha de rede ou validação\n */\nexport const completeOnboarding = async (\n applicationToken: string,\n onboardingToken: string,\n faceImage: Blob,\n documentImage: Blob\n): Promise<{\n success: boolean;\n message: string;\n person_id?: string;\n identity_data_id?: string;\n confidence_score?: number;\n processing_time?: number;\n}> => {\n ensureSecureContext();\n\n const controller = createTimeoutController();\n\n // Comprime ambas imagens para otimizar upload\n const compressedFace = await compressImage(faceImage);\n const compressedDocument = await compressImage(documentImage);\n\n try {\n const formData = new FormData();\n formData.append('face_image', compressedFace, 'face.jpg');\n formData.append('document_image', compressedDocument, 'document.jpg');\n\n const response = await fetch(\n `${getApiBaseUrl()}/api/v1/onboardings/${encodeURIComponent(onboardingToken)}/complete`,\n {\n method: 'POST',\n headers: {\n 'X-App-Token': applicationToken,\n },\n body: formData,\n signal: controller.signal,\n }\n );\n\n const data = await response.json();\n\n if (!response.ok) {\n if (response.status === 401 || response.status === 403) {\n throw new NeoFaceError('Token de aplicação inválido ou expirado', ErrorType.INVALID_TOKEN);\n }\n if (response.status === 404) {\n throw new NeoFaceError('Link de onboarding não encontrado', ErrorType.NOT_FOUND);\n }\n // Retorna mensagem específica do backend se disponível\n throw new NeoFaceError(\n data?.error || data?.message || 'Falha ao concluir onboarding',\n ErrorType.NETWORK\n );\n }\n\n return {\n success: !!data.success,\n message: data.message || 'Onboarding concluído',\n person_id: data.person_id,\n identity_data_id: data.identity_data_id,\n confidence_score: data.confidence_score,\n processing_time: data.processing_time,\n };\n } catch (error) {\n if (error instanceof Error) {\n if (error.name === 'AbortError') {\n throw new NeoFaceError('Tempo de requisição excedido', ErrorType.NETWORK);\n }\n if (error instanceof NeoFaceError) {\n throw error;\n }\n throw new NeoFaceError(error.message, ErrorType.NETWORK);\n }\n throw new NeoFaceError('Erro desconhecido', ErrorType.NETWORK_ERROR);\n }\n};\n\n/**\n * Conclui o processo de onboarding com dados completos da pessoa\n *\n * RECEBE:\n * - Nome, DataDeNascimento, CPF, CNPJ, Email\n * - Imagens da Pessoa (array de Blobs)\n * - Imagem do documento\n *\n * AÇÃO (backend):\n * - Se não é cadastrado no NeoFaceId, cadastra-o\n * - Escaneia o documento e confere com os dados passados\n * - Confere se a imagem/vídeo passa nos critérios de prova de vida\n *\n * @param applicationToken Token da aplicação (header `X-App-Token`)\n * @param onboardingToken Token do link de onboarding\n * @param personData Dados da pessoa (Nome, DataDeNascimento, CPF, CNPJ, Email)\n * @param personImages Array de imagens/vídeos da pessoa para prova de vida (mínimo 1)\n * @param documentImage Blob da imagem do documento\n * @returns Resposta da conclusão do onboarding\n * @throws NeoFaceError em caso de falha de rede ou validação\n */\nexport const completeOnboardingWithData = async (\n applicationToken: string,\n onboardingToken: string,\n personData: OnboardingPersonData,\n personImages: Blob[],\n documentImage: Blob\n): Promise<{\n success: boolean;\n message: string;\n person_id?: string;\n identity_data_id?: string;\n confidence_score?: number;\n processing_time?: number;\n liveness_passed?: boolean;\n document_verified?: boolean;\n person_created?: boolean;\n}> => {\n ensureSecureContext();\n\n if (!personImages || personImages.length === 0) {\n throw new NeoFaceError(\n 'Pelo menos uma imagem da pessoa é obrigatória',\n ErrorType.VALIDATION_ERROR\n );\n }\n\n const controller = createTimeoutController();\n\n try {\n // Comprime todas as imagens\n const compressedPersonImages = await Promise.all(personImages.map(img => compressImage(img)));\n const compressedDocument = await compressImage(documentImage);\n\n // Cria FormData com todos os dados\n const formData = new FormData();\n\n // Adiciona imagens da pessoa (múltiplas para prova de vida)\n compressedPersonImages.forEach((img, index) => {\n formData.append('person_images', img, `person_${index + 1}.jpg`);\n });\n\n // Adiciona imagem do documento\n formData.append('document_image', compressedDocument, 'document.jpg');\n\n // Adiciona dados da pessoa (se fornecidos)\n if (personData.name) {\n formData.append('name', personData.name);\n }\n if (personData.birth_date) {\n formData.append('birth_date', personData.birth_date);\n }\n if (personData.cpf) {\n formData.append('cpf', personData.cpf);\n }\n if (personData.cnpj) {\n formData.append('cnpj', personData.cnpj);\n }\n if (personData.email) {\n formData.append('email', personData.email);\n }\n\n const response = await fetch(\n `${getApiBaseUrl()}/api/v1/onboardings/${encodeURIComponent(onboardingToken)}/complete`,\n {\n method: 'POST',\n headers: {\n 'X-App-Token': applicationToken,\n },\n body: formData,\n signal: controller.signal,\n }\n );\n\n const data = await response.json();\n\n if (!response.ok) {\n if (response.status === 401 || response.status === 403) {\n throw new NeoFaceError('Token de aplicação inválido ou expirado', ErrorType.INVALID_TOKEN);\n }\n if (response.status === 404) {\n throw new NeoFaceError('Link de onboarding não encontrado', ErrorType.NOT_FOUND);\n }\n // Retorna mensagem específica do backend se disponível\n throw new NeoFaceError(\n data?.error || data?.message || 'Falha ao concluir onboarding',\n ErrorType.NETWORK\n );\n }\n\n return {\n success: !!data.success,\n message: data.message || 'Onboarding concluído com sucesso',\n person_id: data.person_id,\n identity_data_id: data.identity_data_id,\n confidence_score: data.confidence_score,\n processing_time: data.processing_time,\n liveness_passed: data.liveness_passed,\n document_verified: data.document_verified,\n person_created: data.person_created,\n };\n } catch (error) {\n if (error instanceof Error) {\n if (error.name === 'AbortError') {\n throw new NeoFaceError('Tempo de requisição excedido', ErrorType.NETWORK);\n }\n if (error instanceof NeoFaceError) {\n throw error;\n }\n throw new NeoFaceError(error.message, ErrorType.NETWORK);\n }\n throw new NeoFaceError('Erro desconhecido', ErrorType.NETWORK_ERROR);\n }\n};\n\n/**\n * Recognizes a face from an image and returns user data\n * @param image The image blob to process\n * @param applicationToken The application token for authentication\n * @returns Promise with access token and user payload\n * @throws NeoFaceError if request fails\n * @throws NeoFaceError if recognition fails\n */\nexport const recognize = async (\n image: Blob,\n applicationToken: string\n): Promise<{\n accessToken: string;\n payload: { name: string; email: string; documentId: string };\n}> => {\n ensureSecureContext();\n\n const controller = createTimeoutController();\n const compressedImage = await compressImage(image);\n\n try {\n const formData = new FormData();\n formData.append('face_frames', compressedImage);\n\n const response = await fetch(`${getApiBaseUrl()}/api/v1/external/recognition/face/`, {\n method: 'POST',\n headers: {\n 'X-App-Token': applicationToken,\n },\n body: formData,\n signal: controller.signal,\n });\n\n if (!response.ok) {\n if (response.status === 401 || response.status === 403) {\n throw new NeoFaceError('Invalid or expired application token', ErrorType.INVALID_TOKEN);\n }\n throw new NeoFaceError(`Server returned status ${response.status}`, ErrorType.NETWORK);\n }\n\n const data = await response.json();\n\n if (!data.success) {\n throw new NeoFaceError(data.message || 'Recognition failed', ErrorType.RECOGNITION_FAILED);\n }\n\n return {\n accessToken: data.accessToken,\n payload: data.payload,\n };\n } catch (error) {\n if (error instanceof Error) {\n if (error.name === 'AbortError') {\n throw new NeoFaceError('Request timed out', ErrorType.NETWORK);\n }\n if (error instanceof NeoFaceError) {\n throw error;\n }\n throw new NeoFaceError(error.message, ErrorType.NETWORK);\n }\n throw new NeoFaceError('Unknown error occurred', ErrorType.NETWORK_ERROR);\n }\n};\n\n/**\n * Performs biometric login using face recognition\n * @param image The image blob to process\n * @param applicationToken The application token for authentication\n * @returns Promise with login result\n * @throws NeoFaceError if request fails\n */\nexport const loginWithBiometric = async (\n image: Blob,\n applicationToken: string\n): Promise<BiometricLoginResult> => {\n ensureSecureContext();\n\n const controller = createTimeoutController();\n const compressedImage = await compressImage(image);\n\n try {\n // Convert Blob to base64\n const arrayBuffer = await compressedImage.arrayBuffer();\n const base64Image = btoa(\n new Uint8Array(arrayBuffer).reduce((data, byte) => data + String.fromCharCode(byte), '')\n );\n\n const response = await fetch(`${getApiBaseUrl()}/api/v1/login/donor/face/`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'X-App-Token': applicationToken,\n },\n body: JSON.stringify({\n image: base64Image,\n biometric_type: 'FACE',\n }),\n signal: controller.signal,\n });\n\n if (!response.ok) {\n if (response.status === 401 || response.status === 403) {\n throw new NeoFaceError('Invalid or expired application token', ErrorType.INVALID_TOKEN);\n }\n throw new NeoFaceError(`Server returned status ${response.status}`, ErrorType.NETWORK);\n }\n\n const data = await response.json();\n\n if (!data.success) {\n throw new NeoFaceError(data.message || 'Login failed', ErrorType.LOGIN_FAILED);\n }\n\n return {\n success: true,\n accessToken: data.accessToken,\n alias: data.alias,\n user: {\n id: data.user?.id || '',\n email: data.user?.email || '',\n role: data.user?.role || '',\n validated: data.user?.validated || false,\n active: data.user?.active || false,\n },\n person: data.person\n ? {\n name: data.person.name || '',\n birth_date: data.person.birth_date || '',\n }\n : undefined,\n confidence_score: data.confidence_score,\n recognition_id: data.recognition_id,\n };\n } catch (error) {\n if (error instanceof Error) {\n if (error.name === 'AbortError') {\n throw new NeoFaceError('Request timed out', ErrorType.NETWORK);\n }\n if (error instanceof NeoFaceError) {\n throw error;\n }\n throw new NeoFaceError(error.message, ErrorType.NETWORK);\n }\n throw new NeoFaceError('Unknown error occurred', ErrorType.NETWORK_ERROR);\n }\n};\n\n/**\n * Performs biometric recognition with liveness check\n *\n * Note: person_id is NOT returned for security reasons.\n * Use recognizeByPurpose for external integrations that require signature validation.\n *\n * @param image The image blob to process\n * @param applicationToken The application token for authentication\n * @param livenessCheck Whether to perform liveness check (default: true)\n * @param confidenceThreshold Confidence threshold for recognition (default: 0.8)\n * @returns Promise with recognition result\n * @throws NeoFaceError if request fails\n */\nexport const recognizeBiometric = async (\n image: Blob,\n applicationToken: string,\n livenessCheck: boolean = true,\n confidenceThreshold: number = 0.8\n): Promise<{\n success: boolean;\n confidenceScore?: number;\n accessToken?: string;\n payload?: { name: string; email: string; documentId: string };\n // Note: personId is intentionally NOT returned for security reasons\n}> => {\n ensureSecureContext();\n\n const controller = createTimeoutController();\n const compressedImage = await compressImage(image);\n\n try {\n const formData = new FormData();\n formData.append('face_frames', compressedImage);\n formData.append('liveness_check', livenessCheck.toString());\n formData.append('confidence_threshold', confidenceThreshold.toString());\n\n const response = await fetch(`${getApiBaseUrl()}/api/v1/external/recognition/biometric/`, {\n method: 'POST',\n headers: {\n 'X-App-Token': applicationToken,\n },\n body: formData,\n signal: controller.signal,\n });\n\n if (!response.ok) {\n if (response.status === 401 || response.status === 403) {\n throw new NeoFaceError('Invalid or expired application token', ErrorType.INVALID_TOKEN);\n }\n throw new NeoFaceError(`Server returned status ${response.status}`, ErrorType.NETWORK);\n }\n\n const data = await response.json();\n\n return {\n success: data.success,\n // personId is intentionally NOT returned for security reasons\n confidenceScore: data.confidence_score,\n accessToken: data.access_token,\n payload: data.payload,\n };\n } catch (error) {\n if (error instanceof Error) {\n if (error.name === 'AbortError') {\n throw new NeoFaceError('Request timed out', ErrorType.NETWORK);\n }\n if (error instanceof NeoFaceError) {\n throw error;\n }\n throw new NeoFaceError(error.message, ErrorType.NETWORK);\n }\n throw new NeoFaceError('Unknown error occurred', ErrorType.NETWORK_ERROR);\n }\n};\n\n/**\n * Performs simple identification using document data\n * @param documentType Type of document (e.g., 'CPF')\n * @param documentNumber Document number\n * @param applicationToken The application token for authentication\n * @param purpose Purpose of identification (default: 'LOGIN')\n * @returns Promise with identification result\n * @throws NeoFaceError if request fails\n */\nexport const simpleIdentification = async (\n documentType: string,\n documentNumber: string,\n applicationToken: string,\n purpose: string = 'LOGIN'\n): Promise<{\n success: boolean;\n personId?: string;\n accessToken?: string;\n payload?: { name: string; email: string; documentId: string };\n}> => {\n ensureSecureContext();\n\n const controller = createTimeoutController();\n\n try {\n const response = await fetch(`${getApiBaseUrl()}/api/v1/external/recognition/simple/`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'X-App-Token': applicationToken,\n },\n body: JSON.stringify({\n document_type: documentType,\n document_number: documentNumber,\n purpose: purpose,\n }),\n signal: controller.signal,\n });\n\n if (!response.ok) {\n if (response.status === 401 || response.status === 403) {\n throw new NeoFaceError('Invalid or expired application token', ErrorType.INVALID_TOKEN);\n }\n throw new NeoFaceError(`Server returned status ${response.status}`, ErrorType.NETWORK);\n }\n\n const data = await response.json();\n\n if (!data.success) {\n throw new NeoFaceError(data.message || 'Identification failed', ErrorType.RECOGNITION_FAILED);\n }\n\n return {\n success: data.success,\n personId: data.person_id,\n accessToken: data.access_token,\n payload: data.payload,\n };\n } catch (error) {\n if (error instanceof Error) {\n if (error.name === 'AbortError') {\n throw new NeoFaceError('Request timed out', ErrorType.NETWORK);\n }\n if (error instanceof NeoFaceError) {\n throw error;\n }\n throw new NeoFaceError(error.message, ErrorType.NETWORK);\n }\n throw new NeoFaceError('Unknown error occurred', ErrorType.NETWORK_ERROR);\n }\n};\n\n/**\n * Session data interface for external integrations\n */\nexport interface SessionData {\n email: string;\n cpf: string;\n sessionId: string;\n}\n\n/**\n * Performs purpose-specific recognition\n * @param image The image blob to process\n * @param applicationToken The application token for authentication\n * @param purpose Purpose of recognition ('LOGIN', 'PROOF_OF_LIFE', 'AUTHORIZATION', etc.)\n * @param confidenceThreshold Confidence threshold for recognition (default: 0.8)\n * @param signature Optional signature for external integrations (HMAC-SHA256 hex string)\n * @param sessionData Optional session data for external integrations (email, cpf, sessionId)\n * @returns Promise with recognition result\n * @throws NeoFaceError if request fails\n */\nexport const recognizeByPurpose = async (\n image: Blob,\n applicationToken: string,\n purpose:\n | 'LOGIN'\n | 'PROOF_OF_LIFE'\n | 'AUTHORIZATION'\n | 'SIMPLE_IDENTIFICATION'\n | 'SIMPLIFIED_REGISTRATION',\n confidenceThreshold: number = 0.8,\n signature?: string,\n sessionData?: SessionData\n): Promise<{\n success: boolean;\n personName?: string;\n email?: string;\n cpf?: string;\n confidenceScore?: number;\n accessToken?: string;\n payload?: any;\n signature?: string;\n sessionId?: string;\n // Note: personId is NOT returned for security reasons in external integrations\n}> => {\n ensureSecureContext();\n\n const controller = createTimeoutController();\n const compressedImage = await compressImage(image);\n\n // Convert image to base64\n const base64Image = await new Promise<string>((resolve, reject) => {\n const reader = new FileReader();\n reader.onload = () => {\n const result = reader.result as string;\n resolve(result.split(',')[1]); // Remove data:image/... prefix\n };\n reader.onerror = reject;\n reader.readAsDataURL(compressedImage);\n });\n\n try {\n const requestBody: any = {\n biometric_data: base64Image,\n type_of_identification: 'FACE',\n purpose: purpose,\n confidence_threshold: confidenceThreshold,\n };\n\n // Include signature if provided (for external integrations)\n if (signature) {\n requestBody.signature = signature;\n }\n\n const response = await fetch(`${getApiBaseUrl()}/api/v1/external/recognition/purpose/`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'X-App-Token': applicationToken,\n },\n body: JSON.stringify(requestBody),\n signal: controller.signal,\n });\n\n if (!response.ok) {\n if (response.status === 401 || response.status === 403) {\n throw new NeoFaceError('Invalid or expired application token', ErrorType.INVALID_TOKEN);\n }\n throw new NeoFaceError(`Server returned status ${response.status}`, ErrorType.NETWORK);\n }\n\n const data = await response.json();\n\n return {\n success: data.success,\n personName: data.person_name || data.person?.name,\n email: data.email || sessionData?.email,\n cpf: data.cpf || sessionData?.cpf,\n confidenceScore: data.confidence_score,\n accessToken: data.access_token,\n payload: data.payload,\n // Return signature and sessionId for external integrations\n signature: signature,\n sessionId: sessionData?.sessionId,\n // Note: personId is intentionally NOT returned for security reasons\n };\n } catch (error) {\n if (error instanceof Error) {\n if (error.name === 'AbortError') {\n throw new NeoFaceError('Request timed out', ErrorType.NETWORK);\n }\n if (error instanceof NeoFaceError) {\n throw error;\n }\n throw new NeoFaceError(error.message, ErrorType.NETWORK);\n }\n throw new NeoFaceError('Unknown error occurred', ErrorType.NETWORK_ERROR);\n }\n};\n\n/**\n * Register a person without face biometric data\n * @param personData Person registration data\n * @param applicationToken The application token for authentication\n * @param options Optional document images\n * @returns Promise with registration result\n */\nexport const registerPersonWithoutFace = async (\n personData: {\n name: string;\n birth_date: string;\n cpf: string;\n email: string;\n password: string;\n },\n applicationToken: string,\n options?: {\n documentFront?: Blob;\n documentBack?: Blob;\n documentType?: string;\n }\n): Promise<{\n success: boolean;\n message: string;\n person_id?: string;\n user_id?: string;\n person_validated?: boolean;\n onboarding_link?: string;\n data?: any;\n}> => {\n ensureSecureContext();\n\n const controller = createTimeoutController();\n\n try {\n // Convert document photos to base64 if provided\n let documentFrontBase64: string | undefined;\n let documentBackBase64: string | undefined;\n\n if (options?.documentFront) {\n const compressed = await compressDocumentImage(options.documentFront);\n documentFrontBase64 = await new Promise<string>((resolve, reject) => {\n const reader = new FileReader();\n reader.onload = () => resolve(reader.result as string);\n reader.onerror = reject;\n reader.readAsDataURL(compressed);\n });\n }\n\n if (options?.documentBack) {\n const compressed = await compressDocumentImage(options.documentBack);\n documentBackBase64 = await new Promise<string>((resolve, reject) => {\n const reader = new FileReader();\n reader.onload = () => resolve(reader.result as string);\n reader.onerror = reject;\n reader.readAsDataURL(compressed);\n });\n }\n\n const body: any = {\n name: personData.name,\n birth_date: personData.birth_date,\n cpf: personData.cpf,\n email: personData.email,\n password: personData.password,\n password_confirm: personData.password,\n };\n\n // Add document_images in the format expected by backend: array of {front, back, type}\n if (documentFrontBase64) {\n body.document_images = [{\n front: documentFrontBase64,\n back: documentBackBase64 || undefined,\n type: options?.documentType || 'RG'\n }];\n }\n\n const response = await fetch(`${getApiBaseUrl()}/api/v1/signup/donor/`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'X-App-Token': applicationToken,\n },\n body: JSON.stringify(body),\n signal: controller.signal,\n });\n\n if (!response.ok) {\n if (response.status === 401 || response.status === 403) {\n throw new NeoFaceError('Invalid or expired application token', ErrorType.INVALID_TOKEN);\n }\n throw new NeoFaceError(`Server returned status ${response.status}`, ErrorType.NETWORK);\n }\n\n const data = await response.json();\n return data;\n } catch (error) {\n if (error instanceof Error) {\n if (error.name === 'AbortError') {\n throw new NeoFaceError('Request timed out', ErrorType.NETWORK);\n }\n if (error instanceof NeoFaceError) {\n throw error;\n }\n throw new NeoFaceError(error.message, ErrorType.NETWORK);\n }\n throw new NeoFaceError('Unknown error occurred', ErrorType.NETWORK);\n }\n};\n\n/**\n * Register a person with face biometric data and liveness detection\n * @param personData Person registration data\n * @param facePhotos Array of face photos for biometric registration (up to 5 photos)\n * @param applicationToken The application token for authentication\n * @param options Optional document images\n * @returns Promise with registration result\n */\nexport const registerPersonWithBiometric = async (\n personData: {\n name: string;\n birth_date: string;\n cpf: string;\n email: string;\n password: string;\n },\n facePhotos: Blob[],\n applicationToken: string,\n options?: {\n documentFront?: Blob;\n documentBack?: Blob;\n documentType?: string;\n }\n): Promise<{\n success: boolean;\n message: string;\n person_id?: string;\n user_id?: string;\n person_validated?: boolean;\n face_processing?: any;\n onboarding_link?: string;\n task_id?: string;\n status?: string;\n}> => {\n ensureSecureContext();\n\n if (!facePhotos || facePhotos.length === 0) {\n throw new NeoFaceError(\n 'At least one face photo is required for biometric registration',\n ErrorType.VALIDATION_ERROR\n );\n }\n\n if (facePhotos.length > 5) {\n throw new NeoFaceError('Maximum of 5 face photos allowed', ErrorType.VALIDATION_ERROR);\n }\n\n const controller = createTimeoutController();\n\n try {\n // Convert all face photos to base64\n const facePhotosBase64 = await Promise.all(\n facePhotos.map(async photo => {\n const compressedImage = await compressImage(photo);\n return new Promise<string>((resolve, reject) => {\n const reader = new FileReader();\n reader.onload = () => {\n const result = reader.result as string;\n resolve(result); // Keep the full data URL for backend processing\n };\n reader.onerror = reject;\n reader.readAsDataURL(compressedImage);\n });\n })\n );\n\n // Convert document photos to base64 if provided\n let documentFrontBase64: string | undefined;\n let documentBackBase64: string | undefined;\n\n if (options?.documentFront) {\n const compressed = await compressDocumentImage(options.documentFront);\n documentFrontBase64 = await new Promise<string>((resolve, reject) => {\n const reader = new FileReader();\n reader.onload = () => resolve(reader.result as string);\n reader.onerror = reject;\n reader.readAsDataURL(compressed);\n });\n }\n\n if (options?.documentBack) {\n const compressed = await compressDocumentImage(options.documentBack);\n documentBackBase64 = await new Promise<string>((resolve, reject) => {\n const reader = new FileReader();\n reader.onload = () => resolve(reader.result as string);\n reader.onerror = reject;\n reader.readAsDataURL(compressed);\n });\n }\n\n const body: any = {\n name: personData.name,\n birth_date: personData.birth_date,\n cpf: personData.cpf,\n email: personData.email,\n password: personData.password,\n password_confirm: personData.password,\n face_photos: facePhotosBase64,\n };\n\n // Add document_images in the format expected by backend: array of {front, back, type}\n if (documentFrontBase64) {\n body.document_images = [{\n front: documentFrontBase64,\n back: documentBackBase64 || undefined,\n type: options?.documentType || 'RG'\n }];\n }\n\n const response = await fetch(`${getApiBaseUrl()}/api/v1/signup/donor/`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'X-App-Token': applicationToken,\n },\n body: JSON.stringify(body),\n signal: controller.signal,\n });\n\n if (!response.ok) {\n if (response.status === 401 || response.status === 403) {\n throw new NeoFaceError('Invalid or expired application token', ErrorType.INVALID_TOKEN);\n }\n\n // Try to get error details from response\n try {\n const errorData = await response.json();\n\n // Handle validation errors with detailed messages\n if (errorData.errors) {\n const errorMessages = Object.entries(errorData.errors)\n .map(([field, messages]: [string, any]) => {\n const fieldName =\n field === 'password'\n ? 'senha'\n : field === 'password_confirm'\n ? 'confirmação de senha'\n : field;\n const messageList = Array.isArray(messages) ? messages.join(', ') : String(messages);\n return `${fieldName}: ${messageList}`;\n })\n .join('; ');\n throw new NeoFaceError(`Erro de validação: ${errorMessages}`, ErrorType.VALIDATION_ERROR);\n }\n\n if (errorData.error === 'fraud_detected') {\n throw new NeoFaceError(\n 'Fraud detected in biometric images. Registration blocked.',\n ErrorType.VALIDATION_ERROR\n );\n }\n if (errorData.error === 'user_exists' || errorData.error_code === 'ALREADY_EXISTS') {\n throw new NeoFaceError('User with this email already exists', ErrorType.VALIDATION_ERROR);\n }\n if (errorData.error === 'person_exists') {\n throw new NeoFaceError('Person with this CPF already exists', ErrorType.VALIDATION_ERROR);\n }\n\n // Use error message from backend if available\n const errorMessage =\n errorData.message || errorData.error || `Server returned status ${response.status}`;\n throw new NeoFaceError(errorMessage, ErrorType.VALIDATION_ERROR);\n } catch (parseError) {\n // If we can't parse the error, throw a generic error\n if (parseError instanceof NeoFaceError) {\n throw parseError;\n }\n throw new NeoFaceError(`Server returned status ${response.status}`, ErrorType.NETWORK);\n }\n }\n\n const data = await response.json();\n\n // Handle async processing (status 202) - returns task_id\n if (response.status === 202 && data.data?.task_id) {\n return {\n success: data.success,\n message:\n data.message ||\n 'Registration is being processed. You will receive an email when completed.',\n task_id: data.data.task_id,\n status: data.data.status || 'processing',\n person_id: undefined,\n user_id: undefined,\n person_validated: undefined,\n face_processing: undefined,\n onboarding_link: undefined,\n };\n }\n\n // Handle sync processing (status 201) - returns person_id and user_id\n return {\n success: data.success,\n message: data.message,\n person_id: data.data?.person_id || data.person_id,\n user_id: data.data?.user_id || data.user_id,\n person_validated: data.data?.person_validated || data.person_validated,\n face_processing: data.data?.face_processing || data.face_processing,\n onboarding_link: data.data?.onboarding_link || data.onboarding_link,\n task_id: data.data?.task_id,\n status: data.data?.status,\n };\n } catch (error) {\n if (error instanceof Error) {\n if (error.name === 'AbortError') {\n throw new NeoFaceError('Request timed out', ErrorType.NETWORK);\n }\n if (error instanceof NeoFaceError) {\n throw error;\n }\n throw new NeoFaceError(error.message, ErrorType.NETWORK);\n }\n throw new NeoFaceError('Unknown error occurred', ErrorType.NETWORK_ERROR);\n }\n};\n\n/**\n * Performs login using email and password\n * @param email User's email\n * @param password User's password\n * @param applicationToken The application token for authentication\n * @returns Promise with login result\n * @throws NeoFaceError if request fails\n */\nexport const loginWithEmail = async (\n email: string,\n password: string,\n applicationToken: string\n): Promise<BiometricLoginResult> => {\n ensureSecureContext();\n\n const controller = createTimeoutController();\n\n try {\n const response = await fetch(`${getApiBaseUrl()}/api/v1/auth/login/`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'X-App-Token': applicationToken,\n },\n body: JSON.stringify({ email, password }),\n signal: controller.signal,\n });\n\n if (!response.ok) {\n if (response.status === 401 || response.status === 403) {\n throw new NeoFaceError('Invalid credentials or application token', ErrorType.INVALID_TOKEN);\n }\n throw new NeoFaceError(`Server returned status ${response.status}`, ErrorType.NETWORK);\n }\n\n const data = await response.json();\n\n return {\n success: true,\n // Compatível com diferentes formatos de resposta do backend\n accessToken: data.access_token || data.access,\n alias: (data.user && data.user.alias) || data.alias,\n user: {\n id: (data.user && data.user.id) || '',\n email: (data.user && data.user.email) || '',\n role: (data.user && data.user.role) || '',\n validated: (data.user && data.user.validated) || false,\n active: (data.user && data.user.active) || false,\n },\n };\n } catch (error) {\n if (error instanceof Error) {\n if (error.name === 'AbortError') {\n throw new NeoFaceError('Request timed out', ErrorType.NETWORK);\n }\n if (error instanceof NeoFaceError) {\n throw error;\n }\n throw new NeoFaceError(error.message, ErrorType.NETWORK);\n }\n throw new NeoFaceError('Unknown error occurred', ErrorType.NETWORK_ERROR);\n }\n};\n\n/**\n * Identifies a person from an image and returns name and age\n * @param image The image blob to process\n * @param applicationToken The application token for authentication\n * @returns Promise with person name and age\n * @throws NeoFaceError if request fails\n * @throws NeoFaceError if identification fails\n */\nexport const identifyPerson = async (\n image: Blob,\n applicationToken: string\n): Promise<{\n name: string;\n age: number;\n}> => {\n ensureSecureContext();\n\n const controller = createTimeoutController();\n const compressedImage = await compressImage(image);\n\n // Convert image to base64\n const base64Image = await new Promise<string>((resolve, reject) => {\n const reader = new FileReader();\n reader.onload = () => {\n const result = reader.result as string;\n resolve(result.split(',')[1]); // Remove data:image/... prefix\n };\n reader.onerror = reject;\n reader.readAsDataURL(compressedImage);\n });\n\n try {\n const response = await fetch(`${getApiBaseUrl()}/api/v1/external/recognition/face/`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'X-App-Token': applicationToken,\n },\n body: JSON.stringify({\n image: base64Image,\n purpose: 'SIMPLE_IDENTIFICATION_F',\n }),\n signal: controller.signal,\n });\n\n if (!response.ok) {\n if (response.status === 401 || response.status === 403) {\n throw new NeoFaceError('Invalid or expired application token', ErrorType.INVALID_TOKEN);\n }\n throw new NeoFaceError(`Server returned status ${response.status}`, ErrorType.NETWORK);\n }\n\n const data = await response.json();\n\n if (!data.success) {\n throw new NeoFaceError(\n data.message || 'Person identification failed',\n ErrorType.RECOGNITION_FAILED\n );\n }\n\n // Extract name and birth_date from response\n const personName = data.person?.name || '';\n const birthDateStr = data.person?.birth_date;\n\n if (!personName) {\n throw new NeoFaceError('Person name not found in response', ErrorType.PERSON_NOT_FOUND);\n }\n\n if (!birthDateStr) {\n throw new NeoFaceError('Birth date not found in response', ErrorType.PERSON_NOT_FOUND);\n }\n\n // Calculate age from birth_date\n const birthDate = new Date(birthDateStr);\n const today = new Date();\n let age = today.getFullYear() - birthDate.getFullYear();\n const monthDiff = today.getMonth() - birthDate.getMonth();\n\n // Adjust age if birthday hasn't occurred this year\n if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birthDate.getDate())) {\n age--;\n }\n\n return {\n name: personName,\n age: age,\n };\n } catch (error) {\n if (error instanceof Error) {\n if (error.name === 'AbortError') {\n throw new NeoFaceError('Request timed out', ErrorType.NETWORK);\n }\n if (error instanceof NeoFaceError) {\n throw error;\n }\n throw new NeoFaceError(error.message, ErrorType.NETWORK);\n }\n throw new NeoFaceError('Unknown error occurred', ErrorType.NETWORK_ERROR);\n }\n};\n\n/**\n * NEO-101: Register biometric data for an existing person\n *\n * This method registers facial biometric data for a person already in the system.\n * It validates the image and sends it to the backend.\n *\n * @param personId The ID of the person to register biometric data for\n * @param faceImage The face image blob to register\n * @param applicationToken The application token for authentication\n * @returns Promise with registration result\n * @throws NeoFaceError if request fails\n * @throws NeoFaceError if validation fails\n */\nexport const registerBiometric = async (\n personId: string,\n faceImage: Blob,\n applicationToken: string\n): Promise<{\n success: boolean;\n message: string;\n identity_data_id?: string;\n type_of_identification?: string;\n have_biometric_data?: boolean;\n created_at?: string;\n}> => {\n ensureSecureContext();\n\n if (!personId) {\n throw new NeoFaceError('Person ID is required', ErrorType.VALIDATION_ERROR);\n }\n\n if (!faceImage) {\n throw new NeoFaceError('Face image is required', ErrorType.VALIDATION_ERROR);\n }\n\n const controller = createTimeoutController();\n const compressedImage = await compressImage(faceImage);\n\n // Convert image to base64 for biometric data\n const base64Image = await new Promise<string>((resolve, reject) => {\n const reader = new FileReader();\n reader.onload = () => {\n const result = reader.result as string;\n resolve(result.split(',')[1]); // Remove data:image/... prefix\n };\n reader.onerror = reject;\n reader.readAsDataURL(compressedImage);\n });\n\n try {\n const response = await fetch(`${getApiBaseUrl()}/api/v1/identity-data/`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'X-App-Token': applicationToken,\n },\n body: JSON.stringify({\n person: personId,\n type_of_identification: 'FACE',\n biometric_data: base64Image,\n }),\n signal: controller.signal,\n });\n\n if (!response.ok) {\n if (response.status === 401 || response.status === 403) {\n throw new NeoFaceError(\n 'Invalid or expired application token, or insufficient permissions',\n ErrorType.INVALID_TOKEN\n );\n }\n if (response.status === 400) {\n // Try to get validation error details\n try {\n const errorData = await response.json();\n const errorMessage = errorData.message || errorData.error || 'Invalid biometric data';\n throw new NeoFaceError(errorMessage, ErrorType.VALIDATION_ERROR);\n } catch (parseError) {\n if (parseError instanceof NeoFaceError) {\n throw parseError;\n }\n throw new NeoFaceError('Invalid biometric data', ErrorType.VALIDATION_ERROR);\n }\n }\n throw new NeoFaceError(`Server returned status ${response.status}`, ErrorType.NETWORK);\n }\n\n const data = await response.json();\n\n return {\n success: true,\n message: 'Biometric data registered successfully',\n identity_data_id: data.id,\n type_of_identification: data.type_of_identification,\n have_biometric_data: data.have_biometric_data,\n created_at: data.created_at,\n };\n } catch (error) {\n if (error instanceof Error) {\n if (error.name === 'AbortError') {\n throw new NeoFaceError('Request timed out', ErrorType.NETWORK);\n }\n if (error instanceof NeoFaceError) {\n throw error;\n }\n throw new NeoFaceError(error.message, ErrorType.NETWORK);\n }\n throw new NeoFaceError('Unknown error occurred', ErrorType.NETWORK_ERROR);\n }\n};\n\n/**\n * Register a new application for a consumer\n * @param jwtToken JWT authentication token from logged user\n * @param consumerId Consumer ID (UUID) who will own the application\n * @param applicationData Application registration data\n * @returns Promise with registration result\n * @throws NeoFaceError if registration fails\n *\n * @example\n * ```typescript\n * const result = await registerApplication(\n * 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...',\n * 'consumer-uuid-123',\n * {\n * applicationName: 'My App',\n * domain: 'example.com',\n * acceptOnlyEmailWithSameDomain: true\n * }\n * );\n * console.log('App Token:', result.application.app_token);\n * ```\n */\nexport const registerApplication = async (\n jwtToken: string,\n consumerId: string,\n applicationData: ApplicationRegistrationData\n): Promise<ApplicationRegistrationResult> => {\n ensureSecureContext();\n\n const controller = createTimeoutController();\n\n try {\n const response = await fetch(`${getApiBaseUrl()}/api/v1/applications/`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${jwtToken}`,\n },\n body: JSON.stringify({\n consumer: consumerId,\n application_name: applicationData.applicationName,\n domain: applicationData.domain,\n accept_only_email_with_same_domain: applicationData.acceptOnlyEmailWithSameDomain,\n }),\n signal: controller.signal,\n });\n\n if (!response.ok) {\n if (response.status === 401) {\n throw new NeoFaceError('Invalid or expired JWT token', ErrorType.INVALID_TOKEN);\n }\n if (response.status === 403) {\n throw new NeoFaceError(\n 'Insufficient permissions to create application',\n ErrorType.INVALID_TOKEN\n );\n }\n if (response.status === 400) {\n // Try to get validation error details\n try {\n const errorData = await response.json();\n const errorMessage = errorData.message || errorData.error || 'Invalid application data';\n throw new NeoFaceError(errorMessage, ErrorType.VALIDATION_ERROR);\n } catch (parseError) {\n if (parseError instanceof NeoFaceError) {\n throw parseError;\n }\n throw new NeoFaceError('Invalid application data', ErrorType.VALIDATION_ERROR);\n }\n }\n throw new NeoFaceError(`Server returned status ${response.status}`, ErrorType.NETWORK);\n }\n\n const data = await response.json();\n\n return {\n success: true,\n application: {\n id: data.id,\n application_name: data.application_name,\n domain: data.domain,\n accept_only_email_with_same_domain: data.accept_only_email_with_same_domain,\n app_token: data.app_token,\n is_active: data.is_active,\n created_at: data.created_at,\n },\n message: 'Application registered successfully',\n };\n } catch (error) {\n if (error instanceof Error) {\n if (error.name === 'AbortError') {\n throw new NeoFaceError('Request timed out', ErrorType.NETWORK);\n }\n if (error instanceof NeoFaceError) {\n throw error;\n }\n throw new NeoFaceError(error.message, ErrorType.NETWORK);\n }\n throw new NeoFaceError('Unknown error occurred', ErrorType.NETWORK_ERROR);\n }\n};\n\n// ============================================================================\n// PROOF OF LIFE - Interfaces and Functions\n// ============================================================================\n\n/**\n * Personal data item returned by PROOF_OF_LIFE\n */\nexport interface PersonalDataItem {\n type: string;\n english_label?: string;\n value: string;\n masked: boolean;\n validated: boolean;\n is_principal_identification?: boolean;\n data_classification?: string;\n}\n\n/**\n * Result of PROOF_OF_LIFE operation\n */\nexport interface ProofOfLifeResult {\n success: boolean;\n isLive: boolean;\n livenessScore: number;\n faceRecognitionScore: number;\n combinedConfidence: number;\n personId?: string;\n personalData: PersonalDataItem[];\n processingTime: number;\n historyId: string;\n taskId: string;\n message?: string;\n error?: string;\n}\n\n/**\n * Task status response from backend\n */\nexport interface TaskStatusResponse {\n success: boolean;\n message: string;\n timestamp: string;\n data: {\n task_id: string;\n status: 'pending' | 'started' | 'success' | 'failure' | 'retry' | 'revoked';\n message?: string;\n result?: {\n success: boolean;\n is_live?: boolean;\n liveness_score?: number;\n face_recognition_score?: number;\n combined_confidence?: number;\n person_id?: string;\n personal_data?: PersonalDataItem[];\n personal_data_count?: number;\n processing_time?: number;\n history_id?: string;\n error?: string;\n message?: string;\n };\n error?: string;\n progress?: {\n current: number;\n total: number;\n percentage: number;\n };\n };\n}\n\n/**\n * Options for recording face video\n */\nexport interface RecordVideoOptions {\n durationMs?: number; // Duration in milliseconds (default: 3000)\n mimeType?: string; // MIME type for video (default: 'video/webm')\n onProgress?: (progress: number) => void; // Progress callback (0-100)\n}\n\n/**\n * Options for polling task status\n */\nexport interface PollTaskStatusOptions {\n maxAttempts?: number; // Maximum polling attempts (default: 60)\n intervalMs?: number; // Interval between polls in ms (default: 1000)\n onStatusChange?: (status: string, progress?: number) => void; // Status change callback\n}\n\n/**\n * Records a video from the camera for a specified duration\n * @param options Recording options\n * @returns Promise that resolves to a video Blob\n * @throws NeoFaceError if recording fails\n */\nexport const recordFaceVideo = async (options: RecordVideoOptions = {}): Promise<Blob> => {\n ensureSecureContext();\n\n const { durationMs = 3000, mimeType = 'video/webm', onProgress } = options;\n\n return new Promise(async (resolve, reject) => {\n let stream: MediaStream | null = null;\n let mediaRecorder: MediaRecorder | null = null;\n const chunks: Blob[] = [];\n\n try {\n // Request camera access\n stream = await navigator.mediaDevices.getUserMedia({\n video: {\n facingMode: 'user',\n width: { ideal: 640 },\n height: { ideal: 480 },\n },\n audio: false,\n });\n\n // Determine the best supported MIME type\n let selectedMimeType = mimeType;\n const mimeTypes = ['video/webm;codecs=vp9', 'video/webm;codecs=vp8', 'video/webm', 'video/mp4'];\n\n for (const type of mimeTypes) {\n if (MediaRecorder.isTypeSupported(type)) {\n selectedMimeType = type;\n break;\n }\n }\n\n // Create MediaRecorder\n mediaRecorder = new MediaRecorder(stream, {\n mimeType: selectedMimeType,\n videoBitsPerSecond: 1000000, // 1 Mbps\n });\n\n // Collect video chunks\n mediaRecorder.ondataavailable = event => {\n if (event.data.size > 0) {\n chunks.push(event.data);\n }\n };\n\n // Handle recording complete\n mediaRecorder.onstop = () => {\n // Stop all tracks\n if (stream) {\n stream.getTracks().forEach(track => track.stop());\n }\n\n // Create video blob\n const videoBlob = new Blob(chunks, { type: selectedMimeType });\n resolve(videoBlob);\n };\n\n // Handle recording error\n mediaRecorder.onerror = event => {\n if (stream) {\n stream.getTracks().forEach(track => track.stop());\n }\n reject(new NeoFaceError(`Recording error: ${event}`, ErrorType.CAPTURE_ERROR));\n };\n\n // Start recording\n mediaRecorder.start(100); // Collect data every 100ms\n\n // Progress tracking\n if (onProgress) {\n const progressInterval = 100; // Update every 100ms\n let elapsed = 0;\n\n const progressTimer = setInterval(() => {\n elapsed += progressInterval;\n const progress = Math.min(100, (elapsed / durationMs) * 100);\n onProgress(progress);\n\n if (elapsed >= durationMs) {\n clearInterval(progressTimer);\n }\n }, progressInterval);\n }\n\n // Stop recording after duration\n setTimeout(() => {\n if (mediaRecorder && mediaRecorder.state === 'recording') {\n mediaRecorder.stop();\n }\n }, durationMs);\n } catch (error) {\n // Cleanup on error\n if (stream) {\n stream.getTracks().forEach(track => track.stop());\n }\n\n if (error instanceof Error) {\n if (error.name === 'NotAllowedError') {\n reject(new NeoFaceError('Camera permission denied', ErrorType.CAMERA_ERROR));\n } else if (error.name === 'NotFoundError') {\n reject(new NeoFaceError('No camera found', ErrorType.NO_CAMERA));\n } else {\n reject(new NeoFaceError(error.message, ErrorType.CAPTURE_ERROR));\n }\n } else {\n reject(new NeoFaceError('Failed to record video', ErrorType.CAPTURE_ERROR));\n }\n }\n });\n};\n\n/**\n * Polls the task status until completion or failure\n * @param taskId Task ID to poll\n * @param applicationToken Application token for authentication\n * @param options Polling options\n * @returns Promise that resolves to the final task result\n * @throws NeoFaceError if polling fails or task fails\n */\nexport const pollTaskStatus = async (\n taskId: string,\n applicationToken: string,\n options: PollTaskStatusOptions = {}\n): Promise<TaskStatusResponse['data']['result']> => {\n const { maxAttempts = 60, intervalMs = 1000, onStatusChange } = options;\n\n let attempts = 0;\n\n while (attempts < maxAttempts) {\n attempts++;\n\n try {\n const response = await fetch(`${getApiBaseUrl()}/api/v1/tasks/status/?task_id=${taskId}`, {\n method: 'GET',\n headers: {\n Accept: 'application/json',\n 'X-App-Token': applicationToken,\n },\n });\n\n if (!response.ok) {\n throw new NeoFaceError(`Task status check failed: ${response.status}`, ErrorType.NETWORK);\n }\n\n const data: TaskStatusResponse = await response.json();\n const status = data.data?.status || 'unknown';\n\n // Notify status change\n if (onStatusChange) {\n const progress = data.data?.progress?.percentage;\n onStatusChange(status, progress);\n }\n\n // Check terminal states\n if (status === 'success') {\n return data.data.result;\n }\n\n if (status === 'failure') {\n const errorMessage = data.data?.result?.message || data.data?.error || 'Task failed';\n throw new NeoFaceError(errorMessage, ErrorType.RECOGNITION_FAILED);\n }\n\n if (status === 'revoked') {\n throw new NeoFaceError('Task was cancelled', ErrorType.RECOGNITION_FAILED);\n }\n\n // Wait before next poll\n await new Promise(resolve => setTimeout(resolve, intervalMs));\n } catch (error) {\n if (error instanceof NeoFaceError) {\n throw error;\n }\n throw new NeoFaceError(\n `Polling error: ${error instanceof Error ? error.message : 'Unknown error'}`,\n ErrorType.NETWORK\n );\n }\n }\n\n throw new NeoFaceError('Task status polling timed out', ErrorType.NETWORK);\n};\n\n/**\n * Starts the PROOF_OF_LIFE recognition process\n * @param videoBase64 Video data as base64 string\n * @param applicationToken Application token for authentication\n * @returns Promise with task_id and history_id for polling\n * @throws NeoFaceError if request fails\n */\nexport const startProofOfLife = async (\n videoBase64: string,\n applicationToken: string\n): Promise<{\n taskId: string;\n historyId: string;\n status: string;\n message: string;\n}> => {\n ensureSecureContext();\n\n const controller = createTimeoutController();\n\n try {\n const response = await fetch(`${getApiBaseUrl()}/api/v1/auth/recognition/face/external/`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'X-App-Token': applicationToken,\n },\n body: JSON.stringify({\n video: videoBase64,\n purpose: 'PROOF_OF_LIFE',\n }),\n signal: controller.signal,\n });\n\n if (!response.ok) {\n if (response.status === 401 || response.status === 403) {\n throw new NeoFaceError('Invalid or expired application token', ErrorType.INVALID_TOKEN);\n }\n if (response.status === 400) {\n const errorData = await response.json();\n throw new NeoFaceError(\n errorData.message || 'Invalid request data',\n ErrorType.VALIDATION_ERROR\n );\n }\n throw new NeoFaceError(`Server returned status ${response.status}`, ErrorType.NETWORK);\n }\n\n const data = await response.json();\n\n if (!data.success || !data.data?.task_id) {\n throw new NeoFaceError(\n data.message || 'Failed to start proof of life',\n ErrorType.RECOGNITION_FAILED\n );\n }\n\n return {\n taskId: data.data.task_id,\n historyId: data.data.history_id,\n status: data.data.status,\n message: data.message || 'Recognition started successfully',\n };\n } catch (error) {\n if (error instanceof Error) {\n if (error.name === 'AbortError') {\n throw new NeoFaceError('Request timed out', ErrorType.NETWORK);\n }\n if (error instanceof NeoFaceError) {\n throw error;\n }\n throw new NeoFaceError(error.message, ErrorType.NETWORK);\n }\n throw new NeoFaceError('Unknown error occurred', ErrorType.NETWORK_ERROR);\n }\n};\n\n/**\n * Converts a video Blob to base64 string\n * @param videoBlob Video blob to convert\n * @returns Promise that resolves to base64 string (without data URI prefix)\n */\nexport const videoToBase64 = async (videoBlob: Blob): Promise<string> => {\n return new Promise((resolve, reject) => {\n const reader = new FileReader();\n reader.onload = () => {\n const result = reader.result as string;\n // Remove data URI prefix if present (e.g., \"data:video/webm;base64,\")\n const base64 = result.includes(',') ? result.split(',')[1] : result;\n resolve(base64);\n };\n reader.onerror = () => reject(new NeoFaceError('Failed to convert video to base64', ErrorType.CAPTURE_ERROR));\n reader.readAsDataURL(videoBlob);\n });\n};\n","import React, { useEffect, useRef, useState, useReducer, CSSProperties } from 'react';\nimport * as faceapi from 'face-api.js';\nimport { useCamera } from '../hooks/useCamera';\nimport { recognize } from '../api';\n\ninterface RecognitionResult {\n success: boolean;\n data: {\n faceId: string;\n confidence: number;\n };\n}\n\ninterface FaceCaptureModalProps {\n accessToken: string;\n onClose: () => void;\n onSuccess: (result: RecognitionResult) => void;\n}\n\n// Define the state types\ntype ModalState =\n | { status: 'requestingPermission' }\n | { status: 'ready' }\n | { status: 'capturing' }\n | { status: 'uploading' }\n | { status: 'success' }\n | { status: 'error'; message: string };\n\n// Define the action types\ntype ModalAction =\n | { type: 'PERMISSION_GRANTED' }\n | { type: 'PERMISSION_DENIED' }\n | { type: 'START_CAPTURE' }\n | { type: 'UPLOAD_START' }\n | { type: 'UPLOAD_SUCCESS' }\n | { type: 'UPLOAD_ERROR'; message: string }\n | { type: 'RESET' }\n | { type: 'SET_ERROR'; message: string };\n\n// Reducer function\nconst modalReducer = (state: ModalState, action: ModalAction): ModalState => {\n switch (action.type) {\n case 'PERMISSION_GRANTED':\n return { status: 'ready' };\n case 'PERMISSION_DENIED':\n return { status: 'error', message: 'Permissão para acessar a câmera foi negada' };\n case 'START_CAPTURE':\n return { status: 'capturing' };\n case 'UPLOAD_START':\n return { status: 'uploading' };\n case 'UPLOAD_SUCCESS':\n return { status: 'success' };\n case 'UPLOAD_ERROR':\n return { status: 'error', message: action.message };\n case 'RESET':\n return { status: 'ready' };\n case 'SET_ERROR':\n return { status: 'error', message: action.message };\n default:\n return state;\n }\n};\n\n// Styles\nconst styles: Record<string, CSSProperties> = {\n overlay: {\n position: 'fixed',\n top: 0,\n left: 0,\n right: 0,\n bottom: 0,\n backgroundColor: 'rgba(0, 0, 0, 0.8)',\n display: 'flex',\n justifyContent: 'center',\n alignItems: 'center',\n zIndex: 1000,\n },\n modal: {\n backgroundColor: '#000',\n borderRadius: '12px',\n overflow: 'hidden',\n position: 'relative',\n width: '60%',\n maxWidth: '600px',\n maxHeight: '80vh',\n },\n video: {\n width: '100%',\n height: 'auto',\n display: 'block',\n },\n canvas: {\n display: 'none',\n },\n guide: {\n position: 'absolute',\n top: '50%',\n left: '50%',\n transform: 'translate(-50%, -50%)',\n width: '200px',\n height: '200px',\n border: '2px solid rgba(255, 255, 255, 0.5)',\n borderRadius: '50%',\n pointerEvents: 'none',\n },\n controls: {\n position: 'absolute',\n bottom: '20px',\n left: 0,\n right: 0,\n display: 'flex',\n justifyContent: 'center',\n gap: '10px',\n },\n button: {\n padding: '12px 24px',\n borderRadius: '24px',\n border: 'none',\n backgroundColor: '#007AFF',\n color: 'white',\n fontSize: '16px',\n fontWeight: 'bold',\n cursor: 'pointer',\n },\n message: {\n position: 'absolute',\n top: '20px',\n left: 0,\n right: 0,\n textAlign: 'center',\n color: 'white',\n fontSize: '18px',\n fontWeight: 'bold',\n },\n spinner: {\n position: 'absolute',\n top: '50%',\n left: '50%',\n transform: 'translate(-50%, -50%)',\n width: '40px',\n height: '40px',\n border: '4px solid rgba(255, 255, 255, 0.3)',\n borderRadius: '50%',\n borderTopColor: 'white',\n animation: 'spin 1s linear infinite',\n },\n closeButton: {\n position: 'absolute',\n top: '20px',\n right: '20px',\n backgroundColor: 'transparent',\n border: 'none',\n color: 'white',\n fontSize: '24px',\n cursor: 'pointer',\n },\n};\n\n// Media query styles\nconst mediaStyles = `\n @media (max-width: 768px) {\n .modal {\n width: 100%;\n height: 100%;\n max-height: 100vh;\n border-radius: 0;\n }\n .button {\n padding: 16px 32px;\n font-size: 18px;\n }\n }\n`;\n\n// Add keyframes for spinner animation\nconst keyframes = `\n @keyframes spin {\n 0% { transform: translate(-50%, -50%) rotate(0deg); }\n 100% { transform: translate(-50%, -50%) rotate(360deg); }\n }\n`;\n\nexport function FaceCaptureModal({ accessToken, onClose, onSuccess }: FaceCaptureModalProps) {\n // Refs\n const videoRef = useRef<HTMLVideoElement>(null);\n const canvasRef = useRef<HTMLCanvasElement>(null);\n const streamRef = useRef<MediaStream | null>(null);\n const faceDetectionTimeoutRef = useRef<number | null>(null);\n const livenessCheckRef = useRef<number | null>(null);\n\n // State\n const [state, dispatch] = useReducer(modalReducer, { status: 'requestingPermission' });\n const [faceDetected, setFaceDetected] = useState(false);\n const [livenessAttempts, setLivenessAttempts] = useState(0);\n\n // Camera hook\n const { startCamera, stopCamera } = useCamera();\n\n // Check if face is centered in the frame\n const isFaceCentered = (detection: faceapi.FaceDetection): boolean => {\n if (!videoRef.current) return false;\n\n const { box } = detection;\n const { videoWidth, videoHeight } = videoRef.current;\n\n // Define the center region (40% of the frame)\n const centerX = videoWidth / 2;\n const centerY = videoHeight / 2;\n const centerWidth = videoWidth * 0.4;\n const centerHeight = videoHeight * 0.4;\n\n // Check if face is within the center region\n return (\n box.x + box.width / 2 > centerX - centerWidth / 2 &&\n box.x + box.width / 2 < centerX + centerWidth / 2 &&\n box.y + box.height / 2 > centerY - centerHeight / 2 &&\n box.y + box.height / 2 < centerY + centerHeight / 2\n );\n };\n\n // Start liveness check\n const startLivenessCheck = () => {\n // Wait 3 seconds before starting liveness check\n livenessCheckRef.current = window.setTimeout(() => {\n if (state.status === 'ready') {\n setLivenessAttempts(prev => prev + 1);\n dispatch({ type: 'START_CAPTURE' });\n }\n }, 3000);\n };\n\n // Face detection function\n const startFaceDetection = () => {\n if (!videoRef.current) return;\n\n const detectFaces = async () => {\n if (!videoRef.current || state.status !== 'ready') return;\n\n try {\n const detections = await faceapi.detectAllFaces(\n videoRef.current,\n new faceapi.TinyFaceDetectorOptions()\n );\n\n // Check if exactly one face is detected and it's centered\n const isOneFace = detections.length === 1;\n const isCentered = isOneFace && isFaceCentered(detections[0]);\n\n setFaceDetected(isOneFace && isCentered);\n\n // If face is detected and centered, proceed with liveness check\n if (isOneFace && isCentered && livenessAttempts === 0) {\n startLivenessCheck();\n }\n } catch (error) {\n console.error('Face detection error:', error);\n }\n\n // Continue detection\n faceDetectionTimeoutRef.current = window.setTimeout(detectFaces, 100);\n };\n\n detectFaces();\n };\n\n // Initialize face-api.js models\n useEffect(() => {\n const loadModels = async () => {\n try {\n await Promise.all([\n faceapi.nets.tinyFaceDetector.loadFromUri('./models'),\n faceapi.nets.faceLandmark68Net.loadFromUri('./models'),\n ]);\n } catch (error) {\n console.error('Failed to load face-api.js models:', error);\n }\n };\n\n loadModels();\n\n return () => {\n // Clean up any pending timeouts\n if (faceDetectionTimeoutRef.current) {\n window.clearTimeout(faceDetectionTimeoutRef.current);\n }\n if (livenessCheckRef.current) {\n window.clearInterval(livenessCheckRef.current);\n }\n };\n }, []);\n\n // Start camera when component mounts\n useEffect(() => {\n const initializeCamera = async () => {\n try {\n const stream = await startCamera();\n streamRef.current = stream;\n\n if (videoRef.current) {\n videoRef.current.srcObject = stream;\n }\n\n dispatch({ type: 'PERMISSION_GRANTED' });\n startFaceDetection();\n } catch (error) {\n dispatch({ type: 'PERMISSION_DENIED' });\n }\n };\n\n initializeCamera();\n\n return () => {\n if (streamRef.current) {\n stopCamera(streamRef.current);\n }\n };\n }, [startCamera, stopCamera]);\n\n // Capture and process image\n const handleCapture = async () => {\n if (!canvasRef.current || !videoRef.current) return;\n\n try {\n dispatch({ type: 'START_CAPTURE' });\n\n // Get the canvas context\n const context = canvasRef.current.getContext('2d');\n if (!context) {\n dispatch({ type: 'SET_ERROR', message: 'Failed to get canvas context' });\n return;\n }\n\n // Set canvas dimensions to match video\n canvasRef.current.width = videoRef.current.videoWidth;\n canvasRef.current.height = videoRef.current.videoHeight;\n\n // Draw the video frame to the canvas\n context.drawImage(videoRef.current, 0, 0, canvasRef.current.width, canvasRef.current.height);\n\n // Convert canvas to blob\n const blob = await new Promise<Blob>((resolve, reject) => {\n canvasRef.current?.toBlob(\n b => {\n if (b) resolve(b);\n else reject(new Error('Failed to convert canvas to blob'));\n },\n 'image/jpeg',\n 0.95\n );\n });\n\n // Process recognition\n try {\n const result = await recognize(blob, accessToken);\n onSuccess({\n success: true,\n data: {\n faceId: result.payload.documentId,\n confidence: 1.0,\n },\n });\n dispatch({ type: 'UPLOAD_SUCCESS' });\n setTimeout(onClose, 1500);\n } catch (error) {\n dispatch({ type: 'SET_ERROR', message: 'Face recognition failed' });\n }\n } catch (error) {\n dispatch({ type: 'SET_ERROR', message: 'Failed to capture image' });\n }\n };\n\n // Handle retry\n const handleRetry = () => {\n dispatch({ type: 'RESET' });\n setLivenessAttempts(0);\n };\n\n return (\n <div style={styles.overlay}>\n <style>{keyframes}</style>\n <style>{mediaStyles}</style>\n <div style={styles.modal} className=\"modal\">\n <button type=\"button\" style={styles.closeButton} onClick={onClose}>\n ×\n </button>\n\n <video ref={videoRef} style={styles.video} autoPlay playsInline muted />\n\n <canvas ref={canvasRef} style={styles.canvas} />\n\n <div style={styles.guide} />\n\n {state.status === 'ready' && !faceDetected && (\n <div style={styles.message}>Centralize seu rosto no círculo</div>\n )}\n\n {state.status === 'capturing' && (\n <div style={styles.message}>Por favor, pisque os olhos</div>\n )}\n\n {state.status === 'uploading' && <div style={styles.spinner} />}\n\n {state.status === 'success' && <div style={styles.message}>Autenticado!</div>}\n\n {state.status === 'error' && <div style={styles.message}>{state.message}</div>}\n\n <div style={styles.controls}>\n {state.status === 'ready' && faceDetected && (\n <button type=\"button\" style={styles.button} onClick={handleCapture}>\n Capturar\n </button>\n )}\n\n {state.status === 'capturing' && (\n <button type=\"button\" style={styles.button} onClick={handleCapture}>\n Capturar\n </button>\n )}\n\n {state.status === 'error' && (\n <button type=\"button\" style={styles.button} onClick={handleRetry}>\n Tentar Novamente\n </button>\n )}\n </div>\n </div>\n </div>\n );\n}\n","import { useEffect, useRef, useState, useReducer } from 'react';\nimport * as faceapi from 'face-api.js';\nimport { registerPersonWithBiometric } from '../api';\n\n// Tipos\ninterface BiometricRegistrationResult {\n success: boolean;\n message: string;\n person_id?: string;\n user_id?: string;\n person_validated?: boolean;\n face_processing?: any;\n onboarding_link?: string;\n}\n\ninterface BiometricRegistrationModalProps {\n personData?: {\n name: string;\n birth_date: string;\n cpf: string;\n email: string;\n password: string;\n };\n applicationToken?: string;\n onClose: () => void;\n onSuccess?: (result: BiometricRegistrationResult) => void;\n onPhotosCaptured?: (photos: Blob[]) => void;\n onError: (error: string) => void;\n // Flag para controlar se deve fazer requisição real ao backend (sempre true em produção)\n useRealApi?: boolean;\n}\n\ntype LivenessStep = 'center' | 'up' | 'down' | 'left' | 'right' | 'complete';\n\ntype ModalState =\n | { status: 'instructions' }\n | {\n status: 'liveness';\n step: LivenessStep;\n capturedPhotos: Blob[];\n stepSequence: LivenessStep[];\n currentStepIndex: number;\n stepProgress: number;\n }\n | { status: 'processing' }\n | { status: 'success' }\n | { status: 'error'; message: string };\n\ntype ModalAction =\n | { type: 'START_LIVENESS'; stepSequence: LivenessStep[] }\n | { type: 'NEXT_STEP'; step: LivenessStep; photo?: Blob }\n | { type: 'UPDATE_PROGRESS'; progress: number }\n | { type: 'START_PROCESSING' }\n | { type: 'SUCCESS' }\n | { type: 'ERROR'; message: string }\n | { type: 'RESET' };\n\nconst modalReducer = (state: ModalState, action: ModalAction): ModalState => {\n switch (action.type) {\n case 'START_LIVENESS':\n return {\n status: 'liveness',\n step: action.stepSequence[0],\n capturedPhotos: [],\n stepSequence: action.stepSequence,\n currentStepIndex: 0,\n stepProgress: 0,\n };\n case 'NEXT_STEP':\n if (state.status === 'liveness') {\n const newPhotos = action.photo\n ? [...state.capturedPhotos, action.photo]\n : state.capturedPhotos;\n\n return {\n status: 'liveness',\n step: action.step,\n capturedPhotos: newPhotos,\n stepSequence: state.stepSequence,\n currentStepIndex: state.currentStepIndex + 1,\n stepProgress: 0,\n };\n }\n return state;\n case 'UPDATE_PROGRESS':\n if (state.status === 'liveness') {\n return {\n ...state,\n stepProgress: action.progress,\n };\n }\n return state;\n case 'START_PROCESSING':\n return { status: 'processing' };\n case 'SUCCESS':\n return { status: 'success' };\n case 'ERROR':\n return { status: 'error', message: action.message };\n case 'RESET':\n return initialState;\n default:\n return state;\n }\n};\n\nconst livenessInstructions: Record<LivenessStep, string> = {\n center: 'Posicione seu rosto no centro',\n up: 'Incline a cabeça para cima',\n down: 'Incline a cabeça para baixo',\n left: 'Vire o rosto para a esquerda',\n right: 'Vire o rosto para a direita',\n complete: 'Processando...',\n};\n\nconst generateRandomStepSequence = (): LivenessStep[] => {\n return ['center', 'up', 'down', 'left', 'right', 'complete'];\n};\n\nconst initialState: ModalState = (() => {\n const sequence = generateRandomStepSequence();\n return {\n status: 'liveness',\n step: sequence[0],\n capturedPhotos: [],\n stepSequence: sequence,\n currentStepIndex: 0,\n stepProgress: 0,\n };\n})();\n\n// Calibragem\nconst CALIBRATION = {\n PITCH_DEAD_ZONE: 8,\n YAW_DEAD_ZONE: 10,\n PITCH_OFFSET: 26,\n PITCH_UP_THRESHOLD: 8,\n PITCH_DOWN_THRESHOLD: -8,\n YAW_LEFT_THRESHOLD: 15,\n YAW_RIGHT_THRESHOLD: -15,\n CAPTURE_HOLD_TIME: 1500,\n DETECTION_INTERVAL: 200,\n};\n\nexport function BiometricRegistrationModal({\n personData,\n applicationToken,\n onClose,\n onSuccess,\n onPhotosCaptured,\n onError,\n useRealApi = true, // Sempre usa API real em produção\n}: BiometricRegistrationModalProps) {\n const videoRef = useRef<HTMLVideoElement>(null);\n const canvasRef = useRef<HTMLCanvasElement>(null);\n const streamRef = useRef<MediaStream | null>(null);\n const holdStartTimeRef = useRef<number | null>(null);\n const progressIntervalRef = useRef<number | null>(null);\n const isCapturingRef = useRef<boolean>(false);\n const currentStepRef = useRef<LivenessStep>('center');\n\n const captureStateRef = useRef<{\n stepSequence: LivenessStep[];\n currentStepIndex: number;\n capturedPhotos: Blob[];\n } | null>(null);\n\n const [state, dispatch] = useReducer(modalReducer, initialState);\n const [faceDetected, setFaceDetected] = useState(false);\n const [faceDistance, setFaceDistance] = useState<'ok' | 'too-close' | 'too-far'>('ok');\n\n // Atualizar referência da etapa atual\n useEffect(() => {\n if (state.status === 'liveness') {\n const newStep = state.step;\n\n if (currentStepRef.current !== newStep) {\n currentStepRef.current = newStep;\n\n if (!isCapturingRef.current) {\n if (holdStartTimeRef.current) {\n holdStartTimeRef.current = null;\n }\n\n if (progressIntervalRef.current) {\n clearInterval(progressIntervalRef.current);\n progressIntervalRef.current = null;\n }\n\n dispatch({ type: 'UPDATE_PROGRESS', progress: 0 });\n }\n }\n\n captureStateRef.current = {\n stepSequence: state.stepSequence,\n currentStepIndex: state.currentStepIndex,\n capturedPhotos: state.capturedPhotos,\n };\n }\n }, [\n state.status,\n state.status === 'liveness' && state.step,\n state.status === 'liveness' && state.currentStepIndex,\n ]);\n\n // Calcular ângulos da cabeça\n const calculateHeadAngles = (landmarks: any) => {\n try {\n const points = landmarks.positions || landmarks._positions;\n\n if (!Array.isArray(points) || points.length < 68) {\n return null;\n }\n\n const getPoint = (index: number) => {\n const point = points[index];\n return {\n x: point._x !== undefined ? point._x : point.x,\n y: point._y !== undefined ? point._y : point.y,\n };\n };\n\n const noseTip = getPoint(30);\n const chin = getPoint(8);\n const leftEye = getPoint(36);\n const rightEye = getPoint(45);\n\n const eyeCenterX = (leftEye.x + rightEye.x) / 2;\n const noseOffsetX = noseTip.x - eyeCenterX;\n const eyeDistance = Math.abs(rightEye.x - leftEye.x);\n const yaw = (noseOffsetX / eyeDistance) * 90;\n\n const eyeCenterY = (leftEye.y + rightEye.y) / 2;\n const noseOffsetY = noseTip.y - eyeCenterY;\n const faceHeight = Math.abs(chin.y - eyeCenterY);\n const pitchRaw = -(noseOffsetY / faceHeight) * 90;\n const pitch = pitchRaw + CALIBRATION.PITCH_OFFSET;\n\n return {\n pitch: Math.round(pitch),\n yaw: Math.round(yaw),\n };\n } catch (error) {\n return null;\n }\n };\n\n // Verificar posição\n const checkPositionMatch = (pitch: number, yaw: number, step: LivenessStep): boolean => {\n switch (step) {\n case 'center':\n return (\n Math.abs(pitch) < CALIBRATION.PITCH_DEAD_ZONE && Math.abs(yaw) < CALIBRATION.YAW_DEAD_ZONE\n );\n case 'up':\n return pitch > CALIBRATION.PITCH_UP_THRESHOLD;\n case 'down':\n return pitch < CALIBRATION.PITCH_DOWN_THRESHOLD;\n case 'left':\n return yaw > CALIBRATION.YAW_LEFT_THRESHOLD;\n case 'right':\n return yaw < CALIBRATION.YAW_RIGHT_THRESHOLD;\n default:\n return false;\n }\n };\n\n // Gerenciar hold e captura\n const manageHoldAndCapture = (positionCorrect: boolean) => {\n if (isCapturingRef.current || currentStepRef.current === 'complete') {\n return;\n }\n\n const now = Date.now();\n\n if (positionCorrect) {\n if (!holdStartTimeRef.current) {\n holdStartTimeRef.current = now;\n\n if (progressIntervalRef.current) {\n clearInterval(progressIntervalRef.current);\n }\n\n progressIntervalRef.current = window.setInterval(() => {\n if (!holdStartTimeRef.current) return;\n\n const elapsed = Date.now() - holdStartTimeRef.current;\n const progress = Math.min((elapsed / CALIBRATION.CAPTURE_HOLD_TIME) * 100, 100);\n\n dispatch({ type: 'UPDATE_PROGRESS', progress });\n\n if (progress >= 100) {\n clearInterval(progressIntervalRef.current!);\n progressIntervalRef.current = null;\n capturePhoto();\n }\n }, 50);\n }\n } else {\n if (holdStartTimeRef.current) {\n holdStartTimeRef.current = null;\n\n if (progressIntervalRef.current) {\n clearInterval(progressIntervalRef.current);\n progressIntervalRef.current = null;\n }\n\n dispatch({ type: 'UPDATE_PROGRESS', progress: 0 });\n }\n }\n };\n\n // Carregar modelos\n useEffect(() => {\n const loadModels = async () => {\n try {\n const MODEL_URL = 'https://cdn.jsdelivr.net/npm/@vladmandic/face-api/model';\n await Promise.all([\n faceapi.nets.tinyFaceDetector.loadFromUri(MODEL_URL),\n faceapi.nets.faceLandmark68Net.loadFromUri(MODEL_URL),\n ]);\n } catch (error) {\n dispatch({ type: 'ERROR', message: 'Erro ao carregar modelos de IA' });\n }\n };\n\n loadModels();\n }, []);\n\n // Inicializar câmera\n useEffect(() => {\n let mounted = true;\n\n const initCamera = async () => {\n if (state.status !== 'liveness' || streamRef.current) return;\n\n try {\n const stream = await navigator.mediaDevices.getUserMedia({\n video: {\n width: { ideal: 640 },\n height: { ideal: 480 },\n facingMode: 'user',\n },\n });\n\n if (!mounted) {\n stream.getTracks().forEach(track => track.stop());\n return;\n }\n\n const waitForVideo = async () => {\n let attempts = 0;\n while (!videoRef.current && attempts < 50) {\n await new Promise(resolve => setTimeout(resolve, 100));\n attempts++;\n }\n return videoRef.current;\n };\n\n const video = await waitForVideo();\n\n if (!video) {\n throw new Error('Elemento de vídeo não encontrado');\n }\n\n video.srcObject = stream;\n streamRef.current = stream;\n\n await video.play();\n } catch (error) {\n if (!mounted) return;\n\n const errorMessage = error instanceof Error ? error.message : 'Erro ao acessar câmera';\n dispatch({ type: 'ERROR', message: errorMessage });\n }\n };\n\n initCamera();\n\n return () => {\n mounted = false;\n\n if (streamRef.current) {\n streamRef.current.getTracks().forEach(track => track.stop());\n streamRef.current = null;\n }\n\n if (progressIntervalRef.current) {\n clearInterval(progressIntervalRef.current);\n progressIntervalRef.current = null;\n }\n\n holdStartTimeRef.current = null;\n };\n }, [state.status]);\n\n // Loop de detecção\n useEffect(() => {\n if (state.status !== 'liveness' || !videoRef.current) {\n return;\n }\n\n let isRunning = true;\n let lastDetectionTime = 0;\n\n const detectFace = async () => {\n if (!isRunning || state.status !== 'liveness') {\n return;\n }\n\n if (isCapturingRef.current || currentStepRef.current === 'complete') {\n requestAnimationFrame(detectFace);\n return;\n }\n\n const now = Date.now();\n\n if (now - lastDetectionTime < CALIBRATION.DETECTION_INTERVAL) {\n requestAnimationFrame(detectFace);\n return;\n }\n\n lastDetectionTime = now;\n\n try {\n const detection = await faceapi\n .detectSingleFace(\n videoRef.current!,\n new faceapi.TinyFaceDetectorOptions({\n inputSize: 160,\n scoreThreshold: 0.4,\n })\n )\n .withFaceLandmarks();\n\n if (!isRunning) return;\n\n const hasFace = !!detection;\n setFaceDetected(hasFace);\n\n // Verificar distância do rosto\n if (hasFace && detection.detection) {\n const box = detection.detection.box;\n const faceWidth = box.width;\n\n // Calcular se está muito perto ou muito longe baseado na largura do rosto\n if (faceWidth > 250) {\n setFaceDistance('too-close');\n } else if (faceWidth < 120) {\n setFaceDistance('too-far');\n } else {\n setFaceDistance('ok');\n }\n }\n\n if (hasFace && detection.landmarks) {\n const angles = calculateHeadAngles(detection.landmarks);\n\n if (angles) {\n const positionCorrect = checkPositionMatch(\n angles.pitch,\n angles.yaw,\n currentStepRef.current\n );\n\n manageHoldAndCapture(positionCorrect);\n }\n } else {\n if (holdStartTimeRef.current) {\n holdStartTimeRef.current = null;\n\n if (progressIntervalRef.current) {\n clearInterval(progressIntervalRef.current);\n progressIntervalRef.current = null;\n }\n\n dispatch({ type: 'UPDATE_PROGRESS', progress: 0 });\n }\n }\n } catch (error) {\n // Silently handle detection errors\n }\n\n if (isRunning && state.status === 'liveness') {\n requestAnimationFrame(detectFace);\n }\n };\n\n const startTimer = setTimeout(() => {\n if (isRunning && videoRef.current?.readyState === 4) {\n detectFace();\n }\n }, 1000);\n\n return () => {\n isRunning = false;\n clearTimeout(startTimer);\n\n if (progressIntervalRef.current) {\n clearInterval(progressIntervalRef.current);\n progressIntervalRef.current = null;\n }\n\n holdStartTimeRef.current = null;\n };\n }, [state.status]);\n\n // Capturar foto\n const capturePhoto = async () => {\n if (!videoRef.current || !canvasRef.current || isCapturingRef.current) {\n return;\n }\n\n isCapturingRef.current = true;\n holdStartTimeRef.current = null;\n\n const capturedState = captureStateRef.current;\n\n if (!capturedState) {\n isCapturingRef.current = false;\n return;\n }\n\n const canvas = canvasRef.current;\n const video = videoRef.current;\n const ctx = canvas.getContext('2d');\n\n if (!ctx) {\n isCapturingRef.current = false;\n return;\n }\n\n canvas.width = video.videoWidth;\n canvas.height = video.videoHeight;\n\n ctx.scale(-1, 1);\n ctx.drawImage(video, -canvas.width, 0, canvas.width, canvas.height);\n\n canvas.toBlob(\n blob => {\n if (!blob) {\n isCapturingRef.current = false;\n return;\n }\n\n const nextStepIndex = capturedState.currentStepIndex + 1;\n const nextStep = capturedState.stepSequence[nextStepIndex];\n\n if (nextStep === 'complete') {\n const allPhotos = [...capturedState.capturedPhotos, blob];\n dispatch({ type: 'START_PROCESSING' });\n processRegistration(allPhotos);\n } else {\n setTimeout(() => {\n isCapturingRef.current = false;\n dispatch({ type: 'NEXT_STEP', step: nextStep, photo: blob });\n }, 100);\n }\n },\n 'image/jpeg',\n 0.85\n );\n };\n\n // Processar registro\n const processRegistration = async (photos: Blob[]) => {\n try {\n if (onPhotosCaptured) {\n dispatch({ type: 'SUCCESS' });\n onPhotosCaptured(photos);\n return;\n }\n\n if (!personData || !applicationToken) {\n throw new Error('Dados de registro não fornecidos');\n }\n\n // 🚀 PRODUÇÃO: Fazer requisição real ao backend\n console.log('🚀 Fazendo requisição ao backend...');\n\n const result = await registerPersonWithBiometric(personData, photos, applicationToken);\n\n // Adaptar o resultado da API para o formato esperado pelo componente\n const adaptedResult: BiometricRegistrationResult = {\n success: result.success,\n message: result.message,\n person_id: result.person_id,\n user_id: result.user_id,\n person_validated: result.person_validated,\n face_processing: result.face_processing,\n onboarding_link: result.onboarding_link,\n };\n\n dispatch({ type: 'SUCCESS' });\n if (onSuccess) {\n onSuccess(adaptedResult);\n }\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : 'Erro desconhecido';\n dispatch({ type: 'ERROR', message: errorMessage });\n onError(errorMessage);\n }\n };\n\n // Renderizar liveness\n const renderLivenessDetection = () => {\n if (state.status !== 'liveness') return null;\n\n const progress = `${state.currentStepIndex + 1}/${state.stepSequence.length - 1}`;\n const stepProgress = state.stepProgress;\n\n return (\n <div className=\"liveness-container\">\n {/* Progress Indicator */}\n <div className=\"progress-indicator\">\n <span className=\"progress-text\">{progress}</span>\n </div>\n\n {/* Video Circle */}\n <div className=\"video-wrapper\">\n <div\n className={`video-circle ${faceDetected ? 'face-detected' : ''} ${stepProgress > 0 ? 'capturing' : ''\n }`}\n >\n <video ref={videoRef} className=\"video-feed\" autoPlay muted playsInline />\n\n {/* Face Detected Badge */}\n {faceDetected && (\n <div className=\"face-badge\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 20 20\" fill=\"none\">\n <path\n d=\"M5 10 L8 13 L15 6\"\n stroke=\"currentColor\"\n strokeWidth=\"2.5\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n <span>Rosto detectado</span>\n </div>\n )}\n </div>\n </div>\n\n {/* Instruction Text */}\n <div className=\"instruction-box\">\n <p className=\"instruction-text\">{livenessInstructions[state.step]}</p>\n {stepProgress > 0 && (\n <div className=\"progress-bar\">\n <div className=\"progress-fill\" style={{ width: `${stepProgress}%` }} />\n </div>\n )}\n </div>\n\n {/* Branding */}\n <div className=\"branding-small\">\n <img src=\"/logo-icone.png\" alt=\"NeoFaceId\" className=\"brand-logo-small\" />\n <span>NeoFaceId by</span>\n <span className=\"brand-name\">OCTA</span>\n </div>\n </div>\n );\n };\n\n // Renderizar processamento\n const renderProcessing = () => (\n <div className=\"processing-container\">\n <div className=\"spinner-wrapper\">\n <div className=\"spinner\" />\n <div className=\"spinner-ring\" />\n </div>\n <h3>Processando biometria</h3>\n <p>Analisando suas capturas faciais...</p>\n\n <div\n className=\"branding\"\n style={{\n marginTop: '40px',\n paddingTop: '24px',\n borderTop: '1px solid rgba(255, 255, 255, 0.1)',\n }}\n >\n <span>NeoFaceId by</span>\n <span className=\"brand-name\">OCTA</span>\n </div>\n </div>\n );\n\n // Renderizar erro\n const renderError = () => {\n if (state.status !== 'error') return null;\n\n return (\n <div className=\"result-container error\">\n <div className=\"result-icon error-icon\">\n <svg width=\"60\" height=\"60\" viewBox=\"0 0 60 60\" fill=\"none\">\n <circle cx=\"30\" cy=\"30\" r=\"28\" stroke=\"currentColor\" strokeWidth=\"3\" />\n <path\n d=\"M20 20 L40 40 M40 20 L20 40\"\n stroke=\"currentColor\"\n strokeWidth=\"3\"\n strokeLinecap=\"round\"\n />\n </svg>\n </div>\n <h3>Não foi possível concluir</h3>\n <p>{state.message}</p>\n <div className=\"button-group\">\n <button className=\"secondary-button\" onClick={() => dispatch({ type: 'RESET' })}>\n Tentar Novamente\n </button>\n <button className=\"ghost-button\" onClick={onClose}>\n Cancelar\n </button>\n </div>\n </div>\n );\n };\n\n // Renderizar sucesso\n const renderSuccess = () => (\n <div className=\"result-container success\">\n <div className=\"result-icon success-icon\">\n <svg width=\"60\" height=\"60\" viewBox=\"0 0 60 60\" fill=\"none\">\n <circle cx=\"30\" cy=\"30\" r=\"28\" stroke=\"currentColor\" strokeWidth=\"3\" />\n <path\n d=\"M15 30 L25 40 L45 20\"\n stroke=\"currentColor\"\n strokeWidth=\"3\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n </div>\n <h3>{onPhotosCaptured ? 'Captura concluída!' : 'Verificação concluída!'}</h3>\n <p>{onPhotosCaptured ? 'Suas capturas faciais foram realizadas' : 'Sua biometria foi registrada com sucesso'}</p>\n <button className=\"primary-button\" onClick={onClose}>\n Continuar\n </button>\n </div>\n );\n\n return (\n <>\n <style>{`\n @keyframes fadeIn {\n from { opacity: 0; transform: scale(0.95); }\n to { opacity: 1; transform: scale(1); }\n }\n\n @keyframes slideUp {\n from { opacity: 0; transform: translateY(20px); }\n to { opacity: 1; transform: translateY(0); }\n }\n\n @keyframes slideDown {\n from { opacity: 0; transform: translateY(-10px); }\n to { opacity: 1; transform: translateY(0); }\n }\n\n @keyframes spin {\n from { transform: rotate(0deg); }\n to { transform: rotate(360deg); }\n }\n\n @keyframes pulseRing {\n 0%, 100% { \n box-shadow: 0 0 0 4px rgba(16, 185, 129, 0.8), 0 0 40px rgba(16, 185, 129, 0.4);\n }\n 50% { \n box-shadow: 0 0 0 6px rgba(16, 185, 129, 0.9), 0 0 50px rgba(16, 185, 129, 0.5);\n }\n }\n\n @keyframes checkmark {\n 0% {\n stroke-dashoffset: 100;\n }\n 100% {\n stroke-dashoffset: 0;\n }\n }\n\n @keyframes scaleIn {\n 0% {\n transform: scale(0);\n opacity: 0;\n }\n 50% {\n transform: scale(1.1);\n }\n 100% {\n transform: scale(1);\n opacity: 1;\n }\n }\n\n @keyframes scanRing {\n 0%, 100% { transform: scale(1); opacity: 0.3; }\n 50% { transform: scale(1.1); opacity: 0.6; }\n }\n\n @keyframes cornerBlink {\n 0%, 100% { opacity: 0.3; }\n 50% { opacity: 1; }\n }\n\n .modal-overlay {\n position: fixed;\n inset: 0;\n background: rgba(0, 0, 0, 0.92);\n backdrop-filter: blur(8px);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 9999;\n padding: 20px;\n }\n\n .modal-content {\n background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);\n border-radius: 24px;\n width: 100%;\n max-width: 420px;\n max-height: 90vh;\n overflow: hidden;\n box-shadow: 0 24px 48px rgba(0, 0, 0, 0.4), 0 0 0 1px rgba(255, 255, 255, 0.1);\n animation: fadeIn 0.3s ease-out;\n position: relative;\n }\n\n .close-button {\n position: absolute;\n top: 20px;\n right: 20px;\n width: 32px;\n height: 32px;\n background: transparent;\n border: none;\n color: rgba(255, 255, 255, 0.6);\n font-size: 28px;\n font-weight: 300;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: all 0.2s;\n z-index: 10;\n line-height: 1;\n }\n\n .close-button:hover {\n color: rgba(255, 255, 255, 0.9);\n transform: rotate(90deg);\n }\n\n /* Instructions Screen */\n .instructions-container {\n padding: 60px 40px 40px;\n color: white;\n text-align: center;\n animation: slideUp 0.4s ease-out;\n }\n\n .instructions-header {\n margin-bottom: 40px;\n }\n\n .icon-wrapper {\n display: inline-block;\n color: #7c3aed;\n margin-bottom: 24px;\n }\n\n .icon-wrapper .scan-ring {\n animation: scanRing 2s ease-in-out infinite;\n }\n\n .icon-wrapper .corner {\n animation: cornerBlink 2s ease-in-out infinite;\n }\n\n .icon-wrapper .corner.tl { animation-delay: 0s; }\n .icon-wrapper .corner.tr { animation-delay: 0.5s; }\n .icon-wrapper .corner.bl { animation-delay: 1s; }\n .icon-wrapper .corner.br { animation-delay: 1.5s; }\n\n .instructions-header h2 {\n font-size: 28px;\n font-weight: 700;\n margin: 0 0 12px 0;\n background: linear-gradient(135deg, #7c3aed 0%, #a78bfa 100%);\n -webkit-background-clip: text;\n -webkit-text-fill-color: transparent;\n background-clip: text;\n }\n\n .subtitle {\n font-size: 15px;\n color: rgba(255, 255, 255, 0.7);\n margin: 0;\n line-height: 1.5;\n }\n\n .requirements-list {\n text-align: left;\n margin: 0 auto 40px;\n max-width: 360px;\n }\n\n .requirement-item {\n display: flex;\n align-items: center;\n gap: 16px;\n padding: 14px 0;\n font-size: 15px;\n color: rgba(255, 255, 255, 0.9);\n border-bottom: 1px solid rgba(255, 255, 255, 0.05);\n }\n\n .requirement-item:last-child {\n border-bottom: none;\n }\n\n .req-icon {\n width: 32px;\n height: 32px;\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n }\n\n .req-icon.success {\n background: rgba(16, 185, 129, 0.15);\n color: #10b981;\n border: 1.5px solid rgba(16, 185, 129, 0.3);\n }\n\n .primary-button {\n width: 100%;\n padding: 16px 32px;\n border-radius: 12px;\n border: none;\n background: linear-gradient(135deg, #7c3aed 0%, #6d28d9 100%);\n color: white;\n font-size: 16px;\n font-weight: 600;\n cursor: pointer;\n transition: all 0.3s;\n box-shadow: 0 4px 16px rgba(124, 58, 237, 0.3);\n }\n\n .primary-button:hover {\n transform: translateY(-2px);\n box-shadow: 0 8px 24px rgba(124, 58, 237, 0.4);\n }\n\n .primary-button:active {\n transform: translateY(0);\n }\n\n .branding {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n margin-top: 32px;\n padding-top: 24px;\n border-top: 1px solid rgba(255, 255, 255, 0.1);\n font-size: 13px;\n color: rgba(255, 255, 255, 0.5);\n }\n\n .brand-logo {\n height: 20px;\n width: auto;\n }\n\n .brand-name {\n font-weight: 600;\n font-family: 'SF Pro Display', -apple-system, system-ui, sans-serif;\n color: #ffffff;\n letter-spacing: 0.3px;\n }\n\n /* Liveness Detection */\n .liveness-container {\n height: 100%;\n min-height: 520px;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 70px 20px 80px;\n position: relative;\n animation: fadeIn 0.3s ease-out;\n }\n\n .progress-indicator {\n position: absolute;\n top: 20px;\n left: 50%;\n transform: translateX(-50%);\n background: rgba(255, 255, 255, 0.08);\n backdrop-filter: blur(12px);\n border-radius: 20px;\n padding: 8px 20px;\n border: 1px solid rgba(255, 255, 255, 0.12);\n z-index: 5;\n }\n\n .progress-text {\n font-size: 14px;\n font-weight: 600;\n color: white;\n }\n\n .video-wrapper {\n position: relative;\n margin: 20px 0;\n }\n\n .video-circle {\n width: 320px;\n height: 400px;\n border-radius: 50%;\n overflow: hidden;\n position: relative;\n transition: all 0.3s ease;\n box-shadow: 0 0 0 4px rgba(124, 58, 237, 0.3);\n }\n\n .video-circle.face-detected {\n box-shadow: 0 0 0 4px rgba(16, 185, 129, 0.5), 0 0 40px rgba(16, 185, 129, 0.3);\n }\n\n .video-circle.capturing {\n box-shadow: 0 0 0 4px rgba(16, 185, 129, 0.9), 0 0 40px rgba(16, 185, 129, 0.5);\n transition: box-shadow 0.3s ease;\n }\n\n .video-feed {\n width: 100%;\n height: 100%;\n object-fit: cover;\n transform: scaleX(-1);\n }\n\n .face-badge {\n position: absolute;\n top: 20px;\n left: 50%;\n transform: translateX(-50%);\n background: rgba(16, 185, 129, 0.95);\n backdrop-filter: blur(10px);\n border-radius: 20px;\n padding: 8px 16px;\n display: flex;\n align-items: center;\n gap: 8px;\n color: white;\n font-size: 13px;\n font-weight: 600;\n border: 1px solid rgba(255, 255, 255, 0.3);\n box-shadow: 0 4px 12px rgba(16, 185, 129, 0.3);\n animation: slideDown 0.3s ease-out;\n }\n\n .face-badge.warning {\n background: rgba(251, 191, 36, 0.95);\n border: 1px solid rgba(255, 255, 255, 0.3);\n box-shadow: 0 4px 12px rgba(251, 191, 36, 0.3);\n color: #78350f;\n }\n\n .face-badge.error {\n background: rgba(239, 68, 68, 0.95);\n border: 1px solid rgba(255, 255, 255, 0.3);\n box-shadow: 0 4px 12px rgba(239, 68, 68, 0.3);\n color: white;\n }\n\n .instruction-box {\n background: rgba(255, 255, 255, 0.08);\n backdrop-filter: blur(12px);\n border-radius: 16px;\n padding: 16px 24px;\n margin-top: 20px;\n border: 1px solid rgba(255, 255, 255, 0.12);\n max-width: 320px;\n animation: slideUp 0.3s ease-out;\n }\n\n .instruction-text {\n font-size: 18px;\n font-weight: 600;\n color: white;\n margin: 0 0 12px 0;\n text-align: center;\n }\n\n .progress-bar {\n height: 4px;\n background: rgba(255, 255, 255, 0.2);\n border-radius: 2px;\n overflow: hidden;\n }\n\n .progress-fill {\n height: 100%;\n background: linear-gradient(90deg, #7c3aed 0%, #10b981 100%);\n border-radius: 2px;\n transition: width 0.05s linear;\n }\n\n .branding-small {\n position: absolute;\n bottom: 24px;\n left: 50%;\n transform: translateX(-50%);\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 5px;\n font-size: 12px;\n color: rgba(255, 255, 255, 0.5);\n }\n\n .brand-logo-small {\n height: 18px;\n width: 18px;\n }\n\n /* Processing */\n .processing-container {\n padding: 80px 40px;\n text-align: center;\n color: white;\n animation: fadeIn 0.3s ease-out;\n }\n\n .spinner-wrapper {\n position: relative;\n width: 80px;\n height: 80px;\n margin: 0 auto 32px;\n }\n\n .spinner {\n position: absolute;\n inset: 0;\n border: 4px solid rgba(124, 58, 237, 0.2);\n border-top-color: #7c3aed;\n border-radius: 50%;\n animation: spin 1s linear infinite;\n }\n\n .spinner-ring {\n position: absolute;\n inset: -10px;\n border: 2px solid rgba(124, 58, 237, 0.1);\n border-radius: 50%;\n animation: pulseSpinner 2s ease-in-out infinite;\n }\n\n @keyframes pulseSpinner {\n 0%, 100% { transform: scale(1); opacity: 1; }\n 50% { transform: scale(1.1); opacity: 0.5; }\n }\n\n .processing-container h3 {\n font-size: 24px;\n font-weight: 700;\n margin: 0 0 12px 0;\n }\n\n .processing-container p {\n font-size: 15px;\n color: rgba(255, 255, 255, 0.7);\n margin: 0;\n }\n\n /* Result Screens */\n .result-container {\n padding: 80px 40px 40px;\n text-align: center;\n color: white;\n animation: slideUp 0.4s ease-out;\n }\n\n .result-icon {\n display: inline-flex;\n margin-bottom: 24px;\n }\n\n .success-icon {\n color: #10b981;\n animation: scaleIn 0.6s ease-out;\n }\n\n .success-icon svg circle {\n animation: scaleIn 0.5s ease-out;\n }\n\n .success-icon svg path {\n stroke-dasharray: 100;\n stroke-dashoffset: 100;\n animation: checkmark 0.6s ease-out 0.3s forwards;\n }\n\n .error-icon {\n color: #ef4444;\n animation: fadeIn 0.5s ease-out;\n }\n\n .result-container h3 {\n font-size: 26px;\n font-weight: 700;\n margin: 0 0 12px 0;\n }\n\n .result-container p {\n font-size: 15px;\n color: rgba(255, 255, 255, 0.7);\n margin: 0 0 32px 0;\n line-height: 1.5;\n }\n\n .button-group {\n display: flex;\n gap: 12px;\n margin-top: 32px;\n }\n\n .secondary-button {\n flex: 1;\n padding: 14px 24px;\n border-radius: 12px;\n border: 1px solid rgba(255, 255, 255, 0.2);\n background: rgba(255, 255, 255, 0.1);\n color: white;\n font-size: 15px;\n font-weight: 600;\n cursor: pointer;\n transition: all 0.2s;\n }\n\n .secondary-button:hover {\n background: rgba(255, 255, 255, 0.15);\n border-color: rgba(255, 255, 255, 0.3);\n }\n\n .ghost-button {\n flex: 1;\n padding: 14px 24px;\n border-radius: 12px;\n border: none;\n background: transparent;\n color: rgba(255, 255, 255, 0.7);\n font-size: 15px;\n font-weight: 600;\n cursor: pointer;\n transition: all 0.2s;\n }\n\n .ghost-button:hover {\n color: white;\n background: rgba(255, 255, 255, 0.05);\n }\n\n /* Canvas */\n canvas {\n display: none;\n }\n\n /* Responsive */\n @media (max-width: 640px) {\n .modal-content {\n max-width: 100%;\n border-radius: 0;\n max-height: 100vh;\n }\n\n .instructions-container {\n padding: 50px 24px 24px;\n }\n\n .instructions-header h2 {\n font-size: 24px;\n }\n\n .video-circle {\n width: 280px;\n height: 350px;\n }\n\n .liveness-container {\n min-height: 100vh;\n }\n }\n `}</style>\n\n <div className=\"modal-overlay\">\n <div className=\"modal-content\">\n <button className=\"close-button\" onClick={onClose}>\n ×\n </button>\n\n {state.status === 'liveness' && renderLivenessDetection()}\n {state.status === 'processing' && renderProcessing()}\n {state.status === 'error' && renderError()}\n {state.status === 'success' && renderSuccess()}\n </div>\n </div>\n\n <canvas ref={canvasRef} />\n </>\n );\n}\n","export type OverlayStatus = 'preparing' | 'verifying' | 'success' | 'error';\n\n/**\n * Overlay minimalista estilo FaceID para login biométrico\n * Implementação vanilla JS (sem React) para evitar conflitos de versão\n */\nexport class BiometricStatusOverlay {\n private container: HTMLDivElement | null = null;\n private currentStatus: OverlayStatus = 'preparing';\n\n private getStyles(): string {\n return `\n @keyframes neofaceid-fadeIn {\n from { opacity: 0; transform: scale(0.9); }\n to { opacity: 1; transform: scale(1); }\n }\n\n @keyframes neofaceid-fadeOut {\n from { opacity: 1; transform: scale(1); }\n to { opacity: 0; transform: scale(0.9); }\n }\n\n @keyframes neofaceid-pulse {\n 0%, 100% { \n transform: scale(1);\n opacity: 1;\n }\n 50% { \n transform: scale(1.1);\n opacity: 0.9;\n }\n }\n\n @keyframes neofaceid-checkmark {\n 0% {\n stroke-dashoffset: 100;\n opacity: 0;\n }\n 50% {\n opacity: 1;\n }\n 100% {\n stroke-dashoffset: 0;\n opacity: 1;\n }\n }\n\n @keyframes neofaceid-errorShake {\n 0%, 100% { transform: translateX(0) scale(1); }\n 10%, 30%, 50%, 70%, 90% { transform: translateX(-4px) scale(1); }\n 20%, 40%, 60%, 80% { transform: translateX(4px) scale(1); }\n }\n\n @keyframes neofaceid-scaleIn {\n 0% {\n transform: scale(0);\n opacity: 0;\n }\n 50% {\n transform: scale(1.1);\n }\n 100% {\n transform: scale(1);\n opacity: 1;\n }\n }\n\n .neofaceid-overlay-container {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 10000;\n pointer-events: none;\n animation: neofaceid-fadeIn 0.3s ease-out;\n }\n\n .neofaceid-overlay-container.fade-out {\n animation: neofaceid-fadeOut 0.3s ease-out forwards;\n }\n\n .neofaceid-overlay-content {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 24px;\n pointer-events: auto;\n }\n\n .neofaceid-logo-wrapper {\n position: relative;\n width: 120px;\n height: 120px;\n display: flex;\n align-items: center;\n justify-content: center;\n }\n\n .neofaceid-logo {\n width: 80px;\n height: 80px;\n object-fit: contain;\n filter: drop-shadow(0 4px 12px rgba(124, 58, 237, 0.3));\n }\n\n .neofaceid-logo.pulsing {\n animation: neofaceid-pulse 2s ease-in-out infinite;\n }\n\n .neofaceid-status-icon {\n position: absolute;\n inset: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n }\n\n .neofaceid-success-icon {\n width: 60px;\n height: 60px;\n color: #10b981;\n animation: neofaceid-scaleIn 0.6s ease-out;\n }\n\n .neofaceid-error-icon {\n width: 60px;\n height: 60px;\n color: #ef4444;\n animation: neofaceid-errorShake 0.5s ease-out, neofaceid-scaleIn 0.4s ease-out;\n }\n\n @media (max-width: 640px) {\n .neofaceid-logo-wrapper {\n width: 100px;\n height: 100px;\n }\n\n .neofaceid-logo {\n width: 70px;\n height: 70px;\n }\n }\n `;\n }\n\n private renderContent(): string {\n const isPulsing = this.currentStatus === 'verifying';\n const showLogo = this.currentStatus === 'preparing' || this.currentStatus === 'verifying';\n const showSuccess = this.currentStatus === 'success';\n const showError = this.currentStatus === 'error';\n\n return `\n <style>${this.getStyles()}</style>\n <div class=\"neofaceid-overlay-container\">\n <div class=\"neofaceid-overlay-content\">\n <div class=\"neofaceid-logo-wrapper\">\n ${showLogo ? `\n <img\n src=\"/logo-icone-no-background.png\"\n alt=\"NeoFaceId\"\n class=\"neofaceid-logo ${isPulsing ? 'pulsing' : ''}\"\n onerror=\"this.style.display='none'\"\n />\n ` : ''}\n \n ${showSuccess ? `\n <div class=\"neofaceid-status-icon\">\n <svg class=\"neofaceid-success-icon\" viewBox=\"0 0 60 60\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <circle cx=\"30\" cy=\"30\" r=\"28\" stroke=\"currentColor\" stroke-width=\"3\" fill=\"none\"/>\n <path d=\"M15 30 L25 40 L45 20\" stroke=\"currentColor\" stroke-width=\"3\" stroke-linecap=\"round\" stroke-linejoin=\"round\" fill=\"none\"/>\n </svg>\n </div>\n ` : ''}\n \n ${showError ? `\n <div class=\"neofaceid-status-icon\">\n <svg class=\"neofaceid-error-icon\" viewBox=\"0 0 60 60\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <circle cx=\"30\" cy=\"30\" r=\"28\" stroke=\"currentColor\" stroke-width=\"3\" fill=\"none\"/>\n <path d=\"M20 20 L40 40 M40 20 L20 40\" stroke=\"currentColor\" stroke-width=\"3\" stroke-linecap=\"round\"/>\n </svg>\n </div>\n ` : ''}\n </div>\n </div>\n </div>\n `;\n }\n\n /**\n * Cria e exibe o overlay\n */\n public show(status: OverlayStatus = 'preparing'): void {\n if (this.container) {\n this.updateStatus(status);\n return;\n }\n\n this.container = document.createElement('div');\n this.container.id = 'neofaceid-status-overlay';\n this.currentStatus = status;\n this.container.innerHTML = this.renderContent();\n document.body.appendChild(this.container);\n }\n\n /**\n * Atualiza o status do overlay\n */\n public updateStatus(status: OverlayStatus): void {\n this.currentStatus = status;\n if (this.container) {\n this.container.innerHTML = this.renderContent();\n }\n }\n\n /**\n * Fecha e remove o overlay\n */\n public close(): void {\n if (this.container) {\n const overlayElement = this.container.querySelector('.neofaceid-overlay-container');\n if (overlayElement) {\n overlayElement.classList.add('fade-out');\n }\n\n setTimeout(() => {\n if (this.container && document.body.contains(this.container)) {\n document.body.removeChild(this.container);\n }\n this.container = null;\n }, 300);\n }\n }\n}\n","import React from 'react';\nimport { createRoot } from 'react-dom/client';\nimport { NeoFaceError } from '../errors';\n\ninterface FallbackPromptProps {\n error?: NeoFaceError;\n onRetry: () => void;\n onCredentials: () => void;\n onCancel?: () => void;\n}\n\n/**\n * Prompt de fallback após múltiplas tentativas falhadas\n * - Oferece \"Tentar novamente\" e \"Entrar com email e senha\"\n * - Design minimalista e clean\n */\nfunction FallbackPromptComponent({\n error,\n onRetry,\n onCredentials,\n onCancel,\n}: FallbackPromptProps) {\n return (\n <>\n <style>{`\n @keyframes slideUp {\n from {\n opacity: 0;\n transform: translateY(20px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n }\n\n @keyframes fadeIn {\n from { opacity: 0; }\n to { opacity: 1; }\n }\n\n .neofaceid-fallback-overlay {\n position: fixed;\n inset: 0;\n background: rgba(0, 0, 0, 0.6);\n backdrop-filter: blur(8px);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 10001;\n padding: 20px;\n animation: fadeIn 0.3s ease-out;\n }\n\n .neofaceid-fallback-prompt {\n background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);\n border-radius: 20px;\n padding: 32px;\n max-width: 400px;\n width: 100%;\n box-shadow: 0 24px 48px rgba(0, 0, 0, 0.4), 0 0 0 1px rgba(255, 255, 255, 0.1);\n animation: slideUp 0.4s ease-out;\n text-align: center;\n }\n\n .neofaceid-fallback-icon {\n width: 64px;\n height: 64px;\n margin: 0 auto 20px;\n color: #ef4444;\n animation: scaleIn 0.4s ease-out;\n }\n\n @keyframes scaleIn {\n 0% {\n transform: scale(0);\n opacity: 0;\n }\n 50% {\n transform: scale(1.1);\n }\n 100% {\n transform: scale(1);\n opacity: 1;\n }\n }\n\n .neofaceid-fallback-title {\n font-size: 22px;\n font-weight: 700;\n color: white;\n margin: 0 0 12px 0;\n }\n\n .neofaceid-fallback-message {\n font-size: 15px;\n color: rgba(255, 255, 255, 0.7);\n margin: 0 0 28px 0;\n line-height: 1.5;\n }\n\n .neofaceid-fallback-actions {\n display: flex;\n flex-direction: column;\n gap: 12px;\n }\n\n .neofaceid-fallback-button {\n padding: 14px 24px;\n border-radius: 12px;\n border: none;\n font-size: 16px;\n font-weight: 600;\n cursor: pointer;\n transition: all 0.2s;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n }\n\n .neofaceid-fallback-button-primary {\n background: linear-gradient(135deg, #7c3aed 0%, #6d28d9 100%);\n color: white;\n box-shadow: 0 4px 16px rgba(124, 58, 237, 0.3);\n }\n\n .neofaceid-fallback-button-primary:hover {\n transform: translateY(-2px);\n box-shadow: 0 8px 24px rgba(124, 58, 237, 0.4);\n }\n\n .neofaceid-fallback-button-primary:active {\n transform: translateY(0);\n }\n\n .neofaceid-fallback-button-secondary {\n background: rgba(255, 255, 255, 0.1);\n color: white;\n border: 1px solid rgba(255, 255, 255, 0.2);\n }\n\n .neofaceid-fallback-button-secondary:hover {\n background: rgba(255, 255, 255, 0.15);\n border-color: rgba(255, 255, 255, 0.3);\n }\n\n .neofaceid-fallback-button-ghost {\n background: transparent;\n color: rgba(255, 255, 255, 0.7);\n padding: 10px;\n }\n\n .neofaceid-fallback-button-ghost:hover {\n color: white;\n background: rgba(255, 255, 255, 0.05);\n }\n\n @media (max-width: 640px) {\n .neofaceid-fallback-prompt {\n padding: 24px;\n border-radius: 16px;\n }\n\n .neofaceid-fallback-title {\n font-size: 20px;\n }\n }\n `}</style>\n\n <div className=\"neofaceid-fallback-overlay\">\n <div className=\"neofaceid-fallback-prompt\">\n <div className=\"neofaceid-fallback-icon\">\n <svg viewBox=\"0 0 64 64\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <circle cx=\"32\" cy=\"32\" r=\"30\" stroke=\"currentColor\" strokeWidth=\"3\" fill=\"none\" />\n <path\n d=\"M20 20 L44 44 M44 20 L20 44\"\n stroke=\"currentColor\"\n strokeWidth=\"3\"\n strokeLinecap=\"round\"\n />\n </svg>\n </div>\n\n <h3 className=\"neofaceid-fallback-title\">Não foi possível reconhecer</h3>\n <p className=\"neofaceid-fallback-message\">\n {error?.message || 'Não conseguimos reconhecer sua face. Tente novamente ou use email e senha.'}\n </p>\n\n <div className=\"neofaceid-fallback-actions\">\n <button\n type=\"button\"\n className=\"neofaceid-fallback-button neofaceid-fallback-button-primary\"\n onClick={onRetry}\n >\n Tentar Novamente\n </button>\n <button\n type=\"button\"\n className=\"neofaceid-fallback-button neofaceid-fallback-button-secondary\"\n onClick={onCredentials}\n >\n Entrar com Email e Senha\n </button>\n {onCancel && (\n <button\n type=\"button\"\n className=\"neofaceid-fallback-button neofaceid-fallback-button-ghost\"\n onClick={onCancel}\n >\n Cancelar\n </button>\n )}\n </div>\n </div>\n </div>\n </>\n );\n}\n\n/**\n * Classe para gerenciar o prompt de fallback\n */\nexport class FallbackPrompt {\n private container: HTMLDivElement | null = null;\n private root: ReturnType<typeof createRoot> | null = null;\n\n /**\n * Exibe o prompt de fallback\n */\n public show(\n error: NeoFaceError | undefined,\n onRetry: () => void,\n onCredentials: () => void,\n onCancel?: () => void\n ): void {\n if (this.container) {\n return; // Já está aberto\n }\n\n this.container = document.createElement('div');\n this.container.id = 'neofaceid-fallback-prompt';\n document.body.appendChild(this.container);\n\n this.root = createRoot(this.container);\n this.render(error, onRetry, onCredentials, onCancel);\n }\n\n /**\n * Fecha o prompt\n */\n public close(): void {\n if (this.container && document.body.contains(this.container)) {\n if (this.root) {\n this.root.unmount();\n }\n document.body.removeChild(this.container);\n this.container = null;\n this.root = null;\n }\n }\n\n /**\n * Renderiza o componente\n */\n private render(\n error: NeoFaceError | undefined,\n onRetry: () => void,\n onCredentials: () => void,\n onCancel?: () => void\n ): void {\n if (!this.root || !this.container) return;\n\n this.root.render(\n <FallbackPromptComponent\n error={error}\n onRetry={() => {\n this.close();\n onRetry();\n }}\n onCredentials={() => {\n this.close();\n onCredentials();\n }}\n onCancel={() => {\n this.close();\n onCancel?.();\n }}\n />\n );\n }\n}\n\n","import { NeoFaceError, ErrorType } from './errors';\nimport { loginWithEmail, BiometricLoginResult } from './api';\n\nexport interface EmailPasswordOptions {\n applicationToken: string;\n onSuccess: (result: BiometricLoginResult) => void;\n onError: (error: NeoFaceError) => void;\n onCancel?: () => void;\n title?: string;\n subtitle?: string;\n}\n\n/**\n * Modal para entrada de email e senha como fallback para login.\n */\nexport class EmailPasswordModal {\n private options: EmailPasswordOptions;\n private modalElement: HTMLDivElement | null = null;\n\n constructor(options: EmailPasswordOptions) {\n this.options = options;\n }\n\n /**\n * Abre o modal de login com email e senha.\n */\n public async open(): Promise<void> {\n this.createModal();\n document.body.appendChild(this.modalElement!);\n }\n\n /**\n * Fecha o modal.\n */\n public close(): void {\n if (this.modalElement) {\n document.body.removeChild(this.modalElement);\n this.modalElement = null;\n }\n if (this.options.onCancel) {\n this.options.onCancel();\n }\n }\n\n private createModal(): void {\n this.addStyles();\n\n this.modalElement = document.createElement('div');\n this.modalElement.className = 'neoface-email-password-modal';\n this.modalElement.innerHTML = `\n <div class=\"neoface-email-modal-overlay\">\n <div class=\"neoface-email-modal-content\">\n <h2>${this.options.title || 'Login com Email e Senha'}</h2>\n <p>${this.options.subtitle || 'Informe seus dados para login.'}</p>\n <form id=\"email-password-form\">\n <input type=\"email\" id=\"email\" placeholder=\"Email\" required />\n <input type=\"password\" id=\"password\" placeholder=\"Senha\" required />\n <button type=\"submit\" class=\"neoface-email-submit-btn\">Entrar</button>\n </form>\n <button class=\"neoface-email-cancel-btn\">Cancelar</button>\n \n <!-- Branding -->\n <div class=\"neoface-email-branding\">\n <img src=\"/logo-icone-no-background.png\" alt=\"NeoFaceId\" class=\"neoface-email-brand-logo\" onerror=\"this.style.display='none'\" />\n <span>NeoFaceId by</span>\n <span class=\"neoface-email-brand-name\">OCTA</span>\n </div>\n </div>\n </div>\n `;\n\n const form = this.modalElement.querySelector('#email-password-form') as HTMLFormElement;\n form.addEventListener('submit', this.handleSubmit.bind(this));\n\n const cancelButton = this.modalElement.querySelector(\n '.neoface-email-cancel-btn'\n ) as HTMLButtonElement;\n cancelButton.addEventListener('click', this.close.bind(this));\n }\n\n private addStyles(): void {\n const styleId = 'neoface-email-password-modal-styles';\n\n // Remove estilos existentes se houver\n const existingStyles = document.getElementById(styleId);\n if (existingStyles) {\n return; // Já existe, não precisa adicionar novamente\n }\n\n const style = document.createElement('style');\n style.id = styleId;\n style.textContent = `\n .neoface-email-password-modal {\n position: fixed;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n z-index: 10002;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n }\n\n .neoface-email-modal-overlay {\n position: absolute;\n inset: 0;\n background: rgba(0, 0, 0, 0.6);\n backdrop-filter: blur(8px);\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 20px;\n animation: fadeIn 0.3s ease-out;\n }\n\n @keyframes fadeIn {\n from { opacity: 0; }\n to { opacity: 1; }\n }\n\n @keyframes slideUp {\n from {\n opacity: 0;\n transform: translateY(20px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n }\n\n .neoface-email-modal-content {\n background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);\n border-radius: 20px;\n padding: 32px;\n max-width: 400px;\n width: 100%;\n box-shadow: 0 24px 48px rgba(0, 0, 0, 0.4), 0 0 0 1px rgba(255, 255, 255, 0.1);\n animation: slideUp 0.4s ease-out;\n }\n\n .neoface-email-modal-content h2 {\n font-size: 24px;\n font-weight: 700;\n color: white;\n margin: 0 0 12px 0;\n text-align: center;\n }\n\n .neoface-email-modal-content p {\n font-size: 15px;\n color: rgba(255, 255, 255, 0.7);\n margin: 0 0 24px 0;\n text-align: center;\n line-height: 1.5;\n }\n\n .neoface-email-modal-content form {\n display: flex;\n flex-direction: column;\n gap: 16px;\n margin-bottom: 16px;\n }\n\n .neoface-email-modal-content input {\n width: 100%;\n padding: 14px 16px;\n border: 2px solid rgba(255, 255, 255, 0.2);\n border-radius: 12px;\n font-size: 15px;\n background: rgba(255, 255, 255, 0.1);\n color: white;\n font-family: inherit;\n transition: all 0.2s;\n box-sizing: border-box;\n }\n\n .neoface-email-modal-content input::placeholder {\n color: rgba(255, 255, 255, 0.5);\n }\n\n .neoface-email-modal-content input:focus {\n outline: none;\n border-color: #7c3aed;\n box-shadow: 0 0 0 3px rgba(124, 58, 237, 0.2);\n background: rgba(255, 255, 255, 0.15);\n }\n\n .neoface-email-submit-btn {\n width: 100%;\n padding: 14px 24px;\n border-radius: 12px;\n border: none;\n background: linear-gradient(135deg, #7c3aed 0%, #6d28d9 100%);\n color: white;\n font-size: 16px;\n font-weight: 600;\n cursor: pointer;\n transition: all 0.2s;\n box-shadow: 0 4px 16px rgba(124, 58, 237, 0.3);\n font-family: inherit;\n }\n\n .neoface-email-submit-btn:hover {\n transform: translateY(-2px);\n box-shadow: 0 8px 24px rgba(124, 58, 237, 0.4);\n }\n\n .neoface-email-submit-btn:active {\n transform: translateY(0);\n }\n\n .neoface-email-submit-btn:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n transform: none;\n }\n\n .neoface-email-cancel-btn {\n width: 100%;\n padding: 12px 24px;\n border-radius: 12px;\n border: 1px solid rgba(255, 255, 255, 0.2);\n background: transparent;\n color: rgba(255, 255, 255, 0.7);\n font-size: 15px;\n font-weight: 600;\n cursor: pointer;\n transition: all 0.2s;\n font-family: inherit;\n }\n\n .neoface-email-cancel-btn:hover {\n background: rgba(255, 255, 255, 0.1);\n color: white;\n border-color: rgba(255, 255, 255, 0.3);\n }\n\n .neoface-email-branding {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n margin-top: 32px;\n padding-top: 24px;\n border-top: 1px solid rgba(255, 255, 255, 0.1);\n font-size: 13px;\n color: rgba(255, 255, 255, 0.5);\n }\n\n .neoface-email-brand-logo {\n height: 20px;\n width: auto;\n }\n\n .neoface-email-brand-name {\n font-weight: 600;\n font-family: 'SF Pro Display', -apple-system, system-ui, sans-serif;\n color: #ffffff;\n letter-spacing: 0.3px;\n }\n\n @media (max-width: 640px) {\n .neoface-email-modal-content {\n padding: 24px;\n border-radius: 16px;\n }\n\n .neoface-email-modal-content h2 {\n font-size: 20px;\n }\n }\n `;\n\n document.head.appendChild(style);\n }\n\n private async handleSubmit(event: Event): Promise<void> {\n event.preventDefault();\n const emailInput = this.modalElement!.querySelector('#email') as HTMLInputElement;\n const passwordInput = this.modalElement!.querySelector('#password') as HTMLInputElement;\n\n const email = emailInput.value.trim();\n const password = passwordInput.value.trim();\n\n if (!email || !password) {\n this.options.onError(\n new NeoFaceError('Email e senha são obrigatórios', ErrorType.VALIDATION_ERROR)\n );\n return;\n }\n\n try {\n const result = await loginWithEmail(email, password, this.options.applicationToken);\n this.options.onSuccess(result);\n this.close();\n } catch (error) {\n this.options.onError(error as NeoFaceError);\n }\n }\n}\n","import { BiometricStatusOverlay } from '../components/BiometricStatusOverlay';\nimport { FallbackPrompt } from '../components/FallbackPrompt';\nimport { loginWithBiometric, BiometricLoginResult } from '../api';\nimport { NeoFaceError, ErrorType } from '../errors';\nimport * as faceapi from 'face-api.js';\n\nconst MAX_ATTEMPTS = 2;\nconst RETRY_DELAY = 500; // ms entre tentativas\n\n// ============================================\n// OTIMIZAÇÕES DE PERFORMANCE v1.7.1\n// ============================================\n// Delays otimizados para login rápido (~2-3s)\nconst CAMERA_READY_DELAY = 200; // Era 800ms\nconst CAMERA_STABILITY_DELAY = 100; // Era 500ms\nconst FACE_DETECTION_TIME = 5000; // 5 segundos para encontrar rosto\nconst DETECTION_INTERVAL = 100; // Era 200ms\nconst SUCCESS_DELAY = 200; // Era 800ms\nconst INITIAL_DELAY = 0; // Era 300ms\n// ============================================\n\ninterface BiometricLoginFlowOptions {\n applicationToken: string;\n onSuccess: (result: BiometricLoginResult) => void;\n onError: (error: NeoFaceError) => void;\n onFallbackRequest?: () => void;\n onCancel?: () => void;\n /** Modo rápido: captura assim que detecta rosto, sem esperar tempo fixo */\n fastMode?: boolean;\n}\n\n// Cache para modelos do face-api.js\nlet modelsLoaded = false;\nlet modelsLoading: Promise<void> | null = null;\n\n/**\n * Pré-carrega modelos do face-api.js (chame no início da aplicação)\n */\nexport async function preloadFaceDetectionModels(): Promise<void> {\n if (modelsLoaded) return;\n if (modelsLoading) return modelsLoading;\n\n modelsLoading = (async () => {\n try {\n const MODEL_URL = 'https://cdn.jsdelivr.net/npm/@vladmandic/face-api/model';\n await faceapi.nets.tinyFaceDetector.loadFromUri(MODEL_URL);\n modelsLoaded = true;\n console.log('✅ Face detection models preloaded');\n } catch (error) {\n console.warn('⚠️ Face-api.js models not loaded:', error);\n }\n })();\n\n return modelsLoading;\n}\n\n/**\n * Captura uma imagem da câmera sem mostrar o vídeo\n * @returns Promise that resolves to a Blob of the captured image\n */\nexport async function captureFaceSilently(): Promise<Blob> {\n return new Promise((resolve, reject) => {\n const video = document.createElement('video');\n video.style.position = 'fixed';\n video.style.top = '-9999px';\n video.style.left = '-9999px';\n video.style.width = '1px';\n video.style.height = '1px';\n video.style.opacity = '0';\n video.setAttribute('autoplay', '');\n video.setAttribute('muted', '');\n video.setAttribute('playsinline', '');\n\n const canvas = document.createElement('canvas');\n canvas.style.display = 'none';\n\n let stream: MediaStream | null = null;\n let faceDetectionAttempts = 0;\n const MAX_FACE_DETECTION_ATTEMPTS = 10;\n\n const cleanup = () => {\n if (stream) {\n stream.getTracks().forEach(track => track.stop());\n }\n if (video.parentElement) {\n video.parentElement.removeChild(video);\n }\n if (canvas.parentElement) {\n canvas.parentElement.removeChild(canvas);\n }\n };\n\n // Adiciona elementos ao DOM (mas invisíveis)\n document.body.appendChild(video);\n document.body.appendChild(canvas);\n\n navigator.mediaDevices\n .getUserMedia({\n video: {\n width: { ideal: 640 },\n height: { ideal: 480 },\n facingMode: 'user',\n },\n audio: false,\n })\n .then(mediaStream => {\n stream = mediaStream;\n video.srcObject = stream;\n video.play();\n\n const tryCapture = () => {\n if (!video.videoWidth || !video.videoHeight) {\n if (faceDetectionAttempts < MAX_FACE_DETECTION_ATTEMPTS) {\n faceDetectionAttempts++;\n setTimeout(tryCapture, 100);\n return;\n }\n cleanup();\n reject(new NeoFaceError('Câmera não está pronta', ErrorType.CAMERA_ERROR));\n return;\n }\n\n canvas.width = video.videoWidth;\n canvas.height = video.videoHeight;\n\n const ctx = canvas.getContext('2d');\n if (!ctx) {\n cleanup();\n reject(new Error('Failed to get canvas context'));\n return;\n }\n\n // Tenta detectar face (opcional - não bloqueia se falhar)\n const detectFace = async () => {\n if (!modelsLoaded) return true; // Se modelos não carregaram, continua\n try {\n const detection = await faceapi.detectSingleFace(\n video,\n new faceapi.TinyFaceDetectorOptions({ inputSize: 160, scoreThreshold: 0.3 })\n );\n return !!detection;\n } catch {\n return true; // Se falhar, continua mesmo assim\n }\n };\n\n detectFace().then(hasFace => {\n // Aguarda um pouco mais se não detectou face\n if (!hasFace && faceDetectionAttempts < MAX_FACE_DETECTION_ATTEMPTS) {\n faceDetectionAttempts++;\n setTimeout(tryCapture, 100);\n return;\n }\n\n // Captura a imagem\n ctx.drawImage(video, 0, 0, canvas.width, canvas.height);\n\n canvas.toBlob(\n blob => {\n cleanup();\n if (blob) {\n resolve(blob);\n } else {\n reject(new Error('Failed to capture image'));\n }\n },\n 'image/jpeg',\n 0.85\n );\n });\n };\n\n video.onloadedmetadata = () => {\n // Delay otimizado\n setTimeout(tryCapture, CAMERA_READY_DELAY);\n };\n\n video.onerror = () => {\n cleanup();\n reject(new NeoFaceError('Erro ao acessar câmera', ErrorType.CAMERA_ERROR));\n };\n })\n .catch(error => {\n cleanup();\n reject(\n new NeoFaceError(\n 'Não foi possível acessar a câmera: ' + error.message,\n ErrorType.CAMERA_ERROR\n )\n );\n });\n });\n}\n\n/**\n * Detecta rosto rapidamente - retorna assim que detectar\n * Versão otimizada para login rápido\n */\nasync function detectFaceQuickly(\n video: HTMLVideoElement,\n maxTime: number = FACE_DETECTION_TIME,\n interval: number = DETECTION_INTERVAL\n): Promise<boolean> {\n return new Promise((resolve) => {\n let attempts = 0;\n const maxAttempts = Math.ceil(maxTime / interval);\n let timeoutId: number | null = null;\n let isResolved = false;\n\n const finish = (success: boolean) => {\n if (isResolved) return;\n isResolved = true;\n if (timeoutId) clearTimeout(timeoutId);\n resolve(success);\n };\n\n const checkFace = async () => {\n if (isResolved) return;\n\n attempts++;\n\n // Se modelos não carregaram, assume que tem rosto e deixa backend validar\n if (!modelsLoaded) {\n finish(true);\n return;\n }\n\n try {\n const detection = await faceapi.detectSingleFace(\n video,\n new faceapi.TinyFaceDetectorOptions({ inputSize: 160, scoreThreshold: 0.3 })\n );\n\n // FAST MODE: Detectou rosto? Captura imediatamente!\n if (detection) {\n finish(true);\n return;\n }\n\n // Continua tentando até o limite\n if (attempts >= maxAttempts) {\n finish(false);\n return;\n }\n\n timeoutId = window.setTimeout(checkFace, interval);\n } catch (error) {\n // Em caso de erro, assume que tem rosto e deixa backend validar\n finish(true);\n }\n };\n\n // Timeout de segurança\n window.setTimeout(() => {\n if (!isResolved) {\n // Timeout: assume que tem rosto e deixa backend decidir\n finish(true);\n }\n }, maxTime + 500);\n\n // Inicia verificação imediatamente\n checkFace();\n });\n}\n\n/**\n * Executa uma tentativa de login biométrico - VERSÃO OTIMIZADA\n */\nasync function attemptLogin(\n applicationToken: string,\n overlay: BiometricStatusOverlay,\n fastMode: boolean = false\n): Promise<BiometricLoginResult> {\n // Atualiza overlay para \"verifying\"\n overlay.updateStatus('verifying');\n\n // Cria vídeo invisível para captura\n const video = document.createElement('video');\n video.style.position = 'fixed';\n video.style.top = '-9999px';\n video.style.left = '-9999px';\n video.style.width = '1px';\n video.style.height = '1px';\n video.style.opacity = '0';\n video.setAttribute('autoplay', '');\n video.setAttribute('muted', '');\n video.setAttribute('playsinline', '');\n document.body.appendChild(video);\n\n let stream: MediaStream | null = null;\n\n try {\n // Inicializa câmera\n stream = await navigator.mediaDevices.getUserMedia({\n video: {\n width: { ideal: 640 },\n height: { ideal: 480 },\n facingMode: 'user',\n },\n audio: false,\n });\n\n video.srcObject = stream;\n await video.play();\n\n // Aguarda câmera estar pronta\n await new Promise((resolve) => {\n if (video.readyState >= 2) {\n resolve(undefined);\n } else {\n video.onloadedmetadata = () => resolve(undefined);\n }\n });\n\n // Delay de estabilidade OTIMIZADO\n await new Promise((resolve) => setTimeout(resolve, CAMERA_STABILITY_DELAY));\n\n // Detecta rosto rapidamente (ou pula em fastMode)\n let faceDetected = true;\n if (!fastMode) {\n faceDetected = await detectFaceQuickly(video);\n }\n\n if (!faceDetected) {\n // Limpa recursos antes de lançar erro\n if (stream) {\n stream.getTracks().forEach(track => track.stop());\n }\n if (video.parentElement) {\n video.parentElement.removeChild(video);\n }\n throw new NeoFaceError('Rosto não detectado. Posicione seu rosto na frente da câmera.', ErrorType.VALIDATION_ERROR);\n }\n\n // Captura imagem imediatamente\n const canvas = document.createElement('canvas');\n canvas.width = video.videoWidth;\n canvas.height = video.videoHeight;\n const ctx = canvas.getContext('2d');\n if (!ctx) {\n if (stream) {\n stream.getTracks().forEach(track => track.stop());\n }\n if (video.parentElement) {\n video.parentElement.removeChild(video);\n }\n throw new NeoFaceError('Erro ao capturar imagem', ErrorType.CAPTURE_ERROR);\n }\n\n ctx.drawImage(video, 0, 0, canvas.width, canvas.height);\n\n // Limpa recursos de vídeo\n if (stream) {\n stream.getTracks().forEach(track => track.stop());\n }\n if (video.parentElement) {\n video.parentElement.removeChild(video);\n }\n\n // Converte para blob\n const imageBlob = await new Promise<Blob>((resolve, reject) => {\n canvas.toBlob(\n blob => {\n canvas.remove();\n if (blob) {\n resolve(blob);\n } else {\n reject(new Error('Failed to capture image'));\n }\n },\n 'image/jpeg',\n 0.85\n );\n });\n\n // Faz login com a imagem capturada\n const result = await loginWithBiometric(imageBlob, applicationToken);\n\n if (!result.success) {\n throw new NeoFaceError('Login falhou', ErrorType.LOGIN_FAILED);\n }\n\n return result;\n } catch (error) {\n // Limpa recursos em caso de erro\n if (stream) {\n stream.getTracks().forEach(track => track.stop());\n }\n if (video.parentElement) {\n video.parentElement.removeChild(video);\n }\n\n if (error instanceof NeoFaceError) {\n throw error;\n }\n\n throw new NeoFaceError(\n error instanceof Error ? error.message : 'Erro desconhecido',\n ErrorType.UNKNOWN\n );\n }\n}\n\n/**\n * Executa o fluxo completo de login biométrico - VERSÃO OTIMIZADA\n */\nexport async function executeBiometricLoginFlow(\n options: BiometricLoginFlowOptions\n): Promise<void> {\n const { applicationToken, onSuccess, onError, onFallbackRequest, onCancel, fastMode = false } = options;\n\n const overlay = new BiometricStatusOverlay();\n const fallbackPrompt = new FallbackPrompt();\n\n let attempts = 0;\n let lastError: NeoFaceError | undefined;\n\n // Carrega modelos do face-api.js se necessário (não bloqueia)\n if (!modelsLoaded && !fastMode) {\n preloadFaceDetectionModels().catch(() => {\n // Ignora erro, continua sem detecção local\n });\n }\n\n // Mostra overlay\n overlay.show('preparing');\n\n // Função para tentar login\n const tryLogin = async (): Promise<void> => {\n try {\n attempts++;\n const result = await attemptLogin(applicationToken, overlay, fastMode);\n\n // Sucesso!\n overlay.updateStatus('success');\n setTimeout(() => {\n overlay.close();\n onSuccess(result);\n }, SUCCESS_DELAY);\n } catch (error) {\n lastError = error instanceof NeoFaceError ? error : new NeoFaceError(\n error instanceof Error ? error.message : 'Erro desconhecido',\n ErrorType.UNKNOWN\n );\n\n // Mostra erro no overlay\n overlay.updateStatus('error');\n\n // Se ainda há tentativas, tenta novamente\n if (attempts < MAX_ATTEMPTS) {\n setTimeout(() => {\n tryLogin();\n }, RETRY_DELAY);\n } else {\n // Esgotou tentativas, mostra fallback\n setTimeout(() => {\n overlay.close();\n fallbackPrompt.show(\n lastError,\n () => {\n // Tentar novamente - reseta contador\n attempts = 0;\n overlay.show('preparing');\n setTimeout(() => tryLogin(), RETRY_DELAY);\n },\n () => {\n // Entrar com email e senha\n if (onFallbackRequest) {\n onFallbackRequest();\n } else {\n onError(\n new NeoFaceError(\n 'Por favor, use email e senha para fazer login',\n ErrorType.LOGIN_FAILED\n )\n );\n }\n },\n onCancel\n );\n }, 500);\n }\n }\n };\n\n // Inicia o fluxo IMEDIATAMENTE (sem delay inicial)\n if (INITIAL_DELAY > 0) {\n setTimeout(() => tryLogin(), INITIAL_DELAY);\n } else {\n tryLogin();\n }\n}\n","import { NeoFaceError, ErrorType } from './errors';\nimport { validateToken, BiometricLoginResult } from './api';\nimport { EmailPasswordModal, EmailPasswordOptions } from './EmailPasswordModal';\nimport { executeBiometricLoginFlow } from './utils/biometricLoginFlow';\n\nexport interface BiometricLoginOptions {\n applicationToken: string;\n onSuccess: (result: BiometricLoginResult) => void;\n onError: (error: NeoFaceError) => void;\n onCancel?: () => void;\n onFallbackRequest?: () => void;\n countdown?: number;\n title?: string;\n subtitle?: string;\n}\n\n/**\n * Inicia o processo de login usando reconhecimento facial\n * Usa overlay minimalista estilo FaceID (não mostra câmera)\n * @param options Opções de configuração para o login facial\n */\nexport async function startFaceLogin(options: BiometricLoginOptions): Promise<void> {\n try {\n // Valida o token da aplicação antes de iniciar\n const isValidToken = await validateToken(options.applicationToken);\n if (!isValidToken) {\n options.onError(new NeoFaceError('Token de aplicação inválido', ErrorType.INVALID_TOKEN));\n return;\n }\n\n // Usa o novo fluxo com overlay minimalista\n await executeBiometricLoginFlow({\n applicationToken: options.applicationToken,\n onSuccess: options.onSuccess,\n onError: options.onError,\n onFallbackRequest: options.onFallbackRequest || (() => {\n // Fallback padrão: abre modal de email e senha\n const fallbackOptions: EmailPasswordOptions = {\n applicationToken: options.applicationToken,\n onSuccess: options.onSuccess,\n onError: options.onError,\n title: 'Login Alternativo',\n subtitle: 'Biometria não reconhecida. Por favor, informe email e senha.',\n };\n const modal = new EmailPasswordModal(fallbackOptions);\n modal.open();\n }),\n onCancel: options.onCancel,\n });\n } catch (error) {\n options.onError(\n new NeoFaceError(\n 'Erro ao inicializar login facial: ' + (error as Error).message,\n ErrorType.INITIALIZATION_ERROR\n )\n );\n }\n}\n\n/**\n * Inicia o processo de login usando reconhecimento de mão\n * @param options Opções de configuração para o login por mão\n */\nexport async function startHandLogin(options: BiometricLoginOptions): Promise<void> {\n try {\n // Valida o token da aplicação antes de iniciar\n const isValidToken = await validateToken(options.applicationToken);\n if (!isValidToken) {\n options.onError(new NeoFaceError('Token de aplicação inválido', ErrorType.INVALID_TOKEN));\n return;\n }\n\n const captureOptions: BiometricCaptureOptions = {\n mode: 'hand',\n onSuccess: async (imageData: string, _detectedType: 'face' | 'hand') => {\n try {\n // Convert base64 string to Blob\n const imageBlob = await fetch(imageData).then(res => res.blob());\n const result = await loginWithBiometric(imageBlob, options.applicationToken);\n options.onSuccess(result);\n } catch (error) {\n options.onError(error as NeoFaceError);\n }\n },\n onError: options.onError,\n onCancel: options.onCancel,\n countdown: options.countdown,\n title: options.title || 'Login por Mão',\n subtitle: options.subtitle || 'Posicione sua mão dentro do quadro para fazer login.',\n };\n\n const modal = new BiometricCaptureModal(captureOptions);\n await modal.open();\n } catch (error) {\n options.onError(\n new NeoFaceError(\n 'Erro ao inicializar login por mão: ' + (error as Error).message,\n ErrorType.INITIALIZATION_ERROR\n )\n );\n }\n}\n\n/**\n * Inicia o processo de login com detecção automática do tipo biométrico\n * @param options Opções de configuração para o login automático\n */\nexport async function startAutoLogin(options: BiometricLoginOptions): Promise<void> {\n try {\n // Valida o token da aplicação antes de iniciar\n const isValidToken = await validateToken(options.applicationToken);\n if (!isValidToken) {\n options.onError(new NeoFaceError('Token de aplicação inválido', ErrorType.INVALID_TOKEN));\n return;\n }\n\n const captureOptions: BiometricCaptureOptions = {\n mode: 'auto',\n onSuccess: async (imageData: string, _detectedType: 'face' | 'hand') => {\n try {\n // Convert base64 string to Blob\n const imageBlob = await fetch(imageData).then(res => res.blob());\n const result = await loginWithBiometric(imageBlob, options.applicationToken);\n options.onSuccess(result);\n } catch (error) {\n options.onError(error as NeoFaceError);\n }\n },\n onError: options.onError,\n onCancel: options.onCancel,\n countdown: options.countdown,\n title: options.title || 'Login Biométrico',\n subtitle:\n options.subtitle || 'Posicione seu rosto ou mão dentro do quadro para login automático.',\n };\n\n const modal = new BiometricCaptureModal(captureOptions);\n await modal.open();\n } catch (error) {\n options.onError(\n new NeoFaceError(\n 'Erro ao inicializar login automático: ' + (error as Error).message,\n ErrorType.INITIALIZATION_ERROR\n )\n );\n }\n}\n\n/**\n * Função de conveniência para login biométrico simples\n * @param applicationToken Token da aplicação\n * @param mode Modo de captura ('face', 'hand' ou 'auto')\n * @returns Promise que resolve com o resultado do login\n */\nexport function biometricLogin(\n applicationToken: string,\n mode: 'face' | 'hand' | 'auto' = 'auto'\n): Promise<BiometricLoginResult> {\n return new Promise((resolve, reject) => {\n const options: BiometricLoginOptions = {\n applicationToken,\n onSuccess: resolve,\n onError: reject,\n };\n\n switch (mode) {\n case 'face':\n startFaceLogin(options);\n break;\n case 'hand':\n startHandLogin(options);\n break;\n case 'auto':\n startAutoLogin(options);\n break;\n default:\n reject(new NeoFaceError('Modo de login inválido', ErrorType.VALIDATION_ERROR));\n }\n });\n}\n\n/**\n * Inicia o processo de login biométrico com fallback para email e senha se não houver match (assumindo purpose LOGIN).\n * @param applicationToken Token da aplicação\n * @param mode Modo de captura ('face', 'hand' ou 'auto')\n * @returns Promise que resolve com o resultado do login\n */\nexport function biometricLoginWithFallback(\n applicationToken: string,\n mode: 'face' | 'hand' | 'auto' = 'auto'\n): Promise<BiometricLoginResult> {\n return new Promise((resolve, reject) => {\n const options: BiometricLoginOptions = {\n applicationToken,\n onSuccess: resolve,\n onError: (error) => {\n if (error.type === ErrorType.LOGIN_FAILED) {\n // Abre modal de fallback para email e senha\n const fallbackOptions: EmailPasswordOptions = {\n applicationToken,\n onSuccess: resolve,\n onError: reject,\n title: 'Login Alternativo',\n subtitle: 'Biometria não reconhecida. Por favor, informe email e senha.'\n };\n const modal = new EmailPasswordModal(fallbackOptions);\n modal.open();\n } else {\n reject(error);\n }\n },\n };\n\n switch (mode) {\n case 'face':\n startFaceLogin(options);\n break;\n case 'hand':\n startHandLogin(options);\n break;\n case 'auto':\n startAutoLogin(options);\n break;\n default:\n reject(new NeoFaceError('Modo de login inválido', ErrorType.VALIDATION_ERROR));\n }\n });\n}\n","/**\n * Módulo de detecção automática de tipo biométrico\n * Utiliza face-api.js para detectar se a imagem contém face ou mão\n */\n\nexport interface DetectionResult {\n type: 'face' | 'hand' | 'unknown';\n confidence: number;\n details?: any;\n}\n\n/**\n * Detecta automaticamente o tipo biométrico na imagem\n * @param imageData Dados da imagem em base64\n * @returns Resultado da detecção com tipo e confiança\n */\nexport async function detectBiometricType(imageData: string): Promise<DetectionResult> {\n try {\n // Converte base64 para elemento de imagem\n const img = await loadImageFromBase64(imageData);\n \n // Tenta detectar face primeiro\n const faceResult = await detectFace(img);\n if (faceResult.confidence > 0.7) {\n return {\n type: 'face',\n confidence: faceResult.confidence,\n details: faceResult.details\n };\n }\n\n // Se não detectou face com alta confiança, tenta detectar mão\n const handResult = await detectHand(img);\n if (handResult.confidence > 0.6) {\n return {\n type: 'hand',\n confidence: handResult.confidence,\n details: handResult.details\n };\n }\n\n // Se não detectou nem face nem mão com confiança suficiente\n return {\n type: 'unknown',\n confidence: Math.max(faceResult.confidence, handResult.confidence)\n };\n\n } catch (error) {\n console.warn('Erro na detecção biométrica:', error);\n \n // Em caso de erro, assume face como padrão\n return {\n type: 'face',\n confidence: 0.5\n };\n }\n}\n\n/**\n * Detecta face na imagem usando face-api.js\n * Observação: evita usar descriptors quando o modelo de reconhecimento não está disponível,\n * usando apenas detecção e landmarks para reduzir dependências de arquivos.\n * @param img Elemento de imagem\n * @returns Resultado da detecção facial\n */\nasync function detectFace(img: HTMLImageElement): Promise<{ confidence: number; details?: any }> {\n try {\n // Verifica se face-api.js está disponível\n if (typeof window !== 'undefined' && (window as any).faceapi) {\n const faceapi = (window as any).faceapi;\n \n // Carrega os modelos se ainda não foram carregados\n await loadFaceApiModels();\n \n // Detecta faces na imagem usando TinyFaceDetector para evitar dependência do SsdMobilenet\n const tinyOptions = new faceapi.TinyFaceDetectorOptions({ inputSize: 320, scoreThreshold: 0.5 });\n const detections = await faceapi\n .detectAllFaces(img, tinyOptions)\n .withFaceLandmarks();\n\n if (detections && detections.length > 0) {\n // Retorna a confiança da melhor detecção\n const bestDetection = detections.reduce((best: any, current: any) => \n current.detection.score > best.detection.score ? current : best\n );\n \n return {\n confidence: bestDetection.detection.score,\n details: {\n faces: detections.length,\n landmarks: bestDetection.landmarks,\n box: bestDetection.detection.box\n }\n };\n }\n }\n\n // Se face-api.js não está disponível ou não detectou faces\n return { confidence: 0 };\n\n } catch (error) {\n console.warn('Erro na detecção facial:', error);\n return { confidence: 0 };\n }\n}\n\n/**\n * Detecta mão na imagem usando análise de contornos e características\n * @param img Elemento de imagem\n * @returns Resultado da detecção de mão\n */\nasync function detectHand(img: HTMLImageElement): Promise<{ confidence: number; details?: any }> {\n try {\n // Cria canvas para análise da imagem\n const canvas = document.createElement('canvas');\n const ctx = canvas.getContext('2d');\n \n if (!ctx) {\n return { confidence: 0 };\n }\n\n canvas.width = img.width;\n canvas.height = img.height;\n ctx.drawImage(img, 0, 0);\n\n // Análise básica de características de mão\n const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);\n const handFeatures = analyzeHandFeatures(imageData);\n\n return {\n confidence: handFeatures.confidence,\n details: handFeatures\n };\n\n } catch (error) {\n console.warn('Erro na detecção de mão:', error);\n return { confidence: 0 };\n }\n}\n\n/**\n * Analisa características que indicam presença de mão na imagem\n * @param imageData Dados da imagem do canvas\n * @returns Análise das características de mão\n */\nfunction analyzeHandFeatures(imageData: ImageData): { confidence: number; [key: string]: any } {\n const { data, width, height } = imageData;\n \n // Análise básica de cor de pele\n let skinPixels = 0;\n let totalPixels = 0;\n \n for (let i = 0; i < data.length; i += 4) {\n const r = data[i];\n const g = data[i + 1];\n const b = data[i + 2];\n \n // Verifica se o pixel tem cor de pele\n if (isSkinColor(r, g, b)) {\n skinPixels++;\n }\n totalPixels++;\n }\n\n const skinRatio = skinPixels / totalPixels;\n \n // Análise de formato (aspectos básicos)\n const aspectRatio = width / height;\n const isHandAspectRatio = aspectRatio > 0.6 && aspectRatio < 1.8;\n \n // Calcula confiança baseada nas características\n let confidence = 0;\n \n // Presença de cor de pele (peso: 40%)\n if (skinRatio > 0.15 && skinRatio < 0.7) {\n confidence += 0.4 * (skinRatio / 0.7);\n }\n \n // Proporção adequada para mão (peso: 30%)\n if (isHandAspectRatio) {\n confidence += 0.3;\n }\n \n // Tamanho adequado (peso: 30%)\n const imageSize = width * height;\n if (imageSize > 10000 && imageSize < 500000) {\n confidence += 0.3;\n }\n\n return {\n confidence: Math.min(confidence, 0.8), // Máximo 80% para detecção básica\n skinRatio,\n aspectRatio,\n imageSize,\n skinPixels,\n totalPixels\n };\n}\n\n/**\n * Verifica se uma cor RGB corresponde a cor de pele\n * @param r Componente vermelho\n * @param g Componente verde \n * @param b Componente azul\n * @returns true se for cor de pele\n */\nfunction isSkinColor(r: number, g: number, b: number): boolean {\n // Algoritmo básico para detecção de cor de pele\n // Baseado em ranges típicos de cor de pele humana\n \n // Converte para YCbCr para melhor detecção\n const y = 0.299 * r + 0.587 * g + 0.114 * b;\n const cb = -0.169 * r - 0.331 * g + 0.5 * b + 128;\n const cr = 0.5 * r - 0.419 * g - 0.081 * b + 128;\n \n // Ranges para cor de pele em YCbCr\n return (\n y > 80 && y < 255 &&\n cb > 85 && cb < 135 &&\n cr > 135 && cr < 180\n );\n}\n\n/**\n * Carrega uma imagem a partir de dados base64\n * @param base64Data Dados da imagem em base64\n * @returns Promise que resolve com o elemento de imagem\n */\nfunction loadImageFromBase64(base64Data: string): Promise<HTMLImageElement> {\n return new Promise((resolve, reject) => {\n const img = new Image();\n \n img.onload = () => resolve(img);\n img.onerror = () => reject(new Error('Erro ao carregar imagem'));\n \n // Adiciona prefixo se necessário\n const dataUrl = base64Data.startsWith('data:') \n ? base64Data \n : `data:image/jpeg;base64,${base64Data}`;\n \n img.src = dataUrl;\n });\n}\n\n/**\n * Carrega os modelos do face-api.js se ainda não foram carregados.\n * Evita carregar o modelo de reconhecimento facial (faceRecognitionNet) quando\n * os arquivos não estão presentes, prevenindo erros 404 e \"load model before inference\".\n */\nasync function loadFaceApiModels(): Promise<void> {\n if (typeof window === 'undefined' || !(window as any).faceapi) {\n return;\n }\n\n const faceapi = (window as any).faceapi;\n \n // Verifica se os modelos já foram carregados\n if (faceapi.nets.tinyFaceDetector.isLoaded) {\n return;\n }\n\n try {\n // Carrega modelos básicos para detecção\n await Promise.all([\n faceapi.nets.tinyFaceDetector.loadFromUri('./models'),\n faceapi.nets.faceLandmark68Net.loadFromUri('./models')\n ]);\n\n // Não carregamos faceRecognitionNet por padrão para evitar 404 e dependência de SsdMobilenet\n } catch (error) {\n console.warn('Não foi possível carregar modelos do face-api.js:', error);\n // Continua sem os modelos - usará detecção básica\n }\n}\n\n/**\n * Inicializa o sistema de detecção biométrica\n * Carrega bibliotecas e modelos necessários\n */\nexport async function initializeBiometricDetection(): Promise<void> {\n try {\n // Tenta carregar face-api.js se estiver disponível\n if (typeof window !== 'undefined') {\n await loadFaceApiModels();\n }\n } catch (error) {\n console.warn('Inicialização da detecção biométrica com limitações:', error);\n }\n}\n\n/**\n * Verifica se o sistema de detecção está disponível\n * @returns true se a detecção avançada está disponível\n */\nexport function isAdvancedDetectionAvailable(): boolean {\n return typeof window !== 'undefined' && \n (window as any).faceapi && \n (window as any).faceapi.nets.tinyFaceDetector.isLoaded;\n}","/**\n * Modal de captura para fluxo de Onboarding\n * Responsável por abrir a câmera, capturar uma foto de rosto e uma foto de documento\n * e retornar ambas imagens como Blobs para posterior envio ao backend.\n */\nexport class OnboardingCaptureModal {\n private overlay: HTMLDivElement | null = null;\n private video: HTMLVideoElement | null = null;\n private canvas: HTMLCanvasElement | null = null;\n private stream: MediaStream | null = null;\n private step: 'face' | 'document' = 'face';\n private countdownSeconds: number;\n private title?: string;\n private subtitle?: string;\n\n private faceBlob: Blob | null = null;\n private documentBlob: Blob | null = null;\n\n constructor(options?: { countdown?: number; title?: string; subtitle?: string }) {\n this.countdownSeconds = options?.countdown ?? 3;\n this.title = options?.title;\n this.subtitle = options?.subtitle;\n }\n\n /**\n * Abre o modal, inicia câmera e gerencia o fluxo de captura dos dois passos.\n * Retorna uma promessa resolvida com os Blobs de face e documento.\n */\n async open(): Promise<{ face: Blob; document: Blob }> {\n await this.createDom();\n await this.startCamera();\n\n // Captura de rosto com contagem regressiva\n await this.runFaceCaptureCountdown();\n this.faceBlob = await this.captureFrame();\n\n // Avança para captura de documento\n this.step = 'document';\n this.updateTexts('Captura do Documento', 'Posicione seu documento visível e clique em \"Capturar\"');\n\n // Espera clique do usuário para capturar documento\n await this.waitForUserCaptureClick();\n this.documentBlob = await this.captureFrame();\n\n // Fecha modal e libera recursos\n await this.close();\n\n if (!this.faceBlob || !this.documentBlob) {\n throw new Error('Falha na captura das imagens de onboarding');\n }\n\n return { face: this.faceBlob, document: this.documentBlob };\n }\n\n /**\n * Cria estrutura de DOM do modal e elementos de UI.\n */\n private async createDom(): Promise<void> {\n const overlay = document.createElement('div');\n overlay.className = 'neofaceid-modal-overlay';\n overlay.innerHTML = `\n <div class=\"neofaceid-modal\">\n <div class=\"neofaceid-modal-header\">\n <h2 class=\"neofaceid-title\">${this.title ?? 'Onboarding NeoFaceID'}</h2>\n <button class=\"neofaceid-close-btn\" aria-label=\"Fechar\">×</button>\n </div>\n <div class=\"neofaceid-modal-body\">\n <p class=\"neofaceid-subtitle\">${this.subtitle ?? 'Vamos capturar sua face e documento'}</p>\n <div class=\"neofaceid-camera-container\">\n <video class=\"neofaceid-video\" autoplay playsinline></video>\n <canvas class=\"neofaceid-canvas\"></canvas>\n <div class=\"neofaceid-overlay\">\n <div class=\"neofaceid-frame\"></div>\n <div class=\"neofaceid-countdown\" style=\"display:none\"><span class=\"neofaceid-countdown-number\">${this.countdownSeconds}</span></div>\n </div>\n </div>\n <div class=\"neofaceid-status\"><p class=\"neofaceid-status-text\">Posicione seu rosto no quadro</p></div>\n </div>\n <div class=\"neofaceid-modal-footer\">\n <button class=\"neofaceid-cancel-btn\">Cancelar</button>\n <button class=\"neofaceid-capture-btn\" disabled>Capturar</button>\n </div>\n </div>\n `;\n\n const style = document.createElement('style');\n style.textContent = `\n .neofaceid-modal-overlay { position: fixed; inset: 0; background: rgba(0,0,0,0.6); display:flex; align-items:center; justify-content:center; z-index: 9999; }\n .neofaceid-modal { background:#fff; border-radius:12px; width: 92%; max-width: 640px; box-shadow: 0 20px 60px rgba(0,0,0,0.3); overflow:hidden; }\n .neofaceid-modal-header { padding: 16px 20px; border-bottom: 1px solid #eee; display:flex; align-items:center; justify-content:space-between; }\n .neofaceid-title { margin:0; font-size: 20px; color:#333; }\n .neofaceid-close-btn { background:none; border:none; font-size:24px; cursor:pointer; color:#666; }\n .neofaceid-modal-body { padding: 16px 20px; }\n .neofaceid-subtitle { margin: 0 0 12px 0; color:#666; }\n .neofaceid-camera-container { position: relative; background:#000; border-radius:8px; overflow:hidden; aspect-ratio: 4/3; margin-bottom: 12px; }\n .neofaceid-video { width:100%; height:100%; object-fit: cover; }\n .neofaceid-canvas { position:absolute; top:0; left:0; }\n .neofaceid-overlay { position:absolute; inset:0; display:flex; align-items:center; justify-content:center; }\n .neofaceid-frame { width: 200px; height: 200px; border: 3px solid #4CAF50; border-radius: 50%; box-shadow: 0 0 0 2px rgba(76,175,80,0.3); }\n .neofaceid-countdown { position:absolute; top:16px; right:16px; background: rgba(0,0,0,0.7); color:#fff; width: 48px; height:48px; display:flex; align-items:center; justify-content:center; border-radius:50%; }\n .neofaceid-modal-footer { padding: 16px 20px; border-top: 1px solid #eee; display:flex; gap: 8px; justify-content:flex-end; }\n .neofaceid-cancel-btn { background:#f5f5f5; color:#666; border:none; border-radius:6px; padding:10px 18px; cursor:pointer; }\n .neofaceid-capture-btn { background:#4CAF50; color:#fff; border:none; border-radius:6px; padding:10px 18px; cursor:pointer; }\n .neofaceid-capture-btn:disabled { background:#ccc; cursor:not-allowed; }\n `;\n\n document.head.appendChild(style);\n document.body.appendChild(overlay);\n this.overlay = overlay as HTMLDivElement;\n\n this.video = this.overlay.querySelector('.neofaceid-video') as HTMLVideoElement;\n this.canvas = this.overlay.querySelector('.neofaceid-canvas') as HTMLCanvasElement;\n\n const closeBtn = this.overlay.querySelector('.neofaceid-close-btn') as HTMLButtonElement;\n const cancelBtn = this.overlay.querySelector('.neofaceid-cancel-btn') as HTMLButtonElement;\n closeBtn.onclick = () => this.close();\n cancelBtn.onclick = () => this.close();\n\n const captureBtn = this.overlay.querySelector('.neofaceid-capture-btn') as HTMLButtonElement;\n // No primeiro passo (face) o botão fica desabilitado; no segundo é usado manualmente\n captureBtn.disabled = true;\n }\n\n /**\n * Inicia a câmera do usuário com resolução adequada.\n */\n private async startCamera(): Promise<void> {\n const constraints: MediaStreamConstraints = {\n video: { facingMode: 'user', width: { ideal: 1280 }, height: { ideal: 720 } },\n audio: false,\n };\n this.stream = await navigator.mediaDevices.getUserMedia(constraints);\n if (!this.video) throw new Error('Elemento de vídeo não encontrado');\n this.video.srcObject = this.stream;\n await this.video.play();\n }\n\n /**\n * Executa a contagem regressiva e atualiza textos para captura de rosto.\n */\n private async runFaceCaptureCountdown(): Promise<void> {\n if (!this.overlay) return;\n const countdownEl = this.overlay.querySelector('.neofaceid-countdown') as HTMLDivElement;\n const countdownNumberEl = this.overlay.querySelector('.neofaceid-countdown-number') as HTMLSpanElement;\n const statusText = this.overlay.querySelector('.neofaceid-status-text') as HTMLParagraphElement;\n countdownEl.style.display = 'flex';\n statusText.textContent = 'Prepare-se! Capturando seu rosto...';\n\n let remaining = this.countdownSeconds;\n countdownNumberEl.textContent = String(remaining);\n await new Promise<void>((resolve) => {\n const interval = setInterval(() => {\n remaining -= 1;\n countdownNumberEl.textContent = String(remaining);\n if (remaining <= 0) {\n clearInterval(interval);\n countdownEl.style.display = 'none';\n resolve();\n }\n }, 1000);\n });\n }\n\n /**\n * Aguarda o clique do usuário no botão \"Capturar\" (para documento).\n */\n private async waitForUserCaptureClick(): Promise<void> {\n if (!this.overlay) return;\n const captureBtn = this.overlay.querySelector('.neofaceid-capture-btn') as HTMLButtonElement;\n const statusText = this.overlay.querySelector('.neofaceid-status-text') as HTMLParagraphElement;\n captureBtn.disabled = false;\n statusText.textContent = 'Clique em \"Capturar\" quando o documento estiver visível';\n await new Promise<void>((resolve) => {\n const handler = () => {\n captureBtn.removeEventListener('click', handler);\n resolve();\n };\n captureBtn.addEventListener('click', handler);\n });\n captureBtn.disabled = true;\n }\n\n /**\n * Captura o frame atual do vídeo e retorna como Blob JPEG.\n */\n private async captureFrame(): Promise<Blob> {\n if (!this.video || !this.canvas) throw new Error('Elementos de captura não encontrados');\n const ctx = this.canvas.getContext('2d');\n if (!ctx) throw new Error('Falha ao obter contexto do canvas');\n\n this.canvas.width = this.video.videoWidth;\n this.canvas.height = this.video.videoHeight;\n ctx.drawImage(this.video, 0, 0, this.canvas.width, this.canvas.height);\n\n const blob: Blob = await new Promise((resolve, reject) => {\n this.canvas!.toBlob((b) => (b ? resolve(b) : reject(new Error('Falha ao gerar imagem'))), 'image/jpeg', 0.92);\n });\n return blob;\n }\n\n /**\n * Atualiza título e subtítulo do modal conforme o passo.\n */\n private updateTexts(title: string, subtitle: string): void {\n if (!this.overlay) return;\n const titleEl = this.overlay.querySelector('.neofaceid-title') as HTMLHeadingElement;\n const subtitleEl = this.overlay.querySelector('.neofaceid-subtitle') as HTMLParagraphElement;\n const frameEl = this.overlay.querySelector('.neofaceid-frame') as HTMLDivElement;\n titleEl.textContent = title;\n subtitleEl.textContent = subtitle;\n // Para documento, usa moldura retangular para ajudar enquadramento\n if (this.step === 'document') {\n frameEl.style.borderRadius = '8px';\n frameEl.style.width = '280px';\n frameEl.style.height = '180px';\n }\n }\n\n /**\n * Fecha o modal e libera a câmera.\n */\n private async close(): Promise<void> {\n if (this.stream) {\n this.stream.getTracks().forEach((t) => t.stop());\n this.stream = null;\n }\n if (this.overlay) {\n this.overlay.remove();\n this.overlay = null;\n }\n }\n}","import { NeoFaceError, ErrorType } from './errors';\nimport { recognizeByPurpose, type SessionData } from './api';\n\nexport interface AuthorizationResult {\n success: boolean;\n authorizationHash?: string;\n name?: string;\n birthDate?: string;\n cpf?: string;\n confidenceScore?: number;\n message?: string;\n}\n\nexport interface AuthorizationOptions {\n onProgress?: (step: number, totalSteps: number, instruction: string) => void;\n onPhotoTaken?: (photoNumber: number, blob: Blob) => void;\n captureDelay?: number; // Delay between captures in ms (default: 2000)\n}\n\nconst PHOTO_INSTRUCTIONS = [\n 'Olhe diretamente para a câmera',\n 'Vire levemente a cabeça para a esquerda',\n 'Vire levemente a cabeça para a direita',\n 'Mostre o polegar para cima (👍) e olhe para a câmera',\n];\n\nconst TOTAL_PHOTOS = 4;\n\n/**\n * NEO-108: Authorization operation with multi-photo capture and gesture detection\n * \n * This function captures 4 sequential photos for authorization:\n * - Photo 1: Front face\n * - Photo 2: Left profile\n * - Photo 3: Right profile \n * - Photo 4: Thumbs up gesture with front face\n * \n * @param applicationToken The application token for authentication\n * @param cpf The CPF of the person to authorize\n * @param options Optional configuration for progress callbacks and delays\n * @returns Promise with authorization result\n * @throws NeoFaceError if capture or recognition fails\n */\nexport const authorizeOperation = async (\n applicationToken: string,\n cpf: string,\n options?: AuthorizationOptions\n): Promise<AuthorizationResult> => {\n if (!applicationToken) {\n throw new NeoFaceError('Application token is required', ErrorType.VALIDATION_ERROR);\n }\n\n if (!cpf) {\n throw new NeoFaceError('CPF is required', ErrorType.VALIDATION_ERROR);\n }\n\n // Validate CPF format (basic validation)\n const cpfClean = cpf.replace(/\\D/g, '');\n if (cpfClean.length !== 11) {\n throw new NeoFaceError('Invalid CPF format', ErrorType.VALIDATION_ERROR);\n }\n\n const capturedPhotos: Blob[] = [];\n const captureDelay = options?.captureDelay || 2000;\n\n try {\n // Check camera availability\n if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {\n throw new NeoFaceError('Camera not available', ErrorType.NO_CAMERA);\n }\n\n // Request camera access\n const stream = await navigator.mediaDevices.getUserMedia({\n video: {\n facingMode: 'user',\n width: { ideal: 1280 },\n height: { ideal: 720 },\n },\n });\n\n // Create video element\n const video = document.createElement('video');\n video.srcObject = stream;\n video.autoplay = true;\n video.playsInline = true;\n\n // Wait for video to be ready\n await new Promise<void>((resolve) => {\n video.onloadedmetadata = () => {\n video.play();\n resolve();\n };\n });\n\n // Create canvas for photo capture\n const canvas = document.createElement('canvas');\n canvas.width = video.videoWidth;\n canvas.height = video.videoHeight;\n const ctx = canvas.getContext('2d');\n\n if (!ctx) {\n throw new NeoFaceError('Failed to get canvas context', ErrorType.CAPTURE_ERROR);\n }\n\n // Capture photos with instructions\n for (let i = 0; i < TOTAL_PHOTOS; i++) {\n const instruction = PHOTO_INSTRUCTIONS[i];\n\n // Report progress\n if (options?.onProgress) {\n options.onProgress(i + 1, TOTAL_PHOTOS, instruction);\n }\n\n // Wait for delay between captures\n if (i > 0) {\n await new Promise((resolve) => setTimeout(resolve, captureDelay));\n }\n\n // Capture photo\n ctx.drawImage(video, 0, 0, canvas.width, canvas.height);\n\n // Convert to blob\n const blob = await new Promise<Blob>((resolve, reject) => {\n canvas.toBlob(\n (blob) => {\n if (blob) {\n resolve(blob);\n } else {\n reject(new Error('Failed to capture photo'));\n }\n },\n 'image/jpeg',\n 0.9\n );\n });\n\n capturedPhotos.push(blob);\n\n // Report photo taken\n if (options?.onPhotoTaken) {\n options.onPhotoTaken(i + 1, blob);\n }\n }\n\n // Stop camera\n stream.getTracks().forEach((track) => track.stop());\n\n // Send to backend for authorization\n // Note: Currently sends only the last photo (with gesture) as the main photo\n // The backend should be updated to accept all 4 photos\n const lastPhoto = capturedPhotos[TOTAL_PHOTOS - 1]; // Photo with thumbs up\n\n const sessionData: SessionData = {\n cpf: cpfClean,\n email: '', // Not required for authorization\n sessionId: `auth_${Date.now()}_${Math.random().toString(36).substring(7)}`,\n };\n\n // Call recognizeByPurpose with AUTHORIZATION purpose\n const recognitionResult = await recognizeByPurpose(\n lastPhoto,\n applicationToken,\n 'AUTHORIZATION',\n 0.8, // Confidence threshold\n undefined, // No signature for now\n sessionData\n );\n\n if (!recognitionResult.success) {\n throw new NeoFaceError(\n 'Authorization failed: Person not recognized',\n ErrorType.RECOGNITION_FAILED\n );\n }\n\n // Return authorization result\n // NOTE: authorizationHash should come from backend but it's not implemented yet\n return {\n success: true,\n authorizationHash: sessionData.sessionId, // TEMPORARY: Using sessionId as hash\n name: recognitionResult.personName,\n birthDate: undefined, // Should come from backend\n cpf: recognitionResult.cpf || cpfClean,\n confidenceScore: recognitionResult.confidenceScore,\n message: 'Authorization successful',\n };\n } catch (error) {\n if (error instanceof NeoFaceError) {\n throw error;\n }\n if (error instanceof Error) {\n throw new NeoFaceError(error.message, ErrorType.CAPTURE_ERROR);\n }\n throw new NeoFaceError('Unknown error during authorization', ErrorType.UNKNOWN);\n }\n};\n","/**\n * Modal de captura de documento para validação de identidade\n * Suporta RG, CNH e CPF com etapas de frente e verso quando aplicável\n */\n\nexport type DocumentType = 'RG' | 'CNH' | 'CPF';\n\nexport interface DocumentCaptureOptions {\n title?: string;\n subtitle?: string;\n useBackCamera?: boolean;\n onCancel?: () => void;\n // Se fornecido, pula a tela de seleção\n preSelectedDocument?: DocumentType;\n}\n\nexport interface DocumentCaptureResult {\n success: boolean;\n documentType?: DocumentType;\n frontImage: Blob | null;\n backImage: Blob | null;\n error?: string;\n}\n\ninterface DocumentInfo {\n name: string;\n icon: string;\n hasBack: boolean;\n frontLabel: string;\n backLabel?: string;\n}\n\nconst DOCUMENT_INFO: Record<DocumentType, DocumentInfo> = {\n RG: {\n name: 'RG / Carteira de Identidade',\n 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>',\n hasBack: true,\n frontLabel: 'Frente do RG',\n backLabel: 'Verso do RG',\n },\n CNH: {\n name: 'CNH / Carteira de Motorista',\n 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>',\n hasBack: true,\n frontLabel: 'Frente da CNH',\n backLabel: 'Verso da CNH',\n },\n CPF: {\n name: 'CPF',\n 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>',\n hasBack: false,\n frontLabel: 'Cartão CPF',\n },\n};\n\ntype CaptureStep = 'select' | 'front' | 'preview-front' | 'back' | 'preview-back' | 'complete';\n\nexport class DocumentCaptureModal {\n private overlay: HTMLDivElement | null = null;\n private video: HTMLVideoElement | null = null;\n private canvas: HTMLCanvasElement | null = null;\n private stream: MediaStream | null = null;\n private resolvePromise: ((result: DocumentCaptureResult) => void) | null = null;\n private options: DocumentCaptureOptions;\n\n private selectedDocument: DocumentType | null = null;\n private currentStep: CaptureStep = 'select';\n private frontBlob: Blob | null = null;\n private backBlob: Blob | null = null;\n\n constructor(options?: DocumentCaptureOptions) {\n this.options = {\n title: options?.title ?? 'Validação de Identidade',\n subtitle: options?.subtitle ?? 'Selecione o tipo de documento',\n useBackCamera: options?.useBackCamera ?? true,\n onCancel: options?.onCancel,\n preSelectedDocument: options?.preSelectedDocument,\n };\n }\n\n async open(): Promise<DocumentCaptureResult> {\n return new Promise((resolve) => {\n this.resolvePromise = resolve;\n\n if (this.options.preSelectedDocument) {\n this.selectedDocument = this.options.preSelectedDocument;\n this.currentStep = 'front';\n }\n\n this.render();\n });\n }\n\n private render(): void {\n if (!this.overlay) {\n this.createOverlay();\n }\n\n const content = this.overlay?.querySelector('.neoface-doc-content');\n if (!content) return;\n\n switch (this.currentStep) {\n case 'select':\n content.innerHTML = this.renderSelectStep();\n this.bindSelectEvents();\n break;\n case 'front':\n case 'back':\n content.innerHTML = this.renderCaptureStep();\n this.bindCaptureEvents();\n this.startCamera();\n break;\n case 'preview-front':\n case 'preview-back':\n content.innerHTML = this.renderPreviewStep();\n this.bindPreviewEvents();\n break;\n }\n }\n\n private createOverlay(): void {\n const overlay = document.createElement('div');\n overlay.className = 'neoface-doc-overlay';\n overlay.innerHTML = `\n <div class=\"neoface-doc-modal\">\n <div class=\"neoface-doc-content\"></div>\n </div>\n `;\n\n // Inject styles\n if (!document.getElementById('neoface-doc-styles-v2')) {\n const style = document.createElement('style');\n style.id = 'neoface-doc-styles-v2';\n style.textContent = this.getStyles();\n document.head.appendChild(style);\n }\n\n document.body.appendChild(overlay);\n this.overlay = overlay;\n }\n\n private renderSelectStep(): string {\n return `\n <div class=\"neoface-doc-header\">\n <div class=\"neoface-doc-header-left\">\n <h2 class=\"neoface-doc-title\">${this.options.title}</h2>\n <p class=\"neoface-doc-subtitle\">${this.options.subtitle}</p>\n </div>\n <button class=\"neoface-doc-close-btn\" aria-label=\"Fechar\">\n <svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M18 6L6 18M6 6l12 12\"/>\n </svg>\n </button>\n </div>\n \n <div class=\"neoface-doc-body\">\n <div class=\"neoface-doc-select-grid\">\n ${Object.entries(DOCUMENT_INFO).map(([type, info]) => `\n <button class=\"neoface-doc-select-option\" data-type=\"${type}\">\n <span class=\"neoface-doc-select-icon\">${info.icon}</span>\n <span class=\"neoface-doc-select-name\">${info.name}</span>\n <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>\n </button>\n `).join('')}\n </div>\n \n <div class=\"neoface-doc-tips\">\n <div class=\"neoface-doc-tip\">\n <span>Garanta boa iluminação e que todos os dados estejam legíveis</span>\n </div>\n </div>\n </div>\n \n ${this.renderBranding()}\n `;\n }\n\n private renderCaptureStep(): string {\n const docInfo = this.selectedDocument ? DOCUMENT_INFO[this.selectedDocument] : null;\n if (!docInfo) return '';\n\n const isBack = this.currentStep === 'back';\n const label = isBack ? docInfo.backLabel : docInfo.frontLabel;\n const stepNumber = isBack ? 2 : 1;\n const totalSteps = docInfo.hasBack ? 2 : 1;\n\n return `\n <div class=\"neoface-doc-header\">\n <div class=\"neoface-doc-header-left\">\n <div class=\"neoface-doc-step-indicator\">\n <span class=\"neoface-doc-step-badge\">${stepNumber}/${totalSteps}</span>\n <span class=\"neoface-doc-step-label\">${label}</span>\n </div>\n </div>\n <button class=\"neoface-doc-close-btn\" aria-label=\"Fechar\">\n <svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M18 6L6 18M6 6l12 12\"/>\n </svg>\n </button>\n </div>\n \n <div class=\"neoface-doc-body\">\n <div class=\"neoface-doc-camera-container\">\n <video class=\"neoface-doc-video\" autoplay playsinline></video>\n <canvas class=\"neoface-doc-canvas\"></canvas>\n \n <div class=\"neoface-doc-guide\">\n <div class=\"neoface-doc-frame\">\n <div class=\"neoface-doc-corner neoface-doc-corner-tl\"></div>\n <div class=\"neoface-doc-corner neoface-doc-corner-tr\"></div>\n <div class=\"neoface-doc-corner neoface-doc-corner-bl\"></div>\n <div class=\"neoface-doc-corner neoface-doc-corner-br\"></div>\n <div class=\"neoface-doc-frame-label\">${label}</div>\n </div>\n </div>\n \n <div class=\"neoface-doc-loading\" style=\"display:none\">\n <div class=\"neoface-doc-spinner\"></div>\n </div>\n </div>\n \n <div class=\"neoface-doc-instructions\">\n <div class=\"neoface-doc-instruction\">\n <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>\n <span>Posicione o documento dentro do quadro</span>\n </div>\n <div class=\"neoface-doc-instruction\">\n <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>\n <span>Evite reflexos e sombras</span>\n </div>\n </div>\n </div>\n \n <div class=\"neoface-doc-footer\">\n <button class=\"neoface-doc-btn neoface-doc-btn-secondary neoface-doc-back-btn\">\n ← Voltar\n </button>\n <input type=\"file\" accept=\"image/*\" class=\"neoface-doc-file-input\" style=\"display:none\" />\n <button class=\"neoface-doc-btn neoface-doc-btn-secondary neoface-doc-upload-btn\">\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4\"/>\n <polyline points=\"17,8 12,3 7,8\"/>\n <line x1=\"12\" y1=\"3\" x2=\"12\" y2=\"15\"/>\n </svg>\n Anexar\n </button>\n <button class=\"neoface-doc-btn neoface-doc-btn-primary neoface-doc-capture-btn\">\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <circle cx=\"12\" cy=\"12\" r=\"10\"/>\n <circle cx=\"12\" cy=\"12\" r=\"4\"/>\n </svg>\n Capturar\n </button>\n </div>\n \n ${this.renderBranding()}\n `;\n }\n\n private renderPreviewStep(): string {\n const docInfo = this.selectedDocument ? DOCUMENT_INFO[this.selectedDocument] : null;\n if (!docInfo) return '';\n\n const isPreviewFront = this.currentStep === 'preview-front';\n const label = isPreviewFront ? docInfo.frontLabel : docInfo.backLabel;\n const isLastStep = !docInfo.hasBack || this.currentStep === 'preview-back';\n const stepNumber = isPreviewFront ? 1 : 2;\n const totalSteps = docInfo.hasBack ? 2 : 1;\n\n return `\n <div class=\"neoface-doc-header\">\n <div class=\"neoface-doc-header-left\">\n <div class=\"neoface-doc-step-indicator\">\n <span class=\"neoface-doc-step-badge\">${stepNumber}/${totalSteps}</span>\n <span class=\"neoface-doc-step-label\">Confirmar ${label}</span>\n </div>\n </div>\n <button class=\"neoface-doc-close-btn\" aria-label=\"Fechar\">\n <svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M18 6L6 18M6 6l12 12\"/>\n </svg>\n </button>\n </div>\n \n <div class=\"neoface-doc-body\">\n <div class=\"neoface-doc-preview-single\">\n <span class=\"neoface-doc-preview-label\">${label}</span>\n <img class=\"neoface-doc-preview-img neoface-doc-preview-current\" />\n </div>\n \n <div class=\"neoface-doc-preview-hint\">\n <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>\n <span>Verifique se todos os dados estão legíveis</span>\n </div>\n </div>\n \n <div class=\"neoface-doc-footer\">\n <button class=\"neoface-doc-btn neoface-doc-btn-secondary neoface-doc-retake-btn\">\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M1 4v6h6M23 20v-6h-6\"/>\n <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\"/>\n </svg>\n Refazer\n </button>\n <button class=\"neoface-doc-btn neoface-doc-btn-success neoface-doc-confirm-btn\">\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M20 6L9 17l-5-5\"/>\n </svg>\n ${isLastStep ? 'Confirmar e Enviar' : 'Próximo'}\n </button>\n </div>\n \n ${this.renderBranding()}\n `;\n }\n\n private renderBranding(): string {\n return `\n <div class=\"neoface-doc-branding\">\n <img src=\"/logo-icone-no-background.png\" \n alt=\"NeoFaceId\" class=\"neoface-doc-brand-icon\" \n onerror=\"this.style.display='none'\" />\n <span class=\"neoface-doc-brand-name-text\">NeoFaceId</span>\n <span class=\"neoface-doc-brand-text\">by</span>\n <span class=\"neoface-doc-brand-name\">OCTA</span>\n </div>\n `;\n }\n\n private bindSelectEvents(): void {\n const options = this.overlay?.querySelectorAll('.neoface-doc-select-option');\n options?.forEach(option => {\n option.addEventListener('click', () => {\n const type = option.getAttribute('data-type') as DocumentType;\n this.selectedDocument = type;\n this.currentStep = 'front';\n this.render();\n });\n });\n\n this.bindCloseEvent();\n }\n\n private bindCaptureEvents(): void {\n const captureBtn = this.overlay?.querySelector('.neoface-doc-capture-btn');\n const backBtn = this.overlay?.querySelector('.neoface-doc-back-btn');\n const uploadBtn = this.overlay?.querySelector('.neoface-doc-upload-btn');\n const fileInput = this.overlay?.querySelector('.neoface-doc-file-input') as HTMLInputElement;\n\n captureBtn?.addEventListener('click', () => this.handleCapture());\n backBtn?.addEventListener('click', () => this.handleBack());\n\n // Upload file handling\n uploadBtn?.addEventListener('click', () => fileInput?.click());\n fileInput?.addEventListener('change', (e) => this.handleFileUpload(e));\n\n this.bindCloseEvent();\n }\n\n private async handleFileUpload(event: Event): Promise<void> {\n const input = event.target as HTMLInputElement;\n const file = input.files?.[0];\n if (!file) return;\n\n // Show loading indicator\n const loadingEl = this.overlay?.querySelector('.neoface-doc-loading') as HTMLElement;\n if (loadingEl) loadingEl.style.display = 'flex';\n\n try {\n // Compress the uploaded image to avoid 413 errors\n const compressedBlob = await this.compressUploadedImage(file);\n\n if (this.currentStep === 'front') {\n this.frontBlob = compressedBlob;\n this.currentStep = 'preview-front';\n } else if (this.currentStep === 'back') {\n this.backBlob = compressedBlob;\n this.currentStep = 'preview-back';\n }\n\n this.stopCamera();\n this.render();\n } catch (error) {\n console.error('[DocumentCapture] Error compressing uploaded image:', error);\n // Fallback to original file\n const blob = new Blob([file], { type: file.type });\n if (this.currentStep === 'front') {\n this.frontBlob = blob;\n this.currentStep = 'preview-front';\n } else if (this.currentStep === 'back') {\n this.backBlob = blob;\n this.currentStep = 'preview-back';\n }\n this.stopCamera();\n this.render();\n }\n }\n\n private compressUploadedImage(file: File): Promise<Blob> {\n return new Promise((resolve, reject) => {\n const img = new Image();\n img.onload = () => {\n // Calculate new dimensions (max 1280px on longest side to avoid 413 errors)\n const MAX_SIZE = 1280;\n let width = img.width;\n let height = img.height;\n\n if (width > height && width > MAX_SIZE) {\n height = (height * MAX_SIZE) / width;\n width = MAX_SIZE;\n } else if (height > MAX_SIZE) {\n width = (width * MAX_SIZE) / height;\n height = MAX_SIZE;\n }\n\n const canvas = document.createElement('canvas');\n canvas.width = width;\n canvas.height = height;\n\n const ctx = canvas.getContext('2d');\n if (!ctx) {\n reject(new Error('Failed to get canvas context'));\n return;\n }\n\n ctx.drawImage(img, 0, 0, width, height);\n\n canvas.toBlob(\n blob => {\n if (blob) {\n console.log(`[DocumentCapture] Compressed image from ${(file.size / 1024).toFixed(0)}KB to ${(blob.size / 1024).toFixed(0)}KB`);\n resolve(blob);\n } else {\n reject(new Error('Failed to compress image'));\n }\n },\n 'image/jpeg',\n 0.75 // 75% quality - keeps file size manageable for server limits\n );\n };\n\n img.onerror = () => reject(new Error('Failed to load image'));\n img.src = URL.createObjectURL(file);\n });\n }\n\n private bindPreviewEvents(): void {\n const confirmBtn = this.overlay?.querySelector('.neoface-doc-confirm-btn');\n const retakeBtn = this.overlay?.querySelector('.neoface-doc-retake-btn');\n\n confirmBtn?.addEventListener('click', () => this.handleConfirm());\n retakeBtn?.addEventListener('click', () => this.handleRetake());\n\n // Load preview image for current step\n const previewImg = this.overlay?.querySelector('.neoface-doc-preview-current') as HTMLImageElement;\n if (previewImg) {\n const blob = this.currentStep === 'preview-front' ? this.frontBlob : this.backBlob;\n if (blob) previewImg.src = URL.createObjectURL(blob);\n }\n\n this.bindCloseEvent();\n }\n\n private bindCloseEvent(): void {\n const closeBtn = this.overlay?.querySelector('.neoface-doc-close-btn');\n closeBtn?.addEventListener('click', () => this.handleCancel());\n }\n\n private async startCamera(): Promise<void> {\n try {\n const constraints: MediaStreamConstraints = {\n video: {\n facingMode: this.options.useBackCamera ? 'environment' : 'user',\n width: { ideal: 1920 },\n height: { ideal: 1080 },\n },\n audio: false,\n };\n\n this.stream = await navigator.mediaDevices.getUserMedia(constraints);\n\n this.video = this.overlay?.querySelector('.neoface-doc-video') as HTMLVideoElement;\n this.canvas = this.overlay?.querySelector('.neoface-doc-canvas') as HTMLCanvasElement;\n\n if (this.video) {\n this.video.srcObject = this.stream;\n await this.video.play();\n }\n } catch (error) {\n console.error('[DocumentCapture] Camera error:', error);\n // Try fallback to front camera\n if (this.options.useBackCamera) {\n try {\n this.stream = await navigator.mediaDevices.getUserMedia({\n video: { facingMode: 'user' },\n audio: false,\n });\n if (this.video) {\n this.video.srcObject = this.stream;\n await this.video.play();\n }\n } catch (fallbackError) {\n this.resolvePromise?.({\n success: false,\n frontImage: null,\n backImage: null,\n error: 'Não foi possível acessar a câmera',\n });\n this.close();\n }\n }\n }\n }\n\n private stopCamera(): void {\n if (this.stream) {\n this.stream.getTracks().forEach(track => track.stop());\n this.stream = null;\n }\n }\n\n private async handleCapture(): Promise<void> {\n if (!this.video || !this.canvas) return;\n\n const loading = this.overlay?.querySelector('.neoface-doc-loading') as HTMLElement;\n if (loading) loading.style.display = 'flex';\n\n const ctx = this.canvas.getContext('2d');\n if (!ctx) return;\n\n // Dimensões do vídeo\n const videoWidth = this.video.videoWidth;\n const videoHeight = this.video.videoHeight;\n\n // Calcular área do enquadramento (mesmas proporções do CSS: 75% largura, 65% altura)\n const frameWidthPercent = 0.75;\n const frameHeightPercent = 0.65;\n\n const cropWidth = Math.floor(videoWidth * frameWidthPercent);\n const cropHeight = Math.floor(videoHeight * frameHeightPercent);\n\n // Centralizar o crop\n const cropX = Math.floor((videoWidth - cropWidth) / 2);\n const cropY = Math.floor((videoHeight - cropHeight) / 2);\n\n // Definir tamanho do canvas como a área cropada\n this.canvas.width = cropWidth;\n this.canvas.height = cropHeight;\n\n // Desenhar apenas a área do enquadramento\n ctx.drawImage(\n this.video,\n cropX, cropY, cropWidth, cropHeight, // Área de origem (crop)\n 0, 0, cropWidth, cropHeight // Área de destino (canvas inteiro)\n );\n\n try {\n const blob = await new Promise<Blob>((resolve, reject) => {\n this.canvas!.toBlob(\n (b) => (b ? resolve(b) : reject(new Error('Capture failed'))),\n 'image/jpeg',\n 0.92\n );\n });\n\n if (this.currentStep === 'front') {\n this.frontBlob = blob;\n this.stopCamera();\n this.currentStep = 'preview-front';\n this.render();\n } else if (this.currentStep === 'back') {\n this.backBlob = blob;\n this.stopCamera();\n this.currentStep = 'preview-back';\n this.render();\n }\n } catch (error) {\n console.error('[DocumentCapture] Capture error:', error);\n } finally {\n if (loading) loading.style.display = 'none';\n }\n }\n\n private handleBack(): void {\n this.stopCamera();\n\n if (this.currentStep === 'back') {\n this.currentStep = 'front';\n this.frontBlob = null;\n } else if (this.currentStep === 'front') {\n this.currentStep = 'select';\n this.selectedDocument = null;\n }\n\n this.render();\n }\n\n private handleRetake(): void {\n // Refaz apenas a captura atual\n if (this.currentStep === 'preview-front') {\n this.frontBlob = null;\n this.currentStep = 'front';\n } else if (this.currentStep === 'preview-back') {\n this.backBlob = null;\n this.currentStep = 'back';\n }\n this.render();\n }\n\n private handleConfirm(): void {\n const docInfo = this.selectedDocument ? DOCUMENT_INFO[this.selectedDocument] : null;\n\n if (this.currentStep === 'preview-front') {\n // Se tem verso, vai para captura do verso\n if (docInfo?.hasBack) {\n this.currentStep = 'back';\n this.render();\n } else {\n // Sem verso, finaliza\n this.resolvePromise?.({\n success: true,\n documentType: this.selectedDocument || undefined,\n frontImage: this.frontBlob,\n backImage: null,\n });\n this.close();\n }\n } else if (this.currentStep === 'preview-back') {\n // Confirmou verso, finaliza\n this.resolvePromise?.({\n success: true,\n documentType: this.selectedDocument || undefined,\n frontImage: this.frontBlob,\n backImage: this.backBlob,\n });\n this.close();\n }\n }\n\n private handleCancel(): void {\n this.options.onCancel?.();\n this.resolvePromise?.({\n success: false,\n frontImage: null,\n backImage: null,\n error: 'cancelled',\n });\n this.close();\n }\n\n private close(): void {\n this.stopCamera();\n if (this.overlay) {\n this.overlay.remove();\n this.overlay = null;\n }\n }\n\n private getStyles(): string {\n return `\n .neoface-doc-overlay {\n position: fixed;\n inset: 0;\n background: rgba(0, 0, 0, 0.85);\n backdrop-filter: blur(8px);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 10000;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n padding: 16px;\n }\n \n .neoface-doc-modal {\n background: linear-gradient(145deg, #1a1a2e 0%, #16213e 100%);\n border-radius: 24px;\n width: 100%;\n max-width: 520px;\n box-shadow: 0 25px 80px rgba(0, 0, 0, 0.6), 0 0 0 1px rgba(255,255,255,0.1);\n overflow: hidden;\n animation: neoface-doc-slideIn 0.3s ease;\n }\n \n @keyframes neoface-doc-slideIn {\n from { transform: translateY(20px) scale(0.98); opacity: 0; }\n to { transform: translateY(0) scale(1); opacity: 1; }\n }\n \n .neoface-doc-header {\n padding: 20px 24px;\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n border-bottom: 1px solid rgba(255, 255, 255, 0.08);\n }\n \n .neoface-doc-title {\n margin: 0 0 4px 0;\n font-size: 20px;\n font-weight: 600;\n color: #fff;\n }\n \n .neoface-doc-subtitle {\n margin: 0;\n font-size: 14px;\n color: rgba(255, 255, 255, 0.6);\n }\n \n .neoface-doc-step-indicator {\n display: flex;\n align-items: center;\n gap: 12px;\n }\n \n .neoface-doc-step-badge {\n background: linear-gradient(135deg, #00c8ff, #0099cc);\n color: #fff;\n font-size: 12px;\n font-weight: 600;\n padding: 4px 10px;\n border-radius: 20px;\n }\n \n .neoface-doc-step-label {\n font-size: 16px;\n font-weight: 500;\n color: #fff;\n }\n \n .neoface-doc-close-btn {\n background: transparent;\n border: none;\n padding: 4px;\n cursor: pointer;\n color: rgba(255, 255, 255, 0.5);\n transition: all 0.2s;\n }\n \n .neoface-doc-close-btn:hover {\n color: #fff;\n }\n \n .neoface-doc-body {\n padding: 20px 24px;\n }\n \n /* Select Step */\n .neoface-doc-select-grid {\n display: flex;\n flex-direction: column;\n gap: 12px;\n }\n \n .neoface-doc-select-option {\n display: flex;\n align-items: center;\n gap: 16px;\n padding: 16px 20px;\n background: rgba(255, 255, 255, 0.05);\n border: 2px solid rgba(255, 255, 255, 0.1);\n border-radius: 14px;\n cursor: pointer;\n transition: all 0.2s;\n text-align: left;\n }\n \n .neoface-doc-select-option:hover {\n background: rgba(0, 200, 255, 0.1);\n border-color: rgba(0, 200, 255, 0.4);\n transform: translateX(4px);\n }\n \n .neoface-doc-select-icon {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 44px;\n height: 44px;\n background: rgba(0, 200, 255, 0.15);\n border-radius: 12px;\n color: #00c8ff;\n }\n \n .neoface-doc-select-icon svg {\n width: 24px;\n height: 24px;\n }\n \n .neoface-doc-select-name {\n flex: 1;\n font-size: 15px;\n font-weight: 500;\n color: #fff;\n }\n \n .neoface-doc-select-arrow {\n color: rgba(255, 255, 255, 0.4);\n transition: transform 0.2s;\n }\n \n .neoface-doc-select-option:hover .neoface-doc-select-arrow {\n color: #00c8ff;\n transform: translateX(4px);\n }\n \n .neoface-doc-tips {\n margin-top: 20px;\n }\n \n .neoface-doc-tip {\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 14px 16px;\n background: rgba(0, 200, 255, 0.08);\n border: 1px solid rgba(0, 200, 255, 0.15);\n border-radius: 10px;\n color: rgba(255, 255, 255, 0.8);\n font-size: 13px;\n line-height: 1;\n text-align: center;\n }\n \n .neoface-doc-tip span {\n transform: translateY(-2px);\n }\n \n .neoface-doc-tip-icon {\n color: #00c8ff;\n flex-shrink: 0;\n }\n \n /* Camera */\n .neoface-doc-camera-container {\n position: relative;\n background: #000;\n border-radius: 16px;\n overflow: hidden;\n aspect-ratio: 4/3;\n }\n \n .neoface-doc-video {\n width: 100%;\n height: 100%;\n object-fit: cover;\n }\n \n .neoface-doc-canvas {\n display: none;\n }\n \n .neoface-doc-guide {\n position: absolute;\n inset: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n pointer-events: none;\n }\n \n .neoface-doc-frame {\n width: 75%;\n height: 65%;\n position: relative;\n border: 2px dashed rgba(0, 200, 255, 0.5);\n border-radius: 12px;\n background: rgba(0, 200, 255, 0.03);\n }\n \n .neoface-doc-corner {\n position: absolute;\n width: 28px;\n height: 28px;\n border-color: #00c8ff;\n border-style: solid;\n }\n \n .neoface-doc-corner-tl { top: -2px; left: -2px; border-width: 4px 0 0 4px; border-radius: 10px 0 0 0; }\n .neoface-doc-corner-tr { top: -2px; right: -2px; border-width: 4px 4px 0 0; border-radius: 0 10px 0 0; }\n .neoface-doc-corner-bl { bottom: -2px; left: -2px; border-width: 0 0 4px 4px; border-radius: 0 0 0 10px; }\n .neoface-doc-corner-br { bottom: -2px; right: -2px; border-width: 0 4px 4px 0; border-radius: 0 0 10px 0; }\n \n .neoface-doc-frame-label {\n position: absolute;\n bottom: -30px;\n left: 50%;\n transform: translateX(-50%);\n background: rgba(0, 0, 0, 0.7);\n color: #00c8ff;\n font-size: 12px;\n font-weight: 500;\n padding: 6px 14px;\n border-radius: 20px;\n white-space: nowrap;\n }\n \n .neoface-doc-loading {\n position: absolute;\n inset: 0;\n background: rgba(0, 0, 0, 0.7);\n display: none;\n align-items: center;\n justify-content: center;\n }\n \n .neoface-doc-spinner {\n width: 48px;\n height: 48px;\n border: 4px solid rgba(255, 255, 255, 0.2);\n border-top-color: #00c8ff;\n border-radius: 50%;\n animation: neoface-doc-spin 0.8s linear infinite;\n }\n \n @keyframes neoface-doc-spin {\n to { transform: rotate(360deg); }\n }\n \n .neoface-doc-instructions {\n display: flex;\n flex-direction: column;\n gap: 8px;\n margin-top: 16px;\n }\n \n .neoface-doc-instruction {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 10px 14px;\n background: rgba(255, 255, 255, 0.04);\n border-radius: 10px;\n font-size: 13px;\n color: rgba(255, 255, 255, 0.75);\n }\n \n .neoface-doc-instruction-icon {\n color: rgba(255, 255, 255, 0.5);\n flex-shrink: 0;\n }\n \n /* Preview */\n .neoface-doc-preview-single {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 12px;\n }\n \n .neoface-doc-preview-label {\n font-size: 12px;\n font-weight: 500;\n color: rgba(255, 255, 255, 0.6);\n text-transform: uppercase;\n letter-spacing: 0.5px;\n text-align: center;\n }\n \n .neoface-doc-preview-img {\n width: 75%;\n height: auto;\n border-radius: 12px;\n border: 2px solid rgba(0, 200, 255, 0.3);\n box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);\n }\n \n .neoface-doc-preview-hint {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 12px 16px;\n background: rgba(0, 200, 255, 0.08);\n border: 1px solid rgba(0, 200, 255, 0.15);\n border-radius: 10px;\n font-size: 13px;\n color: rgba(255, 255, 255, 0.8);\n margin-top: 8px;\n }\n \n .neoface-doc-preview-hint svg {\n color: #00c8ff;\n flex-shrink: 0;\n }\n \n /* Footer */\n .neoface-doc-footer {\n padding: 16px 24px;\n display: flex;\n gap: 12px;\n justify-content: flex-end;\n border-top: 1px solid rgba(255, 255, 255, 0.08);\n }\n \n .neoface-doc-btn {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 12px 20px;\n border-radius: 12px;\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.2s;\n border: none;\n }\n \n .neoface-doc-btn-primary {\n background: linear-gradient(135deg, #00c8ff 0%, #0099cc 100%);\n color: #fff;\n }\n \n .neoface-doc-btn-primary:hover {\n transform: translateY(-2px);\n box-shadow: 0 6px 20px rgba(0, 200, 255, 0.4);\n }\n \n .neoface-doc-btn-success {\n background: linear-gradient(135deg, #10b981 0%, #059669 100%);\n color: #fff;\n }\n \n .neoface-doc-btn-success:hover {\n transform: translateY(-2px);\n box-shadow: 0 6px 20px rgba(16, 185, 129, 0.4);\n }\n \n .neoface-doc-btn-secondary {\n background: rgba(255, 255, 255, 0.08);\n color: rgba(255, 255, 255, 0.8);\n }\n \n .neoface-doc-btn-secondary:hover {\n background: rgba(255, 255, 255, 0.12);\n color: #fff;\n }\n \n /* Branding */\n .neoface-doc-branding {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n padding: 16px 24px;\n border-top: 1px solid rgba(255, 255, 255, 0.06);\n }\n \n .neoface-doc-brand-icon {\n height: 22px;\n width: auto;\n }\n \n .neoface-doc-brand-name-text {\n font-size: 13px;\n font-weight: 600;\n color: rgba(255, 255, 255, 0.9);\n }\n \n .neoface-doc-brand-text {\n font-size: 12px;\n color: rgba(255, 255, 255, 0.5);\n }\n \n .neoface-doc-brand-name {\n font-size: 13px;\n font-weight: 700;\n color: #00c8ff;\n letter-spacing: 1px;\n }\n \n /* Mobile */\n @media (max-width: 500px) {\n .neoface-doc-modal {\n border-radius: 20px;\n }\n \n .neoface-doc-header,\n .neoface-doc-body,\n .neoface-doc-footer {\n padding: 16px 18px;\n }\n \n .neoface-doc-preview-grid.has-back {\n grid-template-columns: 1fr;\n }\n }\n `;\n }\n}\n\n/**\n * Função auxiliar para iniciar a captura de documento\n */\nexport const startDocumentCapture = async (\n options?: DocumentCaptureOptions\n): Promise<DocumentCaptureResult> => {\n const modal = new DocumentCaptureModal(options);\n return modal.open();\n};\n","import { NeoFaceError, ErrorType } from './errors';\n\nexport interface BiometricCaptureOptions {\n mode: 'face' | 'hand' | 'auto';\n onSuccess: (imageData: string, detectedType: 'face' | 'hand') => void;\n onError: (error: NeoFaceError) => void;\n onCancel?: () => void;\n countdown?: number;\n title?: string;\n subtitle?: string;\n}\n\nexport class BiometricCaptureModal {\n private modal: HTMLElement | null = null;\n private video: HTMLVideoElement | null = null;\n private canvas: HTMLCanvasElement | null = null;\n private stream: MediaStream | null = null;\n private options: BiometricCaptureOptions;\n private countdownInterval: number | null = null;\n private isCapturing = false;\n\n constructor(options: BiometricCaptureOptions) {\n this.options = options;\n }\n\n /**\n * Abre o modal de captura biométrica\n */\n public async open(): Promise<void> {\n try {\n await this.createModal();\n await this.initializeCamera();\n this.startCountdown();\n } catch (error) {\n this.options.onError(\n new NeoFaceError(\n 'Erro ao inicializar câmera: ' + (error as Error).message,\n ErrorType.CAMERA_ERROR\n )\n );\n }\n }\n\n /**\n * Fecha o modal e limpa recursos\n */\n public close(): void {\n this.cleanup();\n if (this.modal) {\n document.body.removeChild(this.modal);\n this.modal = null;\n }\n }\n\n /**\n * Cria a estrutura HTML do modal\n */\n private async createModal(): Promise<void> {\n this.modal = document.createElement('div');\n this.modal.className = 'neofaceid-biometric-modal';\n \n const title = this.options.title || this.getDefaultTitle();\n const subtitle = this.options.subtitle || this.getDefaultSubtitle();\n\n this.modal.innerHTML = `\n <div class=\"neofaceid-modal-overlay\">\n <div class=\"neofaceid-modal-content\">\n <div class=\"neofaceid-modal-header\">\n <h2>${title}</h2>\n <button class=\"neofaceid-close-btn\" type=\"button\">&times;</button>\n </div>\n <div class=\"neofaceid-modal-body\">\n <p class=\"neofaceid-subtitle\">${subtitle}</p>\n <div class=\"neofaceid-camera-container\">\n <video class=\"neofaceid-video\" autoplay muted playsinline></video>\n <canvas class=\"neofaceid-canvas\" style=\"display: none;\"></canvas>\n <div class=\"neofaceid-overlay\">\n <div class=\"neofaceid-frame\"></div>\n <div class=\"neofaceid-countdown\">\n <span class=\"neofaceid-countdown-number\">${this.options.countdown || 3}</span>\n </div>\n </div>\n </div>\n <div class=\"neofaceid-status\">\n <p class=\"neofaceid-status-text\">Posicione-se na frente da câmera</p>\n </div>\n </div>\n <div class=\"neofaceid-modal-footer\">\n <button class=\"neofaceid-cancel-btn\" type=\"button\">Cancelar</button>\n <button class=\"neofaceid-capture-btn\" type=\"button\" disabled>Capturar</button>\n </div>\n </div>\n </div>\n `;\n\n // Adiciona estilos CSS\n this.addStyles();\n\n // Adiciona event listeners\n this.addEventListeners();\n\n document.body.appendChild(this.modal);\n }\n\n /**\n * Inicializa a câmera\n */\n private async initializeCamera(): Promise<void> {\n if (!this.modal) throw new Error('Modal não inicializado');\n\n this.video = this.modal.querySelector('.neofaceid-video') as HTMLVideoElement;\n this.canvas = this.modal.querySelector('.neofaceid-canvas') as HTMLCanvasElement;\n\n if (!this.video || !this.canvas) {\n throw new Error('Elementos de vídeo ou canvas não encontrados');\n }\n\n try {\n this.stream = await navigator.mediaDevices.getUserMedia({\n video: {\n width: { ideal: 640 },\n height: { ideal: 480 },\n facingMode: 'user'\n },\n audio: false\n });\n\n this.video.srcObject = this.stream;\n await this.video.play();\n\n // Configura o canvas com as mesmas dimensões do vídeo\n this.canvas.width = this.video.videoWidth;\n this.canvas.height = this.video.videoHeight;\n\n } catch (error) {\n throw new Error('Não foi possível acessar a câmera: ' + (error as Error).message);\n }\n }\n\n /**\n * Inicia a contagem regressiva\n */\n private startCountdown(): void {\n if (!this.modal) return;\n\n const countdownElement = this.modal.querySelector('.neofaceid-countdown-number') as HTMLElement;\n const captureBtn = this.modal.querySelector('.neofaceid-capture-btn') as HTMLButtonElement;\n const statusText = this.modal.querySelector('.neofaceid-status-text') as HTMLElement;\n\n let count = this.options.countdown || 3;\n \n if (countdownElement) {\n countdownElement.textContent = count.toString();\n }\n\n this.countdownInterval = window.setInterval(() => {\n count--;\n \n if (countdownElement) {\n countdownElement.textContent = count.toString();\n }\n\n if (count <= 0) {\n if (this.countdownInterval) {\n clearInterval(this.countdownInterval);\n this.countdownInterval = null;\n }\n \n if (countdownElement) {\n countdownElement.style.display = 'none';\n }\n \n if (captureBtn) {\n captureBtn.disabled = false;\n captureBtn.textContent = 'Capturar Agora';\n }\n \n if (statusText) {\n statusText.textContent = 'Pronto para capturar!';\n }\n\n // Auto-captura se estiver no modo automático\n if (this.options.mode === 'auto') {\n setTimeout(() => this.captureImage(), 500);\n }\n }\n }, 1000);\n }\n\n /**\n * Captura a imagem da câmera\n */\n private async captureImage(): Promise<void> {\n if (this.isCapturing || !this.video || !this.canvas) return;\n\n this.isCapturing = true;\n\n try {\n const context = this.canvas.getContext('2d');\n if (!context) throw new Error('Não foi possível obter contexto do canvas');\n\n // Desenha o frame atual do vídeo no canvas\n context.drawImage(this.video, 0, 0, this.canvas.width, this.canvas.height);\n\n // Converte para base64\n const imageData = this.canvas.toDataURL('image/jpeg', 0.8);\n \n // Remove o prefixo data:image/jpeg;base64,\n const base64Data = imageData.split(',')[1];\n // Mantém também a URL de dados completa para consumo externo\n const dataUrl = `data:image/jpeg;base64,${base64Data}`;\n\n // Detecta o tipo biométrico se estiver no modo auto\n let detectedType: 'face' | 'hand' = this.options.mode === 'auto' \n ? await this.detectBiometricType(base64Data)\n : this.options.mode as 'face' | 'hand';\n\n this.close();\n // Passa a URL de dados completa ao consumidor (SDK), evitando fetch incorreto de caminho longo\n this.options.onSuccess(dataUrl, detectedType);\n\n } catch (error) {\n this.options.onError(\n new NeoFaceError(\n 'Erro ao capturar imagem: ' + (error as Error).message,\n ErrorType.CAPTURE_ERROR\n )\n );\n } finally {\n this.isCapturing = false;\n }\n }\n\n /**\n * Detecta o tipo biométrico na imagem (face ou mão)\n */\n private async detectBiometricType(imageData: string): Promise<'face' | 'hand'> {\n try {\n // Importa a função de detecção\n const { detectBiometricType } = await import('./biometricDetection');\n const result = await detectBiometricType(imageData);\n \n // Se a detecção foi bem-sucedida e tem confiança suficiente\n if (result.type !== 'unknown' && result.confidence > 0.6) {\n return result.type;\n }\n \n // Fallback: assume face como padrão\n return 'face';\n } catch (error) {\n console.warn('Erro na detecção automática, usando face como padrão:', error);\n return 'face';\n }\n }\n\n /**\n * Adiciona event listeners aos elementos do modal\n */\n private addEventListeners(): void {\n if (!this.modal) return;\n\n const closeBtn = this.modal.querySelector('.neofaceid-close-btn');\n const cancelBtn = this.modal.querySelector('.neofaceid-cancel-btn');\n const captureBtn = this.modal.querySelector('.neofaceid-capture-btn');\n\n closeBtn?.addEventListener('click', () => {\n this.close();\n this.options.onCancel?.();\n });\n\n cancelBtn?.addEventListener('click', () => {\n this.close();\n this.options.onCancel?.();\n });\n\n captureBtn?.addEventListener('click', () => {\n this.captureImage();\n });\n\n // Fecha modal ao clicar no overlay\n this.modal.addEventListener('click', (e) => {\n if (e.target === this.modal?.querySelector('.neofaceid-modal-overlay')) {\n this.close();\n this.options.onCancel?.();\n }\n });\n }\n\n /**\n * Limpa recursos (câmera, intervalos, etc.)\n */\n private cleanup(): void {\n if (this.stream) {\n this.stream.getTracks().forEach(track => track.stop());\n this.stream = null;\n }\n\n if (this.countdownInterval) {\n clearInterval(this.countdownInterval);\n this.countdownInterval = null;\n }\n\n this.video = null;\n this.canvas = null;\n this.isCapturing = false;\n }\n\n /**\n * Retorna o título padrão baseado no modo\n */\n private getDefaultTitle(): string {\n switch (this.options.mode) {\n case 'face':\n return 'Autenticação Facial';\n case 'hand':\n return 'Autenticação por Mão';\n case 'auto':\n return 'Autenticação Biométrica';\n default:\n return 'Captura Biométrica';\n }\n }\n\n /**\n * Retorna o subtítulo padrão baseado no modo\n */\n private getDefaultSubtitle(): string {\n switch (this.options.mode) {\n case 'face':\n return 'Posicione seu rosto dentro do quadro e aguarde a captura automática.';\n case 'hand':\n return 'Posicione sua mão dentro do quadro e aguarde a captura automática.';\n case 'auto':\n return 'Posicione seu rosto ou mão dentro do quadro para autenticação automática.';\n default:\n return 'Posicione-se dentro do quadro para captura.';\n }\n }\n\n /**\n * Adiciona estilos CSS ao modal\n */\n private addStyles(): void {\n const styleId = 'neofaceid-biometric-modal-styles';\n \n // Remove estilos existentes se houver\n const existingStyles = document.getElementById(styleId);\n if (existingStyles) {\n existingStyles.remove();\n }\n\n const style = document.createElement('style');\n style.id = styleId;\n style.textContent = `\n .neofaceid-biometric-modal {\n position: fixed;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n z-index: 10000;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n }\n\n .neofaceid-modal-overlay {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n background: rgba(0, 0, 0, 0.8);\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 20px;\n box-sizing: border-box;\n }\n\n .neofaceid-modal-content {\n background: white;\n border-radius: 12px;\n max-width: 600px;\n width: 100%;\n max-height: 90vh;\n overflow: hidden;\n box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3);\n }\n\n .neofaceid-modal-header {\n padding: 20px;\n border-bottom: 1px solid #e0e0e0;\n display: flex;\n justify-content: space-between;\n align-items: center;\n }\n\n .neofaceid-modal-header h2 {\n margin: 0;\n font-size: 24px;\n font-weight: 600;\n color: #333;\n }\n\n .neofaceid-close-btn {\n background: none;\n border: none;\n font-size: 28px;\n cursor: pointer;\n color: #666;\n padding: 0;\n width: 32px;\n height: 32px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 50%;\n transition: background-color 0.2s;\n }\n\n .neofaceid-close-btn:hover {\n background-color: #f0f0f0;\n }\n\n .neofaceid-modal-body {\n padding: 20px;\n }\n\n .neofaceid-subtitle {\n margin: 0 0 20px 0;\n color: #666;\n font-size: 16px;\n line-height: 1.4;\n }\n\n .neofaceid-camera-container {\n position: relative;\n background: #000;\n border-radius: 8px;\n overflow: hidden;\n aspect-ratio: 4/3;\n margin-bottom: 20px;\n }\n\n .neofaceid-video {\n width: 100%;\n height: 100%;\n object-fit: cover;\n }\n\n .neofaceid-canvas {\n position: absolute;\n top: 0;\n left: 0;\n }\n\n .neofaceid-overlay {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n display: flex;\n align-items: center;\n justify-content: center;\n }\n\n .neofaceid-frame {\n width: 200px;\n height: 200px;\n border: 3px solid #4CAF50;\n border-radius: 50%;\n box-shadow: 0 0 0 2px rgba(76, 175, 80, 0.3);\n animation: pulse 2s infinite;\n }\n\n .neofaceid-countdown {\n position: absolute;\n top: 20px;\n right: 20px;\n background: rgba(0, 0, 0, 0.7);\n color: white;\n padding: 10px;\n border-radius: 50%;\n width: 50px;\n height: 50px;\n display: flex;\n align-items: center;\n justify-content: center;\n }\n\n .neofaceid-countdown-number {\n font-size: 24px;\n font-weight: bold;\n }\n\n .neofaceid-status {\n text-align: center;\n margin-bottom: 20px;\n }\n\n .neofaceid-status-text {\n margin: 0;\n color: #666;\n font-size: 16px;\n }\n\n .neofaceid-modal-footer {\n padding: 20px;\n border-top: 1px solid #e0e0e0;\n display: flex;\n gap: 12px;\n justify-content: flex-end;\n }\n\n .neofaceid-cancel-btn,\n .neofaceid-capture-btn {\n padding: 12px 24px;\n border: none;\n border-radius: 6px;\n font-size: 16px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.2s;\n }\n\n .neofaceid-cancel-btn {\n background: #f5f5f5;\n color: #666;\n }\n\n .neofaceid-cancel-btn:hover {\n background: #e0e0e0;\n }\n\n .neofaceid-capture-btn {\n background: #4CAF50;\n color: white;\n }\n\n .neofaceid-capture-btn:hover:not(:disabled) {\n background: #45a049;\n }\n\n .neofaceid-capture-btn:disabled {\n background: #ccc;\n cursor: not-allowed;\n }\n\n @keyframes pulse {\n 0% { box-shadow: 0 0 0 2px rgba(76, 175, 80, 0.3); }\n 50% { box-shadow: 0 0 0 10px rgba(76, 175, 80, 0.1); }\n 100% { box-shadow: 0 0 0 2px rgba(76, 175, 80, 0.3); }\n }\n\n @media (max-width: 640px) {\n .neofaceid-modal-overlay {\n padding: 10px;\n }\n \n .neofaceid-modal-header,\n .neofaceid-modal-body,\n .neofaceid-modal-footer {\n padding: 15px;\n }\n \n .neofaceid-frame {\n width: 150px;\n height: 150px;\n }\n }\n `;\n\n document.head.appendChild(style);\n }\n}","import { NeoFaceError, ErrorType } from './errors';\nimport { getBaseUrl } from './config';\nimport {\n recognizeByPurpose,\n SessionData,\n registerApplication,\n ApplicationRegistrationData,\n ApplicationRegistrationResult,\n // PROOF_OF_LIFE imports\n recordFaceVideo,\n videoToBase64,\n startProofOfLife,\n pollTaskStatus,\n ProofOfLifeResult,\n PersonalDataItem,\n} from './api';\n\n// Re-export types for external usage\nexport type { ProofOfLifeResult, PersonalDataItem };\n\n/**\n * Configuration for NeoFaceID SDK initialization\n */\nexport interface NeoFaceIDConfig {\n appToken: string;\n baseUrl?: string;\n signature?: string;\n sessionData?: SessionData;\n}\n\n/**\n * Result of login recognition\n */\nexport interface LoginRecognitionResult {\n success: boolean;\n personName: string;\n email: string;\n cpf: string;\n signature?: string;\n sessionId?: string;\n confidenceScore?: number;\n accessToken?: string;\n // Note: person_id is NOT included for security reasons\n}\n\n/**\n * Options for capturing face frames\n */\nexport interface CaptureFaceFramesOptions {\n numFrames: number;\n livenessCheck: boolean;\n}\n\n/**\n * Options for login recognition\n */\nexport interface LoginRecognitionOptions {\n biometricData: Blob | Blob[];\n typeOfIdentification: 'FACE' | 'HAND';\n purpose:\n | 'LOGIN'\n | 'PROOF_OF_LIFE'\n | 'AUTHORIZATION'\n | 'SIMPLE_IDENTIFICATION'\n | 'SIMPLIFIED_REGISTRATION';\n confidenceThreshold?: number;\n}\n\n/**\n * Options for Proof of Life verification\n */\nexport interface ProofOfLifeOptions {\n /** Duration of video recording in milliseconds (default: 3000) */\n videoDurationMs?: number;\n /** Maximum polling attempts for task status (default: 60) */\n maxPollingAttempts?: number;\n /** Interval between polling attempts in ms (default: 1000) */\n pollingIntervalMs?: number;\n /** Callback for recording progress (0-100) */\n onRecordingProgress?: (progress: number) => void;\n /** Callback for task status changes */\n onTaskStatusChange?: (status: string, progress?: number) => void;\n}\n\n/**\n * NeoFaceID SDK class for biometric authentication\n *\n * This class provides a high-level API for biometric recognition,\n * supporting both internal use (without signature) and external integrations\n * (with signature and session data for systems like OpsPay).\n *\n * @example\n * ```typescript\n * // For external integrations (with signature)\n * const sdk = new NeoFaceID({\n * appToken: 'your-application-token',\n * baseUrl: 'https://core.neofaceid.com',\n * signature: signatureFromBackend,\n * sessionData: {\n * email: 'user@example.com',\n * cpf: '12345678901',\n * sessionId: 'session-uuid-123'\n * }\n * });\n *\n * // For internal use (without signature)\n * const sdk = new NeoFaceID({\n * appToken: 'your-application-token'\n * });\n * ```\n */\nexport class NeoFaceID {\n private appToken: string;\n private baseUrl: string;\n private signature?: string;\n private sessionData?: SessionData;\n\n /**\n * Creates a new NeoFaceID instance\n * @param config Configuration object\n * @throws NeoFaceError if signature format is invalid\n */\n constructor(config: NeoFaceIDConfig) {\n this.appToken = config.appToken;\n // Usa baseUrl do config, ou a configuração global do SDK\n this.baseUrl = config.baseUrl || getBaseUrl();\n this.signature = config.signature;\n this.sessionData = config.sessionData;\n\n // Validate signature format if provided\n if (this.signature && !this.validateSignatureFormat(this.signature)) {\n throw new NeoFaceError(\n 'Invalid signature format. Expected HMAC-SHA256 hex string (minimum 64 characters)',\n ErrorType.VALIDATION_ERROR\n );\n }\n\n // Validate session data if provided\n if (this.sessionData) {\n if (!this.sessionData.email || !this.sessionData.cpf || !this.sessionData.sessionId) {\n throw new NeoFaceError(\n 'Session data must include email, cpf, and sessionId',\n ErrorType.VALIDATION_ERROR\n );\n }\n }\n }\n\n /**\n * Validates the format of a signature\n * HMAC-SHA256 produces a 64-character hexadecimal string\n * @param signature The signature to validate\n * @returns true if valid, false otherwise\n */\n private validateSignatureFormat(signature: string): boolean {\n // HMAC-SHA256 produces a 64-character hex string\n // Allow longer strings in case of encoding variations\n return /^[a-f0-9]{64,}$/i.test(signature);\n }\n\n /**\n * Captures multiple face frames for biometric recognition\n * @param options Capture options\n * @returns Promise that resolves to an array of image blobs\n * @throws NeoFaceError if capture fails\n */\n async captureFaceFrames(options: CaptureFaceFramesOptions): Promise<Blob[]> {\n const { numFrames, livenessCheck } = options;\n\n if (numFrames < 1 || numFrames > 10) {\n throw new NeoFaceError(\n 'Number of frames must be between 1 and 10',\n ErrorType.VALIDATION_ERROR\n );\n }\n\n const frames: Blob[] = [];\n\n try {\n // Import dynamically to avoid circular dependencies\n const { captureFaceSilently } = await import('./utils/biometricLoginFlow');\n\n for (let i = 0; i < numFrames; i++) {\n // Capture a single frame\n const frame = await captureFaceSilently();\n frames.push(frame);\n\n // Add delay between frames for liveness detection\n // This helps ensure the person is actually present and not using a photo\n if (livenessCheck && i < numFrames - 1) {\n await new Promise(resolve => setTimeout(resolve, 300));\n }\n }\n\n return frames;\n } catch (error) {\n if (error instanceof NeoFaceError) {\n throw error;\n }\n throw new NeoFaceError(\n `Failed to capture face frames: ${error instanceof Error ? error.message : 'Unknown error'\n }`,\n ErrorType.CAPTURE_ERROR\n );\n }\n }\n\n /**\n * Performs login recognition using biometric data\n *\n * This method is designed for external integrations that require\n * signature validation and session management.\n *\n * @param options Recognition options\n * @returns Promise that resolves to recognition result\n * @throws NeoFaceError if recognition fails\n *\n * @example\n * ```typescript\n * const result = await sdk.loginRecognition({\n * biometricData: await sdk.captureFaceFrames({\n * numFrames: 5,\n * livenessCheck: true\n * }),\n * typeOfIdentification: 'FACE',\n * purpose: 'LOGIN',\n * confidenceThreshold: 0.8\n * });\n *\n * // Result includes signature and sessionId for callback validation\n * console.log(result.signature, result.sessionId);\n * ```\n */\n async loginRecognition(options: LoginRecognitionOptions): Promise<LoginRecognitionResult> {\n const { biometricData, typeOfIdentification, purpose, confidenceThreshold = 0.8 } = options;\n\n // Convert biometric data to Blob array if needed\n const imageBlobs: Blob[] = Array.isArray(biometricData) ? biometricData : [biometricData];\n\n if (imageBlobs.length === 0) {\n throw new NeoFaceError(\n 'At least one biometric data frame is required',\n ErrorType.VALIDATION_ERROR\n );\n }\n\n // Use the first frame for recognition (or implement multi-frame processing)\n const primaryImage = imageBlobs[0];\n\n try {\n // Call recognizeByPurpose with signature and sessionData\n const result = await recognizeByPurpose(\n primaryImage,\n this.appToken,\n purpose,\n confidenceThreshold,\n this.signature,\n this.sessionData\n );\n\n if (!result.success) {\n throw new NeoFaceError('Recognition failed', ErrorType.RECOGNITION_FAILED);\n }\n\n // Ensure required fields are present\n if (!result.personName || !result.email || !result.cpf) {\n throw new NeoFaceError(\n 'Incomplete recognition result: missing personName, email, or cpf',\n ErrorType.RECOGNITION_FAILED\n );\n }\n\n return {\n success: true,\n personName: result.personName,\n email: result.email,\n cpf: result.cpf,\n signature: result.signature,\n sessionId: result.sessionId,\n confidenceScore: result.confidenceScore,\n accessToken: result.accessToken,\n };\n } catch (error) {\n if (error instanceof NeoFaceError) {\n throw error;\n }\n throw new NeoFaceError(\n `Login recognition failed: ${error instanceof Error ? error.message : 'Unknown error'}`,\n ErrorType.RECOGNITION_FAILED\n );\n }\n }\n\n /**\n * Register a new application for a consumer\n *\n * This method allows authenticated users to register new applications\n * that will receive their own app_token for API access.\n *\n * @param jwtToken JWT authentication token from logged user\n * @param consumerId Consumer ID (UUID) who will own the application\n * @param applicationData Application registration data\n * @returns Promise with registration result including app_token\n * @throws NeoFaceError if registration fails\n *\n * @example\n * ```typescript\n * const sdk = new NeoFaceID({\n * appToken: 'your-application-token'\n * });\n *\n * const result = await sdk.registerApplication(\n * userJwtToken,\n * consumerUuid,\n * {\n * applicationName: 'My New App',\n * domain: 'example.com',\n * acceptOnlyEmailWithSameDomain: true\n * }\n * );\n *\n * // Use the generated app_token for the new application\n * console.log('New App Token:', result.application.app_token);\n * ```\n */\n async registerApplication(\n jwtToken: string,\n consumerId: string,\n applicationData: ApplicationRegistrationData\n ): Promise<ApplicationRegistrationResult> {\n try {\n const result = await registerApplication(jwtToken, consumerId, applicationData);\n return result;\n } catch (error) {\n if (error instanceof NeoFaceError) {\n throw error;\n }\n throw new NeoFaceError(\n `Application registration failed: ${error instanceof Error ? error.message : 'Unknown error'\n }`,\n ErrorType.VALIDATION_ERROR\n );\n }\n }\n\n /**\n * Performs Proof of Life verification\n *\n * This method records a video from the camera, sends it to the backend\n * for liveness detection and face recognition, and returns the result\n * with personal data filtered by purpose.\n *\n * The process is asynchronous:\n * 1. Records video from the camera (default 3 seconds)\n * 2. Converts video to base64 and sends to backend\n * 3. Backend processes liveness detection and face recognition\n * 4. Polls for task completion\n * 5. Returns personal data of the identified person\n *\n * @param options Configuration options for the proof of life process\n * @returns Promise that resolves to ProofOfLifeResult\n * @throws NeoFaceError if verification fails\n *\n * @example\n * ```typescript\n * const sdk = new NeoFaceID({\n * appToken: 'your-application-token'\n * });\n *\n * const result = await sdk.proofOfLife({\n * videoDurationMs: 3000, // 3 seconds\n * onRecordingProgress: (progress) => {\n * console.log(`Recording: ${progress}%`);\n * },\n * onTaskStatusChange: (status, progress) => {\n * console.log(`Status: ${status}, Progress: ${progress}%`);\n * }\n * });\n *\n * if (result.success && result.isLive) {\n * console.log('Person verified:', result.personalData);\n * console.log('Liveness score:', result.livenessScore);\n * console.log('Face recognition score:', result.faceRecognitionScore);\n * }\n * ```\n */\n async proofOfLife(options: ProofOfLifeOptions = {}): Promise<ProofOfLifeResult> {\n const {\n videoDurationMs = 3000,\n maxPollingAttempts = 60,\n pollingIntervalMs = 1000,\n onRecordingProgress,\n onTaskStatusChange,\n } = options;\n\n try {\n // 1. Record video from camera\n const videoBlob = await recordFaceVideo({\n durationMs: videoDurationMs,\n onProgress: onRecordingProgress,\n });\n\n // 2. Convert video to base64\n const videoBase64 = await videoToBase64(videoBlob);\n\n // 3. Start proof of life process\n const startResult = await startProofOfLife(videoBase64, this.appToken);\n\n // Notify initial status\n if (onTaskStatusChange) {\n onTaskStatusChange('processing', 0);\n }\n\n // 4. Poll for task completion\n const taskResult = await pollTaskStatus(startResult.taskId, this.appToken, {\n maxAttempts: maxPollingAttempts,\n intervalMs: pollingIntervalMs,\n onStatusChange: onTaskStatusChange,\n });\n\n // 5. Build and return result\n if (!taskResult || !taskResult.success) {\n return {\n success: false,\n isLive: taskResult?.is_live || false,\n livenessScore: taskResult?.liveness_score || 0,\n faceRecognitionScore: taskResult?.face_recognition_score || 0,\n combinedConfidence: taskResult?.combined_confidence || 0,\n personId: taskResult?.person_id,\n personalData: taskResult?.personal_data || [],\n processingTime: taskResult?.processing_time || 0,\n historyId: startResult.historyId,\n taskId: startResult.taskId,\n error: taskResult?.error || 'Verification failed',\n message: taskResult?.message || 'Proof of life verification failed',\n };\n }\n\n return {\n success: true,\n isLive: taskResult.is_live || false,\n livenessScore: taskResult.liveness_score || 0,\n faceRecognitionScore: taskResult.face_recognition_score || 0,\n combinedConfidence: taskResult.combined_confidence || 0,\n personId: taskResult.person_id,\n personalData: taskResult.personal_data || [],\n processingTime: taskResult.processing_time || 0,\n historyId: startResult.historyId,\n taskId: startResult.taskId,\n message: 'Proof of life verification successful',\n };\n } catch (error) {\n if (error instanceof NeoFaceError) {\n throw error;\n }\n throw new NeoFaceError(\n `Proof of life failed: ${error instanceof Error ? error.message : 'Unknown error'}`,\n ErrorType.RECOGNITION_FAILED\n );\n }\n }\n}\n","/**\n * Versão atual do SDK NeoFace ID Web\n * Formato: MAJOR.MINOR.PATCH\n * \n * MAJOR: Incrementado quando há mudanças incompatíveis com versões anteriores\n * MINOR: Incrementado quando adicionamos funcionalidades mantendo compatibilidade\n * PATCH: Incrementado quando corrigimos bugs mantendo compatibilidade\n */\nexport const VERSION = '1.10.0';\n\n/**\n * Data de lançamento da versão atual\n */\nexport const RELEASE_DATE = '2026-01-21';\n\n/**\n * Histórico de versões\n */\nexport const CHANGELOG = {\n '1.8.8': {\n date: '2026-01-20',\n changes: [\n 'Removido fallback localhost:8000 - agora requer VITE_API_BASE_URL configurado',\n 'Adicionado erro explícito quando URL da API não está configurada',\n 'Melhoria na validação de configuração do SDK',\n ],\n },\n '1.8.7': {\n date: '2026-01-19',\n changes: [\n 'Correção de caminhos de assets (logos) para compatibilidade com Vite',\n 'Correção de typo no nome do arquivo do logo ícone',\n 'Otimização do fluxo de login biométrico e tratamento de erros de CORS',\n 'Remoção do prefixo /public/ para melhor suporte em build de produção',\n ],\n },\n '1.7.0': {\n date: '2025-12-21',\n changes: [\n 'NEO-104: Método registerApplication() para cadastro de aplicações',\n 'NEO-109: Método proofOfLife() para prova de vida com gravação de vídeo',\n 'NEO-149: Login biométrico com fluxo INTERNAL_RECOGNITION',\n 'DocumentCaptureModal: Modal de captura de documentos (frente/verso)',\n 'Suporte a upload de arquivo para documentos',\n 'Melhorias no BiometricRegistrationModal',\n 'Melhorias no fluxo de fallback para email/senha',\n ],\n },\n '1.6.1': {\n date: '2025-12-20',\n changes: [\n 'Correções e melhorias de estabilidade',\n 'Merge de funcionalidades anteriores para produção',\n ],\n },\n '1.5.0': {\n date: '2025-11-30',\n changes: [\n 'NEO-101: Adicionado método registerBiometric() para cadastro de biometria facial',\n 'NEO-108: Adicionado método authorizeOperation() com captura de 4 fotos e gesto de aprovação',\n 'Adicionado ErrorType.NO_CAMERA',\n 'Criada página de teste test-authorization.html',\n ],\n },\n '1.0.0': {\n date: '2023-06-15',\n changes: [\n 'Lançamento inicial do SDK',\n 'Captura facial com detecção automática',\n 'Verificação de vivacidade',\n 'Reconhecimento facial',\n ],\n },\n};","import React from 'react';\nimport { createRoot } from 'react-dom/client';\nimport { FaceCaptureModal } from './components/FaceCaptureModal';\nimport { BiometricRegistrationModal } from './components/BiometricRegistrationModal';\nimport { validateToken } from './api';\n\ninterface RecognitionResult {\n success: boolean;\n data: {\n faceId: string;\n confidence: number;\n };\n}\n\ninterface BiometricRegistrationResult {\n success: boolean;\n message: string;\n person_id?: string;\n user_id?: string;\n person_validated?: boolean;\n face_processing?: any;\n onboarding_link?: string;\n}\n\ninterface Callbacks {\n onSuccess(user: { name: string; email: string; documentId: string }): void;\n onError(code: string, message: string): void;\n}\n\ninterface BiometricRegistrationCallbacks {\n onSuccess(result: BiometricRegistrationResult): void;\n onError(code: string, message: string): void;\n}\n\nexport function start(applicationToken: string, callbacks: Callbacks): void {\n // Create a container for the modal\n const container = document.createElement('div');\n container.id = 'neoface-modal-container';\n document.body.appendChild(container);\n\n // Function to clean up resources\n const cleanup = () => {\n // Remove the container from the DOM\n if (document.body.contains(container)) {\n document.body.removeChild(container);\n }\n };\n\n // Validate the application token\n validateToken(applicationToken)\n .then(() => {\n // Render the modal\n const modalElement = React.createElement(FaceCaptureModal, {\n accessToken: applicationToken,\n onClose: cleanup,\n onSuccess: (result: RecognitionResult) => {\n // Clean up the modal\n cleanup();\n\n // Call the success callback with user data\n callbacks.onSuccess({\n name: result.data.faceId,\n email: result.data.faceId,\n documentId: result.data.faceId,\n });\n },\n });\n\n const root = createRoot(container);\n root.render(modalElement);\n })\n .catch((error: Error) => {\n // Clean up the container\n cleanup();\n\n // Call the error callback\n callbacks.onError('TOKEN_VALIDATION_ERROR', error.message || 'Failed to validate token');\n });\n}\n\n/**\n * Start biometric registration process with liveness detection\n * @param personData Person registration data (name, cpf, birth_date, email, password)\n * @param applicationToken Application token for authentication\n * @param callbacks Success and error callbacks\n */\nexport function startBiometricRegistration(\n personData: {\n name: string;\n birth_date: string;\n cpf: string;\n email: string;\n password: string;\n },\n applicationToken: string,\n callbacks: BiometricRegistrationCallbacks,\n options?: {\n // Flag para usar API real (sempre true em produção)\n useRealApi?: boolean;\n }\n): void {\n // Create a container for the modal\n const container = document.createElement('div');\n container.id = 'neoface-biometric-registration-container';\n document.body.appendChild(container);\n\n // Function to clean up resources\n const cleanup = () => {\n // Remove the container from the DOM\n if (document.body.contains(container)) {\n document.body.removeChild(container);\n }\n };\n\n // Validate the application token\n validateToken(applicationToken)\n .then(() => {\n // Render the biometric registration modal\n const modalElement = React.createElement(BiometricRegistrationModal, {\n personData,\n applicationToken,\n useRealApi: options?.useRealApi ?? true, // Sempre usa API real em produção\n onClose: cleanup,\n onSuccess: (result: BiometricRegistrationResult) => {\n // Clean up the modal\n cleanup();\n // Call the success callback\n callbacks.onSuccess(result);\n },\n onError: (error: string) => {\n // Clean up the modal\n cleanup();\n // Call the error callback\n callbacks.onError('BIOMETRIC_REGISTRATION_ERROR', error);\n },\n });\n\n const root = createRoot(container);\n root.render(modalElement);\n })\n .catch((error: Error) => {\n // Clean up the container\n cleanup();\n\n // Call the error callback\n callbacks.onError('TOKEN_VALIDATION_ERROR', error.message || 'Failed to validate token');\n });\n}\n\n/**\n * Start liveness capture process only (no registration)\n * @param applicationToken Application token for authentication\n * @param callbacks Success and error callbacks\n */\nexport function startLivenessCapture(\n applicationToken: string,\n callbacks: {\n onSuccess(photos: Blob[]): void;\n onError(code: string, message: string): void;\n }\n): void {\n // Create a container for the modal\n const container = document.createElement('div');\n container.id = 'neoface-liveness-capture-container';\n document.body.appendChild(container);\n\n // Function to clean up resources\n const cleanup = () => {\n // Remove the container from the DOM\n if (document.body.contains(container)) {\n document.body.removeChild(container);\n }\n };\n\n // Validate the application token\n validateToken(applicationToken)\n .then(() => {\n // Render the liveness capture modal\n const modalElement = React.createElement(BiometricRegistrationModal, {\n applicationToken,\n onClose: cleanup,\n onPhotosCaptured: (photos: Blob[]) => {\n // Clean up the modal\n // callbacks.onSuccess(photos);\n // Wait for onClose or custom \"Continuar\" button in Success screen\n // The BiometricRegistrationModal onSuccess prop is not used here, \n // instead we use onPhotosCaptured which is called by processRegistration\n // but we still want to show the success state in UI.\n // The modal Success screen will call onClose when user clicks \"Continuar\".\n // So we should call onSuccess then?\n // Actually, let's call onSuccess immediately so the parent knows we have the photos\n // but the modal is still visible until user clicks \"Continuar\".\n callbacks.onSuccess(photos);\n },\n onError: (error: string) => {\n // Clean up the modal\n cleanup();\n // Call the error callback\n callbacks.onError('LIVENESS_CAPTURE_ERROR', error);\n },\n });\n\n const root = createRoot(container);\n root.render(modalElement);\n })\n .catch((error: Error) => {\n // Clean up the container\n cleanup();\n\n // Call the error callback\n callbacks.onError('TOKEN_VALIDATION_ERROR', error.message || 'Failed to validate token');\n });\n}\n\n// Re-export all public APIs\nexport {\n validateToken,\n validateOnboardingToken,\n recognize,\n recognizeBiometric,\n simpleIdentification,\n recognizeByPurpose,\n loginWithBiometric,\n loginWithEmail,\n registerPersonWithoutFace,\n registerPersonWithBiometric,\n registerBiometric,\n identifyPerson,\n completeOnboarding,\n completeOnboardingWithData,\n type BiometricLoginResult,\n type OnboardingPersonData,\n} from './api';\nexport { FaceCaptureModal } from './components/FaceCaptureModal';\nexport { BiometricRegistrationModal } from './components/BiometricRegistrationModal';\nexport { BiometricCaptureModal } from './BiometricCaptureModal';\nexport { BiometricStatusOverlay, type OverlayStatus } from './components/BiometricStatusOverlay';\nexport { FallbackPrompt } from './components/FallbackPrompt';\nexport { EmailPasswordModal, type EmailPasswordOptions } from './EmailPasswordModal';\nexport {\n // Fluxos de login biométrico\n startFaceLogin,\n startHandLogin,\n startAutoLogin,\n biometricLogin,\n biometricLoginWithFallback,\n // Tipos\n type BiometricLoginOptions,\n} from './biometricLogin';\nexport {\n // Utilitários de performance\n preloadFaceDetectionModels,\n} from './utils/biometricLoginFlow';\nexport {\n detectBiometricType,\n initializeBiometricDetection,\n isAdvancedDetectionAvailable,\n type DetectionResult,\n} from './biometricDetection';\nexport { VERSION, RELEASE_DATE } from './version';\nexport { NeoFaceError, ErrorType } from './errors';\n// SDK Configuration - permite inicializar o SDK com ambiente específico\nexport {\n init,\n getBaseUrl,\n getEnvironment,\n getApplicationToken,\n isInitialized,\n getConfig,\n ENVIRONMENT_URLS,\n type Environment,\n type SDKInitOptions,\n} from './config';\nexport { startOnboarding, type StartOnboardingOptions } from './onboarding';\nexport { NeoFaceID } from './NeoFaceID';\nexport type {\n NeoFaceIDConfig,\n LoginRecognitionResult,\n CaptureFaceFramesOptions,\n LoginRecognitionOptions,\n ProofOfLifeOptions,\n ProofOfLifeResult,\n PersonalDataItem,\n} from './NeoFaceID';\nexport type {\n SessionData,\n ApplicationRegistrationData,\n ApplicationRegistrationResult,\n} from './api';\nexport {\n authorizeOperation,\n type AuthorizationResult,\n type AuthorizationOptions,\n} from './authorization';\nexport {\n DocumentCaptureModal,\n startDocumentCapture,\n type DocumentType,\n type DocumentCaptureOptions,\n type DocumentCaptureResult,\n} from './DocumentCaptureModal';\n","import { NeoFaceError, ErrorType } from './errors';\nimport { getOnboardingDetails, completeOnboarding, OnboardingLinkDetails } from './api';\nimport { OnboardingCaptureModal } from './OnboardingCaptureModal';\n\nexport interface StartOnboardingOptions {\n applicationToken: string;\n onboardingToken: string;\n countdown?: number;\n title?: string;\n subtitle?: string;\n onSuccess: (result: {\n success: boolean;\n message: string;\n person_id?: string;\n identity_data_id?: string;\n confidence_score?: number;\n processing_time?: number;\n details: OnboardingLinkDetails;\n }) => void;\n onError: (error: NeoFaceError) => void;\n onCancel?: () => void;\n}\n\n/**\n * Inicia o fluxo de onboarding: valida token, captura face e documento, conclui no backend.\n * Este método orquestra o processo completo e expõe callbacks de sucesso/erro.\n */\nexport const startOnboarding = async (options: StartOnboardingOptions): Promise<void> => {\n const { applicationToken, onboardingToken, countdown, title, subtitle, onSuccess, onError } = options;\n\n try {\n // 1) Valida e obtém detalhes do link de onboarding\n const details = await getOnboardingDetails(applicationToken, onboardingToken);\n if (!details?.is_valid) {\n throw new NeoFaceError('Link de onboarding inválido ou expirado', ErrorType.VALIDATION_ERROR);\n }\n\n // 2) Configura UI conforme propósito/fluxo (por enquanto sempre face + documento)\n const modal = new OnboardingCaptureModal({ countdown, title, subtitle });\n const { face, document } = await modal.open();\n\n // 3) Envia imagens para concluir onboarding\n const result = await completeOnboarding(applicationToken, onboardingToken, face, document);\n\n // 4) Callback de sucesso\n onSuccess({ ...result, details });\n } catch (err: any) {\n // Tratamento padronizado de erro\n if (err instanceof NeoFaceError) {\n onError(err);\n return;\n }\n const message = err?.message || 'Erro desconhecido no onboarding';\n onError(new NeoFaceError(message, ErrorType.UNKNOWN));\n }\n};"],"names":["m","require$$0","process","env","NODE_ENV","client","createRoot","hydrateRoot","i","__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED","c","o","usingClientEntryPoint","h","React","REACT_ELEMENT_TYPE","Symbol","for","REACT_PORTAL_TYPE","REACT_FRAGMENT_TYPE","REACT_STRICT_MODE_TYPE","REACT_PROFILER_TYPE","REACT_PROVIDER_TYPE","REACT_CONTEXT_TYPE","REACT_FORWARD_REF_TYPE","REACT_SUSPENSE_TYPE","REACT_SUSPENSE_LIST_TYPE","REACT_MEMO_TYPE","REACT_LAZY_TYPE","REACT_OFFSCREEN_TYPE","MAYBE_ITERATOR_SYMBOL","iterator","ReactSharedInternals","error","format","_len2","arguments","length","args","Array","_key2","level","stack","ReactDebugCurrentFrame","getStackAddendum","concat","argsWithFormat","map","item","String","unshift","Function","prototype","apply","call","console","printWarning","REACT_MODULE_REFERENCE","getContextName","type","displayName","getComponentNameFromType","tag","name","$$typeof","_context","outerType","innerType","wrapperName","functionName","getWrappedName","render","outerName","lazyComponent","payload","_payload","init","_init","x","prevLog","prevInfo","prevWarn","prevError","prevGroup","prevGroupCollapsed","prevGroupEnd","assign","Object","disabledDepth","disabledLog","__reactDisabledLog","prefix","ReactCurrentDispatcher","describeBuiltInComponentFrame","source","ownerFn","Error","match","trim","componentFrameCache","reentry","PossiblyWeakMap","WeakMap","Map","describeNativeComponentFrame","fn","construct","control","frame","get","previousDispatcher","previousPrepareStackTrace","prepareStackTrace","current","log","info","warn","group","groupCollapsed","groupEnd","props","configurable","enumerable","value","writable","defineProperties","disableLogs","Fake","defineProperty","set","Reflect","sample","sampleLines","split","controlLines","s","_frame","replace","includes","reenableLogs","syntheticFrame","describeUnknownElementTypeFrameInDEV","isReactComponent","hasOwnProperty","loggedTypeFailures","setCurrentlyValidatingElement","element","owner","_owner","_source","setExtraStackFrame","isArrayImpl","isArray","a","testStringCoercion","checkKeyStringCoercion","e","willCoercionThrow","toStringTag","constructor","typeName","specialPropKeyWarningShown","specialPropRefWarningShown","ReactCurrentOwner","RESERVED_PROPS","key","ref","__self","__source","jsxDEV","config","maybeKey","self","propName","getter","getOwnPropertyDescriptor","isReactWarning","hasValidKey","hasValidRef","warnIfStringRefCannotBeAutoConverted","defaultProps","warnAboutAccessingKey","defineKeyPropWarningGetter","warnAboutAccessingRef","defineRefPropWarningGetter","_store","freeze","ReactElement","propTypesMisspellWarningShown","ReactCurrentOwner$1","ReactDebugCurrentFrame$1","setCurrentlyValidatingElement$1","isValidElement","object","getDeclarationErrorAddendum","ownerHasKeyUseWarning","validateExplicitKey","parentType","validated","currentComponentErrorInfo","parentName","getCurrentComponentErrorInfo","childOwner","validateChildKeys","node","child","iteratorFn","maybeIterable","maybeIterator","getIteratorFn","entries","step","next","done","validatePropTypes","propTypes","typeSpecs","values","location","componentName","has","bind","typeSpecName","error$1","err","ex","message","checkPropTypes","PropTypes","getDefaultProps","isReactClassApproved","didWarnAboutKeySpread","jsxWithValidation","isStaticChildren","validType","getModuleId","isValidElementType","keys","typeString","children","filter","k","beforeExample","join","fragment","validateFragmentProps","jsx","jsxs","reactJsxRuntime_development","Fragment","jsxRuntimeModule","exports","f","l","n","p","q","g","b","d","reactJsxRuntime_production_min","require$$1","CameraPermissionDenied","super","this","CameraStream","stream","__publicField","getStream","stop","getTracks","forEach","track","useCamera","streamRef","useRef","isActive","setIsActive","useState","isInitializingRef","startCamera","useCallback","async","constraints","existingStream","Promise","resolve","setTimeout","currentStream","mediaConstraints","isMobile","test","navigator","userAgent","video","facingMode","width","ideal","height","aspectRatio","audio","getOptimalConstraints","mediaDevices","getUserMedia","cameraStream","stopCamera","cleanup","ErrorType","NeoFaceError","ENVIRONMENT_URLS","development","sandbox","production","globalConfig","baseUrl","applicationToken","environment","initialized","getBaseUrl","getApiBaseUrl","ensureSecureContext","isLocalhost","window","hostname","isSecureContext","NETWORK","createTimeoutController","controller","AbortController","timeoutId","abort","signal","addEventListener","clearTimeout","compressImage","imageBlob","reject","img","Image","onload","canvas","document","createElement","ctx","getContext","drawImage","toBlob","blob","onerror","src","URL","createObjectURL","compressDocumentImage","MAX_SIZE","validateToken","fetch","method","headers","body","JSON","stringify","ok","NETWORK_ERROR","completeOnboarding","onboardingToken","faceImage","documentImage","compressedFace","compressedDocument","formData","FormData","append","response","encodeURIComponent","data","json","status","INVALID_TOKEN","NOT_FOUND","success","person_id","identity_data_id","confidence_score","processing_time","recognize","image","compressedImage","RECOGNITION_FAILED","accessToken","loginWithBiometric","arrayBuffer","base64Image","btoa","Uint8Array","reduce","byte","fromCharCode","biometric_type","LOGIN_FAILED","alias","user","id","_a","email","_b","role","_c","_d","active","_e","person","birth_date","recognition_id","recognizeByPurpose","purpose","confidenceThreshold","signature","sessionData","reader","FileReader","result","readAsDataURL","requestBody","biometric_data","type_of_identification","confidence_threshold","personName","person_name","cpf","confidenceScore","access_token","sessionId","registerPersonWithBiometric","personData","facePhotos","options","VALIDATION_ERROR","facePhotosBase64","all","photo","documentFrontBase64","documentBackBase64","documentFront","compressed","documentBack","password","password_confirm","face_photos","document_images","front","back","documentType","errorData","errors","errorMessages","field","messages","error_code","errorMessage","parseError","task_id","user_id","person_validated","face_processing","onboarding_link","_f","_g","_h","loginWithEmail","access","modalReducer","state","action","styles","overlay","position","top","left","right","bottom","backgroundColor","display","justifyContent","alignItems","zIndex","modal","borderRadius","overflow","maxWidth","maxHeight","guide","transform","border","pointerEvents","controls","gap","button","padding","color","fontSize","fontWeight","cursor","textAlign","spinner","borderTopColor","animation","closeButton","FaceCaptureModal","onClose","onSuccess","videoRef","canvasRef","faceDetectionTimeoutRef","livenessCheckRef","dispatch","useReducer","faceDetected","setFaceDetected","livenessAttempts","setLivenessAttempts","startFaceDetection","detectFaces","detections","faceapi","detectAllFaces","TinyFaceDetectorOptions","isOneFace","isCentered","detection","box","videoWidth","videoHeight","centerX","centerY","centerWidth","centerHeight","y","isFaceCentered","prev","useEffect","nets","tinyFaceDetector","loadFromUri","faceLandmark68Net","loadModels","clearInterval","srcObject","initializeCamera","handleCapture","context","faceId","documentId","confidence","style","className","onClick","autoPlay","playsInline","muted","stepSequence","capturedPhotos","currentStepIndex","stepProgress","newPhotos","progress","initialState","livenessInstructions","center","up","down","complete","sequence","CALIBRATION","BiometricRegistrationModal","onPhotosCaptured","onError","useRealApi","holdStartTimeRef","progressIntervalRef","isCapturingRef","currentStepRef","captureStateRef","faceDistance","setFaceDistance","newStep","MODEL_URL","mounted","waitForVideo","attempts","play","initCamera","isRunning","lastDetectionTime","detectFace","requestAnimationFrame","now","Date","detectSingleFace","inputSize","scoreThreshold","withFaceLandmarks","hasFace","faceWidth","landmarks","angles","points","positions","_positions","getPoint","index","point","_x","_y","noseTip","chin","leftEye","rightEye","eyeCenterX","yaw","Math","abs","eyeCenterY","noseOffsetY","pitch","round","calculateHeadAngles","positionCorrect","setInterval","elapsed","min","capturePhoto","manageHoldAndCapture","checkPositionMatch","startTimer","readyState","capturedState","scale","nextStepIndex","nextStep","allPhotos","processRegistration","photos","adaptedResult","viewBox","fill","stroke","strokeWidth","strokeLinecap","strokeLinejoin","alt","renderLivenessDetection","marginTop","paddingTop","borderTop","cx","cy","r","BiometricStatusOverlay","getStyles","renderContent","isPulsing","currentStatus","showLogo","showSuccess","showError","show","container","updateStatus","innerHTML","appendChild","close","overlayElement","querySelector","classList","add","contains","removeChild","FallbackPromptComponent","onRetry","onCredentials","onCancel","xmlns","FallbackPrompt","root","unmount","EmailPasswordModal","open","createModal","modalElement","addStyles","title","subtitle","handleSubmit","styleId","getElementById","textContent","head","event","preventDefault","emailInput","passwordInput","modelsLoaded","modelsLoading","preloadFaceDetectionModels","attemptLogin","fastMode","opacity","setAttribute","onloadedmetadata","maxTime","interval","maxAttempts","ceil","isResolved","finish","checkFace","detectFaceQuickly","parentElement","CAPTURE_ERROR","remove","UNKNOWN","executeBiometricLoginFlow","onFallbackRequest","fallbackPrompt","lastError","catch","tryLogin","faceDetectionAttempts","then","mediaStream","tryCapture","CAMERA_ERROR","startFaceLogin","fallbackOptions","INITIALIZATION_ERROR","startHandLogin","captureOptions","mode","imageData","_detectedType","res","countdown","BiometricCaptureModal","startAutoLogin","detectBiometricType","base64Data","dataUrl","startsWith","faceResult","loadFaceApiModels","tinyOptions","bestDetection","best","score","details","faces","handResult","handFeatures","skinPixels","totalPixels","isSkinColor","skinRatio","imageSize","analyzeHandFeatures","getImageData","detectHand","max","cb","cr","isLoaded","initializeBiometricDetection","isAdvancedDetectionAvailable","OnboardingCaptureModal","countdownSeconds","createDom","runFaceCaptureCountdown","faceBlob","captureFrame","updateTexts","waitForUserCaptureClick","documentBlob","face","closeBtn","cancelBtn","onclick","disabled","countdownEl","countdownNumberEl","statusText","remaining","captureBtn","handler","removeEventListener","titleEl","subtitleEl","frameEl","t","PHOTO_INSTRUCTIONS","DOCUMENT_INFO","RG","icon","hasBack","frontLabel","backLabel","CNH","CPF","DocumentCaptureModal","useBackCamera","preSelectedDocument","resolvePromise","selectedDocument","currentStep","createOverlay","content","renderSelectStep","bindSelectEvents","renderCaptureStep","bindCaptureEvents","renderPreviewStep","bindPreviewEvents","renderBranding","docInfo","isBack","label","isPreviewFront","isLastStep","querySelectorAll","option","getAttribute","bindCloseEvent","backBtn","uploadBtn","fileInput","handleBack","click","handleFileUpload","file","target","files","loadingEl","compressedBlob","compressUploadedImage","frontBlob","backBlob","Blob","confirmBtn","retakeBtn","handleConfirm","handleRetake","previewImg","handleCancel","fallbackError","frontImage","backImage","loading","cropWidth","floor","cropHeight","cropX","cropY","startCountdown","getDefaultTitle","getDefaultSubtitle","addEventListeners","countdownElement","count","toString","countdownInterval","captureImage","isCapturing","toDataURL","detectedType","biometricDetection","existingStyles","appToken","validateSignatureFormat","captureFaceFrames","numFrames","livenessCheck","frames","captureFaceSilently","biometricLoginFlow","push","loginRecognition","biometricData","typeOfIdentification","imageBlobs","primaryImage","registerApplication","jwtToken","consumerId","applicationData","Authorization","consumer","application_name","applicationName","domain","accept_only_email_with_same_domain","acceptOnlyEmailWithSameDomain","application","app_token","is_active","created_at","proofOfLife","videoDurationMs","maxPollingAttempts","pollingIntervalMs","onRecordingProgress","onTaskStatusChange","videoBlob","durationMs","mimeType","onProgress","mediaRecorder","chunks","selectedMimeType","mimeTypes","MediaRecorder","isTypeSupported","videoBitsPerSecond","ondataavailable","size","onstop","start","progressInterval","progressTimer","NO_CAMERA","recordFaceVideo","videoBase64","base64","videoToBase64","startResult","taskId","historyId","history_id","startProofOfLife","taskResult","intervalMs","onStatusChange","Accept","percentage","pollTaskStatus","isLive","is_live","livenessScore","liveness_score","faceRecognitionScore","face_recognition_score","combinedConfidence","combined_confidence","personId","personalData","personal_data","processingTime","cpfClean","captureDelay","autoplay","instruction","onPhotoTaken","lastPhoto","TOTAL_PHOTOS","random","substring","recognitionResult","authorizationHash","birthDate","personImages","compressedPersonImages","cnpj","liveness_passed","document_verified","person_created","birthDateStr","PERSON_NOT_FOUND","today","age","getFullYear","monthDiff","getMonth","getDate","envUrl","url","require","pathToFileURL","__filename","href","_documentCurrentScript","tagName","toUpperCase","baseURI","__vite_import_meta_env__","VITE_API_BASE_URL","have_biometric_data","documentNumber","document_type","document_number","callbacks","getOnboardingDetails","is_valid"],"mappings":"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","x_google_ignoreList":[0,1,2,3]}
1
+ {"version":3,"file":"neoface-id-sdk.umd.js","sources":["../node_modules/react-dom/client.js","../node_modules/react/cjs/react-jsx-runtime.development.js","../node_modules/react/jsx-runtime.js","../node_modules/react/cjs/react-jsx-runtime.production.min.js","../src/hooks/useCamera.ts","../src/errors.ts","../src/config.ts","../src/api.ts","../src/components/FaceCaptureModal.tsx","../src/components/BiometricRegistrationModal.tsx","../src/BiometricCaptureModal.ts","../src/components/BiometricStatusOverlay.ts","../src/components/FallbackPrompt.tsx","../src/EmailPasswordModal.ts","../src/utils/biometricLoginFlow.ts","../src/biometricLogin.ts","../src/biometricDetection.ts","../src/OnboardingCaptureModal.ts","../src/authorization.ts","../src/DocumentCaptureModal.ts","../src/NeoFaceID.ts","../src/version.ts","../src/index.ts","../src/onboarding.ts"],"sourcesContent":["'use strict';\n\nvar m = require('react-dom');\nif (process.env.NODE_ENV === 'production') {\n exports.createRoot = m.createRoot;\n exports.hydrateRoot = m.hydrateRoot;\n} else {\n var i = m.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;\n exports.createRoot = function(c, o) {\n i.usingClientEntryPoint = true;\n try {\n return m.createRoot(c, o);\n } finally {\n i.usingClientEntryPoint = false;\n }\n };\n exports.hydrateRoot = function(c, h, o) {\n i.usingClientEntryPoint = true;\n try {\n return m.hydrateRoot(c, h, o);\n } finally {\n i.usingClientEntryPoint = false;\n }\n };\n}\n","/**\n * @license React\n * react-jsx-runtime.development.js\n *\n * Copyright (c) Facebook, Inc. and its affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n'use strict';\n\nif (process.env.NODE_ENV !== \"production\") {\n (function() {\n'use strict';\n\nvar React = require('react');\n\n// ATTENTION\n// When adding new symbols to this file,\n// Please consider also adding to 'react-devtools-shared/src/backend/ReactSymbols'\n// The Symbol used to tag the ReactElement-like types.\nvar REACT_ELEMENT_TYPE = Symbol.for('react.element');\nvar REACT_PORTAL_TYPE = Symbol.for('react.portal');\nvar REACT_FRAGMENT_TYPE = Symbol.for('react.fragment');\nvar REACT_STRICT_MODE_TYPE = Symbol.for('react.strict_mode');\nvar REACT_PROFILER_TYPE = Symbol.for('react.profiler');\nvar REACT_PROVIDER_TYPE = Symbol.for('react.provider');\nvar REACT_CONTEXT_TYPE = Symbol.for('react.context');\nvar REACT_FORWARD_REF_TYPE = Symbol.for('react.forward_ref');\nvar REACT_SUSPENSE_TYPE = Symbol.for('react.suspense');\nvar REACT_SUSPENSE_LIST_TYPE = Symbol.for('react.suspense_list');\nvar REACT_MEMO_TYPE = Symbol.for('react.memo');\nvar REACT_LAZY_TYPE = Symbol.for('react.lazy');\nvar REACT_OFFSCREEN_TYPE = Symbol.for('react.offscreen');\nvar MAYBE_ITERATOR_SYMBOL = Symbol.iterator;\nvar FAUX_ITERATOR_SYMBOL = '@@iterator';\nfunction getIteratorFn(maybeIterable) {\n if (maybeIterable === null || typeof maybeIterable !== 'object') {\n return null;\n }\n\n var maybeIterator = MAYBE_ITERATOR_SYMBOL && maybeIterable[MAYBE_ITERATOR_SYMBOL] || maybeIterable[FAUX_ITERATOR_SYMBOL];\n\n if (typeof maybeIterator === 'function') {\n return maybeIterator;\n }\n\n return null;\n}\n\nvar ReactSharedInternals = React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;\n\nfunction error(format) {\n {\n {\n for (var _len2 = arguments.length, args = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {\n args[_key2 - 1] = arguments[_key2];\n }\n\n printWarning('error', format, args);\n }\n }\n}\n\nfunction printWarning(level, format, args) {\n // When changing this logic, you might want to also\n // update consoleWithStackDev.www.js as well.\n {\n var ReactDebugCurrentFrame = ReactSharedInternals.ReactDebugCurrentFrame;\n var stack = ReactDebugCurrentFrame.getStackAddendum();\n\n if (stack !== '') {\n format += '%s';\n args = args.concat([stack]);\n } // eslint-disable-next-line react-internal/safe-string-coercion\n\n\n var argsWithFormat = args.map(function (item) {\n return String(item);\n }); // Careful: RN currently depends on this prefix\n\n argsWithFormat.unshift('Warning: ' + format); // We intentionally don't use spread (or .apply) directly because it\n // breaks IE9: https://github.com/facebook/react/issues/13610\n // eslint-disable-next-line react-internal/no-production-logging\n\n Function.prototype.apply.call(console[level], console, argsWithFormat);\n }\n}\n\n// -----------------------------------------------------------------------------\n\nvar enableScopeAPI = false; // Experimental Create Event Handle API.\nvar enableCacheElement = false;\nvar enableTransitionTracing = false; // No known bugs, but needs performance testing\n\nvar enableLegacyHidden = false; // Enables unstable_avoidThisFallback feature in Fiber\n// stuff. Intended to enable React core members to more easily debug scheduling\n// issues in DEV builds.\n\nvar enableDebugTracing = false; // Track which Fiber(s) schedule render work.\n\nvar REACT_MODULE_REFERENCE;\n\n{\n REACT_MODULE_REFERENCE = Symbol.for('react.module.reference');\n}\n\nfunction isValidElementType(type) {\n if (typeof type === 'string' || typeof type === 'function') {\n return true;\n } // Note: typeof might be other than 'symbol' or 'number' (e.g. if it's a polyfill).\n\n\n if (type === REACT_FRAGMENT_TYPE || type === REACT_PROFILER_TYPE || enableDebugTracing || type === REACT_STRICT_MODE_TYPE || type === REACT_SUSPENSE_TYPE || type === REACT_SUSPENSE_LIST_TYPE || enableLegacyHidden || type === REACT_OFFSCREEN_TYPE || enableScopeAPI || enableCacheElement || enableTransitionTracing ) {\n return true;\n }\n\n if (typeof type === 'object' && type !== null) {\n if (type.$$typeof === REACT_LAZY_TYPE || type.$$typeof === REACT_MEMO_TYPE || type.$$typeof === REACT_PROVIDER_TYPE || type.$$typeof === REACT_CONTEXT_TYPE || type.$$typeof === REACT_FORWARD_REF_TYPE || // This needs to include all possible module reference object\n // types supported by any Flight configuration anywhere since\n // we don't know which Flight build this will end up being used\n // with.\n type.$$typeof === REACT_MODULE_REFERENCE || type.getModuleId !== undefined) {\n return true;\n }\n }\n\n return false;\n}\n\nfunction getWrappedName(outerType, innerType, wrapperName) {\n var displayName = outerType.displayName;\n\n if (displayName) {\n return displayName;\n }\n\n var functionName = innerType.displayName || innerType.name || '';\n return functionName !== '' ? wrapperName + \"(\" + functionName + \")\" : wrapperName;\n} // Keep in sync with react-reconciler/getComponentNameFromFiber\n\n\nfunction getContextName(type) {\n return type.displayName || 'Context';\n} // Note that the reconciler package should generally prefer to use getComponentNameFromFiber() instead.\n\n\nfunction getComponentNameFromType(type) {\n if (type == null) {\n // Host root, text node or just invalid type.\n return null;\n }\n\n {\n if (typeof type.tag === 'number') {\n error('Received an unexpected object in getComponentNameFromType(). ' + 'This is likely a bug in React. Please file an issue.');\n }\n }\n\n if (typeof type === 'function') {\n return type.displayName || type.name || null;\n }\n\n if (typeof type === 'string') {\n return type;\n }\n\n switch (type) {\n case REACT_FRAGMENT_TYPE:\n return 'Fragment';\n\n case REACT_PORTAL_TYPE:\n return 'Portal';\n\n case REACT_PROFILER_TYPE:\n return 'Profiler';\n\n case REACT_STRICT_MODE_TYPE:\n return 'StrictMode';\n\n case REACT_SUSPENSE_TYPE:\n return 'Suspense';\n\n case REACT_SUSPENSE_LIST_TYPE:\n return 'SuspenseList';\n\n }\n\n if (typeof type === 'object') {\n switch (type.$$typeof) {\n case REACT_CONTEXT_TYPE:\n var context = type;\n return getContextName(context) + '.Consumer';\n\n case REACT_PROVIDER_TYPE:\n var provider = type;\n return getContextName(provider._context) + '.Provider';\n\n case REACT_FORWARD_REF_TYPE:\n return getWrappedName(type, type.render, 'ForwardRef');\n\n case REACT_MEMO_TYPE:\n var outerName = type.displayName || null;\n\n if (outerName !== null) {\n return outerName;\n }\n\n return getComponentNameFromType(type.type) || 'Memo';\n\n case REACT_LAZY_TYPE:\n {\n var lazyComponent = type;\n var payload = lazyComponent._payload;\n var init = lazyComponent._init;\n\n try {\n return getComponentNameFromType(init(payload));\n } catch (x) {\n return null;\n }\n }\n\n // eslint-disable-next-line no-fallthrough\n }\n }\n\n return null;\n}\n\nvar assign = Object.assign;\n\n// Helpers to patch console.logs to avoid logging during side-effect free\n// replaying on render function. This currently only patches the object\n// lazily which won't cover if the log function was extracted eagerly.\n// We could also eagerly patch the method.\nvar disabledDepth = 0;\nvar prevLog;\nvar prevInfo;\nvar prevWarn;\nvar prevError;\nvar prevGroup;\nvar prevGroupCollapsed;\nvar prevGroupEnd;\n\nfunction disabledLog() {}\n\ndisabledLog.__reactDisabledLog = true;\nfunction disableLogs() {\n {\n if (disabledDepth === 0) {\n /* eslint-disable react-internal/no-production-logging */\n prevLog = console.log;\n prevInfo = console.info;\n prevWarn = console.warn;\n prevError = console.error;\n prevGroup = console.group;\n prevGroupCollapsed = console.groupCollapsed;\n prevGroupEnd = console.groupEnd; // https://github.com/facebook/react/issues/19099\n\n var props = {\n configurable: true,\n enumerable: true,\n value: disabledLog,\n writable: true\n }; // $FlowFixMe Flow thinks console is immutable.\n\n Object.defineProperties(console, {\n info: props,\n log: props,\n warn: props,\n error: props,\n group: props,\n groupCollapsed: props,\n groupEnd: props\n });\n /* eslint-enable react-internal/no-production-logging */\n }\n\n disabledDepth++;\n }\n}\nfunction reenableLogs() {\n {\n disabledDepth--;\n\n if (disabledDepth === 0) {\n /* eslint-disable react-internal/no-production-logging */\n var props = {\n configurable: true,\n enumerable: true,\n writable: true\n }; // $FlowFixMe Flow thinks console is immutable.\n\n Object.defineProperties(console, {\n log: assign({}, props, {\n value: prevLog\n }),\n info: assign({}, props, {\n value: prevInfo\n }),\n warn: assign({}, props, {\n value: prevWarn\n }),\n error: assign({}, props, {\n value: prevError\n }),\n group: assign({}, props, {\n value: prevGroup\n }),\n groupCollapsed: assign({}, props, {\n value: prevGroupCollapsed\n }),\n groupEnd: assign({}, props, {\n value: prevGroupEnd\n })\n });\n /* eslint-enable react-internal/no-production-logging */\n }\n\n if (disabledDepth < 0) {\n error('disabledDepth fell below zero. ' + 'This is a bug in React. Please file an issue.');\n }\n }\n}\n\nvar ReactCurrentDispatcher = ReactSharedInternals.ReactCurrentDispatcher;\nvar prefix;\nfunction describeBuiltInComponentFrame(name, source, ownerFn) {\n {\n if (prefix === undefined) {\n // Extract the VM specific prefix used by each line.\n try {\n throw Error();\n } catch (x) {\n var match = x.stack.trim().match(/\\n( *(at )?)/);\n prefix = match && match[1] || '';\n }\n } // We use the prefix to ensure our stacks line up with native stack frames.\n\n\n return '\\n' + prefix + name;\n }\n}\nvar reentry = false;\nvar componentFrameCache;\n\n{\n var PossiblyWeakMap = typeof WeakMap === 'function' ? WeakMap : Map;\n componentFrameCache = new PossiblyWeakMap();\n}\n\nfunction describeNativeComponentFrame(fn, construct) {\n // If something asked for a stack inside a fake render, it should get ignored.\n if ( !fn || reentry) {\n return '';\n }\n\n {\n var frame = componentFrameCache.get(fn);\n\n if (frame !== undefined) {\n return frame;\n }\n }\n\n var control;\n reentry = true;\n var previousPrepareStackTrace = Error.prepareStackTrace; // $FlowFixMe It does accept undefined.\n\n Error.prepareStackTrace = undefined;\n var previousDispatcher;\n\n {\n previousDispatcher = ReactCurrentDispatcher.current; // Set the dispatcher in DEV because this might be call in the render function\n // for warnings.\n\n ReactCurrentDispatcher.current = null;\n disableLogs();\n }\n\n try {\n // This should throw.\n if (construct) {\n // Something should be setting the props in the constructor.\n var Fake = function () {\n throw Error();\n }; // $FlowFixMe\n\n\n Object.defineProperty(Fake.prototype, 'props', {\n set: function () {\n // We use a throwing setter instead of frozen or non-writable props\n // because that won't throw in a non-strict mode function.\n throw Error();\n }\n });\n\n if (typeof Reflect === 'object' && Reflect.construct) {\n // We construct a different control for this case to include any extra\n // frames added by the construct call.\n try {\n Reflect.construct(Fake, []);\n } catch (x) {\n control = x;\n }\n\n Reflect.construct(fn, [], Fake);\n } else {\n try {\n Fake.call();\n } catch (x) {\n control = x;\n }\n\n fn.call(Fake.prototype);\n }\n } else {\n try {\n throw Error();\n } catch (x) {\n control = x;\n }\n\n fn();\n }\n } catch (sample) {\n // This is inlined manually because closure doesn't do it for us.\n if (sample && control && typeof sample.stack === 'string') {\n // This extracts the first frame from the sample that isn't also in the control.\n // Skipping one frame that we assume is the frame that calls the two.\n var sampleLines = sample.stack.split('\\n');\n var controlLines = control.stack.split('\\n');\n var s = sampleLines.length - 1;\n var c = controlLines.length - 1;\n\n while (s >= 1 && c >= 0 && sampleLines[s] !== controlLines[c]) {\n // We expect at least one stack frame to be shared.\n // Typically this will be the root most one. However, stack frames may be\n // cut off due to maximum stack limits. In this case, one maybe cut off\n // earlier than the other. We assume that the sample is longer or the same\n // and there for cut off earlier. So we should find the root most frame in\n // the sample somewhere in the control.\n c--;\n }\n\n for (; s >= 1 && c >= 0; s--, c--) {\n // Next we find the first one that isn't the same which should be the\n // frame that called our sample function and the control.\n if (sampleLines[s] !== controlLines[c]) {\n // In V8, the first line is describing the message but other VMs don't.\n // If we're about to return the first line, and the control is also on the same\n // line, that's a pretty good indicator that our sample threw at same line as\n // the control. I.e. before we entered the sample frame. So we ignore this result.\n // This can happen if you passed a class to function component, or non-function.\n if (s !== 1 || c !== 1) {\n do {\n s--;\n c--; // We may still have similar intermediate frames from the construct call.\n // The next one that isn't the same should be our match though.\n\n if (c < 0 || sampleLines[s] !== controlLines[c]) {\n // V8 adds a \"new\" prefix for native classes. Let's remove it to make it prettier.\n var _frame = '\\n' + sampleLines[s].replace(' at new ', ' at '); // If our component frame is labeled \"<anonymous>\"\n // but we have a user-provided \"displayName\"\n // splice it in to make the stack more readable.\n\n\n if (fn.displayName && _frame.includes('<anonymous>')) {\n _frame = _frame.replace('<anonymous>', fn.displayName);\n }\n\n {\n if (typeof fn === 'function') {\n componentFrameCache.set(fn, _frame);\n }\n } // Return the line we found.\n\n\n return _frame;\n }\n } while (s >= 1 && c >= 0);\n }\n\n break;\n }\n }\n }\n } finally {\n reentry = false;\n\n {\n ReactCurrentDispatcher.current = previousDispatcher;\n reenableLogs();\n }\n\n Error.prepareStackTrace = previousPrepareStackTrace;\n } // Fallback to just using the name if we couldn't make it throw.\n\n\n var name = fn ? fn.displayName || fn.name : '';\n var syntheticFrame = name ? describeBuiltInComponentFrame(name) : '';\n\n {\n if (typeof fn === 'function') {\n componentFrameCache.set(fn, syntheticFrame);\n }\n }\n\n return syntheticFrame;\n}\nfunction describeFunctionComponentFrame(fn, source, ownerFn) {\n {\n return describeNativeComponentFrame(fn, false);\n }\n}\n\nfunction shouldConstruct(Component) {\n var prototype = Component.prototype;\n return !!(prototype && prototype.isReactComponent);\n}\n\nfunction describeUnknownElementTypeFrameInDEV(type, source, ownerFn) {\n\n if (type == null) {\n return '';\n }\n\n if (typeof type === 'function') {\n {\n return describeNativeComponentFrame(type, shouldConstruct(type));\n }\n }\n\n if (typeof type === 'string') {\n return describeBuiltInComponentFrame(type);\n }\n\n switch (type) {\n case REACT_SUSPENSE_TYPE:\n return describeBuiltInComponentFrame('Suspense');\n\n case REACT_SUSPENSE_LIST_TYPE:\n return describeBuiltInComponentFrame('SuspenseList');\n }\n\n if (typeof type === 'object') {\n switch (type.$$typeof) {\n case REACT_FORWARD_REF_TYPE:\n return describeFunctionComponentFrame(type.render);\n\n case REACT_MEMO_TYPE:\n // Memo may contain any component type so we recursively resolve it.\n return describeUnknownElementTypeFrameInDEV(type.type, source, ownerFn);\n\n case REACT_LAZY_TYPE:\n {\n var lazyComponent = type;\n var payload = lazyComponent._payload;\n var init = lazyComponent._init;\n\n try {\n // Lazy may contain any component type so we recursively resolve it.\n return describeUnknownElementTypeFrameInDEV(init(payload), source, ownerFn);\n } catch (x) {}\n }\n }\n }\n\n return '';\n}\n\nvar hasOwnProperty = Object.prototype.hasOwnProperty;\n\nvar loggedTypeFailures = {};\nvar ReactDebugCurrentFrame = ReactSharedInternals.ReactDebugCurrentFrame;\n\nfunction setCurrentlyValidatingElement(element) {\n {\n if (element) {\n var owner = element._owner;\n var stack = describeUnknownElementTypeFrameInDEV(element.type, element._source, owner ? owner.type : null);\n ReactDebugCurrentFrame.setExtraStackFrame(stack);\n } else {\n ReactDebugCurrentFrame.setExtraStackFrame(null);\n }\n }\n}\n\nfunction checkPropTypes(typeSpecs, values, location, componentName, element) {\n {\n // $FlowFixMe This is okay but Flow doesn't know it.\n var has = Function.call.bind(hasOwnProperty);\n\n for (var typeSpecName in typeSpecs) {\n if (has(typeSpecs, typeSpecName)) {\n var error$1 = void 0; // Prop type validation may throw. In case they do, we don't want to\n // fail the render phase where it didn't fail before. So we log it.\n // After these have been cleaned up, we'll let them throw.\n\n try {\n // This is intentionally an invariant that gets caught. It's the same\n // behavior as without this statement except with a better message.\n if (typeof typeSpecs[typeSpecName] !== 'function') {\n // eslint-disable-next-line react-internal/prod-error-codes\n var err = Error((componentName || 'React class') + ': ' + location + ' type `' + typeSpecName + '` is invalid; ' + 'it must be a function, usually from the `prop-types` package, but received `' + typeof typeSpecs[typeSpecName] + '`.' + 'This often happens because of typos such as `PropTypes.function` instead of `PropTypes.func`.');\n err.name = 'Invariant Violation';\n throw err;\n }\n\n error$1 = typeSpecs[typeSpecName](values, typeSpecName, componentName, location, null, 'SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED');\n } catch (ex) {\n error$1 = ex;\n }\n\n if (error$1 && !(error$1 instanceof Error)) {\n setCurrentlyValidatingElement(element);\n\n error('%s: type specification of %s' + ' `%s` is invalid; the type checker ' + 'function must return `null` or an `Error` but returned a %s. ' + 'You may have forgotten to pass an argument to the type checker ' + 'creator (arrayOf, instanceOf, objectOf, oneOf, oneOfType, and ' + 'shape all require an argument).', componentName || 'React class', location, typeSpecName, typeof error$1);\n\n setCurrentlyValidatingElement(null);\n }\n\n if (error$1 instanceof Error && !(error$1.message in loggedTypeFailures)) {\n // Only monitor this failure once because there tends to be a lot of the\n // same error.\n loggedTypeFailures[error$1.message] = true;\n setCurrentlyValidatingElement(element);\n\n error('Failed %s type: %s', location, error$1.message);\n\n setCurrentlyValidatingElement(null);\n }\n }\n }\n }\n}\n\nvar isArrayImpl = Array.isArray; // eslint-disable-next-line no-redeclare\n\nfunction isArray(a) {\n return isArrayImpl(a);\n}\n\n/*\n * The `'' + value` pattern (used in in perf-sensitive code) throws for Symbol\n * and Temporal.* types. See https://github.com/facebook/react/pull/22064.\n *\n * The functions in this module will throw an easier-to-understand,\n * easier-to-debug exception with a clear errors message message explaining the\n * problem. (Instead of a confusing exception thrown inside the implementation\n * of the `value` object).\n */\n// $FlowFixMe only called in DEV, so void return is not possible.\nfunction typeName(value) {\n {\n // toStringTag is needed for namespaced types like Temporal.Instant\n var hasToStringTag = typeof Symbol === 'function' && Symbol.toStringTag;\n var type = hasToStringTag && value[Symbol.toStringTag] || value.constructor.name || 'Object';\n return type;\n }\n} // $FlowFixMe only called in DEV, so void return is not possible.\n\n\nfunction willCoercionThrow(value) {\n {\n try {\n testStringCoercion(value);\n return false;\n } catch (e) {\n return true;\n }\n }\n}\n\nfunction testStringCoercion(value) {\n // If you ended up here by following an exception call stack, here's what's\n // happened: you supplied an object or symbol value to React (as a prop, key,\n // DOM attribute, CSS property, string ref, etc.) and when React tried to\n // coerce it to a string using `'' + value`, an exception was thrown.\n //\n // The most common types that will cause this exception are `Symbol` instances\n // and Temporal objects like `Temporal.Instant`. But any object that has a\n // `valueOf` or `[Symbol.toPrimitive]` method that throws will also cause this\n // exception. (Library authors do this to prevent users from using built-in\n // numeric operators like `+` or comparison operators like `>=` because custom\n // methods are needed to perform accurate arithmetic or comparison.)\n //\n // To fix the problem, coerce this object or symbol value to a string before\n // passing it to React. The most reliable way is usually `String(value)`.\n //\n // To find which value is throwing, check the browser or debugger console.\n // Before this exception was thrown, there should be `console.error` output\n // that shows the type (Symbol, Temporal.PlainDate, etc.) that caused the\n // problem and how that type was used: key, atrribute, input value prop, etc.\n // In most cases, this console output also shows the component and its\n // ancestor components where the exception happened.\n //\n // eslint-disable-next-line react-internal/safe-string-coercion\n return '' + value;\n}\nfunction checkKeyStringCoercion(value) {\n {\n if (willCoercionThrow(value)) {\n error('The provided key is an unsupported type %s.' + ' This value must be coerced to a string before before using it here.', typeName(value));\n\n return testStringCoercion(value); // throw (to help callers find troubleshooting comments)\n }\n }\n}\n\nvar ReactCurrentOwner = ReactSharedInternals.ReactCurrentOwner;\nvar RESERVED_PROPS = {\n key: true,\n ref: true,\n __self: true,\n __source: true\n};\nvar specialPropKeyWarningShown;\nvar specialPropRefWarningShown;\nvar didWarnAboutStringRefs;\n\n{\n didWarnAboutStringRefs = {};\n}\n\nfunction hasValidRef(config) {\n {\n if (hasOwnProperty.call(config, 'ref')) {\n var getter = Object.getOwnPropertyDescriptor(config, 'ref').get;\n\n if (getter && getter.isReactWarning) {\n return false;\n }\n }\n }\n\n return config.ref !== undefined;\n}\n\nfunction hasValidKey(config) {\n {\n if (hasOwnProperty.call(config, 'key')) {\n var getter = Object.getOwnPropertyDescriptor(config, 'key').get;\n\n if (getter && getter.isReactWarning) {\n return false;\n }\n }\n }\n\n return config.key !== undefined;\n}\n\nfunction warnIfStringRefCannotBeAutoConverted(config, self) {\n {\n if (typeof config.ref === 'string' && ReactCurrentOwner.current && self && ReactCurrentOwner.current.stateNode !== self) {\n var componentName = getComponentNameFromType(ReactCurrentOwner.current.type);\n\n if (!didWarnAboutStringRefs[componentName]) {\n 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);\n\n didWarnAboutStringRefs[componentName] = true;\n }\n }\n }\n}\n\nfunction defineKeyPropWarningGetter(props, displayName) {\n {\n var warnAboutAccessingKey = function () {\n if (!specialPropKeyWarningShown) {\n specialPropKeyWarningShown = true;\n\n error('%s: `key` is not a prop. Trying to access it will result ' + 'in `undefined` being returned. If you need to access the same ' + 'value within the child component, you should pass it as a different ' + 'prop. (https://reactjs.org/link/special-props)', displayName);\n }\n };\n\n warnAboutAccessingKey.isReactWarning = true;\n Object.defineProperty(props, 'key', {\n get: warnAboutAccessingKey,\n configurable: true\n });\n }\n}\n\nfunction defineRefPropWarningGetter(props, displayName) {\n {\n var warnAboutAccessingRef = function () {\n if (!specialPropRefWarningShown) {\n specialPropRefWarningShown = true;\n\n error('%s: `ref` is not a prop. Trying to access it will result ' + 'in `undefined` being returned. If you need to access the same ' + 'value within the child component, you should pass it as a different ' + 'prop. (https://reactjs.org/link/special-props)', displayName);\n }\n };\n\n warnAboutAccessingRef.isReactWarning = true;\n Object.defineProperty(props, 'ref', {\n get: warnAboutAccessingRef,\n configurable: true\n });\n }\n}\n/**\n * Factory method to create a new React element. This no longer adheres to\n * the class pattern, so do not use new to call it. Also, instanceof check\n * will not work. Instead test $$typeof field against Symbol.for('react.element') to check\n * if something is a React Element.\n *\n * @param {*} type\n * @param {*} props\n * @param {*} key\n * @param {string|object} ref\n * @param {*} owner\n * @param {*} self A *temporary* helper to detect places where `this` is\n * different from the `owner` when React.createElement is called, so that we\n * can warn. We want to get rid of owner and replace string `ref`s with arrow\n * functions, and as long as `this` and owner are the same, there will be no\n * change in behavior.\n * @param {*} source An annotation object (added by a transpiler or otherwise)\n * indicating filename, line number, and/or other information.\n * @internal\n */\n\n\nvar ReactElement = function (type, key, ref, self, source, owner, props) {\n var element = {\n // This tag allows us to uniquely identify this as a React Element\n $$typeof: REACT_ELEMENT_TYPE,\n // Built-in properties that belong on the element\n type: type,\n key: key,\n ref: ref,\n props: props,\n // Record the component responsible for creating this element.\n _owner: owner\n };\n\n {\n // The validation flag is currently mutative. We put it on\n // an external backing store so that we can freeze the whole object.\n // This can be replaced with a WeakMap once they are implemented in\n // commonly used development environments.\n element._store = {}; // To make comparing ReactElements easier for testing purposes, we make\n // the validation flag non-enumerable (where possible, which should\n // include every environment we run tests in), so the test framework\n // ignores it.\n\n Object.defineProperty(element._store, 'validated', {\n configurable: false,\n enumerable: false,\n writable: true,\n value: false\n }); // self and source are DEV only properties.\n\n Object.defineProperty(element, '_self', {\n configurable: false,\n enumerable: false,\n writable: false,\n value: self\n }); // Two elements created in two different places should be considered\n // equal for testing purposes and therefore we hide it from enumeration.\n\n Object.defineProperty(element, '_source', {\n configurable: false,\n enumerable: false,\n writable: false,\n value: source\n });\n\n if (Object.freeze) {\n Object.freeze(element.props);\n Object.freeze(element);\n }\n }\n\n return element;\n};\n/**\n * https://github.com/reactjs/rfcs/pull/107\n * @param {*} type\n * @param {object} props\n * @param {string} key\n */\n\nfunction jsxDEV(type, config, maybeKey, source, self) {\n {\n var propName; // Reserved names are extracted\n\n var props = {};\n var key = null;\n var ref = null; // Currently, key can be spread in as a prop. This causes a potential\n // issue if key is also explicitly declared (ie. <div {...props} key=\"Hi\" />\n // or <div key=\"Hi\" {...props} /> ). We want to deprecate key spread,\n // but as an intermediary step, we will use jsxDEV for everything except\n // <div {...props} key=\"Hi\" />, because we aren't currently able to tell if\n // key is explicitly declared to be undefined or not.\n\n if (maybeKey !== undefined) {\n {\n checkKeyStringCoercion(maybeKey);\n }\n\n key = '' + maybeKey;\n }\n\n if (hasValidKey(config)) {\n {\n checkKeyStringCoercion(config.key);\n }\n\n key = '' + config.key;\n }\n\n if (hasValidRef(config)) {\n ref = config.ref;\n warnIfStringRefCannotBeAutoConverted(config, self);\n } // Remaining properties are added to a new props object\n\n\n for (propName in config) {\n if (hasOwnProperty.call(config, propName) && !RESERVED_PROPS.hasOwnProperty(propName)) {\n props[propName] = config[propName];\n }\n } // Resolve default props\n\n\n if (type && type.defaultProps) {\n var defaultProps = type.defaultProps;\n\n for (propName in defaultProps) {\n if (props[propName] === undefined) {\n props[propName] = defaultProps[propName];\n }\n }\n }\n\n if (key || ref) {\n var displayName = typeof type === 'function' ? type.displayName || type.name || 'Unknown' : type;\n\n if (key) {\n defineKeyPropWarningGetter(props, displayName);\n }\n\n if (ref) {\n defineRefPropWarningGetter(props, displayName);\n }\n }\n\n return ReactElement(type, key, ref, self, source, ReactCurrentOwner.current, props);\n }\n}\n\nvar ReactCurrentOwner$1 = ReactSharedInternals.ReactCurrentOwner;\nvar ReactDebugCurrentFrame$1 = ReactSharedInternals.ReactDebugCurrentFrame;\n\nfunction setCurrentlyValidatingElement$1(element) {\n {\n if (element) {\n var owner = element._owner;\n var stack = describeUnknownElementTypeFrameInDEV(element.type, element._source, owner ? owner.type : null);\n ReactDebugCurrentFrame$1.setExtraStackFrame(stack);\n } else {\n ReactDebugCurrentFrame$1.setExtraStackFrame(null);\n }\n }\n}\n\nvar propTypesMisspellWarningShown;\n\n{\n propTypesMisspellWarningShown = false;\n}\n/**\n * Verifies the object is a ReactElement.\n * See https://reactjs.org/docs/react-api.html#isvalidelement\n * @param {?object} object\n * @return {boolean} True if `object` is a ReactElement.\n * @final\n */\n\n\nfunction isValidElement(object) {\n {\n return typeof object === 'object' && object !== null && object.$$typeof === REACT_ELEMENT_TYPE;\n }\n}\n\nfunction getDeclarationErrorAddendum() {\n {\n if (ReactCurrentOwner$1.current) {\n var name = getComponentNameFromType(ReactCurrentOwner$1.current.type);\n\n if (name) {\n return '\\n\\nCheck the render method of `' + name + '`.';\n }\n }\n\n return '';\n }\n}\n\nfunction getSourceInfoErrorAddendum(source) {\n {\n if (source !== undefined) {\n var fileName = source.fileName.replace(/^.*[\\\\\\/]/, '');\n var lineNumber = source.lineNumber;\n return '\\n\\nCheck your code at ' + fileName + ':' + lineNumber + '.';\n }\n\n return '';\n }\n}\n/**\n * Warn if there's no key explicitly set on dynamic arrays of children or\n * object keys are not valid. This allows us to keep track of children between\n * updates.\n */\n\n\nvar ownerHasKeyUseWarning = {};\n\nfunction getCurrentComponentErrorInfo(parentType) {\n {\n var info = getDeclarationErrorAddendum();\n\n if (!info) {\n var parentName = typeof parentType === 'string' ? parentType : parentType.displayName || parentType.name;\n\n if (parentName) {\n info = \"\\n\\nCheck the top-level render call using <\" + parentName + \">.\";\n }\n }\n\n return info;\n }\n}\n/**\n * Warn if the element doesn't have an explicit key assigned to it.\n * This element is in an array. The array could grow and shrink or be\n * reordered. All children that haven't already been validated are required to\n * have a \"key\" property assigned to it. Error statuses are cached so a warning\n * will only be shown once.\n *\n * @internal\n * @param {ReactElement} element Element that requires a key.\n * @param {*} parentType element's parent's type.\n */\n\n\nfunction validateExplicitKey(element, parentType) {\n {\n if (!element._store || element._store.validated || element.key != null) {\n return;\n }\n\n element._store.validated = true;\n var currentComponentErrorInfo = getCurrentComponentErrorInfo(parentType);\n\n if (ownerHasKeyUseWarning[currentComponentErrorInfo]) {\n return;\n }\n\n ownerHasKeyUseWarning[currentComponentErrorInfo] = true; // Usually the current owner is the offender, but if it accepts children as a\n // property, it may be the creator of the child that's responsible for\n // assigning it a key.\n\n var childOwner = '';\n\n if (element && element._owner && element._owner !== ReactCurrentOwner$1.current) {\n // Give the component that originally created this child.\n childOwner = \" It was passed a child from \" + getComponentNameFromType(element._owner.type) + \".\";\n }\n\n setCurrentlyValidatingElement$1(element);\n\n error('Each child in a list should have a unique \"key\" prop.' + '%s%s See https://reactjs.org/link/warning-keys for more information.', currentComponentErrorInfo, childOwner);\n\n setCurrentlyValidatingElement$1(null);\n }\n}\n/**\n * Ensure that every element either is passed in a static location, in an\n * array with an explicit keys property defined, or in an object literal\n * with valid key property.\n *\n * @internal\n * @param {ReactNode} node Statically passed child of any type.\n * @param {*} parentType node's parent's type.\n */\n\n\nfunction validateChildKeys(node, parentType) {\n {\n if (typeof node !== 'object') {\n return;\n }\n\n if (isArray(node)) {\n for (var i = 0; i < node.length; i++) {\n var child = node[i];\n\n if (isValidElement(child)) {\n validateExplicitKey(child, parentType);\n }\n }\n } else if (isValidElement(node)) {\n // This element was passed in a valid location.\n if (node._store) {\n node._store.validated = true;\n }\n } else if (node) {\n var iteratorFn = getIteratorFn(node);\n\n if (typeof iteratorFn === 'function') {\n // Entry iterators used to provide implicit keys,\n // but now we print a separate warning for them later.\n if (iteratorFn !== node.entries) {\n var iterator = iteratorFn.call(node);\n var step;\n\n while (!(step = iterator.next()).done) {\n if (isValidElement(step.value)) {\n validateExplicitKey(step.value, parentType);\n }\n }\n }\n }\n }\n }\n}\n/**\n * Given an element, validate that its props follow the propTypes definition,\n * provided by the type.\n *\n * @param {ReactElement} element\n */\n\n\nfunction validatePropTypes(element) {\n {\n var type = element.type;\n\n if (type === null || type === undefined || typeof type === 'string') {\n return;\n }\n\n var propTypes;\n\n if (typeof type === 'function') {\n propTypes = type.propTypes;\n } else if (typeof type === 'object' && (type.$$typeof === REACT_FORWARD_REF_TYPE || // Note: Memo only checks outer props here.\n // Inner props are checked in the reconciler.\n type.$$typeof === REACT_MEMO_TYPE)) {\n propTypes = type.propTypes;\n } else {\n return;\n }\n\n if (propTypes) {\n // Intentionally inside to avoid triggering lazy initializers:\n var name = getComponentNameFromType(type);\n checkPropTypes(propTypes, element.props, 'prop', name, element);\n } else if (type.PropTypes !== undefined && !propTypesMisspellWarningShown) {\n propTypesMisspellWarningShown = true; // Intentionally inside to avoid triggering lazy initializers:\n\n var _name = getComponentNameFromType(type);\n\n error('Component %s declared `PropTypes` instead of `propTypes`. Did you misspell the property assignment?', _name || 'Unknown');\n }\n\n if (typeof type.getDefaultProps === 'function' && !type.getDefaultProps.isReactClassApproved) {\n error('getDefaultProps is only used on classic React.createClass ' + 'definitions. Use a static property named `defaultProps` instead.');\n }\n }\n}\n/**\n * Given a fragment, validate that it can only be provided with fragment props\n * @param {ReactElement} fragment\n */\n\n\nfunction validateFragmentProps(fragment) {\n {\n var keys = Object.keys(fragment.props);\n\n for (var i = 0; i < keys.length; i++) {\n var key = keys[i];\n\n if (key !== 'children' && key !== 'key') {\n setCurrentlyValidatingElement$1(fragment);\n\n error('Invalid prop `%s` supplied to `React.Fragment`. ' + 'React.Fragment can only have `key` and `children` props.', key);\n\n setCurrentlyValidatingElement$1(null);\n break;\n }\n }\n\n if (fragment.ref !== null) {\n setCurrentlyValidatingElement$1(fragment);\n\n error('Invalid attribute `ref` supplied to `React.Fragment`.');\n\n setCurrentlyValidatingElement$1(null);\n }\n }\n}\n\nvar didWarnAboutKeySpread = {};\nfunction jsxWithValidation(type, props, key, isStaticChildren, source, self) {\n {\n var validType = isValidElementType(type); // We warn in this case but don't throw. We expect the element creation to\n // succeed and there will likely be errors in render.\n\n if (!validType) {\n var info = '';\n\n if (type === undefined || typeof type === 'object' && type !== null && Object.keys(type).length === 0) {\n 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.\";\n }\n\n var sourceInfo = getSourceInfoErrorAddendum(source);\n\n if (sourceInfo) {\n info += sourceInfo;\n } else {\n info += getDeclarationErrorAddendum();\n }\n\n var typeString;\n\n if (type === null) {\n typeString = 'null';\n } else if (isArray(type)) {\n typeString = 'array';\n } else if (type !== undefined && type.$$typeof === REACT_ELEMENT_TYPE) {\n typeString = \"<\" + (getComponentNameFromType(type.type) || 'Unknown') + \" />\";\n info = ' Did you accidentally export a JSX literal instead of a component?';\n } else {\n typeString = typeof type;\n }\n\n error('React.jsx: type is invalid -- expected a string (for ' + 'built-in components) or a class/function (for composite ' + 'components) but got: %s.%s', typeString, info);\n }\n\n var element = jsxDEV(type, props, key, source, self); // The result can be nullish if a mock or a custom function is used.\n // TODO: Drop this when these are no longer allowed as the type argument.\n\n if (element == null) {\n return element;\n } // Skip key warning if the type isn't valid since our key validation logic\n // doesn't expect a non-string/function type and can throw confusing errors.\n // We don't want exception behavior to differ between dev and prod.\n // (Rendering will throw with a helpful message and as soon as the type is\n // fixed, the key warnings will appear.)\n\n\n if (validType) {\n var children = props.children;\n\n if (children !== undefined) {\n if (isStaticChildren) {\n if (isArray(children)) {\n for (var i = 0; i < children.length; i++) {\n validateChildKeys(children[i], type);\n }\n\n if (Object.freeze) {\n Object.freeze(children);\n }\n } else {\n error('React.jsx: Static children should always be an array. ' + 'You are likely explicitly calling React.jsxs or React.jsxDEV. ' + 'Use the Babel transform instead.');\n }\n } else {\n validateChildKeys(children, type);\n }\n }\n }\n\n {\n if (hasOwnProperty.call(props, 'key')) {\n var componentName = getComponentNameFromType(type);\n var keys = Object.keys(props).filter(function (k) {\n return k !== 'key';\n });\n var beforeExample = keys.length > 0 ? '{key: someKey, ' + keys.join(': ..., ') + ': ...}' : '{key: someKey}';\n\n if (!didWarnAboutKeySpread[componentName + beforeExample]) {\n var afterExample = keys.length > 0 ? '{' + keys.join(': ..., ') + ': ...}' : '{}';\n\n error('A props object containing a \"key\" prop is being spread into JSX:\\n' + ' let props = %s;\\n' + ' <%s {...props} />\\n' + 'React keys must be passed directly to JSX without using spread:\\n' + ' let props = %s;\\n' + ' <%s key={someKey} {...props} />', beforeExample, componentName, afterExample, componentName);\n\n didWarnAboutKeySpread[componentName + beforeExample] = true;\n }\n }\n }\n\n if (type === REACT_FRAGMENT_TYPE) {\n validateFragmentProps(element);\n } else {\n validatePropTypes(element);\n }\n\n return element;\n }\n} // These two functions exist to still get child warnings in dev\n// even with the prod transform. This means that jsxDEV is purely\n// opt-in behavior for better messages but that we won't stop\n// giving you warnings if you use production apis.\n\nfunction jsxWithValidationStatic(type, props, key) {\n {\n return jsxWithValidation(type, props, key, true);\n }\n}\nfunction jsxWithValidationDynamic(type, props, key) {\n {\n return jsxWithValidation(type, props, key, false);\n }\n}\n\nvar jsx = jsxWithValidationDynamic ; // we may want to special case jsxs internally to take advantage of static children.\n// for now we can ship identical prod functions\n\nvar jsxs = jsxWithValidationStatic ;\n\nexports.Fragment = REACT_FRAGMENT_TYPE;\nexports.jsx = jsx;\nexports.jsxs = jsxs;\n })();\n}\n","'use strict';\n\nif (process.env.NODE_ENV === 'production') {\n module.exports = require('./cjs/react-jsx-runtime.production.min.js');\n} else {\n module.exports = require('./cjs/react-jsx-runtime.development.js');\n}\n","/**\n * @license React\n * react-jsx-runtime.production.min.js\n *\n * Copyright (c) Facebook, Inc. and its affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n'use strict';var f=require(\"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:!0,ref:!0,__self:!0,__source:!0};\nfunction q(c,a,g){var b,d={},e=null,h=null;void 0!==g&&(e=\"\"+g);void 0!==a.key&&(e=\"\"+a.key);void 0!==a.ref&&(h=a.ref);for(b in a)m.call(a,b)&&!p.hasOwnProperty(b)&&(d[b]=a[b]);if(c&&c.defaultProps)for(b in a=c.defaultProps,a)void 0===d[b]&&(d[b]=a[b]);return{$$typeof:k,type:c,key:e,ref:h,props:d,_owner:n.current}}exports.Fragment=l;exports.jsx=q;exports.jsxs=q;\n","import { useCallback, useRef, useState } from 'react';\n\n// Error class for camera permission denied\nexport class CameraPermissionDenied extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'CameraPermissionDenied';\n }\n}\n\n// Private MediaStream wrapper to prevent direct access to tracks\nclass CameraStream {\n private stream: MediaStream;\n\n constructor(stream: MediaStream) {\n this.stream = stream;\n }\n\n // Get the underlying MediaStream (for internal use only)\n getStream(): MediaStream {\n return this.stream;\n }\n\n // Stop all tracks\n stop(): void {\n this.stream.getTracks().forEach(track => track.stop());\n }\n}\n\n// Hook to manage camera access\nexport const useCamera = () => {\n // Store the camera stream reference\n const streamRef = useRef<CameraStream | null>(null);\n // Track if camera is active\n const [isActive, setIsActive] = useState<boolean>(false);\n // Flag to prevent duplicate initialization\n const isInitializingRef = useRef<boolean>(false);\n\n // Determine optimal camera constraints based on device\n const getOptimalConstraints = (): MediaStreamConstraints => {\n const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(\n navigator.userAgent\n );\n\n return {\n video: {\n facingMode: 'user',\n width: { ideal: isMobile ? 640 : 1280 },\n height: { ideal: isMobile ? 480 : 720 },\n aspectRatio: { ideal: 1.777777778 }, // 16:9\n },\n audio: false,\n };\n };\n\n // Start the camera\n // 🔥 CORREÇÃO CRÍTICA: Remover isActive das dependências para evitar loop infinito\n const startCamera = useCallback(\n async (constraints?: MediaStreamConstraints): Promise<MediaStream> => {\n console.log('🎬 [useCamera] startCamera chamado', {\n hasExistingStream: !!streamRef.current,\n isInitializing: isInitializingRef.current,\n });\n\n // 🔥 CORREÇÃO: Se já existe um stream ativo, retorná-lo ao invés de lançar erro\n const existingStream = streamRef.current;\n if (existingStream) {\n console.log('⚠️ [useCamera] Câmera já está ativa, retornando stream existente');\n return existingStream.getStream();\n }\n\n // 🔥 CORREÇÃO: Prevenir inicialização duplicada (React Strict Mode)\n if (isInitializingRef.current) {\n console.log('⏳ [useCamera] Inicialização já em andamento, aguardando...');\n // Aguardar um pouco e tentar novamente\n await new Promise(resolve => setTimeout(resolve, 100));\n const currentStream = streamRef.current;\n if (currentStream) {\n console.log('✅ [useCamera] Stream criado por outra chamada, usando-o');\n return currentStream.getStream();\n }\n }\n\n isInitializingRef.current = true;\n\n try {\n // Use provided constraints or get optimal ones\n const mediaConstraints = constraints || getOptimalConstraints();\n\n console.log('📹 [useCamera] Solicitando acesso à câmera...', mediaConstraints);\n\n // Request camera access\n const stream = await navigator.mediaDevices.getUserMedia(mediaConstraints);\n\n console.log('✅ [useCamera] Stream obtido com sucesso:', {\n tracks: stream.getTracks().length,\n videoTracks: stream.getVideoTracks().length,\n active: stream.active,\n });\n\n // Wrap the stream to prevent direct access to tracks\n const cameraStream = new CameraStream(stream);\n streamRef.current = cameraStream;\n setIsActive(true);\n isInitializingRef.current = false;\n\n console.log('✅ [useCamera] Câmera iniciada com sucesso');\n\n // Return the stream for the caller to use\n return stream;\n } catch (error) {\n isInitializingRef.current = false;\n\n console.error('❌ [useCamera] Erro ao acessar câmera:', error);\n\n // 🔥 CORREÇÃO: Tratamento mais específico de erros\n if (error instanceof Error) {\n console.log('📋 [useCamera] Detalhes do erro:', {\n name: error.name,\n message: error.message,\n stack: error.stack?.substring(0, 200),\n });\n\n // Handle permission denied error\n if (error.name === 'NotAllowedError' || error.name === 'PermissionDeniedError') {\n throw new CameraPermissionDenied(\n 'Permissão para acessar a câmera foi negada pelo usuário'\n );\n }\n\n // Handle device not found\n if (error.name === 'NotFoundError' || error.name === 'DevicesNotFoundError') {\n throw new Error('Nenhuma câmera foi encontrada no dispositivo');\n }\n\n // Handle device already in use\n if (error.name === 'NotReadableError' || error.name === 'TrackStartError') {\n throw new Error('A câmera já está sendo usada por outro aplicativo');\n }\n\n // Handle overconstrained\n if (\n error.name === 'OverconstrainedError' ||\n error.name === 'ConstraintNotSatisfiedError'\n ) {\n throw new Error('As configurações solicitadas não são suportadas pela câmera');\n }\n }\n\n // Re-throw other errors with more context\n throw new Error(\n `Erro ao inicializar câmera: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n },\n [] // 🔥 Array vazio - função é criada apenas uma vez\n );\n\n // Stop the camera\n const stopCamera = useCallback((stream: MediaStream): void => {\n console.log('🛑 [useCamera] Parando câmera...');\n\n // Find the corresponding CameraStream\n const cameraStream = streamRef.current;\n\n if (cameraStream && cameraStream.getStream() === stream) {\n // Stop all tracks\n cameraStream.stop();\n console.log('✅ [useCamera] Todas as tracks paradas');\n\n // Clear references\n streamRef.current = null;\n setIsActive(false);\n isInitializingRef.current = false;\n\n console.log('✅ [useCamera] Câmera desligada com sucesso');\n } else {\n console.warn(\n '⚠️ [useCamera] Tentativa de parar um stream que não foi iniciado por este hook'\n );\n }\n }, []);\n\n // Clean up on unmount\n const cleanup = useCallback(() => {\n console.log('🧹 [useCamera] Cleanup chamado');\n if (streamRef.current) {\n streamRef.current.stop();\n streamRef.current = null;\n setIsActive(false);\n isInitializingRef.current = false;\n console.log('✅ [useCamera] Cleanup concluído');\n }\n }, []);\n\n return {\n startCamera,\n stopCamera,\n cleanup,\n isActive,\n };\n};\n","/**\n * Tipos padronizados de erro do SDK NeoFace ID.\n * Utilize estes tipos para categorizar e tratar erros de forma consistente.\n */\nexport enum ErrorType {\n NETWORK = 'NetworkError',\n NETWORK_ERROR = 'NetworkError',\n INVALID_TOKEN = 'InvalidTokenError',\n RECOGNITION_FAILED = 'RecognitionFailedError',\n LOGIN_FAILED = 'LoginFailedError',\n VALIDATION_ERROR = 'ValidationError',\n API_ERROR = 'ApiError',\n PERSON_NOT_FOUND = 'PersonNotFoundError',\n INITIALIZATION_ERROR = 'InitializationError',\n CAMERA_ERROR = 'CameraError',\n NO_CAMERA = 'NoCameraError',\n CAPTURE_ERROR = 'CaptureError',\n NOT_FOUND = 'NotFoundError',\n UNKNOWN = 'UnknownError',\n}\n\n/**\n * Erro customizado do SDK que carrega um tipo semântico.\n * Permite aos consumidores diferenciar falhas por categoria com `error.type`.\n */\nexport class NeoFaceError extends Error {\n type: ErrorType;\n\n /**\n * Constrói um erro do SDK com mensagem e tipo categórico.\n * @param message Mensagem descritiva do erro (pode ser técnica)\n * @param type Tipo categórico do erro\n */\n constructor(message: string, type: ErrorType) {\n super(message);\n this.name = type;\n this.type = type;\n }\n\n /**\n * Retorna uma mensagem amigável para o usuário final, ocultando termos técnicos.\n */\n public getFriendlyMessage(): string {\n // Se a mensagem já parece amigável (contém acentos ou é curta e em PT), retorna ela mesma\n if (/[áéíóúãõç]/i.test(this.message)) {\n return this.message;\n }\n\n switch (this.type) {\n case ErrorType.NETWORK:\n if (this.message.includes('timeout')) return 'A conexão demorou muito. Tente novamente.';\n return 'Não foi possível conectar ao servidor. Verifique sua internet.';\n\n case ErrorType.INVALID_TOKEN:\n return 'Falha na autenticação do aplicativo. Entre em contato com o suporte.';\n\n case ErrorType.CAMERA_ERROR:\n case ErrorType.NO_CAMERA:\n return 'Não conseguimos acessar sua câmera. Verifique as permissões do navegador.';\n\n case ErrorType.LOGIN_FAILED:\n case ErrorType.RECOGNITION_FAILED:\n return 'Não foi possível reconhecer seu rosto. Tente se posicionar em um local mais iluminado.';\n\n case ErrorType.VALIDATION_ERROR:\n return 'Dados inválidos ou rosto não detectado corretamente.';\n\n case ErrorType.API_ERROR:\n if (this.message.includes('400')) return 'Requisição inválida. Tente novamente.';\n if (this.message.includes('500')) return 'Ocorreu um erro interno no sistema. Tente mais tarde.';\n return 'O serviço encontrou um problema inesperado.';\n\n default:\n return 'Ocorreu um erro inesperado. Por favor, tente novamente.';\n }\n }\n}\n","/**\n * SDK Global Configuration\n * \n * Este módulo gerencia a configuração global do SDK, permitindo\n * inicialização com detecção automática de ambiente ou override manual.\n */\n\nexport type Environment = 'development' | 'sandbox' | 'production';\n\n/**\n * Mapeamento de ambientes para URLs base da API\n */\nconst ENVIRONMENT_URLS: Record<Environment, string> = {\n development: 'http://localhost:8000',\n sandbox: 'https://sandbox-core.neofaceid.com',\n production: 'https://core.neofaceid.com.br',\n};\n\n/**\n * Opções de inicialização do SDK\n */\nexport interface SDKInitOptions {\n /**\n * Ambiente da aplicação. Se fornecido, a URL base será determinada automaticamente.\n * Pode ser 'development', 'sandbox' ou 'production'.\n */\n environment?: Environment;\n \n /**\n * URL base da API. Se fornecido, tem precedência sobre 'environment'.\n * Útil para ambientes customizados ou testes.\n */\n baseUrl?: string;\n \n /**\n * Token de aplicação para autenticação com a API.\n * Obtido através do painel administrativo do NeoFaceID.\n */\n applicationToken?: string;\n}\n\n/**\n * Configuração interna do SDK\n */\ninterface SDKConfig {\n baseUrl: string;\n applicationToken: string | null;\n environment: Environment | 'custom';\n initialized: boolean;\n}\n\n/**\n * Configuração global do SDK (singleton)\n */\nlet globalConfig: SDKConfig = {\n baseUrl: ENVIRONMENT_URLS.sandbox, // Default para sandbox\n applicationToken: null,\n environment: 'sandbox',\n initialized: false,\n};\n\n/**\n * Inicializa o SDK com as configurações fornecidas.\n * Deve ser chamado uma vez no início da aplicação, antes de usar qualquer função do SDK.\n * \n * @example\n * // Usando ambiente predefinido\n * NeoFaceSDK.init({ environment: 'production' });\n * \n * // Usando URL customizada\n * NeoFaceSDK.init({ baseUrl: 'https://my-custom-api.com' });\n * \n * // Com token de aplicação\n * NeoFaceSDK.init({ \n * environment: 'sandbox',\n * applicationToken: 'your-app-token'\n * });\n * \n * @param options Opções de inicialização\n */\nexport function init(options: SDKInitOptions = {}): void {\n const { environment, baseUrl, applicationToken } = options;\n \n // Determinar a URL base\n if (baseUrl) {\n // URL explícita tem precedência\n globalConfig.baseUrl = baseUrl;\n globalConfig.environment = 'custom';\n } else if (environment) {\n // Usar ambiente predefinido\n if (!ENVIRONMENT_URLS[environment]) {\n console.warn(`[NeoFaceID SDK] Ambiente '${environment}' não reconhecido. Usando 'sandbox'.`);\n globalConfig.baseUrl = ENVIRONMENT_URLS.sandbox;\n globalConfig.environment = 'sandbox';\n } else {\n globalConfig.baseUrl = ENVIRONMENT_URLS[environment];\n globalConfig.environment = environment;\n }\n } else {\n // Fallback: usar variável de ambiente ou sandbox\n const envUrl = (typeof import.meta !== 'undefined' && (import.meta as any).env?.VITE_API_BASE_URL);\n if (envUrl) {\n globalConfig.baseUrl = envUrl;\n globalConfig.environment = 'custom';\n }\n // Caso contrário, mantém o default (sandbox)\n }\n \n // Configurar token de aplicação\n if (applicationToken) {\n globalConfig.applicationToken = applicationToken;\n }\n \n globalConfig.initialized = true;\n \n console.log(`[NeoFaceID SDK] Inicializado - Ambiente: ${globalConfig.environment}, URL: ${globalConfig.baseUrl}`);\n}\n\n/**\n * Retorna a URL base configurada para a API.\n * Se o SDK não foi inicializado, retorna o fallback para sandbox.\n */\nexport function getBaseUrl(): string {\n if (!globalConfig.initialized) {\n console.warn('[NeoFaceID SDK] SDK não foi inicializado. Usando configuração padrão (sandbox).');\n }\n return globalConfig.baseUrl;\n}\n\n/**\n * Retorna o token de aplicação configurado, se houver.\n */\nexport function getApplicationToken(): string | null {\n return globalConfig.applicationToken;\n}\n\n/**\n * Retorna o ambiente atual configurado.\n */\nexport function getEnvironment(): Environment | 'custom' {\n return globalConfig.environment;\n}\n\n/**\n * Verifica se o SDK foi inicializado.\n */\nexport function isInitialized(): boolean {\n return globalConfig.initialized;\n}\n\n/**\n * Retorna toda a configuração atual (para debug).\n */\nexport function getConfig(): Readonly<SDKConfig> {\n return { ...globalConfig };\n}\n\n/**\n * Reseta a configuração do SDK (útil para testes).\n * @internal\n */\nexport function _resetConfig(): void {\n globalConfig = {\n baseUrl: ENVIRONMENT_URLS.sandbox,\n applicationToken: null,\n environment: 'sandbox',\n initialized: false,\n };\n}\n\n/**\n * Exporta as URLs de ambiente para uso externo se necessário.\n */\nexport { ENVIRONMENT_URLS };\n","import { NeoFaceError, ErrorType } from './errors';\n\nexport interface NeoFaceApiConfig {\n apiKey: string;\n baseUrl: string;\n}\n\nexport interface BiometricLoginResult {\n success: boolean;\n accessToken: string;\n alias: string;\n user: {\n id: string;\n email: string;\n role: string;\n validated: boolean;\n active: boolean;\n };\n person?: {\n name: string;\n birth_date: string;\n };\n confidence_score?: number;\n recognition_id?: number;\n}\n\n/**\n * Application registration data\n */\nexport interface ApplicationRegistrationData {\n applicationName: string;\n domain: string;\n acceptOnlyEmailWithSameDomain: boolean;\n}\n\n/**\n * Application registration result\n */\nexport interface ApplicationRegistrationResult {\n success: boolean;\n application: {\n id: string;\n application_name: string;\n domain: string;\n accept_only_email_with_same_domain: boolean;\n app_token: string;\n is_active: boolean;\n created_at: string;\n };\n message: string;\n}\n\n// Tipagem para detalhes do link de onboarding\nexport interface OnboardingLinkDetails {\n id: string;\n token: string;\n event: string;\n custom_flow_id?: string | null;\n expire: boolean;\n expires_at?: string | null;\n notification: boolean;\n callback_url?: string | null;\n callback_email?: string | null;\n person_name: string;\n person_birth_date: string;\n person_cpf: string;\n person_email: string;\n person_phone?: string | null;\n status: string;\n created_at: string;\n updated_at: string;\n completed_at?: string | null;\n is_valid: boolean;\n is_expired: boolean;\n onboarding_url: string;\n consumer_name?: string;\n}\n\n// API constants\nimport { getBaseUrl } from './config';\n\n// Getter para a URL base da API - usa a configuração global do SDK\nconst getApiBaseUrl = (): string => getBaseUrl();\n\nconst REQUEST_TIMEOUT = 10000; // 10 seconds\n\n// Helper function to check secure context\n// Permite localhost para desenvolvimento\nconst ensureSecureContext = (): void => {\n const isLocalhost =\n window.location.hostname === 'localhost' ||\n window.location.hostname === '127.0.0.1' ||\n window.location.hostname === '';\n\n if (!window.isSecureContext && !isLocalhost) {\n throw new NeoFaceError(\n 'Secure context (HTTPS) is required for this operation',\n ErrorType.NETWORK\n );\n }\n};\n\n// Helper function to create timeout abort controller\nconst createTimeoutController = (): AbortController => {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), REQUEST_TIMEOUT);\n\n // Clean up timeout when controller is aborted\n controller.signal.addEventListener('abort', () => clearTimeout(timeoutId));\n\n return controller;\n};\n\n// Helper function to compress image (for faces - 70% quality is fine)\nconst compressImage = async (imageBlob: Blob): Promise<Blob> =>\n new Promise((resolve, reject) => {\n const img = new Image();\n img.onload = () => {\n const canvas = document.createElement('canvas');\n canvas.width = img.width;\n canvas.height = img.height;\n\n const ctx = canvas.getContext('2d');\n if (!ctx) {\n reject(new Error('Failed to get canvas context'));\n return;\n }\n\n ctx.drawImage(img, 0, 0);\n\n canvas.toBlob(\n blob => {\n if (blob) {\n resolve(blob);\n } else {\n reject(new Error('Failed to compress image'));\n }\n },\n 'image/jpeg',\n 0.7\n );\n };\n\n img.onerror = () => {\n reject(new Error('Failed to load image'));\n };\n\n img.src = URL.createObjectURL(imageBlob);\n });\n\n// Helper function to compress document images (75% quality + resize to avoid 413 errors)\nconst compressDocumentImage = async (imageBlob: Blob): Promise<Blob> =>\n new Promise((resolve, reject) => {\n const img = new Image();\n img.onload = () => {\n // Resize to max 1280px on longest side\n const MAX_SIZE = 1280;\n let width = img.width;\n let height = img.height;\n\n if (width > height && width > MAX_SIZE) {\n height = (height * MAX_SIZE) / width;\n width = MAX_SIZE;\n } else if (height > MAX_SIZE) {\n width = (width * MAX_SIZE) / height;\n height = MAX_SIZE;\n }\n\n const canvas = document.createElement('canvas');\n canvas.width = width;\n canvas.height = height;\n\n const ctx = canvas.getContext('2d');\n if (!ctx) {\n reject(new Error('Failed to get canvas context'));\n return;\n }\n\n ctx.drawImage(img, 0, 0, width, height);\n\n canvas.toBlob(\n blob => {\n if (blob) {\n resolve(blob);\n } else {\n reject(new Error('Failed to compress document image'));\n }\n },\n 'image/jpeg',\n 0.75 // 75% quality to keep file size manageable\n );\n };\n\n img.onerror = () => {\n reject(new Error('Failed to load document image'));\n };\n\n img.src = URL.createObjectURL(imageBlob);\n });\n\n/**\n * Validates an application token\n * @param applicationToken The application token to validate\n * @returns Promise that resolves to true if token is valid, false otherwise\n * @throws NeoFaceError if request fails\n */\nexport const validateToken = async (applicationToken: string): Promise<boolean> => {\n ensureSecureContext();\n\n const controller = createTimeoutController();\n\n try {\n const response = await fetch(`${getApiBaseUrl()}/api/v1/auth/application/validate_token/`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'X-App-Token': applicationToken,\n },\n body: JSON.stringify({}),\n signal: controller.signal,\n });\n\n if (!response.ok) {\n throw new NeoFaceError(`API Error ${response.status}`, ErrorType.INVALID_TOKEN);\n }\n\n return response.ok;\n } catch (error) {\n if (error instanceof Error) {\n if (error.name === 'AbortError') {\n throw new NeoFaceError('Request timeout', ErrorType.NETWORK_ERROR);\n }\n throw new NeoFaceError(error.message, ErrorType.NETWORK_ERROR);\n }\n throw new NeoFaceError('Unknown error', ErrorType.NETWORK_ERROR);\n }\n};\n\n/**\n * Obtém detalhes de um link de onboarding por token\n * @param applicationToken Token da aplicação (header `X-App-Token`)\n * @param onboardingToken Token do link de onboarding\n * @returns Detalhes do link de onboarding\n * @throws NeoFaceError em caso de falha de rede ou autorização\n */\nexport const getOnboardingDetails = async (\n applicationToken: string,\n onboardingToken: string\n): Promise<OnboardingLinkDetails> => {\n ensureSecureContext();\n\n const controller = createTimeoutController();\n\n try {\n const response = await fetch(\n `${getApiBaseUrl()}/api/v1/onboardings/${encodeURIComponent(onboardingToken)}/`,\n {\n method: 'GET',\n headers: {\n 'X-App-Token': applicationToken,\n },\n signal: controller.signal,\n }\n );\n\n if (!response.ok) {\n if (response.status === 401 || response.status === 403) {\n throw new NeoFaceError('Token de aplicação inválido ou expirado', ErrorType.INVALID_TOKEN);\n }\n if (response.status === 404) {\n throw new NeoFaceError('Link de onboarding não encontrado', ErrorType.NOT_FOUND);\n }\n throw new NeoFaceError(`Falha ao obter detalhes (${response.status})`, ErrorType.NETWORK);\n }\n\n const data = (await response.json()) as OnboardingLinkDetails;\n return data;\n } catch (error) {\n if (error instanceof Error) {\n if (error.name === 'AbortError') {\n throw new NeoFaceError('Tempo de requisição excedido', ErrorType.NETWORK);\n }\n if (error instanceof NeoFaceError) {\n throw error;\n }\n throw new NeoFaceError(error.message, ErrorType.NETWORK);\n }\n throw new NeoFaceError('Erro desconhecido', ErrorType.NETWORK_ERROR);\n }\n};\n\n/**\n * Valida se um token de onboarding é válido\n * @param applicationToken Token da aplicação (header `X-App-Token`)\n * @param onboardingToken Token do link de onboarding a ser validado\n * @returns Promise que resolve para true se o token é válido, false caso contrário\n * @throws NeoFaceError em caso de falha de rede ou autorização\n */\nexport const validateOnboardingToken = async (\n applicationToken: string,\n onboardingToken: string\n): Promise<boolean> => {\n ensureSecureContext();\n\n const controller = createTimeoutController();\n\n try {\n const response = await fetch(\n `${getApiBaseUrl()}/api/v1/onboardings/${encodeURIComponent(onboardingToken)}/`,\n {\n method: 'GET',\n headers: {\n 'X-App-Token': applicationToken,\n },\n signal: controller.signal,\n }\n );\n\n if (!response.ok) {\n if (response.status === 401 || response.status === 403) {\n throw new NeoFaceError('Token de aplicação inválido ou expirado', ErrorType.INVALID_TOKEN);\n }\n if (response.status === 404) {\n // Token não encontrado = inválido\n return false;\n }\n throw new NeoFaceError(`Falha ao validar token (${response.status})`, ErrorType.NETWORK);\n }\n\n const data = (await response.json()) as OnboardingLinkDetails;\n\n // Retorna true apenas se o link existe e está válido\n return data?.is_valid === true;\n } catch (error) {\n if (error instanceof Error) {\n if (error.name === 'AbortError') {\n throw new NeoFaceError('Tempo de requisição excedido', ErrorType.NETWORK);\n }\n if (error instanceof NeoFaceError) {\n throw error;\n }\n throw new NeoFaceError(error.message, ErrorType.NETWORK);\n }\n throw new NeoFaceError('Erro desconhecido', ErrorType.NETWORK_ERROR);\n }\n};\n\n/**\n * Interface para dados da pessoa no onboarding\n */\nexport interface OnboardingPersonData {\n name?: string;\n birth_date?: string; // Formato: YYYY-MM-DD\n cpf?: string;\n cnpj?: string;\n email?: string;\n}\n\n/**\n * Conclui o processo de onboarding enviando imagens de face e documento\n * @param applicationToken Token da aplicação (header `X-App-Token`)\n * @param onboardingToken Token do link de onboarding\n * @param faceImage Blob da imagem de rosto\n * @param documentImage Blob da imagem do documento\n * @returns Resposta da conclusão do onboarding\n * @throws NeoFaceError em caso de falha de rede ou validação\n */\nexport const completeOnboarding = async (\n applicationToken: string,\n onboardingToken: string,\n faceImage: Blob,\n documentImage: Blob\n): Promise<{\n success: boolean;\n message: string;\n person_id?: string;\n identity_data_id?: string;\n confidence_score?: number;\n processing_time?: number;\n}> => {\n ensureSecureContext();\n\n const controller = createTimeoutController();\n\n // Comprime ambas imagens para otimizar upload\n const compressedFace = await compressImage(faceImage);\n const compressedDocument = await compressImage(documentImage);\n\n try {\n const formData = new FormData();\n formData.append('face_image', compressedFace, 'face.jpg');\n formData.append('document_image', compressedDocument, 'document.jpg');\n\n const response = await fetch(\n `${getApiBaseUrl()}/api/v1/onboardings/${encodeURIComponent(onboardingToken)}/complete`,\n {\n method: 'POST',\n headers: {\n 'X-App-Token': applicationToken,\n },\n body: formData,\n signal: controller.signal,\n }\n );\n\n const data = await response.json();\n\n if (!response.ok) {\n if (response.status === 401 || response.status === 403) {\n throw new NeoFaceError('Token de aplicação inválido ou expirado', ErrorType.INVALID_TOKEN);\n }\n if (response.status === 404) {\n throw new NeoFaceError('Link de onboarding não encontrado', ErrorType.NOT_FOUND);\n }\n // Retorna mensagem específica do backend se disponível\n throw new NeoFaceError(\n data?.error || data?.message || 'Falha ao concluir onboarding',\n ErrorType.NETWORK\n );\n }\n\n return {\n success: !!data.success,\n message: data.message || 'Onboarding concluído',\n person_id: data.person_id,\n identity_data_id: data.identity_data_id,\n confidence_score: data.confidence_score,\n processing_time: data.processing_time,\n };\n } catch (error) {\n if (error instanceof Error) {\n if (error.name === 'AbortError') {\n throw new NeoFaceError('Tempo de requisição excedido', ErrorType.NETWORK);\n }\n if (error instanceof NeoFaceError) {\n throw error;\n }\n throw new NeoFaceError(error.message, ErrorType.NETWORK);\n }\n throw new NeoFaceError('Erro desconhecido', ErrorType.NETWORK_ERROR);\n }\n};\n\n/**\n * Conclui o processo de onboarding com dados completos da pessoa\n *\n * RECEBE:\n * - Nome, DataDeNascimento, CPF, CNPJ, Email\n * - Imagens da Pessoa (array de Blobs)\n * - Imagem do documento\n *\n * AÇÃO (backend):\n * - Se não é cadastrado no NeoFaceId, cadastra-o\n * - Escaneia o documento e confere com os dados passados\n * - Confere se a imagem/vídeo passa nos critérios de prova de vida\n *\n * @param applicationToken Token da aplicação (header `X-App-Token`)\n * @param onboardingToken Token do link de onboarding\n * @param personData Dados da pessoa (Nome, DataDeNascimento, CPF, CNPJ, Email)\n * @param personImages Array de imagens/vídeos da pessoa para prova de vida (mínimo 1)\n * @param documentImage Blob da imagem do documento\n * @returns Resposta da conclusão do onboarding\n * @throws NeoFaceError em caso de falha de rede ou validação\n */\nexport const completeOnboardingWithData = async (\n applicationToken: string,\n onboardingToken: string,\n personData: OnboardingPersonData,\n personImages: Blob[],\n documentImage: Blob\n): Promise<{\n success: boolean;\n message: string;\n person_id?: string;\n identity_data_id?: string;\n confidence_score?: number;\n processing_time?: number;\n liveness_passed?: boolean;\n document_verified?: boolean;\n person_created?: boolean;\n}> => {\n ensureSecureContext();\n\n if (!personImages || personImages.length === 0) {\n throw new NeoFaceError(\n 'Pelo menos uma imagem da pessoa é obrigatória',\n ErrorType.VALIDATION_ERROR\n );\n }\n\n const controller = createTimeoutController();\n\n try {\n // Comprime todas as imagens\n const compressedPersonImages = await Promise.all(personImages.map(img => compressImage(img)));\n const compressedDocument = await compressImage(documentImage);\n\n // Cria FormData com todos os dados\n const formData = new FormData();\n\n // Adiciona imagens da pessoa (múltiplas para prova de vida)\n compressedPersonImages.forEach((img, index) => {\n formData.append('person_images', img, `person_${index + 1}.jpg`);\n });\n\n // Adiciona imagem do documento\n formData.append('document_image', compressedDocument, 'document.jpg');\n\n // Adiciona dados da pessoa (se fornecidos)\n if (personData.name) {\n formData.append('name', personData.name);\n }\n if (personData.birth_date) {\n formData.append('birth_date', personData.birth_date);\n }\n if (personData.cpf) {\n formData.append('cpf', personData.cpf);\n }\n if (personData.cnpj) {\n formData.append('cnpj', personData.cnpj);\n }\n if (personData.email) {\n formData.append('email', personData.email);\n }\n\n const response = await fetch(\n `${getApiBaseUrl()}/api/v1/onboardings/${encodeURIComponent(onboardingToken)}/complete`,\n {\n method: 'POST',\n headers: {\n 'X-App-Token': applicationToken,\n },\n body: formData,\n signal: controller.signal,\n }\n );\n\n const data = await response.json();\n\n if (!response.ok) {\n if (response.status === 401 || response.status === 403) {\n throw new NeoFaceError('Token de aplicação inválido ou expirado', ErrorType.INVALID_TOKEN);\n }\n if (response.status === 404) {\n throw new NeoFaceError('Link de onboarding não encontrado', ErrorType.NOT_FOUND);\n }\n // Retorna mensagem específica do backend se disponível\n throw new NeoFaceError(\n data?.error || data?.message || 'Falha ao concluir onboarding',\n ErrorType.NETWORK\n );\n }\n\n return {\n success: !!data.success,\n message: data.message || 'Onboarding concluído com sucesso',\n person_id: data.person_id,\n identity_data_id: data.identity_data_id,\n confidence_score: data.confidence_score,\n processing_time: data.processing_time,\n liveness_passed: data.liveness_passed,\n document_verified: data.document_verified,\n person_created: data.person_created,\n };\n } catch (error) {\n if (error instanceof Error) {\n if (error.name === 'AbortError') {\n throw new NeoFaceError('Tempo de requisição excedido', ErrorType.NETWORK);\n }\n if (error instanceof NeoFaceError) {\n throw error;\n }\n throw new NeoFaceError(error.message, ErrorType.NETWORK);\n }\n throw new NeoFaceError('Erro desconhecido', ErrorType.NETWORK_ERROR);\n }\n};\n\n/**\n * Recognizes a face from an image and returns user data\n * @param image The image blob to process\n * @param applicationToken The application token for authentication\n * @returns Promise with access token and user payload\n * @throws NeoFaceError if request fails\n * @throws NeoFaceError if recognition fails\n */\nexport const recognize = async (\n image: Blob,\n applicationToken: string\n): Promise<{\n accessToken: string;\n payload: { name: string; email: string; documentId: string };\n}> => {\n ensureSecureContext();\n\n const controller = createTimeoutController();\n const compressedImage = await compressImage(image);\n\n try {\n const formData = new FormData();\n formData.append('face_frames', compressedImage);\n\n const response = await fetch(`${getApiBaseUrl()}/api/v1/external/recognition/face/`, {\n method: 'POST',\n headers: {\n 'X-App-Token': applicationToken,\n },\n body: formData,\n signal: controller.signal,\n });\n\n if (!response.ok) {\n if (response.status === 401 || response.status === 403) {\n throw new NeoFaceError('Invalid or expired application token', ErrorType.INVALID_TOKEN);\n }\n throw new NeoFaceError(`Server returned status ${response.status}`, ErrorType.NETWORK);\n }\n\n const data = await response.json();\n\n if (!data.success) {\n throw new NeoFaceError(data.message || 'Recognition failed', ErrorType.RECOGNITION_FAILED);\n }\n\n return {\n accessToken: data.accessToken,\n payload: data.payload,\n };\n } catch (error) {\n if (error instanceof Error) {\n if (error.name === 'AbortError') {\n throw new NeoFaceError('Request timed out', ErrorType.NETWORK);\n }\n if (error instanceof NeoFaceError) {\n throw error;\n }\n throw new NeoFaceError(error.message, ErrorType.NETWORK);\n }\n throw new NeoFaceError('Unknown error occurred', ErrorType.NETWORK_ERROR);\n }\n};\n\n/**\n * Performs biometric login using face recognition\n * @param image The image blob to process\n * @param applicationToken The application token for authentication\n * @returns Promise with login result\n * @throws NeoFaceError if request fails\n */\nexport const loginWithBiometric = async (\n image: Blob,\n applicationToken: string\n): Promise<BiometricLoginResult> => {\n ensureSecureContext();\n\n const controller = createTimeoutController();\n const compressedImage = await compressImage(image);\n\n try {\n // Convert Blob to base64\n const arrayBuffer = await compressedImage.arrayBuffer();\n const base64Image = btoa(\n new Uint8Array(arrayBuffer).reduce((data, byte) => data + String.fromCharCode(byte), '')\n );\n\n const response = await fetch(`${getApiBaseUrl()}/api/v1/login/donor/face/`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'X-App-Token': applicationToken,\n },\n body: JSON.stringify({\n image: base64Image,\n biometric_type: 'FACE',\n }),\n signal: controller.signal,\n });\n\n if (!response.ok) {\n if (response.status === 401 || response.status === 403) {\n throw new NeoFaceError('Invalid application token', ErrorType.INVALID_TOKEN);\n }\n if (response.status >= 500) {\n throw new NeoFaceError(`Server Error ${response.status}`, ErrorType.API_ERROR);\n }\n throw new NeoFaceError(`API Error ${response.status}`, ErrorType.API_ERROR);\n }\n\n const data = await response.json();\n\n if (!data.success) {\n throw new NeoFaceError(data.message || 'Login failed', ErrorType.LOGIN_FAILED);\n }\n\n return {\n success: true,\n accessToken: data.accessToken,\n alias: data.alias,\n user: {\n id: data.user?.id || '',\n email: data.user?.email || '',\n role: data.user?.role || '',\n validated: data.user?.validated || false,\n active: data.user?.active || false,\n },\n person: data.person\n ? {\n name: data.person.name || '',\n birth_date: data.person.birth_date || '',\n }\n : undefined,\n confidence_score: data.confidence_score,\n recognition_id: data.recognition_id,\n };\n } catch (error) {\n if (error instanceof Error) {\n if (error.name === 'AbortError') {\n throw new NeoFaceError('Request timed out', ErrorType.NETWORK);\n }\n if (error instanceof NeoFaceError) {\n throw error;\n }\n throw new NeoFaceError(error.message, ErrorType.NETWORK);\n }\n throw new NeoFaceError('Unknown error occurred', ErrorType.NETWORK_ERROR);\n }\n};\n\n/**\n * Performs biometric recognition with liveness check\n *\n * Note: person_id is NOT returned for security reasons.\n * Use recognizeByPurpose for external integrations that require signature validation.\n *\n * @param image The image blob to process\n * @param applicationToken The application token for authentication\n * @param livenessCheck Whether to perform liveness check (default: true)\n * @param confidenceThreshold Confidence threshold for recognition (default: 0.8)\n * @returns Promise with recognition result\n * @throws NeoFaceError if request fails\n */\nexport const recognizeBiometric = async (\n image: Blob,\n applicationToken: string,\n livenessCheck: boolean = true,\n confidenceThreshold: number = 0.8\n): Promise<{\n success: boolean;\n confidenceScore?: number;\n accessToken?: string;\n payload?: { name: string; email: string; documentId: string };\n // Note: personId is intentionally NOT returned for security reasons\n}> => {\n ensureSecureContext();\n\n const controller = createTimeoutController();\n const compressedImage = await compressImage(image);\n\n try {\n const formData = new FormData();\n formData.append('face_frames', compressedImage);\n formData.append('liveness_check', livenessCheck.toString());\n formData.append('confidence_threshold', confidenceThreshold.toString());\n\n const response = await fetch(`${getApiBaseUrl()}/api/v1/external/recognition/biometric/`, {\n method: 'POST',\n headers: {\n 'X-App-Token': applicationToken,\n },\n body: formData,\n signal: controller.signal,\n });\n\n if (!response.ok) {\n if (response.status === 401 || response.status === 403) {\n throw new NeoFaceError('Invalid or expired application token', ErrorType.INVALID_TOKEN);\n }\n throw new NeoFaceError(`Server returned status ${response.status}`, ErrorType.NETWORK);\n }\n\n const data = await response.json();\n\n return {\n success: data.success,\n // personId is intentionally NOT returned for security reasons\n confidenceScore: data.confidence_score,\n accessToken: data.access_token,\n payload: data.payload,\n };\n } catch (error) {\n if (error instanceof Error) {\n if (error.name === 'AbortError') {\n throw new NeoFaceError('Request timed out', ErrorType.NETWORK);\n }\n if (error instanceof NeoFaceError) {\n throw error;\n }\n throw new NeoFaceError(error.message, ErrorType.NETWORK);\n }\n throw new NeoFaceError('Unknown error occurred', ErrorType.NETWORK_ERROR);\n }\n};\n\n/**\n * Performs simple identification using document data\n * @param documentType Type of document (e.g., 'CPF')\n * @param documentNumber Document number\n * @param applicationToken The application token for authentication\n * @param purpose Purpose of identification (default: 'LOGIN')\n * @returns Promise with identification result\n * @throws NeoFaceError if request fails\n */\nexport const simpleIdentification = async (\n documentType: string,\n documentNumber: string,\n applicationToken: string,\n purpose: string = 'LOGIN'\n): Promise<{\n success: boolean;\n personId?: string;\n accessToken?: string;\n payload?: { name: string; email: string; documentId: string };\n}> => {\n ensureSecureContext();\n\n const controller = createTimeoutController();\n\n try {\n const response = await fetch(`${getApiBaseUrl()}/api/v1/external/recognition/simple/`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'X-App-Token': applicationToken,\n },\n body: JSON.stringify({\n document_type: documentType,\n document_number: documentNumber,\n purpose: purpose,\n }),\n signal: controller.signal,\n });\n\n if (!response.ok) {\n if (response.status === 401 || response.status === 403) {\n throw new NeoFaceError('Invalid or expired application token', ErrorType.INVALID_TOKEN);\n }\n throw new NeoFaceError(`Server returned status ${response.status}`, ErrorType.NETWORK);\n }\n\n const data = await response.json();\n\n if (!data.success) {\n throw new NeoFaceError(data.message || 'Identification failed', ErrorType.RECOGNITION_FAILED);\n }\n\n return {\n success: data.success,\n personId: data.person_id,\n accessToken: data.access_token,\n payload: data.payload,\n };\n } catch (error) {\n if (error instanceof Error) {\n if (error.name === 'AbortError') {\n throw new NeoFaceError('Request timed out', ErrorType.NETWORK);\n }\n if (error instanceof NeoFaceError) {\n throw error;\n }\n throw new NeoFaceError(error.message, ErrorType.NETWORK);\n }\n throw new NeoFaceError('Unknown error occurred', ErrorType.NETWORK_ERROR);\n }\n};\n\n/**\n * Session data interface for external integrations\n */\nexport interface SessionData {\n email: string;\n cpf: string;\n sessionId: string;\n}\n\n/**\n * Performs purpose-specific recognition\n * @param image The image blob to process\n * @param applicationToken The application token for authentication\n * @param purpose Purpose of recognition ('LOGIN', 'PROOF_OF_LIFE', 'AUTHORIZATION', etc.)\n * @param confidenceThreshold Confidence threshold for recognition (default: 0.8)\n * @param signature Optional signature for external integrations (HMAC-SHA256 hex string)\n * @param sessionData Optional session data for external integrations (email, cpf, sessionId)\n * @returns Promise with recognition result\n * @throws NeoFaceError if request fails\n */\nexport const recognizeByPurpose = async (\n image: Blob,\n applicationToken: string,\n purpose:\n | 'LOGIN'\n | 'PROOF_OF_LIFE'\n | 'AUTHORIZATION'\n | 'SIMPLE_IDENTIFICATION'\n | 'SIMPLIFIED_REGISTRATION',\n confidenceThreshold: number = 0.8,\n signature?: string,\n sessionData?: SessionData\n): Promise<{\n success: boolean;\n personName?: string;\n email?: string;\n cpf?: string;\n confidenceScore?: number;\n accessToken?: string;\n payload?: any;\n signature?: string;\n sessionId?: string;\n // Note: personId is NOT returned for security reasons in external integrations\n}> => {\n ensureSecureContext();\n\n const controller = createTimeoutController();\n const compressedImage = await compressImage(image);\n\n // Convert image to base64\n const base64Image = await new Promise<string>((resolve, reject) => {\n const reader = new FileReader();\n reader.onload = () => {\n const result = reader.result as string;\n resolve(result.split(',')[1]); // Remove data:image/... prefix\n };\n reader.onerror = reject;\n reader.readAsDataURL(compressedImage);\n });\n\n try {\n const requestBody: any = {\n biometric_data: base64Image,\n type_of_identification: 'FACE',\n purpose: purpose,\n confidence_threshold: confidenceThreshold,\n };\n\n // Include signature if provided (for external integrations)\n if (signature) {\n requestBody.signature = signature;\n }\n\n const response = await fetch(`${getApiBaseUrl()}/api/v1/external/recognition/purpose/`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'X-App-Token': applicationToken,\n },\n body: JSON.stringify(requestBody),\n signal: controller.signal,\n });\n\n if (!response.ok) {\n if (response.status === 401 || response.status === 403) {\n throw new NeoFaceError('Invalid or expired application token', ErrorType.INVALID_TOKEN);\n }\n throw new NeoFaceError(`Server returned status ${response.status}`, ErrorType.NETWORK);\n }\n\n const data = await response.json();\n\n return {\n success: data.success,\n personName: data.person_name || data.person?.name,\n email: data.email || sessionData?.email,\n cpf: data.cpf || sessionData?.cpf,\n confidenceScore: data.confidence_score,\n accessToken: data.access_token,\n payload: data.payload,\n // Return signature and sessionId for external integrations\n signature: signature,\n sessionId: sessionData?.sessionId,\n // Note: personId is intentionally NOT returned for security reasons\n };\n } catch (error) {\n if (error instanceof Error) {\n if (error.name === 'AbortError') {\n throw new NeoFaceError('Request timed out', ErrorType.NETWORK);\n }\n if (error instanceof NeoFaceError) {\n throw error;\n }\n throw new NeoFaceError(error.message, ErrorType.NETWORK);\n }\n throw new NeoFaceError('Unknown error occurred', ErrorType.NETWORK_ERROR);\n }\n};\n\n/**\n * Register a person without face biometric data\n * @param personData Person registration data\n * @param applicationToken The application token for authentication\n * @param options Optional document images\n * @returns Promise with registration result\n */\nexport const registerPersonWithoutFace = async (\n personData: {\n name: string;\n birth_date: string;\n cpf: string;\n email: string;\n password: string;\n },\n applicationToken: string,\n options?: {\n documentFront?: Blob;\n documentBack?: Blob;\n documentType?: string;\n }\n): Promise<{\n success: boolean;\n message: string;\n person_id?: string;\n user_id?: string;\n person_validated?: boolean;\n onboarding_link?: string;\n data?: any;\n}> => {\n ensureSecureContext();\n\n const controller = createTimeoutController();\n\n try {\n // Convert document photos to base64 if provided\n let documentFrontBase64: string | undefined;\n let documentBackBase64: string | undefined;\n\n if (options?.documentFront) {\n const compressed = await compressDocumentImage(options.documentFront);\n documentFrontBase64 = await new Promise<string>((resolve, reject) => {\n const reader = new FileReader();\n reader.onload = () => resolve(reader.result as string);\n reader.onerror = reject;\n reader.readAsDataURL(compressed);\n });\n }\n\n if (options?.documentBack) {\n const compressed = await compressDocumentImage(options.documentBack);\n documentBackBase64 = await new Promise<string>((resolve, reject) => {\n const reader = new FileReader();\n reader.onload = () => resolve(reader.result as string);\n reader.onerror = reject;\n reader.readAsDataURL(compressed);\n });\n }\n\n const body: any = {\n name: personData.name,\n birth_date: personData.birth_date,\n cpf: personData.cpf,\n email: personData.email,\n password: personData.password,\n password_confirm: personData.password,\n };\n\n // Add document_images in the format expected by backend: array of {front, back, type}\n if (documentFrontBase64) {\n body.document_images = [{\n front: documentFrontBase64,\n back: documentBackBase64 || undefined,\n type: options?.documentType || 'RG'\n }];\n }\n\n const response = await fetch(`${getApiBaseUrl()}/api/v1/signup/donor/`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'X-App-Token': applicationToken,\n },\n body: JSON.stringify(body),\n signal: controller.signal,\n });\n\n if (!response.ok) {\n if (response.status === 401 || response.status === 403) {\n throw new NeoFaceError('Invalid or expired application token', ErrorType.INVALID_TOKEN);\n }\n throw new NeoFaceError(`Server returned status ${response.status}`, ErrorType.NETWORK);\n }\n\n const data = await response.json();\n return data;\n } catch (error) {\n if (error instanceof Error) {\n if (error.name === 'AbortError') {\n throw new NeoFaceError('Request timed out', ErrorType.NETWORK);\n }\n if (error instanceof NeoFaceError) {\n throw error;\n }\n throw new NeoFaceError(error.message, ErrorType.NETWORK);\n }\n throw new NeoFaceError('Unknown error occurred', ErrorType.NETWORK);\n }\n};\n\n/**\n * Register a person with face biometric data and liveness detection\n * @param personData Person registration data\n * @param facePhotos Array of face photos for biometric registration (up to 5 photos)\n * @param applicationToken The application token for authentication\n * @param options Optional document images\n * @returns Promise with registration result\n */\nexport const registerPersonWithBiometric = async (\n personData: {\n name: string;\n birth_date: string;\n cpf: string;\n email: string;\n password: string;\n },\n facePhotos: Blob[],\n applicationToken: string,\n options?: {\n documentFront?: Blob;\n documentBack?: Blob;\n documentType?: string;\n }\n): Promise<{\n success: boolean;\n message: string;\n person_id?: string;\n user_id?: string;\n person_validated?: boolean;\n face_processing?: any;\n onboarding_link?: string;\n task_id?: string;\n status?: string;\n}> => {\n ensureSecureContext();\n\n if (!facePhotos || facePhotos.length === 0) {\n throw new NeoFaceError(\n 'At least one face photo is required for biometric registration',\n ErrorType.VALIDATION_ERROR\n );\n }\n\n if (facePhotos.length > 5) {\n throw new NeoFaceError('Maximum of 5 face photos allowed', ErrorType.VALIDATION_ERROR);\n }\n\n const controller = createTimeoutController();\n\n try {\n // Convert all face photos to base64\n const facePhotosBase64 = await Promise.all(\n facePhotos.map(async photo => {\n const compressedImage = await compressImage(photo);\n return new Promise<string>((resolve, reject) => {\n const reader = new FileReader();\n reader.onload = () => {\n const result = reader.result as string;\n resolve(result); // Keep the full data URL for backend processing\n };\n reader.onerror = reject;\n reader.readAsDataURL(compressedImage);\n });\n })\n );\n\n // Convert document photos to base64 if provided\n let documentFrontBase64: string | undefined;\n let documentBackBase64: string | undefined;\n\n if (options?.documentFront) {\n const compressed = await compressDocumentImage(options.documentFront);\n documentFrontBase64 = await new Promise<string>((resolve, reject) => {\n const reader = new FileReader();\n reader.onload = () => resolve(reader.result as string);\n reader.onerror = reject;\n reader.readAsDataURL(compressed);\n });\n }\n\n if (options?.documentBack) {\n const compressed = await compressDocumentImage(options.documentBack);\n documentBackBase64 = await new Promise<string>((resolve, reject) => {\n const reader = new FileReader();\n reader.onload = () => resolve(reader.result as string);\n reader.onerror = reject;\n reader.readAsDataURL(compressed);\n });\n }\n\n const body: any = {\n name: personData.name,\n birth_date: personData.birth_date,\n cpf: personData.cpf,\n email: personData.email,\n password: personData.password,\n password_confirm: personData.password,\n face_photos: facePhotosBase64,\n };\n\n // Add document_images in the format expected by backend: array of {front, back, type}\n if (documentFrontBase64) {\n body.document_images = [{\n front: documentFrontBase64,\n back: documentBackBase64 || undefined,\n type: options?.documentType || 'RG'\n }];\n }\n\n const response = await fetch(`${getApiBaseUrl()}/api/v1/signup/donor/`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'X-App-Token': applicationToken,\n },\n body: JSON.stringify(body),\n signal: controller.signal,\n });\n\n if (!response.ok) {\n if (response.status === 401 || response.status === 403) {\n throw new NeoFaceError('Invalid or expired application token', ErrorType.INVALID_TOKEN);\n }\n\n // Try to get error details from response\n try {\n const errorData = await response.json();\n\n // Handle validation errors with detailed messages\n if (errorData.errors) {\n const errorMessages = Object.entries(errorData.errors)\n .map(([field, messages]: [string, any]) => {\n const fieldName =\n field === 'password'\n ? 'senha'\n : field === 'password_confirm'\n ? 'confirmação de senha'\n : field;\n const messageList = Array.isArray(messages) ? messages.join(', ') : String(messages);\n return `${fieldName}: ${messageList}`;\n })\n .join('; ');\n throw new NeoFaceError(`Erro de validação: ${errorMessages}`, ErrorType.VALIDATION_ERROR);\n }\n\n if (errorData.error === 'fraud_detected') {\n throw new NeoFaceError(\n 'Fraud detected in biometric images. Registration blocked.',\n ErrorType.VALIDATION_ERROR\n );\n }\n if (errorData.error === 'user_exists' || errorData.error_code === 'ALREADY_EXISTS') {\n throw new NeoFaceError('User with this email already exists', ErrorType.VALIDATION_ERROR);\n }\n if (errorData.error === 'person_exists') {\n throw new NeoFaceError('Person with this CPF already exists', ErrorType.VALIDATION_ERROR);\n }\n\n // Use error message from backend if available\n const errorMessage =\n errorData.message || errorData.error || `Server returned status ${response.status}`;\n throw new NeoFaceError(errorMessage, ErrorType.VALIDATION_ERROR);\n } catch (parseError) {\n // If we can't parse the error, throw a generic error\n if (parseError instanceof NeoFaceError) {\n throw parseError;\n }\n throw new NeoFaceError(`Server returned status ${response.status}`, ErrorType.NETWORK);\n }\n }\n\n const data = await response.json();\n\n // Handle async processing (status 202) - returns task_id\n if (response.status === 202 && data.data?.task_id) {\n return {\n success: data.success,\n message:\n data.message ||\n 'Registration is being processed. You will receive an email when completed.',\n task_id: data.data.task_id,\n status: data.data.status || 'processing',\n person_id: undefined,\n user_id: undefined,\n person_validated: undefined,\n face_processing: undefined,\n onboarding_link: undefined,\n };\n }\n\n // Handle sync processing (status 201) - returns person_id and user_id\n return {\n success: data.success,\n message: data.message,\n person_id: data.data?.person_id || data.person_id,\n user_id: data.data?.user_id || data.user_id,\n person_validated: data.data?.person_validated || data.person_validated,\n face_processing: data.data?.face_processing || data.face_processing,\n onboarding_link: data.data?.onboarding_link || data.onboarding_link,\n task_id: data.data?.task_id,\n status: data.data?.status,\n };\n } catch (error) {\n if (error instanceof Error) {\n if (error.name === 'AbortError') {\n throw new NeoFaceError('Request timed out', ErrorType.NETWORK);\n }\n if (error instanceof NeoFaceError) {\n throw error;\n }\n throw new NeoFaceError(error.message, ErrorType.NETWORK);\n }\n throw new NeoFaceError('Unknown error occurred', ErrorType.NETWORK_ERROR);\n }\n};\n\n/**\n * Performs login using email and password\n * @param email User's email\n * @param password User's password\n * @param applicationToken The application token for authentication\n * @returns Promise with login result\n * @throws NeoFaceError if request fails\n */\nexport const loginWithEmail = async (\n email: string,\n password: string,\n applicationToken: string\n): Promise<BiometricLoginResult> => {\n ensureSecureContext();\n\n const controller = createTimeoutController();\n\n try {\n const response = await fetch(`${getApiBaseUrl()}/api/v1/auth/login/`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'X-App-Token': applicationToken,\n },\n body: JSON.stringify({ email, password }),\n signal: controller.signal,\n });\n\n if (!response.ok) {\n if (response.status === 401 || response.status === 403) {\n throw new NeoFaceError('Email ou senha inválidos', ErrorType.INVALID_TOKEN);\n }\n throw new NeoFaceError(`API Error ${response.status}`, ErrorType.API_ERROR);\n }\n\n const data = await response.json();\n\n return {\n success: true,\n // Compatível com diferentes formatos de resposta do backend\n accessToken: data.access_token || data.access,\n alias: (data.user && data.user.alias) || data.alias,\n user: {\n id: (data.user && data.user.id) || '',\n email: (data.user && data.user.email) || '',\n role: (data.user && data.user.role) || '',\n validated: (data.user && data.user.validated) || false,\n active: (data.user && data.user.active) || false,\n },\n };\n } catch (error) {\n if (error instanceof Error) {\n if (error.name === 'AbortError') {\n throw new NeoFaceError('Request timed out', ErrorType.NETWORK);\n }\n if (error instanceof NeoFaceError) {\n throw error;\n }\n throw new NeoFaceError(error.message, ErrorType.NETWORK);\n }\n throw new NeoFaceError('Unknown error occurred', ErrorType.NETWORK_ERROR);\n }\n};\n\n/**\n * Identifies a person from an image and returns name and age\n * @param image The image blob to process\n * @param applicationToken The application token for authentication\n * @returns Promise with person name and age\n * @throws NeoFaceError if request fails\n * @throws NeoFaceError if identification fails\n */\nexport const identifyPerson = async (\n image: Blob,\n applicationToken: string\n): Promise<{\n name: string;\n age: number;\n}> => {\n ensureSecureContext();\n\n const controller = createTimeoutController();\n const compressedImage = await compressImage(image);\n\n // Convert image to base64\n const base64Image = await new Promise<string>((resolve, reject) => {\n const reader = new FileReader();\n reader.onload = () => {\n const result = reader.result as string;\n resolve(result.split(',')[1]); // Remove data:image/... prefix\n };\n reader.onerror = reject;\n reader.readAsDataURL(compressedImage);\n });\n\n try {\n const response = await fetch(`${getApiBaseUrl()}/api/v1/external/recognition/face/`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'X-App-Token': applicationToken,\n },\n body: JSON.stringify({\n image: base64Image,\n purpose: 'SIMPLE_IDENTIFICATION_F',\n }),\n signal: controller.signal,\n });\n\n if (!response.ok) {\n if (response.status === 401 || response.status === 403) {\n throw new NeoFaceError('Invalid or expired application token', ErrorType.INVALID_TOKEN);\n }\n throw new NeoFaceError(`Server returned status ${response.status}`, ErrorType.NETWORK);\n }\n\n const data = await response.json();\n\n if (!data.success) {\n throw new NeoFaceError(\n data.message || 'Person identification failed',\n ErrorType.RECOGNITION_FAILED\n );\n }\n\n // Extract name and birth_date from response\n const personName = data.person?.name || '';\n const birthDateStr = data.person?.birth_date;\n\n if (!personName) {\n throw new NeoFaceError('Person name not found in response', ErrorType.PERSON_NOT_FOUND);\n }\n\n if (!birthDateStr) {\n throw new NeoFaceError('Birth date not found in response', ErrorType.PERSON_NOT_FOUND);\n }\n\n // Calculate age from birth_date\n const birthDate = new Date(birthDateStr);\n const today = new Date();\n let age = today.getFullYear() - birthDate.getFullYear();\n const monthDiff = today.getMonth() - birthDate.getMonth();\n\n // Adjust age if birthday hasn't occurred this year\n if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birthDate.getDate())) {\n age--;\n }\n\n return {\n name: personName,\n age: age,\n };\n } catch (error) {\n if (error instanceof Error) {\n if (error.name === 'AbortError') {\n throw new NeoFaceError('Request timed out', ErrorType.NETWORK);\n }\n if (error instanceof NeoFaceError) {\n throw error;\n }\n throw new NeoFaceError(error.message, ErrorType.NETWORK);\n }\n throw new NeoFaceError('Unknown error occurred', ErrorType.NETWORK_ERROR);\n }\n};\n\n/**\n * NEO-101: Register biometric data for an existing person\n *\n * This method registers facial biometric data for a person already in the system.\n * It validates the image and sends it to the backend.\n *\n * @param personId The ID of the person to register biometric data for\n * @param faceImage The face image blob to register\n * @param applicationToken The application token for authentication\n * @returns Promise with registration result\n * @throws NeoFaceError if request fails\n * @throws NeoFaceError if validation fails\n */\nexport const registerBiometric = async (\n personId: string,\n faceImage: Blob,\n applicationToken: string\n): Promise<{\n success: boolean;\n message: string;\n identity_data_id?: string;\n type_of_identification?: string;\n have_biometric_data?: boolean;\n created_at?: string;\n}> => {\n ensureSecureContext();\n\n if (!personId) {\n throw new NeoFaceError('Person ID is required', ErrorType.VALIDATION_ERROR);\n }\n\n if (!faceImage) {\n throw new NeoFaceError('Face image is required', ErrorType.VALIDATION_ERROR);\n }\n\n const controller = createTimeoutController();\n const compressedImage = await compressImage(faceImage);\n\n // Convert image to base64 for biometric data\n const base64Image = await new Promise<string>((resolve, reject) => {\n const reader = new FileReader();\n reader.onload = () => {\n const result = reader.result as string;\n resolve(result.split(',')[1]); // Remove data:image/... prefix\n };\n reader.onerror = reject;\n reader.readAsDataURL(compressedImage);\n });\n\n try {\n const response = await fetch(`${getApiBaseUrl()}/api/v1/identity-data/`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'X-App-Token': applicationToken,\n },\n body: JSON.stringify({\n person: personId,\n type_of_identification: 'FACE',\n biometric_data: base64Image,\n }),\n signal: controller.signal,\n });\n\n if (!response.ok) {\n if (response.status === 401 || response.status === 403) {\n throw new NeoFaceError(\n 'Invalid or expired application token, or insufficient permissions',\n ErrorType.INVALID_TOKEN\n );\n }\n if (response.status === 400) {\n // Try to get validation error details\n try {\n const errorData = await response.json();\n const errorMessage = errorData.message || errorData.error || 'Invalid biometric data';\n throw new NeoFaceError(errorMessage, ErrorType.VALIDATION_ERROR);\n } catch (parseError) {\n if (parseError instanceof NeoFaceError) {\n throw parseError;\n }\n throw new NeoFaceError('Invalid biometric data', ErrorType.VALIDATION_ERROR);\n }\n }\n throw new NeoFaceError(`Server returned status ${response.status}`, ErrorType.NETWORK);\n }\n\n const data = await response.json();\n\n return {\n success: true,\n message: 'Biometric data registered successfully',\n identity_data_id: data.id,\n type_of_identification: data.type_of_identification,\n have_biometric_data: data.have_biometric_data,\n created_at: data.created_at,\n };\n } catch (error) {\n if (error instanceof Error) {\n if (error.name === 'AbortError') {\n throw new NeoFaceError('Request timed out', ErrorType.NETWORK);\n }\n if (error instanceof NeoFaceError) {\n throw error;\n }\n throw new NeoFaceError(error.message, ErrorType.NETWORK);\n }\n throw new NeoFaceError('Unknown error occurred', ErrorType.NETWORK_ERROR);\n }\n};\n\n/**\n * Register a new application for a consumer\n * @param jwtToken JWT authentication token from logged user\n * @param consumerId Consumer ID (UUID) who will own the application\n * @param applicationData Application registration data\n * @returns Promise with registration result\n * @throws NeoFaceError if registration fails\n *\n * @example\n * ```typescript\n * const result = await registerApplication(\n * 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...',\n * 'consumer-uuid-123',\n * {\n * applicationName: 'My App',\n * domain: 'example.com',\n * acceptOnlyEmailWithSameDomain: true\n * }\n * );\n * console.log('App Token:', result.application.app_token);\n * ```\n */\nexport const registerApplication = async (\n jwtToken: string,\n consumerId: string,\n applicationData: ApplicationRegistrationData\n): Promise<ApplicationRegistrationResult> => {\n ensureSecureContext();\n\n const controller = createTimeoutController();\n\n try {\n const response = await fetch(`${getApiBaseUrl()}/api/v1/applications/`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${jwtToken}`,\n },\n body: JSON.stringify({\n consumer: consumerId,\n application_name: applicationData.applicationName,\n domain: applicationData.domain,\n accept_only_email_with_same_domain: applicationData.acceptOnlyEmailWithSameDomain,\n }),\n signal: controller.signal,\n });\n\n if (!response.ok) {\n if (response.status === 401) {\n throw new NeoFaceError('Invalid or expired JWT token', ErrorType.INVALID_TOKEN);\n }\n if (response.status === 403) {\n throw new NeoFaceError(\n 'Insufficient permissions to create application',\n ErrorType.INVALID_TOKEN\n );\n }\n if (response.status === 400) {\n // Try to get validation error details\n try {\n const errorData = await response.json();\n const errorMessage = errorData.message || errorData.error || 'Invalid application data';\n throw new NeoFaceError(errorMessage, ErrorType.VALIDATION_ERROR);\n } catch (parseError) {\n if (parseError instanceof NeoFaceError) {\n throw parseError;\n }\n throw new NeoFaceError('Invalid application data', ErrorType.VALIDATION_ERROR);\n }\n }\n throw new NeoFaceError(`Server returned status ${response.status}`, ErrorType.NETWORK);\n }\n\n const data = await response.json();\n\n return {\n success: true,\n application: {\n id: data.id,\n application_name: data.application_name,\n domain: data.domain,\n accept_only_email_with_same_domain: data.accept_only_email_with_same_domain,\n app_token: data.app_token,\n is_active: data.is_active,\n created_at: data.created_at,\n },\n message: 'Application registered successfully',\n };\n } catch (error) {\n if (error instanceof Error) {\n if (error.name === 'AbortError') {\n throw new NeoFaceError('Request timed out', ErrorType.NETWORK);\n }\n if (error instanceof NeoFaceError) {\n throw error;\n }\n throw new NeoFaceError(error.message, ErrorType.NETWORK);\n }\n throw new NeoFaceError('Unknown error occurred', ErrorType.NETWORK_ERROR);\n }\n};\n\n// ============================================================================\n// PROOF OF LIFE - Interfaces and Functions\n// ============================================================================\n\n/**\n * Personal data item returned by PROOF_OF_LIFE\n */\nexport interface PersonalDataItem {\n type: string;\n english_label?: string;\n value: string;\n masked: boolean;\n validated: boolean;\n is_principal_identification?: boolean;\n data_classification?: string;\n}\n\n/**\n * Result of PROOF_OF_LIFE operation\n */\nexport interface ProofOfLifeResult {\n success: boolean;\n isLive: boolean;\n livenessScore: number;\n faceRecognitionScore: number;\n combinedConfidence: number;\n personId?: string;\n personalData: PersonalDataItem[];\n processingTime: number;\n historyId: string;\n taskId: string;\n message?: string;\n error?: string;\n}\n\n/**\n * Task status response from backend\n */\nexport interface TaskStatusResponse {\n success: boolean;\n message: string;\n timestamp: string;\n data: {\n task_id: string;\n status: 'pending' | 'started' | 'success' | 'failure' | 'retry' | 'revoked';\n message?: string;\n result?: {\n success: boolean;\n is_live?: boolean;\n liveness_score?: number;\n face_recognition_score?: number;\n combined_confidence?: number;\n person_id?: string;\n personal_data?: PersonalDataItem[];\n personal_data_count?: number;\n processing_time?: number;\n history_id?: string;\n error?: string;\n message?: string;\n };\n error?: string;\n progress?: {\n current: number;\n total: number;\n percentage: number;\n };\n };\n}\n\n/**\n * Options for recording face video\n */\nexport interface RecordVideoOptions {\n durationMs?: number; // Duration in milliseconds (default: 3000)\n mimeType?: string; // MIME type for video (default: 'video/webm')\n onProgress?: (progress: number) => void; // Progress callback (0-100)\n}\n\n/**\n * Options for polling task status\n */\nexport interface PollTaskStatusOptions {\n maxAttempts?: number; // Maximum polling attempts (default: 60)\n intervalMs?: number; // Interval between polls in ms (default: 1000)\n onStatusChange?: (status: string, progress?: number) => void; // Status change callback\n}\n\n/**\n * Records a video from the camera for a specified duration\n * @param options Recording options\n * @returns Promise that resolves to a video Blob\n * @throws NeoFaceError if recording fails\n */\nexport const recordFaceVideo = async (options: RecordVideoOptions = {}): Promise<Blob> => {\n ensureSecureContext();\n\n const { durationMs = 3000, mimeType = 'video/webm', onProgress } = options;\n\n return new Promise(async (resolve, reject) => {\n let stream: MediaStream | null = null;\n let mediaRecorder: MediaRecorder | null = null;\n const chunks: Blob[] = [];\n\n try {\n // Request camera access\n stream = await navigator.mediaDevices.getUserMedia({\n video: {\n facingMode: 'user',\n width: { ideal: 640 },\n height: { ideal: 480 },\n },\n audio: false,\n });\n\n // Determine the best supported MIME type\n let selectedMimeType = mimeType;\n const mimeTypes = ['video/webm;codecs=vp9', 'video/webm;codecs=vp8', 'video/webm', 'video/mp4'];\n\n for (const type of mimeTypes) {\n if (MediaRecorder.isTypeSupported(type)) {\n selectedMimeType = type;\n break;\n }\n }\n\n // Create MediaRecorder\n mediaRecorder = new MediaRecorder(stream, {\n mimeType: selectedMimeType,\n videoBitsPerSecond: 1000000, // 1 Mbps\n });\n\n // Collect video chunks\n mediaRecorder.ondataavailable = event => {\n if (event.data.size > 0) {\n chunks.push(event.data);\n }\n };\n\n // Handle recording complete\n mediaRecorder.onstop = () => {\n // Stop all tracks\n if (stream) {\n stream.getTracks().forEach(track => track.stop());\n }\n\n // Create video blob\n const videoBlob = new Blob(chunks, { type: selectedMimeType });\n resolve(videoBlob);\n };\n\n // Handle recording error\n mediaRecorder.onerror = event => {\n if (stream) {\n stream.getTracks().forEach(track => track.stop());\n }\n reject(new NeoFaceError(`Recording error: ${event}`, ErrorType.CAPTURE_ERROR));\n };\n\n // Start recording\n mediaRecorder.start(100); // Collect data every 100ms\n\n // Progress tracking\n if (onProgress) {\n const progressInterval = 100; // Update every 100ms\n let elapsed = 0;\n\n const progressTimer = setInterval(() => {\n elapsed += progressInterval;\n const progress = Math.min(100, (elapsed / durationMs) * 100);\n onProgress(progress);\n\n if (elapsed >= durationMs) {\n clearInterval(progressTimer);\n }\n }, progressInterval);\n }\n\n // Stop recording after duration\n setTimeout(() => {\n if (mediaRecorder && mediaRecorder.state === 'recording') {\n mediaRecorder.stop();\n }\n }, durationMs);\n } catch (error) {\n // Cleanup on error\n if (stream) {\n stream.getTracks().forEach(track => track.stop());\n }\n\n if (error instanceof Error) {\n if (error.name === 'NotAllowedError') {\n reject(new NeoFaceError('Camera permission denied', ErrorType.CAMERA_ERROR));\n } else if (error.name === 'NotFoundError') {\n reject(new NeoFaceError('No camera found', ErrorType.NO_CAMERA));\n } else {\n reject(new NeoFaceError(error.message, ErrorType.CAPTURE_ERROR));\n }\n } else {\n reject(new NeoFaceError('Failed to record video', ErrorType.CAPTURE_ERROR));\n }\n }\n });\n};\n\n/**\n * Polls the task status until completion or failure\n * @param taskId Task ID to poll\n * @param applicationToken Application token for authentication\n * @param options Polling options\n * @returns Promise that resolves to the final task result\n * @throws NeoFaceError if polling fails or task fails\n */\nexport const pollTaskStatus = async (\n taskId: string,\n applicationToken: string,\n options: PollTaskStatusOptions = {}\n): Promise<TaskStatusResponse['data']['result']> => {\n const { maxAttempts = 60, intervalMs = 1000, onStatusChange } = options;\n\n let attempts = 0;\n\n while (attempts < maxAttempts) {\n attempts++;\n\n try {\n const response = await fetch(`${getApiBaseUrl()}/api/v1/tasks/status/?task_id=${taskId}`, {\n method: 'GET',\n headers: {\n Accept: 'application/json',\n 'X-App-Token': applicationToken,\n },\n });\n\n if (!response.ok) {\n throw new NeoFaceError(`Task status check failed: ${response.status}`, ErrorType.NETWORK);\n }\n\n const data: TaskStatusResponse = await response.json();\n const status = data.data?.status || 'unknown';\n\n // Notify status change\n if (onStatusChange) {\n const progress = data.data?.progress?.percentage;\n onStatusChange(status, progress);\n }\n\n // Check terminal states\n if (status === 'success') {\n return data.data.result;\n }\n\n if (status === 'failure') {\n const errorMessage = data.data?.result?.message || data.data?.error || 'Task failed';\n throw new NeoFaceError(errorMessage, ErrorType.RECOGNITION_FAILED);\n }\n\n if (status === 'revoked') {\n throw new NeoFaceError('Task was cancelled', ErrorType.RECOGNITION_FAILED);\n }\n\n // Wait before next poll\n await new Promise(resolve => setTimeout(resolve, intervalMs));\n } catch (error) {\n if (error instanceof NeoFaceError) {\n throw error;\n }\n throw new NeoFaceError(\n `Polling error: ${error instanceof Error ? error.message : 'Unknown error'}`,\n ErrorType.NETWORK\n );\n }\n }\n\n throw new NeoFaceError('Task status polling timed out', ErrorType.NETWORK);\n};\n\n/**\n * Starts the PROOF_OF_LIFE recognition process\n * @param videoBase64 Video data as base64 string\n * @param applicationToken Application token for authentication\n * @returns Promise with task_id and history_id for polling\n * @throws NeoFaceError if request fails\n */\nexport const startProofOfLife = async (\n videoBase64: string,\n applicationToken: string\n): Promise<{\n taskId: string;\n historyId: string;\n status: string;\n message: string;\n}> => {\n ensureSecureContext();\n\n const controller = createTimeoutController();\n\n try {\n const response = await fetch(`${getApiBaseUrl()}/api/v1/auth/recognition/face/external/`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'X-App-Token': applicationToken,\n },\n body: JSON.stringify({\n video: videoBase64,\n purpose: 'PROOF_OF_LIFE',\n }),\n signal: controller.signal,\n });\n\n if (!response.ok) {\n if (response.status === 401 || response.status === 403) {\n throw new NeoFaceError('Invalid or expired application token', ErrorType.INVALID_TOKEN);\n }\n if (response.status === 400) {\n const errorData = await response.json();\n throw new NeoFaceError(\n errorData.message || 'Invalid request data',\n ErrorType.VALIDATION_ERROR\n );\n }\n throw new NeoFaceError(`Server returned status ${response.status}`, ErrorType.NETWORK);\n }\n\n const data = await response.json();\n\n if (!data.success || !data.data?.task_id) {\n throw new NeoFaceError(\n data.message || 'Failed to start proof of life',\n ErrorType.RECOGNITION_FAILED\n );\n }\n\n return {\n taskId: data.data.task_id,\n historyId: data.data.history_id,\n status: data.data.status,\n message: data.message || 'Recognition started successfully',\n };\n } catch (error) {\n if (error instanceof Error) {\n if (error.name === 'AbortError') {\n throw new NeoFaceError('Request timed out', ErrorType.NETWORK);\n }\n if (error instanceof NeoFaceError) {\n throw error;\n }\n throw new NeoFaceError(error.message, ErrorType.NETWORK);\n }\n throw new NeoFaceError('Unknown error occurred', ErrorType.NETWORK_ERROR);\n }\n};\n\n/**\n * Converts a video Blob to base64 string\n * @param videoBlob Video blob to convert\n * @returns Promise that resolves to base64 string (without data URI prefix)\n */\nexport const videoToBase64 = async (videoBlob: Blob): Promise<string> => {\n return new Promise((resolve, reject) => {\n const reader = new FileReader();\n reader.onload = () => {\n const result = reader.result as string;\n // Remove data URI prefix if present (e.g., \"data:video/webm;base64,\")\n const base64 = result.includes(',') ? result.split(',')[1] : result;\n resolve(base64);\n };\n reader.onerror = () => reject(new NeoFaceError('Failed to convert video to base64', ErrorType.CAPTURE_ERROR));\n reader.readAsDataURL(videoBlob);\n });\n};\n","import { useEffect, useRef, useState, useReducer, CSSProperties } from 'react';\nimport * as faceapi from 'face-api.js';\nimport { useCamera } from '../hooks/useCamera';\nimport { recognize } from '../api';\n\ninterface RecognitionResult {\n success: boolean;\n data: {\n faceId: string;\n confidence: number;\n };\n}\n\ninterface FaceCaptureModalProps {\n accessToken: string;\n onClose: () => void;\n onSuccess: (result: RecognitionResult) => void;\n}\n\n// Define the state types\ntype ModalState =\n | { status: 'requestingPermission' }\n | { status: 'ready' }\n | { status: 'capturing' }\n | { status: 'uploading' }\n | { status: 'success' }\n | { status: 'error'; message: string };\n\n// Define the action types\ntype ModalAction =\n | { type: 'PERMISSION_GRANTED' }\n | { type: 'PERMISSION_DENIED' }\n | { type: 'START_CAPTURE' }\n | { type: 'UPLOAD_START' }\n | { type: 'UPLOAD_SUCCESS' }\n | { type: 'UPLOAD_ERROR'; message: string }\n | { type: 'RESET' }\n | { type: 'SET_ERROR'; message: string };\n\n// Reducer function\nconst modalReducer = (state: ModalState, action: ModalAction): ModalState => {\n switch (action.type) {\n case 'PERMISSION_GRANTED':\n return { status: 'ready' };\n case 'PERMISSION_DENIED':\n return { status: 'error', message: 'Permissão para acessar a câmera foi negada' };\n case 'START_CAPTURE':\n return { status: 'capturing' };\n case 'UPLOAD_START':\n return { status: 'uploading' };\n case 'UPLOAD_SUCCESS':\n return { status: 'success' };\n case 'UPLOAD_ERROR':\n return { status: 'error', message: action.message };\n case 'RESET':\n return { status: 'ready' };\n case 'SET_ERROR':\n return { status: 'error', message: action.message };\n default:\n return state;\n }\n};\n\n// Styles\nconst styles: Record<string, CSSProperties> = {\n overlay: {\n position: 'fixed',\n top: 0,\n left: 0,\n right: 0,\n bottom: 0,\n backgroundColor: 'rgba(0, 0, 0, 0.8)',\n display: 'flex',\n justifyContent: 'center',\n alignItems: 'center',\n zIndex: 1000,\n },\n modal: {\n backgroundColor: '#000',\n borderRadius: '12px',\n overflow: 'hidden',\n position: 'relative',\n width: '60%',\n maxWidth: '600px',\n maxHeight: '80vh',\n },\n video: {\n width: '100%',\n height: 'auto',\n display: 'block',\n },\n canvas: {\n display: 'none',\n },\n guide: {\n position: 'absolute',\n top: '50%',\n left: '50%',\n transform: 'translate(-50%, -50%)',\n width: '200px',\n height: '200px',\n border: '2px solid rgba(255, 255, 255, 0.5)',\n borderRadius: '50%',\n pointerEvents: 'none',\n },\n controls: {\n position: 'absolute',\n bottom: '20px',\n left: 0,\n right: 0,\n display: 'flex',\n justifyContent: 'center',\n gap: '10px',\n },\n button: {\n padding: '12px 24px',\n borderRadius: '24px',\n border: 'none',\n backgroundColor: '#007AFF',\n color: 'white',\n fontSize: '16px',\n fontWeight: 'bold',\n cursor: 'pointer',\n },\n message: {\n position: 'absolute',\n top: '20px',\n left: 0,\n right: 0,\n textAlign: 'center',\n color: 'white',\n fontSize: '18px',\n fontWeight: 'bold',\n },\n spinner: {\n position: 'absolute',\n top: '50%',\n left: '50%',\n transform: 'translate(-50%, -50%)',\n width: '40px',\n height: '40px',\n border: '4px solid rgba(255, 255, 255, 0.3)',\n borderRadius: '50%',\n borderTopColor: 'white',\n animation: 'spin 1s linear infinite',\n },\n closeButton: {\n position: 'absolute',\n top: '20px',\n right: '20px',\n backgroundColor: 'transparent',\n border: 'none',\n color: 'white',\n fontSize: '24px',\n cursor: 'pointer',\n },\n};\n\n// Media query styles\nconst mediaStyles = `\n @media (max-width: 768px) {\n .modal {\n width: 100%;\n height: 100%;\n max-height: 100vh;\n border-radius: 0;\n }\n .button {\n padding: 16px 32px;\n font-size: 18px;\n }\n }\n`;\n\n// Add keyframes for spinner animation\nconst keyframes = `\n @keyframes spin {\n 0% { transform: translate(-50%, -50%) rotate(0deg); }\n 100% { transform: translate(-50%, -50%) rotate(360deg); }\n }\n`;\n\nexport function FaceCaptureModal({ accessToken, onClose, onSuccess }: FaceCaptureModalProps) {\n // Refs\n const videoRef = useRef<HTMLVideoElement>(null);\n const canvasRef = useRef<HTMLCanvasElement>(null);\n const streamRef = useRef<MediaStream | null>(null);\n const faceDetectionTimeoutRef = useRef<number | null>(null);\n const livenessCheckRef = useRef<number | null>(null);\n\n // State\n const [state, dispatch] = useReducer(modalReducer, { status: 'requestingPermission' });\n const [faceDetected, setFaceDetected] = useState(false);\n const [livenessAttempts, setLivenessAttempts] = useState(0);\n\n // Camera hook\n const { startCamera, stopCamera } = useCamera();\n\n // Check if face is centered in the frame\n const isFaceCentered = (detection: faceapi.FaceDetection): boolean => {\n if (!videoRef.current) return false;\n\n const { box } = detection;\n const { videoWidth, videoHeight } = videoRef.current;\n\n // Define the center region (40% of the frame)\n const centerX = videoWidth / 2;\n const centerY = videoHeight / 2;\n const centerWidth = videoWidth * 0.4;\n const centerHeight = videoHeight * 0.4;\n\n // Check if face is within the center region\n return (\n box.x + box.width / 2 > centerX - centerWidth / 2 &&\n box.x + box.width / 2 < centerX + centerWidth / 2 &&\n box.y + box.height / 2 > centerY - centerHeight / 2 &&\n box.y + box.height / 2 < centerY + centerHeight / 2\n );\n };\n\n // Start liveness check\n const startLivenessCheck = () => {\n // Wait 3 seconds before starting liveness check\n livenessCheckRef.current = window.setTimeout(() => {\n if (state.status === 'ready') {\n setLivenessAttempts(prev => prev + 1);\n dispatch({ type: 'START_CAPTURE' });\n }\n }, 3000);\n };\n\n // Face detection function\n const startFaceDetection = () => {\n if (!videoRef.current) return;\n\n const detectFaces = async () => {\n if (!videoRef.current || state.status !== 'ready') return;\n\n try {\n const detections = await faceapi.detectAllFaces(\n videoRef.current,\n new faceapi.TinyFaceDetectorOptions()\n );\n\n // Check if exactly one face is detected and it's centered\n const isOneFace = detections.length === 1;\n const isCentered = isOneFace && isFaceCentered(detections[0]);\n\n setFaceDetected(isOneFace && isCentered);\n\n // If face is detected and centered, proceed with liveness check\n if (isOneFace && isCentered && livenessAttempts === 0) {\n startLivenessCheck();\n }\n } catch (error) {\n console.error('Face detection error:', error);\n }\n\n // Continue detection\n faceDetectionTimeoutRef.current = window.setTimeout(detectFaces, 100);\n };\n\n detectFaces();\n };\n\n // Initialize face-api.js models\n useEffect(() => {\n const loadModels = async () => {\n try {\n await Promise.all([\n faceapi.nets.tinyFaceDetector.loadFromUri('./models'),\n faceapi.nets.faceLandmark68Net.loadFromUri('./models'),\n ]);\n } catch (error) {\n console.error('Failed to load face-api.js models:', error);\n }\n };\n\n loadModels();\n\n return () => {\n // Clean up any pending timeouts\n if (faceDetectionTimeoutRef.current) {\n window.clearTimeout(faceDetectionTimeoutRef.current);\n }\n if (livenessCheckRef.current) {\n window.clearInterval(livenessCheckRef.current);\n }\n };\n }, []);\n\n // Start camera when component mounts\n useEffect(() => {\n const initializeCamera = async () => {\n try {\n const stream = await startCamera();\n streamRef.current = stream;\n\n if (videoRef.current) {\n videoRef.current.srcObject = stream;\n }\n\n dispatch({ type: 'PERMISSION_GRANTED' });\n startFaceDetection();\n } catch (error) {\n dispatch({ type: 'PERMISSION_DENIED' });\n }\n };\n\n initializeCamera();\n\n return () => {\n if (streamRef.current) {\n stopCamera(streamRef.current);\n }\n };\n }, [startCamera, stopCamera]);\n\n // Capture and process image\n const handleCapture = async () => {\n if (!canvasRef.current || !videoRef.current) return;\n\n try {\n dispatch({ type: 'START_CAPTURE' });\n\n // Get the canvas context\n const context = canvasRef.current.getContext('2d');\n if (!context) {\n dispatch({ type: 'SET_ERROR', message: 'Failed to get canvas context' });\n return;\n }\n\n // Set canvas dimensions to match video\n canvasRef.current.width = videoRef.current.videoWidth;\n canvasRef.current.height = videoRef.current.videoHeight;\n\n // Draw the video frame to the canvas\n context.drawImage(videoRef.current, 0, 0, canvasRef.current.width, canvasRef.current.height);\n\n // Convert canvas to blob\n const blob = await new Promise<Blob>((resolve, reject) => {\n canvasRef.current?.toBlob(\n b => {\n if (b) resolve(b);\n else reject(new Error('Failed to convert canvas to blob'));\n },\n 'image/jpeg',\n 0.95\n );\n });\n\n // Process recognition\n try {\n const result = await recognize(blob, accessToken);\n onSuccess({\n success: true,\n data: {\n faceId: result.payload.documentId,\n confidence: 1.0,\n },\n });\n dispatch({ type: 'UPLOAD_SUCCESS' });\n setTimeout(onClose, 1500);\n } catch (error) {\n dispatch({ type: 'SET_ERROR', message: 'Face recognition failed' });\n }\n } catch (error) {\n dispatch({ type: 'SET_ERROR', message: 'Failed to capture image' });\n }\n };\n\n // Handle retry\n const handleRetry = () => {\n dispatch({ type: 'RESET' });\n setLivenessAttempts(0);\n };\n\n return (\n <div style={styles.overlay}>\n <style>{keyframes}</style>\n <style>{mediaStyles}</style>\n <div style={styles.modal} className=\"modal\">\n <button type=\"button\" style={styles.closeButton} onClick={onClose}>\n ×\n </button>\n\n <video ref={videoRef} style={styles.video} autoPlay playsInline muted />\n\n <canvas ref={canvasRef} style={styles.canvas} />\n\n <div style={styles.guide} />\n\n {state.status === 'ready' && !faceDetected && (\n <div style={styles.message}>Centralize seu rosto no círculo</div>\n )}\n\n {state.status === 'capturing' && (\n <div style={styles.message}>Por favor, pisque os olhos</div>\n )}\n\n {state.status === 'uploading' && <div style={styles.spinner} />}\n\n {state.status === 'success' && <div style={styles.message}>Autenticado!</div>}\n\n {state.status === 'error' && <div style={styles.message}>{state.message}</div>}\n\n <div style={styles.controls}>\n {state.status === 'ready' && faceDetected && (\n <button type=\"button\" style={styles.button} onClick={handleCapture}>\n Capturar\n </button>\n )}\n\n {state.status === 'capturing' && (\n <button type=\"button\" style={styles.button} onClick={handleCapture}>\n Capturar\n </button>\n )}\n\n {state.status === 'error' && (\n <button type=\"button\" style={styles.button} onClick={handleRetry}>\n Tentar Novamente\n </button>\n )}\n </div>\n </div>\n </div>\n );\n}\n","import { useEffect, useRef, useState, useReducer } from 'react';\nimport * as faceapi from 'face-api.js';\nimport { registerPersonWithBiometric } from '../api';\n\n// Tipos\ninterface BiometricRegistrationResult {\n success: boolean;\n message: string;\n person_id?: string;\n user_id?: string;\n person_validated?: boolean;\n face_processing?: any;\n onboarding_link?: string;\n}\n\ninterface BiometricRegistrationModalProps {\n personData?: {\n name: string;\n birth_date: string;\n cpf: string;\n email: string;\n password: string;\n };\n applicationToken?: string;\n onClose: () => void;\n onSuccess?: (result: BiometricRegistrationResult) => void;\n onPhotosCaptured?: (photos: Blob[]) => void;\n onError: (error: string) => void;\n // Flag para controlar se deve fazer requisição real ao backend (sempre true em produção)\n useRealApi?: boolean;\n}\n\ntype LivenessStep = 'center' | 'up' | 'down' | 'left' | 'right' | 'complete';\n\ntype ModalState =\n | { status: 'instructions' }\n | {\n status: 'liveness';\n step: LivenessStep;\n capturedPhotos: Blob[];\n stepSequence: LivenessStep[];\n currentStepIndex: number;\n stepProgress: number;\n }\n | { status: 'camera-loading' } // câmera ainda inicializando\n | { status: 'processing' }\n | { status: 'success' }\n | { status: 'error'; message: string; retryable: boolean };\n\ntype ModalAction =\n | { type: 'START_LIVENESS'; stepSequence: LivenessStep[] }\n | { type: 'NEXT_STEP'; step: LivenessStep; photo?: Blob }\n | { type: 'UPDATE_PROGRESS'; progress: number }\n | { type: 'START_PROCESSING' }\n | { type: 'SUCCESS' }\n | { type: 'ERROR'; message: string; retryable?: boolean }\n | { type: 'CAMERA_LOADING' }\n | { type: 'RESET' };\n\nconst modalReducer = (state: ModalState, action: ModalAction): ModalState => {\n switch (action.type) {\n case 'START_LIVENESS':\n return {\n status: 'liveness',\n step: action.stepSequence[0],\n capturedPhotos: [],\n stepSequence: action.stepSequence,\n currentStepIndex: 0,\n stepProgress: 0,\n };\n case 'NEXT_STEP':\n if (state.status === 'liveness') {\n const newPhotos = action.photo\n ? [...state.capturedPhotos, action.photo]\n : state.capturedPhotos;\n\n return {\n status: 'liveness',\n step: action.step,\n capturedPhotos: newPhotos,\n stepSequence: state.stepSequence,\n currentStepIndex: state.currentStepIndex + 1,\n stepProgress: 0,\n };\n }\n return state;\n case 'UPDATE_PROGRESS':\n if (state.status === 'liveness') {\n return {\n ...state,\n stepProgress: action.progress,\n };\n }\n return state;\n case 'CAMERA_LOADING':\n return { status: 'camera-loading' };\n case 'START_PROCESSING':\n return { status: 'processing' };\n case 'SUCCESS':\n return { status: 'success' };\n case 'ERROR':\n return { status: 'error', message: action.message, retryable: action.retryable ?? true };\n case 'RESET':\n return initialState;\n default:\n return state;\n }\n};\n\nconst livenessInstructions: Record<LivenessStep, string> = {\n center: 'Posicione seu rosto no centro',\n up: 'Incline a cabeça para cima',\n down: 'Incline a cabeça para baixo',\n left: 'Vire o rosto para a esquerda',\n right: 'Vire o rosto para a direita',\n complete: 'Processando...',\n};\n\nconst generateRandomStepSequence = (): LivenessStep[] => {\n return ['center', 'up', 'down', 'left', 'right', 'complete'];\n};\n\nconst initialState: ModalState = (() => {\n const sequence = generateRandomStepSequence();\n return {\n status: 'liveness',\n step: sequence[0],\n capturedPhotos: [],\n stepSequence: sequence,\n currentStepIndex: 0,\n stepProgress: 0,\n };\n})();\n\n// Calibragem\nconst CALIBRATION = {\n PITCH_DEAD_ZONE: 8,\n YAW_DEAD_ZONE: 10,\n PITCH_OFFSET: 26,\n PITCH_UP_THRESHOLD: 8,\n PITCH_DOWN_THRESHOLD: -8,\n YAW_LEFT_THRESHOLD: 15,\n YAW_RIGHT_THRESHOLD: -15,\n CAPTURE_HOLD_TIME: 1500,\n DETECTION_INTERVAL: 200,\n};\n\nexport function BiometricRegistrationModal({\n personData,\n applicationToken,\n onClose,\n onSuccess,\n onPhotosCaptured,\n onError,\n}: BiometricRegistrationModalProps) {\n const videoRef = useRef<HTMLVideoElement>(null);\n const canvasRef = useRef<HTMLCanvasElement>(null);\n const streamRef = useRef<MediaStream | null>(null);\n const holdStartTimeRef = useRef<number | null>(null);\n const progressIntervalRef = useRef<number | null>(null);\n const isCapturingRef = useRef<boolean>(false);\n const currentStepRef = useRef<LivenessStep>('center');\n // Watchdog: se em 20s nenhuma detecção ocorreu, avisa o usuário\n const watchdogTimerRef = useRef<number | null>(null);\n // Flag para saber se o loop de detecção já foi iniciado\n const detectionStartedRef = useRef<boolean>(false);\n\n const captureStateRef = useRef<{\n stepSequence: LivenessStep[];\n currentStepIndex: number;\n capturedPhotos: Blob[];\n } | null>(null);\n\n const [state, dispatch] = useReducer(modalReducer, initialState);\n const [faceDetected, setFaceDetected] = useState(false);\n const [_faceDistance, setFaceDistance] = useState<'ok' | 'too-close' | 'too-far'>('ok');\n\n // Atualizar referência da etapa atual\n useEffect(() => {\n if (state.status === 'liveness') {\n const newStep = state.step;\n\n if (currentStepRef.current !== newStep) {\n currentStepRef.current = newStep;\n\n if (!isCapturingRef.current) {\n if (holdStartTimeRef.current) {\n holdStartTimeRef.current = null;\n }\n\n if (progressIntervalRef.current) {\n clearInterval(progressIntervalRef.current);\n progressIntervalRef.current = null;\n }\n\n dispatch({ type: 'UPDATE_PROGRESS', progress: 0 });\n }\n }\n\n captureStateRef.current = {\n stepSequence: state.stepSequence,\n currentStepIndex: state.currentStepIndex,\n capturedPhotos: state.capturedPhotos,\n };\n }\n }, [\n state.status,\n state.status === 'liveness' && state.step,\n state.status === 'liveness' && state.currentStepIndex,\n ]);\n\n // Calcular ângulos da cabeça\n const calculateHeadAngles = (landmarks: any) => {\n try {\n const points = landmarks.positions || landmarks._positions;\n\n if (!Array.isArray(points) || points.length < 68) {\n return null;\n }\n\n const getPoint = (index: number) => {\n const point = points[index];\n return {\n x: point._x !== undefined ? point._x : point.x,\n y: point._y !== undefined ? point._y : point.y,\n };\n };\n\n const noseTip = getPoint(30);\n const chin = getPoint(8);\n const leftEye = getPoint(36);\n const rightEye = getPoint(45);\n\n const eyeCenterX = (leftEye.x + rightEye.x) / 2;\n const noseOffsetX = noseTip.x - eyeCenterX;\n const eyeDistance = Math.abs(rightEye.x - leftEye.x);\n const yaw = (noseOffsetX / eyeDistance) * 90;\n\n const eyeCenterY = (leftEye.y + rightEye.y) / 2;\n const noseOffsetY = noseTip.y - eyeCenterY;\n const faceHeight = Math.abs(chin.y - eyeCenterY);\n const pitchRaw = -(noseOffsetY / faceHeight) * 90;\n const pitch = pitchRaw + CALIBRATION.PITCH_OFFSET;\n\n return {\n pitch: Math.round(pitch),\n yaw: Math.round(yaw),\n };\n } catch (error) {\n return null;\n }\n };\n\n // Verificar posição\n const checkPositionMatch = (pitch: number, yaw: number, step: LivenessStep): boolean => {\n switch (step) {\n case 'center':\n return (\n Math.abs(pitch) < CALIBRATION.PITCH_DEAD_ZONE && Math.abs(yaw) < CALIBRATION.YAW_DEAD_ZONE\n );\n case 'up':\n return pitch > CALIBRATION.PITCH_UP_THRESHOLD;\n case 'down':\n return pitch < CALIBRATION.PITCH_DOWN_THRESHOLD;\n case 'left':\n return yaw > CALIBRATION.YAW_LEFT_THRESHOLD;\n case 'right':\n return yaw < CALIBRATION.YAW_RIGHT_THRESHOLD;\n default:\n return false;\n }\n };\n\n // Gerenciar hold e captura\n const manageHoldAndCapture = (positionCorrect: boolean) => {\n if (isCapturingRef.current || currentStepRef.current === 'complete') {\n return;\n }\n\n const now = Date.now();\n\n if (positionCorrect) {\n if (!holdStartTimeRef.current) {\n holdStartTimeRef.current = now;\n\n if (progressIntervalRef.current) {\n clearInterval(progressIntervalRef.current);\n }\n\n progressIntervalRef.current = window.setInterval(() => {\n if (!holdStartTimeRef.current) return;\n\n const elapsed = Date.now() - holdStartTimeRef.current;\n const progress = Math.min((elapsed / CALIBRATION.CAPTURE_HOLD_TIME) * 100, 100);\n\n dispatch({ type: 'UPDATE_PROGRESS', progress });\n\n if (progress >= 100) {\n clearInterval(progressIntervalRef.current!);\n progressIntervalRef.current = null;\n capturePhoto();\n }\n }, 50);\n }\n } else {\n if (holdStartTimeRef.current) {\n holdStartTimeRef.current = null;\n\n if (progressIntervalRef.current) {\n clearInterval(progressIntervalRef.current);\n progressIntervalRef.current = null;\n }\n\n dispatch({ type: 'UPDATE_PROGRESS', progress: 0 });\n }\n }\n };\n\n // Carregar modelos com timeout para não bloquear indefinidamente\n useEffect(() => {\n const loadModels = async () => {\n try {\n const MODEL_URL = 'https://cdn.jsdelivr.net/npm/@vladmandic/face-api/model';\n const timeout = new Promise<never>((_, reject) =>\n setTimeout(() => reject(new Error('Timeout ao carregar modelos')), 10000)\n );\n await Promise.race([\n Promise.all([\n faceapi.nets.tinyFaceDetector.loadFromUri(MODEL_URL),\n faceapi.nets.faceLandmark68Net.loadFromUri(MODEL_URL),\n ]),\n timeout,\n ]);\n } catch (error) {\n // Se falhar ou der timeout, despacha erro com retry disponível\n dispatch({\n type: 'ERROR',\n message: 'Não foi possível carregar os modelos de IA. Verifique sua conexão e tente novamente.',\n retryable: true,\n });\n }\n };\n\n loadModels();\n }, []);\n\n // ── Safety-net de desmontagem ───────────────────────────────────────────────\n // Garante que câmera e timers sejam liberados SEMPRE que o componente\n // for removido do DOM (ex: usuário clicou no X enquanto getUserMedia estava\n // em andamento ou durante qualquer outro estado).\n useEffect(() => {\n return () => {\n // Para todos os tracks do stream imediatamente\n if (streamRef.current) {\n streamRef.current.getTracks().forEach(track => {\n track.stop();\n });\n streamRef.current = null;\n }\n // Limpa o srcObject do vídeo para liberar o indicador da câmera no browser\n if (videoRef.current) {\n videoRef.current.srcObject = null;\n }\n // Cancela watchdog e progress intervals\n if (watchdogTimerRef.current) {\n clearTimeout(watchdogTimerRef.current);\n watchdogTimerRef.current = null;\n }\n if (progressIntervalRef.current) {\n clearInterval(progressIntervalRef.current);\n progressIntervalRef.current = null;\n }\n };\n }, []); // [] = só roda o return (cleanup) na desmontagem\n\n // Inicializar câmera\n useEffect(() => {\n let mounted = true;\n\n const initCamera = async () => {\n if (state.status !== 'liveness' || streamRef.current) return;\n\n // Reseta flag de detecção ao (re)iniciar\n detectionStartedRef.current = false;\n\n // Limpa watchdog anterior se existir\n if (watchdogTimerRef.current) {\n clearTimeout(watchdogTimerRef.current);\n watchdogTimerRef.current = null;\n }\n\n // Inicia watchdog global de 20s: se o usuário não avançou nenhum step,\n // exibe mensagem de erro acionável (sem erros silenciosos)\n watchdogTimerRef.current = window.setTimeout(() => {\n if (!mounted) return;\n // Só dispara se ainda está em liveness e nenhuma captura ocorreu\n if (detectionStartedRef.current === false) {\n dispatch({\n type: 'ERROR',\n message: 'A câmera não respondeu a tempo. Verifique se ela está disponível e tente novamente.',\n retryable: true,\n });\n }\n }, 20000);\n\n try {\n const stream = await navigator.mediaDevices.getUserMedia({\n video: {\n width: { ideal: 640 },\n height: { ideal: 480 },\n facingMode: 'user',\n },\n });\n\n if (!mounted) {\n stream.getTracks().forEach(track => track.stop());\n return;\n }\n\n const waitForVideo = async () => {\n let attempts = 0;\n while (!videoRef.current && attempts < 50) {\n await new Promise(resolve => setTimeout(resolve, 100));\n attempts++;\n }\n return videoRef.current;\n };\n\n const video = await waitForVideo();\n\n if (!video) {\n throw new Error('Elemento de vídeo não encontrado');\n }\n\n video.srcObject = stream;\n streamRef.current = stream;\n\n await video.play();\n } catch (error) {\n if (!mounted) return;\n\n // Limpa watchdog pois já vamos mostrar o erro\n if (watchdogTimerRef.current) {\n clearTimeout(watchdogTimerRef.current);\n watchdogTimerRef.current = null;\n }\n\n let errorMessage = error instanceof Error ? error.message : 'Erro ao acessar câmera';\n\n // Erros de permissão têm mensagem mais amigável\n if (error instanceof DOMException) {\n if (error.name === 'NotAllowedError' || error.name === 'PermissionDeniedError') {\n errorMessage = 'Permissão de câmera negada. Permita o acesso à câmera nas configurações do navegador e tente novamente.';\n } else if (error.name === 'NotFoundError' || error.name === 'DevicesNotFoundError') {\n errorMessage = 'Nenhuma câmera encontrada. Conecte uma câmera e tente novamente.';\n } else if (error.name === 'NotReadableError') {\n errorMessage = 'A câmera está sendo usada por outro programa. Feche-o e tente novamente.';\n }\n }\n\n dispatch({ type: 'ERROR', message: errorMessage, retryable: true });\n }\n };\n\n initCamera();\n\n return () => {\n mounted = false;\n\n if (watchdogTimerRef.current) {\n clearTimeout(watchdogTimerRef.current);\n watchdogTimerRef.current = null;\n }\n\n if (streamRef.current) {\n streamRef.current.getTracks().forEach(track => track.stop());\n streamRef.current = null;\n }\n\n if (progressIntervalRef.current) {\n clearInterval(progressIntervalRef.current);\n progressIntervalRef.current = null;\n }\n\n holdStartTimeRef.current = null;\n };\n }, [state.status]);\n\n // Loop de detecção\n useEffect(() => {\n if (state.status !== 'liveness' || !videoRef.current) {\n return;\n }\n\n let isRunning = true;\n let lastDetectionTime = 0;\n\n const detectFace = async () => {\n if (!isRunning || state.status !== 'liveness') {\n return;\n }\n\n if (isCapturingRef.current || currentStepRef.current === 'complete') {\n requestAnimationFrame(detectFace);\n return;\n }\n\n const now = Date.now();\n\n if (now - lastDetectionTime < CALIBRATION.DETECTION_INTERVAL) {\n requestAnimationFrame(detectFace);\n return;\n }\n\n lastDetectionTime = now;\n\n try {\n const detection = await faceapi\n .detectSingleFace(\n videoRef.current!,\n new faceapi.TinyFaceDetectorOptions({\n inputSize: 160,\n scoreThreshold: 0.4,\n })\n )\n .withFaceLandmarks();\n\n if (!isRunning) return;\n\n const hasFace = !!detection;\n setFaceDetected(hasFace);\n\n // Verificar distância do rosto\n if (hasFace && detection.detection) {\n const box = detection.detection.box;\n const faceWidth = box.width;\n\n // Calcular se está muito perto ou muito longe baseado na largura do rosto\n if (faceWidth > 250) {\n setFaceDistance('too-close');\n } else if (faceWidth < 120) {\n setFaceDistance('too-far');\n } else {\n setFaceDistance('ok');\n }\n }\n\n if (hasFace && detection.landmarks) {\n const angles = calculateHeadAngles(detection.landmarks);\n\n if (angles) {\n const positionCorrect = checkPositionMatch(\n angles.pitch,\n angles.yaw,\n currentStepRef.current\n );\n\n manageHoldAndCapture(positionCorrect);\n }\n } else {\n if (holdStartTimeRef.current) {\n holdStartTimeRef.current = null;\n\n if (progressIntervalRef.current) {\n clearInterval(progressIntervalRef.current);\n progressIntervalRef.current = null;\n }\n\n dispatch({ type: 'UPDATE_PROGRESS', progress: 0 });\n }\n }\n } catch (error) {\n // Silently handle detection errors\n }\n\n if (isRunning && state.status === 'liveness') {\n requestAnimationFrame(detectFace);\n }\n };\n\n // Função que inicia o loop de detecção assim que o vídeo estiver pronto\n const startDetection = () => {\n if (!isRunning || !videoRef.current) return;\n detectionStartedRef.current = true;\n\n // Cancela watchdog: câmera respondeu com sucesso\n if (watchdogTimerRef.current) {\n clearTimeout(watchdogTimerRef.current);\n watchdogTimerRef.current = null;\n }\n\n detectFace();\n };\n\n // Estratégia dupla:\n // 1. Se o vídeo já está pronto (readyState >= 2), inicia imediatamente\n // 2. Se não, escuta o evento 'canplay' para iniciar assim que o vídeo carregar\n // Isso evita o problema de o setTimeout de 1s disparar antes do vídeo estar pronto\n const video = videoRef.current;\n\n if (video && video.readyState >= 2) {\n // Vídeo já pronto, pequeno delay para estabilidade do frame\n const startTimer = setTimeout(startDetection, 200);\n return () => {\n isRunning = false;\n clearTimeout(startTimer);\n if (progressIntervalRef.current) {\n clearInterval(progressIntervalRef.current);\n progressIntervalRef.current = null;\n }\n holdStartTimeRef.current = null;\n };\n } else {\n // Vídeo ainda não pronto: aguarda evento canplay\n const onCanPlay = () => {\n if (isRunning) startDetection();\n };\n\n // Fallback: caso canplay nunca dispare (bug de browser),\n // polling a cada 300ms por até 15s\n let pollCount = 0;\n const MAX_POLLS = 50; // 50 × 300ms = 15s\n const pollTimer = window.setInterval(() => {\n pollCount++;\n if (!isRunning) {\n clearInterval(pollTimer);\n return;\n }\n if (videoRef.current && videoRef.current.readyState >= 2) {\n clearInterval(pollTimer);\n videoRef.current.removeEventListener('canplay', onCanPlay);\n startDetection();\n } else if (pollCount >= MAX_POLLS) {\n clearInterval(pollTimer);\n // Não encontrou câmera pronta em 15s, watchdog vai tratar\n }\n }, 300);\n\n video?.addEventListener('canplay', onCanPlay, { once: true });\n\n return () => {\n isRunning = false;\n clearInterval(pollTimer);\n video?.removeEventListener('canplay', onCanPlay);\n if (progressIntervalRef.current) {\n clearInterval(progressIntervalRef.current);\n progressIntervalRef.current = null;\n }\n holdStartTimeRef.current = null;\n };\n }\n }, [state.status]);\n\n // Capturar foto\n const capturePhoto = async () => {\n if (!videoRef.current || !canvasRef.current || isCapturingRef.current) {\n return;\n }\n\n isCapturingRef.current = true;\n holdStartTimeRef.current = null;\n\n const capturedState = captureStateRef.current;\n\n if (!capturedState) {\n isCapturingRef.current = false;\n return;\n }\n\n const canvas = canvasRef.current;\n const video = videoRef.current;\n const ctx = canvas.getContext('2d');\n\n if (!ctx) {\n isCapturingRef.current = false;\n return;\n }\n\n canvas.width = video.videoWidth;\n canvas.height = video.videoHeight;\n\n ctx.scale(-1, 1);\n ctx.drawImage(video, -canvas.width, 0, canvas.width, canvas.height);\n\n canvas.toBlob(\n blob => {\n if (!blob) {\n isCapturingRef.current = false;\n return;\n }\n\n const nextStepIndex = capturedState.currentStepIndex + 1;\n const nextStep = capturedState.stepSequence[nextStepIndex];\n\n if (nextStep === 'complete') {\n const allPhotos = [...capturedState.capturedPhotos, blob];\n dispatch({ type: 'START_PROCESSING' });\n processRegistration(allPhotos);\n } else {\n setTimeout(() => {\n isCapturingRef.current = false;\n dispatch({ type: 'NEXT_STEP', step: nextStep, photo: blob });\n }, 100);\n }\n },\n 'image/jpeg',\n 0.85\n );\n };\n\n // Processar registro\n const processRegistration = async (photos: Blob[]) => {\n try {\n if (onPhotosCaptured) {\n dispatch({ type: 'SUCCESS' });\n onPhotosCaptured(photos);\n return;\n }\n\n if (!personData || !applicationToken) {\n throw new Error('Dados de registro não fornecidos');\n }\n\n // 🚀 PRODUÇÃO: Fazer requisição real ao backend\n console.log('🚀 Fazendo requisição ao backend...');\n\n const result = await registerPersonWithBiometric(personData, photos, applicationToken);\n\n // Adaptar o resultado da API para o formato esperado pelo componente\n const adaptedResult: BiometricRegistrationResult = {\n success: result.success,\n message: result.message,\n person_id: result.person_id,\n user_id: result.user_id,\n person_validated: result.person_validated,\n face_processing: result.face_processing,\n onboarding_link: result.onboarding_link,\n };\n\n dispatch({ type: 'SUCCESS' });\n if (onSuccess) {\n onSuccess(adaptedResult);\n }\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : 'Erro desconhecido';\n dispatch({ type: 'ERROR', message: errorMessage });\n onError(errorMessage);\n }\n };\n\n // Renderizar liveness\n const renderLivenessDetection = () => {\n if (state.status !== 'liveness') return null;\n\n const progress = `${state.currentStepIndex + 1}/${state.stepSequence.length - 1}`;\n const stepProgress = state.stepProgress;\n\n return (\n <div className=\"liveness-container\">\n {/* Progress Indicator */}\n <div className=\"progress-indicator\">\n <span className=\"progress-text\">{progress}</span>\n </div>\n\n {/* Video Circle */}\n <div className=\"video-wrapper\">\n <div\n className={`video-circle ${faceDetected ? 'face-detected' : ''} ${stepProgress > 0 ? 'capturing' : ''\n }`}\n >\n <video ref={videoRef} className=\"video-feed\" autoPlay muted playsInline />\n\n {/* Face Detected Badge */}\n {faceDetected && (\n <div className=\"face-badge\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 20 20\" fill=\"none\">\n <path\n d=\"M5 10 L8 13 L15 6\"\n stroke=\"currentColor\"\n strokeWidth=\"2.5\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n <span>Rosto detectado</span>\n </div>\n )}\n </div>\n </div>\n\n {/* Instruction Text */}\n <div className=\"instruction-box\">\n <p className=\"instruction-text\">{livenessInstructions[state.step]}</p>\n {stepProgress > 0 && (\n <div className=\"progress-bar\">\n <div className=\"progress-fill\" style={{ width: `${stepProgress}%` }} />\n </div>\n )}\n </div>\n\n {/* Branding */}\n <div className=\"branding-small\">\n <img src=\"/logo-icone.png\" alt=\"NeoFaceId\" className=\"brand-logo-small\" />\n <span>NeoFaceId by</span>\n <span className=\"brand-name\">OCTA</span>\n </div>\n </div>\n );\n };\n\n // Renderizar processamento\n const renderProcessing = () => (\n <div className=\"processing-container\">\n <div className=\"spinner-wrapper\">\n <div className=\"spinner\" />\n <div className=\"spinner-ring\" />\n </div>\n <h3>Processando biometria</h3>\n <p>Analisando suas capturas faciais...</p>\n\n <div\n className=\"branding\"\n style={{\n marginTop: '40px',\n paddingTop: '24px',\n borderTop: '1px solid rgba(255, 255, 255, 0.1)',\n }}\n >\n <span>NeoFaceId by</span>\n <span className=\"brand-name\">OCTA</span>\n </div>\n </div>\n );\n\n // Renderizar erro\n const renderError = () => {\n if (state.status !== 'error') return null;\n\n return (\n <div className=\"result-container error\">\n <div className=\"result-icon error-icon\">\n <svg width=\"60\" height=\"60\" viewBox=\"0 0 60 60\" fill=\"none\">\n <circle cx=\"30\" cy=\"30\" r=\"28\" stroke=\"currentColor\" strokeWidth=\"3\" />\n <path\n d=\"M20 20 L40 40 M40 20 L20 40\"\n stroke=\"currentColor\"\n strokeWidth=\"3\"\n strokeLinecap=\"round\"\n />\n </svg>\n </div>\n <h3>Não foi possível processar</h3>\n <p>{state.message}</p>\n <div className=\"button-group\">\n {state.retryable && (\n <button className=\"secondary-button\" onClick={() => dispatch({ type: 'RESET' })}>\n Tentar Novamente\n </button>\n )}\n <button className=\"ghost-button\" onClick={onClose}>\n Cancelar\n </button>\n </div>\n </div>\n );\n };\n\n // Renderizar carregamento da câmera\n const renderCameraLoading = () => (\n <div className=\"processing-container\">\n <div className=\"spinner-wrapper\">\n <div className=\"spinner\" />\n <div className=\"spinner-ring\" />\n </div>\n <h3>Inicializando câmera</h3>\n <p>Aguarde enquanto preparamos a câmera...</p>\n <div\n className=\"branding\"\n style={{\n marginTop: '40px',\n paddingTop: '24px',\n borderTop: '1px solid rgba(255, 255, 255, 0.1)',\n }}\n >\n <span>NeoFaceId by</span>\n <span className=\"brand-name\">OCTA</span>\n </div>\n </div>\n );\n\n // Renderizar sucesso\n const renderSuccess = () => (\n <div className=\"result-container success\">\n <div className=\"result-icon success-icon\">\n <svg width=\"60\" height=\"60\" viewBox=\"0 0 60 60\" fill=\"none\">\n <circle cx=\"30\" cy=\"30\" r=\"28\" stroke=\"currentColor\" strokeWidth=\"3\" />\n <path\n d=\"M15 30 L25 40 L45 20\"\n stroke=\"currentColor\"\n strokeWidth=\"3\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n </div>\n <h3>{onPhotosCaptured ? 'Captura concluída!' : 'Verificação concluída!'}</h3>\n <p>{onPhotosCaptured ? 'Suas capturas faciais foram realizadas' : 'Sua biometria foi registrada com sucesso'}</p>\n <button className=\"primary-button\" onClick={onClose}>\n Continuar\n </button>\n </div>\n );\n\n return (\n <>\n <style>{`\n @keyframes fadeIn {\n from { opacity: 0; transform: scale(0.95); }\n to { opacity: 1; transform: scale(1); }\n }\n\n @keyframes slideUp {\n from { opacity: 0; transform: translateY(20px); }\n to { opacity: 1; transform: translateY(0); }\n }\n\n @keyframes slideDown {\n from { opacity: 0; transform: translateY(-10px); }\n to { opacity: 1; transform: translateY(0); }\n }\n\n @keyframes spin {\n from { transform: rotate(0deg); }\n to { transform: rotate(360deg); }\n }\n\n @keyframes pulseRing {\n 0%, 100% { \n box-shadow: 0 0 0 4px rgba(16, 185, 129, 0.8), 0 0 40px rgba(16, 185, 129, 0.4);\n }\n 50% { \n box-shadow: 0 0 0 6px rgba(16, 185, 129, 0.9), 0 0 50px rgba(16, 185, 129, 0.5);\n }\n }\n\n @keyframes checkmark {\n 0% {\n stroke-dashoffset: 100;\n }\n 100% {\n stroke-dashoffset: 0;\n }\n }\n\n @keyframes scaleIn {\n 0% {\n transform: scale(0);\n opacity: 0;\n }\n 50% {\n transform: scale(1.1);\n }\n 100% {\n transform: scale(1);\n opacity: 1;\n }\n }\n\n @keyframes scanRing {\n 0%, 100% { transform: scale(1); opacity: 0.3; }\n 50% { transform: scale(1.1); opacity: 0.6; }\n }\n\n @keyframes cornerBlink {\n 0%, 100% { opacity: 0.3; }\n 50% { opacity: 1; }\n }\n\n .modal-overlay {\n position: fixed;\n inset: 0;\n background: rgba(0, 0, 0, 0.92);\n backdrop-filter: blur(8px);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 9999;\n padding: 20px;\n }\n\n .modal-content {\n background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);\n border-radius: 24px;\n width: 100%;\n max-width: 420px;\n max-height: 90vh;\n overflow: hidden;\n box-shadow: 0 24px 48px rgba(0, 0, 0, 0.4), 0 0 0 1px rgba(255, 255, 255, 0.1);\n animation: fadeIn 0.3s ease-out;\n position: relative;\n }\n\n .close-button {\n position: absolute;\n top: 20px;\n right: 20px;\n width: 32px;\n height: 32px;\n background: transparent;\n border: none;\n color: rgba(255, 255, 255, 0.6);\n font-size: 28px;\n font-weight: 300;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: all 0.2s;\n z-index: 10;\n line-height: 1;\n }\n\n .close-button:hover {\n color: rgba(255, 255, 255, 0.9);\n transform: rotate(90deg);\n }\n\n /* Instructions Screen */\n .instructions-container {\n padding: 60px 40px 40px;\n color: white;\n text-align: center;\n animation: slideUp 0.4s ease-out;\n }\n\n .instructions-header {\n margin-bottom: 40px;\n }\n\n .icon-wrapper {\n display: inline-block;\n color: #7c3aed;\n margin-bottom: 24px;\n }\n\n .icon-wrapper .scan-ring {\n animation: scanRing 2s ease-in-out infinite;\n }\n\n .icon-wrapper .corner {\n animation: cornerBlink 2s ease-in-out infinite;\n }\n\n .icon-wrapper .corner.tl { animation-delay: 0s; }\n .icon-wrapper .corner.tr { animation-delay: 0.5s; }\n .icon-wrapper .corner.bl { animation-delay: 1s; }\n .icon-wrapper .corner.br { animation-delay: 1.5s; }\n\n .instructions-header h2 {\n font-size: 28px;\n font-weight: 700;\n margin: 0 0 12px 0;\n background: linear-gradient(135deg, #7c3aed 0%, #a78bfa 100%);\n -webkit-background-clip: text;\n -webkit-text-fill-color: transparent;\n background-clip: text;\n }\n\n .subtitle {\n font-size: 15px;\n color: rgba(255, 255, 255, 0.7);\n margin: 0;\n line-height: 1.5;\n }\n\n .requirements-list {\n text-align: left;\n margin: 0 auto 40px;\n max-width: 360px;\n }\n\n .requirement-item {\n display: flex;\n align-items: center;\n gap: 16px;\n padding: 14px 0;\n font-size: 15px;\n color: rgba(255, 255, 255, 0.9);\n border-bottom: 1px solid rgba(255, 255, 255, 0.05);\n }\n\n .requirement-item:last-child {\n border-bottom: none;\n }\n\n .req-icon {\n width: 32px;\n height: 32px;\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n }\n\n .req-icon.success {\n background: rgba(16, 185, 129, 0.15);\n color: #10b981;\n border: 1.5px solid rgba(16, 185, 129, 0.3);\n }\n\n .primary-button {\n width: 100%;\n padding: 16px 32px;\n border-radius: 12px;\n border: none;\n background: linear-gradient(135deg, #7c3aed 0%, #6d28d9 100%);\n color: white;\n font-size: 16px;\n font-weight: 600;\n cursor: pointer;\n transition: all 0.3s;\n box-shadow: 0 4px 16px rgba(124, 58, 237, 0.3);\n }\n\n .primary-button:hover {\n transform: translateY(-2px);\n box-shadow: 0 8px 24px rgba(124, 58, 237, 0.4);\n }\n\n .primary-button:active {\n transform: translateY(0);\n }\n\n .branding {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n margin-top: 32px;\n padding-top: 24px;\n border-top: 1px solid rgba(255, 255, 255, 0.1);\n font-size: 13px;\n color: rgba(255, 255, 255, 0.5);\n }\n\n .brand-logo {\n height: 20px;\n width: auto;\n }\n\n .brand-name {\n font-weight: 600;\n font-family: 'SF Pro Display', -apple-system, system-ui, sans-serif;\n color: #ffffff;\n letter-spacing: 0.3px;\n }\n\n /* Liveness Detection */\n .liveness-container {\n height: 100%;\n min-height: 520px;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 70px 20px 80px;\n position: relative;\n animation: fadeIn 0.3s ease-out;\n }\n\n .progress-indicator {\n position: absolute;\n top: 20px;\n left: 50%;\n transform: translateX(-50%);\n background: rgba(255, 255, 255, 0.08);\n backdrop-filter: blur(12px);\n border-radius: 20px;\n padding: 8px 20px;\n border: 1px solid rgba(255, 255, 255, 0.12);\n z-index: 5;\n }\n\n .progress-text {\n font-size: 14px;\n font-weight: 600;\n color: white;\n }\n\n .video-wrapper {\n position: relative;\n margin: 20px 0;\n }\n\n .video-circle {\n width: 320px;\n height: 400px;\n border-radius: 50%;\n overflow: hidden;\n position: relative;\n transition: all 0.3s ease;\n box-shadow: 0 0 0 4px rgba(124, 58, 237, 0.3);\n }\n\n .video-circle.face-detected {\n box-shadow: 0 0 0 4px rgba(16, 185, 129, 0.5), 0 0 40px rgba(16, 185, 129, 0.3);\n }\n\n .video-circle.capturing {\n box-shadow: 0 0 0 4px rgba(16, 185, 129, 0.9), 0 0 40px rgba(16, 185, 129, 0.5);\n transition: box-shadow 0.3s ease;\n }\n\n .video-feed {\n width: 100%;\n height: 100%;\n object-fit: cover;\n transform: scaleX(-1);\n }\n\n .face-badge {\n position: absolute;\n top: 20px;\n left: 50%;\n transform: translateX(-50%);\n background: rgba(16, 185, 129, 0.95);\n backdrop-filter: blur(10px);\n border-radius: 20px;\n padding: 8px 16px;\n display: flex;\n align-items: center;\n gap: 8px;\n color: white;\n font-size: 13px;\n font-weight: 600;\n border: 1px solid rgba(255, 255, 255, 0.3);\n box-shadow: 0 4px 12px rgba(16, 185, 129, 0.3);\n animation: slideDown 0.3s ease-out;\n }\n\n .face-badge.warning {\n background: rgba(251, 191, 36, 0.95);\n border: 1px solid rgba(255, 255, 255, 0.3);\n box-shadow: 0 4px 12px rgba(251, 191, 36, 0.3);\n color: #78350f;\n }\n\n .face-badge.error {\n background: rgba(239, 68, 68, 0.95);\n border: 1px solid rgba(255, 255, 255, 0.3);\n box-shadow: 0 4px 12px rgba(239, 68, 68, 0.3);\n color: white;\n }\n\n .instruction-box {\n background: rgba(255, 255, 255, 0.08);\n backdrop-filter: blur(12px);\n border-radius: 16px;\n padding: 16px 24px;\n margin-top: 20px;\n border: 1px solid rgba(255, 255, 255, 0.12);\n max-width: 320px;\n animation: slideUp 0.3s ease-out;\n }\n\n .instruction-text {\n font-size: 18px;\n font-weight: 600;\n color: white;\n margin: 0 0 12px 0;\n text-align: center;\n }\n\n .progress-bar {\n height: 4px;\n background: rgba(255, 255, 255, 0.2);\n border-radius: 2px;\n overflow: hidden;\n }\n\n .progress-fill {\n height: 100%;\n background: linear-gradient(90deg, #7c3aed 0%, #10b981 100%);\n border-radius: 2px;\n transition: width 0.05s linear;\n }\n\n .branding-small {\n position: absolute;\n bottom: 24px;\n left: 50%;\n transform: translateX(-50%);\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 5px;\n font-size: 12px;\n color: rgba(255, 255, 255, 0.5);\n }\n\n .brand-logo-small {\n height: 18px;\n width: 18px;\n }\n\n /* Processing */\n .processing-container {\n padding: 80px 40px;\n text-align: center;\n color: white;\n animation: fadeIn 0.3s ease-out;\n }\n\n .spinner-wrapper {\n position: relative;\n width: 80px;\n height: 80px;\n margin: 0 auto 32px;\n }\n\n .spinner {\n position: absolute;\n inset: 0;\n border: 4px solid rgba(124, 58, 237, 0.2);\n border-top-color: #7c3aed;\n border-radius: 50%;\n animation: spin 1s linear infinite;\n }\n\n .spinner-ring {\n position: absolute;\n inset: -10px;\n border: 2px solid rgba(124, 58, 237, 0.1);\n border-radius: 50%;\n animation: pulseSpinner 2s ease-in-out infinite;\n }\n\n @keyframes pulseSpinner {\n 0%, 100% { transform: scale(1); opacity: 1; }\n 50% { transform: scale(1.1); opacity: 0.5; }\n }\n\n .processing-container h3 {\n font-size: 24px;\n font-weight: 700;\n margin: 0 0 12px 0;\n }\n\n .processing-container p {\n font-size: 15px;\n color: rgba(255, 255, 255, 0.7);\n margin: 0;\n }\n\n /* Result Screens */\n .result-container {\n padding: 80px 40px 40px;\n text-align: center;\n color: white;\n animation: slideUp 0.4s ease-out;\n }\n\n .result-icon {\n display: inline-flex;\n margin-bottom: 24px;\n }\n\n .success-icon {\n color: #10b981;\n animation: scaleIn 0.6s ease-out;\n }\n\n .success-icon svg circle {\n animation: scaleIn 0.5s ease-out;\n }\n\n .success-icon svg path {\n stroke-dasharray: 100;\n stroke-dashoffset: 100;\n animation: checkmark 0.6s ease-out 0.3s forwards;\n }\n\n .error-icon {\n color: #ef4444;\n animation: fadeIn 0.5s ease-out;\n }\n\n .result-container h3 {\n font-size: 26px;\n font-weight: 700;\n margin: 0 0 12px 0;\n }\n\n .result-container p {\n font-size: 15px;\n color: rgba(255, 255, 255, 0.7);\n margin: 0 0 32px 0;\n line-height: 1.5;\n }\n\n .button-group {\n display: flex;\n gap: 12px;\n margin-top: 32px;\n }\n\n .secondary-button {\n flex: 1;\n padding: 14px 24px;\n border-radius: 12px;\n border: 1px solid rgba(255, 255, 255, 0.2);\n background: rgba(255, 255, 255, 0.1);\n color: white;\n font-size: 15px;\n font-weight: 600;\n cursor: pointer;\n transition: all 0.2s;\n }\n\n .secondary-button:hover {\n background: rgba(255, 255, 255, 0.15);\n border-color: rgba(255, 255, 255, 0.3);\n }\n\n .ghost-button {\n flex: 1;\n padding: 14px 24px;\n border-radius: 12px;\n border: none;\n background: transparent;\n color: rgba(255, 255, 255, 0.7);\n font-size: 15px;\n font-weight: 600;\n cursor: pointer;\n transition: all 0.2s;\n }\n\n .ghost-button:hover {\n color: white;\n background: rgba(255, 255, 255, 0.05);\n }\n\n /* Canvas */\n canvas {\n display: none;\n }\n\n /* Responsive */\n @media (max-width: 640px) {\n .modal-content {\n max-width: 100%;\n border-radius: 0;\n max-height: 100vh;\n }\n\n .instructions-container {\n padding: 50px 24px 24px;\n }\n\n .instructions-header h2 {\n font-size: 24px;\n }\n\n .video-circle {\n width: 280px;\n height: 350px;\n }\n\n .liveness-container {\n min-height: 100vh;\n }\n }\n `}</style>\n\n <div className=\"modal-overlay\">\n <div className=\"modal-content\">\n <button className=\"close-button\" onClick={onClose}>\n ×\n </button>\n\n {state.status === 'liveness' && renderLivenessDetection()}\n {state.status === 'camera-loading' && renderCameraLoading()}\n {state.status === 'processing' && renderProcessing()}\n {state.status === 'error' && renderError()}\n {state.status === 'success' && renderSuccess()}\n </div>\n </div>\n\n <canvas ref={canvasRef} />\n </>\n );\n}\n","import { NeoFaceError, ErrorType } from './errors';\n\nexport interface BiometricCaptureOptions {\n mode: 'face' | 'hand' | 'auto';\n onSuccess: (imageData: string, detectedType: 'face' | 'hand') => void;\n onError: (error: NeoFaceError) => void;\n onCancel?: () => void;\n countdown?: number;\n title?: string;\n subtitle?: string;\n}\n\nexport class BiometricCaptureModal {\n private modal: HTMLElement | null = null;\n private video: HTMLVideoElement | null = null;\n private canvas: HTMLCanvasElement | null = null;\n private stream: MediaStream | null = null;\n private options: BiometricCaptureOptions;\n private countdownInterval: number | null = null;\n private isCapturing = false;\n\n constructor(options: BiometricCaptureOptions) {\n this.options = options;\n }\n\n /**\n * Abre o modal de captura biométrica\n */\n public async open(): Promise<void> {\n try {\n await this.createModal();\n await this.initializeCamera();\n this.startCountdown();\n } catch (error) {\n this.options.onError(\n new NeoFaceError(\n 'Erro ao inicializar câmera: ' + (error as Error).message,\n ErrorType.CAMERA_ERROR\n )\n );\n }\n }\n\n /**\n * Fecha o modal e limpa recursos\n */\n public close(): void {\n this.cleanup();\n if (this.modal) {\n document.body.removeChild(this.modal);\n this.modal = null;\n }\n }\n\n /**\n * Cria a estrutura HTML do modal\n */\n private async createModal(): Promise<void> {\n this.modal = document.createElement('div');\n this.modal.className = 'neofaceid-biometric-modal';\n \n const title = this.options.title || this.getDefaultTitle();\n const subtitle = this.options.subtitle || this.getDefaultSubtitle();\n\n this.modal.innerHTML = `\n <div class=\"neofaceid-modal-overlay\">\n <div class=\"neofaceid-modal-content\">\n <div class=\"neofaceid-modal-header\">\n <h2>${title}</h2>\n <button class=\"neofaceid-close-btn\" type=\"button\">&times;</button>\n </div>\n <div class=\"neofaceid-modal-body\">\n <p class=\"neofaceid-subtitle\">${subtitle}</p>\n <div class=\"neofaceid-camera-container\">\n <video class=\"neofaceid-video\" autoplay muted playsinline></video>\n <canvas class=\"neofaceid-canvas\" style=\"display: none;\"></canvas>\n <div class=\"neofaceid-overlay\">\n <div class=\"neofaceid-frame\"></div>\n <div class=\"neofaceid-countdown\">\n <span class=\"neofaceid-countdown-number\">${this.options.countdown || 3}</span>\n </div>\n </div>\n </div>\n <div class=\"neofaceid-status\">\n <p class=\"neofaceid-status-text\">Posicione-se na frente da câmera</p>\n </div>\n </div>\n <div class=\"neofaceid-modal-footer\">\n <button class=\"neofaceid-cancel-btn\" type=\"button\">Cancelar</button>\n <button class=\"neofaceid-capture-btn\" type=\"button\" disabled>Capturar</button>\n </div>\n </div>\n </div>\n `;\n\n // Adiciona estilos CSS\n this.addStyles();\n\n // Adiciona event listeners\n this.addEventListeners();\n\n document.body.appendChild(this.modal);\n }\n\n /**\n * Inicializa a câmera\n */\n private async initializeCamera(): Promise<void> {\n if (!this.modal) throw new Error('Modal não inicializado');\n\n this.video = this.modal.querySelector('.neofaceid-video') as HTMLVideoElement;\n this.canvas = this.modal.querySelector('.neofaceid-canvas') as HTMLCanvasElement;\n\n if (!this.video || !this.canvas) {\n throw new Error('Elementos de vídeo ou canvas não encontrados');\n }\n\n try {\n this.stream = await navigator.mediaDevices.getUserMedia({\n video: {\n width: { ideal: 640 },\n height: { ideal: 480 },\n facingMode: 'user'\n },\n audio: false\n });\n\n this.video.srcObject = this.stream;\n await this.video.play();\n\n // Configura o canvas com as mesmas dimensões do vídeo\n this.canvas.width = this.video.videoWidth;\n this.canvas.height = this.video.videoHeight;\n\n } catch (error) {\n throw new Error('Não foi possível acessar a câmera: ' + (error as Error).message);\n }\n }\n\n /**\n * Inicia a contagem regressiva\n */\n private startCountdown(): void {\n if (!this.modal) return;\n\n const countdownElement = this.modal.querySelector('.neofaceid-countdown-number') as HTMLElement;\n const captureBtn = this.modal.querySelector('.neofaceid-capture-btn') as HTMLButtonElement;\n const statusText = this.modal.querySelector('.neofaceid-status-text') as HTMLElement;\n\n let count = this.options.countdown || 3;\n \n if (countdownElement) {\n countdownElement.textContent = count.toString();\n }\n\n this.countdownInterval = window.setInterval(() => {\n count--;\n \n if (countdownElement) {\n countdownElement.textContent = count.toString();\n }\n\n if (count <= 0) {\n if (this.countdownInterval) {\n clearInterval(this.countdownInterval);\n this.countdownInterval = null;\n }\n \n if (countdownElement) {\n countdownElement.style.display = 'none';\n }\n \n if (captureBtn) {\n captureBtn.disabled = false;\n captureBtn.textContent = 'Capturar Agora';\n }\n \n if (statusText) {\n statusText.textContent = 'Pronto para capturar!';\n }\n\n // Auto-captura se estiver no modo automático\n if (this.options.mode === 'auto') {\n setTimeout(() => this.captureImage(), 500);\n }\n }\n }, 1000);\n }\n\n /**\n * Captura a imagem da câmera\n */\n private async captureImage(): Promise<void> {\n if (this.isCapturing || !this.video || !this.canvas) return;\n\n this.isCapturing = true;\n\n try {\n const context = this.canvas.getContext('2d');\n if (!context) throw new Error('Não foi possível obter contexto do canvas');\n\n // Desenha o frame atual do vídeo no canvas\n context.drawImage(this.video, 0, 0, this.canvas.width, this.canvas.height);\n\n // Converte para base64\n const imageData = this.canvas.toDataURL('image/jpeg', 0.8);\n \n // Remove o prefixo data:image/jpeg;base64,\n const base64Data = imageData.split(',')[1];\n // Mantém também a URL de dados completa para consumo externo\n const dataUrl = `data:image/jpeg;base64,${base64Data}`;\n\n // Detecta o tipo biométrico se estiver no modo auto\n let detectedType: 'face' | 'hand' = this.options.mode === 'auto' \n ? await this.detectBiometricType(base64Data)\n : this.options.mode as 'face' | 'hand';\n\n this.close();\n // Passa a URL de dados completa ao consumidor (SDK), evitando fetch incorreto de caminho longo\n this.options.onSuccess(dataUrl, detectedType);\n\n } catch (error) {\n this.options.onError(\n new NeoFaceError(\n 'Erro ao capturar imagem: ' + (error as Error).message,\n ErrorType.CAPTURE_ERROR\n )\n );\n } finally {\n this.isCapturing = false;\n }\n }\n\n /**\n * Detecta o tipo biométrico na imagem (face ou mão)\n */\n private async detectBiometricType(imageData: string): Promise<'face' | 'hand'> {\n try {\n // Importa a função de detecção\n const { detectBiometricType } = await import('./biometricDetection');\n const result = await detectBiometricType(imageData);\n \n // Se a detecção foi bem-sucedida e tem confiança suficiente\n if (result.type !== 'unknown' && result.confidence > 0.6) {\n return result.type;\n }\n \n // Fallback: assume face como padrão\n return 'face';\n } catch (error) {\n console.warn('Erro na detecção automática, usando face como padrão:', error);\n return 'face';\n }\n }\n\n /**\n * Adiciona event listeners aos elementos do modal\n */\n private addEventListeners(): void {\n if (!this.modal) return;\n\n const closeBtn = this.modal.querySelector('.neofaceid-close-btn');\n const cancelBtn = this.modal.querySelector('.neofaceid-cancel-btn');\n const captureBtn = this.modal.querySelector('.neofaceid-capture-btn');\n\n closeBtn?.addEventListener('click', () => {\n this.close();\n this.options.onCancel?.();\n });\n\n cancelBtn?.addEventListener('click', () => {\n this.close();\n this.options.onCancel?.();\n });\n\n captureBtn?.addEventListener('click', () => {\n this.captureImage();\n });\n\n // Fecha modal ao clicar no overlay\n this.modal.addEventListener('click', (e) => {\n if (e.target === this.modal?.querySelector('.neofaceid-modal-overlay')) {\n this.close();\n this.options.onCancel?.();\n }\n });\n }\n\n /**\n * Limpa recursos (câmera, intervalos, etc.)\n */\n private cleanup(): void {\n if (this.stream) {\n this.stream.getTracks().forEach(track => track.stop());\n this.stream = null;\n }\n\n if (this.countdownInterval) {\n clearInterval(this.countdownInterval);\n this.countdownInterval = null;\n }\n\n this.video = null;\n this.canvas = null;\n this.isCapturing = false;\n }\n\n /**\n * Retorna o título padrão baseado no modo\n */\n private getDefaultTitle(): string {\n switch (this.options.mode) {\n case 'face':\n return 'Autenticação Facial';\n case 'hand':\n return 'Autenticação por Mão';\n case 'auto':\n return 'Autenticação Biométrica';\n default:\n return 'Captura Biométrica';\n }\n }\n\n /**\n * Retorna o subtítulo padrão baseado no modo\n */\n private getDefaultSubtitle(): string {\n switch (this.options.mode) {\n case 'face':\n return 'Posicione seu rosto dentro do quadro e aguarde a captura automática.';\n case 'hand':\n return 'Posicione sua mão dentro do quadro e aguarde a captura automática.';\n case 'auto':\n return 'Posicione seu rosto ou mão dentro do quadro para autenticação automática.';\n default:\n return 'Posicione-se dentro do quadro para captura.';\n }\n }\n\n /**\n * Adiciona estilos CSS ao modal\n */\n private addStyles(): void {\n const styleId = 'neofaceid-biometric-modal-styles';\n \n // Remove estilos existentes se houver\n const existingStyles = document.getElementById(styleId);\n if (existingStyles) {\n existingStyles.remove();\n }\n\n const style = document.createElement('style');\n style.id = styleId;\n style.textContent = `\n .neofaceid-biometric-modal {\n position: fixed;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n z-index: 10000;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n }\n\n .neofaceid-modal-overlay {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n background: rgba(0, 0, 0, 0.8);\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 20px;\n box-sizing: border-box;\n }\n\n .neofaceid-modal-content {\n background: white;\n border-radius: 12px;\n max-width: 600px;\n width: 100%;\n max-height: 90vh;\n overflow: hidden;\n box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3);\n }\n\n .neofaceid-modal-header {\n padding: 20px;\n border-bottom: 1px solid #e0e0e0;\n display: flex;\n justify-content: space-between;\n align-items: center;\n }\n\n .neofaceid-modal-header h2 {\n margin: 0;\n font-size: 24px;\n font-weight: 600;\n color: #333;\n }\n\n .neofaceid-close-btn {\n background: none;\n border: none;\n font-size: 28px;\n cursor: pointer;\n color: #666;\n padding: 0;\n width: 32px;\n height: 32px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 50%;\n transition: background-color 0.2s;\n }\n\n .neofaceid-close-btn:hover {\n background-color: #f0f0f0;\n }\n\n .neofaceid-modal-body {\n padding: 20px;\n }\n\n .neofaceid-subtitle {\n margin: 0 0 20px 0;\n color: #666;\n font-size: 16px;\n line-height: 1.4;\n }\n\n .neofaceid-camera-container {\n position: relative;\n background: #000;\n border-radius: 8px;\n overflow: hidden;\n aspect-ratio: 4/3;\n margin-bottom: 20px;\n }\n\n .neofaceid-video {\n width: 100%;\n height: 100%;\n object-fit: cover;\n }\n\n .neofaceid-canvas {\n position: absolute;\n top: 0;\n left: 0;\n }\n\n .neofaceid-overlay {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n display: flex;\n align-items: center;\n justify-content: center;\n }\n\n .neofaceid-frame {\n width: 200px;\n height: 200px;\n border: 3px solid #4CAF50;\n border-radius: 50%;\n box-shadow: 0 0 0 2px rgba(76, 175, 80, 0.3);\n animation: pulse 2s infinite;\n }\n\n .neofaceid-countdown {\n position: absolute;\n top: 20px;\n right: 20px;\n background: rgba(0, 0, 0, 0.7);\n color: white;\n padding: 10px;\n border-radius: 50%;\n width: 50px;\n height: 50px;\n display: flex;\n align-items: center;\n justify-content: center;\n }\n\n .neofaceid-countdown-number {\n font-size: 24px;\n font-weight: bold;\n }\n\n .neofaceid-status {\n text-align: center;\n margin-bottom: 20px;\n }\n\n .neofaceid-status-text {\n margin: 0;\n color: #666;\n font-size: 16px;\n }\n\n .neofaceid-modal-footer {\n padding: 20px;\n border-top: 1px solid #e0e0e0;\n display: flex;\n gap: 12px;\n justify-content: flex-end;\n }\n\n .neofaceid-cancel-btn,\n .neofaceid-capture-btn {\n padding: 12px 24px;\n border: none;\n border-radius: 6px;\n font-size: 16px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.2s;\n }\n\n .neofaceid-cancel-btn {\n background: #f5f5f5;\n color: #666;\n }\n\n .neofaceid-cancel-btn:hover {\n background: #e0e0e0;\n }\n\n .neofaceid-capture-btn {\n background: #4CAF50;\n color: white;\n }\n\n .neofaceid-capture-btn:hover:not(:disabled) {\n background: #45a049;\n }\n\n .neofaceid-capture-btn:disabled {\n background: #ccc;\n cursor: not-allowed;\n }\n\n @keyframes pulse {\n 0% { box-shadow: 0 0 0 2px rgba(76, 175, 80, 0.3); }\n 50% { box-shadow: 0 0 0 10px rgba(76, 175, 80, 0.1); }\n 100% { box-shadow: 0 0 0 2px rgba(76, 175, 80, 0.3); }\n }\n\n @media (max-width: 640px) {\n .neofaceid-modal-overlay {\n padding: 10px;\n }\n \n .neofaceid-modal-header,\n .neofaceid-modal-body,\n .neofaceid-modal-footer {\n padding: 15px;\n }\n \n .neofaceid-frame {\n width: 150px;\n height: 150px;\n }\n }\n `;\n\n document.head.appendChild(style);\n }\n}","export type OverlayStatus = 'preparing' | 'detecting' | 'capturing' | 'verifying' | 'success' | 'error';\n\n/**\n * Overlay minimalista estilo FaceID para login biométrico\n * Implementação vanilla JS (sem React) para evitar conflitos de versão\n */\nexport class BiometricStatusOverlay {\n private container: HTMLDivElement | null = null;\n private currentStatus: OverlayStatus = 'preparing';\n\n private getStyles(): string {\n return `\n @keyframes neofaceid-fadeIn {\n from { opacity: 0; transform: scale(0.9); }\n to { opacity: 1; transform: scale(1); }\n }\n\n @keyframes neofaceid-fadeOut {\n from { opacity: 1; transform: scale(1); }\n to { opacity: 0; transform: scale(0.9); }\n }\n\n @keyframes neofaceid-pulse {\n 0%, 100% { \n transform: scale(1);\n opacity: 1;\n }\n 50% { \n transform: scale(1.05);\n opacity: 0.9;\n }\n }\n\n @keyframes neofaceid-scaleIn {\n 0% { transform: scale(0); opacity: 0; }\n 50% { transform: scale(1.1); }\n 100% { transform: scale(1); opacity: 1; }\n }\n\n @keyframes neofaceid-errorShake {\n 0%, 100% { transform: translateX(0); }\n 10%, 30%, 50%, 70%, 90% { transform: translateX(-4px); }\n 20%, 40%, 60%, 80% { transform: translateX(4px); }\n }\n\n .neofaceid-overlay-container {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 10000;\n pointer-events: none;\n animation: neofaceid-fadeIn 0.3s ease-out;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;\n }\n\n .neofaceid-overlay-container.fade-out {\n animation: neofaceid-fadeOut 0.3s ease-out forwards;\n }\n\n .neofaceid-overlay-content {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 24px;\n pointer-events: auto;\n }\n\n .neofaceid-visual-wrapper {\n position: relative;\n width: 100px;\n height: 100px;\n display: flex;\n align-items: center;\n justify-content: center;\n }\n\n .neofaceid-logo {\n width: 80px;\n height: 80px;\n object-fit: contain;\n transition: all 0.5s ease;\n filter: drop-shadow(0 4px 12px rgba(124, 58, 237, 0.3));\n }\n\n /* Cores por estado */\n .neofaceid-state-preparing .neofaceid-logo { filter: drop-shadow(0 0 15px rgba(124, 58, 237, 0.4)); }\n .neofaceid-state-detecting .neofaceid-logo { filter: hue-rotate(180deg) drop-shadow(0 0 15px rgba(59, 130, 246, 0.6)); }\n .neofaceid-state-capturing .neofaceid-logo { filter: hue-rotate(45deg) drop-shadow(0 0 15px rgba(245, 158, 11, 0.6)); }\n .neofaceid-state-verifying .neofaceid-logo { filter: hue-rotate(90deg) drop-shadow(0 0 15px rgba(34, 197, 94, 0.6)); }\n\n .neofaceid-logo.pulsing {\n animation: neofaceid-pulse 2s ease-in-out infinite;\n }\n\n @keyframes neofaceid-dots {\n 0%, 20% { opacity: 0; transform: translateY(0); }\n 50% { opacity: 1; transform: translateY(-2px); }\n 80%, 100% { opacity: 0; transform: translateY(0); }\n }\n\n .neofaceid-status-text {\n font-size: 15px;\n font-weight: 500;\n color: #ffffff;\n text-align: center;\n padding: 10px 24px;\n background: rgba(15, 23, 42, 0.8);\n backdrop-filter: blur(8px);\n border-radius: 100px;\n box-shadow: 0 10px 25px rgba(0, 0, 0, 0.3);\n border: 1px solid rgba(255, 255, 255, 0.1);\n text-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);\n animation: neofaceid-fadeIn 0.3s ease-out;\n display: flex;\n align-items: center;\n gap: 2px;\n }\n\n .neofaceid-dots {\n display: inline-flex;\n margin-left: 2px;\n }\n\n .neofaceid-dot {\n animation: neofaceid-dots 1.5s infinite;\n opacity: 0;\n }\n\n .neofaceid-dot:nth-child(2) { animation-delay: 0.2s; }\n .neofaceid-dot:nth-child(3) { animation-delay: 0.4s; }\n\n .neofaceid-status-icon {\n position: absolute;\n inset: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n }\n\n .neofaceid-success-icon {\n width: 80px;\n height: 80px;\n color: #10b981;\n animation: neofaceid-scaleIn 0.5s cubic-bezier(0.34, 1.56, 0.64, 1);\n }\n\n .neofaceid-error-icon {\n width: 80px;\n height: 80px;\n color: #ef4444;\n animation: neofaceid-errorShake 0.5s linear, neofaceid-scaleIn 0.4s ease-out;\n }\n `;\n }\n\n private getStatusText(): string {\n switch (this.currentStatus) {\n case 'preparing': return 'Iniciando câmera';\n case 'detecting': return 'Posicione seu rosto';\n case 'capturing': return 'Capturando';\n case 'verifying': return 'Verificando identidade';\n case 'success': return 'Identificado!';\n case 'error': return 'Falha na identificação';\n default: return '';\n }\n }\n\n private renderContent(): string {\n const isPulsing = ['detecting', 'verifying'].includes(this.currentStatus);\n const showLogo = !['success', 'error'].includes(this.currentStatus);\n const showSuccess = this.currentStatus === 'success';\n const showError = this.currentStatus === 'error';\n const statusText = this.getStatusText();\n const showDots = ['preparing', 'detecting', 'capturing', 'verifying'].includes(this.currentStatus);\n\n return `\n <style>${this.getStyles()}</style>\n <div class=\"neofaceid-overlay-container neofaceid-state-${this.currentStatus}\">\n <div class=\"neofaceid-overlay-content\">\n <div class=\"neofaceid-visual-wrapper\">\n ${showLogo ? `\n <img\n src=\"/logo-icone-no-background.png\"\n alt=\"NeoFaceId\"\n class=\"neofaceid-logo ${isPulsing ? 'pulsing' : ''}\"\n onerror=\"this.style.display='none'\"\n />\n ` : ''}\n \n ${showSuccess ? `\n <div class=\"neofaceid-status-icon\">\n <svg class=\"neofaceid-success-icon\" viewBox=\"0 0 60 60\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <circle cx=\"30\" cy=\"30\" r=\"28\" stroke=\"currentColor\" stroke-width=\"3\" fill=\"none\"/>\n <path d=\"M15 30 L25 40 L45 20\" stroke=\"currentColor\" stroke-width=\"3\" stroke-linecap=\"round\" stroke-linejoin=\"round\" fill=\"none\"/>\n </svg>\n </div>\n ` : ''}\n \n ${showError ? `\n <div class=\"neofaceid-status-icon\">\n <svg class=\"neofaceid-error-icon\" viewBox=\"0 0 60 60\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <circle cx=\"30\" cy=\"30\" r=\"28\" stroke=\"currentColor\" stroke-width=\"3\" fill=\"none\"/>\n <path d=\"M20 20 L40 40 M40 20 L20 40\" stroke=\"currentColor\" stroke-width=\"3\" stroke-linecap=\"round\"/>\n </svg>\n </div>\n ` : ''}\n </div>\n \n ${statusText ? `\n <div class=\"neofaceid-status-text\">\n ${statusText}${showDots ? `\n <span class=\"neofaceid-dots\">\n <span class=\"neofaceid-dot\">.</span>\n <span class=\"neofaceid-dot\">.</span>\n <span class=\"neofaceid-dot\">.</span>\n </span>\n ` : ''}\n </div>\n ` : ''}\n </div>\n </div>\n `;\n }\n\n public show(status: OverlayStatus = 'preparing'): void {\n if (this.container) {\n this.updateStatus(status);\n return;\n }\n\n this.container = document.createElement('div');\n this.container.id = 'neofaceid-status-overlay';\n this.currentStatus = status;\n this.container.innerHTML = this.renderContent();\n document.body.appendChild(this.container);\n }\n\n public updateStatus(status: OverlayStatus): void {\n this.currentStatus = status;\n if (this.container) {\n this.container.innerHTML = this.renderContent();\n }\n }\n\n public close(): void {\n if (this.container) {\n const overlayElement = this.container.querySelector('.neofaceid-overlay-container');\n if (overlayElement) {\n overlayElement.classList.add('fade-out');\n }\n\n setTimeout(() => {\n if (this.container && document.body.contains(this.container)) {\n document.body.removeChild(this.container);\n }\n this.container = null;\n }, 300);\n }\n }\n}\n","import { createRoot } from 'react-dom/client';\nimport { NeoFaceError } from '../errors';\n\ninterface FallbackPromptProps {\n error?: NeoFaceError;\n onRetry: () => void;\n onCredentials: () => void;\n onCancel?: () => void;\n}\n\n/**\n * Prompt de fallback após múltiplas tentativas falhadas\n * - Oferece \"Tentar novamente\" e \"Entrar com email e senha\"\n * - Design minimalista e clean\n */\nfunction FallbackPromptComponent({\n error,\n onRetry,\n onCredentials,\n onCancel,\n}: FallbackPromptProps) {\n return (\n <>\n <style>{`\n @keyframes slideUp {\n from {\n opacity: 0;\n transform: translateY(20px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n }\n\n @keyframes fadeIn {\n from { opacity: 0; }\n to { opacity: 1; }\n }\n\n .neofaceid-fallback-overlay {\n position: fixed;\n inset: 0;\n background: rgba(0, 0, 0, 0.6);\n backdrop-filter: blur(8px);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 10001;\n padding: 20px;\n animation: fadeIn 0.3s ease-out;\n }\n\n .neofaceid-fallback-prompt {\n background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);\n border-radius: 20px;\n padding: 32px;\n max-width: 400px;\n width: 100%;\n box-shadow: 0 24px 48px rgba(0, 0, 0, 0.4), 0 0 0 1px rgba(255, 255, 255, 0.1);\n animation: slideUp 0.4s ease-out;\n text-align: center;\n }\n\n .neofaceid-fallback-icon {\n width: 64px;\n height: 64px;\n margin: 0 auto 20px;\n color: #ef4444;\n animation: scaleIn 0.4s ease-out;\n }\n\n @keyframes scaleIn {\n 0% {\n transform: scale(0);\n opacity: 0;\n }\n 50% {\n transform: scale(1.1);\n }\n 100% {\n transform: scale(1);\n opacity: 1;\n }\n }\n\n .neofaceid-fallback-title {\n font-size: 22px;\n font-weight: 700;\n color: white;\n margin: 0 0 12px 0;\n }\n\n .neofaceid-fallback-message {\n font-size: 15px;\n color: rgba(255, 255, 255, 0.7);\n margin: 0 0 28px 0;\n line-height: 1.5;\n }\n\n .neofaceid-fallback-actions {\n display: flex;\n flex-direction: column;\n gap: 12px;\n }\n\n .neofaceid-fallback-button {\n padding: 14px 24px;\n border-radius: 12px;\n border: none;\n font-size: 16px;\n font-weight: 600;\n cursor: pointer;\n transition: all 0.2s;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n }\n\n .neofaceid-fallback-button-primary {\n background: linear-gradient(135deg, #7c3aed 0%, #6d28d9 100%);\n color: white;\n box-shadow: 0 4px 16px rgba(124, 58, 237, 0.3);\n }\n\n .neofaceid-fallback-button-primary:hover {\n transform: translateY(-2px);\n box-shadow: 0 8px 24px rgba(124, 58, 237, 0.4);\n }\n\n .neofaceid-fallback-button-primary:active {\n transform: translateY(0);\n }\n\n .neofaceid-fallback-button-secondary {\n background: rgba(255, 255, 255, 0.1);\n color: white;\n border: 1px solid rgba(255, 255, 255, 0.2);\n }\n\n .neofaceid-fallback-button-secondary:hover {\n background: rgba(255, 255, 255, 0.15);\n border-color: rgba(255, 255, 255, 0.3);\n }\n\n .neofaceid-fallback-button-ghost {\n background: transparent;\n color: rgba(255, 255, 255, 0.7);\n padding: 10px;\n }\n\n .neofaceid-fallback-button-ghost:hover {\n color: white;\n background: rgba(255, 255, 255, 0.05);\n }\n\n @media (max-width: 640px) {\n .neofaceid-fallback-prompt {\n padding: 24px;\n border-radius: 16px;\n }\n\n .neofaceid-fallback-title {\n font-size: 20px;\n }\n }\n `}</style>\n\n <div className=\"neofaceid-fallback-overlay\">\n <div className=\"neofaceid-fallback-prompt\">\n <div className=\"neofaceid-fallback-icon\">\n <svg viewBox=\"0 0 64 64\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <circle cx=\"32\" cy=\"32\" r=\"30\" stroke=\"currentColor\" strokeWidth=\"3\" fill=\"none\" />\n <path\n d=\"M20 20 L44 44 M44 20 L20 44\"\n stroke=\"currentColor\"\n strokeWidth=\"3\"\n strokeLinecap=\"round\"\n />\n </svg>\n </div>\n\n <h3 className=\"neofaceid-fallback-title\">Não foi possível reconhecer</h3>\n <p className=\"neofaceid-fallback-message\">\n {error?.getFriendlyMessage() || 'Não conseguimos reconhecer sua face. Tente novamente ou use email e senha.'}\n </p>\n\n <div className=\"neofaceid-fallback-actions\">\n <button\n type=\"button\"\n className=\"neofaceid-fallback-button neofaceid-fallback-button-primary\"\n onClick={onRetry}\n >\n Tentar Novamente\n </button>\n <button\n type=\"button\"\n className=\"neofaceid-fallback-button neofaceid-fallback-button-secondary\"\n onClick={onCredentials}\n >\n Entrar com Email e Senha\n </button>\n {onCancel && (\n <button\n type=\"button\"\n className=\"neofaceid-fallback-button neofaceid-fallback-button-ghost\"\n onClick={onCancel}\n >\n Cancelar\n </button>\n )}\n </div>\n </div>\n </div>\n </>\n );\n}\n\n/**\n * Classe para gerenciar o prompt de fallback\n */\nexport class FallbackPrompt {\n private container: HTMLDivElement | null = null;\n private root: ReturnType<typeof createRoot> | null = null;\n\n /**\n * Exibe o prompt de fallback\n */\n public show(\n error: NeoFaceError | undefined,\n onRetry: () => void,\n onCredentials: () => void,\n onCancel?: () => void\n ): void {\n if (this.container) {\n return; // Já está aberto\n }\n\n this.container = document.createElement('div');\n this.container.id = 'neofaceid-fallback-prompt';\n document.body.appendChild(this.container);\n\n this.root = createRoot(this.container);\n this.render(error, onRetry, onCredentials, onCancel);\n }\n\n /**\n * Fecha o prompt\n */\n public close(): void {\n if (this.container && document.body.contains(this.container)) {\n if (this.root) {\n this.root.unmount();\n }\n document.body.removeChild(this.container);\n this.container = null;\n this.root = null;\n }\n }\n\n /**\n * Renderiza o componente\n */\n private render(\n error: NeoFaceError | undefined,\n onRetry: () => void,\n onCredentials: () => void,\n onCancel?: () => void\n ): void {\n if (!this.root || !this.container) return;\n\n this.root.render(\n <FallbackPromptComponent\n error={error}\n onRetry={() => {\n this.close();\n onRetry();\n }}\n onCredentials={() => {\n this.close();\n onCredentials();\n }}\n onCancel={() => {\n this.close();\n onCancel?.();\n }}\n />\n );\n }\n}\n\n","import { NeoFaceError, ErrorType } from './errors';\nimport { loginWithEmail, BiometricLoginResult } from './api';\n\nexport interface EmailPasswordOptions {\n applicationToken: string;\n onSuccess: (result: BiometricLoginResult) => void;\n onError: (error: NeoFaceError) => void;\n onCancel?: () => void;\n title?: string;\n subtitle?: string;\n}\n\n/**\n * Modal para entrada de email e senha como fallback para login.\n */\nexport class EmailPasswordModal {\n private options: EmailPasswordOptions;\n private modalElement: HTMLDivElement | null = null;\n\n constructor(options: EmailPasswordOptions) {\n this.options = options;\n }\n\n /**\n * Abre o modal de login com email e senha.\n */\n public async open(): Promise<void> {\n this.createModal();\n document.body.appendChild(this.modalElement!);\n }\n\n /**\n * Fecha o modal.\n */\n public close(): void {\n if (this.modalElement) {\n document.body.removeChild(this.modalElement);\n this.modalElement = null;\n }\n if (this.options.onCancel) {\n this.options.onCancel();\n }\n }\n\n private createModal(): void {\n this.addStyles();\n\n this.modalElement = document.createElement('div');\n this.modalElement.className = 'neoface-email-password-modal';\n this.modalElement.innerHTML = `\n <div class=\"neoface-email-modal-overlay\">\n <div class=\"neoface-email-modal-content\">\n <h2>${this.options.title || 'Login com Email e Senha'}</h2>\n <p>${this.options.subtitle || 'Informe seus dados para login.'}</p>\n <form id=\"email-password-form\">\n <input type=\"email\" id=\"email\" placeholder=\"Email\" required />\n <input type=\"password\" id=\"password\" placeholder=\"Senha\" required />\n <button type=\"submit\" class=\"neoface-email-submit-btn\">Entrar</button>\n </form>\n <button class=\"neoface-email-cancel-btn\">Cancelar</button>\n \n <!-- Branding -->\n <div class=\"neoface-email-branding\">\n <img src=\"/logo-icone-no-background.png\" alt=\"NeoFaceId\" class=\"neoface-email-brand-logo\" onerror=\"this.style.display='none'\" />\n <span>NeoFaceId by</span>\n <span class=\"neoface-email-brand-name\">OCTA</span>\n </div>\n </div>\n </div>\n `;\n\n const form = this.modalElement.querySelector('#email-password-form') as HTMLFormElement;\n form.addEventListener('submit', this.handleSubmit.bind(this));\n\n const cancelButton = this.modalElement.querySelector(\n '.neoface-email-cancel-btn'\n ) as HTMLButtonElement;\n cancelButton.addEventListener('click', this.close.bind(this));\n }\n\n private addStyles(): void {\n const styleId = 'neoface-email-password-modal-styles';\n\n // Remove estilos existentes se houver\n const existingStyles = document.getElementById(styleId);\n if (existingStyles) {\n return; // Já existe, não precisa adicionar novamente\n }\n\n const style = document.createElement('style');\n style.id = styleId;\n style.textContent = `\n .neoface-email-password-modal {\n position: fixed;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n z-index: 10002;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n }\n\n .neoface-email-modal-overlay {\n position: absolute;\n inset: 0;\n background: rgba(0, 0, 0, 0.6);\n backdrop-filter: blur(8px);\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 20px;\n animation: fadeIn 0.3s ease-out;\n }\n\n @keyframes fadeIn {\n from { opacity: 0; }\n to { opacity: 1; }\n }\n\n @keyframes slideUp {\n from {\n opacity: 0;\n transform: translateY(20px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n }\n\n .neoface-email-modal-content {\n background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);\n border-radius: 20px;\n padding: 32px;\n max-width: 400px;\n width: 100%;\n box-shadow: 0 24px 48px rgba(0, 0, 0, 0.4), 0 0 0 1px rgba(255, 255, 255, 0.1);\n animation: slideUp 0.4s ease-out;\n }\n\n .neoface-email-modal-content h2 {\n font-size: 24px;\n font-weight: 700;\n color: white;\n margin: 0 0 12px 0;\n text-align: center;\n }\n\n .neoface-email-modal-content p {\n font-size: 15px;\n color: rgba(255, 255, 255, 0.7);\n margin: 0 0 24px 0;\n text-align: center;\n line-height: 1.5;\n }\n\n .neoface-email-modal-content form {\n display: flex;\n flex-direction: column;\n gap: 16px;\n margin-bottom: 16px;\n }\n\n .neoface-email-modal-content input {\n width: 100%;\n padding: 14px 16px;\n border: 2px solid rgba(255, 255, 255, 0.2);\n border-radius: 12px;\n font-size: 15px;\n background: rgba(255, 255, 255, 0.1);\n color: white;\n font-family: inherit;\n transition: all 0.2s;\n box-sizing: border-box;\n }\n\n .neoface-email-modal-content input::placeholder {\n color: rgba(255, 255, 255, 0.5);\n }\n\n .neoface-email-modal-content input:focus {\n outline: none;\n border-color: #7c3aed;\n box-shadow: 0 0 0 3px rgba(124, 58, 237, 0.2);\n background: rgba(255, 255, 255, 0.15);\n }\n\n .neoface-email-submit-btn {\n width: 100%;\n padding: 14px 24px;\n border-radius: 12px;\n border: none;\n background: linear-gradient(135deg, #7c3aed 0%, #6d28d9 100%);\n color: white;\n font-size: 16px;\n font-weight: 600;\n cursor: pointer;\n transition: all 0.2s;\n box-shadow: 0 4px 16px rgba(124, 58, 237, 0.3);\n font-family: inherit;\n }\n\n .neoface-email-submit-btn:hover {\n transform: translateY(-2px);\n box-shadow: 0 8px 24px rgba(124, 58, 237, 0.4);\n }\n\n .neoface-email-submit-btn:active {\n transform: translateY(0);\n }\n\n .neoface-email-submit-btn:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n transform: none;\n }\n\n .neoface-email-cancel-btn {\n width: 100%;\n padding: 12px 24px;\n border-radius: 12px;\n border: 1px solid rgba(255, 255, 255, 0.2);\n background: transparent;\n color: rgba(255, 255, 255, 0.7);\n font-size: 15px;\n font-weight: 600;\n cursor: pointer;\n transition: all 0.2s;\n font-family: inherit;\n }\n\n .neoface-email-cancel-btn:hover {\n background: rgba(255, 255, 255, 0.1);\n color: white;\n border-color: rgba(255, 255, 255, 0.3);\n }\n\n .neoface-email-branding {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n margin-top: 32px;\n padding-top: 24px;\n border-top: 1px solid rgba(255, 255, 255, 0.1);\n font-size: 13px;\n color: rgba(255, 255, 255, 0.5);\n }\n\n .neoface-email-brand-logo {\n height: 20px;\n width: auto;\n }\n\n .neoface-email-brand-name {\n font-weight: 600;\n font-family: 'SF Pro Display', -apple-system, system-ui, sans-serif;\n color: #ffffff;\n letter-spacing: 0.3px;\n }\n\n @media (max-width: 640px) {\n .neoface-email-modal-content {\n padding: 24px;\n border-radius: 16px;\n }\n\n .neoface-email-modal-content h2 {\n font-size: 20px;\n }\n }\n `;\n\n document.head.appendChild(style);\n }\n\n private async handleSubmit(event: Event): Promise<void> {\n event.preventDefault();\n const emailInput = this.modalElement!.querySelector('#email') as HTMLInputElement;\n const passwordInput = this.modalElement!.querySelector('#password') as HTMLInputElement;\n\n const email = emailInput.value.trim();\n const password = passwordInput.value.trim();\n\n if (!email || !password) {\n this.options.onError(\n new NeoFaceError('Email e senha são obrigatórios', ErrorType.VALIDATION_ERROR)\n );\n return;\n }\n\n try {\n const result = await loginWithEmail(email, password, this.options.applicationToken);\n this.options.onSuccess(result);\n this.close();\n } catch (error) {\n this.options.onError(error as NeoFaceError);\n }\n }\n}\n","import { BiometricStatusOverlay } from '../components/BiometricStatusOverlay';\nimport { FallbackPrompt } from '../components/FallbackPrompt';\nimport { loginWithBiometric, BiometricLoginResult } from '../api';\nimport { NeoFaceError, ErrorType } from '../errors';\nimport * as faceapi from 'face-api.js';\n\nconst MAX_ATTEMPTS = 2;\nconst RETRY_DELAY = 500; // ms entre tentativas\n\n// ============================================\n// OTIMIZAÇÕES DE PERFORMANCE v1.7.1\n// ============================================\n// Delays otimizados para login rápido (~2-3s)\nconst CAMERA_READY_DELAY = 200; // Era 800ms\nconst CAMERA_STABILITY_DELAY = 100; // Era 500ms\nconst FACE_DETECTION_TIME = 5000; // 5 segundos para encontrar rosto\nconst DETECTION_INTERVAL = 100; // Era 200ms\nconst SUCCESS_DELAY = 200; // Era 800ms\nconst INITIAL_DELAY = 0; // Era 300ms\n// ============================================\n\ninterface BiometricLoginFlowOptions {\n applicationToken: string;\n onSuccess: (result: BiometricLoginResult) => void;\n onError: (error: NeoFaceError) => void;\n onFallbackRequest?: () => void;\n onCancel?: () => void;\n /** Modo rápido: captura assim que detecta rosto, sem esperar tempo fixo */\n fastMode?: boolean;\n}\n\n// Cache para modelos do face-api.js\nlet modelsLoaded = false;\nlet modelsLoading: Promise<void> | null = null;\n\n/**\n * Pré-carrega modelos do face-api.js (chame no início da aplicação)\n */\nexport async function preloadFaceDetectionModels(): Promise<void> {\n if (modelsLoaded) return;\n if (modelsLoading) return modelsLoading;\n\n modelsLoading = (async () => {\n try {\n const MODEL_URL = 'https://cdn.jsdelivr.net/npm/@vladmandic/face-api/model';\n await faceapi.nets.tinyFaceDetector.loadFromUri(MODEL_URL);\n modelsLoaded = true;\n console.log('✅ Face detection models preloaded');\n } catch (error) {\n console.warn('⚠️ Face-api.js models not loaded:', error);\n }\n })();\n\n return modelsLoading;\n}\n\n/**\n * Captura uma imagem da câmera sem mostrar o vídeo\n * @returns Promise that resolves to a Blob of the captured image\n */\nexport async function captureFaceSilently(): Promise<Blob> {\n return new Promise((resolve, reject) => {\n const video = document.createElement('video');\n video.style.position = 'fixed';\n video.style.top = '-9999px';\n video.style.left = '-9999px';\n video.style.width = '1px';\n video.style.height = '1px';\n video.style.opacity = '0';\n video.setAttribute('autoplay', '');\n video.setAttribute('muted', '');\n video.setAttribute('playsinline', '');\n\n const canvas = document.createElement('canvas');\n canvas.style.display = 'none';\n\n let stream: MediaStream | null = null;\n let faceDetectionAttempts = 0;\n const MAX_FACE_DETECTION_ATTEMPTS = 10;\n\n const cleanup = () => {\n if (stream) {\n stream.getTracks().forEach(track => track.stop());\n }\n if (video.parentElement) {\n video.parentElement.removeChild(video);\n }\n if (canvas.parentElement) {\n canvas.parentElement.removeChild(canvas);\n }\n };\n\n // Adiciona elementos ao DOM (mas invisíveis)\n document.body.appendChild(video);\n document.body.appendChild(canvas);\n\n navigator.mediaDevices\n .getUserMedia({\n video: {\n width: { ideal: 640 },\n height: { ideal: 480 },\n facingMode: 'user',\n },\n audio: false,\n })\n .then(mediaStream => {\n stream = mediaStream;\n video.srcObject = stream;\n video.play();\n\n const tryCapture = () => {\n if (!video.videoWidth || !video.videoHeight) {\n if (faceDetectionAttempts < MAX_FACE_DETECTION_ATTEMPTS) {\n faceDetectionAttempts++;\n setTimeout(tryCapture, 100);\n return;\n }\n cleanup();\n reject(new NeoFaceError('Câmera não está pronta', ErrorType.CAMERA_ERROR));\n return;\n }\n\n canvas.width = video.videoWidth;\n canvas.height = video.videoHeight;\n\n const ctx = canvas.getContext('2d');\n if (!ctx) {\n cleanup();\n reject(new Error('Failed to get canvas context'));\n return;\n }\n\n // Tenta detectar face (opcional - não bloqueia se falhar)\n const detectFace = async () => {\n if (!modelsLoaded) return true; // Se modelos não carregaram, continua\n try {\n const detection = await faceapi.detectSingleFace(\n video,\n new faceapi.TinyFaceDetectorOptions({ inputSize: 160, scoreThreshold: 0.3 })\n );\n return !!detection;\n } catch {\n return true; // Se falhar, continua mesmo assim\n }\n };\n\n detectFace().then(hasFace => {\n // Aguarda um pouco mais se não detectou face\n if (!hasFace && faceDetectionAttempts < MAX_FACE_DETECTION_ATTEMPTS) {\n faceDetectionAttempts++;\n setTimeout(tryCapture, 100);\n return;\n }\n\n // Captura a imagem\n ctx.drawImage(video, 0, 0, canvas.width, canvas.height);\n\n canvas.toBlob(\n blob => {\n cleanup();\n if (blob) {\n resolve(blob);\n } else {\n reject(new Error('Failed to capture image'));\n }\n },\n 'image/jpeg',\n 0.85\n );\n });\n };\n\n video.onloadedmetadata = () => {\n // Delay otimizado\n setTimeout(tryCapture, CAMERA_READY_DELAY);\n };\n\n video.onerror = () => {\n cleanup();\n reject(new NeoFaceError('Erro ao acessar câmera', ErrorType.CAMERA_ERROR));\n };\n })\n .catch(error => {\n cleanup();\n reject(\n new NeoFaceError(\n 'Não foi possível acessar a câmera: ' + error.message,\n ErrorType.CAMERA_ERROR\n )\n );\n });\n });\n}\n\n/**\n * Detecta rosto rapidamente - retorna assim que detectar\n * Versão otimizada para login rápido\n */\nasync function detectFaceQuickly(\n video: HTMLVideoElement,\n maxTime: number = FACE_DETECTION_TIME,\n interval: number = DETECTION_INTERVAL\n): Promise<boolean> {\n return new Promise((resolve) => {\n let attempts = 0;\n const maxAttempts = Math.ceil(maxTime / interval);\n let timeoutId: number | null = null;\n let isResolved = false;\n\n const finish = (success: boolean) => {\n if (isResolved) return;\n isResolved = true;\n if (timeoutId) clearTimeout(timeoutId);\n resolve(success);\n };\n\n const checkFace = async () => {\n if (isResolved) return;\n\n attempts++;\n\n // Se modelos não carregaram, assume que tem rosto e deixa backend validar\n if (!modelsLoaded) {\n finish(true);\n return;\n }\n\n try {\n const detection = await faceapi.detectSingleFace(\n video,\n new faceapi.TinyFaceDetectorOptions({ inputSize: 160, scoreThreshold: 0.3 })\n );\n\n // FAST MODE: Detectou rosto? Captura imediatamente!\n if (detection) {\n finish(true);\n return;\n }\n\n // Continua tentando até o limite\n if (attempts >= maxAttempts) {\n finish(false);\n return;\n }\n\n timeoutId = window.setTimeout(checkFace, interval);\n } catch (error) {\n // Em caso de erro, assume que tem rosto e deixa backend validar\n finish(true);\n }\n };\n\n // Timeout de segurança\n window.setTimeout(() => {\n if (!isResolved) {\n // Timeout: assume que tem rosto e deixa backend decidir\n finish(true);\n }\n }, maxTime + 500);\n\n // Inicia verificação imediatamente\n checkFace();\n });\n}\n\n/**\n * Executa uma tentativa de login biométrico - VERSÃO OTIMIZADA\n */\nasync function attemptLogin(\n applicationToken: string,\n overlay: BiometricStatusOverlay,\n fastMode: boolean = false,\n isRetry: boolean = false\n): Promise<BiometricLoginResult> {\n // Atualiza overlay para \"preparing\" apenas na primeira tentativa\n // Se for re-tentativa, vai direto para \"detecting\"\n if (!isRetry) {\n overlay.updateStatus('preparing');\n } else {\n overlay.updateStatus('detecting');\n }\n\n // Cria vídeo invisível para captura\n const video = document.createElement('video');\n video.style.position = 'fixed';\n video.style.top = '-9999px';\n video.style.left = '-9999px';\n video.style.width = '1px';\n video.style.height = '1px';\n video.style.opacity = '0';\n video.setAttribute('autoplay', '');\n video.setAttribute('muted', '');\n video.setAttribute('playsinline', '');\n document.body.appendChild(video);\n\n let stream: MediaStream | null = null;\n\n try {\n // Inicializa câmera (Aqui o navegador pede permissão)\n stream = await navigator.mediaDevices.getUserMedia({\n video: {\n width: { ideal: 640 },\n height: { ideal: 480 },\n facingMode: 'user',\n },\n audio: false,\n });\n\n video.srcObject = stream;\n await video.play();\n\n // Aguarda câmera estar pronta\n await new Promise((resolve) => {\n if (video.readyState >= 2) {\n resolve(undefined);\n } else {\n video.onloadedmetadata = () => resolve(undefined);\n }\n });\n\n // Câmera pronta, agora sim pedimos para o usuário se posicionar\n overlay.updateStatus('detecting');\n\n // Delay de estabilidade OTIMIZADO\n await new Promise((resolve) => setTimeout(resolve, CAMERA_STABILITY_DELAY));\n\n // Detecta rosto rapidamente (ou pula em fastMode)\n let faceDetected = true;\n if (!fastMode) {\n faceDetected = await detectFaceQuickly(video);\n }\n\n if (!faceDetected) {\n // Limpa recursos antes de lançar erro\n if (stream) {\n stream.getTracks().forEach(track => track.stop());\n }\n if (video.parentElement) {\n video.parentElement.removeChild(video);\n }\n throw new NeoFaceError('Rosto não detectado. Posicione seu rosto na frente da câmera.', ErrorType.VALIDATION_ERROR);\n }\n\n // Rosto detectado, muda para estado de captura\n overlay.updateStatus('capturing');\n\n // Captura imagem imediatamente\n const canvas = document.createElement('canvas');\n canvas.width = video.videoWidth;\n canvas.height = video.videoHeight;\n const ctx = canvas.getContext('2d');\n if (!ctx) {\n if (stream) {\n stream.getTracks().forEach(track => track.stop());\n }\n if (video.parentElement) {\n video.parentElement.removeChild(video);\n }\n throw new NeoFaceError('Erro ao capturar imagem', ErrorType.CAPTURE_ERROR);\n }\n\n ctx.drawImage(video, 0, 0, canvas.width, canvas.height);\n\n // Limpa recursos de vídeo\n if (stream) {\n stream.getTracks().forEach(track => track.stop());\n }\n if (video.parentElement) {\n video.parentElement.removeChild(video);\n }\n\n // Converte para blob\n const imageBlob = await new Promise<Blob>((resolve, reject) => {\n canvas.toBlob(\n blob => {\n canvas.remove();\n if (blob) {\n resolve(blob);\n } else {\n reject(new Error('Failed to capture image'));\n }\n },\n 'image/jpeg',\n 0.85\n );\n });\n\n // Imagem capturada, agora sim verificando com o servidor\n overlay.updateStatus('verifying');\n\n // Faz login com a imagem capturada\n const result = await loginWithBiometric(imageBlob, applicationToken);\n\n if (!result.success) {\n throw new NeoFaceError('Login falhou', ErrorType.LOGIN_FAILED);\n }\n\n return result;\n } catch (error) {\n // Limpa recursos em caso de erro\n if (stream) {\n stream.getTracks().forEach(track => track.stop());\n }\n if (video.parentElement) {\n video.parentElement.removeChild(video);\n }\n\n if (error instanceof NeoFaceError) {\n throw error;\n }\n\n throw new NeoFaceError(\n error instanceof Error ? error.message : 'Erro desconhecido',\n ErrorType.UNKNOWN\n );\n }\n}\n\n/**\n * Executa o fluxo completo de login biométrico - VERSÃO OTIMIZADA\n */\nexport async function executeBiometricLoginFlow(\n options: BiometricLoginFlowOptions\n): Promise<void> {\n const { applicationToken, onSuccess, onError, onFallbackRequest, onCancel, fastMode = false } = options;\n\n const overlay = new BiometricStatusOverlay();\n const fallbackPrompt = new FallbackPrompt();\n\n let attempts = 0;\n let lastError: NeoFaceError | undefined;\n\n // Carrega modelos do face-api.js se necessário (não bloqueia)\n if (!modelsLoaded && !fastMode) {\n preloadFaceDetectionModels().catch(() => {\n // Ignora erro, continua sem detecção local\n });\n }\n\n // Mostra overlay\n overlay.show('preparing');\n\n // Função para tentar login\n const tryLogin = async (): Promise<void> => {\n try {\n attempts++;\n const result = await attemptLogin(applicationToken, overlay, fastMode, attempts > 1);\n\n // Sucesso!\n overlay.updateStatus('success');\n setTimeout(() => {\n overlay.close();\n onSuccess(result);\n }, SUCCESS_DELAY);\n } catch (error) {\n lastError = error instanceof NeoFaceError ? error : new NeoFaceError(\n error instanceof Error ? error.message : 'Erro desconhecido',\n ErrorType.UNKNOWN\n );\n\n // Mostra erro no overlay\n overlay.updateStatus('error');\n\n // Se ainda há tentativas, tenta novamente\n if (attempts < MAX_ATTEMPTS) {\n setTimeout(() => {\n tryLogin();\n }, RETRY_DELAY);\n } else {\n // Esgotou tentativas, mostra fallback\n setTimeout(() => {\n overlay.close();\n fallbackPrompt.show(\n lastError,\n () => {\n // Tentar novamente - reseta contador\n attempts = 0;\n overlay.show('preparing');\n setTimeout(() => tryLogin(), RETRY_DELAY);\n },\n () => {\n // Entrar com email e senha\n if (onFallbackRequest) {\n onFallbackRequest();\n } else {\n onError(\n new NeoFaceError(\n 'Por favor, use email e senha para fazer login',\n ErrorType.LOGIN_FAILED\n )\n );\n }\n },\n onCancel\n );\n }, 500);\n }\n }\n };\n\n // Inicia o fluxo IMEDIATAMENTE (sem delay inicial)\n if (INITIAL_DELAY > 0) {\n setTimeout(() => tryLogin(), INITIAL_DELAY);\n } else {\n tryLogin();\n }\n}\n","import { NeoFaceError, ErrorType } from './errors';\nimport { validateToken, BiometricLoginResult, loginWithBiometric } from './api';\nimport { EmailPasswordModal, EmailPasswordOptions } from './EmailPasswordModal';\nimport { executeBiometricLoginFlow } from './utils/biometricLoginFlow';\nimport { BiometricCaptureOptions, BiometricCaptureModal } from './BiometricCaptureModal';\n\nexport interface BiometricLoginOptions {\n applicationToken: string;\n onSuccess: (result: BiometricLoginResult) => void;\n onError: (error: NeoFaceError) => void;\n onCancel?: () => void;\n onFallbackRequest?: () => void;\n countdown?: number;\n title?: string;\n subtitle?: string;\n}\n\n/**\n * Inicia o processo de login usando reconhecimento facial\n * Usa overlay minimalista estilo FaceID (não mostra câmera)\n * @param options Opções de configuração para o login facial\n */\nexport async function startFaceLogin(options: BiometricLoginOptions): Promise<void> {\n try {\n // Valida o token da aplicação antes de iniciar\n const isValidToken = await validateToken(options.applicationToken);\n if (!isValidToken) {\n options.onError(new NeoFaceError('Token de aplicação inválido', ErrorType.INVALID_TOKEN));\n return;\n }\n\n // Usa o novo fluxo com overlay minimalista\n await executeBiometricLoginFlow({\n applicationToken: options.applicationToken,\n onSuccess: options.onSuccess,\n onError: options.onError,\n onFallbackRequest: options.onFallbackRequest || (() => {\n // Fallback padrão: abre modal de email e senha\n const fallbackOptions: EmailPasswordOptions = {\n applicationToken: options.applicationToken,\n onSuccess: options.onSuccess,\n onError: options.onError,\n title: 'Login Alternativo',\n subtitle: 'Biometria não reconhecida. Por favor, informe email e senha.',\n };\n const modal = new EmailPasswordModal(fallbackOptions);\n modal.open();\n }),\n onCancel: options.onCancel,\n });\n } catch (error) {\n options.onError(\n new NeoFaceError(\n 'Erro ao inicializar login facial: ' + (error as Error).message,\n ErrorType.INITIALIZATION_ERROR\n )\n );\n }\n}\n\n/**\n * Inicia o processo de login usando reconhecimento de mão\n * @param options Opções de configuração para o login por mão\n */\nexport async function startHandLogin(options: BiometricLoginOptions): Promise<void> {\n try {\n // Valida o token da aplicação antes de iniciar\n const isValidToken = await validateToken(options.applicationToken);\n if (!isValidToken) {\n options.onError(new NeoFaceError('Token de aplicação inválido', ErrorType.INVALID_TOKEN));\n return;\n }\n\n const captureOptions: BiometricCaptureOptions = {\n mode: 'hand',\n onSuccess: async (imageData: string, _detectedType: 'face' | 'hand') => {\n try {\n // Convert base64 string to Blob\n const imageBlob = await fetch(imageData).then(res => res.blob());\n const result = await loginWithBiometric(imageBlob, options.applicationToken);\n options.onSuccess(result);\n } catch (error) {\n options.onError(error as NeoFaceError);\n }\n },\n onError: options.onError,\n onCancel: options.onCancel,\n countdown: options.countdown,\n title: options.title || 'Login por Mão',\n subtitle: options.subtitle || 'Posicione sua mão dentro do quadro para fazer login.',\n };\n\n const modal = new BiometricCaptureModal(captureOptions);\n await modal.open();\n } catch (error) {\n options.onError(\n new NeoFaceError(\n 'Erro ao inicializar login por mão: ' + (error as Error).message,\n ErrorType.INITIALIZATION_ERROR\n )\n );\n }\n}\n\n/**\n * Inicia o processo de login com detecção automática do tipo biométrico\n * @param options Opções de configuração para o login automático\n */\nexport async function startAutoLogin(options: BiometricLoginOptions): Promise<void> {\n try {\n // Valida o token da aplicação antes de iniciar\n const isValidToken = await validateToken(options.applicationToken);\n if (!isValidToken) {\n options.onError(new NeoFaceError('Token de aplicação inválido', ErrorType.INVALID_TOKEN));\n return;\n }\n\n const captureOptions: BiometricCaptureOptions = {\n mode: 'auto',\n onSuccess: async (imageData: string, _detectedType: 'face' | 'hand') => {\n try {\n // Convert base64 string to Blob\n const imageBlob = await fetch(imageData).then(res => res.blob());\n const result = await loginWithBiometric(imageBlob, options.applicationToken);\n options.onSuccess(result);\n } catch (error) {\n options.onError(error as NeoFaceError);\n }\n },\n onError: options.onError,\n onCancel: options.onCancel,\n countdown: options.countdown,\n title: options.title || 'Login Biométrico',\n subtitle:\n options.subtitle || 'Posicione seu rosto ou mão dentro do quadro para login automático.',\n };\n\n const modal = new BiometricCaptureModal(captureOptions);\n await modal.open();\n } catch (error) {\n options.onError(\n new NeoFaceError(\n 'Erro ao inicializar login automático: ' + (error as Error).message,\n ErrorType.INITIALIZATION_ERROR\n )\n );\n }\n}\n\n/**\n * Função de conveniência para login biométrico simples\n * @param applicationToken Token da aplicação\n * @param mode Modo de captura ('face', 'hand' ou 'auto')\n * @returns Promise que resolve com o resultado do login\n */\nexport function biometricLogin(\n applicationToken: string,\n mode: 'face' | 'hand' | 'auto' = 'auto'\n): Promise<BiometricLoginResult> {\n return new Promise((resolve, reject) => {\n const options: BiometricLoginOptions = {\n applicationToken,\n onSuccess: resolve,\n onError: reject,\n };\n\n switch (mode) {\n case 'face':\n startFaceLogin(options);\n break;\n case 'hand':\n startHandLogin(options);\n break;\n case 'auto':\n startAutoLogin(options);\n break;\n default:\n reject(new NeoFaceError('Modo de login inválido', ErrorType.VALIDATION_ERROR));\n }\n });\n}\n\n/**\n * Inicia o processo de login biométrico com fallback para email e senha se não houver match (assumindo purpose LOGIN).\n * @param applicationToken Token da aplicação\n * @param mode Modo de captura ('face', 'hand' ou 'auto')\n * @returns Promise que resolve com o resultado do login\n */\nexport function biometricLoginWithFallback(\n applicationToken: string,\n mode: 'face' | 'hand' | 'auto' = 'auto'\n): Promise<BiometricLoginResult> {\n return new Promise((resolve, reject) => {\n const options: BiometricLoginOptions = {\n applicationToken,\n onSuccess: resolve,\n onError: (error) => {\n if (error.type === ErrorType.LOGIN_FAILED) {\n // Abre modal de fallback para email e senha\n const fallbackOptions: EmailPasswordOptions = {\n applicationToken,\n onSuccess: resolve,\n onError: reject,\n title: 'Login Alternativo',\n subtitle: 'Biometria não reconhecida. Por favor, informe email e senha.'\n };\n const modal = new EmailPasswordModal(fallbackOptions);\n modal.open();\n } else {\n reject(error);\n }\n },\n };\n\n switch (mode) {\n case 'face':\n startFaceLogin(options);\n break;\n case 'hand':\n startHandLogin(options);\n break;\n case 'auto':\n startAutoLogin(options);\n break;\n default:\n reject(new NeoFaceError('Modo de login inválido', ErrorType.VALIDATION_ERROR));\n }\n });\n}\n","/**\n * Módulo de detecção automática de tipo biométrico\n * Utiliza face-api.js para detectar se a imagem contém face ou mão\n */\n\nexport interface DetectionResult {\n type: 'face' | 'hand' | 'unknown';\n confidence: number;\n details?: any;\n}\n\n/**\n * Detecta automaticamente o tipo biométrico na imagem\n * @param imageData Dados da imagem em base64\n * @returns Resultado da detecção com tipo e confiança\n */\nexport async function detectBiometricType(imageData: string): Promise<DetectionResult> {\n try {\n // Converte base64 para elemento de imagem\n const img = await loadImageFromBase64(imageData);\n \n // Tenta detectar face primeiro\n const faceResult = await detectFace(img);\n if (faceResult.confidence > 0.7) {\n return {\n type: 'face',\n confidence: faceResult.confidence,\n details: faceResult.details\n };\n }\n\n // Se não detectou face com alta confiança, tenta detectar mão\n const handResult = await detectHand(img);\n if (handResult.confidence > 0.6) {\n return {\n type: 'hand',\n confidence: handResult.confidence,\n details: handResult.details\n };\n }\n\n // Se não detectou nem face nem mão com confiança suficiente\n return {\n type: 'unknown',\n confidence: Math.max(faceResult.confidence, handResult.confidence)\n };\n\n } catch (error) {\n console.warn('Erro na detecção biométrica:', error);\n \n // Em caso de erro, assume face como padrão\n return {\n type: 'face',\n confidence: 0.5\n };\n }\n}\n\n/**\n * Detecta face na imagem usando face-api.js\n * Observação: evita usar descriptors quando o modelo de reconhecimento não está disponível,\n * usando apenas detecção e landmarks para reduzir dependências de arquivos.\n * @param img Elemento de imagem\n * @returns Resultado da detecção facial\n */\nasync function detectFace(img: HTMLImageElement): Promise<{ confidence: number; details?: any }> {\n try {\n // Verifica se face-api.js está disponível\n if (typeof window !== 'undefined' && (window as any).faceapi) {\n const faceapi = (window as any).faceapi;\n \n // Carrega os modelos se ainda não foram carregados\n await loadFaceApiModels();\n \n // Detecta faces na imagem usando TinyFaceDetector para evitar dependência do SsdMobilenet\n const tinyOptions = new faceapi.TinyFaceDetectorOptions({ inputSize: 320, scoreThreshold: 0.5 });\n const detections = await faceapi\n .detectAllFaces(img, tinyOptions)\n .withFaceLandmarks();\n\n if (detections && detections.length > 0) {\n // Retorna a confiança da melhor detecção\n const bestDetection = detections.reduce((best: any, current: any) => \n current.detection.score > best.detection.score ? current : best\n );\n \n return {\n confidence: bestDetection.detection.score,\n details: {\n faces: detections.length,\n landmarks: bestDetection.landmarks,\n box: bestDetection.detection.box\n }\n };\n }\n }\n\n // Se face-api.js não está disponível ou não detectou faces\n return { confidence: 0 };\n\n } catch (error) {\n console.warn('Erro na detecção facial:', error);\n return { confidence: 0 };\n }\n}\n\n/**\n * Detecta mão na imagem usando análise de contornos e características\n * @param img Elemento de imagem\n * @returns Resultado da detecção de mão\n */\nasync function detectHand(img: HTMLImageElement): Promise<{ confidence: number; details?: any }> {\n try {\n // Cria canvas para análise da imagem\n const canvas = document.createElement('canvas');\n const ctx = canvas.getContext('2d');\n \n if (!ctx) {\n return { confidence: 0 };\n }\n\n canvas.width = img.width;\n canvas.height = img.height;\n ctx.drawImage(img, 0, 0);\n\n // Análise básica de características de mão\n const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);\n const handFeatures = analyzeHandFeatures(imageData);\n\n return {\n confidence: handFeatures.confidence,\n details: handFeatures\n };\n\n } catch (error) {\n console.warn('Erro na detecção de mão:', error);\n return { confidence: 0 };\n }\n}\n\n/**\n * Analisa características que indicam presença de mão na imagem\n * @param imageData Dados da imagem do canvas\n * @returns Análise das características de mão\n */\nfunction analyzeHandFeatures(imageData: ImageData): { confidence: number; [key: string]: any } {\n const { data, width, height } = imageData;\n \n // Análise básica de cor de pele\n let skinPixels = 0;\n let totalPixels = 0;\n \n for (let i = 0; i < data.length; i += 4) {\n const r = data[i];\n const g = data[i + 1];\n const b = data[i + 2];\n \n // Verifica se o pixel tem cor de pele\n if (isSkinColor(r, g, b)) {\n skinPixels++;\n }\n totalPixels++;\n }\n\n const skinRatio = skinPixels / totalPixels;\n \n // Análise de formato (aspectos básicos)\n const aspectRatio = width / height;\n const isHandAspectRatio = aspectRatio > 0.6 && aspectRatio < 1.8;\n \n // Calcula confiança baseada nas características\n let confidence = 0;\n \n // Presença de cor de pele (peso: 40%)\n if (skinRatio > 0.15 && skinRatio < 0.7) {\n confidence += 0.4 * (skinRatio / 0.7);\n }\n \n // Proporção adequada para mão (peso: 30%)\n if (isHandAspectRatio) {\n confidence += 0.3;\n }\n \n // Tamanho adequado (peso: 30%)\n const imageSize = width * height;\n if (imageSize > 10000 && imageSize < 500000) {\n confidence += 0.3;\n }\n\n return {\n confidence: Math.min(confidence, 0.8), // Máximo 80% para detecção básica\n skinRatio,\n aspectRatio,\n imageSize,\n skinPixels,\n totalPixels\n };\n}\n\n/**\n * Verifica se uma cor RGB corresponde a cor de pele\n * @param r Componente vermelho\n * @param g Componente verde \n * @param b Componente azul\n * @returns true se for cor de pele\n */\nfunction isSkinColor(r: number, g: number, b: number): boolean {\n // Algoritmo básico para detecção de cor de pele\n // Baseado em ranges típicos de cor de pele humana\n \n // Converte para YCbCr para melhor detecção\n const y = 0.299 * r + 0.587 * g + 0.114 * b;\n const cb = -0.169 * r - 0.331 * g + 0.5 * b + 128;\n const cr = 0.5 * r - 0.419 * g - 0.081 * b + 128;\n \n // Ranges para cor de pele em YCbCr\n return (\n y > 80 && y < 255 &&\n cb > 85 && cb < 135 &&\n cr > 135 && cr < 180\n );\n}\n\n/**\n * Carrega uma imagem a partir de dados base64\n * @param base64Data Dados da imagem em base64\n * @returns Promise que resolve com o elemento de imagem\n */\nfunction loadImageFromBase64(base64Data: string): Promise<HTMLImageElement> {\n return new Promise((resolve, reject) => {\n const img = new Image();\n \n img.onload = () => resolve(img);\n img.onerror = () => reject(new Error('Erro ao carregar imagem'));\n \n // Adiciona prefixo se necessário\n const dataUrl = base64Data.startsWith('data:') \n ? base64Data \n : `data:image/jpeg;base64,${base64Data}`;\n \n img.src = dataUrl;\n });\n}\n\n/**\n * Carrega os modelos do face-api.js se ainda não foram carregados.\n * Evita carregar o modelo de reconhecimento facial (faceRecognitionNet) quando\n * os arquivos não estão presentes, prevenindo erros 404 e \"load model before inference\".\n */\nasync function loadFaceApiModels(): Promise<void> {\n if (typeof window === 'undefined' || !(window as any).faceapi) {\n return;\n }\n\n const faceapi = (window as any).faceapi;\n \n // Verifica se os modelos já foram carregados\n if (faceapi.nets.tinyFaceDetector.isLoaded) {\n return;\n }\n\n try {\n // Carrega modelos básicos para detecção\n await Promise.all([\n faceapi.nets.tinyFaceDetector.loadFromUri('./models'),\n faceapi.nets.faceLandmark68Net.loadFromUri('./models')\n ]);\n\n // Não carregamos faceRecognitionNet por padrão para evitar 404 e dependência de SsdMobilenet\n } catch (error) {\n console.warn('Não foi possível carregar modelos do face-api.js:', error);\n // Continua sem os modelos - usará detecção básica\n }\n}\n\n/**\n * Inicializa o sistema de detecção biométrica\n * Carrega bibliotecas e modelos necessários\n */\nexport async function initializeBiometricDetection(): Promise<void> {\n try {\n // Tenta carregar face-api.js se estiver disponível\n if (typeof window !== 'undefined') {\n await loadFaceApiModels();\n }\n } catch (error) {\n console.warn('Inicialização da detecção biométrica com limitações:', error);\n }\n}\n\n/**\n * Verifica se o sistema de detecção está disponível\n * @returns true se a detecção avançada está disponível\n */\nexport function isAdvancedDetectionAvailable(): boolean {\n return typeof window !== 'undefined' && \n (window as any).faceapi && \n (window as any).faceapi.nets.tinyFaceDetector.isLoaded;\n}","/**\n * Modal de captura para fluxo de Onboarding\n * Responsável por abrir a câmera, capturar uma foto de rosto e uma foto de documento\n * e retornar ambas imagens como Blobs para posterior envio ao backend.\n */\nexport class OnboardingCaptureModal {\n private overlay: HTMLDivElement | null = null;\n private video: HTMLVideoElement | null = null;\n private canvas: HTMLCanvasElement | null = null;\n private stream: MediaStream | null = null;\n private step: 'face' | 'document' = 'face';\n private countdownSeconds: number;\n private title?: string;\n private subtitle?: string;\n\n private faceBlob: Blob | null = null;\n private documentBlob: Blob | null = null;\n\n constructor(options?: { countdown?: number; title?: string; subtitle?: string }) {\n this.countdownSeconds = options?.countdown ?? 3;\n this.title = options?.title;\n this.subtitle = options?.subtitle;\n }\n\n /**\n * Abre o modal, inicia câmera e gerencia o fluxo de captura dos dois passos.\n * Retorna uma promessa resolvida com os Blobs de face e documento.\n */\n async open(): Promise<{ face: Blob; document: Blob }> {\n await this.createDom();\n await this.startCamera();\n\n // Captura de rosto com contagem regressiva\n await this.runFaceCaptureCountdown();\n this.faceBlob = await this.captureFrame();\n\n // Avança para captura de documento\n this.step = 'document';\n this.updateTexts('Captura do Documento', 'Posicione seu documento visível e clique em \"Capturar\"');\n\n // Espera clique do usuário para capturar documento\n await this.waitForUserCaptureClick();\n this.documentBlob = await this.captureFrame();\n\n // Fecha modal e libera recursos\n await this.close();\n\n if (!this.faceBlob || !this.documentBlob) {\n throw new Error('Falha na captura das imagens de onboarding');\n }\n\n return { face: this.faceBlob, document: this.documentBlob };\n }\n\n /**\n * Cria estrutura de DOM do modal e elementos de UI.\n */\n private async createDom(): Promise<void> {\n const overlay = document.createElement('div');\n overlay.className = 'neofaceid-modal-overlay';\n overlay.innerHTML = `\n <div class=\"neofaceid-modal\">\n <div class=\"neofaceid-modal-header\">\n <h2 class=\"neofaceid-title\">${this.title ?? 'Onboarding NeoFaceID'}</h2>\n <button class=\"neofaceid-close-btn\" aria-label=\"Fechar\">×</button>\n </div>\n <div class=\"neofaceid-modal-body\">\n <p class=\"neofaceid-subtitle\">${this.subtitle ?? 'Vamos capturar sua face e documento'}</p>\n <div class=\"neofaceid-camera-container\">\n <video class=\"neofaceid-video\" autoplay playsinline></video>\n <canvas class=\"neofaceid-canvas\"></canvas>\n <div class=\"neofaceid-overlay\">\n <div class=\"neofaceid-frame\"></div>\n <div class=\"neofaceid-countdown\" style=\"display:none\"><span class=\"neofaceid-countdown-number\">${this.countdownSeconds}</span></div>\n </div>\n </div>\n <div class=\"neofaceid-status\"><p class=\"neofaceid-status-text\">Posicione seu rosto no quadro</p></div>\n </div>\n <div class=\"neofaceid-modal-footer\">\n <button class=\"neofaceid-cancel-btn\">Cancelar</button>\n <button class=\"neofaceid-capture-btn\" disabled>Capturar</button>\n </div>\n </div>\n `;\n\n const style = document.createElement('style');\n style.textContent = `\n .neofaceid-modal-overlay { position: fixed; inset: 0; background: rgba(0,0,0,0.6); display:flex; align-items:center; justify-content:center; z-index: 9999; }\n .neofaceid-modal { background:#fff; border-radius:12px; width: 92%; max-width: 640px; box-shadow: 0 20px 60px rgba(0,0,0,0.3); overflow:hidden; }\n .neofaceid-modal-header { padding: 16px 20px; border-bottom: 1px solid #eee; display:flex; align-items:center; justify-content:space-between; }\n .neofaceid-title { margin:0; font-size: 20px; color:#333; }\n .neofaceid-close-btn { background:none; border:none; font-size:24px; cursor:pointer; color:#666; }\n .neofaceid-modal-body { padding: 16px 20px; }\n .neofaceid-subtitle { margin: 0 0 12px 0; color:#666; }\n .neofaceid-camera-container { position: relative; background:#000; border-radius:8px; overflow:hidden; aspect-ratio: 4/3; margin-bottom: 12px; }\n .neofaceid-video { width:100%; height:100%; object-fit: cover; }\n .neofaceid-canvas { position:absolute; top:0; left:0; }\n .neofaceid-overlay { position:absolute; inset:0; display:flex; align-items:center; justify-content:center; }\n .neofaceid-frame { width: 200px; height: 200px; border: 3px solid #4CAF50; border-radius: 50%; box-shadow: 0 0 0 2px rgba(76,175,80,0.3); }\n .neofaceid-countdown { position:absolute; top:16px; right:16px; background: rgba(0,0,0,0.7); color:#fff; width: 48px; height:48px; display:flex; align-items:center; justify-content:center; border-radius:50%; }\n .neofaceid-modal-footer { padding: 16px 20px; border-top: 1px solid #eee; display:flex; gap: 8px; justify-content:flex-end; }\n .neofaceid-cancel-btn { background:#f5f5f5; color:#666; border:none; border-radius:6px; padding:10px 18px; cursor:pointer; }\n .neofaceid-capture-btn { background:#4CAF50; color:#fff; border:none; border-radius:6px; padding:10px 18px; cursor:pointer; }\n .neofaceid-capture-btn:disabled { background:#ccc; cursor:not-allowed; }\n `;\n\n document.head.appendChild(style);\n document.body.appendChild(overlay);\n this.overlay = overlay as HTMLDivElement;\n\n this.video = this.overlay.querySelector('.neofaceid-video') as HTMLVideoElement;\n this.canvas = this.overlay.querySelector('.neofaceid-canvas') as HTMLCanvasElement;\n\n const closeBtn = this.overlay.querySelector('.neofaceid-close-btn') as HTMLButtonElement;\n const cancelBtn = this.overlay.querySelector('.neofaceid-cancel-btn') as HTMLButtonElement;\n closeBtn.onclick = () => this.close();\n cancelBtn.onclick = () => this.close();\n\n const captureBtn = this.overlay.querySelector('.neofaceid-capture-btn') as HTMLButtonElement;\n // No primeiro passo (face) o botão fica desabilitado; no segundo é usado manualmente\n captureBtn.disabled = true;\n }\n\n /**\n * Inicia a câmera do usuário com resolução adequada.\n */\n private async startCamera(): Promise<void> {\n const constraints: MediaStreamConstraints = {\n video: { facingMode: 'user', width: { ideal: 1280 }, height: { ideal: 720 } },\n audio: false,\n };\n this.stream = await navigator.mediaDevices.getUserMedia(constraints);\n if (!this.video) throw new Error('Elemento de vídeo não encontrado');\n this.video.srcObject = this.stream;\n await this.video.play();\n }\n\n /**\n * Executa a contagem regressiva e atualiza textos para captura de rosto.\n */\n private async runFaceCaptureCountdown(): Promise<void> {\n if (!this.overlay) return;\n const countdownEl = this.overlay.querySelector('.neofaceid-countdown') as HTMLDivElement;\n const countdownNumberEl = this.overlay.querySelector('.neofaceid-countdown-number') as HTMLSpanElement;\n const statusText = this.overlay.querySelector('.neofaceid-status-text') as HTMLParagraphElement;\n countdownEl.style.display = 'flex';\n statusText.textContent = 'Prepare-se! Capturando seu rosto...';\n\n let remaining = this.countdownSeconds;\n countdownNumberEl.textContent = String(remaining);\n await new Promise<void>((resolve) => {\n const interval = setInterval(() => {\n remaining -= 1;\n countdownNumberEl.textContent = String(remaining);\n if (remaining <= 0) {\n clearInterval(interval);\n countdownEl.style.display = 'none';\n resolve();\n }\n }, 1000);\n });\n }\n\n /**\n * Aguarda o clique do usuário no botão \"Capturar\" (para documento).\n */\n private async waitForUserCaptureClick(): Promise<void> {\n if (!this.overlay) return;\n const captureBtn = this.overlay.querySelector('.neofaceid-capture-btn') as HTMLButtonElement;\n const statusText = this.overlay.querySelector('.neofaceid-status-text') as HTMLParagraphElement;\n captureBtn.disabled = false;\n statusText.textContent = 'Clique em \"Capturar\" quando o documento estiver visível';\n await new Promise<void>((resolve) => {\n const handler = () => {\n captureBtn.removeEventListener('click', handler);\n resolve();\n };\n captureBtn.addEventListener('click', handler);\n });\n captureBtn.disabled = true;\n }\n\n /**\n * Captura o frame atual do vídeo e retorna como Blob JPEG.\n */\n private async captureFrame(): Promise<Blob> {\n if (!this.video || !this.canvas) throw new Error('Elementos de captura não encontrados');\n const ctx = this.canvas.getContext('2d');\n if (!ctx) throw new Error('Falha ao obter contexto do canvas');\n\n this.canvas.width = this.video.videoWidth;\n this.canvas.height = this.video.videoHeight;\n ctx.drawImage(this.video, 0, 0, this.canvas.width, this.canvas.height);\n\n const blob: Blob = await new Promise((resolve, reject) => {\n this.canvas!.toBlob((b) => (b ? resolve(b) : reject(new Error('Falha ao gerar imagem'))), 'image/jpeg', 0.92);\n });\n return blob;\n }\n\n /**\n * Atualiza título e subtítulo do modal conforme o passo.\n */\n private updateTexts(title: string, subtitle: string): void {\n if (!this.overlay) return;\n const titleEl = this.overlay.querySelector('.neofaceid-title') as HTMLHeadingElement;\n const subtitleEl = this.overlay.querySelector('.neofaceid-subtitle') as HTMLParagraphElement;\n const frameEl = this.overlay.querySelector('.neofaceid-frame') as HTMLDivElement;\n titleEl.textContent = title;\n subtitleEl.textContent = subtitle;\n // Para documento, usa moldura retangular para ajudar enquadramento\n if (this.step === 'document') {\n frameEl.style.borderRadius = '8px';\n frameEl.style.width = '280px';\n frameEl.style.height = '180px';\n }\n }\n\n /**\n * Fecha o modal e libera a câmera.\n */\n private async close(): Promise<void> {\n if (this.stream) {\n this.stream.getTracks().forEach((t) => t.stop());\n this.stream = null;\n }\n if (this.overlay) {\n this.overlay.remove();\n this.overlay = null;\n }\n }\n}","import { NeoFaceError, ErrorType } from './errors';\nimport { recognizeByPurpose, type SessionData } from './api';\n\nexport interface AuthorizationResult {\n success: boolean;\n authorizationHash?: string;\n name?: string;\n birthDate?: string;\n cpf?: string;\n confidenceScore?: number;\n message?: string;\n}\n\nexport interface AuthorizationOptions {\n onProgress?: (step: number, totalSteps: number, instruction: string) => void;\n onPhotoTaken?: (photoNumber: number, blob: Blob) => void;\n captureDelay?: number; // Delay between captures in ms (default: 2000)\n}\n\nconst PHOTO_INSTRUCTIONS = [\n 'Olhe diretamente para a câmera',\n 'Vire levemente a cabeça para a esquerda',\n 'Vire levemente a cabeça para a direita',\n 'Mostre o polegar para cima (👍) e olhe para a câmera',\n];\n\nconst TOTAL_PHOTOS = 4;\n\n/**\n * NEO-108: Authorization operation with multi-photo capture and gesture detection\n * \n * This function captures 4 sequential photos for authorization:\n * - Photo 1: Front face\n * - Photo 2: Left profile\n * - Photo 3: Right profile \n * - Photo 4: Thumbs up gesture with front face\n * \n * @param applicationToken The application token for authentication\n * @param cpf The CPF of the person to authorize\n * @param options Optional configuration for progress callbacks and delays\n * @returns Promise with authorization result\n * @throws NeoFaceError if capture or recognition fails\n */\nexport const authorizeOperation = async (\n applicationToken: string,\n cpf: string,\n options?: AuthorizationOptions\n): Promise<AuthorizationResult> => {\n if (!applicationToken) {\n throw new NeoFaceError('Application token is required', ErrorType.VALIDATION_ERROR);\n }\n\n if (!cpf) {\n throw new NeoFaceError('CPF is required', ErrorType.VALIDATION_ERROR);\n }\n\n // Validate CPF format (basic validation)\n const cpfClean = cpf.replace(/\\D/g, '');\n if (cpfClean.length !== 11) {\n throw new NeoFaceError('Invalid CPF format', ErrorType.VALIDATION_ERROR);\n }\n\n const capturedPhotos: Blob[] = [];\n const captureDelay = options?.captureDelay || 2000;\n\n try {\n // Check camera availability\n if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {\n throw new NeoFaceError('Camera not available', ErrorType.NO_CAMERA);\n }\n\n // Request camera access\n const stream = await navigator.mediaDevices.getUserMedia({\n video: {\n facingMode: 'user',\n width: { ideal: 1280 },\n height: { ideal: 720 },\n },\n });\n\n // Create video element\n const video = document.createElement('video');\n video.srcObject = stream;\n video.autoplay = true;\n video.playsInline = true;\n\n // Wait for video to be ready\n await new Promise<void>((resolve) => {\n video.onloadedmetadata = () => {\n video.play();\n resolve();\n };\n });\n\n // Create canvas for photo capture\n const canvas = document.createElement('canvas');\n canvas.width = video.videoWidth;\n canvas.height = video.videoHeight;\n const ctx = canvas.getContext('2d');\n\n if (!ctx) {\n throw new NeoFaceError('Failed to get canvas context', ErrorType.CAPTURE_ERROR);\n }\n\n // Capture photos with instructions\n for (let i = 0; i < TOTAL_PHOTOS; i++) {\n const instruction = PHOTO_INSTRUCTIONS[i];\n\n // Report progress\n if (options?.onProgress) {\n options.onProgress(i + 1, TOTAL_PHOTOS, instruction);\n }\n\n // Wait for delay between captures\n if (i > 0) {\n await new Promise((resolve) => setTimeout(resolve, captureDelay));\n }\n\n // Capture photo\n ctx.drawImage(video, 0, 0, canvas.width, canvas.height);\n\n // Convert to blob\n const blob = await new Promise<Blob>((resolve, reject) => {\n canvas.toBlob(\n (blob) => {\n if (blob) {\n resolve(blob);\n } else {\n reject(new Error('Failed to capture photo'));\n }\n },\n 'image/jpeg',\n 0.9\n );\n });\n\n capturedPhotos.push(blob);\n\n // Report photo taken\n if (options?.onPhotoTaken) {\n options.onPhotoTaken(i + 1, blob);\n }\n }\n\n // Stop camera\n stream.getTracks().forEach((track) => track.stop());\n\n // Send to backend for authorization\n // Note: Currently sends only the last photo (with gesture) as the main photo\n // The backend should be updated to accept all 4 photos\n const lastPhoto = capturedPhotos[TOTAL_PHOTOS - 1]; // Photo with thumbs up\n\n const sessionData: SessionData = {\n cpf: cpfClean,\n email: '', // Not required for authorization\n sessionId: `auth_${Date.now()}_${Math.random().toString(36).substring(7)}`,\n };\n\n // Call recognizeByPurpose with AUTHORIZATION purpose\n const recognitionResult = await recognizeByPurpose(\n lastPhoto,\n applicationToken,\n 'AUTHORIZATION',\n 0.8, // Confidence threshold\n undefined, // No signature for now\n sessionData\n );\n\n if (!recognitionResult.success) {\n throw new NeoFaceError(\n 'Authorization failed: Person not recognized',\n ErrorType.RECOGNITION_FAILED\n );\n }\n\n // Return authorization result\n // NOTE: authorizationHash should come from backend but it's not implemented yet\n return {\n success: true,\n authorizationHash: sessionData.sessionId, // TEMPORARY: Using sessionId as hash\n name: recognitionResult.personName,\n birthDate: undefined, // Should come from backend\n cpf: recognitionResult.cpf || cpfClean,\n confidenceScore: recognitionResult.confidenceScore,\n message: 'Authorization successful',\n };\n } catch (error) {\n if (error instanceof NeoFaceError) {\n throw error;\n }\n if (error instanceof Error) {\n throw new NeoFaceError(error.message, ErrorType.CAPTURE_ERROR);\n }\n throw new NeoFaceError('Unknown error during authorization', ErrorType.UNKNOWN);\n }\n};\n","/**\n * Modal de captura de documento para validação de identidade\n * Suporta RG, CNH e CPF com etapas de frente e verso quando aplicável\n */\n\nexport type DocumentType = 'RG' | 'CNH' | 'CPF';\n\nexport interface DocumentCaptureOptions {\n title?: string;\n subtitle?: string;\n useBackCamera?: boolean;\n onCancel?: () => void;\n // Se fornecido, pula a tela de seleção\n preSelectedDocument?: DocumentType;\n}\n\nexport interface DocumentCaptureResult {\n success: boolean;\n documentType?: DocumentType;\n frontImage: Blob | null;\n backImage: Blob | null;\n error?: string;\n}\n\ninterface DocumentInfo {\n name: string;\n icon: string;\n hasBack: boolean;\n frontLabel: string;\n backLabel?: string;\n}\n\nconst DOCUMENT_INFO: Record<DocumentType, DocumentInfo> = {\n RG: {\n name: 'RG / Carteira de Identidade',\n 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>',\n hasBack: true,\n frontLabel: 'Frente do RG',\n backLabel: 'Verso do RG',\n },\n CNH: {\n name: 'CNH / Carteira de Motorista',\n 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>',\n hasBack: true,\n frontLabel: 'Frente da CNH',\n backLabel: 'Verso da CNH',\n },\n CPF: {\n name: 'CPF',\n 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>',\n hasBack: false,\n frontLabel: 'Cartão CPF',\n },\n};\n\ntype CaptureStep = 'select' | 'front' | 'preview-front' | 'back' | 'preview-back' | 'complete';\n\nexport class DocumentCaptureModal {\n private overlay: HTMLDivElement | null = null;\n private video: HTMLVideoElement | null = null;\n private canvas: HTMLCanvasElement | null = null;\n private stream: MediaStream | null = null;\n private resolvePromise: ((result: DocumentCaptureResult) => void) | null = null;\n private options: DocumentCaptureOptions;\n\n private selectedDocument: DocumentType | null = null;\n private currentStep: CaptureStep = 'select';\n private frontBlob: Blob | null = null;\n private backBlob: Blob | null = null;\n\n constructor(options?: DocumentCaptureOptions) {\n this.options = {\n title: options?.title ?? 'Validação de Identidade',\n subtitle: options?.subtitle ?? 'Selecione o tipo de documento',\n useBackCamera: options?.useBackCamera ?? true,\n onCancel: options?.onCancel,\n preSelectedDocument: options?.preSelectedDocument,\n };\n }\n\n async open(): Promise<DocumentCaptureResult> {\n return new Promise((resolve) => {\n this.resolvePromise = resolve;\n\n if (this.options.preSelectedDocument) {\n this.selectedDocument = this.options.preSelectedDocument;\n this.currentStep = 'front';\n }\n\n this.render();\n });\n }\n\n private render(): void {\n if (!this.overlay) {\n this.createOverlay();\n }\n\n const content = this.overlay?.querySelector('.neoface-doc-content');\n if (!content) return;\n\n switch (this.currentStep) {\n case 'select':\n content.innerHTML = this.renderSelectStep();\n this.bindSelectEvents();\n break;\n case 'front':\n case 'back':\n content.innerHTML = this.renderCaptureStep();\n this.bindCaptureEvents();\n this.startCamera();\n break;\n case 'preview-front':\n case 'preview-back':\n content.innerHTML = this.renderPreviewStep();\n this.bindPreviewEvents();\n break;\n }\n }\n\n private createOverlay(): void {\n const overlay = document.createElement('div');\n overlay.className = 'neoface-doc-overlay';\n overlay.innerHTML = `\n <div class=\"neoface-doc-modal\">\n <div class=\"neoface-doc-content\"></div>\n </div>\n `;\n\n // Inject styles\n if (!document.getElementById('neoface-doc-styles-v2')) {\n const style = document.createElement('style');\n style.id = 'neoface-doc-styles-v2';\n style.textContent = this.getStyles();\n document.head.appendChild(style);\n }\n\n document.body.appendChild(overlay);\n this.overlay = overlay;\n }\n\n private renderSelectStep(): string {\n return `\n <div class=\"neoface-doc-header\">\n <div class=\"neoface-doc-header-left\">\n <h2 class=\"neoface-doc-title\">${this.options.title}</h2>\n <p class=\"neoface-doc-subtitle\">${this.options.subtitle}</p>\n </div>\n <button class=\"neoface-doc-close-btn\" aria-label=\"Fechar\">\n <svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M18 6L6 18M6 6l12 12\"/>\n </svg>\n </button>\n </div>\n \n <div class=\"neoface-doc-body\">\n <div class=\"neoface-doc-select-grid\">\n ${Object.entries(DOCUMENT_INFO).map(([type, info]) => `\n <button class=\"neoface-doc-select-option\" data-type=\"${type}\">\n <span class=\"neoface-doc-select-icon\">${info.icon}</span>\n <span class=\"neoface-doc-select-name\">${info.name}</span>\n <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>\n </button>\n `).join('')}\n </div>\n \n <div class=\"neoface-doc-tips\">\n <div class=\"neoface-doc-tip\">\n <span>Garanta boa iluminação e que todos os dados estejam legíveis</span>\n </div>\n </div>\n </div>\n \n ${this.renderBranding()}\n `;\n }\n\n private renderCaptureStep(): string {\n const docInfo = this.selectedDocument ? DOCUMENT_INFO[this.selectedDocument] : null;\n if (!docInfo) return '';\n\n const isBack = this.currentStep === 'back';\n const label = isBack ? docInfo.backLabel : docInfo.frontLabel;\n const stepNumber = isBack ? 2 : 1;\n const totalSteps = docInfo.hasBack ? 2 : 1;\n\n return `\n <div class=\"neoface-doc-header\">\n <div class=\"neoface-doc-header-left\">\n <div class=\"neoface-doc-step-indicator\">\n <span class=\"neoface-doc-step-badge\">${stepNumber}/${totalSteps}</span>\n <span class=\"neoface-doc-step-label\">${label}</span>\n </div>\n </div>\n <button class=\"neoface-doc-close-btn\" aria-label=\"Fechar\">\n <svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M18 6L6 18M6 6l12 12\"/>\n </svg>\n </button>\n </div>\n \n <div class=\"neoface-doc-body\">\n <div class=\"neoface-doc-camera-container\">\n <video class=\"neoface-doc-video\" autoplay playsinline></video>\n <canvas class=\"neoface-doc-canvas\"></canvas>\n \n <div class=\"neoface-doc-guide\">\n <div class=\"neoface-doc-frame\">\n <div class=\"neoface-doc-corner neoface-doc-corner-tl\"></div>\n <div class=\"neoface-doc-corner neoface-doc-corner-tr\"></div>\n <div class=\"neoface-doc-corner neoface-doc-corner-bl\"></div>\n <div class=\"neoface-doc-corner neoface-doc-corner-br\"></div>\n <div class=\"neoface-doc-frame-label\">${label}</div>\n </div>\n </div>\n \n <div class=\"neoface-doc-loading\" style=\"display:none\">\n <div class=\"neoface-doc-spinner\"></div>\n </div>\n </div>\n \n <div class=\"neoface-doc-instructions\">\n <div class=\"neoface-doc-instruction\">\n <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>\n <span>Posicione o documento dentro do quadro</span>\n </div>\n <div class=\"neoface-doc-instruction\">\n <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>\n <span>Evite reflexos e sombras</span>\n </div>\n </div>\n </div>\n \n <div class=\"neoface-doc-footer\">\n <button class=\"neoface-doc-btn neoface-doc-btn-secondary neoface-doc-back-btn\">\n ← Voltar\n </button>\n <input type=\"file\" accept=\"image/*\" class=\"neoface-doc-file-input\" style=\"display:none\" />\n <button class=\"neoface-doc-btn neoface-doc-btn-secondary neoface-doc-upload-btn\">\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4\"/>\n <polyline points=\"17,8 12,3 7,8\"/>\n <line x1=\"12\" y1=\"3\" x2=\"12\" y2=\"15\"/>\n </svg>\n Anexar\n </button>\n <button class=\"neoface-doc-btn neoface-doc-btn-primary neoface-doc-capture-btn\">\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <circle cx=\"12\" cy=\"12\" r=\"10\"/>\n <circle cx=\"12\" cy=\"12\" r=\"4\"/>\n </svg>\n Capturar\n </button>\n </div>\n \n ${this.renderBranding()}\n `;\n }\n\n private renderPreviewStep(): string {\n const docInfo = this.selectedDocument ? DOCUMENT_INFO[this.selectedDocument] : null;\n if (!docInfo) return '';\n\n const isPreviewFront = this.currentStep === 'preview-front';\n const label = isPreviewFront ? docInfo.frontLabel : docInfo.backLabel;\n const isLastStep = !docInfo.hasBack || this.currentStep === 'preview-back';\n const stepNumber = isPreviewFront ? 1 : 2;\n const totalSteps = docInfo.hasBack ? 2 : 1;\n\n return `\n <div class=\"neoface-doc-header\">\n <div class=\"neoface-doc-header-left\">\n <div class=\"neoface-doc-step-indicator\">\n <span class=\"neoface-doc-step-badge\">${stepNumber}/${totalSteps}</span>\n <span class=\"neoface-doc-step-label\">Confirmar ${label}</span>\n </div>\n </div>\n <button class=\"neoface-doc-close-btn\" aria-label=\"Fechar\">\n <svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M18 6L6 18M6 6l12 12\"/>\n </svg>\n </button>\n </div>\n \n <div class=\"neoface-doc-body\">\n <div class=\"neoface-doc-preview-single\">\n <span class=\"neoface-doc-preview-label\">${label}</span>\n <img class=\"neoface-doc-preview-img neoface-doc-preview-current\" />\n </div>\n \n <div class=\"neoface-doc-preview-hint\">\n <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>\n <span>Verifique se todos os dados estão legíveis</span>\n </div>\n </div>\n \n <div class=\"neoface-doc-footer\">\n <button class=\"neoface-doc-btn neoface-doc-btn-secondary neoface-doc-retake-btn\">\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M1 4v6h6M23 20v-6h-6\"/>\n <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\"/>\n </svg>\n Refazer\n </button>\n <button class=\"neoface-doc-btn neoface-doc-btn-success neoface-doc-confirm-btn\">\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M20 6L9 17l-5-5\"/>\n </svg>\n ${isLastStep ? 'Confirmar e Enviar' : 'Próximo'}\n </button>\n </div>\n \n ${this.renderBranding()}\n `;\n }\n\n private renderBranding(): string {\n return `\n <div class=\"neoface-doc-branding\">\n <img src=\"/logo-icone-no-background.png\" \n alt=\"NeoFaceId\" class=\"neoface-doc-brand-icon\" \n onerror=\"this.style.display='none'\" />\n <span class=\"neoface-doc-brand-name-text\">NeoFaceId</span>\n <span class=\"neoface-doc-brand-text\">by</span>\n <span class=\"neoface-doc-brand-name\">OCTA</span>\n </div>\n `;\n }\n\n private bindSelectEvents(): void {\n const options = this.overlay?.querySelectorAll('.neoface-doc-select-option');\n options?.forEach(option => {\n option.addEventListener('click', () => {\n const type = option.getAttribute('data-type') as DocumentType;\n this.selectedDocument = type;\n this.currentStep = 'front';\n this.render();\n });\n });\n\n this.bindCloseEvent();\n }\n\n private bindCaptureEvents(): void {\n const captureBtn = this.overlay?.querySelector('.neoface-doc-capture-btn');\n const backBtn = this.overlay?.querySelector('.neoface-doc-back-btn');\n const uploadBtn = this.overlay?.querySelector('.neoface-doc-upload-btn');\n const fileInput = this.overlay?.querySelector('.neoface-doc-file-input') as HTMLInputElement;\n\n captureBtn?.addEventListener('click', () => this.handleCapture());\n backBtn?.addEventListener('click', () => this.handleBack());\n\n // Upload file handling\n uploadBtn?.addEventListener('click', () => fileInput?.click());\n fileInput?.addEventListener('change', (e) => this.handleFileUpload(e));\n\n this.bindCloseEvent();\n }\n\n private async handleFileUpload(event: Event): Promise<void> {\n const input = event.target as HTMLInputElement;\n const file = input.files?.[0];\n if (!file) return;\n\n // Show loading indicator\n const loadingEl = this.overlay?.querySelector('.neoface-doc-loading') as HTMLElement;\n if (loadingEl) loadingEl.style.display = 'flex';\n\n try {\n // Compress the uploaded image to avoid 413 errors\n const compressedBlob = await this.compressUploadedImage(file);\n\n if (this.currentStep === 'front') {\n this.frontBlob = compressedBlob;\n this.currentStep = 'preview-front';\n } else if (this.currentStep === 'back') {\n this.backBlob = compressedBlob;\n this.currentStep = 'preview-back';\n }\n\n this.stopCamera();\n this.render();\n } catch (error) {\n console.error('[DocumentCapture] Error compressing uploaded image:', error);\n // Fallback to original file\n const blob = new Blob([file], { type: file.type });\n if (this.currentStep === 'front') {\n this.frontBlob = blob;\n this.currentStep = 'preview-front';\n } else if (this.currentStep === 'back') {\n this.backBlob = blob;\n this.currentStep = 'preview-back';\n }\n this.stopCamera();\n this.render();\n }\n }\n\n private compressUploadedImage(file: File): Promise<Blob> {\n return new Promise((resolve, reject) => {\n const img = new Image();\n img.onload = () => {\n // Calculate new dimensions (max 1280px on longest side to avoid 413 errors)\n const MAX_SIZE = 1280;\n let width = img.width;\n let height = img.height;\n\n if (width > height && width > MAX_SIZE) {\n height = (height * MAX_SIZE) / width;\n width = MAX_SIZE;\n } else if (height > MAX_SIZE) {\n width = (width * MAX_SIZE) / height;\n height = MAX_SIZE;\n }\n\n const canvas = document.createElement('canvas');\n canvas.width = width;\n canvas.height = height;\n\n const ctx = canvas.getContext('2d');\n if (!ctx) {\n reject(new Error('Failed to get canvas context'));\n return;\n }\n\n ctx.drawImage(img, 0, 0, width, height);\n\n canvas.toBlob(\n blob => {\n if (blob) {\n console.log(`[DocumentCapture] Compressed image from ${(file.size / 1024).toFixed(0)}KB to ${(blob.size / 1024).toFixed(0)}KB`);\n resolve(blob);\n } else {\n reject(new Error('Failed to compress image'));\n }\n },\n 'image/jpeg',\n 0.75 // 75% quality - keeps file size manageable for server limits\n );\n };\n\n img.onerror = () => reject(new Error('Failed to load image'));\n img.src = URL.createObjectURL(file);\n });\n }\n\n private bindPreviewEvents(): void {\n const confirmBtn = this.overlay?.querySelector('.neoface-doc-confirm-btn');\n const retakeBtn = this.overlay?.querySelector('.neoface-doc-retake-btn');\n\n confirmBtn?.addEventListener('click', () => this.handleConfirm());\n retakeBtn?.addEventListener('click', () => this.handleRetake());\n\n // Load preview image for current step\n const previewImg = this.overlay?.querySelector('.neoface-doc-preview-current') as HTMLImageElement;\n if (previewImg) {\n const blob = this.currentStep === 'preview-front' ? this.frontBlob : this.backBlob;\n if (blob) previewImg.src = URL.createObjectURL(blob);\n }\n\n this.bindCloseEvent();\n }\n\n private bindCloseEvent(): void {\n const closeBtn = this.overlay?.querySelector('.neoface-doc-close-btn');\n closeBtn?.addEventListener('click', () => this.handleCancel());\n }\n\n private async startCamera(): Promise<void> {\n try {\n const constraints: MediaStreamConstraints = {\n video: {\n facingMode: this.options.useBackCamera ? 'environment' : 'user',\n width: { ideal: 1920 },\n height: { ideal: 1080 },\n },\n audio: false,\n };\n\n this.stream = await navigator.mediaDevices.getUserMedia(constraints);\n\n this.video = this.overlay?.querySelector('.neoface-doc-video') as HTMLVideoElement;\n this.canvas = this.overlay?.querySelector('.neoface-doc-canvas') as HTMLCanvasElement;\n\n if (this.video) {\n this.video.srcObject = this.stream;\n await this.video.play();\n }\n } catch (error) {\n console.error('[DocumentCapture] Camera error:', error);\n // Try fallback to front camera\n if (this.options.useBackCamera) {\n try {\n this.stream = await navigator.mediaDevices.getUserMedia({\n video: { facingMode: 'user' },\n audio: false,\n });\n if (this.video) {\n this.video.srcObject = this.stream;\n await this.video.play();\n }\n } catch (fallbackError) {\n this.resolvePromise?.({\n success: false,\n frontImage: null,\n backImage: null,\n error: 'Não foi possível acessar a câmera',\n });\n this.close();\n }\n }\n }\n }\n\n private stopCamera(): void {\n if (this.stream) {\n this.stream.getTracks().forEach(track => track.stop());\n this.stream = null;\n }\n }\n\n private async handleCapture(): Promise<void> {\n if (!this.video || !this.canvas) return;\n\n const loading = this.overlay?.querySelector('.neoface-doc-loading') as HTMLElement;\n if (loading) loading.style.display = 'flex';\n\n const ctx = this.canvas.getContext('2d');\n if (!ctx) return;\n\n // Dimensões do vídeo\n const videoWidth = this.video.videoWidth;\n const videoHeight = this.video.videoHeight;\n\n // Calcular área do enquadramento (mesmas proporções do CSS: 75% largura, 65% altura)\n const frameWidthPercent = 0.75;\n const frameHeightPercent = 0.65;\n\n const cropWidth = Math.floor(videoWidth * frameWidthPercent);\n const cropHeight = Math.floor(videoHeight * frameHeightPercent);\n\n // Centralizar o crop\n const cropX = Math.floor((videoWidth - cropWidth) / 2);\n const cropY = Math.floor((videoHeight - cropHeight) / 2);\n\n // Definir tamanho do canvas como a área cropada\n this.canvas.width = cropWidth;\n this.canvas.height = cropHeight;\n\n // Desenhar apenas a área do enquadramento\n ctx.drawImage(\n this.video,\n cropX, cropY, cropWidth, cropHeight, // Área de origem (crop)\n 0, 0, cropWidth, cropHeight // Área de destino (canvas inteiro)\n );\n\n try {\n const blob = await new Promise<Blob>((resolve, reject) => {\n this.canvas!.toBlob(\n (b) => (b ? resolve(b) : reject(new Error('Capture failed'))),\n 'image/jpeg',\n 0.92\n );\n });\n\n if (this.currentStep === 'front') {\n this.frontBlob = blob;\n this.stopCamera();\n this.currentStep = 'preview-front';\n this.render();\n } else if (this.currentStep === 'back') {\n this.backBlob = blob;\n this.stopCamera();\n this.currentStep = 'preview-back';\n this.render();\n }\n } catch (error) {\n console.error('[DocumentCapture] Capture error:', error);\n } finally {\n if (loading) loading.style.display = 'none';\n }\n }\n\n private handleBack(): void {\n this.stopCamera();\n\n if (this.currentStep === 'back') {\n this.currentStep = 'front';\n this.frontBlob = null;\n } else if (this.currentStep === 'front') {\n this.currentStep = 'select';\n this.selectedDocument = null;\n }\n\n this.render();\n }\n\n private handleRetake(): void {\n // Refaz apenas a captura atual\n if (this.currentStep === 'preview-front') {\n this.frontBlob = null;\n this.currentStep = 'front';\n } else if (this.currentStep === 'preview-back') {\n this.backBlob = null;\n this.currentStep = 'back';\n }\n this.render();\n }\n\n private handleConfirm(): void {\n const docInfo = this.selectedDocument ? DOCUMENT_INFO[this.selectedDocument] : null;\n\n if (this.currentStep === 'preview-front') {\n // Se tem verso, vai para captura do verso\n if (docInfo?.hasBack) {\n this.currentStep = 'back';\n this.render();\n } else {\n // Sem verso, finaliza\n this.resolvePromise?.({\n success: true,\n documentType: this.selectedDocument || undefined,\n frontImage: this.frontBlob,\n backImage: null,\n });\n this.close();\n }\n } else if (this.currentStep === 'preview-back') {\n // Confirmou verso, finaliza\n this.resolvePromise?.({\n success: true,\n documentType: this.selectedDocument || undefined,\n frontImage: this.frontBlob,\n backImage: this.backBlob,\n });\n this.close();\n }\n }\n\n private handleCancel(): void {\n this.options.onCancel?.();\n this.resolvePromise?.({\n success: false,\n frontImage: null,\n backImage: null,\n error: 'cancelled',\n });\n this.close();\n }\n\n private close(): void {\n this.stopCamera();\n if (this.overlay) {\n this.overlay.remove();\n this.overlay = null;\n }\n }\n\n private getStyles(): string {\n return `\n .neoface-doc-overlay {\n position: fixed;\n inset: 0;\n background: rgba(0, 0, 0, 0.85);\n backdrop-filter: blur(8px);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 10000;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n padding: 16px;\n }\n \n .neoface-doc-modal {\n background: linear-gradient(145deg, #1a1a2e 0%, #16213e 100%);\n border-radius: 24px;\n width: 100%;\n max-width: 520px;\n box-shadow: 0 25px 80px rgba(0, 0, 0, 0.6), 0 0 0 1px rgba(255,255,255,0.1);\n overflow: hidden;\n animation: neoface-doc-slideIn 0.3s ease;\n }\n \n @keyframes neoface-doc-slideIn {\n from { transform: translateY(20px) scale(0.98); opacity: 0; }\n to { transform: translateY(0) scale(1); opacity: 1; }\n }\n \n .neoface-doc-header {\n padding: 20px 24px;\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n border-bottom: 1px solid rgba(255, 255, 255, 0.08);\n }\n \n .neoface-doc-title {\n margin: 0 0 4px 0;\n font-size: 20px;\n font-weight: 600;\n color: #fff;\n }\n \n .neoface-doc-subtitle {\n margin: 0;\n font-size: 14px;\n color: rgba(255, 255, 255, 0.6);\n }\n \n .neoface-doc-step-indicator {\n display: flex;\n align-items: center;\n gap: 12px;\n }\n \n .neoface-doc-step-badge {\n background: linear-gradient(135deg, #00c8ff, #0099cc);\n color: #fff;\n font-size: 12px;\n font-weight: 600;\n padding: 4px 10px;\n border-radius: 20px;\n }\n \n .neoface-doc-step-label {\n font-size: 16px;\n font-weight: 500;\n color: #fff;\n }\n \n .neoface-doc-close-btn {\n background: transparent;\n border: none;\n padding: 4px;\n cursor: pointer;\n color: rgba(255, 255, 255, 0.5);\n transition: all 0.2s;\n }\n \n .neoface-doc-close-btn:hover {\n color: #fff;\n }\n \n .neoface-doc-body {\n padding: 20px 24px;\n }\n \n /* Select Step */\n .neoface-doc-select-grid {\n display: flex;\n flex-direction: column;\n gap: 12px;\n }\n \n .neoface-doc-select-option {\n display: flex;\n align-items: center;\n gap: 16px;\n padding: 16px 20px;\n background: rgba(255, 255, 255, 0.05);\n border: 2px solid rgba(255, 255, 255, 0.1);\n border-radius: 14px;\n cursor: pointer;\n transition: all 0.2s;\n text-align: left;\n }\n \n .neoface-doc-select-option:hover {\n background: rgba(0, 200, 255, 0.1);\n border-color: rgba(0, 200, 255, 0.4);\n transform: translateX(4px);\n }\n \n .neoface-doc-select-icon {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 44px;\n height: 44px;\n background: rgba(0, 200, 255, 0.15);\n border-radius: 12px;\n color: #00c8ff;\n }\n \n .neoface-doc-select-icon svg {\n width: 24px;\n height: 24px;\n }\n \n .neoface-doc-select-name {\n flex: 1;\n font-size: 15px;\n font-weight: 500;\n color: #fff;\n }\n \n .neoface-doc-select-arrow {\n color: rgba(255, 255, 255, 0.4);\n transition: transform 0.2s;\n }\n \n .neoface-doc-select-option:hover .neoface-doc-select-arrow {\n color: #00c8ff;\n transform: translateX(4px);\n }\n \n .neoface-doc-tips {\n margin-top: 20px;\n }\n \n .neoface-doc-tip {\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 14px 16px;\n background: rgba(0, 200, 255, 0.08);\n border: 1px solid rgba(0, 200, 255, 0.15);\n border-radius: 10px;\n color: rgba(255, 255, 255, 0.8);\n font-size: 13px;\n line-height: 1;\n text-align: center;\n }\n \n .neoface-doc-tip span {\n transform: translateY(-2px);\n }\n \n .neoface-doc-tip-icon {\n color: #00c8ff;\n flex-shrink: 0;\n }\n \n /* Camera */\n .neoface-doc-camera-container {\n position: relative;\n background: #000;\n border-radius: 16px;\n overflow: hidden;\n aspect-ratio: 4/3;\n }\n \n .neoface-doc-video {\n width: 100%;\n height: 100%;\n object-fit: cover;\n }\n \n .neoface-doc-canvas {\n display: none;\n }\n \n .neoface-doc-guide {\n position: absolute;\n inset: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n pointer-events: none;\n }\n \n .neoface-doc-frame {\n width: 75%;\n height: 65%;\n position: relative;\n border: 2px dashed rgba(0, 200, 255, 0.5);\n border-radius: 12px;\n background: rgba(0, 200, 255, 0.03);\n }\n \n .neoface-doc-corner {\n position: absolute;\n width: 28px;\n height: 28px;\n border-color: #00c8ff;\n border-style: solid;\n }\n \n .neoface-doc-corner-tl { top: -2px; left: -2px; border-width: 4px 0 0 4px; border-radius: 10px 0 0 0; }\n .neoface-doc-corner-tr { top: -2px; right: -2px; border-width: 4px 4px 0 0; border-radius: 0 10px 0 0; }\n .neoface-doc-corner-bl { bottom: -2px; left: -2px; border-width: 0 0 4px 4px; border-radius: 0 0 0 10px; }\n .neoface-doc-corner-br { bottom: -2px; right: -2px; border-width: 0 4px 4px 0; border-radius: 0 0 10px 0; }\n \n .neoface-doc-frame-label {\n position: absolute;\n bottom: -30px;\n left: 50%;\n transform: translateX(-50%);\n background: rgba(0, 0, 0, 0.7);\n color: #00c8ff;\n font-size: 12px;\n font-weight: 500;\n padding: 6px 14px;\n border-radius: 20px;\n white-space: nowrap;\n }\n \n .neoface-doc-loading {\n position: absolute;\n inset: 0;\n background: rgba(0, 0, 0, 0.7);\n display: none;\n align-items: center;\n justify-content: center;\n }\n \n .neoface-doc-spinner {\n width: 48px;\n height: 48px;\n border: 4px solid rgba(255, 255, 255, 0.2);\n border-top-color: #00c8ff;\n border-radius: 50%;\n animation: neoface-doc-spin 0.8s linear infinite;\n }\n \n @keyframes neoface-doc-spin {\n to { transform: rotate(360deg); }\n }\n \n .neoface-doc-instructions {\n display: flex;\n flex-direction: column;\n gap: 8px;\n margin-top: 16px;\n }\n \n .neoface-doc-instruction {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 10px 14px;\n background: rgba(255, 255, 255, 0.04);\n border-radius: 10px;\n font-size: 13px;\n color: rgba(255, 255, 255, 0.75);\n }\n \n .neoface-doc-instruction-icon {\n color: rgba(255, 255, 255, 0.5);\n flex-shrink: 0;\n }\n \n /* Preview */\n .neoface-doc-preview-single {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 12px;\n }\n \n .neoface-doc-preview-label {\n font-size: 12px;\n font-weight: 500;\n color: rgba(255, 255, 255, 0.6);\n text-transform: uppercase;\n letter-spacing: 0.5px;\n text-align: center;\n }\n \n .neoface-doc-preview-img {\n width: 75%;\n height: auto;\n border-radius: 12px;\n border: 2px solid rgba(0, 200, 255, 0.3);\n box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);\n }\n \n .neoface-doc-preview-hint {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 12px 16px;\n background: rgba(0, 200, 255, 0.08);\n border: 1px solid rgba(0, 200, 255, 0.15);\n border-radius: 10px;\n font-size: 13px;\n color: rgba(255, 255, 255, 0.8);\n margin-top: 8px;\n }\n \n .neoface-doc-preview-hint svg {\n color: #00c8ff;\n flex-shrink: 0;\n }\n \n /* Footer */\n .neoface-doc-footer {\n padding: 16px 24px;\n display: flex;\n gap: 12px;\n justify-content: flex-end;\n border-top: 1px solid rgba(255, 255, 255, 0.08);\n }\n \n .neoface-doc-btn {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 12px 20px;\n border-radius: 12px;\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.2s;\n border: none;\n }\n \n .neoface-doc-btn-primary {\n background: linear-gradient(135deg, #00c8ff 0%, #0099cc 100%);\n color: #fff;\n }\n \n .neoface-doc-btn-primary:hover {\n transform: translateY(-2px);\n box-shadow: 0 6px 20px rgba(0, 200, 255, 0.4);\n }\n \n .neoface-doc-btn-success {\n background: linear-gradient(135deg, #10b981 0%, #059669 100%);\n color: #fff;\n }\n \n .neoface-doc-btn-success:hover {\n transform: translateY(-2px);\n box-shadow: 0 6px 20px rgba(16, 185, 129, 0.4);\n }\n \n .neoface-doc-btn-secondary {\n background: rgba(255, 255, 255, 0.08);\n color: rgba(255, 255, 255, 0.8);\n }\n \n .neoface-doc-btn-secondary:hover {\n background: rgba(255, 255, 255, 0.12);\n color: #fff;\n }\n \n /* Branding */\n .neoface-doc-branding {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n padding: 16px 24px;\n border-top: 1px solid rgba(255, 255, 255, 0.06);\n }\n \n .neoface-doc-brand-icon {\n height: 22px;\n width: auto;\n }\n \n .neoface-doc-brand-name-text {\n font-size: 13px;\n font-weight: 600;\n color: rgba(255, 255, 255, 0.9);\n }\n \n .neoface-doc-brand-text {\n font-size: 12px;\n color: rgba(255, 255, 255, 0.5);\n }\n \n .neoface-doc-brand-name {\n font-size: 13px;\n font-weight: 700;\n color: #00c8ff;\n letter-spacing: 1px;\n }\n \n /* Mobile */\n @media (max-width: 500px) {\n .neoface-doc-modal {\n border-radius: 20px;\n }\n \n .neoface-doc-header,\n .neoface-doc-body,\n .neoface-doc-footer {\n padding: 16px 18px;\n }\n \n .neoface-doc-preview-grid.has-back {\n grid-template-columns: 1fr;\n }\n }\n `;\n }\n}\n\n/**\n * Função auxiliar para iniciar a captura de documento\n */\nexport const startDocumentCapture = async (\n options?: DocumentCaptureOptions\n): Promise<DocumentCaptureResult> => {\n const modal = new DocumentCaptureModal(options);\n return modal.open();\n};\n","import { NeoFaceError, ErrorType } from './errors';\nimport {\n recognizeByPurpose,\n SessionData,\n registerApplication,\n ApplicationRegistrationData,\n ApplicationRegistrationResult,\n // PROOF_OF_LIFE imports\n recordFaceVideo,\n videoToBase64,\n startProofOfLife,\n pollTaskStatus,\n ProofOfLifeResult,\n PersonalDataItem,\n} from './api';\n\n// Re-export types for external usage\nexport type { ProofOfLifeResult, PersonalDataItem };\n\n/**\n * Configuration for NeoFaceID SDK initialization\n */\nexport interface NeoFaceIDConfig {\n appToken: string;\n baseUrl?: string;\n signature?: string;\n sessionData?: SessionData;\n}\n\n/**\n * Result of login recognition\n */\nexport interface LoginRecognitionResult {\n success: boolean;\n personName: string;\n email: string;\n cpf: string;\n signature?: string;\n sessionId?: string;\n confidenceScore?: number;\n accessToken?: string;\n // Note: person_id is NOT included for security reasons\n}\n\n/**\n * Options for capturing face frames\n */\nexport interface CaptureFaceFramesOptions {\n numFrames: number;\n livenessCheck: boolean;\n}\n\n/**\n * Options for login recognition\n */\nexport interface LoginRecognitionOptions {\n biometricData: Blob | Blob[];\n typeOfIdentification: 'FACE' | 'HAND';\n purpose:\n | 'LOGIN'\n | 'PROOF_OF_LIFE'\n | 'AUTHORIZATION'\n | 'SIMPLE_IDENTIFICATION'\n | 'SIMPLIFIED_REGISTRATION';\n confidenceThreshold?: number;\n}\n\n/**\n * Options for Proof of Life verification\n */\nexport interface ProofOfLifeOptions {\n /** Duration of video recording in milliseconds (default: 3000) */\n videoDurationMs?: number;\n /** Maximum polling attempts for task status (default: 60) */\n maxPollingAttempts?: number;\n /** Interval between polling attempts in ms (default: 1000) */\n pollingIntervalMs?: number;\n /** Callback for recording progress (0-100) */\n onRecordingProgress?: (progress: number) => void;\n /** Callback for task status changes */\n onTaskStatusChange?: (status: string, progress?: number) => void;\n}\n\n/**\n * NeoFaceID SDK class for biometric authentication\n *\n * This class provides a high-level API for biometric recognition,\n * supporting both internal use (without signature) and external integrations\n * (with signature and session data for systems like OpsPay).\n *\n * @example\n * ```typescript\n * // For external integrations (with signature)\n * const sdk = new NeoFaceID({\n * appToken: 'your-application-token',\n * baseUrl: 'https://core.neofaceid.com',\n * signature: signatureFromBackend,\n * sessionData: {\n * email: 'user@example.com',\n * cpf: '12345678901',\n * sessionId: 'session-uuid-123'\n * }\n * });\n *\n * // For internal use (without signature)\n * const sdk = new NeoFaceID({\n * appToken: 'your-application-token'\n * });\n * ```\n */\nexport class NeoFaceID {\n private appToken: string;\n private signature?: string;\n private sessionData?: SessionData;\n\n /**\n * Creates a new NeoFaceID instance\n * @param config Configuration object\n * @throws NeoFaceError if signature format is invalid\n */\n constructor(config: NeoFaceIDConfig) {\n this.appToken = config.appToken;\n this.signature = config.signature;\n this.sessionData = config.sessionData;\n\n // Validate signature format if provided\n if (this.signature && !this.validateSignatureFormat(this.signature)) {\n throw new NeoFaceError(\n 'Invalid signature format. Expected HMAC-SHA256 hex string (minimum 64 characters)',\n ErrorType.VALIDATION_ERROR\n );\n }\n\n // Validate session data if provided\n if (this.sessionData) {\n if (!this.sessionData.email || !this.sessionData.cpf || !this.sessionData.sessionId) {\n throw new NeoFaceError(\n 'Session data must include email, cpf, and sessionId',\n ErrorType.VALIDATION_ERROR\n );\n }\n }\n }\n\n /**\n * Validates the format of a signature\n * HMAC-SHA256 produces a 64-character hexadecimal string\n * @param signature The signature to validate\n * @returns true if valid, false otherwise\n */\n private validateSignatureFormat(signature: string): boolean {\n // HMAC-SHA256 produces a 64-character hex string\n // Allow longer strings in case of encoding variations\n return /^[a-f0-9]{64,}$/i.test(signature);\n }\n\n /**\n * Captures multiple face frames for biometric recognition\n * @param options Capture options\n * @returns Promise that resolves to an array of image blobs\n * @throws NeoFaceError if capture fails\n */\n async captureFaceFrames(options: CaptureFaceFramesOptions): Promise<Blob[]> {\n const { numFrames, livenessCheck } = options;\n\n if (numFrames < 1 || numFrames > 10) {\n throw new NeoFaceError(\n 'Number of frames must be between 1 and 10',\n ErrorType.VALIDATION_ERROR\n );\n }\n\n const frames: Blob[] = [];\n\n try {\n // Import dynamically to avoid circular dependencies\n const { captureFaceSilently } = await import('./utils/biometricLoginFlow');\n\n for (let i = 0; i < numFrames; i++) {\n // Capture a single frame\n const frame = await captureFaceSilently();\n frames.push(frame);\n\n // Add delay between frames for liveness detection\n // This helps ensure the person is actually present and not using a photo\n if (livenessCheck && i < numFrames - 1) {\n await new Promise(resolve => setTimeout(resolve, 300));\n }\n }\n\n return frames;\n } catch (error) {\n if (error instanceof NeoFaceError) {\n throw error;\n }\n throw new NeoFaceError(\n `Failed to capture face frames: ${error instanceof Error ? error.message : 'Unknown error'\n }`,\n ErrorType.CAPTURE_ERROR\n );\n }\n }\n\n /**\n * Performs login recognition using biometric data\n *\n * This method is designed for external integrations that require\n * signature validation and session management.\n *\n * @param options Recognition options\n * @returns Promise that resolves to recognition result\n * @throws NeoFaceError if recognition fails\n *\n * @example\n * ```typescript\n * const result = await sdk.loginRecognition({\n * biometricData: await sdk.captureFaceFrames({\n * numFrames: 5,\n * livenessCheck: true\n * }),\n * typeOfIdentification: 'FACE',\n * purpose: 'LOGIN',\n * confidenceThreshold: 0.8\n * });\n *\n * // Result includes signature and sessionId for callback validation\n * console.log(result.signature, result.sessionId);\n * ```\n */\n async loginRecognition(options: LoginRecognitionOptions): Promise<LoginRecognitionResult> {\n const { biometricData, purpose, confidenceThreshold = 0.8 } = options;\n\n // Convert biometric data to Blob array if needed\n const imageBlobs: Blob[] = Array.isArray(biometricData) ? biometricData : [biometricData];\n\n if (imageBlobs.length === 0) {\n throw new NeoFaceError(\n 'At least one biometric data frame is required',\n ErrorType.VALIDATION_ERROR\n );\n }\n\n // Use the first frame for recognition (or implement multi-frame processing)\n const primaryImage = imageBlobs[0];\n\n try {\n // Call recognizeByPurpose with signature and sessionData\n const result = await recognizeByPurpose(\n primaryImage,\n this.appToken,\n purpose,\n confidenceThreshold,\n this.signature,\n this.sessionData\n );\n\n if (!result.success) {\n throw new NeoFaceError('Recognition failed', ErrorType.RECOGNITION_FAILED);\n }\n\n // Ensure required fields are present\n if (!result.personName || !result.email || !result.cpf) {\n throw new NeoFaceError(\n 'Incomplete recognition result: missing personName, email, or cpf',\n ErrorType.RECOGNITION_FAILED\n );\n }\n\n return {\n success: true,\n personName: result.personName,\n email: result.email,\n cpf: result.cpf,\n signature: result.signature,\n sessionId: result.sessionId,\n confidenceScore: result.confidenceScore,\n accessToken: result.accessToken,\n };\n } catch (error) {\n if (error instanceof NeoFaceError) {\n throw error;\n }\n throw new NeoFaceError(\n `Login recognition failed: ${error instanceof Error ? error.message : 'Unknown error'}`,\n ErrorType.RECOGNITION_FAILED\n );\n }\n }\n\n /**\n * Register a new application for a consumer\n *\n * This method allows authenticated users to register new applications\n * that will receive their own app_token for API access.\n *\n * @param jwtToken JWT authentication token from logged user\n * @param consumerId Consumer ID (UUID) who will own the application\n * @param applicationData Application registration data\n * @returns Promise with registration result including app_token\n * @throws NeoFaceError if registration fails\n *\n * @example\n * ```typescript\n * const sdk = new NeoFaceID({\n * appToken: 'your-application-token'\n * });\n *\n * const result = await sdk.registerApplication(\n * userJwtToken,\n * consumerUuid,\n * {\n * applicationName: 'My New App',\n * domain: 'example.com',\n * acceptOnlyEmailWithSameDomain: true\n * }\n * );\n *\n * // Use the generated app_token for the new application\n * console.log('New App Token:', result.application.app_token);\n * ```\n */\n async registerApplication(\n jwtToken: string,\n consumerId: string,\n applicationData: ApplicationRegistrationData\n ): Promise<ApplicationRegistrationResult> {\n try {\n const result = await registerApplication(jwtToken, consumerId, applicationData);\n return result;\n } catch (error) {\n if (error instanceof NeoFaceError) {\n throw error;\n }\n throw new NeoFaceError(\n `Application registration failed: ${error instanceof Error ? error.message : 'Unknown error'\n }`,\n ErrorType.VALIDATION_ERROR\n );\n }\n }\n\n /**\n * Performs Proof of Life verification\n *\n * This method records a video from the camera, sends it to the backend\n * for liveness detection and face recognition, and returns the result\n * with personal data filtered by purpose.\n *\n * The process is asynchronous:\n * 1. Records video from the camera (default 3 seconds)\n * 2. Converts video to base64 and sends to backend\n * 3. Backend processes liveness detection and face recognition\n * 4. Polls for task completion\n * 5. Returns personal data of the identified person\n *\n * @param options Configuration options for the proof of life process\n * @returns Promise that resolves to ProofOfLifeResult\n * @throws NeoFaceError if verification fails\n *\n * @example\n * ```typescript\n * const sdk = new NeoFaceID({\n * appToken: 'your-application-token'\n * });\n *\n * const result = await sdk.proofOfLife({\n * videoDurationMs: 3000, // 3 seconds\n * onRecordingProgress: (progress) => {\n * console.log(`Recording: ${progress}%`);\n * },\n * onTaskStatusChange: (status, progress) => {\n * console.log(`Status: ${status}, Progress: ${progress}%`);\n * }\n * });\n *\n * if (result.success && result.isLive) {\n * console.log('Person verified:', result.personalData);\n * console.log('Liveness score:', result.livenessScore);\n * console.log('Face recognition score:', result.faceRecognitionScore);\n * }\n * ```\n */\n async proofOfLife(options: ProofOfLifeOptions = {}): Promise<ProofOfLifeResult> {\n const {\n videoDurationMs = 3000,\n maxPollingAttempts = 60,\n pollingIntervalMs = 1000,\n onRecordingProgress,\n onTaskStatusChange,\n } = options;\n\n try {\n // 1. Record video from camera\n const videoBlob = await recordFaceVideo({\n durationMs: videoDurationMs,\n onProgress: onRecordingProgress,\n });\n\n // 2. Convert video to base64\n const videoBase64 = await videoToBase64(videoBlob);\n\n // 3. Start proof of life process\n const startResult = await startProofOfLife(videoBase64, this.appToken);\n\n // Notify initial status\n if (onTaskStatusChange) {\n onTaskStatusChange('processing', 0);\n }\n\n // 4. Poll for task completion\n const taskResult = await pollTaskStatus(startResult.taskId, this.appToken, {\n maxAttempts: maxPollingAttempts,\n intervalMs: pollingIntervalMs,\n onStatusChange: onTaskStatusChange,\n });\n\n // 5. Build and return result\n if (!taskResult || !taskResult.success) {\n return {\n success: false,\n isLive: taskResult?.is_live || false,\n livenessScore: taskResult?.liveness_score || 0,\n faceRecognitionScore: taskResult?.face_recognition_score || 0,\n combinedConfidence: taskResult?.combined_confidence || 0,\n personId: taskResult?.person_id,\n personalData: taskResult?.personal_data || [],\n processingTime: taskResult?.processing_time || 0,\n historyId: startResult.historyId,\n taskId: startResult.taskId,\n error: taskResult?.error || 'Verification failed',\n message: taskResult?.message || 'Proof of life verification failed',\n };\n }\n\n return {\n success: true,\n isLive: taskResult.is_live || false,\n livenessScore: taskResult.liveness_score || 0,\n faceRecognitionScore: taskResult.face_recognition_score || 0,\n combinedConfidence: taskResult.combined_confidence || 0,\n personId: taskResult.person_id,\n personalData: taskResult.personal_data || [],\n processingTime: taskResult.processing_time || 0,\n historyId: startResult.historyId,\n taskId: startResult.taskId,\n message: 'Proof of life verification successful',\n };\n } catch (error) {\n if (error instanceof NeoFaceError) {\n throw error;\n }\n throw new NeoFaceError(\n `Proof of life failed: ${error instanceof Error ? error.message : 'Unknown error'}`,\n ErrorType.RECOGNITION_FAILED\n );\n }\n }\n}\n","/**\n * Versão atual do SDK NeoFace ID Web\n * Formato: MAJOR.MINOR.PATCH\n * \n * MAJOR: Incrementado quando há mudanças incompatíveis com versões anteriores\n * MINOR: Incrementado quando adicionamos funcionalidades mantendo compatibilidade\n * PATCH: Incrementado quando corrigimos bugs mantendo compatibilidade\n */\nexport const VERSION = '1.13.0';\n\n/**\n * Data de lançamento da versão atual\n */\nexport const RELEASE_DATE = '2026-02-20';\n\n/**\n * Histórico de versões\n */\nexport const CHANGELOG = {\n '1.8.8': {\n date: '2026-01-20',\n changes: [\n 'Removido fallback localhost:8000 - agora requer VITE_API_BASE_URL configurado',\n 'Adicionado erro explícito quando URL da API não está configurada',\n 'Melhoria na validação de configuração do SDK',\n ],\n },\n '1.8.7': {\n date: '2026-01-19',\n changes: [\n 'Correção de caminhos de assets (logos) para compatibilidade com Vite',\n 'Correção de typo no nome do arquivo do logo ícone',\n 'Otimização do fluxo de login biométrico e tratamento de erros de CORS',\n 'Remoção do prefixo /public/ para melhor suporte em build de produção',\n ],\n },\n '1.7.0': {\n date: '2025-12-21',\n changes: [\n 'NEO-104: Método registerApplication() para cadastro de aplicações',\n 'NEO-109: Método proofOfLife() para prova de vida com gravação de vídeo',\n 'NEO-149: Login biométrico com fluxo INTERNAL_RECOGNITION',\n 'DocumentCaptureModal: Modal de captura de documentos (frente/verso)',\n 'Suporte a upload de arquivo para documentos',\n 'Melhorias no BiometricRegistrationModal',\n 'Melhorias no fluxo de fallback para email/senha',\n ],\n },\n '1.6.1': {\n date: '2025-12-20',\n changes: [\n 'Correções e melhorias de estabilidade',\n 'Merge de funcionalidades anteriores para produção',\n ],\n },\n '1.5.0': {\n date: '2025-11-30',\n changes: [\n 'NEO-101: Adicionado método registerBiometric() para cadastro de biometria facial',\n 'NEO-108: Adicionado método authorizeOperation() com captura de 4 fotos e gesto de aprovação',\n 'Adicionado ErrorType.NO_CAMERA',\n 'Criada página de teste test-authorization.html',\n ],\n },\n '1.0.0': {\n date: '2023-06-15',\n changes: [\n 'Lançamento inicial do SDK',\n 'Captura facial com detecção automática',\n 'Verificação de vivacidade',\n 'Reconhecimento facial',\n ],\n },\n};","import React from 'react';\nimport { createRoot } from 'react-dom/client';\nimport { FaceCaptureModal } from './components/FaceCaptureModal';\nimport { BiometricRegistrationModal } from './components/BiometricRegistrationModal';\nimport { validateToken } from './api';\n\ninterface RecognitionResult {\n success: boolean;\n data: {\n faceId: string;\n confidence: number;\n };\n}\n\ninterface BiometricRegistrationResult {\n success: boolean;\n message: string;\n person_id?: string;\n user_id?: string;\n person_validated?: boolean;\n face_processing?: any;\n onboarding_link?: string;\n}\n\ninterface Callbacks {\n onSuccess(user: { name: string; email: string; documentId: string }): void;\n onError(code: string, message: string): void;\n}\n\ninterface BiometricRegistrationCallbacks {\n onSuccess(result: BiometricRegistrationResult): void;\n onError(code: string, message: string): void;\n}\n\nexport function start(applicationToken: string, callbacks: Callbacks): void {\n // Create a container for the modal\n const container = document.createElement('div');\n container.id = 'neoface-modal-container';\n document.body.appendChild(container);\n\n // Function to clean up resources\n const cleanup = () => {\n // Remove the container from the DOM\n if (document.body.contains(container)) {\n document.body.removeChild(container);\n }\n };\n\n // Validate the application token\n validateToken(applicationToken)\n .then(() => {\n // Render the modal\n const modalElement = React.createElement(FaceCaptureModal, {\n accessToken: applicationToken,\n onClose: cleanup,\n onSuccess: (result: RecognitionResult) => {\n // Clean up the modal\n cleanup();\n\n // Call the success callback with user data\n callbacks.onSuccess({\n name: result.data.faceId,\n email: result.data.faceId,\n documentId: result.data.faceId,\n });\n },\n });\n\n const root = createRoot(container);\n root.render(modalElement);\n })\n .catch((error: Error) => {\n // Clean up the container\n cleanup();\n\n // Call the error callback\n callbacks.onError('TOKEN_VALIDATION_ERROR', error.message || 'Failed to validate token');\n });\n}\n\n/**\n * Start biometric registration process with liveness detection\n * @param personData Person registration data (name, cpf, birth_date, email, password)\n * @param applicationToken Application token for authentication\n * @param callbacks Success and error callbacks\n */\nexport function startBiometricRegistration(\n personData: {\n name: string;\n birth_date: string;\n cpf: string;\n email: string;\n password: string;\n },\n applicationToken: string,\n callbacks: BiometricRegistrationCallbacks,\n options?: {\n useRealApi?: boolean;\n }\n): void {\n const container = document.createElement('div');\n container.id = 'neoface-biometric-registration-container';\n document.body.appendChild(container);\n\n // Guarda ref ao root para poder chamar unmount() corretamente\n let rootRef: ReturnType<typeof createRoot> | null = null;\n\n // cleanup: desmonta o React (dispara useEffect cleanups, para a câmera)\n // e só depois remove o container do DOM\n const cleanup = () => {\n if (rootRef) {\n rootRef.unmount();\n rootRef = null;\n }\n if (document.body.contains(container)) {\n document.body.removeChild(container);\n }\n };\n\n validateToken(applicationToken)\n .then(() => {\n const modalElement = React.createElement(BiometricRegistrationModal, {\n personData,\n applicationToken,\n useRealApi: options?.useRealApi ?? true,\n onClose: cleanup,\n onSuccess: (result: BiometricRegistrationResult) => {\n cleanup();\n callbacks.onSuccess(result);\n },\n onError: (error: string) => {\n cleanup();\n callbacks.onError('BIOMETRIC_REGISTRATION_ERROR', error);\n },\n });\n\n rootRef = createRoot(container);\n rootRef.render(modalElement);\n })\n .catch((error: Error) => {\n cleanup();\n callbacks.onError('TOKEN_VALIDATION_ERROR', error.message || 'Failed to validate token');\n });\n}\n\n/**\n * Start liveness capture process only (no registration)\n * @param applicationToken Application token for authentication\n * @param callbacks Success and error callbacks\n */\nexport function startLivenessCapture(\n applicationToken: string,\n callbacks: {\n onSuccess(photos: Blob[]): void;\n onError(code: string, message: string): void;\n onCancel?(): void;\n }\n): void {\n const container = document.createElement('div');\n container.id = 'neoface-liveness-capture-container';\n document.body.appendChild(container);\n\n let completed = false;\n // Guarda ref ao root para poder chamar unmount() corretamente\n let rootRef: ReturnType<typeof createRoot> | null = null;\n\n // cleanup: desmonta o React (dispara useEffect cleanups, para a câmera)\n // e só depois remove o container do DOM\n const cleanup = () => {\n if (rootRef) {\n rootRef.unmount();\n rootRef = null;\n }\n if (document.body.contains(container)) {\n document.body.removeChild(container);\n }\n };\n\n // Handler para o botão X: desmonta + notifica cancelamento\n const handleClose = () => {\n cleanup();\n if (!completed && callbacks.onCancel) {\n callbacks.onCancel();\n }\n };\n\n validateToken(applicationToken)\n .then(() => {\n const modalElement = React.createElement(BiometricRegistrationModal, {\n applicationToken,\n onClose: handleClose,\n onPhotosCaptured: (photos: Blob[]) => {\n completed = true;\n callbacks.onSuccess(photos);\n },\n onError: (error: string) => {\n cleanup();\n callbacks.onError('LIVENESS_CAPTURE_ERROR', error);\n },\n });\n\n rootRef = createRoot(container);\n rootRef.render(modalElement);\n })\n .catch((error: Error) => {\n cleanup();\n callbacks.onError('TOKEN_VALIDATION_ERROR', error.message || 'Failed to validate token');\n });\n}\n\n// Re-export all public APIs\nexport {\n validateToken,\n validateOnboardingToken,\n recognize,\n recognizeBiometric,\n simpleIdentification,\n recognizeByPurpose,\n loginWithBiometric,\n loginWithEmail,\n registerPersonWithoutFace,\n registerPersonWithBiometric,\n registerBiometric,\n identifyPerson,\n completeOnboarding,\n completeOnboardingWithData,\n type BiometricLoginResult,\n type OnboardingPersonData,\n} from './api';\nexport { FaceCaptureModal } from './components/FaceCaptureModal';\nexport { BiometricRegistrationModal } from './components/BiometricRegistrationModal';\nexport { BiometricCaptureModal } from './BiometricCaptureModal';\nexport { BiometricStatusOverlay, type OverlayStatus } from './components/BiometricStatusOverlay';\nexport { FallbackPrompt } from './components/FallbackPrompt';\nexport { EmailPasswordModal, type EmailPasswordOptions } from './EmailPasswordModal';\nexport {\n // Fluxos de login biométrico\n startFaceLogin,\n startHandLogin,\n startAutoLogin,\n biometricLogin,\n biometricLoginWithFallback,\n // Tipos\n type BiometricLoginOptions,\n} from './biometricLogin';\nexport {\n // Utilitários de performance\n preloadFaceDetectionModels,\n} from './utils/biometricLoginFlow';\nexport {\n detectBiometricType,\n initializeBiometricDetection,\n isAdvancedDetectionAvailable,\n type DetectionResult,\n} from './biometricDetection';\nexport { VERSION, RELEASE_DATE } from './version';\nexport { NeoFaceError, ErrorType } from './errors';\n// SDK Configuration - permite inicializar o SDK com ambiente específico\nexport {\n init,\n getBaseUrl,\n getEnvironment,\n getApplicationToken,\n isInitialized,\n getConfig,\n ENVIRONMENT_URLS,\n type Environment,\n type SDKInitOptions,\n} from './config';\nexport { startOnboarding, type StartOnboardingOptions } from './onboarding';\nexport { NeoFaceID } from './NeoFaceID';\nexport type {\n NeoFaceIDConfig,\n LoginRecognitionResult,\n CaptureFaceFramesOptions,\n LoginRecognitionOptions,\n ProofOfLifeOptions,\n ProofOfLifeResult,\n PersonalDataItem,\n} from './NeoFaceID';\nexport type {\n SessionData,\n ApplicationRegistrationData,\n ApplicationRegistrationResult,\n} from './api';\nexport {\n authorizeOperation,\n type AuthorizationResult,\n type AuthorizationOptions,\n} from './authorization';\nexport {\n DocumentCaptureModal,\n startDocumentCapture,\n type DocumentType,\n type DocumentCaptureOptions,\n type DocumentCaptureResult,\n} from './DocumentCaptureModal';\n","import { NeoFaceError, ErrorType } from './errors';\nimport { getOnboardingDetails, completeOnboarding, OnboardingLinkDetails } from './api';\nimport { OnboardingCaptureModal } from './OnboardingCaptureModal';\n\nexport interface StartOnboardingOptions {\n applicationToken: string;\n onboardingToken: string;\n countdown?: number;\n title?: string;\n subtitle?: string;\n onSuccess: (result: {\n success: boolean;\n message: string;\n person_id?: string;\n identity_data_id?: string;\n confidence_score?: number;\n processing_time?: number;\n details: OnboardingLinkDetails;\n }) => void;\n onError: (error: NeoFaceError) => void;\n onCancel?: () => void;\n}\n\n/**\n * Inicia o fluxo de onboarding: valida token, captura face e documento, conclui no backend.\n * Este método orquestra o processo completo e expõe callbacks de sucesso/erro.\n */\nexport const startOnboarding = async (options: StartOnboardingOptions): Promise<void> => {\n const { applicationToken, onboardingToken, countdown, title, subtitle, onSuccess, onError } = options;\n\n try {\n // 1) Valida e obtém detalhes do link de onboarding\n const details = await getOnboardingDetails(applicationToken, onboardingToken);\n if (!details?.is_valid) {\n throw new NeoFaceError('Link de onboarding inválido ou expirado', ErrorType.VALIDATION_ERROR);\n }\n\n // 2) Configura UI conforme propósito/fluxo (por enquanto sempre face + documento)\n const modal = new OnboardingCaptureModal({ countdown, title, subtitle });\n const { face, document } = await modal.open();\n\n // 3) Envia imagens para concluir onboarding\n const result = await completeOnboarding(applicationToken, onboardingToken, face, document);\n\n // 4) Callback de sucesso\n onSuccess({ ...result, details });\n } catch (err: any) {\n // Tratamento padronizado de erro\n if (err instanceof NeoFaceError) {\n onError(err);\n return;\n }\n const message = err?.message || 'Erro desconhecido no onboarding';\n onError(new NeoFaceError(message, ErrorType.UNKNOWN));\n }\n};"],"names":["m","require$$0","process","env","NODE_ENV","client","createRoot","hydrateRoot","i","__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED","c","o","usingClientEntryPoint","h","React","REACT_ELEMENT_TYPE","Symbol","for","REACT_PORTAL_TYPE","REACT_FRAGMENT_TYPE","REACT_STRICT_MODE_TYPE","REACT_PROFILER_TYPE","REACT_PROVIDER_TYPE","REACT_CONTEXT_TYPE","REACT_FORWARD_REF_TYPE","REACT_SUSPENSE_TYPE","REACT_SUSPENSE_LIST_TYPE","REACT_MEMO_TYPE","REACT_LAZY_TYPE","REACT_OFFSCREEN_TYPE","MAYBE_ITERATOR_SYMBOL","iterator","ReactSharedInternals","error","format","_len2","arguments","length","args","Array","_key2","level","stack","ReactDebugCurrentFrame","getStackAddendum","concat","argsWithFormat","map","item","String","unshift","Function","prototype","apply","call","console","printWarning","REACT_MODULE_REFERENCE","getContextName","type","displayName","getComponentNameFromType","tag","name","$$typeof","_context","outerType","innerType","wrapperName","functionName","getWrappedName","render","outerName","lazyComponent","payload","_payload","init","_init","x","prevLog","prevInfo","prevWarn","prevError","prevGroup","prevGroupCollapsed","prevGroupEnd","assign","Object","disabledDepth","disabledLog","__reactDisabledLog","prefix","ReactCurrentDispatcher","describeBuiltInComponentFrame","source","ownerFn","Error","match","trim","componentFrameCache","reentry","PossiblyWeakMap","WeakMap","Map","describeNativeComponentFrame","fn","construct","control","frame","get","previousDispatcher","previousPrepareStackTrace","prepareStackTrace","current","log","info","warn","group","groupCollapsed","groupEnd","props","configurable","enumerable","value","writable","defineProperties","disableLogs","Fake","defineProperty","set","Reflect","sample","sampleLines","split","controlLines","s","_frame","replace","includes","reenableLogs","syntheticFrame","describeUnknownElementTypeFrameInDEV","isReactComponent","hasOwnProperty","loggedTypeFailures","setCurrentlyValidatingElement","element","owner","_owner","_source","setExtraStackFrame","isArrayImpl","isArray","a","testStringCoercion","checkKeyStringCoercion","e","willCoercionThrow","toStringTag","constructor","typeName","specialPropKeyWarningShown","specialPropRefWarningShown","ReactCurrentOwner","RESERVED_PROPS","key","ref","__self","__source","jsxDEV","config","maybeKey","self","propName","getter","getOwnPropertyDescriptor","isReactWarning","hasValidKey","hasValidRef","warnIfStringRefCannotBeAutoConverted","defaultProps","warnAboutAccessingKey","defineKeyPropWarningGetter","warnAboutAccessingRef","defineRefPropWarningGetter","_store","freeze","ReactElement","propTypesMisspellWarningShown","ReactCurrentOwner$1","ReactDebugCurrentFrame$1","setCurrentlyValidatingElement$1","isValidElement","object","getDeclarationErrorAddendum","ownerHasKeyUseWarning","validateExplicitKey","parentType","validated","currentComponentErrorInfo","parentName","getCurrentComponentErrorInfo","childOwner","validateChildKeys","node","child","iteratorFn","maybeIterable","maybeIterator","getIteratorFn","entries","step","next","done","validatePropTypes","propTypes","typeSpecs","values","location","componentName","has","bind","typeSpecName","error$1","err","ex","message","checkPropTypes","PropTypes","getDefaultProps","isReactClassApproved","didWarnAboutKeySpread","jsxWithValidation","isStaticChildren","validType","getModuleId","isValidElementType","keys","typeString","children","filter","k","beforeExample","join","fragment","validateFragmentProps","jsx","jsxs","reactJsxRuntime_development","Fragment","jsxRuntimeModule","exports","f","l","n","p","q","g","b","d","reactJsxRuntime_production_min","require$$1","CameraPermissionDenied","super","this","CameraStream","stream","__publicField","getStream","stop","getTracks","forEach","track","useCamera","streamRef","useRef","isActive","setIsActive","useState","isInitializingRef","startCamera","useCallback","async","constraints","existingStream","Promise","resolve","setTimeout","currentStream","mediaConstraints","isMobile","test","navigator","userAgent","video","facingMode","width","ideal","height","aspectRatio","audio","getOptimalConstraints","mediaDevices","getUserMedia","cameraStream","stopCamera","cleanup","ErrorType","NeoFaceError","getFriendlyMessage","ENVIRONMENT_URLS","development","sandbox","production","globalConfig","baseUrl","applicationToken","environment","initialized","getBaseUrl","getApiBaseUrl","ensureSecureContext","isLocalhost","window","hostname","isSecureContext","NETWORK","createTimeoutController","controller","AbortController","timeoutId","abort","signal","addEventListener","clearTimeout","compressImage","imageBlob","reject","img","Image","onload","canvas","document","createElement","ctx","getContext","drawImage","toBlob","blob","onerror","src","URL","createObjectURL","compressDocumentImage","MAX_SIZE","validateToken","response","fetch","method","headers","body","JSON","stringify","ok","status","INVALID_TOKEN","NETWORK_ERROR","completeOnboarding","onboardingToken","faceImage","documentImage","compressedFace","compressedDocument","formData","FormData","append","encodeURIComponent","data","json","NOT_FOUND","success","person_id","identity_data_id","confidence_score","processing_time","recognize","image","compressedImage","RECOGNITION_FAILED","accessToken","loginWithBiometric","arrayBuffer","base64Image","btoa","Uint8Array","reduce","byte","fromCharCode","biometric_type","API_ERROR","LOGIN_FAILED","alias","user","id","_a","email","_b","role","_c","_d","active","_e","person","birth_date","recognition_id","recognizeByPurpose","purpose","confidenceThreshold","signature","sessionData","reader","FileReader","result","readAsDataURL","requestBody","biometric_data","type_of_identification","confidence_threshold","personName","person_name","cpf","confidenceScore","access_token","sessionId","registerPersonWithBiometric","personData","facePhotos","options","VALIDATION_ERROR","facePhotosBase64","all","photo","documentFrontBase64","documentBackBase64","documentFront","compressed","documentBack","password","password_confirm","face_photos","document_images","front","back","documentType","errorData","errors","errorMessages","field","messages","error_code","errorMessage","parseError","task_id","user_id","person_validated","face_processing","onboarding_link","_f","_g","_h","loginWithEmail","access","modalReducer","state","action","styles","overlay","position","top","left","right","bottom","backgroundColor","display","justifyContent","alignItems","zIndex","modal","borderRadius","overflow","maxWidth","maxHeight","guide","transform","border","pointerEvents","controls","gap","button","padding","color","fontSize","fontWeight","cursor","textAlign","spinner","borderTopColor","animation","closeButton","FaceCaptureModal","onClose","onSuccess","videoRef","canvasRef","faceDetectionTimeoutRef","livenessCheckRef","dispatch","useReducer","faceDetected","setFaceDetected","livenessAttempts","setLivenessAttempts","startFaceDetection","detectFaces","detections","faceapi","detectAllFaces","TinyFaceDetectorOptions","isOneFace","isCentered","detection","box","videoWidth","videoHeight","centerX","centerY","centerWidth","centerHeight","y","isFaceCentered","prev","useEffect","nets","tinyFaceDetector","loadFromUri","faceLandmark68Net","loadModels","clearInterval","srcObject","initializeCamera","handleCapture","context","faceId","documentId","confidence","style","className","onClick","autoPlay","playsInline","muted","stepSequence","capturedPhotos","currentStepIndex","stepProgress","newPhotos","progress","retryable","initialState","livenessInstructions","center","up","down","complete","sequence","CALIBRATION","BiometricRegistrationModal","onPhotosCaptured","onError","holdStartTimeRef","progressIntervalRef","isCapturingRef","currentStepRef","watchdogTimerRef","detectionStartedRef","captureStateRef","_faceDistance","setFaceDistance","newStep","MODEL_URL","timeout","_","race","mounted","waitForVideo","attempts","play","DOMException","initCamera","isRunning","lastDetectionTime","detectFace","requestAnimationFrame","now","Date","detectSingleFace","inputSize","scoreThreshold","withFaceLandmarks","hasFace","faceWidth","landmarks","angles","points","positions","_positions","getPoint","index","point","_x","_y","noseTip","chin","leftEye","rightEye","eyeCenterX","yaw","Math","abs","eyeCenterY","noseOffsetY","pitch","round","calculateHeadAngles","positionCorrect","setInterval","elapsed","min","capturePhoto","manageHoldAndCapture","checkPositionMatch","startDetection","readyState","startTimer","onCanPlay","pollCount","MAX_POLLS","pollTimer","removeEventListener","once","capturedState","scale","nextStepIndex","nextStep","allPhotos","processRegistration","photos","adaptedResult","viewBox","fill","stroke","strokeWidth","strokeLinecap","strokeLinejoin","alt","renderLivenessDetection","marginTop","paddingTop","borderTop","cx","cy","r","BiometricCaptureModal","open","createModal","startCountdown","CAMERA_ERROR","close","removeChild","title","getDefaultTitle","subtitle","getDefaultSubtitle","innerHTML","countdown","addStyles","addEventListeners","appendChild","querySelector","countdownElement","captureBtn","statusText","count","textContent","toString","countdownInterval","disabled","mode","captureImage","isCapturing","base64Data","toDataURL","dataUrl","detectedType","detectBiometricType","CAPTURE_ERROR","imageData","then","biometricDetection","closeBtn","cancelBtn","onCancel","target","styleId","existingStyles","getElementById","remove","head","BiometricStatusOverlay","getStyles","getStatusText","currentStatus","renderContent","isPulsing","showLogo","showSuccess","showError","showDots","show","container","updateStatus","overlayElement","classList","add","contains","FallbackPromptComponent","onRetry","onCredentials","xmlns","FallbackPrompt","root","unmount","EmailPasswordModal","modalElement","handleSubmit","event","preventDefault","emailInput","passwordInput","modelsLoaded","modelsLoading","preloadFaceDetectionModels","attemptLogin","fastMode","isRetry","opacity","setAttribute","onloadedmetadata","maxTime","interval","maxAttempts","ceil","isResolved","finish","checkFace","detectFaceQuickly","parentElement","UNKNOWN","executeBiometricLoginFlow","onFallbackRequest","fallbackPrompt","lastError","catch","tryLogin","faceDetectionAttempts","mediaStream","tryCapture","startFaceLogin","fallbackOptions","INITIALIZATION_ERROR","startHandLogin","captureOptions","_detectedType","res","startAutoLogin","startsWith","faceResult","loadFaceApiModels","tinyOptions","bestDetection","best","score","details","faces","handResult","handFeatures","skinPixels","totalPixels","isSkinColor","skinRatio","imageSize","analyzeHandFeatures","getImageData","detectHand","max","cb","cr","isLoaded","initializeBiometricDetection","isAdvancedDetectionAvailable","OnboardingCaptureModal","countdownSeconds","createDom","runFaceCaptureCountdown","faceBlob","captureFrame","updateTexts","waitForUserCaptureClick","documentBlob","face","onclick","countdownEl","countdownNumberEl","remaining","handler","titleEl","subtitleEl","frameEl","t","PHOTO_INSTRUCTIONS","DOCUMENT_INFO","RG","icon","hasBack","frontLabel","backLabel","CNH","CPF","DocumentCaptureModal","useBackCamera","preSelectedDocument","resolvePromise","selectedDocument","currentStep","createOverlay","content","renderSelectStep","bindSelectEvents","renderCaptureStep","bindCaptureEvents","renderPreviewStep","bindPreviewEvents","renderBranding","docInfo","isBack","label","isPreviewFront","isLastStep","querySelectorAll","option","getAttribute","bindCloseEvent","backBtn","uploadBtn","fileInput","handleBack","click","handleFileUpload","file","files","loadingEl","compressedBlob","compressUploadedImage","frontBlob","backBlob","Blob","confirmBtn","retakeBtn","handleConfirm","handleRetake","previewImg","handleCancel","fallbackError","frontImage","backImage","loading","cropWidth","floor","cropHeight","cropX","cropY","appToken","validateSignatureFormat","captureFaceFrames","numFrames","livenessCheck","frames","captureFaceSilently","biometricLoginFlow","push","loginRecognition","biometricData","imageBlobs","primaryImage","registerApplication","jwtToken","consumerId","applicationData","Authorization","consumer","application_name","applicationName","domain","accept_only_email_with_same_domain","acceptOnlyEmailWithSameDomain","application","app_token","is_active","created_at","proofOfLife","videoDurationMs","maxPollingAttempts","pollingIntervalMs","onRecordingProgress","onTaskStatusChange","videoBlob","durationMs","mimeType","onProgress","mediaRecorder","chunks","selectedMimeType","mimeTypes","MediaRecorder","isTypeSupported","videoBitsPerSecond","ondataavailable","size","onstop","start","progressInterval","progressTimer","NO_CAMERA","recordFaceVideo","videoBase64","base64","videoToBase64","startResult","taskId","historyId","history_id","startProofOfLife","taskResult","intervalMs","onStatusChange","Accept","percentage","pollTaskStatus","isLive","is_live","livenessScore","liveness_score","faceRecognitionScore","face_recognition_score","combinedConfidence","combined_confidence","personId","personalData","personal_data","processingTime","cpfClean","captureDelay","autoplay","instruction","onPhotoTaken","lastPhoto","TOTAL_PHOTOS","random","substring","recognitionResult","authorizationHash","birthDate","personImages","compressedPersonImages","cnpj","liveness_passed","document_verified","person_created","birthDateStr","PERSON_NOT_FOUND","today","age","getFullYear","monthDiff","getMonth","getDate","envUrl","url","require","pathToFileURL","__filename","href","_documentCurrentScript","tagName","toUpperCase","baseURI","__vite_import_meta_env__","VITE_API_BASE_URL","have_biometric_data","documentNumber","document_type","document_number","callbacks","rootRef","useRealApi","completed","handleClose","getOnboardingDetails","is_valid"],"mappings":"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","x_google_ignoreList":[0,1,2,3]}