@neofaceid/web-sdk 1.42.3 → 1.44.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 +79 -3
- package/dist/neoface-id-sdk.es.js +494 -22
- package/dist/neoface-id-sdk.umd.js +1 -1
- package/package.json +2 -1
|
@@ -5678,7 +5678,6 @@ const loginWithEmail = async (email2, password, applicationToken) => {
|
|
|
5678
5678
|
}
|
|
5679
5679
|
};
|
|
5680
5680
|
const requestDeviceEnrollmentToken = async (userAccessToken, applicationToken) => {
|
|
5681
|
-
var _a2, _b, _c;
|
|
5682
5681
|
if (!userAccessToken) {
|
|
5683
5682
|
throw new NeoFaceError(
|
|
5684
5683
|
"Inscrição de aparelho exige um usuário autenticado (access token ausente).",
|
|
@@ -5697,23 +5696,36 @@ const requestDeviceEnrollmentToken = async (userAccessToken, applicationToken) =
|
|
|
5697
5696
|
signal: controller.signal
|
|
5698
5697
|
});
|
|
5699
5698
|
if (!response.ok) {
|
|
5700
|
-
if (response.status === 401
|
|
5699
|
+
if (response.status === 401) {
|
|
5701
5700
|
throw new NeoFaceError(
|
|
5702
|
-
"
|
|
5701
|
+
"Sua sessão expirou. Entre novamente para inscrever o aparelho.",
|
|
5702
|
+
ErrorType.INVALID_TOKEN
|
|
5703
|
+
);
|
|
5704
|
+
}
|
|
5705
|
+
if (response.status === 403) {
|
|
5706
|
+
throw new NeoFaceError(
|
|
5707
|
+
"Esta conta não pode inscrever aparelhos. Procure o suporte.",
|
|
5703
5708
|
ErrorType.INVALID_TOKEN
|
|
5704
5709
|
);
|
|
5705
5710
|
}
|
|
5706
5711
|
throw new NeoFaceError(`API Error ${response.status}`, ErrorType.API_ERROR);
|
|
5707
5712
|
}
|
|
5708
5713
|
const data = await response.json();
|
|
5709
|
-
const
|
|
5710
|
-
|
|
5711
|
-
|
|
5714
|
+
const body = data.data ?? data;
|
|
5715
|
+
const token = body.token;
|
|
5716
|
+
const handle = body.handle;
|
|
5717
|
+
if (!token || !handle) {
|
|
5718
|
+
throw new NeoFaceError(
|
|
5719
|
+
"Resposta de inscrição incompleta (token ou handle ausente)",
|
|
5720
|
+
ErrorType.API_ERROR
|
|
5721
|
+
);
|
|
5712
5722
|
}
|
|
5713
5723
|
return {
|
|
5724
|
+
handle,
|
|
5725
|
+
jti: body.jti ?? "",
|
|
5714
5726
|
token,
|
|
5715
|
-
expiresIn:
|
|
5716
|
-
purpose:
|
|
5727
|
+
expiresIn: body.expires_in ?? 300,
|
|
5728
|
+
purpose: body.purpose ?? "device-enrollment"
|
|
5717
5729
|
};
|
|
5718
5730
|
} catch (error) {
|
|
5719
5731
|
if (error instanceof NeoFaceError) throw error;
|
|
@@ -8886,8 +8898,8 @@ function BiometricRegistrationModal({
|
|
|
8886
8898
|
/* @__PURE__ */ jsx("canvas", { ref: canvasRef })
|
|
8887
8899
|
] });
|
|
8888
8900
|
}
|
|
8889
|
-
const VERSION = "1.
|
|
8890
|
-
const RELEASE_DATE = "2026-09-
|
|
8901
|
+
const VERSION = "1.44.0";
|
|
8902
|
+
const RELEASE_DATE = "2026-09-16";
|
|
8891
8903
|
const CONSENT_TRAIL_STORAGE_KEY = "neoface_consent_trail_v1";
|
|
8892
8904
|
const CONSENT_TRAIL_MAX_LIMIT = 100;
|
|
8893
8905
|
async function hashString(inputStr) {
|
|
@@ -9599,8 +9611,8 @@ function StatusPill({ tone, children, icon, className }) {
|
|
|
9599
9611
|
}
|
|
9600
9612
|
);
|
|
9601
9613
|
}
|
|
9602
|
-
const STYLE_ID$
|
|
9603
|
-
const CSS$
|
|
9614
|
+
const STYLE_ID$3 = "neofaceid-awaiting-push-styles";
|
|
9615
|
+
const CSS$2 = `
|
|
9604
9616
|
.neofaceid-root .nfid-push {
|
|
9605
9617
|
display: flex; flex-direction: column; align-items: center; text-align: center; gap: 10px;
|
|
9606
9618
|
padding: 8px 8px 4px;
|
|
@@ -9651,7 +9663,7 @@ const CSS$1 = `
|
|
|
9651
9663
|
.neofaceid-root .nfid-push-pulse { animation: none; }
|
|
9652
9664
|
}
|
|
9653
9665
|
`;
|
|
9654
|
-
function formatRemaining(seconds) {
|
|
9666
|
+
function formatRemaining$1(seconds) {
|
|
9655
9667
|
const safe = Math.max(0, seconds);
|
|
9656
9668
|
const mm = Math.floor(safe / 60);
|
|
9657
9669
|
const ss = safe % 60;
|
|
@@ -9665,7 +9677,7 @@ function AwaitingPushView({
|
|
|
9665
9677
|
}) {
|
|
9666
9678
|
useEffect(() => {
|
|
9667
9679
|
injectThemeStyles();
|
|
9668
|
-
injectScopedStyles(STYLE_ID$
|
|
9680
|
+
injectScopedStyles(STYLE_ID$3, CSS$2);
|
|
9669
9681
|
}, []);
|
|
9670
9682
|
return /* @__PURE__ */ jsxs("div", { className: "nfid-push", role: "status", "aria-live": "polite", children: [
|
|
9671
9683
|
/* @__PURE__ */ jsx(StatusPill, { tone: "attention", children: "Aguardando confirmação" }),
|
|
@@ -9705,7 +9717,7 @@ function AwaitingPushView({
|
|
|
9705
9717
|
] }),
|
|
9706
9718
|
/* @__PURE__ */ jsxs("p", { className: "nfid-push-countdown", children: [
|
|
9707
9719
|
"Expira em ",
|
|
9708
|
-
formatRemaining(remainingSeconds)
|
|
9720
|
+
formatRemaining$1(remainingSeconds)
|
|
9709
9721
|
] }),
|
|
9710
9722
|
/* @__PURE__ */ jsx("div", { className: "nfid-push-actions", children: /* @__PURE__ */ jsx(Button, { variant: "ghost", onClick: onCancel, children: "Cancelar" }) }),
|
|
9711
9723
|
/* @__PURE__ */ jsx("div", { className: "nfid-push-footer", children: /* @__PURE__ */ jsx(BrandFooter, {}) })
|
|
@@ -9714,7 +9726,7 @@ function AwaitingPushView({
|
|
|
9714
9726
|
function PushExpiredView({ onFallback, onCancel }) {
|
|
9715
9727
|
useEffect(() => {
|
|
9716
9728
|
injectThemeStyles();
|
|
9717
|
-
injectScopedStyles(STYLE_ID$
|
|
9729
|
+
injectScopedStyles(STYLE_ID$3, CSS$2);
|
|
9718
9730
|
}, []);
|
|
9719
9731
|
return /* @__PURE__ */ jsxs("div", { className: "nfid-push", role: "alert", "aria-live": "assertive", children: [
|
|
9720
9732
|
/* @__PURE__ */ jsxs(
|
|
@@ -9744,10 +9756,52 @@ function PushExpiredView({ onFallback, onCancel }) {
|
|
|
9744
9756
|
/* @__PURE__ */ jsx("div", { className: "nfid-push-footer", children: /* @__PURE__ */ jsx(BrandFooter, {}) })
|
|
9745
9757
|
] });
|
|
9746
9758
|
}
|
|
9759
|
+
function PushCancelledView({
|
|
9760
|
+
appName,
|
|
9761
|
+
onFallback,
|
|
9762
|
+
onCancel
|
|
9763
|
+
}) {
|
|
9764
|
+
useEffect(() => {
|
|
9765
|
+
injectThemeStyles();
|
|
9766
|
+
injectScopedStyles(STYLE_ID$3, CSS$2);
|
|
9767
|
+
}, []);
|
|
9768
|
+
return /* @__PURE__ */ jsxs("div", { className: "nfid-push", role: "alert", "aria-live": "assertive", children: [
|
|
9769
|
+
/* @__PURE__ */ jsxs(
|
|
9770
|
+
"svg",
|
|
9771
|
+
{
|
|
9772
|
+
className: "nfid-push-phone-icon",
|
|
9773
|
+
viewBox: "0 0 24 24",
|
|
9774
|
+
fill: "none",
|
|
9775
|
+
stroke: "currentColor",
|
|
9776
|
+
strokeWidth: "1.75",
|
|
9777
|
+
strokeLinecap: "round",
|
|
9778
|
+
strokeLinejoin: "round",
|
|
9779
|
+
"aria-hidden": "true",
|
|
9780
|
+
style: { color: "var(--nfid-text-muted)" },
|
|
9781
|
+
children: [
|
|
9782
|
+
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "9" }),
|
|
9783
|
+
/* @__PURE__ */ jsx("path", { d: "M8 12h8" })
|
|
9784
|
+
]
|
|
9785
|
+
}
|
|
9786
|
+
),
|
|
9787
|
+
/* @__PURE__ */ jsx("h2", { children: "Solicitação cancelada" }),
|
|
9788
|
+
/* @__PURE__ */ jsxs("p", { children: [
|
|
9789
|
+
"O pedido de confirmação foi cancelado antes de ser respondido. Você pode tentar de novo pelo",
|
|
9790
|
+
" ",
|
|
9791
|
+
appName,
|
|
9792
|
+
"."
|
|
9793
|
+
] }),
|
|
9794
|
+
/* @__PURE__ */ jsx("div", { className: "nfid-push-actions", children: onFallback ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
9795
|
+
/* @__PURE__ */ jsx(Button, { variant: "primary", onClick: onFallback, children: "Entrar de outro jeito" }),
|
|
9796
|
+
/* @__PURE__ */ jsx(Button, { variant: "ghost", onClick: onCancel, children: "Agora não" })
|
|
9797
|
+
] }) : /* @__PURE__ */ jsx(Button, { variant: "primary", onClick: onCancel, children: "Entendi" }) }),
|
|
9798
|
+
/* @__PURE__ */ jsx("div", { className: "nfid-push-footer", children: /* @__PURE__ */ jsx(BrandFooter, {}) })
|
|
9799
|
+
] });
|
|
9800
|
+
}
|
|
9747
9801
|
function PushDeniedView({ appName, onCancel }) {
|
|
9748
9802
|
useEffect(() => {
|
|
9749
9803
|
injectThemeStyles();
|
|
9750
|
-
injectScopedStyles(STYLE_ID$
|
|
9804
|
+
injectScopedStyles(STYLE_ID$3, CSS$2);
|
|
9751
9805
|
}, []);
|
|
9752
9806
|
return /* @__PURE__ */ jsxs("div", { className: "nfid-push", role: "alert", "aria-live": "assertive", children: [
|
|
9753
9807
|
/* @__PURE__ */ jsxs(
|
|
@@ -9778,9 +9832,9 @@ function PushDeniedView({ appName, onCancel }) {
|
|
|
9778
9832
|
/* @__PURE__ */ jsx("div", { className: "nfid-push-footer", children: /* @__PURE__ */ jsx(BrandFooter, {}) })
|
|
9779
9833
|
] });
|
|
9780
9834
|
}
|
|
9781
|
-
const CONTAINER_ID = "neofaceid-push-approval-container";
|
|
9835
|
+
const CONTAINER_ID$1 = "neofaceid-push-approval-container";
|
|
9782
9836
|
function classifyAuthorizationFrame(frame) {
|
|
9783
|
-
var _a2, _b;
|
|
9837
|
+
var _a2, _b, _c;
|
|
9784
9838
|
switch (frame.status) {
|
|
9785
9839
|
case "approved":
|
|
9786
9840
|
case "success":
|
|
@@ -9792,12 +9846,26 @@ function classifyAuthorizationFrame(frame) {
|
|
|
9792
9846
|
};
|
|
9793
9847
|
case "expired":
|
|
9794
9848
|
return { kind: "resolve", value: { status: "expired" } };
|
|
9849
|
+
// Quinto terminal do core: a origem desistiu, ou o Push cancelou a
|
|
9850
|
+
// solicitação. Não é traduzido para `expired` de propósito — o prazo não
|
|
9851
|
+
// estourou, alguém decidiu parar, e a tela precisa dizer coisas diferentes
|
|
9852
|
+
// nos dois casos. Sem este ramo o frame cairia em `pending` e o fluxo
|
|
9853
|
+
// morreria no tempo limite.
|
|
9854
|
+
case "cancelled":
|
|
9855
|
+
return {
|
|
9856
|
+
kind: "resolve",
|
|
9857
|
+
value: {
|
|
9858
|
+
status: "cancelled",
|
|
9859
|
+
source: "server",
|
|
9860
|
+
reason: ((_b = frame.data) == null ? void 0 : _b.reason) ?? frame.message
|
|
9861
|
+
}
|
|
9862
|
+
};
|
|
9795
9863
|
case "failed":
|
|
9796
9864
|
case "error":
|
|
9797
9865
|
return {
|
|
9798
9866
|
kind: "reject",
|
|
9799
9867
|
error: new NeoFaceError(
|
|
9800
|
-
frame.message ?? ((
|
|
9868
|
+
frame.message ?? ((_c = frame.data) == null ? void 0 : _c.message) ?? "Falha na confirmação",
|
|
9801
9869
|
ErrorType.API_ERROR
|
|
9802
9870
|
)
|
|
9803
9871
|
};
|
|
@@ -9864,6 +9932,10 @@ function PushApprovalModal({
|
|
|
9864
9932
|
setPhase({ kind: "expired" });
|
|
9865
9933
|
return;
|
|
9866
9934
|
}
|
|
9935
|
+
if (outcome.status === "cancelled") {
|
|
9936
|
+
setPhase({ kind: "cancelled", reason: outcome.reason });
|
|
9937
|
+
return;
|
|
9938
|
+
}
|
|
9867
9939
|
onSettle(outcome);
|
|
9868
9940
|
}).catch(() => {
|
|
9869
9941
|
if (done || controller.signal.aborted) return;
|
|
@@ -9878,6 +9950,19 @@ function PushApprovalModal({
|
|
|
9878
9950
|
if (phase.kind === "denied") {
|
|
9879
9951
|
return /* @__PURE__ */ jsx(Sheet, { open: true, dismissOnOverlay: false, ariaLabel: "Confirmação recusada", children: /* @__PURE__ */ jsx(PushDeniedView, { appName: headerAppName, onCancel: () => onSettle({ status: "denied" }) }) });
|
|
9880
9952
|
}
|
|
9953
|
+
if (phase.kind === "cancelled") {
|
|
9954
|
+
return /* @__PURE__ */ jsx(Sheet, { open: true, dismissOnOverlay: false, ariaLabel: "Solicitação cancelada", children: /* @__PURE__ */ jsx(
|
|
9955
|
+
PushCancelledView,
|
|
9956
|
+
{
|
|
9957
|
+
appName: headerAppName,
|
|
9958
|
+
onFallback: onFallback ? () => {
|
|
9959
|
+
onFallback();
|
|
9960
|
+
onSettle({ status: "cancelled", source: "server", reason: phase.reason });
|
|
9961
|
+
} : void 0,
|
|
9962
|
+
onCancel: () => onSettle({ status: "cancelled", source: "server", reason: phase.reason })
|
|
9963
|
+
}
|
|
9964
|
+
) });
|
|
9965
|
+
}
|
|
9881
9966
|
if (phase.kind === "expired") {
|
|
9882
9967
|
return /* @__PURE__ */ jsx(Sheet, { open: true, dismissOnOverlay: false, ariaLabel: "Tempo esgotado", children: /* @__PURE__ */ jsx(
|
|
9883
9968
|
PushExpiredView,
|
|
@@ -9896,14 +9981,14 @@ function PushApprovalModal({
|
|
|
9896
9981
|
appName: headerAppName,
|
|
9897
9982
|
numericCode,
|
|
9898
9983
|
remainingSeconds: remaining,
|
|
9899
|
-
onCancel: () => onSettle({ status: "cancelled" })
|
|
9984
|
+
onCancel: () => onSettle({ status: "cancelled", source: "user" })
|
|
9900
9985
|
}
|
|
9901
9986
|
) });
|
|
9902
9987
|
}
|
|
9903
9988
|
function awaitPushApproval(options) {
|
|
9904
9989
|
return new Promise((resolve) => {
|
|
9905
9990
|
const container = document.createElement("div");
|
|
9906
|
-
container.id = CONTAINER_ID;
|
|
9991
|
+
container.id = CONTAINER_ID$1;
|
|
9907
9992
|
document.body.appendChild(container);
|
|
9908
9993
|
const root = createRoot(container);
|
|
9909
9994
|
const settle = (outcome) => {
|
|
@@ -9914,6 +9999,392 @@ function awaitPushApproval(options) {
|
|
|
9914
9999
|
root.render(/* @__PURE__ */ jsx(PushApprovalModal, { ...options, onSettle: settle }));
|
|
9915
10000
|
});
|
|
9916
10001
|
}
|
|
10002
|
+
const STYLE_ID$2 = "neofaceid-device-enrollment-styles";
|
|
10003
|
+
const CSS$1 = `
|
|
10004
|
+
.neofaceid-root .nfid-enroll {
|
|
10005
|
+
display: flex; flex-direction: column; align-items: center; text-align: center; gap: 10px;
|
|
10006
|
+
padding: 8px 8px 4px;
|
|
10007
|
+
}
|
|
10008
|
+
.neofaceid-root .nfid-enroll h2 {
|
|
10009
|
+
margin: 6px 0 0; font-size: 20px; font-weight: 700; color: var(--nfid-text); line-height: 1.25;
|
|
10010
|
+
letter-spacing: -0.015em;
|
|
10011
|
+
}
|
|
10012
|
+
.neofaceid-root .nfid-enroll p {
|
|
10013
|
+
margin: 0; font-size: 14px; color: var(--nfid-text-muted); line-height: 1.5;
|
|
10014
|
+
}
|
|
10015
|
+
/* O QR é sempre escuro sobre branco, independente do tema: leitor de código
|
|
10016
|
+
falha com contraste invertido, e a moldura clara faz a zona de silêncio. */
|
|
10017
|
+
.neofaceid-root .nfid-enroll-qr {
|
|
10018
|
+
background: #FFFFFF; padding: 14px; border-radius: 16px; margin-top: 12px;
|
|
10019
|
+
line-height: 0; box-shadow: 0 0 0 1px var(--nfid-line);
|
|
10020
|
+
}
|
|
10021
|
+
.neofaceid-root .nfid-enroll-qr svg { display: block; width: 184px; height: 184px; }
|
|
10022
|
+
.neofaceid-root .nfid-enroll-handle {
|
|
10023
|
+
display: flex; flex-direction: column; align-items: center; gap: 4px;
|
|
10024
|
+
margin-top: 14px; padding: 12px 24px;
|
|
10025
|
+
background: var(--nfid-surface-muted); border-radius: 12px; width: 100%;
|
|
10026
|
+
}
|
|
10027
|
+
.neofaceid-root .nfid-enroll-handle-label {
|
|
10028
|
+
font-size: 11px; font-weight: 600; letter-spacing: 0.08em; text-transform: uppercase;
|
|
10029
|
+
color: var(--nfid-text-subtle);
|
|
10030
|
+
}
|
|
10031
|
+
.neofaceid-root .nfid-enroll-handle-value {
|
|
10032
|
+
font-size: 26px; font-weight: 800; line-height: 1.1; letter-spacing: 0.12em;
|
|
10033
|
+
color: var(--nfid-text); font-variant-numeric: tabular-nums;
|
|
10034
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
10035
|
+
}
|
|
10036
|
+
.neofaceid-root .nfid-enroll-countdown {
|
|
10037
|
+
font-size: 13px; color: var(--nfid-text-muted); font-variant-numeric: tabular-nums;
|
|
10038
|
+
margin-top: 10px;
|
|
10039
|
+
}
|
|
10040
|
+
.neofaceid-root .nfid-enroll-actions {
|
|
10041
|
+
display: flex; flex-direction: column; gap: 10px; width: 100%; margin-top: 16px;
|
|
10042
|
+
}
|
|
10043
|
+
.neofaceid-root .nfid-enroll-note {
|
|
10044
|
+
font-size: 12px; line-height: 1.45; color: var(--nfid-text-muted);
|
|
10045
|
+
background: var(--nfid-surface-muted); border-radius: 10px;
|
|
10046
|
+
padding: 10px 14px; margin-top: 12px; text-align: left; width: 100%;
|
|
10047
|
+
}
|
|
10048
|
+
.neofaceid-root .nfid-enroll-icon { width: 56px; height: 56px; }
|
|
10049
|
+
.neofaceid-root .nfid-enroll-footer {
|
|
10050
|
+
display: flex; justify-content: center; margin-top: 16px; padding-top: 12px;
|
|
10051
|
+
border-top: 1px solid var(--nfid-line); width: 100%;
|
|
10052
|
+
}
|
|
10053
|
+
`;
|
|
10054
|
+
function formatRemaining(seconds) {
|
|
10055
|
+
const safe = Math.max(0, seconds);
|
|
10056
|
+
const mm = Math.floor(safe / 60);
|
|
10057
|
+
const ss = safe % 60;
|
|
10058
|
+
return `${mm}:${String(ss).padStart(2, "0")}`;
|
|
10059
|
+
}
|
|
10060
|
+
function QrCode({ value }) {
|
|
10061
|
+
const [path, setPath] = useState(null);
|
|
10062
|
+
useEffect(() => {
|
|
10063
|
+
let cancelled = false;
|
|
10064
|
+
import("./qrcode-DYfGiWu6.js").then((mod) => {
|
|
10065
|
+
if (cancelled) return;
|
|
10066
|
+
const qrcode = mod.default ?? mod;
|
|
10067
|
+
const qr = qrcode(0, "M");
|
|
10068
|
+
qr.addData(value);
|
|
10069
|
+
qr.make();
|
|
10070
|
+
const count = qr.getModuleCount();
|
|
10071
|
+
const parts = [];
|
|
10072
|
+
for (let r = 0; r < count; r += 1) {
|
|
10073
|
+
for (let c = 0; c < count; c += 1) {
|
|
10074
|
+
if (qr.isDark(r, c)) parts.push(`M${c} ${r}h1v1h-1z`);
|
|
10075
|
+
}
|
|
10076
|
+
}
|
|
10077
|
+
setPath({ d: parts.join(""), count });
|
|
10078
|
+
}).catch(() => {
|
|
10079
|
+
if (!cancelled) setPath(null);
|
|
10080
|
+
});
|
|
10081
|
+
return () => {
|
|
10082
|
+
cancelled = true;
|
|
10083
|
+
};
|
|
10084
|
+
}, [value]);
|
|
10085
|
+
if (!path) {
|
|
10086
|
+
return /* @__PURE__ */ jsx("svg", { viewBox: "0 0 21 21", "aria-hidden": "true" });
|
|
10087
|
+
}
|
|
10088
|
+
return /* @__PURE__ */ jsx(
|
|
10089
|
+
"svg",
|
|
10090
|
+
{
|
|
10091
|
+
viewBox: `0 0 ${path.count} ${path.count}`,
|
|
10092
|
+
shapeRendering: "crispEdges",
|
|
10093
|
+
role: "img",
|
|
10094
|
+
"aria-label": `Código para inscrição do aparelho: ${value.split("").join(" ")}`,
|
|
10095
|
+
children: /* @__PURE__ */ jsx("path", { d: path.d, fill: "#000000" })
|
|
10096
|
+
}
|
|
10097
|
+
);
|
|
10098
|
+
}
|
|
10099
|
+
function DeviceEnrollmentView({
|
|
10100
|
+
appName,
|
|
10101
|
+
handle,
|
|
10102
|
+
remainingSeconds,
|
|
10103
|
+
onCancel
|
|
10104
|
+
}) {
|
|
10105
|
+
useEffect(() => {
|
|
10106
|
+
injectThemeStyles();
|
|
10107
|
+
injectScopedStyles(STYLE_ID$2, CSS$1);
|
|
10108
|
+
}, []);
|
|
10109
|
+
return /* @__PURE__ */ jsxs("div", { className: "nfid-enroll", role: "status", "aria-live": "polite", children: [
|
|
10110
|
+
/* @__PURE__ */ jsx(StatusPill, { tone: "attention", children: "Aguardando o aparelho" }),
|
|
10111
|
+
/* @__PURE__ */ jsx("h2", { children: "Vincule seu celular" }),
|
|
10112
|
+
/* @__PURE__ */ jsxs("p", { children: [
|
|
10113
|
+
"Abra o NeoFaceID Push no celular e aponte para este código para vinculá-lo à sua conta no",
|
|
10114
|
+
" ",
|
|
10115
|
+
appName,
|
|
10116
|
+
"."
|
|
10117
|
+
] }),
|
|
10118
|
+
/* @__PURE__ */ jsx("div", { className: "nfid-enroll-qr", children: /* @__PURE__ */ jsx(QrCode, { value: handle }) }),
|
|
10119
|
+
/* @__PURE__ */ jsxs("div", { className: "nfid-enroll-handle", children: [
|
|
10120
|
+
/* @__PURE__ */ jsx("span", { className: "nfid-enroll-handle-label", children: "Ou digite este código" }),
|
|
10121
|
+
/* @__PURE__ */ jsx("span", { className: "nfid-enroll-handle-value", children: handle })
|
|
10122
|
+
] }),
|
|
10123
|
+
/* @__PURE__ */ jsxs("p", { className: "nfid-enroll-countdown", children: [
|
|
10124
|
+
"O código expira em ",
|
|
10125
|
+
formatRemaining(remainingSeconds)
|
|
10126
|
+
] }),
|
|
10127
|
+
/* @__PURE__ */ jsx("div", { className: "nfid-enroll-actions", children: /* @__PURE__ */ jsx(Button, { variant: "ghost", onClick: onCancel, children: "Cancelar" }) }),
|
|
10128
|
+
/* @__PURE__ */ jsx("div", { className: "nfid-enroll-footer", children: /* @__PURE__ */ jsx(BrandFooter, {}) })
|
|
10129
|
+
] });
|
|
10130
|
+
}
|
|
10131
|
+
function DeviceEnrolledView({ deviceLabel, onDone }) {
|
|
10132
|
+
useEffect(() => {
|
|
10133
|
+
injectThemeStyles();
|
|
10134
|
+
injectScopedStyles(STYLE_ID$2, CSS$1);
|
|
10135
|
+
}, []);
|
|
10136
|
+
return /* @__PURE__ */ jsxs("div", { className: "nfid-enroll", role: "alert", "aria-live": "assertive", children: [
|
|
10137
|
+
/* @__PURE__ */ jsxs(
|
|
10138
|
+
"svg",
|
|
10139
|
+
{
|
|
10140
|
+
className: "nfid-enroll-icon",
|
|
10141
|
+
viewBox: "0 0 24 24",
|
|
10142
|
+
fill: "none",
|
|
10143
|
+
stroke: "currentColor",
|
|
10144
|
+
strokeWidth: "1.75",
|
|
10145
|
+
strokeLinecap: "round",
|
|
10146
|
+
strokeLinejoin: "round",
|
|
10147
|
+
"aria-hidden": "true",
|
|
10148
|
+
style: { color: "#1E9E6A" },
|
|
10149
|
+
children: [
|
|
10150
|
+
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "9" }),
|
|
10151
|
+
/* @__PURE__ */ jsx("path", { d: "M8.5 12.5l2.5 2.5 4.5-5" })
|
|
10152
|
+
]
|
|
10153
|
+
}
|
|
10154
|
+
),
|
|
10155
|
+
/* @__PURE__ */ jsx("h2", { children: "Aparelho vinculado" }),
|
|
10156
|
+
deviceLabel ? /* @__PURE__ */ jsxs("p", { children: [
|
|
10157
|
+
/* @__PURE__ */ jsx("strong", { children: deviceLabel }),
|
|
10158
|
+
" agora pode confirmar suas operações."
|
|
10159
|
+
] }) : /* @__PURE__ */ jsx("p", { children: "Um aparelho acabou de ser vinculado à sua conta." }),
|
|
10160
|
+
/* @__PURE__ */ jsx("div", { className: "nfid-enroll-note", children: deviceLabel ? /* @__PURE__ */ jsx(Fragment, { children: "Se este não é o seu aparelho, procure o suporte agora — alguém pode ter vinculado um celular à sua conta." }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
10161
|
+
/* @__PURE__ */ jsx("strong", { children: "Não conseguimos identificar qual aparelho foi vinculado." }),
|
|
10162
|
+
" Se não foi você quem acabou de fazer isso, procure o suporte agora — alguém pode ter vinculado um celular à sua conta."
|
|
10163
|
+
] }) }),
|
|
10164
|
+
/* @__PURE__ */ jsx("div", { className: "nfid-enroll-actions", children: /* @__PURE__ */ jsx(Button, { variant: "primary", onClick: onDone, children: "Entendi" }) }),
|
|
10165
|
+
/* @__PURE__ */ jsx("div", { className: "nfid-enroll-footer", children: /* @__PURE__ */ jsx(BrandFooter, {}) })
|
|
10166
|
+
] });
|
|
10167
|
+
}
|
|
10168
|
+
function DeviceEnrollmentExpiredView({
|
|
10169
|
+
onRetry,
|
|
10170
|
+
onCancel
|
|
10171
|
+
}) {
|
|
10172
|
+
useEffect(() => {
|
|
10173
|
+
injectThemeStyles();
|
|
10174
|
+
injectScopedStyles(STYLE_ID$2, CSS$1);
|
|
10175
|
+
}, []);
|
|
10176
|
+
return /* @__PURE__ */ jsxs("div", { className: "nfid-enroll", role: "alert", "aria-live": "assertive", children: [
|
|
10177
|
+
/* @__PURE__ */ jsxs(
|
|
10178
|
+
"svg",
|
|
10179
|
+
{
|
|
10180
|
+
className: "nfid-enroll-icon",
|
|
10181
|
+
viewBox: "0 0 24 24",
|
|
10182
|
+
fill: "none",
|
|
10183
|
+
stroke: "currentColor",
|
|
10184
|
+
strokeWidth: "1.75",
|
|
10185
|
+
strokeLinecap: "round",
|
|
10186
|
+
strokeLinejoin: "round",
|
|
10187
|
+
"aria-hidden": "true",
|
|
10188
|
+
style: { color: "var(--nfid-text-muted)" },
|
|
10189
|
+
children: [
|
|
10190
|
+
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "9" }),
|
|
10191
|
+
/* @__PURE__ */ jsx("path", { d: "M12 7v5l3 2" })
|
|
10192
|
+
]
|
|
10193
|
+
}
|
|
10194
|
+
),
|
|
10195
|
+
/* @__PURE__ */ jsx("h2", { children: "O código expirou" }),
|
|
10196
|
+
/* @__PURE__ */ jsx("p", { children: "Códigos de vinculação valem poucos minutos, por segurança. Gere um novo para tentar." }),
|
|
10197
|
+
/* @__PURE__ */ jsxs("div", { className: "nfid-enroll-actions", children: [
|
|
10198
|
+
/* @__PURE__ */ jsx(Button, { variant: "primary", onClick: onRetry, children: "Gerar novo código" }),
|
|
10199
|
+
/* @__PURE__ */ jsx(Button, { variant: "ghost", onClick: onCancel, children: "Agora não" })
|
|
10200
|
+
] }),
|
|
10201
|
+
/* @__PURE__ */ jsx("div", { className: "nfid-enroll-footer", children: /* @__PURE__ */ jsx(BrandFooter, {}) })
|
|
10202
|
+
] });
|
|
10203
|
+
}
|
|
10204
|
+
const CONTAINER_ID = "neofaceid-device-enrollment-container";
|
|
10205
|
+
function classifyEnrollmentFrame(frame) {
|
|
10206
|
+
var _a2, _b, _c;
|
|
10207
|
+
switch (frame.status) {
|
|
10208
|
+
case "enrolled":
|
|
10209
|
+
return {
|
|
10210
|
+
kind: "resolve",
|
|
10211
|
+
value: {
|
|
10212
|
+
status: "enrolled",
|
|
10213
|
+
// Vem `null` enquanto a PUSH-133 não sai. Propagamos o nulo em vez de
|
|
10214
|
+
// inventar um rótulo: a tela precisa poder dizer que não identificou.
|
|
10215
|
+
deviceLabel: ((_a2 = frame.data) == null ? void 0 : _a2.device_label) ?? null,
|
|
10216
|
+
enrolledAt: (_b = frame.data) == null ? void 0 : _b.enrolled_at
|
|
10217
|
+
}
|
|
10218
|
+
};
|
|
10219
|
+
case "expired":
|
|
10220
|
+
return { kind: "resolve", value: { status: "expired" } };
|
|
10221
|
+
case "error":
|
|
10222
|
+
case "failed":
|
|
10223
|
+
return {
|
|
10224
|
+
kind: "reject",
|
|
10225
|
+
error: new NeoFaceError(
|
|
10226
|
+
frame.message ?? ((_c = frame.data) == null ? void 0 : _c.message) ?? "Falha ao inscrever o aparelho",
|
|
10227
|
+
ErrorType.API_ERROR
|
|
10228
|
+
)
|
|
10229
|
+
};
|
|
10230
|
+
default:
|
|
10231
|
+
return { kind: "pending" };
|
|
10232
|
+
}
|
|
10233
|
+
}
|
|
10234
|
+
function DeviceEnrollmentModal({
|
|
10235
|
+
userAccessToken,
|
|
10236
|
+
applicationToken,
|
|
10237
|
+
appName,
|
|
10238
|
+
attempt,
|
|
10239
|
+
onSettle,
|
|
10240
|
+
onRetry,
|
|
10241
|
+
onFatal
|
|
10242
|
+
}) {
|
|
10243
|
+
const headerAppName = appName ?? getAppName() ?? "sua aplicação";
|
|
10244
|
+
const [phase, setPhase] = useState({ kind: "loading" });
|
|
10245
|
+
const [remaining, setRemaining] = useState(0);
|
|
10246
|
+
useEffect(() => {
|
|
10247
|
+
const controller = new AbortController();
|
|
10248
|
+
let done = false;
|
|
10249
|
+
(async () => {
|
|
10250
|
+
let issued;
|
|
10251
|
+
try {
|
|
10252
|
+
issued = await requestDeviceEnrollmentToken(userAccessToken, applicationToken);
|
|
10253
|
+
} catch (err) {
|
|
10254
|
+
if (!done) onFatal(err);
|
|
10255
|
+
return;
|
|
10256
|
+
}
|
|
10257
|
+
if (done) return;
|
|
10258
|
+
setPhase({
|
|
10259
|
+
kind: "waiting",
|
|
10260
|
+
handle: issued.handle,
|
|
10261
|
+
jti: issued.jti,
|
|
10262
|
+
expiresIn: issued.expiresIn
|
|
10263
|
+
});
|
|
10264
|
+
setRemaining(issued.expiresIn);
|
|
10265
|
+
const base = getBaseUrl();
|
|
10266
|
+
const channel = `${base}/api/v1/devices/enrollment/${encodeURIComponent(issued.jti)}/events/`;
|
|
10267
|
+
try {
|
|
10268
|
+
const outcome = await consumeSseStream({
|
|
10269
|
+
signal: controller.signal,
|
|
10270
|
+
timeoutMs: (issued.expiresIn + 30) * 1e3,
|
|
10271
|
+
requestTicket: async () => {
|
|
10272
|
+
var _a2;
|
|
10273
|
+
const res = await fetch(`${channel}ticket/`, {
|
|
10274
|
+
method: "POST",
|
|
10275
|
+
headers: { "Content-Type": "application/json", "X-App-Token": applicationToken }
|
|
10276
|
+
});
|
|
10277
|
+
if (!res.ok) {
|
|
10278
|
+
throw new NeoFaceError(
|
|
10279
|
+
`Falha ao abrir o canal de inscrição: ${res.status}`,
|
|
10280
|
+
res.status === 401 || res.status === 403 ? ErrorType.INVALID_TOKEN : ErrorType.NETWORK
|
|
10281
|
+
);
|
|
10282
|
+
}
|
|
10283
|
+
const body = await res.json();
|
|
10284
|
+
const ticket = ((_a2 = body == null ? void 0 : body.data) == null ? void 0 : _a2.ticket) ?? (body == null ? void 0 : body.ticket);
|
|
10285
|
+
if (!ticket) {
|
|
10286
|
+
throw new NeoFaceError("Canal de inscrição sem ticket", ErrorType.API_ERROR);
|
|
10287
|
+
}
|
|
10288
|
+
return ticket;
|
|
10289
|
+
},
|
|
10290
|
+
buildUrl: (ticket) => `${channel}?ticket=${encodeURIComponent(ticket)}`,
|
|
10291
|
+
classify: classifyEnrollmentFrame
|
|
10292
|
+
});
|
|
10293
|
+
if (done) return;
|
|
10294
|
+
if (outcome.status === "enrolled") {
|
|
10295
|
+
setPhase({
|
|
10296
|
+
kind: "enrolled",
|
|
10297
|
+
deviceLabel: outcome.deviceLabel,
|
|
10298
|
+
enrolledAt: outcome.enrolledAt
|
|
10299
|
+
});
|
|
10300
|
+
return;
|
|
10301
|
+
}
|
|
10302
|
+
setPhase({ kind: "expired" });
|
|
10303
|
+
} catch {
|
|
10304
|
+
if (done || controller.signal.aborted) return;
|
|
10305
|
+
setPhase({ kind: "expired" });
|
|
10306
|
+
}
|
|
10307
|
+
})();
|
|
10308
|
+
return () => {
|
|
10309
|
+
done = true;
|
|
10310
|
+
controller.abort();
|
|
10311
|
+
};
|
|
10312
|
+
}, [attempt]);
|
|
10313
|
+
useEffect(() => {
|
|
10314
|
+
if (phase.kind !== "waiting") return void 0;
|
|
10315
|
+
const id = setInterval(() => setRemaining((r) => r > 0 ? r - 1 : 0), 1e3);
|
|
10316
|
+
return () => clearInterval(id);
|
|
10317
|
+
}, [phase.kind]);
|
|
10318
|
+
if (phase.kind === "loading") return null;
|
|
10319
|
+
if (phase.kind === "enrolled") {
|
|
10320
|
+
return /* @__PURE__ */ jsx(Sheet, { open: true, dismissOnOverlay: false, ariaLabel: "Aparelho vinculado", children: /* @__PURE__ */ jsx(
|
|
10321
|
+
DeviceEnrolledView,
|
|
10322
|
+
{
|
|
10323
|
+
deviceLabel: phase.deviceLabel,
|
|
10324
|
+
onDone: () => onSettle({
|
|
10325
|
+
status: "enrolled",
|
|
10326
|
+
deviceLabel: phase.deviceLabel,
|
|
10327
|
+
enrolledAt: phase.enrolledAt
|
|
10328
|
+
})
|
|
10329
|
+
}
|
|
10330
|
+
) });
|
|
10331
|
+
}
|
|
10332
|
+
if (phase.kind === "expired") {
|
|
10333
|
+
return /* @__PURE__ */ jsx(Sheet, { open: true, dismissOnOverlay: false, ariaLabel: "Código expirado", children: /* @__PURE__ */ jsx(
|
|
10334
|
+
DeviceEnrollmentExpiredView,
|
|
10335
|
+
{
|
|
10336
|
+
onRetry,
|
|
10337
|
+
onCancel: () => onSettle({ status: "expired" })
|
|
10338
|
+
}
|
|
10339
|
+
) });
|
|
10340
|
+
}
|
|
10341
|
+
return /* @__PURE__ */ jsx(Sheet, { open: true, dismissOnOverlay: false, ariaLabel: "Vincular aparelho", children: /* @__PURE__ */ jsx(
|
|
10342
|
+
DeviceEnrollmentView,
|
|
10343
|
+
{
|
|
10344
|
+
appName: headerAppName,
|
|
10345
|
+
handle: phase.handle,
|
|
10346
|
+
remainingSeconds: remaining,
|
|
10347
|
+
onCancel: () => onSettle({ status: "cancelled" })
|
|
10348
|
+
}
|
|
10349
|
+
) });
|
|
10350
|
+
}
|
|
10351
|
+
function startDeviceEnrollment(options) {
|
|
10352
|
+
return new Promise((resolve, reject) => {
|
|
10353
|
+
const container = document.createElement("div");
|
|
10354
|
+
container.id = CONTAINER_ID;
|
|
10355
|
+
document.body.appendChild(container);
|
|
10356
|
+
const root = createRoot(container);
|
|
10357
|
+
let attempt = 0;
|
|
10358
|
+
const cleanup = () => {
|
|
10359
|
+
root.unmount();
|
|
10360
|
+
if (document.body.contains(container)) document.body.removeChild(container);
|
|
10361
|
+
};
|
|
10362
|
+
const render = () => {
|
|
10363
|
+
root.render(
|
|
10364
|
+
/* @__PURE__ */ jsx(
|
|
10365
|
+
DeviceEnrollmentModal,
|
|
10366
|
+
{
|
|
10367
|
+
...options,
|
|
10368
|
+
attempt,
|
|
10369
|
+
onSettle: (outcome) => {
|
|
10370
|
+
cleanup();
|
|
10371
|
+
resolve(outcome);
|
|
10372
|
+
},
|
|
10373
|
+
onRetry: () => {
|
|
10374
|
+
attempt += 1;
|
|
10375
|
+
render();
|
|
10376
|
+
},
|
|
10377
|
+
onFatal: (error) => {
|
|
10378
|
+
cleanup();
|
|
10379
|
+
reject(error);
|
|
10380
|
+
}
|
|
10381
|
+
}
|
|
10382
|
+
)
|
|
10383
|
+
);
|
|
10384
|
+
};
|
|
10385
|
+
render();
|
|
10386
|
+
});
|
|
10387
|
+
}
|
|
9917
10388
|
function evaluateFacePosition(box, videoWidth, videoHeight) {
|
|
9918
10389
|
if (!box || !videoWidth || !videoHeight) {
|
|
9919
10390
|
return {
|
|
@@ -15430,6 +15901,7 @@ export {
|
|
|
15430
15901
|
start,
|
|
15431
15902
|
startAutoLogin,
|
|
15432
15903
|
startBiometricRegistration,
|
|
15904
|
+
startDeviceEnrollment,
|
|
15433
15905
|
startDocumentCapture,
|
|
15434
15906
|
startFaceLogin,
|
|
15435
15907
|
startHandLogin,
|