@neofaceid/web-sdk 1.42.2 → 1.43.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
@@ -1372,6 +1372,12 @@ export declare interface ProofOfLifeResult {
1372
1372
  * `denied` e `expired` são desfechos, não exceções — quem chama precisa
1373
1373
  * distinguir os dois para dirigir a cascata de fallback: expirar pode cair
1374
1374
  * para senha, recusar **não pode**.
1375
+ *
1376
+ * `cancelled` carrega `source` pelo mesmo motivo: a pessoa fechar o modal no
1377
+ * navegador e a solicitação ser cancelada do outro lado são situações
1378
+ * diferentes, e dizer "você cancelou" para quem não cancelou é afirmar algo
1379
+ * falso. É o colapso que o core pediu para evitar entre `denied` e `expired`,
1380
+ * um nível abaixo.
1375
1381
  */
1376
1382
  export declare type PushApprovalOutcome = {
1377
1383
  status: 'approved';
@@ -1383,6 +1389,8 @@ export declare type PushApprovalOutcome = {
1383
1389
  status: 'expired';
1384
1390
  } | {
1385
1391
  status: 'cancelled';
1392
+ source: 'user' | 'server';
1393
+ reason?: string;
1386
1394
  };
1387
1395
 
1388
1396
  declare interface RecognitionResult {
@@ -1595,7 +1603,7 @@ export declare const registerPersonWithoutFace: (personData: {
1595
1603
  /**
1596
1604
  * Data de lançamento da versão atual
1597
1605
  */
1598
- export declare const RELEASE_DATE = "2026-09-14";
1606
+ export declare const RELEASE_DATE = "2026-09-15";
1599
1607
 
1600
1608
  declare interface RequestChallengeWithSessionParams {
1601
1609
  applicationToken: string;
@@ -2032,7 +2040,7 @@ export declare const validateToken: (applicationToken: string) => Promise<boolea
2032
2040
  * MINOR: Incrementado quando adicionamos funcionalidades mantendo compatibilidade
2033
2041
  * PATCH: Incrementado quando corrigimos bugs mantendo compatibilidade
2034
2042
  */
2035
- export declare const VERSION = "1.42.2";
2043
+ export declare const VERSION = "1.43.0";
2036
2044
 
2037
2045
  /**
2038
2046
  * Executa `fn(sessionId)`. Se o servidor devolver 410 (sessão consumida/expirada),
@@ -6083,7 +6083,11 @@ const recordFaceVideo = async (options = {}) => {
6083
6083
  const pollTaskStatus = async (taskId, applicationToken, options = {}) => {
6084
6084
  var _a2, _b, _c, _d, _e, _f, _g, _h, _i, _j;
6085
6085
  const { maxAttempts = 60, intervalMs = 1e3, onStatusChange } = options;
6086
+ const wait = () => new Promise((resolve) => {
6087
+ setTimeout(resolve, intervalMs);
6088
+ });
6086
6089
  let attempts = 0;
6090
+ let lastTransient = "";
6087
6091
  while (attempts < maxAttempts) {
6088
6092
  attempts++;
6089
6093
  try {
@@ -6094,8 +6098,22 @@ const pollTaskStatus = async (taskId, applicationToken, options = {}) => {
6094
6098
  "X-App-Token": applicationToken
6095
6099
  }
6096
6100
  });
6101
+ if (response.status === 404) {
6102
+ throw new NeoFaceError(
6103
+ "Não foi possível recuperar o andamento deste processo. Ele pode já ter sido concluído há algum tempo, ou nunca ter existido.",
6104
+ ErrorType.NOT_FOUND
6105
+ );
6106
+ }
6107
+ if (response.status >= 500) {
6108
+ lastTransient = `HTTP ${response.status}`;
6109
+ await wait();
6110
+ continue;
6111
+ }
6112
+ if (response.status === 401 || response.status === 403) {
6113
+ throw new NeoFaceError("Invalid application token", ErrorType.INVALID_TOKEN);
6114
+ }
6097
6115
  if (!response.ok) {
6098
- throw new NeoFaceError(`Task status check failed: ${response.status}`, ErrorType.NETWORK);
6116
+ throw new NeoFaceError(`Task status check failed: ${response.status}`, ErrorType.API_ERROR);
6099
6117
  }
6100
6118
  const data = await response.json();
6101
6119
  const status = ((_a2 = data.data) == null ? void 0 : _a2.status) || "unknown";
@@ -6117,18 +6135,19 @@ const pollTaskStatus = async (taskId, applicationToken, options = {}) => {
6117
6135
  const reason = ((_h = (_g = data.data) == null ? void 0 : _g.result) == null ? void 0 : _h.message) || ((_i = data.data) == null ? void 0 : _i.error) || ((_j = data.data) == null ? void 0 : _j.message) || data.message || "Task failed";
6118
6136
  throw new NeoFaceError(reason, ErrorType.RECOGNITION_FAILED);
6119
6137
  }
6120
- await new Promise((resolve) => setTimeout(resolve, intervalMs));
6138
+ await wait();
6121
6139
  } catch (error) {
6122
6140
  if (error instanceof NeoFaceError) {
6123
6141
  throw error;
6124
6142
  }
6125
- throw new NeoFaceError(
6126
- `Polling error: ${error instanceof Error ? error.message : "Unknown error"}`,
6127
- ErrorType.NETWORK
6128
- );
6143
+ lastTransient = error instanceof Error ? error.message : "erro desconhecido";
6144
+ await wait();
6129
6145
  }
6130
6146
  }
6131
- throw new NeoFaceError("Task status polling timed out", ErrorType.NETWORK);
6147
+ throw new NeoFaceError(
6148
+ lastTransient ? `Task status polling timed out (última falha transitória: ${lastTransient})` : "Task status polling timed out",
6149
+ ErrorType.NETWORK
6150
+ );
6132
6151
  };
6133
6152
  const startProofOfLife = async (videoBase64, applicationToken) => {
6134
6153
  var _a2;
@@ -8867,8 +8886,8 @@ function BiometricRegistrationModal({
8867
8886
  /* @__PURE__ */ jsx("canvas", { ref: canvasRef })
8868
8887
  ] });
8869
8888
  }
8870
- const VERSION = "1.42.2";
8871
- const RELEASE_DATE = "2026-09-14";
8889
+ const VERSION = "1.43.0";
8890
+ const RELEASE_DATE = "2026-09-15";
8872
8891
  const CONSENT_TRAIL_STORAGE_KEY = "neoface_consent_trail_v1";
8873
8892
  const CONSENT_TRAIL_MAX_LIMIT = 100;
8874
8893
  async function hashString(inputStr) {
@@ -9725,6 +9744,48 @@ function PushExpiredView({ onFallback, onCancel }) {
9725
9744
  /* @__PURE__ */ jsx("div", { className: "nfid-push-footer", children: /* @__PURE__ */ jsx(BrandFooter, {}) })
9726
9745
  ] });
9727
9746
  }
9747
+ function PushCancelledView({
9748
+ appName,
9749
+ onFallback,
9750
+ onCancel
9751
+ }) {
9752
+ useEffect(() => {
9753
+ injectThemeStyles();
9754
+ injectScopedStyles(STYLE_ID$2, CSS$1);
9755
+ }, []);
9756
+ return /* @__PURE__ */ jsxs("div", { className: "nfid-push", role: "alert", "aria-live": "assertive", children: [
9757
+ /* @__PURE__ */ jsxs(
9758
+ "svg",
9759
+ {
9760
+ className: "nfid-push-phone-icon",
9761
+ viewBox: "0 0 24 24",
9762
+ fill: "none",
9763
+ stroke: "currentColor",
9764
+ strokeWidth: "1.75",
9765
+ strokeLinecap: "round",
9766
+ strokeLinejoin: "round",
9767
+ "aria-hidden": "true",
9768
+ style: { color: "var(--nfid-text-muted)" },
9769
+ children: [
9770
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "9" }),
9771
+ /* @__PURE__ */ jsx("path", { d: "M8 12h8" })
9772
+ ]
9773
+ }
9774
+ ),
9775
+ /* @__PURE__ */ jsx("h2", { children: "Solicitação cancelada" }),
9776
+ /* @__PURE__ */ jsxs("p", { children: [
9777
+ "O pedido de confirmação foi cancelado antes de ser respondido. Você pode tentar de novo pelo",
9778
+ " ",
9779
+ appName,
9780
+ "."
9781
+ ] }),
9782
+ /* @__PURE__ */ jsx("div", { className: "nfid-push-actions", children: onFallback ? /* @__PURE__ */ jsxs(Fragment, { children: [
9783
+ /* @__PURE__ */ jsx(Button, { variant: "primary", onClick: onFallback, children: "Entrar de outro jeito" }),
9784
+ /* @__PURE__ */ jsx(Button, { variant: "ghost", onClick: onCancel, children: "Agora não" })
9785
+ ] }) : /* @__PURE__ */ jsx(Button, { variant: "primary", onClick: onCancel, children: "Entendi" }) }),
9786
+ /* @__PURE__ */ jsx("div", { className: "nfid-push-footer", children: /* @__PURE__ */ jsx(BrandFooter, {}) })
9787
+ ] });
9788
+ }
9728
9789
  function PushDeniedView({ appName, onCancel }) {
9729
9790
  useEffect(() => {
9730
9791
  injectThemeStyles();
@@ -9761,7 +9822,7 @@ function PushDeniedView({ appName, onCancel }) {
9761
9822
  }
9762
9823
  const CONTAINER_ID = "neofaceid-push-approval-container";
9763
9824
  function classifyAuthorizationFrame(frame) {
9764
- var _a2, _b;
9825
+ var _a2, _b, _c;
9765
9826
  switch (frame.status) {
9766
9827
  case "approved":
9767
9828
  case "success":
@@ -9773,12 +9834,26 @@ function classifyAuthorizationFrame(frame) {
9773
9834
  };
9774
9835
  case "expired":
9775
9836
  return { kind: "resolve", value: { status: "expired" } };
9837
+ // Quinto terminal do core: a origem desistiu, ou o Push cancelou a
9838
+ // solicitação. Não é traduzido para `expired` de propósito — o prazo não
9839
+ // estourou, alguém decidiu parar, e a tela precisa dizer coisas diferentes
9840
+ // nos dois casos. Sem este ramo o frame cairia em `pending` e o fluxo
9841
+ // morreria no tempo limite.
9842
+ case "cancelled":
9843
+ return {
9844
+ kind: "resolve",
9845
+ value: {
9846
+ status: "cancelled",
9847
+ source: "server",
9848
+ reason: ((_b = frame.data) == null ? void 0 : _b.reason) ?? frame.message
9849
+ }
9850
+ };
9776
9851
  case "failed":
9777
9852
  case "error":
9778
9853
  return {
9779
9854
  kind: "reject",
9780
9855
  error: new NeoFaceError(
9781
- frame.message ?? ((_b = frame.data) == null ? void 0 : _b.message) ?? "Falha na confirmação",
9856
+ frame.message ?? ((_c = frame.data) == null ? void 0 : _c.message) ?? "Falha na confirmação",
9782
9857
  ErrorType.API_ERROR
9783
9858
  )
9784
9859
  };
@@ -9845,6 +9920,10 @@ function PushApprovalModal({
9845
9920
  setPhase({ kind: "expired" });
9846
9921
  return;
9847
9922
  }
9923
+ if (outcome.status === "cancelled") {
9924
+ setPhase({ kind: "cancelled", reason: outcome.reason });
9925
+ return;
9926
+ }
9848
9927
  onSettle(outcome);
9849
9928
  }).catch(() => {
9850
9929
  if (done || controller.signal.aborted) return;
@@ -9859,6 +9938,19 @@ function PushApprovalModal({
9859
9938
  if (phase.kind === "denied") {
9860
9939
  return /* @__PURE__ */ jsx(Sheet, { open: true, dismissOnOverlay: false, ariaLabel: "Confirmação recusada", children: /* @__PURE__ */ jsx(PushDeniedView, { appName: headerAppName, onCancel: () => onSettle({ status: "denied" }) }) });
9861
9940
  }
9941
+ if (phase.kind === "cancelled") {
9942
+ return /* @__PURE__ */ jsx(Sheet, { open: true, dismissOnOverlay: false, ariaLabel: "Solicitação cancelada", children: /* @__PURE__ */ jsx(
9943
+ PushCancelledView,
9944
+ {
9945
+ appName: headerAppName,
9946
+ onFallback: onFallback ? () => {
9947
+ onFallback();
9948
+ onSettle({ status: "cancelled", source: "server", reason: phase.reason });
9949
+ } : void 0,
9950
+ onCancel: () => onSettle({ status: "cancelled", source: "server", reason: phase.reason })
9951
+ }
9952
+ ) });
9953
+ }
9862
9954
  if (phase.kind === "expired") {
9863
9955
  return /* @__PURE__ */ jsx(Sheet, { open: true, dismissOnOverlay: false, ariaLabel: "Tempo esgotado", children: /* @__PURE__ */ jsx(
9864
9956
  PushExpiredView,
@@ -9877,7 +9969,7 @@ function PushApprovalModal({
9877
9969
  appName: headerAppName,
9878
9970
  numericCode,
9879
9971
  remainingSeconds: remaining,
9880
- onCancel: () => onSettle({ status: "cancelled" })
9972
+ onCancel: () => onSettle({ status: "cancelled", source: "user" })
9881
9973
  }
9882
9974
  ) });
9883
9975
  }