@auth-link/web 0.0.4 → 1.1.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.js CHANGED
@@ -1673,10 +1673,9 @@ function validateTicket(value, expectedMode, trustedOrigin) {
1673
1673
  }
1674
1674
  if (qr.v !== 1 || qr.requestId !== m.requestId || typeof qr.token !== "string" || !token.test(qr.token) || origin.protocol !== "https:" || origin.origin !== qr.origin || origin.origin.length > 512 || origin.username || origin.password || trustedOrigin && origin.origin !== trustedOrigin) fail("UNTRUSTED_QR");
1675
1675
  const displayCode = m.displayCode === void 0 ? void 0 : validateDisplayCode(m.displayCode);
1676
- if (m.purpose === "confirm" && !displayCode) fail("DYNAMIC_CODE_REQUIRED");
1677
1676
  const expiry = Date.parse(String(m.expiresAt));
1678
1677
  const now = displayCode ? Date.parse(displayCode.serverTime) : Date.now();
1679
- if (!Number.isFinite(expiry) || expiry > now + 18e4) fail("INVALID_EXPIRY");
1678
+ if (!Number.isFinite(expiry) || expiry > now + 36e4) fail("INVALID_EXPIRY");
1680
1679
  return {
1681
1680
  requestId: m.requestId,
1682
1681
  purpose: m.purpose,
@@ -1728,7 +1727,7 @@ async function limitedJson(response) {
1728
1727
  return fail("INVALID_RESPONSE");
1729
1728
  }
1730
1729
  }
1731
- function createAuthLinkClient(options) {
1730
+ function createBackendCall(options) {
1732
1731
  if ("apiKey" in options || "secret" in options) fail("SERVER_SECRET_FORBIDDEN");
1733
1732
  const base = options.baseUrl ?? "/api/authlink";
1734
1733
  const allowedOrigins = new Set(options.allowedBackendOrigins ?? []);
@@ -1793,7 +1792,15 @@ function createAuthLinkClient(options) {
1793
1792
  headers,
1794
1793
  body: JSON.stringify(body)
1795
1794
  });
1796
- if (!response.ok) fail(response.status === 401 || response.status === 403 ? "CMS_UNAUTHORIZED" : "CMS_REQUEST_FAILED");
1795
+ if (!response.ok) {
1796
+ let code = "";
1797
+ try {
1798
+ code = String(object(await limitedJson(response)).code ?? "").replace(/^AUTHLINK_/, "");
1799
+ } catch {
1800
+ }
1801
+ if (["CONFIRMATION_CODE_INVALID", "CONFIRMATION_CODE_LOCKED", "APPROVAL_REQUIRED", "REQUEST_EXPIRED", "PASSCODE_INVALID", "PASSCODE_USED", "PASSCODE_LOCKED", "OTP_NOT_CONFIGURED"].includes(code)) fail(code);
1802
+ fail(response.status === 401 || response.status === 403 ? "CMS_UNAUTHORIZED" : "CMS_REQUEST_FAILED");
1803
+ }
1797
1804
  return await limitedJson(response);
1798
1805
  } catch (error) {
1799
1806
  if (error instanceof AuthLinkError) throw error;
@@ -1804,14 +1811,19 @@ function createAuthLinkClient(options) {
1804
1811
  signal?.removeEventListener("abort", cancel);
1805
1812
  }
1806
1813
  };
1814
+ return call;
1815
+ }
1816
+ function createAuthLinkClient(options) {
1817
+ const call = createBackendCall(options);
1807
1818
  return {
1808
1819
  async create(mode, { idempotencyKey, signal }) {
1809
1820
  if (!["bind", "confirm"].includes(mode) || !token.test(idempotencyKey)) fail("INVALID_CREATE_OPTIONS");
1810
1821
  return validateTicket(await call("/requests", { purpose: mode }, signal, idempotencyKey), mode, options.authLinkOrigin);
1811
1822
  },
1812
- async result(ticket, signal) {
1823
+ async result(ticket, signal, confirmationCode) {
1813
1824
  if (!token.test(ticket.requestId)) fail("INVALID_TICKET");
1814
- return await call(`/requests/${ticket.requestId}/result`, {}, signal);
1825
+ if (typeof confirmationCode !== "string" || !/^[0-9]{6}$/.test(confirmationCode)) fail("CONFIRMATION_CODE_REQUIRED");
1826
+ return await call(`/requests/${ticket.requestId}/result`, { confirmationCode }, signal);
1815
1827
  }
1816
1828
  };
1817
1829
  }
@@ -1833,116 +1845,117 @@ function abortable(promise, signal) {
1833
1845
  }
1834
1846
  async function observeRequest(input, options) {
1835
1847
  const { client, signal, onUpdate } = options;
1836
- const interval = options.intervalMs ?? 1500;
1837
1848
  let ticket;
1838
1849
  try {
1839
- if (interval < 250 || interval > 1e4 || !Number.isFinite(interval)) fail("INVALID_INTERVAL");
1840
1850
  ticket = validateTicket(input, options.expectedMode);
1841
1851
  } catch (error) {
1842
- if (!signal.aborted) {
1843
- const e = error instanceof Error ? error : new AuthLinkError("INVALID_TICKET");
1844
- onUpdate({ ...initialSnapshot(), status: "error", error: e });
1845
- options.onError?.(e);
1846
- }
1852
+ if (!signal.aborted) options.onError?.(error);
1847
1853
  return;
1848
1854
  }
1849
- let frame = ticket.displayCode;
1850
- let serverAnchor = frame ? Date.parse(frame.serverTime) : Date.now();
1851
- let monotonicAnchor = performance.now();
1852
- const serverNow = () => serverAnchor + performance.now() - monotonicAnchor;
1853
1855
  const expiry = Date.parse(ticket.expiresAt);
1854
- let ended = false;
1856
+ const deadline = performance.now() + Math.max(0, expiry - Date.now());
1857
+ let ended = false, busy = false;
1858
+ let done;
1859
+ const finished = new Promise((resolve) => {
1860
+ done = resolve;
1861
+ });
1855
1862
  const controller = new AbortController();
1856
- const cancel = () => controller.abort(signal.reason);
1857
- signal.addEventListener("abort", cancel, { once: true });
1858
- if (signal.aborted) cancel();
1859
- let last = initialSnapshot();
1860
- const update = (value) => {
1861
- if (signal.aborted) return;
1862
- if (JSON.stringify(value) !== JSON.stringify(last)) {
1863
- last = value;
1864
- onUpdate(value);
1865
- }
1863
+ const cancel = () => {
1864
+ ended = true;
1865
+ controller.abort();
1866
+ done();
1866
1867
  };
1868
+ signal.addEventListener("abort", cancel, { once: true });
1869
+ let snapshot = initialSnapshot();
1870
+ const remaining = () => Math.max(0, (deadline - performance.now()) / 1e3);
1867
1871
  const tick = () => {
1868
1872
  if (ended || signal.aborted) return;
1869
- const now = serverNow();
1870
- if (now >= expiry) {
1871
- controller.abort(new AuthLinkError("REQUEST_EXPIRED"));
1873
+ if (remaining() <= 0) {
1874
+ ended = true;
1875
+ controller.abort();
1876
+ onUpdate({ ...snapshot, status: "expired", code: null, submitting: false });
1877
+ void Promise.resolve().then(() => options.onExpired?.()).catch(() => options.onError?.(new AuthLinkError("CALLBACK_FAILED")));
1878
+ done();
1872
1879
  return;
1873
1880
  }
1874
- const codeRemaining = Math.max(0, ((frame ? Date.parse(frame.validUntil) : expiry) - now) / 1e3);
1875
- options.onCodeTiming?.(codeRemaining * 1e3);
1876
- update({
1881
+ snapshot = {
1882
+ ...snapshot,
1877
1883
  status: "pending",
1878
- code: codeRemaining > 0 ? frame?.value ?? ticket.pairingCode : null,
1879
- secondsRemaining: Math.max(0, Math.ceil((expiry - now) / 1e3)),
1880
- codeSecondsRemaining: Math.ceil(codeRemaining),
1881
- error: null
1882
- });
1884
+ code: ticket.pairingCode,
1885
+ secondsRemaining: Math.ceil(remaining()),
1886
+ codeSecondsRemaining: Math.ceil(remaining()),
1887
+ submitting: busy
1888
+ };
1889
+ onUpdate(snapshot);
1883
1890
  };
1884
- const timer = setInterval(tick, 200);
1885
- const finish = (status, result, error) => {
1886
- if (ended || signal.aborted) return;
1887
- ended = true;
1888
- update({ ...last, status, code: null, error: error ?? null });
1889
- try {
1890
- let effect = void 0;
1891
- if (status === "confirmed") effect = options.onConfirmed?.(result);
1892
- else if (status === "rejected") effect = options.onRejected?.({ status: "rejected", code: "REJECTED", requestId: ticket.requestId, purpose: ticket.purpose });
1893
- else if (status === "expired") effect = options.onExpired?.();
1894
- else options.onError?.(error);
1895
- void Promise.resolve(effect).catch(() => {
1896
- if (!signal.aborted) options.onError?.(new AuthLinkError("CALLBACK_FAILED"));
1897
- });
1898
- } catch {
1899
- if (status !== "error") options.onError?.(new AuthLinkError("CALLBACK_FAILED"));
1891
+ const submit = async (confirmationCode) => {
1892
+ if (ended || signal.aborted || busy) return;
1893
+ if (!/^[0-9]{6}$/.test(confirmationCode)) {
1894
+ snapshot = { ...snapshot, error: new AuthLinkError("CONFIRMATION_CODE_REQUIRED") };
1895
+ tick();
1896
+ return;
1900
1897
  }
1901
- };
1902
- try {
1898
+ if (remaining() <= 0) {
1899
+ tick();
1900
+ return;
1901
+ }
1902
+ busy = true;
1903
+ snapshot = { ...snapshot, error: null };
1903
1904
  tick();
1904
- for (; ; ) {
1905
- controller.signal.throwIfAborted();
1906
- const value = object(await abortable(client.result(ticket, controller.signal), controller.signal));
1907
- controller.signal.throwIfAborted();
1905
+ try {
1906
+ const value = object(await abortable(client.result(ticket, controller.signal, confirmationCode), controller.signal));
1907
+ if (ended || signal.aborted) return;
1908
1908
  if (value.requestId !== ticket.requestId) fail("REQUEST_MISMATCH");
1909
1909
  if (value.status === "confirmed") {
1910
- if (!token.test(String(value.verificationId)) || !["device-presence.v1"].includes(String(value.authenticationPolicy)) || value.expiresAt !== ticket.expiresAt || value.purpose !== ticket.purpose || serverNow() >= expiry || "action" in value || "transactionHash" in value) fail("INVALID_CONFIRMATION");
1911
- finish("confirmed", value);
1912
- return;
1913
- }
1914
- if (value.status === "rejected" || value.status === "expired") {
1915
- finish(value.status);
1916
- return;
1917
- }
1918
- if (!["new", "pending", "approved", "consumed"].includes(String(value.status))) fail("INVALID_RESPONSE");
1919
- if (value.displayCode !== void 0) {
1920
- const nextFrame = validateDisplayCode(value.displayCode);
1921
- const nextExpiry = Date.parse(nextFrame.validUntil);
1922
- const currentExpiry = frame ? Date.parse(frame.validUntil) : 0;
1923
- if (!frame || nextExpiry > currentExpiry) {
1924
- serverAnchor = Math.max(serverNow(), Date.parse(nextFrame.serverTime));
1925
- monotonicAnchor = performance.now();
1910
+ if (!token.test(String(value.verificationId)) || value.authenticationPolicy !== "device-presence.v1" || value.expiresAt !== ticket.expiresAt || value.purpose !== ticket.purpose || remaining() <= 0 || "action" in value || "transactionHash" in value) fail("INVALID_CONFIRMATION");
1911
+ ended = true;
1912
+ onUpdate({ ...snapshot, status: "confirmed", code: null, submitting: false });
1913
+ try {
1914
+ await options.onConfirmed?.(value);
1915
+ } catch {
1916
+ options.onError?.(new AuthLinkError("CALLBACK_FAILED"));
1926
1917
  }
1927
- if (!frame || nextExpiry >= currentExpiry) frame = nextFrame;
1918
+ done();
1919
+ } else if (value.status === "rejected" || value.status === "expired") {
1920
+ ended = true;
1921
+ onUpdate({ ...snapshot, status: value.status, code: null, submitting: false });
1922
+ try {
1923
+ if (value.status === "rejected") await options.onRejected?.({ status: "rejected", code: "REJECTED", requestId: ticket.requestId, purpose: ticket.purpose });
1924
+ else await options.onExpired?.();
1925
+ } catch {
1926
+ options.onError?.(new AuthLinkError("CALLBACK_FAILED"));
1927
+ } finally {
1928
+ done();
1929
+ }
1930
+ } else {
1931
+ fail("CONFIRMATION_NOT_READY");
1928
1932
  }
1929
- tick();
1930
- let pause;
1931
- try {
1932
- await abortable(new Promise((resolve) => {
1933
- pause = setTimeout(resolve, interval);
1934
- }), controller.signal);
1935
- } finally {
1936
- clearTimeout(pause);
1933
+ } catch (error) {
1934
+ if (!ended && !signal.aborted) {
1935
+ const raw = error instanceof Error ? error : new AuthLinkError("NETWORK_ERROR");
1936
+ const code = raw.message.replace(/^AUTHLINK_/, "");
1937
+ const failure = ["CONFIRMATION_CODE_LOCKED", "CONFIRMATION_CODE_INVALID", "APPROVAL_REQUIRED", "REQUEST_EXPIRED"].includes(code) ? new AuthLinkError(code) : raw;
1938
+ snapshot = { ...snapshot, error: failure };
1939
+ if (code === "CONFIRMATION_CODE_LOCKED" || code === "REQUEST_EXPIRED") {
1940
+ ended = true;
1941
+ onUpdate({ ...snapshot, code: null, submitting: false, status: code === "REQUEST_EXPIRED" ? "expired" : "error" });
1942
+ if (code === "REQUEST_EXPIRED") void Promise.resolve().then(() => options.onExpired?.()).catch(() => options.onError?.(new AuthLinkError("CALLBACK_FAILED")));
1943
+ else options.onError?.(failure);
1944
+ done();
1945
+ } else options.onError?.(failure);
1937
1946
  }
1947
+ } finally {
1948
+ busy = false;
1949
+ tick();
1938
1950
  }
1939
- } catch (error) {
1940
- if (!signal.aborted && !ended) {
1941
- if (error instanceof AuthLinkError && error.code === "REQUEST_EXPIRED") finish("expired");
1942
- else finish("error", void 0, error instanceof AuthLinkError ? error : new AuthLinkError("NETWORK_ERROR"));
1943
- }
1951
+ };
1952
+ tick();
1953
+ options.onReady?.(submit);
1954
+ const timer = setInterval(tick, 1e3);
1955
+ if (signal.aborted) cancel();
1956
+ try {
1957
+ await finished;
1944
1958
  } finally {
1945
- ended = true;
1946
1959
  clearInterval(timer);
1947
1960
  controller.abort();
1948
1961
  signal.removeEventListener("abort", cancel);
@@ -1954,8 +1967,8 @@ var en_default = {
1954
1967
  bind: "Bind your phone",
1955
1968
  confirm: "Confirm verification",
1956
1969
  scan: "Scan with AuthLink",
1957
- code: "Matching code",
1958
- help: "Check that the numbers match in the app, then confirm on your phone.",
1970
+ code: "Matching digits",
1971
+ help: "Approve in the App, then enter its 6-digit confirmation code here.",
1959
1972
  waiting: "Waiting for your phone",
1960
1973
  loading: "Connecting",
1961
1974
  confirmed: "Confirmed",
@@ -1968,7 +1981,16 @@ var en_default = {
1968
1981
  close: "Close",
1969
1982
  cancel: "Cancel",
1970
1983
  autoUpdate: "Code updates automatically every 30 seconds",
1971
- expiring: "Request expiring soon"
1984
+ expiring: "Request expiring soon",
1985
+ confirmationCode: "App confirmation code",
1986
+ submitCode: "Verify",
1987
+ codeError: "The code could not be verified. Check the code and approval, then retry.",
1988
+ codeLocked: "Too many incorrect codes. Close this window and start a new request.",
1989
+ otpCode: "App verification code",
1990
+ otpHelp: "Open AuthLink and enter this account\u2019s current six-digit code. No approval is needed.",
1991
+ otpWaiting: "Enter the current code",
1992
+ otpError: "Verification failed. Check the account; wait for a new code if already used. After repeated failures, try again later.",
1993
+ otpSetup: "Enable verification codes on this account\u2019s card in the app first."
1972
1994
  };
1973
1995
 
1974
1996
  // src/locales/zh-CN.json
@@ -1976,8 +1998,8 @@ var zh_CN_default = {
1976
1998
  bind: "\u7ED1\u5B9A\u624B\u673A",
1977
1999
  confirm: "\u786E\u8BA4\u8EAB\u4EFD\u9A8C\u8BC1",
1978
2000
  scan: "\u4F7F\u7528 AuthLink \u626B\u63CF\u4E8C\u7EF4\u7801",
1979
- code: "\u52A8\u6001\u6838\u5BF9\u7801",
1980
- help: "\u6838\u5BF9 App \u4E2D\u7684\u6570\u5B57\u4E00\u81F4\u540E\uFF0C\u5728\u624B\u673A\u4E0A\u786E\u8BA4\u3002",
2001
+ code: "\u6838\u5BF9\u6570\u5B57",
2002
+ help: "\u5728 App \u6838\u5BF9\u5E76\u6279\u51C6\u540E\uFF0C\u5C06 App \u663E\u793A\u7684 6 \u4F4D\u786E\u8BA4\u7801\u8F93\u5165\u4E0B\u65B9\u3002",
1981
2003
  waiting: "\u7B49\u5F85\u624B\u673A\u786E\u8BA4",
1982
2004
  loading: "\u6B63\u5728\u8FDE\u63A5",
1983
2005
  confirmed: "\u5DF2\u786E\u8BA4",
@@ -1990,7 +2012,16 @@ var zh_CN_default = {
1990
2012
  close: "\u5173\u95ED",
1991
2013
  cancel: "\u53D6\u6D88",
1992
2014
  autoUpdate: "\u6838\u5BF9\u7801\u6BCF 30 \u79D2\u81EA\u52A8\u66F4\u65B0",
1993
- expiring: "\u8BF7\u6C42\u5373\u5C06\u8FC7\u671F"
2015
+ expiring: "\u8BF7\u6C42\u5373\u5C06\u8FC7\u671F",
2016
+ confirmationCode: "App \u786E\u8BA4\u7801",
2017
+ submitCode: "\u63D0\u4EA4\u9A8C\u8BC1",
2018
+ codeError: "\u9A8C\u8BC1\u672A\u901A\u8FC7\uFF0C\u8BF7\u68C0\u67E5\u786E\u8BA4\u7801\u548C\u624B\u673A\u6279\u51C6\u72B6\u6001\u540E\u91CD\u8BD5\u3002",
2019
+ codeLocked: "\u786E\u8BA4\u7801\u8F93\u5165\u9519\u8BEF\u6B21\u6570\u8FC7\u591A\uFF0C\u8BF7\u5173\u95ED\u7A97\u53E3\u540E\u91CD\u65B0\u53D1\u8D77\u8BF7\u6C42\u3002",
2020
+ otpCode: "App \u52A8\u6001\u7801",
2021
+ otpHelp: "\u6253\u5F00 AuthLink\uFF0C\u8F93\u5165\u8BE5\u8D26\u53F7\u5F53\u524D\u7684\u516D\u4F4D\u52A8\u6001\u7801\uFF0C\u65E0\u9700\u6279\u51C6\u8BF7\u6C42\u3002",
2022
+ otpWaiting: "\u8BF7\u8F93\u5165\u5F53\u524D\u52A8\u6001\u7801",
2023
+ otpError: "\u9A8C\u8BC1\u672A\u901A\u8FC7\u3002\u8BF7\u6838\u5BF9\u8D26\u53F7\uFF1B\u5DF2\u4F7F\u7528\u7684\u7801\u9700\u7B49\u5F85\u66F4\u65B0\uFF0C\u591A\u6B21\u5931\u8D25\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002",
2024
+ otpSetup: "\u8BF7\u5148\u5728 App \u7684\u8D26\u53F7\u5361\u7247\u4E2D\u542F\u7528\u52A8\u6001\u7801\u3002"
1994
2025
  };
1995
2026
 
1996
2027
  // src/locales/zh-TW.json
@@ -1998,8 +2029,8 @@ var zh_TW_default = {
1998
2029
  bind: "\u7D81\u5B9A\u624B\u6A5F",
1999
2030
  confirm: "\u78BA\u8A8D\u8EAB\u5206\u9A57\u8B49",
2000
2031
  scan: "\u4F7F\u7528 AuthLink \u6383\u63CF QR \u78BC",
2001
- code: "\u52D5\u614B\u6838\u5C0D\u78BC",
2002
- help: "\u6838\u5C0D App \u4E2D\u7684\u6578\u5B57\u4E00\u81F4\u5F8C\uFF0C\u5728\u624B\u6A5F\u4E0A\u78BA\u8A8D\u3002",
2032
+ code: "\u6838\u5C0D\u6578\u5B57",
2033
+ help: "\u5728 App \u6838\u5C0D\u4E26\u6279\u51C6\u5F8C\uFF0C\u5C07 App \u986F\u793A\u7684 6 \u4F4D\u78BA\u8A8D\u78BC\u8F38\u5165\u4E0B\u65B9\u3002",
2003
2034
  waiting: "\u7B49\u5F85\u624B\u6A5F\u78BA\u8A8D",
2004
2035
  loading: "\u6B63\u5728\u9023\u7DDA",
2005
2036
  confirmed: "\u5DF2\u78BA\u8A8D",
@@ -2012,7 +2043,16 @@ var zh_TW_default = {
2012
2043
  close: "\u95DC\u9589",
2013
2044
  cancel: "\u53D6\u6D88",
2014
2045
  autoUpdate: "\u6838\u5C0D\u78BC\u6BCF 30 \u79D2\u81EA\u52D5\u66F4\u65B0",
2015
- expiring: "\u8ACB\u6C42\u5373\u5C07\u5230\u671F"
2046
+ expiring: "\u8ACB\u6C42\u5373\u5C07\u5230\u671F",
2047
+ confirmationCode: "App \u78BA\u8A8D\u78BC",
2048
+ submitCode: "\u63D0\u4EA4\u9A57\u8B49",
2049
+ codeError: "\u9A57\u8B49\u672A\u901A\u904E\uFF0C\u8ACB\u6AA2\u67E5\u78BA\u8A8D\u78BC\u53CA\u624B\u6A5F\u6279\u51C6\u72C0\u614B\u5F8C\u91CD\u8A66\u3002",
2050
+ codeLocked: "\u78BA\u8A8D\u78BC\u8F38\u5165\u932F\u8AA4\u6B21\u6578\u904E\u591A\uFF0C\u8ACB\u95DC\u9589\u8996\u7A97\u5F8C\u91CD\u65B0\u767C\u8D77\u8ACB\u6C42\u3002",
2051
+ otpCode: "App \u52D5\u614B\u78BC",
2052
+ otpHelp: "\u958B\u555F AuthLink\uFF0C\u8F38\u5165\u6B64\u5E33\u865F\u76EE\u524D\u7684\u516D\u4F4D\u52D5\u614B\u78BC\uFF0C\u7121\u9700\u6279\u51C6\u8ACB\u6C42\u3002",
2053
+ otpWaiting: "\u8ACB\u8F38\u5165\u76EE\u524D\u7684\u52D5\u614B\u78BC",
2054
+ otpError: "\u9A57\u8B49\u672A\u901A\u904E\u3002\u8ACB\u6838\u5C0D\u5E33\u865F\uFF1B\u5DF2\u4F7F\u7528\u7684\u78BC\u9700\u7B49\u5F85\u66F4\u65B0\uFF0C\u591A\u6B21\u5931\u6557\u8ACB\u7A0D\u5F8C\u91CD\u8A66\u3002",
2055
+ otpSetup: "\u8ACB\u5148\u5728 App \u7684\u5E33\u865F\u5361\u7247\u4E2D\u555F\u7528\u52D5\u614B\u78BC\u3002"
2016
2056
  };
2017
2057
 
2018
2058
  // src/locales/ja.json
@@ -2020,8 +2060,8 @@ var ja_default = {
2020
2060
  bind: "\u30B9\u30DE\u30FC\u30C8\u30D5\u30A9\u30F3\u3092\u767B\u9332",
2021
2061
  confirm: "\u672C\u4EBA\u78BA\u8A8D",
2022
2062
  scan: "AuthLink \u3067 QR \u30B3\u30FC\u30C9\u3092\u30B9\u30AD\u30E3\u30F3",
2023
- code: "\u7167\u5408\u30B3\u30FC\u30C9",
2024
- help: "\u30A2\u30D7\u30EA\u306E\u6570\u5B57\u304C\u4E00\u81F4\u3059\u308B\u3053\u3068\u3092\u78BA\u8A8D\u3057\u3001\u30B9\u30DE\u30FC\u30C8\u30D5\u30A9\u30F3\u3067\u627F\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
2063
+ code: "\u7167\u5408\u7528\u306E\u6570\u5B57",
2064
+ help: "App \u3067\u627F\u8A8D\u3057\u305F\u5F8C\u3001\u8868\u793A\u3055\u308C\u305F6\u6841\u306E\u78BA\u8A8D\u30B3\u30FC\u30C9\u3092\u5165\u529B\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
2025
2065
  waiting: "\u30B9\u30DE\u30FC\u30C8\u30D5\u30A9\u30F3\u306E\u78BA\u8A8D\u3092\u5F85\u3063\u3066\u3044\u307E\u3059",
2026
2066
  loading: "\u63A5\u7D9A\u4E2D",
2027
2067
  confirmed: "\u78BA\u8A8D\u6E08\u307F",
@@ -2034,7 +2074,16 @@ var ja_default = {
2034
2074
  close: "\u9589\u3058\u308B",
2035
2075
  cancel: "\u30AD\u30E3\u30F3\u30BB\u30EB",
2036
2076
  autoUpdate: "\u7167\u5408\u30B3\u30FC\u30C9\u306F30\u79D2\u3054\u3068\u306B\u81EA\u52D5\u66F4\u65B0",
2037
- expiring: "\u30EA\u30AF\u30A8\u30B9\u30C8\u306E\u6709\u52B9\u671F\u9650\u304C\u307E\u3082\u306A\u304F\u5207\u308C\u307E\u3059"
2077
+ expiring: "\u30EA\u30AF\u30A8\u30B9\u30C8\u306E\u6709\u52B9\u671F\u9650\u304C\u307E\u3082\u306A\u304F\u5207\u308C\u307E\u3059",
2078
+ confirmationCode: "App \u306E\u78BA\u8A8D\u30B3\u30FC\u30C9",
2079
+ submitCode: "\u78BA\u8A8D\u3059\u308B",
2080
+ codeError: "\u78BA\u8A8D\u3067\u304D\u307E\u305B\u3093\u3067\u3057\u305F\u3002\u30B3\u30FC\u30C9\u3068\u627F\u8A8D\u72B6\u6CC1\u3092\u78BA\u8A8D\u3057\u3066\u518D\u8A66\u884C\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
2081
+ codeLocked: "\u30B3\u30FC\u30C9\u306E\u8AA4\u5165\u529B\u304C\u591A\u3059\u304E\u307E\u3059\u3002\u753B\u9762\u3092\u9589\u3058\u3001\u65B0\u3057\u3044\u30EA\u30AF\u30A8\u30B9\u30C8\u3092\u958B\u59CB\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
2082
+ otpCode: "\u30A2\u30D7\u30EA\u306E\u8A8D\u8A3C\u30B3\u30FC\u30C9",
2083
+ otpHelp: "AuthLink \u3092\u958B\u304D\u3001\u3053\u306E\u30A2\u30AB\u30A6\u30F3\u30C8\u306E\u73FE\u5728\u306E6\u6841\u30B3\u30FC\u30C9\u3092\u5165\u529B\u3057\u3066\u304F\u3060\u3055\u3044\u3002\u627F\u8A8D\u64CD\u4F5C\u306F\u4E0D\u8981\u3067\u3059\u3002",
2084
+ otpWaiting: "\u73FE\u5728\u306E\u30B3\u30FC\u30C9\u3092\u5165\u529B",
2085
+ otpError: "\u8A8D\u8A3C\u3067\u304D\u307E\u305B\u3093\u3067\u3057\u305F\u3002\u30A2\u30AB\u30A6\u30F3\u30C8\u3092\u78BA\u8A8D\u3057\u3001\u4F7F\u7528\u6E08\u307F\u306A\u3089\u6B21\u306E\u30B3\u30FC\u30C9\u3092\u5F85\u3063\u3066\u304F\u3060\u3055\u3044\u3002\u5931\u6557\u304C\u7D9A\u304F\u5834\u5408\u306F\u6642\u9593\u3092\u304A\u3044\u3066\u518D\u8A66\u884C\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
2086
+ otpSetup: "\u5148\u306B\u30A2\u30D7\u30EA\u306E\u30A2\u30AB\u30A6\u30F3\u30C8\u753B\u9762\u3067\u8A8D\u8A3C\u30B3\u30FC\u30C9\u3092\u6709\u52B9\u306B\u3057\u3066\u304F\u3060\u3055\u3044\u3002"
2038
2087
  };
2039
2088
 
2040
2089
  // src/locales/bn.json
@@ -2043,7 +2092,7 @@ var bn_default = {
2043
2092
  confirm: "\u09AF\u09BE\u099A\u09BE\u0987\u0995\u09B0\u09A3 \u09A8\u09BF\u09B6\u09CD\u099A\u09BF\u09A4 \u0995\u09B0\u09C1\u09A8",
2044
2093
  scan: "AuthLink \u09A6\u09BF\u09AF\u09BC\u09C7 \u09B8\u09CD\u0995\u09CD\u09AF\u09BE\u09A8 \u0995\u09B0\u09C1\u09A8",
2045
2094
  code: "\u09AE\u09BF\u09B2\u09BF\u09AF\u09BC\u09C7 \u09A6\u09C7\u0996\u09BE\u09B0 \u0995\u09CB\u09A1",
2046
- help: "\u0985\u09CD\u09AF\u09BE\u09AA\u09C7\u09B0 \u09B8\u0982\u0996\u09CD\u09AF\u09BE\u0997\u09C1\u09B2\u09CB \u09AE\u09BF\u09B2\u09C7 \u0997\u09C7\u09B2\u09C7 \u09AB\u09CB\u09A8\u09C7 \u09A8\u09BF\u09B6\u09CD\u099A\u09BF\u09A4 \u0995\u09B0\u09C1\u09A8\u0964",
2095
+ help: "\u0985\u09CD\u09AF\u09BE\u09AA\u09C7 \u0985\u09A8\u09C1\u09AE\u09CB\u09A6\u09A8 \u09A6\u09BF\u09A8, \u09A4\u09BE\u09B0\u09AA\u09B0 \u098F\u0996\u09BE\u09A8\u09C7 \u09EC \u09B8\u0982\u0996\u09CD\u09AF\u09BE\u09B0 \u09A8\u09BF\u09B6\u09CD\u099A\u09BF\u09A4\u0995\u09B0\u09A3 \u0995\u09CB\u09A1 \u09B2\u09BF\u0996\u09C1\u09A8\u0964",
2047
2096
  waiting: "\u09AB\u09CB\u09A8\u09C7 \u09A8\u09BF\u09B6\u09CD\u099A\u09BF\u09A4\u0995\u09B0\u09A3\u09C7\u09B0 \u0985\u09AA\u09C7\u0995\u09CD\u09B7\u09BE\u09AF\u09BC",
2048
2097
  loading: "\u09B8\u0982\u09AF\u09CB\u0997 \u0995\u09B0\u09BE \u09B9\u099A\u09CD\u099B\u09C7",
2049
2098
  confirmed: "\u09A8\u09BF\u09B6\u09CD\u099A\u09BF\u09A4 \u09B9\u09AF\u09BC\u09C7\u099B\u09C7",
@@ -2056,7 +2105,16 @@ var bn_default = {
2056
2105
  close: "\u09AC\u09A8\u09CD\u09A7 \u0995\u09B0\u09C1\u09A8",
2057
2106
  cancel: "\u09AC\u09BE\u09A4\u09BF\u09B2",
2058
2107
  autoUpdate: "\u0995\u09CB\u09A1 \u09AA\u09CD\u09B0\u09A4\u09BF \u09E9\u09E6 \u09B8\u09C7\u0995\u09C7\u09A8\u09CD\u09A1\u09C7 \u09B8\u09CD\u09AC\u09AF\u09BC\u0982\u0995\u09CD\u09B0\u09BF\u09AF\u09BC\u09AD\u09BE\u09AC\u09C7 \u09AC\u09A6\u09B2\u09BE\u09AF\u09BC",
2059
- expiring: "\u0985\u09A8\u09C1\u09B0\u09CB\u09A7\u09C7\u09B0 \u09AE\u09C7\u09AF\u09BC\u09BE\u09A6 \u09B6\u09C0\u0998\u09CD\u09B0\u0987 \u09B6\u09C7\u09B7 \u09B9\u09AC\u09C7"
2108
+ expiring: "\u0985\u09A8\u09C1\u09B0\u09CB\u09A7\u09C7\u09B0 \u09AE\u09C7\u09AF\u09BC\u09BE\u09A6 \u09B6\u09C0\u0998\u09CD\u09B0\u0987 \u09B6\u09C7\u09B7 \u09B9\u09AC\u09C7",
2109
+ confirmationCode: "\u0985\u09CD\u09AF\u09BE\u09AA\u09C7\u09B0 \u09A8\u09BF\u09B6\u09CD\u099A\u09BF\u09A4\u0995\u09B0\u09A3 \u0995\u09CB\u09A1",
2110
+ submitCode: "\u09AF\u09BE\u099A\u09BE\u0987 \u0995\u09B0\u09C1\u09A8",
2111
+ codeError: "\u09AF\u09BE\u099A\u09BE\u0987 \u0995\u09B0\u09BE \u09AF\u09BE\u09AF\u09BC\u09A8\u09BF\u0964 \u0995\u09CB\u09A1 \u0993 \u0985\u09A8\u09C1\u09AE\u09CB\u09A6\u09A8 \u09AA\u09B0\u09C0\u0995\u09CD\u09B7\u09BE \u0995\u09B0\u09C7 \u0986\u09AC\u09BE\u09B0 \u099A\u09C7\u09B7\u09CD\u099F\u09BE \u0995\u09B0\u09C1\u09A8\u0964",
2112
+ codeLocked: "\u0985\u09A8\u09C7\u0995\u09AC\u09BE\u09B0 \u09AD\u09C1\u09B2 \u0995\u09CB\u09A1 \u09A6\u09C7\u0993\u09AF\u09BC\u09BE \u09B9\u09AF\u09BC\u09C7\u099B\u09C7\u0964 \u0989\u0987\u09A8\u09CD\u09A1\u09CB \u09AC\u09A8\u09CD\u09A7 \u0995\u09B0\u09C7 \u09A8\u09A4\u09C1\u09A8 \u0985\u09A8\u09C1\u09B0\u09CB\u09A7 \u09B6\u09C1\u09B0\u09C1 \u0995\u09B0\u09C1\u09A8\u0964",
2113
+ otpCode: "\u0985\u09CD\u09AF\u09BE\u09AA\u09C7\u09B0 \u0995\u09CB\u09A1",
2114
+ otpHelp: "AuthLink \u0996\u09C1\u09B2\u09C7 \u098F\u0987 \u0985\u09CD\u09AF\u09BE\u0995\u09BE\u0989\u09A8\u09CD\u099F\u09C7\u09B0 \u09AC\u09B0\u09CD\u09A4\u09AE\u09BE\u09A8 \u099B\u09AF\u09BC \u0985\u0999\u09CD\u0995\u09C7\u09B0 \u0995\u09CB\u09A1 \u09B2\u09BF\u0996\u09C1\u09A8\u0964 \u0985\u09A8\u09C1\u09B0\u09CB\u09A7 \u0985\u09A8\u09C1\u09AE\u09CB\u09A6\u09A8\u09C7\u09B0 \u09AA\u09CD\u09B0\u09AF\u09BC\u09CB\u099C\u09A8 \u09A8\u09C7\u0987\u0964",
2115
+ otpWaiting: "\u09AC\u09B0\u09CD\u09A4\u09AE\u09BE\u09A8 \u0995\u09CB\u09A1 \u09B2\u09BF\u0996\u09C1\u09A8",
2116
+ otpError: "\u09AF\u09BE\u099A\u09BE\u0987 \u09AC\u09CD\u09AF\u09B0\u09CD\u09A5\u0964 \u0985\u09CD\u09AF\u09BE\u0995\u09BE\u0989\u09A8\u09CD\u099F \u09AA\u09B0\u09C0\u0995\u09CD\u09B7\u09BE \u0995\u09B0\u09C1\u09A8 \u098F\u09AC\u0982 \u09AC\u09CD\u09AF\u09AC\u09B9\u09C3\u09A4 \u0995\u09CB\u09A1 \u09B9\u09B2\u09C7 \u09A8\u09A4\u09C1\u09A8 \u0995\u09CB\u09A1\u09C7\u09B0 \u099C\u09A8\u09CD\u09AF \u0985\u09AA\u09C7\u0995\u09CD\u09B7\u09BE \u0995\u09B0\u09C1\u09A8\u0964 \u09AC\u09BE\u09B0\u09AC\u09BE\u09B0 \u09AC\u09CD\u09AF\u09B0\u09CD\u09A5 \u09B9\u09B2\u09C7 \u09AA\u09B0\u09C7 \u099A\u09C7\u09B7\u09CD\u099F\u09BE \u0995\u09B0\u09C1\u09A8\u0964",
2117
+ otpSetup: "\u09AA\u09CD\u09B0\u09A5\u09AE\u09C7 \u0985\u09CD\u09AF\u09BE\u09AA\u09C7 \u098F\u0987 \u0985\u09CD\u09AF\u09BE\u0995\u09BE\u0989\u09A8\u09CD\u099F\u09C7\u09B0 \u0995\u09BE\u09B0\u09CD\u09A1\u09C7 \u0995\u09CB\u09A1 \u099A\u09BE\u09B2\u09C1 \u0995\u09B0\u09C1\u09A8\u0964"
2060
2118
  };
2061
2119
 
2062
2120
  // src/locales/es.json
@@ -2065,7 +2123,7 @@ var es_default = {
2065
2123
  confirm: "Confirma la verificaci\xF3n",
2066
2124
  scan: "Escanea con AuthLink",
2067
2125
  code: "C\xF3digo de comparaci\xF3n",
2068
- help: "Comprueba que los n\xFAmeros coincidan en la aplicaci\xF3n y confirma en tu tel\xE9fono.",
2126
+ help: "Aprueba en la app e introduce aqu\xED su c\xF3digo de 6 d\xEDgitos.",
2069
2127
  waiting: "Esperando la confirmaci\xF3n del tel\xE9fono",
2070
2128
  loading: "Conectando",
2071
2129
  confirmed: "Confirmado",
@@ -2078,7 +2136,16 @@ var es_default = {
2078
2136
  close: "Cerrar",
2079
2137
  cancel: "Cancelar",
2080
2138
  autoUpdate: "El c\xF3digo se actualiza autom\xE1ticamente cada 30 segundos",
2081
- expiring: "La solicitud est\xE1 por caducar"
2139
+ expiring: "La solicitud est\xE1 por caducar",
2140
+ confirmationCode: "C\xF3digo de confirmaci\xF3n de la app",
2141
+ submitCode: "Verificar",
2142
+ codeError: "No se pudo verificar. Revisa el c\xF3digo y la aprobaci\xF3n.",
2143
+ codeLocked: "Demasiados c\xF3digos incorrectos. Cierra la ventana e inicia una nueva solicitud.",
2144
+ otpCode: "C\xF3digo de la app",
2145
+ otpHelp: "Abre AuthLink e introduce el c\xF3digo actual de seis d\xEDgitos de esta cuenta. No hace falta aprobar una solicitud.",
2146
+ otpWaiting: "Introduce el c\xF3digo actual",
2147
+ otpError: "No se pudo verificar. Comprueba la cuenta y espera un c\xF3digo nuevo si ya lo usaste. Si fallas varias veces, int\xE9ntalo m\xE1s tarde.",
2148
+ otpSetup: "Activa primero los c\xF3digos en la tarjeta de esta cuenta en la app."
2082
2149
  };
2083
2150
 
2084
2151
  // src/locales/fil.json
@@ -2087,7 +2154,7 @@ var fil_default = {
2087
2154
  confirm: "Kumpirmahin ang pagpapatunay",
2088
2155
  scan: "Mag-scan gamit ang AuthLink",
2089
2156
  code: "Code na paghahambingin",
2090
- help: "Tiyaking magkapareho ang mga numero sa app, pagkatapos ay kumpirmahin sa iyong telepono.",
2157
+ help: "Aprubahan sa app, saka ilagay dito ang 6-digit na confirmation code.",
2091
2158
  waiting: "Hinihintay ang kumpirmasyon sa telepono",
2092
2159
  loading: "Kumokonekta",
2093
2160
  confirmed: "Nakumpirma",
@@ -2100,7 +2167,16 @@ var fil_default = {
2100
2167
  close: "Isara",
2101
2168
  cancel: "Kanselahin",
2102
2169
  autoUpdate: "Awtomatikong nagbabago ang code kada 30 segundo",
2103
- expiring: "Malapit nang mag-expire ang kahilingan"
2170
+ expiring: "Malapit nang mag-expire ang kahilingan",
2171
+ confirmationCode: "Confirmation code ng app",
2172
+ submitCode: "I-verify",
2173
+ codeError: "Hindi ma-verify. Suriin ang code at pag-apruba at subukan muli.",
2174
+ codeLocked: "Masyadong maraming maling code. Isara ang window at magsimula ng bagong request.",
2175
+ otpCode: "Code ng app",
2176
+ otpHelp: "Buksan ang AuthLink at ilagay ang kasalukuyang anim na digit na code ng account na ito. Hindi kailangang aprubahan ang request.",
2177
+ otpWaiting: "Ilagay ang kasalukuyang code",
2178
+ otpError: "Hindi napatunayan. Tingnan ang account at maghintay ng bagong code kung nagamit na. Subukang muli mamaya kung maraming maling pagtatangka.",
2179
+ otpSetup: "Paganahin muna ang mga code sa card ng account na ito sa app."
2104
2180
  };
2105
2181
 
2106
2182
  // src/locales/hi.json
@@ -2109,7 +2185,7 @@ var hi_default = {
2109
2185
  confirm: "\u0938\u0924\u094D\u092F\u093E\u092A\u0928 \u0915\u0940 \u092A\u0941\u0937\u094D\u091F\u093F \u0915\u0930\u0947\u0902",
2110
2186
  scan: "AuthLink \u0938\u0947 \u0938\u094D\u0915\u0948\u0928 \u0915\u0930\u0947\u0902",
2111
2187
  code: "\u092E\u093F\u0932\u093E\u0928 \u0915\u094B\u0921",
2112
- help: "\u0910\u092A \u092E\u0947\u0902 \u0938\u0902\u0916\u094D\u092F\u093E\u0913\u0902 \u0915\u093E \u092E\u093F\u0932\u093E\u0928 \u0915\u0930\u0947\u0902, \u092B\u093F\u0930 \u0905\u092A\u0928\u0947 \u092B\u093C\u094B\u0928 \u092A\u0930 \u092A\u0941\u0937\u094D\u091F\u093F \u0915\u0930\u0947\u0902\u0964",
2188
+ help: "\u0910\u092A \u092E\u0947\u0902 \u0938\u094D\u0935\u0940\u0915\u0943\u0924\u093F \u0926\u0947\u0902, \u092B\u093F\u0930 \u092F\u0939\u093E\u0901 6 \u0905\u0902\u0915\u094B\u0902 \u0915\u093E \u092A\u0941\u0937\u094D\u091F\u093F \u0915\u094B\u0921 \u0926\u0930\u094D\u091C \u0915\u0930\u0947\u0902\u0964",
2113
2189
  waiting: "\u092B\u093C\u094B\u0928 \u092A\u0930 \u092A\u0941\u0937\u094D\u091F\u093F \u0915\u0940 \u092A\u094D\u0930\u0924\u0940\u0915\u094D\u0937\u093E \u0939\u0948",
2114
2190
  loading: "\u0915\u0928\u0947\u0915\u094D\u091F \u0939\u094B \u0930\u0939\u093E \u0939\u0948",
2115
2191
  confirmed: "\u092A\u0941\u0937\u094D\u091F\u093F \u0939\u094B \u0917\u0908",
@@ -2122,7 +2198,16 @@ var hi_default = {
2122
2198
  close: "\u092C\u0902\u0926 \u0915\u0930\u0947\u0902",
2123
2199
  cancel: "\u0930\u0926\u094D\u0926 \u0915\u0930\u0947\u0902",
2124
2200
  autoUpdate: "\u0915\u094B\u0921 \u0939\u0930 30 \u0938\u0947\u0915\u0902\u0921 \u092E\u0947\u0902 \u0905\u092A\u0928\u0947 \u0906\u092A \u092C\u0926\u0932\u0924\u093E \u0939\u0948",
2125
- expiring: "\u0905\u0928\u0941\u0930\u094B\u0927 \u0915\u0940 \u0938\u092E\u092F-\u0938\u0940\u092E\u093E \u0938\u092E\u093E\u092A\u094D\u0924 \u0939\u094B\u0928\u0947 \u0935\u093E\u0932\u0940 \u0939\u0948"
2201
+ expiring: "\u0905\u0928\u0941\u0930\u094B\u0927 \u0915\u0940 \u0938\u092E\u092F-\u0938\u0940\u092E\u093E \u0938\u092E\u093E\u092A\u094D\u0924 \u0939\u094B\u0928\u0947 \u0935\u093E\u0932\u0940 \u0939\u0948",
2202
+ confirmationCode: "\u0910\u092A \u0915\u093E \u092A\u0941\u0937\u094D\u091F\u093F \u0915\u094B\u0921",
2203
+ submitCode: "\u0938\u0924\u094D\u092F\u093E\u092A\u093F\u0924 \u0915\u0930\u0947\u0902",
2204
+ codeError: "\u0938\u0924\u094D\u092F\u093E\u092A\u0928 \u0935\u093F\u092B\u0932 \u0930\u0939\u093E\u0964 \u0915\u094B\u0921 \u0914\u0930 \u0938\u094D\u0935\u0940\u0915\u0943\u0924\u093F \u091C\u093E\u0901\u091A\u0915\u0930 \u092A\u0941\u0928\u0903 \u092A\u094D\u0930\u092F\u093E\u0938 \u0915\u0930\u0947\u0902\u0964",
2205
+ codeLocked: "\u092C\u0939\u0941\u0924 \u0905\u0927\u093F\u0915 \u0917\u0932\u0924 \u0915\u094B\u0921\u0964 \u0935\u093F\u0902\u0921\u094B \u092C\u0902\u0926 \u0915\u0930\u0915\u0947 \u0928\u092F\u093E \u0905\u0928\u0941\u0930\u094B\u0927 \u0936\u0941\u0930\u0942 \u0915\u0930\u0947\u0902\u0964",
2206
+ otpCode: "\u0910\u092A \u0915\u093E \u0915\u094B\u0921",
2207
+ otpHelp: "AuthLink \u0916\u094B\u0932\u0947\u0902 \u0914\u0930 \u0907\u0938 \u0916\u093E\u0924\u0947 \u0915\u093E \u0935\u0930\u094D\u0924\u092E\u093E\u0928 \u091B\u0939 \u0905\u0902\u0915\u094B\u0902 \u0915\u093E \u0915\u094B\u0921 \u0926\u0930\u094D\u091C \u0915\u0930\u0947\u0902\u0964 \u0905\u0928\u0941\u0930\u094B\u0927 \u0938\u094D\u0935\u0940\u0915\u0943\u0924 \u0915\u0930\u0928\u0947 \u0915\u0940 \u0906\u0935\u0936\u094D\u092F\u0915\u0924\u093E \u0928\u0939\u0940\u0902 \u0939\u0948\u0964",
2208
+ otpWaiting: "\u0935\u0930\u094D\u0924\u092E\u093E\u0928 \u0915\u094B\u0921 \u0926\u0930\u094D\u091C \u0915\u0930\u0947\u0902",
2209
+ otpError: "\u0938\u0924\u094D\u092F\u093E\u092A\u0928 \u0905\u0938\u092B\u0932\u0964 \u0916\u093E\u0924\u093E \u091C\u093E\u0901\u091A\u0947\u0902 \u0914\u0930 \u0909\u092A\u092F\u094B\u0917 \u0939\u094B \u091A\u0941\u0915\u093E \u0939\u094B \u0924\u094B \u0928\u090F \u0915\u094B\u0921 \u0915\u0940 \u092A\u094D\u0930\u0924\u0940\u0915\u094D\u0937\u093E \u0915\u0930\u0947\u0902\u0964 \u092C\u093E\u0930-\u092C\u093E\u0930 \u0905\u0938\u092B\u0932 \u0939\u094B\u0928\u0947 \u092A\u0930 \u092C\u093E\u0926 \u092E\u0947\u0902 \u092A\u094D\u0930\u092F\u093E\u0938 \u0915\u0930\u0947\u0902\u0964",
2210
+ otpSetup: "\u092A\u0939\u0932\u0947 \u0910\u092A \u092E\u0947\u0902 \u0907\u0938 \u0916\u093E\u0924\u0947 \u0915\u0947 \u0915\u093E\u0930\u094D\u0921 \u092A\u0930 \u0915\u094B\u0921 \u0938\u0915\u094D\u0937\u092E \u0915\u0930\u0947\u0902\u0964"
2126
2211
  };
2127
2212
 
2128
2213
  // src/locales/id.json
@@ -2131,7 +2216,7 @@ var id_default = {
2131
2216
  confirm: "Konfirmasi verifikasi",
2132
2217
  scan: "Pindai dengan AuthLink",
2133
2218
  code: "Kode pencocokan",
2134
- help: "Pastikan angka di aplikasi cocok, lalu konfirmasi di ponsel Anda.",
2219
+ help: "Setujui di aplikasi, lalu masukkan kode konfirmasi 6 digit di sini.",
2135
2220
  waiting: "Menunggu konfirmasi dari ponsel",
2136
2221
  loading: "Menghubungkan",
2137
2222
  confirmed: "Dikonfirmasi",
@@ -2144,7 +2229,16 @@ var id_default = {
2144
2229
  close: "Tutup",
2145
2230
  cancel: "Batal",
2146
2231
  autoUpdate: "Kode diperbarui otomatis setiap 30 detik",
2147
- expiring: "Permintaan akan segera kedaluwarsa"
2232
+ expiring: "Permintaan akan segera kedaluwarsa",
2233
+ confirmationCode: "Kode konfirmasi aplikasi",
2234
+ submitCode: "Verifikasi",
2235
+ codeError: "Verifikasi gagal. Periksa kode dan persetujuan, lalu coba lagi.",
2236
+ codeLocked: "Terlalu banyak kode salah. Tutup jendela dan mulai permintaan baru.",
2237
+ otpCode: "Kode aplikasi",
2238
+ otpHelp: "Buka AuthLink dan masukkan kode enam digit terkini untuk akun ini. Tidak perlu menyetujui permintaan.",
2239
+ otpWaiting: "Masukkan kode terkini",
2240
+ otpError: "Verifikasi gagal. Periksa akun dan tunggu kode baru jika sudah digunakan. Setelah beberapa kegagalan, coba lagi nanti.",
2241
+ otpSetup: "Aktifkan kode di kartu akun ini dalam aplikasi terlebih dahulu."
2148
2242
  };
2149
2243
 
2150
2244
  // src/locales/ms.json
@@ -2153,7 +2247,7 @@ var ms_default = {
2153
2247
  confirm: "Sahkan pengesahan",
2154
2248
  scan: "Imbas dengan AuthLink",
2155
2249
  code: "Kod padanan",
2156
- help: "Pastikan nombor dalam aplikasi sepadan, kemudian sahkan pada telefon anda.",
2250
+ help: "Luluskan dalam aplikasi, kemudian masukkan kod pengesahan 6 digit di sini.",
2157
2251
  waiting: "Menunggu pengesahan daripada telefon",
2158
2252
  loading: "Sedang menyambung",
2159
2253
  confirmed: "Disahkan",
@@ -2166,7 +2260,16 @@ var ms_default = {
2166
2260
  close: "Tutup",
2167
2261
  cancel: "Batal",
2168
2262
  autoUpdate: "Kod dikemas kini secara automatik setiap 30 saat",
2169
- expiring: "Permintaan akan tamat tempoh tidak lama lagi"
2263
+ expiring: "Permintaan akan tamat tempoh tidak lama lagi",
2264
+ confirmationCode: "Kod pengesahan aplikasi",
2265
+ submitCode: "Sahkan",
2266
+ codeError: "Pengesahan gagal. Semak kod dan kelulusan, kemudian cuba lagi.",
2267
+ codeLocked: "Terlalu banyak kod salah. Tutup tetingkap dan mulakan permintaan baharu.",
2268
+ otpCode: "Kod aplikasi",
2269
+ otpHelp: "Buka AuthLink dan masukkan kod enam digit semasa untuk akaun ini. Tiada kelulusan permintaan diperlukan.",
2270
+ otpWaiting: "Masukkan kod semasa",
2271
+ otpError: "Pengesahan gagal. Semak akaun dan tunggu kod baharu jika sudah digunakan. Selepas beberapa kegagalan, cuba lagi kemudian.",
2272
+ otpSetup: "Aktifkan kod pada kad akaun ini dalam aplikasi dahulu."
2170
2273
  };
2171
2274
 
2172
2275
  // src/locales/pt.json
@@ -2175,7 +2278,7 @@ var pt_default = {
2175
2278
  confirm: "Confirme a verifica\xE7\xE3o",
2176
2279
  scan: "Escaneie com o AuthLink",
2177
2280
  code: "C\xF3digo de compara\xE7\xE3o",
2178
- help: "Confira se os n\xFAmeros no aplicativo coincidem e confirme no celular.",
2281
+ help: "Aprove no app e digite aqui o c\xF3digo de 6 d\xEDgitos.",
2179
2282
  waiting: "Aguardando confirma\xE7\xE3o no celular",
2180
2283
  loading: "Conectando",
2181
2284
  confirmed: "Confirmado",
@@ -2188,7 +2291,16 @@ var pt_default = {
2188
2291
  close: "Fechar",
2189
2292
  cancel: "Cancelar",
2190
2293
  autoUpdate: "O c\xF3digo \xE9 atualizado automaticamente a cada 30 segundos",
2191
- expiring: "A solicita\xE7\xE3o est\xE1 prestes a expirar"
2294
+ expiring: "A solicita\xE7\xE3o est\xE1 prestes a expirar",
2295
+ confirmationCode: "C\xF3digo de confirma\xE7\xE3o do app",
2296
+ submitCode: "Verificar",
2297
+ codeError: "N\xE3o foi poss\xEDvel verificar. Confira o c\xF3digo e a aprova\xE7\xE3o.",
2298
+ codeLocked: "Muitos c\xF3digos incorretos. Feche a janela e inicie uma nova solicita\xE7\xE3o.",
2299
+ otpCode: "C\xF3digo do aplicativo",
2300
+ otpHelp: "Abra o AuthLink e digite o c\xF3digo atual de seis d\xEDgitos desta conta. N\xE3o \xE9 necess\xE1rio aprovar uma solicita\xE7\xE3o.",
2301
+ otpWaiting: "Digite o c\xF3digo atual",
2302
+ otpError: "Falha na verifica\xE7\xE3o. Confira a conta e aguarde um novo c\xF3digo se j\xE1 o usou. Ap\xF3s v\xE1rias falhas, tente mais tarde.",
2303
+ otpSetup: "Ative primeiro os c\xF3digos no cart\xE3o desta conta no aplicativo."
2192
2304
  };
2193
2305
 
2194
2306
  // src/locales/th.json
@@ -2197,7 +2309,7 @@ var th_default = {
2197
2309
  confirm: "\u0E22\u0E37\u0E19\u0E22\u0E31\u0E19\u0E01\u0E32\u0E23\u0E15\u0E23\u0E27\u0E08\u0E2A\u0E2D\u0E1A",
2198
2310
  scan: "\u0E2A\u0E41\u0E01\u0E19\u0E14\u0E49\u0E27\u0E22 AuthLink",
2199
2311
  code: "\u0E23\u0E2B\u0E31\u0E2A\u0E2A\u0E33\u0E2B\u0E23\u0E31\u0E1A\u0E40\u0E1B\u0E23\u0E35\u0E22\u0E1A\u0E40\u0E17\u0E35\u0E22\u0E1A",
2200
- help: "\u0E15\u0E23\u0E27\u0E08\u0E2A\u0E2D\u0E1A\u0E27\u0E48\u0E32\u0E15\u0E31\u0E27\u0E40\u0E25\u0E02\u0E43\u0E19\u0E41\u0E2D\u0E1B\u0E15\u0E23\u0E07\u0E01\u0E31\u0E19 \u0E41\u0E25\u0E49\u0E27\u0E08\u0E36\u0E07\u0E22\u0E37\u0E19\u0E22\u0E31\u0E19\u0E1A\u0E19\u0E42\u0E17\u0E23\u0E28\u0E31\u0E1E\u0E17\u0E4C\u0E02\u0E2D\u0E07\u0E04\u0E38\u0E13",
2312
+ help: "\u0E2D\u0E19\u0E38\u0E21\u0E31\u0E15\u0E34\u0E43\u0E19\u0E41\u0E2D\u0E1B \u0E41\u0E25\u0E49\u0E27\u0E01\u0E23\u0E2D\u0E01\u0E23\u0E2B\u0E31\u0E2A\u0E22\u0E37\u0E19\u0E22\u0E31\u0E19 6 \u0E2B\u0E25\u0E31\u0E01\u0E17\u0E35\u0E48\u0E19\u0E35\u0E48",
2201
2313
  waiting: "\u0E01\u0E33\u0E25\u0E31\u0E07\u0E23\u0E2D\u0E01\u0E32\u0E23\u0E22\u0E37\u0E19\u0E22\u0E31\u0E19\u0E08\u0E32\u0E01\u0E42\u0E17\u0E23\u0E28\u0E31\u0E1E\u0E17\u0E4C",
2202
2314
  loading: "\u0E01\u0E33\u0E25\u0E31\u0E07\u0E40\u0E0A\u0E37\u0E48\u0E2D\u0E21\u0E15\u0E48\u0E2D",
2203
2315
  confirmed: "\u0E22\u0E37\u0E19\u0E22\u0E31\u0E19\u0E41\u0E25\u0E49\u0E27",
@@ -2210,7 +2322,16 @@ var th_default = {
2210
2322
  close: "\u0E1B\u0E34\u0E14",
2211
2323
  cancel: "\u0E22\u0E01\u0E40\u0E25\u0E34\u0E01",
2212
2324
  autoUpdate: "\u0E23\u0E2B\u0E31\u0E2A\u0E08\u0E30\u0E2D\u0E31\u0E1B\u0E40\u0E14\u0E15\u0E2D\u0E31\u0E15\u0E42\u0E19\u0E21\u0E31\u0E15\u0E34\u0E17\u0E38\u0E01 30 \u0E27\u0E34\u0E19\u0E32\u0E17\u0E35",
2213
- expiring: "\u0E04\u0E33\u0E02\u0E2D\u0E43\u0E01\u0E25\u0E49\u0E2B\u0E21\u0E14\u0E2D\u0E32\u0E22\u0E38"
2325
+ expiring: "\u0E04\u0E33\u0E02\u0E2D\u0E43\u0E01\u0E25\u0E49\u0E2B\u0E21\u0E14\u0E2D\u0E32\u0E22\u0E38",
2326
+ confirmationCode: "\u0E23\u0E2B\u0E31\u0E2A\u0E22\u0E37\u0E19\u0E22\u0E31\u0E19\u0E08\u0E32\u0E01\u0E41\u0E2D\u0E1B",
2327
+ submitCode: "\u0E22\u0E37\u0E19\u0E22\u0E31\u0E19",
2328
+ codeError: "\u0E22\u0E37\u0E19\u0E22\u0E31\u0E19\u0E44\u0E21\u0E48\u0E2A\u0E33\u0E40\u0E23\u0E47\u0E08 \u0E15\u0E23\u0E27\u0E08\u0E2A\u0E2D\u0E1A\u0E23\u0E2B\u0E31\u0E2A\u0E41\u0E25\u0E30\u0E01\u0E32\u0E23\u0E2D\u0E19\u0E38\u0E21\u0E31\u0E15\u0E34\u0E41\u0E25\u0E49\u0E27\u0E25\u0E2D\u0E07\u0E2D\u0E35\u0E01\u0E04\u0E23\u0E31\u0E49\u0E07",
2329
+ codeLocked: "\u0E01\u0E23\u0E2D\u0E01\u0E23\u0E2B\u0E31\u0E2A\u0E1C\u0E34\u0E14\u0E2B\u0E25\u0E32\u0E22\u0E04\u0E23\u0E31\u0E49\u0E07\u0E40\u0E01\u0E34\u0E19\u0E44\u0E1B \u0E42\u0E1B\u0E23\u0E14\u0E1B\u0E34\u0E14\u0E2B\u0E19\u0E49\u0E32\u0E15\u0E48\u0E32\u0E07\u0E41\u0E25\u0E49\u0E27\u0E40\u0E23\u0E34\u0E48\u0E21\u0E04\u0E33\u0E02\u0E2D\u0E43\u0E2B\u0E21\u0E48",
2330
+ otpCode: "\u0E23\u0E2B\u0E31\u0E2A\u0E08\u0E32\u0E01\u0E41\u0E2D\u0E1B",
2331
+ otpHelp: "\u0E40\u0E1B\u0E34\u0E14 AuthLink \u0E41\u0E25\u0E30\u0E01\u0E23\u0E2D\u0E01\u0E23\u0E2B\u0E31\u0E2A\u0E2B\u0E01\u0E2B\u0E25\u0E31\u0E01\u0E1B\u0E31\u0E08\u0E08\u0E38\u0E1A\u0E31\u0E19\u0E02\u0E2D\u0E07\u0E1A\u0E31\u0E0D\u0E0A\u0E35\u0E19\u0E35\u0E49 \u0E44\u0E21\u0E48\u0E15\u0E49\u0E2D\u0E07\u0E2D\u0E19\u0E38\u0E21\u0E31\u0E15\u0E34\u0E04\u0E33\u0E02\u0E2D",
2332
+ otpWaiting: "\u0E01\u0E23\u0E2D\u0E01\u0E23\u0E2B\u0E31\u0E2A\u0E1B\u0E31\u0E08\u0E08\u0E38\u0E1A\u0E31\u0E19",
2333
+ otpError: "\u0E22\u0E37\u0E19\u0E22\u0E31\u0E19\u0E44\u0E21\u0E48\u0E2A\u0E33\u0E40\u0E23\u0E47\u0E08 \u0E42\u0E1B\u0E23\u0E14\u0E15\u0E23\u0E27\u0E08\u0E2A\u0E2D\u0E1A\u0E1A\u0E31\u0E0D\u0E0A\u0E35\u0E41\u0E25\u0E30\u0E23\u0E2D\u0E23\u0E2B\u0E31\u0E2A\u0E43\u0E2B\u0E21\u0E48\u0E2B\u0E32\u0E01\u0E43\u0E0A\u0E49\u0E44\u0E1B\u0E41\u0E25\u0E49\u0E27 \u0E2B\u0E32\u0E01\u0E1C\u0E34\u0E14\u0E2B\u0E25\u0E32\u0E22\u0E04\u0E23\u0E31\u0E49\u0E07\u0E43\u0E2B\u0E49\u0E25\u0E2D\u0E07\u0E43\u0E2B\u0E21\u0E48\u0E20\u0E32\u0E22\u0E2B\u0E25\u0E31\u0E07",
2334
+ otpSetup: "\u0E40\u0E1B\u0E34\u0E14\u0E43\u0E0A\u0E49\u0E23\u0E2B\u0E31\u0E2A\u0E1A\u0E19\u0E01\u0E32\u0E23\u0E4C\u0E14\u0E1A\u0E31\u0E0D\u0E0A\u0E35\u0E19\u0E35\u0E49\u0E43\u0E19\u0E41\u0E2D\u0E1B\u0E01\u0E48\u0E2D\u0E19"
2214
2335
  };
2215
2336
 
2216
2337
  // src/locales/vi.json
@@ -2219,7 +2340,7 @@ var vi_default = {
2219
2340
  confirm: "X\xE1c nh\u1EADn x\xE1c th\u1EF1c",
2220
2341
  scan: "Qu\xE9t b\u1EB1ng AuthLink",
2221
2342
  code: "M\xE3 \u0111\u1ED1i chi\u1EBFu",
2222
- help: "Ki\u1EC3m tra c\xE1c ch\u1EEF s\u1ED1 trong \u1EE9ng d\u1EE5ng c\xF3 kh\u1EDBp kh\xF4ng, r\u1ED3i x\xE1c nh\u1EADn tr\xEAn \u0111i\u1EC7n tho\u1EA1i.",
2343
+ help: "Ph\xEA duy\u1EC7t trong \u1EE9ng d\u1EE5ng r\u1ED3i nh\u1EADp m\xE3 x\xE1c nh\u1EADn 6 ch\u1EEF s\u1ED1 t\u1EA1i \u0111\xE2y.",
2223
2344
  waiting: "\u0110ang ch\u1EDD x\xE1c nh\u1EADn t\u1EEB \u0111i\u1EC7n tho\u1EA1i",
2224
2345
  loading: "\u0110ang k\u1EBFt n\u1ED1i",
2225
2346
  confirmed: "\u0110\xE3 x\xE1c nh\u1EADn",
@@ -2232,7 +2353,16 @@ var vi_default = {
2232
2353
  close: "\u0110\xF3ng",
2233
2354
  cancel: "H\u1EE7y",
2234
2355
  autoUpdate: "M\xE3 t\u1EF1 \u0111\u1ED9ng c\u1EADp nh\u1EADt m\u1ED7i 30 gi\xE2y",
2235
- expiring: "Y\xEAu c\u1EA7u s\u1EAFp h\u1EBFt h\u1EA1n"
2356
+ expiring: "Y\xEAu c\u1EA7u s\u1EAFp h\u1EBFt h\u1EA1n",
2357
+ confirmationCode: "M\xE3 x\xE1c nh\u1EADn trong \u1EE9ng d\u1EE5ng",
2358
+ submitCode: "X\xE1c minh",
2359
+ codeError: "Kh\xF4ng th\u1EC3 x\xE1c minh. Ki\u1EC3m tra m\xE3 v\xE0 tr\u1EA1ng th\xE1i ph\xEA duy\u1EC7t r\u1ED3i th\u1EED l\u1EA1i.",
2360
+ codeLocked: "Nh\u1EADp sai m\xE3 qu\xE1 nhi\u1EC1u l\u1EA7n. \u0110\xF3ng c\u1EEDa s\u1ED5 v\xE0 t\u1EA1o y\xEAu c\u1EA7u m\u1EDBi.",
2361
+ otpCode: "M\xE3 \u1EE9ng d\u1EE5ng",
2362
+ otpHelp: "M\u1EDF AuthLink v\xE0 nh\u1EADp m\xE3 s\xE1u ch\u1EEF s\u1ED1 hi\u1EC7n t\u1EA1i c\u1EE7a t\xE0i kho\u1EA3n n\xE0y. Kh\xF4ng c\u1EA7n ph\xEA duy\u1EC7t y\xEAu c\u1EA7u.",
2363
+ otpWaiting: "Nh\u1EADp m\xE3 hi\u1EC7n t\u1EA1i",
2364
+ otpError: "X\xE1c minh th\u1EA5t b\u1EA1i. Ki\u1EC3m tra t\xE0i kho\u1EA3n v\xE0 ch\u1EDD m\xE3 m\u1EDBi n\u1EBFu \u0111\xE3 d\xF9ng. N\u1EBFu sai nhi\u1EC1u l\u1EA7n, h\xE3y th\u1EED l\u1EA1i sau.",
2365
+ otpSetup: "Tr\u01B0\u1EDBc ti\xEAn h\xE3y b\u1EADt m\xE3 tr\xEAn th\u1EBB t\xE0i kho\u1EA3n n\xE0y trong \u1EE9ng d\u1EE5ng."
2236
2366
  };
2237
2367
 
2238
2368
  // src/i18n.ts
@@ -2270,7 +2400,7 @@ function widgetView(options) {
2270
2400
  const locale = resolveLocale(options.locale), variant = resolveVariant(options.variant);
2271
2401
  const l = getMessages(locale);
2272
2402
  const pending = s.status === "pending", hasQr = pending && (mode === "bind" || Boolean(options.showQr)) && Boolean(options.qr);
2273
- const status = { idle: l.loading, pending: l.waiting, confirmed: l.confirmed, rejected: l.rejected, expired: l.expired, error: l.error }[s.status];
2403
+ const status = { idle: l.loading, pending: options.passcodeMode ? l.otpWaiting : l.waiting, confirmed: l.confirmed, rejected: l.rejected, expired: l.expired, error: l.error }[s.status];
2274
2404
  const expiring = pending && s.secondsRemaining <= 30;
2275
2405
  const expiryTime = `00:${String(Math.max(0, Math.ceil(s.secondsRemaining))).padStart(2, "0")}`;
2276
2406
  const statusNode = () => node(
@@ -2299,50 +2429,52 @@ function widgetView(options) {
2299
2429
  node("img", { class: "authlink-qr", src: options.qr, alt: l.scan, width: 240, height: 240 }),
2300
2430
  node("figcaption", { class: "authlink-qr-caption" }, l.scan)
2301
2431
  )] : [];
2302
- const seconds = Math.max(0, Math.min(30, s.codeSecondsRemaining));
2303
- const code = node(
2304
- "output",
2305
- {
2306
- class: "authlink-code",
2307
- "aria-label": s.code ? `${l.code}: ${s.code.split("").join(" ")}` : l.updating,
2308
- "aria-live": "polite",
2309
- "data-testid": "authlink-code"
2310
- },
2311
- node("span", { class: "authlink-code-group" }, s.code?.slice(0, 3) ?? "---"),
2312
- node("span", { class: "authlink-code-group" }, s.code?.slice(3) ?? "---")
2313
- );
2314
- const panel = node(
2315
- "div",
2316
- { class: "authlink-code-panel" },
2317
- node("div", { class: "authlink-code-values" }, node("div", { class: "authlink-code-label" }, l.code), code),
2432
+ const detail = [
2433
+ node("p", { class: "authlink-help" }, options.passcodeMode ? l.otpHelp : l.help),
2318
2434
  node(
2319
- "div",
2320
- {
2321
- class: "authlink-progress",
2322
- role: "progressbar",
2323
- "aria-valuemin": 0,
2324
- "aria-valuemax": 30,
2325
- "aria-valuenow": seconds,
2326
- "aria-label": l.autoUpdate
2327
- },
2328
- node("span", { class: "authlink-progress-fill", "aria-hidden": "true", style: `transform:scaleX(${seconds / 30})` })
2435
+ "form",
2436
+ { class: "authlink-code-form", onSubmit: (event) => {
2437
+ event.preventDefault();
2438
+ const input = event.currentTarget.querySelector("input");
2439
+ if (input) void options.submitCode?.(input.value);
2440
+ } },
2441
+ node("label", { for: "authlink-confirmation-code" }, options.passcodeMode ? l.otpCode : l.confirmationCode),
2442
+ node("input", {
2443
+ id: "authlink-confirmation-code",
2444
+ name: options.passcodeMode ? "passcode" : "confirmationCode",
2445
+ type: "text",
2446
+ inputmode: "numeric",
2447
+ autocomplete: "one-time-code",
2448
+ pattern: "[0-9]{6}",
2449
+ maxlength: 6,
2450
+ minlength: 6,
2451
+ required: true,
2452
+ "data-authlink-focus": "confirmation-code",
2453
+ value: options.inputCode ?? "",
2454
+ onInput: (event) => options.setCode?.(event.target.value)
2455
+ }),
2456
+ node(
2457
+ "button",
2458
+ { type: "submit", class: "authlink-button", ...s.submitting ? { disabled: true } : {} },
2459
+ s.submitting ? l.loading : l.submitCode
2460
+ )
2329
2461
  ),
2330
- node("p", { class: "authlink-code-caption" }, s.code ? l.autoUpdate : l.updating)
2331
- );
2332
- const detail = [panel, node("p", { class: "authlink-help" }, l.help)];
2462
+ ...s.error ? [node("p", { class: "authlink-input-error", role: "alert" }, options.passcodeMode ? s.error.message === "OTP_NOT_CONFIGURED" ? l.otpSetup : l.otpError : l.codeError)] : []
2463
+ ];
2333
2464
  children.push(node("div", { class: "authlink-body" }, ...qr, node("div", { class: "authlink-details" }, ...detail)));
2334
2465
  if (variant === "A") children.push(node("div", { class: "authlink-footer" }, statusNode()));
2335
2466
  if (variant === "B") children.push(node("div", { class: "authlink-footer" }, statusNode(), ...cancel()));
2336
2467
  if (variant === "C") children.push(node("div", { class: "authlink-footer" }, statusNode(), ...cancel()));
2337
2468
  } else {
2338
2469
  children.push(statusNode());
2339
- if (s.status === "error") children.push(node("button", {
2470
+ if (s.error?.message === "CONFIRMATION_CODE_LOCKED") children.push(node("p", { class: "authlink-input-error", role: "alert" }, l.codeLocked));
2471
+ if (s.status === "error" && s.error?.message !== "CONFIRMATION_CODE_LOCKED") children.push(node("button", {
2340
2472
  type: "button",
2341
2473
  class: "authlink-button",
2342
2474
  "data-authlink-focus": "retry",
2343
2475
  onClick: retry
2344
2476
  }, l.retry));
2345
- if (refresh && ["expired", "rejected"].includes(s.status)) children.push(node("button", {
2477
+ if (refresh && ["expired", "rejected", "error"].includes(s.status)) children.push(node("button", {
2346
2478
  type: "button",
2347
2479
  class: "authlink-button",
2348
2480
  "data-authlink-focus": "refresh",
@@ -2365,7 +2497,7 @@ function element(value, svg = false) {
2365
2497
  svg ||= value.tag === "svg";
2366
2498
  const result = svg ? document.createElementNS("http://www.w3.org/2000/svg", value.tag) : document.createElement(value.tag);
2367
2499
  for (const [key, v] of Object.entries(value.props)) {
2368
- if (key === "onClick" && typeof v === "function") result.addEventListener("click", v);
2500
+ if (key.startsWith("on") && typeof v === "function") result.addEventListener(key.slice(2).toLowerCase(), v);
2369
2501
  else if (v !== void 0 && v !== null) result.setAttribute(key, String(v));
2370
2502
  }
2371
2503
  result.append(...value.children.map((child) => element(child, svg)));
@@ -2399,6 +2531,8 @@ function openAuthLink(options) {
2399
2531
  dialog.append(header, content);
2400
2532
  let locale = options.locale, variant = resolveVariant(options.variant), snapshot = initialSnapshot(), closed = false;
2401
2533
  let abort;
2534
+ let inputCode = "";
2535
+ let submitCode;
2402
2536
  const image = qrImage(ticket);
2403
2537
  let progressFill, progressFrame, progressDeadline = 0;
2404
2538
  const motion = window.matchMedia?.("(prefers-reduced-motion: reduce)");
@@ -2438,6 +2572,11 @@ function openAuthLink(options) {
2438
2572
  snapshot,
2439
2573
  qr: image,
2440
2574
  showQr: options.showQr,
2575
+ inputCode,
2576
+ setCode: (code) => {
2577
+ inputCode = code;
2578
+ },
2579
+ submitCode: (code) => submitCode?.(code) ?? Promise.resolve(),
2441
2580
  retry,
2442
2581
  refresh: options.onRefresh,
2443
2582
  close: () => dispose(true)
@@ -2456,6 +2595,9 @@ function openAuthLink(options) {
2456
2595
  oldProgress.setAttribute("aria-label", nextProgress.getAttribute("aria-label"));
2457
2596
  nextProgress.replaceWith(oldProgress);
2458
2597
  }
2598
+ const oldInput = content.querySelector("input[name=confirmationCode]");
2599
+ const nextInput = next.querySelector("input[name=confirmationCode]");
2600
+ if (oldInput && nextInput) nextInput.replaceWith(oldInput);
2459
2601
  content.replaceChildren(next);
2460
2602
  progressFill = content.querySelector(".authlink-progress-fill") ?? void 0;
2461
2603
  drawProgress();
@@ -2497,6 +2639,9 @@ function openAuthLink(options) {
2497
2639
  intervalMs: options.intervalMs,
2498
2640
  expectedMode: mode,
2499
2641
  signal: abort.signal,
2642
+ onReady: (submit) => {
2643
+ submitCode = submit;
2644
+ },
2500
2645
  onCodeTiming: syncProgress,
2501
2646
  onUpdate: (value) => {
2502
2647
  snapshot = value;
@@ -2556,14 +2701,183 @@ function openAuthLink(options) {
2556
2701
  }
2557
2702
  var openAuthLinkBind = (options) => openAuthLink({ ...options, mode: "bind" });
2558
2703
  var openAuthLinkConfirm = (options) => openAuthLink({ ...options, mode: "confirm" });
2704
+
2705
+ // src/passcode.ts
2706
+ function validateOTPResult(value) {
2707
+ if (!value || typeof value !== "object") throw new AuthLinkError("INVALID_RESPONSE");
2708
+ const result = value;
2709
+ const expiry = typeof result.expiresAt === "string" ? Date.parse(result.expiresAt) : NaN;
2710
+ if (result.status !== "confirmed" || result.authenticationPolicy !== "device-totp.v1" || typeof result.verificationId !== "string" || !/^[A-Za-z0-9_-]{43}$/.test(result.verificationId) || !Number.isFinite(expiry) || expiry <= Date.now() || expiry > Date.now() + 9e4 || "requestId" in result || "state" in result || "businessId" in result) throw new AuthLinkError("INVALID_CONFIRMATION");
2711
+ return result;
2712
+ }
2713
+ function createPasscodeClient(options) {
2714
+ const call = createBackendCall(options);
2715
+ return {
2716
+ async verify(passcode, signal) {
2717
+ if (!/^[0-9]{6}$/.test(passcode)) throw new AuthLinkError("PASSCODE_INVALID");
2718
+ return validateOTPResult(await call("/auth", { passcode }, signal));
2719
+ }
2720
+ };
2721
+ }
2722
+ function openPasscode(options) {
2723
+ if (typeof document === "undefined" || !document.body) throw new AuthLinkError("BROWSER_REQUIRED");
2724
+ const deadline = Date.parse(options.expiresAt);
2725
+ if (!Number.isFinite(deadline) || deadline <= Date.now() || deadline > Date.now() + 3e5) throw new AuthLinkError("INVALID_EXPIRY");
2726
+ const previousFocus = document.activeElement instanceof HTMLElement ? document.activeElement : null;
2727
+ const dialog = document.createElement("dialog");
2728
+ if (typeof dialog.showModal !== "function") throw new AuthLinkError("DIALOG_UNSUPPORTED");
2729
+ dialog.className = "authlink-dialog";
2730
+ dialog.setAttribute("aria-modal", "true");
2731
+ const header = document.createElement("div");
2732
+ header.className = "authlink-header";
2733
+ const brand = document.createElement("span");
2734
+ brand.className = "authlink-brand";
2735
+ brand.textContent = "AuthLink";
2736
+ const close = document.createElement("button");
2737
+ close.type = "button";
2738
+ close.className = "authlink-close";
2739
+ const icon = document.createElement("span");
2740
+ icon.className = "authlink-close-icon";
2741
+ icon.setAttribute("aria-hidden", "true");
2742
+ close.append(icon);
2743
+ const content = document.createElement("div");
2744
+ content.className = "authlink-modal-content";
2745
+ header.append(brand, close);
2746
+ dialog.append(header, content);
2747
+ let locale = options.locale, variant = resolveVariant(options.variant), closed = false, busy = false, code = "", error;
2748
+ const mode = options.bindingQr ? "bind" : "confirm";
2749
+ let image;
2750
+ if (options.bindingQr) {
2751
+ let payload;
2752
+ try {
2753
+ payload = JSON.parse(options.bindingQr);
2754
+ } catch {
2755
+ throw new AuthLinkError("INVALID_TICKET");
2756
+ }
2757
+ image = qrImage(validateTicket({ requestId: payload.requestId, purpose: "bind", qr: options.bindingQr, pairingCode: "000000", expiresAt: options.expiresAt }, "bind", options.authLinkOrigin));
2758
+ }
2759
+ const abort = new AbortController();
2760
+ let timer;
2761
+ const dispose = (notify) => {
2762
+ if (closed) return;
2763
+ closed = true;
2764
+ abort.abort();
2765
+ clearInterval(timer);
2766
+ dialog.remove();
2767
+ previousFocus?.focus();
2768
+ if (notify) options.onClose?.();
2769
+ };
2770
+ const render = () => {
2771
+ if (closed) return;
2772
+ const l = getMessages(locale);
2773
+ dialog.lang = resolveLocale(locale);
2774
+ dialog.dataset.variant = variant;
2775
+ dialog.dataset.mode = mode;
2776
+ dialog.dataset.hasQr = String(Boolean(image));
2777
+ dialog.setAttribute("aria-label", l[mode]);
2778
+ close.setAttribute("aria-label", l.close);
2779
+ const focused = content.contains(document.activeElement);
2780
+ const remaining = Math.max(0, Math.ceil((deadline - Date.now()) / 1e3));
2781
+ const snapshot = { status: remaining ? "pending" : "expired", code: null, secondsRemaining: remaining, codeSecondsRemaining: remaining, submitting: busy, error: error ?? null };
2782
+ content.replaceChildren(element(widgetView({
2783
+ mode,
2784
+ locale,
2785
+ variant,
2786
+ passcodeMode: true,
2787
+ snapshot,
2788
+ qr: image,
2789
+ showQr: Boolean(image),
2790
+ inputCode: code,
2791
+ setCode: (value) => {
2792
+ code = value;
2793
+ },
2794
+ submitCode: submit,
2795
+ retry: () => {
2796
+ error = void 0;
2797
+ render();
2798
+ },
2799
+ close: () => dispose(true)
2800
+ })));
2801
+ if (focused) content.querySelector("input")?.focus();
2802
+ };
2803
+ const submit = async (value) => {
2804
+ if (closed || busy) return;
2805
+ if (Date.now() >= deadline) {
2806
+ render();
2807
+ return;
2808
+ }
2809
+ if (!/^[0-9]{6}$/.test(value)) {
2810
+ error = new AuthLinkError("PASSCODE_INVALID");
2811
+ render();
2812
+ return;
2813
+ }
2814
+ code = value;
2815
+ busy = true;
2816
+ error = void 0;
2817
+ render();
2818
+ try {
2819
+ const result = validateOTPResult(await options.client.verify(value, abort.signal));
2820
+ if (closed || abort.signal.aborted || Date.now() >= deadline) return;
2821
+ try {
2822
+ await options.onVerified?.(result);
2823
+ } catch {
2824
+ options.onError?.(new AuthLinkError("CALLBACK_FAILED"));
2825
+ }
2826
+ dispose(false);
2827
+ } catch (cause) {
2828
+ if (!closed && !abort.signal.aborted) {
2829
+ error = cause instanceof Error ? cause : new AuthLinkError("NETWORK_ERROR");
2830
+ options.onError?.(error);
2831
+ }
2832
+ } finally {
2833
+ busy = false;
2834
+ render();
2835
+ }
2836
+ };
2837
+ close.addEventListener("click", () => dispose(true));
2838
+ dialog.addEventListener("cancel", (event) => {
2839
+ event.preventDefault();
2840
+ dispose(true);
2841
+ });
2842
+ document.body.append(dialog);
2843
+ render();
2844
+ dialog.showModal();
2845
+ content.querySelector("input")?.focus();
2846
+ timer = setInterval(() => {
2847
+ if (closed) return;
2848
+ if (Date.now() >= deadline) {
2849
+ render();
2850
+ clearInterval(timer);
2851
+ void Promise.resolve(options.onExpired?.()).catch(() => options.onError?.(new AuthLinkError("CALLBACK_FAILED")));
2852
+ }
2853
+ }, 1e3);
2854
+ return {
2855
+ close: () => dispose(true),
2856
+ destroy: () => dispose(false),
2857
+ setLocale: (value) => {
2858
+ locale = value;
2859
+ render();
2860
+ },
2861
+ setVariant: (value) => {
2862
+ variant = resolveVariant(value);
2863
+ render();
2864
+ },
2865
+ retry: () => {
2866
+ error = void 0;
2867
+ render();
2868
+ }
2869
+ };
2870
+ }
2559
2871
  export {
2560
2872
  AuthLinkError,
2561
2873
  createAuthLinkClient,
2874
+ createPasscodeClient,
2562
2875
  initialSnapshot,
2563
2876
  observeRequest,
2564
2877
  openAuthLink,
2565
2878
  openAuthLinkBind,
2566
2879
  openAuthLinkConfirm,
2880
+ openPasscode,
2567
2881
  qrImage,
2568
2882
  requestKey,
2569
2883
  supportedLocales,