@neofaceid/web-sdk 1.36.1 → 1.37.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -88,7 +88,12 @@ export declare interface AuthorizeOptions {
88
88
  amount?: number;
89
89
  onChallengeStart?: (gesture: ChallengeGesture, index: number, total: number) => void;
90
90
  onChallengeComplete?: (gesture: ChallengeGesture, index: number, total: number) => void;
91
- onSuccess?: (result: LivenessChallengeResult) => void;
91
+ /**
92
+ * NEO-427: agora recebe o resultado da COLETA (sessionId + challenge + gestures
93
+ * capturadas). O SDK não decide aprovação — envie os frames via seu próprio
94
+ * pipeline de recognition (ex.: `POST /auth/recognition/face/`).
95
+ */
96
+ onSuccess?: (result: RunLivenessChallengeCollectResult) => void;
92
97
  onError?: (error: NeoFaceError) => void;
93
98
  onCancel?: () => void;
94
99
  /**
@@ -329,6 +334,11 @@ export declare interface CaptureSession {
329
334
  export declare interface ChallengeGesture {
330
335
  index: number;
331
336
  gesture_key: GestureKey | string;
337
+ /**
338
+ * Instrução pronta pelo backend (v1.4.x, respeita `Accept-Language`).
339
+ * Nos payloads legados vinha via `instruction_i18n`.
340
+ */
341
+ instruction?: string;
332
342
  instruction_i18n?: Record<string, string>;
333
343
  }
334
344
 
@@ -411,6 +421,24 @@ export declare function confirmPasswordReset(token: string, new_password: string
411
421
  message: string;
412
422
  }>;
413
423
 
424
+ /**
425
+ * Dados de consentimento LGPD informados pelo integrador.
426
+ * Usados pelo SDK para registrar a base legal da coleta biométrica.
427
+ */
428
+ export declare interface Consent {
429
+ /** Finalidade da coleta (ex: 'authentication', 'onboarding'). */
430
+ purpose: string;
431
+ /**
432
+ * Base legal. Legítimo interesse não é permitido para dado sensível (LGPD art. 11).
433
+ * Valores: 'consent' | 'legal_obligation' | 'fraud_prevention'.
434
+ */
435
+ legalBasis: 'consent' | 'legal_obligation' | 'fraud_prevention';
436
+ /** URL da política de privacidade apresentada ao titular. */
437
+ privacyPolicyUrl: string;
438
+ /** Prazo de retenção em dias (0 = indefinido/não informado). */
439
+ retentionDays: number;
440
+ }
441
+
414
442
  declare type ConsentFlow = 'verification' | 'registration';
415
443
 
416
444
  export declare interface ConsentInfo {
@@ -637,7 +665,12 @@ export declare class ForgotPasswordModal {
637
665
  private addStyles;
638
666
  }
639
667
 
640
- export declare type GestureKey = 'turn_left' | 'turn_right' | 'turn_up' | 'turn_down' | 'blink' | 'smile' | 'open_mouth';
668
+ /**
669
+ * Chaves de gesto reconhecidas pelo backend real (case-insensitive; SDK
670
+ * normaliza para maiúsculo). Novos gestos podem aparecer sem impacto no SDK
671
+ * — o campo é `string` na runtime.
672
+ */
673
+ export declare type GestureKey = 'LOOK_LEFT' | 'LOOK_RIGHT' | 'LOOK_UP' | 'LOOK_DOWN' | 'BLINK' | 'SMILE' | 'OPEN_MOUTH' | 'turn_left' | 'turn_right' | 'look_up' | 'look_down' | 'blink' | 'smile' | 'open_mouth';
641
674
 
642
675
  export declare interface GestureResponse {
643
676
  gesture_key: string;
@@ -669,6 +702,9 @@ export declare function getCachedCaptureSession(): CaptureSession | null;
669
702
  */
670
703
  export declare function getConfig(): Readonly<SDKConfig>;
671
704
 
705
+ /** Retorna os dados de consentimento configurados (ou os defaults conservadores). */
706
+ export declare function getConsent(): Consent;
707
+
672
708
  /**
673
709
  * Retorna o ambiente atual configurado.
674
710
  */
@@ -769,9 +805,18 @@ export declare function isAdvancedDetectionAvailable(): boolean;
769
805
  export declare function isInitialized(): boolean;
770
806
 
771
807
  export declare interface LivenessChallenge {
772
- challenge_id: string;
808
+ /**
809
+ * ID do desafio. **Opcional no contrato v1.4.x** — o backend real rastreia
810
+ * o desafio pelo `session_id` e não devolve `challenge_id` no payload.
811
+ * Mantido opcional para consumidores que ainda dependem do valor.
812
+ */
813
+ challenge_id?: string;
773
814
  gestures: ChallengeGesture[];
815
+ /** Data ISO derivada de `expires_in` (segundos) ou legado `expires_at`. */
774
816
  expires_at?: string;
817
+ /** Segundos até expirar (contrato v1.4.x). */
818
+ expires_in?: number;
819
+ language?: string;
775
820
  }
776
821
 
777
822
  export declare interface LivenessChallengeResult {
@@ -1310,7 +1355,7 @@ export declare const registerPersonWithoutFace: (personData: {
1310
1355
  /**
1311
1356
  * Data de lançamento da versão atual
1312
1357
  */
1313
- export declare const RELEASE_DATE = "2026-09-03";
1358
+ export declare const RELEASE_DATE = "2026-09-05";
1314
1359
 
1315
1360
  declare interface RequestChallengeWithSessionParams {
1316
1361
  applicationToken: string;
@@ -1367,16 +1412,33 @@ declare interface ResolvedTheme {
1367
1412
  }
1368
1413
 
1369
1414
  /**
1370
- * Orquestra o fluxo completo:
1415
+ * Orquestra:
1371
1416
  * 1. Abre `capture/session`
1372
- * 2. Pede `liveness/challenge` (sequência sorteada pelo servidor)
1417
+ * 2. Pede `liveness/challenge` (backend sorteia)
1373
1418
  * 3. Delega a coleta de frames ao consumidor gesto a gesto
1374
- * 4. Submete a resposta para o servidor validar
1419
+ * 4. **Retorna a coleta** para o consumidor submeter no pipeline dele.
1375
1420
  *
1376
- * Retry: em 410 no submit, reabre a sessão + refaz o desafio + reenvia UMA vez.
1377
- * Segunda falha propaga.
1421
+ * Retry: em 410 ao abrir sessão/pedir desafio, reabre + refaz 1x. Segunda falha propaga.
1422
+ *
1423
+ * Nota: até 1.36.1 esta função chamava `submitLivenessChallenge` no fim, mas o
1424
+ * endpoint não existe no backend real. NEO-427 removeu essa chamada. Quando
1425
+ * NEO-428 identificar o endpoint real de submit, o consumidor pode usá-lo, ou
1426
+ * uma variante `runLivenessChallengeAndSubmit` pode ser reintroduzida.
1427
+ */
1428
+ export declare function runLivenessChallenge({ applicationToken, purpose, collectFramesForGesture, retryOnSessionExpired, }: RunLivenessChallengeParams): Promise<RunLivenessChallengeCollectResult>;
1429
+
1430
+ /**
1431
+ * Resultado da coleta orquestrada. NEO-427: NÃO submete os frames automaticamente
1432
+ * (o endpoint de submit `/challenge/{id}/submit/` não existe no backend v1.4.x —
1433
+ * devolve 404). O consumidor recebe as gestures coletadas e envia via seu próprio
1434
+ * pipeline (ex.: `POST /auth/recognition/face/` com `challenge_response` no body).
1378
1435
  */
1379
- export declare function runLivenessChallenge({ applicationToken, purpose, collectFramesForGesture, retryOnSessionExpired, }: RunLivenessChallengeParams): Promise<LivenessChallengeResult>;
1436
+ export declare interface RunLivenessChallengeCollectResult {
1437
+ sessionId: string;
1438
+ challenge: LivenessChallenge;
1439
+ /** Frames coletados por gesto, na ordem sorteada pelo servidor. */
1440
+ capturedGestures: GestureResponse[];
1441
+ }
1380
1442
 
1381
1443
  export declare interface RunLivenessChallengeParams {
1382
1444
  applicationToken: string;
@@ -1384,10 +1446,10 @@ export declare interface RunLivenessChallengeParams {
1384
1446
  /**
1385
1447
  * Callback fornecido pelo consumidor: para cada gesto na sequência sorteada
1386
1448
  * pelo servidor, retorna os frames coletados. SDK NÃO decide se o gesto foi
1387
- * cumprido — só coleta e envia; validação é 100% servidor.
1449
+ * cumprido — só coleta; validação é 100% servidor.
1388
1450
  */
1389
1451
  collectFramesForGesture: (gesture: ChallengeGesture, index: number, total: number) => Promise<string[]>;
1390
- /** Se true, quando o servidor devolver 410 no submit, reabre sessão + refaz o desafio 1x. */
1452
+ /** Se true, quando o servidor devolver 410 na abertura do desafio, reabre sessão + refaz 1x. */
1391
1453
  retryOnSessionExpired?: boolean;
1392
1454
  }
1393
1455
 
@@ -1405,6 +1467,7 @@ declare interface SDKConfig {
1405
1467
  theme: SDKTheme;
1406
1468
  locale: string;
1407
1469
  resolvedTheme: ResolvedTheme | null;
1470
+ consent: Consent;
1408
1471
  }
1409
1472
 
1410
1473
  /**
@@ -1456,6 +1519,11 @@ export declare interface SDKInitOptions {
1456
1519
  * Locale da UI. Padrão `pt-BR`.
1457
1520
  */
1458
1521
  locale?: string;
1522
+ /**
1523
+ * Dados de consentimento LGPD. Se omitido, o SDK usa defaults conservadores
1524
+ * e emite `console.warn` uma única vez.
1525
+ */
1526
+ consent?: Consent;
1459
1527
  }
1460
1528
 
1461
1529
  export declare type SDKRadius = 8 | 16 | 24;
@@ -1583,14 +1651,18 @@ export declare interface StartOnboardingOptions {
1583
1651
  }
1584
1652
 
1585
1653
  /**
1586
- * Envia os frames coletados para o servidor validar o desafio de liveness.
1587
- * Endpoint: `POST /api/v1/auth/liveness/challenge/{challenge_id}/submit/`
1588
- * O SDK apenas empacota e envia — nenhuma decisão de aprovar/reprovar aqui.
1654
+ * @deprecated NEO-427: o endpoint `POST /liveness/challenge/{id}/submit/` NÃO
1655
+ * EXISTE no backend v1.4.x — devolve 404. `runLivenessChallenge` não chama mais
1656
+ * esta função. Mantida apenas para consumidores que ainda dependem do símbolo;
1657
+ * será removida quando o endpoint real de submit for identificado (NEO-428).
1658
+ *
1659
+ * Continua tentando o path histórico e propaga o erro do servidor.
1589
1660
  */
1590
1661
  export declare function submitLivenessChallenge({ challengeId, sessionId, applicationToken, gestures, }: SubmitLivenessChallengeParams): Promise<LivenessChallengeResult>;
1591
1662
 
1592
1663
  export declare interface SubmitLivenessChallengeParams {
1593
- challengeId: string;
1664
+ /** Opcional no v1.4.x — backend rastreia via session_id. */
1665
+ challengeId?: string;
1594
1666
  sessionId: string;
1595
1667
  applicationToken: string;
1596
1668
  gestures: GestureResponse[];
@@ -1650,7 +1722,7 @@ export declare const validateToken: (applicationToken: string) => Promise<boolea
1650
1722
  * MINOR: Incrementado quando adicionamos funcionalidades mantendo compatibilidade
1651
1723
  * PATCH: Incrementado quando corrigimos bugs mantendo compatibilidade
1652
1724
  */
1653
- export declare const VERSION = "1.36.1";
1725
+ export declare const VERSION = "1.37.0";
1654
1726
 
1655
1727
  /**
1656
1728
  * Executa `fn(sessionId)`. Se o servidor devolver 410 (sessão consumida/expirada),
@@ -4534,6 +4534,12 @@ function injectScopedStyles(id, css) {
4534
4534
  document.head.appendChild(style);
4535
4535
  }
4536
4536
  const __vite_import_meta_env__ = {};
4537
+ const DEFAULT_CONSENT = {
4538
+ purpose: "authentication",
4539
+ legalBasis: "fraud_prevention",
4540
+ privacyPolicyUrl: "",
4541
+ retentionDays: 0
4542
+ };
4537
4543
  const ENVIRONMENT_URLS = {
4538
4544
  development: "http://localhost:8000",
4539
4545
  sandbox: "https://sandbox-core.neofaceid.com",
@@ -4554,12 +4560,25 @@ function makeDefaultConfig() {
4554
4560
  radius: DEFAULT_RADIUS,
4555
4561
  theme: DEFAULT_THEME,
4556
4562
  locale: DEFAULT_LOCALE,
4557
- resolvedTheme: null
4563
+ resolvedTheme: null,
4564
+ consent: { ...DEFAULT_CONSENT }
4558
4565
  };
4559
4566
  }
4560
4567
  let globalConfig = makeDefaultConfig();
4568
+ let consentWarningShown = false;
4561
4569
  function init(options = {}) {
4562
- const { environment, baseUrl, applicationToken, appName, accent, radius, theme, locale } = options;
4570
+ const { environment, baseUrl, applicationToken, appName, accent, radius, theme, locale, consent } = options;
4571
+ if (consent) {
4572
+ globalConfig.consent = consent;
4573
+ } else {
4574
+ globalConfig.consent = { ...DEFAULT_CONSENT };
4575
+ if (!consentWarningShown) {
4576
+ console.warn(
4577
+ "[NeoFaceID SDK] Consentimento não configurado em init(). Usando valores padrão (fraud_prevention). Configure consent: { purpose, legalBasis, privacyPolicyUrl, retentionDays }."
4578
+ );
4579
+ consentWarningShown = true;
4580
+ }
4581
+ }
4563
4582
  if (baseUrl) {
4564
4583
  globalConfig.baseUrl = baseUrl;
4565
4584
  globalConfig.environment = "custom";
@@ -4659,6 +4678,9 @@ function getResolvedThemeMode() {
4659
4678
  function getLocale() {
4660
4679
  return globalConfig.locale;
4661
4680
  }
4681
+ function getConsent() {
4682
+ return { ...globalConfig.consent };
4683
+ }
4662
4684
  const getApiBaseUrl = () => getBaseUrl();
4663
4685
  const REQUEST_TIMEOUT = 1e4;
4664
4686
  const ensureSecureContext = () => {
@@ -11177,8 +11199,8 @@ const biometricDetection = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.
11177
11199
  initializeBiometricDetection,
11178
11200
  isAdvancedDetectionAvailable
11179
11201
  }, Symbol.toStringTag, { value: "Module" }));
11180
- const VERSION = "1.36.1";
11181
- const RELEASE_DATE = "2026-09-03";
11202
+ const VERSION = "1.37.0";
11203
+ const RELEASE_DATE = "2026-09-05";
11182
11204
  let cachedSession = null;
11183
11205
  function getCachedCaptureSession() {
11184
11206
  return cachedSession;
@@ -11305,18 +11327,33 @@ async function requestLivenessChallenge({
11305
11327
  }
11306
11328
  const raw = await response.json().catch(() => null);
11307
11329
  const payload = (raw == null ? void 0 : raw.data) ?? raw;
11308
- const challengeId = payload == null ? void 0 : payload.challenge_id;
11309
- const gestures = payload == null ? void 0 : payload.gestures;
11310
- if (!challengeId || !Array.isArray(gestures) || gestures.length === 0) {
11330
+ const rawSteps = payload == null ? void 0 : payload.steps;
11331
+ const rawGestures = payload == null ? void 0 : payload.gestures;
11332
+ const source = Array.isArray(rawSteps) ? rawSteps : Array.isArray(rawGestures) ? rawGestures : void 0;
11333
+ if (!source || source.length === 0) {
11311
11334
  throw new NeoFaceError(
11312
- "Resposta inválida ao solicitar desafio (campos ausentes)",
11335
+ "Resposta inválida ao solicitar desafio (steps/gestures ausentes)",
11313
11336
  ErrorType.API_ERROR
11314
11337
  );
11315
11338
  }
11339
+ const gestures = source.map((s, i) => {
11340
+ const item = s;
11341
+ const gestureRaw = item.gesture ?? item.gesture_key;
11342
+ return {
11343
+ index: typeof item.order === "number" ? item.order : typeof item.index === "number" ? item.index : i + 1,
11344
+ gesture_key: gestureRaw ?? "",
11345
+ instruction: typeof item.instruction === "string" ? item.instruction : void 0,
11346
+ instruction_i18n: item.instruction_i18n && typeof item.instruction_i18n === "object" ? item.instruction_i18n : void 0
11347
+ };
11348
+ });
11349
+ const expiresIn = typeof (payload == null ? void 0 : payload.expires_in) === "number" ? payload.expires_in : void 0;
11350
+ const expiresAt = typeof (payload == null ? void 0 : payload.expires_at) === "string" && payload.expires_at.length > 0 ? payload.expires_at : expiresIn !== void 0 ? new Date(Date.now() + expiresIn * 1e3).toISOString() : void 0;
11316
11351
  return {
11317
- challenge_id: challengeId,
11352
+ challenge_id: typeof (payload == null ? void 0 : payload.challenge_id) === "string" ? payload.challenge_id : void 0,
11318
11353
  gestures,
11319
- expires_at: payload == null ? void 0 : payload.expires_at
11354
+ expires_at: expiresAt,
11355
+ expires_in: expiresIn,
11356
+ language: typeof (payload == null ? void 0 : payload.language) === "string" ? payload.language : void 0
11320
11357
  };
11321
11358
  }
11322
11359
  async function requestLivenessChallengeWithSession({
@@ -11342,22 +11379,26 @@ async function submitLivenessChallenge({
11342
11379
  applicationToken,
11343
11380
  gestures
11344
11381
  }) {
11382
+ console.warn(
11383
+ "[NeoFaceID SDK] submitLivenessChallenge está deprecated (NEO-427): o endpoint /liveness/challenge/{id}/submit/ não existe no backend v1.4.x. Coleta os frames via runLivenessChallenge e envie via seu próprio pipeline até que NEO-428 defina o endpoint real."
11384
+ );
11345
11385
  if (!applicationToken) {
11346
11386
  throw new NeoFaceError(
11347
11387
  "applicationToken é obrigatório para submeter desafio",
11348
11388
  ErrorType.INVALID_TOKEN
11349
11389
  );
11350
11390
  }
11351
- if (!challengeId || !sessionId) {
11391
+ if (!sessionId) {
11352
11392
  throw new NeoFaceError(
11353
- "challenge_id e session_id são obrigatórios para submeter desafio",
11393
+ "session_id é obrigatório para submeter desafio",
11354
11394
  ErrorType.VALIDATION_ERROR
11355
11395
  );
11356
11396
  }
11357
11397
  if (!Array.isArray(gestures) || gestures.length === 0) {
11358
11398
  throw new NeoFaceError("gestures[] vazio — nada para submeter", ErrorType.VALIDATION_ERROR);
11359
11399
  }
11360
- const url = `${getBaseUrl()}/api/v1/auth/liveness/challenge/${encodeURIComponent(challengeId)}/submit/`;
11400
+ const idSuffix = challengeId ? `${encodeURIComponent(challengeId)}/submit/` : "submit/";
11401
+ const url = `${getBaseUrl()}/api/v1/auth/liveness/challenge/${idSuffix}`;
11361
11402
  let response;
11362
11403
  try {
11363
11404
  response = await fetch(url, {
@@ -11408,18 +11449,13 @@ async function runLivenessChallenge({
11408
11449
  sessionId: session.session_id,
11409
11450
  applicationToken
11410
11451
  });
11411
- const gestures = [];
11452
+ const capturedGestures = [];
11412
11453
  for (let i = 0; i < challenge.gestures.length; i += 1) {
11413
11454
  const g = challenge.gestures[i];
11414
11455
  const images = await collectFramesForGesture(g, i, challenge.gestures.length);
11415
- gestures.push({ gesture_key: String(g.gesture_key), images });
11456
+ capturedGestures.push({ gesture_key: String(g.gesture_key), images });
11416
11457
  }
11417
- return submitLivenessChallenge({
11418
- challengeId: challenge.challenge_id,
11419
- sessionId: session.session_id,
11420
- applicationToken,
11421
- gestures
11422
- });
11458
+ return { sessionId: session.session_id, challenge, capturedGestures };
11423
11459
  };
11424
11460
  try {
11425
11461
  return await attempt();
@@ -12924,12 +12960,6 @@ function AuthorizeModalComponent({
12924
12960
  });
12925
12961
  if (stream) stream.getTracks().forEach((t) => t.stop());
12926
12962
  stream = null;
12927
- if (result.success === false || result.liveness_passed === false) {
12928
- setFailureCode("LIVENESS_LOW");
12929
- setFailureAt(/* @__PURE__ */ new Date());
12930
- setPhase("failure");
12931
- return;
12932
- }
12933
12963
  setPhase("success");
12934
12964
  if (onSuccess) onSuccess(result);
12935
12965
  } catch (err) {
@@ -14283,6 +14313,7 @@ export {
14283
14313
  getBaseUrl,
14284
14314
  getCachedCaptureSession,
14285
14315
  getConfig,
14316
+ getConsent,
14286
14317
  getEnvironment,
14287
14318
  getLocale,
14288
14319
  getRadius,