@tatchi-xyz/sdk 0.19.0 → 0.21.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/cjs/core/EmailRecovery/index.js +25 -0
- package/dist/cjs/core/EmailRecovery/index.js.map +1 -1
- package/dist/cjs/core/TatchiPasskey/emailRecovery.js +135 -77
- package/dist/cjs/core/TatchiPasskey/emailRecovery.js.map +1 -1
- package/dist/cjs/core/TatchiPasskey/index.js +2 -1
- package/dist/cjs/core/TatchiPasskey/index.js.map +1 -1
- package/dist/cjs/core/TatchiPasskey/linkDevice.js +2 -1
- package/dist/cjs/core/TatchiPasskey/linkDevice.js.map +1 -1
- package/dist/cjs/core/TatchiPasskey/scanDevice.js +5 -3
- package/dist/cjs/core/TatchiPasskey/scanDevice.js.map +1 -1
- package/dist/cjs/core/WalletIframe/client/router.js +1 -1
- package/dist/cjs/core/WalletIframe/client/router.js.map +1 -1
- package/dist/cjs/core/WebAuthnManager/SignerWorkerManager/handlers/validation.js +3 -4
- package/dist/cjs/core/WebAuthnManager/SignerWorkerManager/handlers/validation.js.map +1 -1
- package/dist/cjs/core/defaultConfigs.js +3 -7
- package/dist/cjs/core/defaultConfigs.js.map +1 -1
- package/dist/cjs/core/nearCrypto.js +29 -5
- package/dist/cjs/core/nearCrypto.js.map +1 -1
- package/dist/cjs/core/rpcCalls.js +56 -26
- package/dist/cjs/core/rpcCalls.js.map +1 -1
- package/dist/cjs/core/types/emailRecovery.js +33 -0
- package/dist/cjs/core/types/emailRecovery.js.map +1 -0
- package/dist/cjs/index.js +4 -0
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/react/components/AccountMenuButton/{LinkedDevicesModal-CSSowiHP.css → LinkedDevicesModal-BRtht0XI.css} +1 -1
- package/dist/{esm/react/components/AccountMenuButton/LinkedDevicesModal-CSSowiHP.css.map → cjs/react/components/AccountMenuButton/LinkedDevicesModal-BRtht0XI.css.map} +1 -1
- package/dist/cjs/react/components/AccountMenuButton/{ProfileDropdown-CEPMZ1gY.css → ProfileDropdown-BG_6hcim.css} +1 -1
- package/dist/{esm/react/components/AccountMenuButton/ProfileDropdown-CEPMZ1gY.css.map → cjs/react/components/AccountMenuButton/ProfileDropdown-BG_6hcim.css.map} +1 -1
- package/dist/cjs/react/components/AccountMenuButton/{Web3AuthProfileButton-DopOg7Xc.css → Web3AuthProfileButton-k8_FAYFq.css} +1 -1
- package/dist/cjs/react/components/AccountMenuButton/{Web3AuthProfileButton-DopOg7Xc.css.map → Web3AuthProfileButton-k8_FAYFq.css.map} +1 -1
- package/dist/cjs/react/components/AccountMenuButton/icons/{TouchIcon-BQWentvJ.css → TouchIcon-C-RcGfr5.css} +1 -1
- package/dist/cjs/react/components/AccountMenuButton/icons/{TouchIcon-BQWentvJ.css.map → TouchIcon-C-RcGfr5.css.map} +1 -1
- package/dist/cjs/react/components/PasskeyAuthMenu/{PasskeyAuthMenu-DwrzWMYx.css → PasskeyAuthMenu-DKMiLeT9.css} +59 -4
- package/dist/cjs/react/components/PasskeyAuthMenu/{PasskeyAuthMenu-DwrzWMYx.css.map → PasskeyAuthMenu-DKMiLeT9.css.map} +1 -1
- package/dist/cjs/react/components/PasskeyAuthMenu/adapters/tatchi.js +1 -0
- package/dist/cjs/react/components/PasskeyAuthMenu/adapters/tatchi.js.map +1 -1
- package/dist/cjs/react/components/PasskeyAuthMenu/client.js +30 -8
- package/dist/cjs/react/components/PasskeyAuthMenu/client.js.map +1 -1
- package/dist/cjs/react/components/PasskeyAuthMenu/controller/useSDKEvents.js +22 -0
- package/dist/cjs/react/components/PasskeyAuthMenu/controller/useSDKEvents.js.map +1 -0
- package/dist/cjs/react/components/PasskeyAuthMenu/ui/ContentSwitcher.js +17 -4
- package/dist/cjs/react/components/PasskeyAuthMenu/ui/ContentSwitcher.js.map +1 -1
- package/dist/cjs/react/components/PasskeyAuthMenu/ui/EmailRecoverySlide.js +354 -154
- package/dist/cjs/react/components/PasskeyAuthMenu/ui/EmailRecoverySlide.js.map +1 -1
- package/dist/cjs/react/components/{ShowQRCode-CCN4h6Uv.css → ShowQRCode-CB0UCQ_h.css} +1 -1
- package/dist/cjs/react/components/{ShowQRCode-CCN4h6Uv.css.map → ShowQRCode-CB0UCQ_h.css.map} +1 -1
- package/dist/cjs/react/context/useSDKFlowRuntime.js +183 -0
- package/dist/cjs/react/context/useSDKFlowRuntime.js.map +1 -0
- package/dist/cjs/react/context/useTatchiContextValue.js +24 -15
- package/dist/cjs/react/context/useTatchiContextValue.js.map +1 -1
- package/dist/cjs/react/context/useTatchiWithSdkFlow.js +96 -0
- package/dist/cjs/react/context/useTatchiWithSdkFlow.js.map +1 -0
- package/dist/cjs/react/sdk/src/core/EmailRecovery/index.js +26 -0
- package/dist/cjs/react/sdk/src/core/EmailRecovery/index.js.map +1 -1
- package/dist/cjs/react/sdk/src/core/TatchiPasskey/emailRecovery.js +135 -77
- package/dist/cjs/react/sdk/src/core/TatchiPasskey/emailRecovery.js.map +1 -1
- package/dist/cjs/react/sdk/src/core/TatchiPasskey/index.js +2 -1
- package/dist/cjs/react/sdk/src/core/TatchiPasskey/index.js.map +1 -1
- package/dist/cjs/react/sdk/src/core/TatchiPasskey/linkDevice.js +2 -1
- package/dist/cjs/react/sdk/src/core/TatchiPasskey/linkDevice.js.map +1 -1
- package/dist/cjs/react/sdk/src/core/TatchiPasskey/scanDevice.js +5 -3
- package/dist/cjs/react/sdk/src/core/TatchiPasskey/scanDevice.js.map +1 -1
- package/dist/cjs/react/sdk/src/core/WalletIframe/client/router.js +1 -1
- package/dist/cjs/react/sdk/src/core/WalletIframe/client/router.js.map +1 -1
- package/dist/cjs/react/sdk/src/core/WebAuthnManager/SignerWorkerManager/handlers/validation.js +3 -4
- package/dist/cjs/react/sdk/src/core/WebAuthnManager/SignerWorkerManager/handlers/validation.js.map +1 -1
- package/dist/cjs/react/sdk/src/core/defaultConfigs.js +3 -7
- package/dist/cjs/react/sdk/src/core/defaultConfigs.js.map +1 -1
- package/dist/cjs/react/sdk/src/core/nearCrypto.js +29 -5
- package/dist/cjs/react/sdk/src/core/nearCrypto.js.map +1 -1
- package/dist/cjs/react/sdk/src/core/rpcCalls.js +56 -26
- package/dist/cjs/react/sdk/src/core/rpcCalls.js.map +1 -1
- package/dist/cjs/react/sdk/src/core/types/emailRecovery.js +33 -0
- package/dist/cjs/react/sdk/src/core/types/emailRecovery.js.map +1 -0
- package/dist/cjs/server/email-recovery/emailParsers.js +2 -1
- package/dist/cjs/server/email-recovery/emailParsers.js.map +1 -1
- package/dist/cjs/server/email-recovery/index.js +6 -6
- package/dist/cjs/server/email-recovery/index.js.map +1 -1
- package/dist/cjs/server/email-recovery/rpcCalls.js +22 -3
- package/dist/cjs/server/email-recovery/rpcCalls.js.map +1 -1
- package/dist/cjs/server/router/cloudflare.js +8 -3
- package/dist/cjs/server/router/cloudflare.js.map +1 -1
- package/dist/cjs/server/router/express.js.map +1 -1
- package/dist/cjs/server/sdk/src/core/defaultConfigs.js +2 -4
- package/dist/cjs/server/sdk/src/core/defaultConfigs.js.map +1 -1
- package/dist/cjs/server/sdk/src/core/nearCrypto.js +26 -7
- package/dist/cjs/server/sdk/src/core/nearCrypto.js.map +1 -1
- package/dist/esm/core/EmailRecovery/index.js +25 -1
- package/dist/esm/core/EmailRecovery/index.js.map +1 -1
- package/dist/esm/core/TatchiPasskey/emailRecovery.js +136 -78
- package/dist/esm/core/TatchiPasskey/emailRecovery.js.map +1 -1
- package/dist/esm/core/TatchiPasskey/index.js +2 -1
- package/dist/esm/core/TatchiPasskey/index.js.map +1 -1
- package/dist/esm/core/TatchiPasskey/linkDevice.js +2 -1
- package/dist/esm/core/TatchiPasskey/linkDevice.js.map +1 -1
- package/dist/esm/core/TatchiPasskey/scanDevice.js +5 -3
- package/dist/esm/core/TatchiPasskey/scanDevice.js.map +1 -1
- package/dist/esm/core/WalletIframe/client/router.js +1 -1
- package/dist/esm/core/WalletIframe/client/router.js.map +1 -1
- package/dist/esm/core/WebAuthnManager/SignerWorkerManager/handlers/validation.js +2 -3
- package/dist/esm/core/WebAuthnManager/SignerWorkerManager/handlers/validation.js.map +1 -1
- package/dist/esm/core/defaultConfigs.js +3 -7
- package/dist/esm/core/defaultConfigs.js.map +1 -1
- package/dist/esm/core/nearCrypto.js +24 -6
- package/dist/esm/core/nearCrypto.js.map +1 -1
- package/dist/esm/core/rpcCalls.js +56 -26
- package/dist/esm/core/rpcCalls.js.map +1 -1
- package/dist/esm/core/types/emailRecovery.js +26 -0
- package/dist/esm/core/types/emailRecovery.js.map +1 -0
- package/dist/esm/index.js +3 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/react/components/AccountMenuButton/{LinkedDevicesModal-CSSowiHP.css → LinkedDevicesModal-BRtht0XI.css} +1 -1
- package/dist/{cjs/react/components/AccountMenuButton/LinkedDevicesModal-CSSowiHP.css.map → esm/react/components/AccountMenuButton/LinkedDevicesModal-BRtht0XI.css.map} +1 -1
- package/dist/esm/react/components/AccountMenuButton/{ProfileDropdown-CEPMZ1gY.css → ProfileDropdown-BG_6hcim.css} +1 -1
- package/dist/{cjs/react/components/AccountMenuButton/ProfileDropdown-CEPMZ1gY.css.map → esm/react/components/AccountMenuButton/ProfileDropdown-BG_6hcim.css.map} +1 -1
- package/dist/esm/react/components/AccountMenuButton/{Web3AuthProfileButton-DopOg7Xc.css → Web3AuthProfileButton-k8_FAYFq.css} +1 -1
- package/dist/esm/react/components/AccountMenuButton/{Web3AuthProfileButton-DopOg7Xc.css.map → Web3AuthProfileButton-k8_FAYFq.css.map} +1 -1
- package/dist/esm/react/components/AccountMenuButton/icons/{TouchIcon-BQWentvJ.css → TouchIcon-C-RcGfr5.css} +1 -1
- package/dist/esm/react/components/AccountMenuButton/icons/{TouchIcon-BQWentvJ.css.map → TouchIcon-C-RcGfr5.css.map} +1 -1
- package/dist/esm/react/components/PasskeyAuthMenu/{PasskeyAuthMenu-DwrzWMYx.css → PasskeyAuthMenu-DKMiLeT9.css} +59 -4
- package/dist/esm/react/components/PasskeyAuthMenu/{PasskeyAuthMenu-DwrzWMYx.css.map → PasskeyAuthMenu-DKMiLeT9.css.map} +1 -1
- package/dist/esm/react/components/PasskeyAuthMenu/adapters/tatchi.js +1 -0
- package/dist/esm/react/components/PasskeyAuthMenu/adapters/tatchi.js.map +1 -1
- package/dist/esm/react/components/PasskeyAuthMenu/client.js +30 -8
- package/dist/esm/react/components/PasskeyAuthMenu/client.js.map +1 -1
- package/dist/esm/react/components/PasskeyAuthMenu/controller/useSDKEvents.js +20 -0
- package/dist/esm/react/components/PasskeyAuthMenu/controller/useSDKEvents.js.map +1 -0
- package/dist/esm/react/components/PasskeyAuthMenu/ui/ContentSwitcher.js +17 -4
- package/dist/esm/react/components/PasskeyAuthMenu/ui/ContentSwitcher.js.map +1 -1
- package/dist/esm/react/components/PasskeyAuthMenu/ui/EmailRecoverySlide.js +354 -154
- package/dist/esm/react/components/PasskeyAuthMenu/ui/EmailRecoverySlide.js.map +1 -1
- package/dist/esm/react/components/{ShowQRCode-CCN4h6Uv.css → ShowQRCode-CB0UCQ_h.css} +1 -1
- package/dist/esm/react/components/{ShowQRCode-CCN4h6Uv.css.map → ShowQRCode-CB0UCQ_h.css.map} +1 -1
- package/dist/esm/react/context/useSDKFlowRuntime.js +181 -0
- package/dist/esm/react/context/useSDKFlowRuntime.js.map +1 -0
- package/dist/esm/react/context/useTatchiContextValue.js +25 -16
- package/dist/esm/react/context/useTatchiContextValue.js.map +1 -1
- package/dist/esm/react/context/useTatchiWithSdkFlow.js +94 -0
- package/dist/esm/react/context/useTatchiWithSdkFlow.js.map +1 -0
- package/dist/esm/react/sdk/src/core/EmailRecovery/index.js +25 -1
- package/dist/esm/react/sdk/src/core/EmailRecovery/index.js.map +1 -1
- package/dist/esm/react/sdk/src/core/TatchiPasskey/emailRecovery.js +136 -78
- package/dist/esm/react/sdk/src/core/TatchiPasskey/emailRecovery.js.map +1 -1
- package/dist/esm/react/sdk/src/core/TatchiPasskey/index.js +2 -1
- package/dist/esm/react/sdk/src/core/TatchiPasskey/index.js.map +1 -1
- package/dist/esm/react/sdk/src/core/TatchiPasskey/linkDevice.js +2 -1
- package/dist/esm/react/sdk/src/core/TatchiPasskey/linkDevice.js.map +1 -1
- package/dist/esm/react/sdk/src/core/TatchiPasskey/scanDevice.js +5 -3
- package/dist/esm/react/sdk/src/core/TatchiPasskey/scanDevice.js.map +1 -1
- package/dist/esm/react/sdk/src/core/WalletIframe/client/router.js +1 -1
- package/dist/esm/react/sdk/src/core/WalletIframe/client/router.js.map +1 -1
- package/dist/esm/react/sdk/src/core/WebAuthnManager/SignerWorkerManager/handlers/validation.js +2 -3
- package/dist/esm/react/sdk/src/core/WebAuthnManager/SignerWorkerManager/handlers/validation.js.map +1 -1
- package/dist/esm/react/sdk/src/core/defaultConfigs.js +3 -7
- package/dist/esm/react/sdk/src/core/defaultConfigs.js.map +1 -1
- package/dist/esm/react/sdk/src/core/nearCrypto.js +24 -6
- package/dist/esm/react/sdk/src/core/nearCrypto.js.map +1 -1
- package/dist/esm/react/sdk/src/core/rpcCalls.js +56 -26
- package/dist/esm/react/sdk/src/core/rpcCalls.js.map +1 -1
- package/dist/esm/react/sdk/src/core/types/emailRecovery.js +26 -0
- package/dist/esm/react/sdk/src/core/types/emailRecovery.js.map +1 -0
- package/dist/esm/react/styles/styles.css +58 -3
- package/dist/esm/sdk/{defaultConfigs-DpslkAQd.js → defaultConfigs-CfQDV-ya.js} +3 -7
- package/dist/esm/sdk/{getDeviceNumber-fXizNGQl.js → getDeviceNumber-BpernPnM.js} +4 -8
- package/dist/esm/sdk/getDeviceNumber-BpernPnM.js.map +1 -0
- package/dist/esm/sdk/offline-export-app.js +23 -6
- package/dist/esm/sdk/offline-export-app.js.map +1 -1
- package/dist/esm/sdk/{router-DuGYOd3G.js → router-BWtacLJg.js} +1 -1
- package/dist/esm/sdk/{rpcCalls-BQrJMTdg.js → rpcCalls-CYGJSCgm.js} +3 -3
- package/dist/esm/sdk/{rpcCalls-YVeUVMk2.js → rpcCalls-DZZSa-sk.js} +57 -27
- package/dist/esm/sdk/{transactions-bqaAwL4k.js → transactions-Cn9xTWlK.js} +2 -2
- package/dist/esm/sdk/{transactions-bqaAwL4k.js.map → transactions-Cn9xTWlK.js.map} +1 -1
- package/dist/esm/sdk/{transactions-BalIhtJ9.js → transactions-DfdwDQCn.js} +1 -1
- package/dist/esm/sdk/wallet-iframe-host.js +660 -590
- package/dist/esm/server/email-recovery/emailParsers.js +3 -1
- package/dist/esm/server/email-recovery/emailParsers.js.map +1 -1
- package/dist/esm/server/email-recovery/index.js +6 -6
- package/dist/esm/server/email-recovery/index.js.map +1 -1
- package/dist/esm/server/email-recovery/rpcCalls.js +22 -3
- package/dist/esm/server/email-recovery/rpcCalls.js.map +1 -1
- package/dist/esm/server/router/cloudflare.js +8 -3
- package/dist/esm/server/router/cloudflare.js.map +1 -1
- package/dist/esm/server/router/express.js.map +1 -1
- package/dist/esm/server/sdk/src/core/defaultConfigs.js +2 -4
- package/dist/esm/server/sdk/src/core/defaultConfigs.js.map +1 -1
- package/dist/esm/server/sdk/src/core/nearCrypto.js +26 -8
- package/dist/esm/server/sdk/src/core/nearCrypto.js.map +1 -1
- package/dist/esm/wasm_vrf_worker/pkg/wasm_vrf_worker_bg.wasm +0 -0
- package/dist/types/src/core/EmailRecovery/index.d.ts +8 -0
- package/dist/types/src/core/EmailRecovery/index.d.ts.map +1 -1
- package/dist/types/src/core/TatchiPasskey/emailRecovery.d.ts +8 -5
- package/dist/types/src/core/TatchiPasskey/emailRecovery.d.ts.map +1 -1
- package/dist/types/src/core/TatchiPasskey/index.d.ts +1 -1
- package/dist/types/src/core/TatchiPasskey/index.d.ts.map +1 -1
- package/dist/types/src/core/TatchiPasskey/scanDevice.d.ts.map +1 -1
- package/dist/types/src/core/WalletIframe/TatchiPasskeyIframe.d.ts +1 -1
- package/dist/types/src/core/WalletIframe/TatchiPasskeyIframe.d.ts.map +1 -1
- package/dist/types/src/core/WalletIframe/client/router.d.ts +1 -1
- package/dist/types/src/core/WalletIframe/client/router.d.ts.map +1 -1
- package/dist/types/src/core/WalletIframe/shared/messages.d.ts +1 -1
- package/dist/types/src/core/WalletIframe/shared/messages.d.ts.map +1 -1
- package/dist/types/src/core/WebAuthnManager/SignerWorkerManager/handlers/validation.d.ts +2 -1
- package/dist/types/src/core/WebAuthnManager/SignerWorkerManager/handlers/validation.d.ts.map +1 -1
- package/dist/types/src/core/defaultConfigs.d.ts.map +1 -1
- package/dist/types/src/core/nearCrypto.d.ts +14 -0
- package/dist/types/src/core/nearCrypto.d.ts.map +1 -1
- package/dist/types/src/core/rpcCalls.d.ts +11 -8
- package/dist/types/src/core/rpcCalls.d.ts.map +1 -1
- package/dist/types/src/core/types/emailRecovery.d.ts +10 -0
- package/dist/types/src/core/types/emailRecovery.d.ts.map +1 -0
- package/dist/types/src/core/types/index.d.ts +1 -0
- package/dist/types/src/core/types/index.d.ts.map +1 -1
- package/dist/types/src/core/types/tatchi.d.ts +0 -4
- package/dist/types/src/core/types/tatchi.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +1 -0
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/react/components/PasskeyAuthMenu/adapters/tatchi.d.ts +2 -0
- package/dist/types/src/react/components/PasskeyAuthMenu/adapters/tatchi.d.ts.map +1 -1
- package/dist/types/src/react/components/PasskeyAuthMenu/client.d.ts.map +1 -1
- package/dist/types/src/react/components/PasskeyAuthMenu/controller/useSDKEvents.d.ts +10 -0
- package/dist/types/src/react/components/PasskeyAuthMenu/controller/useSDKEvents.d.ts.map +1 -0
- package/dist/types/src/react/components/PasskeyAuthMenu/types.d.ts +8 -3
- package/dist/types/src/react/components/PasskeyAuthMenu/types.d.ts.map +1 -1
- package/dist/types/src/react/components/PasskeyAuthMenu/ui/ContentSwitcher.d.ts +2 -0
- package/dist/types/src/react/components/PasskeyAuthMenu/ui/ContentSwitcher.d.ts.map +1 -1
- package/dist/types/src/react/components/PasskeyAuthMenu/ui/EmailRecoverySlide.d.ts +1 -1
- package/dist/types/src/react/components/PasskeyAuthMenu/ui/EmailRecoverySlide.d.ts.map +1 -1
- package/dist/types/src/react/context/useSDKFlowRuntime.d.ts +10 -0
- package/dist/types/src/react/context/useSDKFlowRuntime.d.ts.map +1 -0
- package/dist/types/src/react/context/useTatchiContextValue.d.ts.map +1 -1
- package/dist/types/src/react/context/useTatchiWithSdkFlow.d.ts +9 -0
- package/dist/types/src/react/context/useTatchiWithSdkFlow.d.ts.map +1 -0
- package/dist/types/src/react/types.d.ts +31 -0
- package/dist/types/src/react/types.d.ts.map +1 -1
- package/dist/types/src/server/email-recovery/emailParsers.d.ts.map +1 -1
- package/dist/types/src/server/email-recovery/index.d.ts +5 -6
- package/dist/types/src/server/email-recovery/index.d.ts.map +1 -1
- package/dist/types/src/server/email-recovery/rpcCalls.d.ts +1 -0
- package/dist/types/src/server/email-recovery/rpcCalls.d.ts.map +1 -1
- package/dist/types/src/server/router/cloudflare-adaptor.d.ts.map +1 -1
- package/dist/workers/wasm_vrf_worker_bg.wasm +0 -0
- package/package.json +1 -1
- package/dist/esm/sdk/getDeviceNumber-fXizNGQl.js.map +0 -1
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { AccountRecoveryPhase, AccountRecoveryStatus, LoginPhase, LoginStatus, RegistrationPhase, RegistrationStatus, init_sdkSentEvents } from "../sdk/src/core/types/sdkSentEvents.js";
|
|
2
|
+
import { useMemo } from "react";
|
|
3
|
+
|
|
4
|
+
//#region src/react/context/useTatchiWithSdkFlow.ts
|
|
5
|
+
init_sdkSentEvents();
|
|
6
|
+
function useTatchiWithSdkFlow(args) {
|
|
7
|
+
const { tatchi, beginSdkFlow, appendSdkEventMessage, endSdkFlow } = args;
|
|
8
|
+
return useMemo(() => {
|
|
9
|
+
const loginAndCreateSessionWithSdkFlow = async (nearAccountId, options) => {
|
|
10
|
+
const seq = beginSdkFlow("login", nearAccountId);
|
|
11
|
+
const wrappedOptions = {
|
|
12
|
+
...options,
|
|
13
|
+
onEvent: (event) => {
|
|
14
|
+
appendSdkEventMessage(seq, event.message);
|
|
15
|
+
if (event.phase === LoginPhase.STEP_4_LOGIN_COMPLETE && event.status === LoginStatus.SUCCESS) endSdkFlow("login", seq, "success");
|
|
16
|
+
else if (event.phase === LoginPhase.LOGIN_ERROR || event.status === LoginStatus.ERROR) {
|
|
17
|
+
const error = "error" in event ? event.error : event.message;
|
|
18
|
+
endSdkFlow("login", seq, "error", error || event.message);
|
|
19
|
+
}
|
|
20
|
+
options?.onEvent?.(event);
|
|
21
|
+
},
|
|
22
|
+
onError: (error) => {
|
|
23
|
+
appendSdkEventMessage(seq, error.message);
|
|
24
|
+
endSdkFlow("login", seq, "error", error.message);
|
|
25
|
+
options?.onError?.(error);
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
return await tatchi.loginAndCreateSession(nearAccountId, wrappedOptions);
|
|
29
|
+
};
|
|
30
|
+
const registerPasskeyWithSdkFlow = async (nearAccountId, options) => {
|
|
31
|
+
const seq = beginSdkFlow("register", nearAccountId);
|
|
32
|
+
const wrappedOptions = {
|
|
33
|
+
...options,
|
|
34
|
+
onEvent: (event) => {
|
|
35
|
+
appendSdkEventMessage(seq, event.message);
|
|
36
|
+
if (event.phase === RegistrationPhase.STEP_8_REGISTRATION_COMPLETE && event.status === RegistrationStatus.SUCCESS) endSdkFlow("register", seq, "success");
|
|
37
|
+
else if (event.phase === RegistrationPhase.REGISTRATION_ERROR || event.status === RegistrationStatus.ERROR) {
|
|
38
|
+
const error = "error" in event ? event.error : event.message;
|
|
39
|
+
endSdkFlow("register", seq, "error", error || event.message);
|
|
40
|
+
}
|
|
41
|
+
options?.onEvent?.(event);
|
|
42
|
+
},
|
|
43
|
+
onError: (error) => {
|
|
44
|
+
appendSdkEventMessage(seq, error.message);
|
|
45
|
+
endSdkFlow("register", seq, "error", error.message);
|
|
46
|
+
options?.onError?.(error);
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
return await tatchi.registerPasskey(nearAccountId, wrappedOptions);
|
|
50
|
+
};
|
|
51
|
+
const recoverAccountFlowWithSdkFlow = async (args$1) => {
|
|
52
|
+
const seq = beginSdkFlow("recover", args$1?.accountId);
|
|
53
|
+
const options = args$1?.options;
|
|
54
|
+
const wrappedOptions = {
|
|
55
|
+
...options,
|
|
56
|
+
onEvent: (event) => {
|
|
57
|
+
appendSdkEventMessage(seq, event.message);
|
|
58
|
+
if (event.phase === AccountRecoveryPhase.STEP_5_ACCOUNT_RECOVERY_COMPLETE && event.status === AccountRecoveryStatus.SUCCESS) endSdkFlow("recover", seq, "success");
|
|
59
|
+
else if (event.phase === AccountRecoveryPhase.ERROR || event.status === AccountRecoveryStatus.ERROR) {
|
|
60
|
+
const error = "error" in event ? event.error : event.message;
|
|
61
|
+
endSdkFlow("recover", seq, "error", error || event.message);
|
|
62
|
+
}
|
|
63
|
+
options?.onEvent?.(event);
|
|
64
|
+
},
|
|
65
|
+
onError: (error) => {
|
|
66
|
+
appendSdkEventMessage(seq, error.message);
|
|
67
|
+
endSdkFlow("recover", seq, "error", error.message);
|
|
68
|
+
options?.onError?.(error);
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
return await tatchi.recoverAccountFlow({
|
|
72
|
+
...args$1,
|
|
73
|
+
options: wrappedOptions
|
|
74
|
+
});
|
|
75
|
+
};
|
|
76
|
+
return new Proxy(tatchi, { get(target, prop, receiver) {
|
|
77
|
+
if (prop === "loginAndCreateSession") return loginAndCreateSessionWithSdkFlow;
|
|
78
|
+
if (prop === "registerPasskey") return registerPasskeyWithSdkFlow;
|
|
79
|
+
if (prop === "recoverAccountFlow") return recoverAccountFlowWithSdkFlow;
|
|
80
|
+
const value = Reflect.get(target, prop, receiver);
|
|
81
|
+
if (typeof value === "function") return value.bind(target);
|
|
82
|
+
return value;
|
|
83
|
+
} });
|
|
84
|
+
}, [
|
|
85
|
+
appendSdkEventMessage,
|
|
86
|
+
beginSdkFlow,
|
|
87
|
+
endSdkFlow,
|
|
88
|
+
tatchi
|
|
89
|
+
]);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
//#endregion
|
|
93
|
+
export { useTatchiWithSdkFlow };
|
|
94
|
+
//# sourceMappingURL=useTatchiWithSdkFlow.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useTatchiWithSdkFlow.js","names":["loginAndCreateSessionWithSdkFlow: LoginAndCreateSessionFn","wrappedOptions: LoginHooksOptions","registerPasskeyWithSdkFlow: RegisterPasskeyFn","wrappedOptions: RegistrationHooksOptions","recoverAccountFlowWithSdkFlow: RecoverAccountFlowFn","args","options: AccountRecoveryHooksOptions | undefined","wrappedOptions: AccountRecoveryHooksOptions","value: unknown"],"sources":["../../../../src/react/context/useTatchiWithSdkFlow.ts"],"sourcesContent":["import { useMemo } from 'react';\nimport type { TatchiPasskey } from '@/core/TatchiPasskey';\nimport {\n AccountRecoveryPhase,\n AccountRecoveryStatus,\n type AccountRecoveryHooksOptions,\n type AccountRecoverySSEEvent,\n LoginPhase,\n LoginStatus,\n type LoginHooksOptions,\n type LoginSSEvent,\n RegistrationPhase,\n RegistrationStatus,\n type RegistrationHooksOptions,\n type RegistrationSSEEvent,\n} from '@/core/types/sdkSentEvents';\n\nexport function useTatchiWithSdkFlow(args: {\n tatchi: TatchiPasskey;\n beginSdkFlow: (kind: 'login' | 'register' | 'recover', accountId?: string) => number;\n appendSdkEventMessage: (seq: number, message: string) => void;\n endSdkFlow: (kind: 'login' | 'register' | 'recover', seq: number, status: 'success' | 'error', error?: string) => void;\n}): TatchiPasskey {\n const { tatchi, beginSdkFlow, appendSdkEventMessage, endSdkFlow } = args;\n\n return useMemo(() => {\n /**\n * We use a `Proxy` to instrument a few core flow entrypoints (login/register/recover)\n * while preserving the full `TatchiPasskey` API surface.\n *\n * This lets *all* callers (not just PasskeyAuthMenu) use `ctx.tatchi.*` directly and\n * still have `sdkFlow` update as events stream in.\n */\n type LoginAndCreateSessionFn = TatchiPasskey['loginAndCreateSession'];\n type RegisterPasskeyFn = TatchiPasskey['registerPasskey'];\n type RecoverAccountFlowFn = TatchiPasskey['recoverAccountFlow'];\n\n const loginAndCreateSessionWithSdkFlow: LoginAndCreateSessionFn = async (\n nearAccountId,\n options,\n ) => {\n const seq = beginSdkFlow('login', nearAccountId);\n const wrappedOptions: LoginHooksOptions = {\n ...options,\n onEvent: (event: LoginSSEvent) => {\n appendSdkEventMessage(seq, event.message);\n if (event.phase === LoginPhase.STEP_4_LOGIN_COMPLETE && event.status === LoginStatus.SUCCESS) {\n endSdkFlow('login', seq, 'success');\n } else if (event.phase === LoginPhase.LOGIN_ERROR || event.status === LoginStatus.ERROR) {\n const error = 'error' in event ? event.error : event.message;\n endSdkFlow('login', seq, 'error', error || event.message);\n }\n options?.onEvent?.(event);\n },\n onError: (error: Error) => {\n appendSdkEventMessage(seq, error.message);\n endSdkFlow('login', seq, 'error', error.message);\n options?.onError?.(error);\n },\n };\n\n return await tatchi.loginAndCreateSession(nearAccountId, wrappedOptions);\n };\n\n const registerPasskeyWithSdkFlow: RegisterPasskeyFn = async (\n nearAccountId,\n options,\n ) => {\n const seq = beginSdkFlow('register', nearAccountId);\n const wrappedOptions: RegistrationHooksOptions = {\n ...options,\n onEvent: (event: RegistrationSSEEvent) => {\n appendSdkEventMessage(seq, event.message);\n if (\n event.phase === RegistrationPhase.STEP_8_REGISTRATION_COMPLETE &&\n event.status === RegistrationStatus.SUCCESS\n ) {\n endSdkFlow('register', seq, 'success');\n } else if (event.phase === RegistrationPhase.REGISTRATION_ERROR || event.status === RegistrationStatus.ERROR) {\n const error = 'error' in event ? event.error : event.message;\n endSdkFlow('register', seq, 'error', error || event.message);\n }\n options?.onEvent?.(event);\n },\n onError: (error: Error) => {\n appendSdkEventMessage(seq, error.message);\n endSdkFlow('register', seq, 'error', error.message);\n options?.onError?.(error);\n },\n };\n\n return await tatchi.registerPasskey(nearAccountId, wrappedOptions);\n };\n\n const recoverAccountFlowWithSdkFlow: RecoverAccountFlowFn = async (args) => {\n const seq = beginSdkFlow('recover', args?.accountId);\n const options: AccountRecoveryHooksOptions | undefined = args?.options;\n\n const wrappedOptions: AccountRecoveryHooksOptions = {\n ...options,\n onEvent: (event: AccountRecoverySSEEvent) => {\n appendSdkEventMessage(seq, event.message);\n if (\n event.phase === AccountRecoveryPhase.STEP_5_ACCOUNT_RECOVERY_COMPLETE &&\n event.status === AccountRecoveryStatus.SUCCESS\n ) {\n endSdkFlow('recover', seq, 'success');\n } else if (event.phase === AccountRecoveryPhase.ERROR || event.status === AccountRecoveryStatus.ERROR) {\n const error = 'error' in event ? event.error : event.message;\n endSdkFlow('recover', seq, 'error', error || event.message);\n }\n options?.onEvent?.(event);\n },\n onError: (error: Error) => {\n appendSdkEventMessage(seq, error.message);\n endSdkFlow('recover', seq, 'error', error.message);\n options?.onError?.(error);\n },\n };\n\n return await tatchi.recoverAccountFlow({\n ...args,\n options: wrappedOptions,\n });\n };\n\n return new Proxy(tatchi, {\n get(target, prop, receiver) {\n if (prop === 'loginAndCreateSession') {\n return loginAndCreateSessionWithSdkFlow;\n }\n\n if (prop === 'registerPasskey') {\n return registerPasskeyWithSdkFlow;\n }\n\n if (prop === 'recoverAccountFlow') {\n return recoverAccountFlowWithSdkFlow;\n }\n\n const value: unknown = Reflect.get(target as object, prop, receiver);\n // For non-wrapped methods, bind to preserve `this` on the class instance.\n if (typeof value === 'function') return (value as (...args: unknown[]) => unknown).bind(target);\n return value;\n },\n });\n }, [appendSdkEventMessage, beginSdkFlow, endSdkFlow, tatchi]);\n}\n\nexport default useTatchiWithSdkFlow;\n"],"mappings":";;;;;AAiBA,SAAgB,qBAAqB,MAKnB;CAChB,MAAM,EAAE,QAAQ,cAAc,uBAAuB,eAAe;AAEpE,QAAO,cAAc;EAYnB,MAAMA,mCAA4D,OAChE,eACA,YACG;GACH,MAAM,MAAM,aAAa,SAAS;GAClC,MAAMC,iBAAoC;IACxC,GAAG;IACH,UAAU,UAAwB;AAChC,2BAAsB,KAAK,MAAM;AACjC,SAAI,MAAM,UAAU,WAAW,yBAAyB,MAAM,WAAW,YAAY,QACnF,YAAW,SAAS,KAAK;cAChB,MAAM,UAAU,WAAW,eAAe,MAAM,WAAW,YAAY,OAAO;MACvF,MAAM,QAAQ,WAAW,QAAQ,MAAM,QAAQ,MAAM;AACrD,iBAAW,SAAS,KAAK,SAAS,SAAS,MAAM;;AAEnD,cAAS,UAAU;;IAErB,UAAU,UAAiB;AACzB,2BAAsB,KAAK,MAAM;AACjC,gBAAW,SAAS,KAAK,SAAS,MAAM;AACxC,cAAS,UAAU;;;AAIvB,UAAO,MAAM,OAAO,sBAAsB,eAAe;;EAG3D,MAAMC,6BAAgD,OACpD,eACA,YACG;GACH,MAAM,MAAM,aAAa,YAAY;GACrC,MAAMC,iBAA2C;IAC/C,GAAG;IACH,UAAU,UAAgC;AACxC,2BAAsB,KAAK,MAAM;AACjC,SACE,MAAM,UAAU,kBAAkB,gCAClC,MAAM,WAAW,mBAAmB,QAEpC,YAAW,YAAY,KAAK;cACnB,MAAM,UAAU,kBAAkB,sBAAsB,MAAM,WAAW,mBAAmB,OAAO;MAC5G,MAAM,QAAQ,WAAW,QAAQ,MAAM,QAAQ,MAAM;AACrD,iBAAW,YAAY,KAAK,SAAS,SAAS,MAAM;;AAEtD,cAAS,UAAU;;IAErB,UAAU,UAAiB;AACzB,2BAAsB,KAAK,MAAM;AACjC,gBAAW,YAAY,KAAK,SAAS,MAAM;AAC3C,cAAS,UAAU;;;AAIvB,UAAO,MAAM,OAAO,gBAAgB,eAAe;;EAGrD,MAAMC,gCAAsD,OAAO,WAAS;GAC1E,MAAM,MAAM,aAAa,WAAWC,QAAM;GAC1C,MAAMC,UAAmDD,QAAM;GAE/D,MAAME,iBAA8C;IAClD,GAAG;IACH,UAAU,UAAmC;AAC3C,2BAAsB,KAAK,MAAM;AACjC,SACE,MAAM,UAAU,qBAAqB,oCACrC,MAAM,WAAW,sBAAsB,QAEvC,YAAW,WAAW,KAAK;cAClB,MAAM,UAAU,qBAAqB,SAAS,MAAM,WAAW,sBAAsB,OAAO;MACrG,MAAM,QAAQ,WAAW,QAAQ,MAAM,QAAQ,MAAM;AACrD,iBAAW,WAAW,KAAK,SAAS,SAAS,MAAM;;AAErD,cAAS,UAAU;;IAErB,UAAU,UAAiB;AACzB,2BAAsB,KAAK,MAAM;AACjC,gBAAW,WAAW,KAAK,SAAS,MAAM;AAC1C,cAAS,UAAU;;;AAIvB,UAAO,MAAM,OAAO,mBAAmB;IACrC,GAAGF;IACH,SAAS;;;AAIb,SAAO,IAAI,MAAM,QAAQ,EACvB,IAAI,QAAQ,MAAM,UAAU;AAC1B,OAAI,SAAS,wBACX,QAAO;AAGT,OAAI,SAAS,kBACX,QAAO;AAGT,OAAI,SAAS,qBACX,QAAO;GAGT,MAAMG,QAAiB,QAAQ,IAAI,QAAkB,MAAM;AAE3D,OAAI,OAAO,UAAU,WAAY,QAAQ,MAA0C,KAAK;AACxF,UAAO;;IAGV;EAAC;EAAuB;EAAc;EAAY"}
|
|
@@ -1,9 +1,32 @@
|
|
|
1
1
|
import { __esm } from "../../../../_virtual/rolldown_runtime.js";
|
|
2
2
|
import { init_accountIds, toAccountId } from "../types/accountIds.js";
|
|
3
3
|
import { IndexedDBManager, init_IndexedDBManager } from "../IndexedDBManager/index.js";
|
|
4
|
+
import { base64Decode, init_base64 } from "../../utils/base64.js";
|
|
4
5
|
import { EmailRecoveryPendingStore, init_emailRecoveryPendingStore } from "./emailRecoveryPendingStore.js";
|
|
5
6
|
|
|
6
7
|
//#region src/core/EmailRecovery/index.ts
|
|
8
|
+
function getTxSuccessValueBase64(outcome) {
|
|
9
|
+
const status = outcome.status;
|
|
10
|
+
if (!status || typeof status !== "object") return null;
|
|
11
|
+
if (!("SuccessValue" in status)) return null;
|
|
12
|
+
const value = status.SuccessValue;
|
|
13
|
+
return typeof value === "string" && value.length > 0 ? value : null;
|
|
14
|
+
}
|
|
15
|
+
function parseLinkDeviceRegisterUserResponse(outcome) {
|
|
16
|
+
try {
|
|
17
|
+
const successValueB64 = getTxSuccessValueBase64(outcome);
|
|
18
|
+
if (!successValueB64) return null;
|
|
19
|
+
const bytes = base64Decode(successValueB64);
|
|
20
|
+
const text = new TextDecoder().decode(bytes);
|
|
21
|
+
if (!text.trim()) return null;
|
|
22
|
+
const parsed = JSON.parse(text);
|
|
23
|
+
if (!parsed || typeof parsed !== "object") return null;
|
|
24
|
+
const candidate = parsed;
|
|
25
|
+
return typeof candidate.verified === "boolean" ? candidate : null;
|
|
26
|
+
} catch {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
7
30
|
async function hashRecoveryEmails(emails, accountId) {
|
|
8
31
|
const encoder = new TextEncoder();
|
|
9
32
|
const salt = (accountId || "").trim().toLowerCase();
|
|
@@ -54,6 +77,7 @@ var canonicalizeEmail, bytesToHex;
|
|
|
54
77
|
var init_EmailRecovery = __esm({ "src/core/EmailRecovery/index.ts": (() => {
|
|
55
78
|
init_accountIds();
|
|
56
79
|
init_IndexedDBManager();
|
|
80
|
+
init_base64();
|
|
57
81
|
init_emailRecoveryPendingStore();
|
|
58
82
|
canonicalizeEmail = (email) => {
|
|
59
83
|
const raw = String(email || "").trim();
|
|
@@ -77,5 +101,5 @@ var init_EmailRecovery = __esm({ "src/core/EmailRecovery/index.ts": (() => {
|
|
|
77
101
|
|
|
78
102
|
//#endregion
|
|
79
103
|
init_EmailRecovery();
|
|
80
|
-
export { bytesToHex, getLocalRecoveryEmails, init_EmailRecovery, prepareRecoveryEmails };
|
|
104
|
+
export { bytesToHex, canonicalizeEmail, getLocalRecoveryEmails, init_EmailRecovery, parseLinkDeviceRegisterUserResponse, prepareRecoveryEmails };
|
|
81
105
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["hashed: number[][]","pairs: RecoveryEmailEntry[]"],"sources":["../../../../../../../src/core/EmailRecovery/index.ts"],"sourcesContent":["import type { AccountId } from '../types/accountIds';\nimport { toAccountId } from '../types/accountIds';\nimport { IndexedDBManager, type RecoveryEmailRecord } from '../IndexedDBManager';\nexport { EmailRecoveryPendingStore, type PendingStore } from './emailRecoveryPendingStore';\n\nexport type RecoveryEmailEntry = {\n hashHex: string;\n email: string;\n};\n\nexport { type RecoveryEmailRecord };\n\nexport const canonicalizeEmail = (email: string): string => {\n const raw = String(email || '').trim();\n if (!raw) return '';\n\n // Handle cases where a full header line is passed in (e.g. \"From: ...\").\n const withoutHeaderName = raw.replace(/^[a-z0-9-]+\\s*:\\s*/i, '').trim();\n\n const emailRegex =\n /([a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\\.[a-zA-Z0-9-]+)*)/;\n\n // Prefer the common \"Name <email@domain>\" format when present, but still\n // validate/extract the actual address via regex.\n const angleMatch = withoutHeaderName.match(/<([^>]+)>/);\n const candidates = [\n angleMatch?.[1],\n withoutHeaderName,\n ].filter((v): v is string => typeof v === 'string' && v.length > 0);\n\n for (const candidate of candidates) {\n const cleaned = candidate.replace(/^mailto:\\s*/i, '');\n const match = cleaned.match(emailRegex);\n if (match?.[1]) {\n return match[1].trim().toLowerCase();\n }\n }\n\n return withoutHeaderName.toLowerCase();\n};\n\nexport const bytesToHex = (bytes: number[] | Uint8Array): string => {\n const arr = bytes instanceof Uint8Array ? bytes : Uint8Array.from(bytes);\n return `0x${Array.from(arr)\n .map(b => b.toString(16).padStart(2, '0'))\n .join('')}`;\n};\n\nasync function hashRecoveryEmails(emails: string[], accountId: AccountId): Promise<number[][]> {\n const encoder = new TextEncoder();\n const salt = (accountId || '').trim().toLowerCase();\n const normalized = (emails || [])\n .map(e => e.trim())\n .filter(e => e.length > 0);\n\n const hashed: number[][] = [];\n\n for (const email of normalized) {\n try {\n const canonicalEmail = canonicalizeEmail(email);\n const input = `${canonicalEmail}|${salt}`;\n const data = encoder.encode(input);\n const digest = await crypto.subtle.digest('SHA-256', data);\n const bytes = new Uint8Array(digest);\n hashed.push(Array.from(bytes));\n } catch {\n const bytes = encoder.encode(email.toLowerCase());\n hashed.push(Array.from(bytes));\n }\n }\n\n return hashed;\n}\n\n/**\n * Canonicalize and hash recovery emails for an account, and persist the mapping\n * (hashHex → canonical email) in IndexedDB on a best-effort basis.\n */\nexport async function prepareRecoveryEmails(nearAccountId: AccountId, recoveryEmails: string[]): Promise<{\n hashes: number[][];\n pairs: RecoveryEmailEntry[];\n}> {\n const accountId = toAccountId(nearAccountId);\n\n const trimmedEmails = (recoveryEmails || []).map(e => e.trim()).filter(e => e.length > 0);\n const canonicalEmails = trimmedEmails.map(canonicalizeEmail);\n const recoveryEmailHashes = await hashRecoveryEmails(recoveryEmails, accountId);\n\n const pairs: RecoveryEmailEntry[] = recoveryEmailHashes.map((hashBytes, idx) => ({\n hashHex: bytesToHex(hashBytes),\n email: canonicalEmails[idx],\n }));\n\n void (async () => {\n try {\n await IndexedDBManager.upsertRecoveryEmails(accountId, pairs);\n } catch (error) {\n console.warn('[EmailRecovery] Failed to persist local recovery emails', error);\n }\n })();\n\n return { hashes: recoveryEmailHashes, pairs };\n}\n\nexport async function getLocalRecoveryEmails(nearAccountId: AccountId): Promise<RecoveryEmailRecord[]> {\n return IndexedDBManager.getRecoveryEmails(nearAccountId);\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","names":["hashed: number[][]","pairs: RecoveryEmailEntry[]"],"sources":["../../../../../../../src/core/EmailRecovery/index.ts"],"sourcesContent":["import type { AccountId } from '../types/accountIds';\nimport { toAccountId } from '../types/accountIds';\nimport { IndexedDBManager, type RecoveryEmailRecord } from '../IndexedDBManager';\nimport type { FinalExecutionOutcome } from '@near-js/types';\nimport { base64Decode } from '../../utils/base64';\nexport { EmailRecoveryPendingStore, type PendingStore } from './emailRecoveryPendingStore';\n\nexport type RecoveryEmailEntry = {\n hashHex: string;\n email: string;\n};\n\nexport { type RecoveryEmailRecord };\n\nexport type LinkDeviceRegisterUserResponse = {\n verified?: boolean;\n registration_info?: unknown;\n registrationInfo?: unknown;\n error?: unknown;\n};\n\nfunction getTxSuccessValueBase64(outcome: FinalExecutionOutcome): string | null {\n const status = outcome.status;\n if (!status || typeof status !== 'object') return null;\n if (!('SuccessValue' in status)) return null;\n const value = status.SuccessValue;\n return typeof value === 'string' && value.length > 0 ? value : null;\n}\n\nexport function parseLinkDeviceRegisterUserResponse(\n outcome: FinalExecutionOutcome\n): LinkDeviceRegisterUserResponse | null {\n try {\n const successValueB64 = getTxSuccessValueBase64(outcome);\n if (!successValueB64) return null;\n\n const bytes = base64Decode(successValueB64);\n const text = new TextDecoder().decode(bytes);\n if (!text.trim()) return null;\n\n const parsed = JSON.parse(text) as unknown;\n if (!parsed || typeof parsed !== 'object') return null;\n const candidate = parsed as LinkDeviceRegisterUserResponse;\n return typeof candidate.verified === 'boolean' ? candidate : null;\n } catch {\n return null;\n }\n}\n\nexport const canonicalizeEmail = (email: string): string => {\n const raw = String(email || '').trim();\n if (!raw) return '';\n\n // Handle cases where a full header line is passed in (e.g. \"From: ...\").\n const withoutHeaderName = raw.replace(/^[a-z0-9-]+\\s*:\\s*/i, '').trim();\n\n const emailRegex =\n /([a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\\.[a-zA-Z0-9-]+)*)/;\n\n // Prefer the common \"Name <email@domain>\" format when present, but still\n // validate/extract the actual address via regex.\n const angleMatch = withoutHeaderName.match(/<([^>]+)>/);\n const candidates = [\n angleMatch?.[1],\n withoutHeaderName,\n ].filter((v): v is string => typeof v === 'string' && v.length > 0);\n\n for (const candidate of candidates) {\n const cleaned = candidate.replace(/^mailto:\\s*/i, '');\n const match = cleaned.match(emailRegex);\n if (match?.[1]) {\n return match[1].trim().toLowerCase();\n }\n }\n\n return withoutHeaderName.toLowerCase();\n};\n\nexport const bytesToHex = (bytes: number[] | Uint8Array): string => {\n const arr = bytes instanceof Uint8Array ? bytes : Uint8Array.from(bytes);\n return `0x${Array.from(arr)\n .map(b => b.toString(16).padStart(2, '0'))\n .join('')}`;\n};\n\nasync function hashRecoveryEmails(emails: string[], accountId: AccountId): Promise<number[][]> {\n const encoder = new TextEncoder();\n const salt = (accountId || '').trim().toLowerCase();\n const normalized = (emails || [])\n .map(e => e.trim())\n .filter(e => e.length > 0);\n\n const hashed: number[][] = [];\n\n for (const email of normalized) {\n try {\n const canonicalEmail = canonicalizeEmail(email);\n const input = `${canonicalEmail}|${salt}`;\n const data = encoder.encode(input);\n const digest = await crypto.subtle.digest('SHA-256', data);\n const bytes = new Uint8Array(digest);\n hashed.push(Array.from(bytes));\n } catch {\n const bytes = encoder.encode(email.toLowerCase());\n hashed.push(Array.from(bytes));\n }\n }\n\n return hashed;\n}\n\n/**\n * Canonicalize and hash recovery emails for an account, and persist the mapping\n * (hashHex → canonical email) in IndexedDB on a best-effort basis.\n */\nexport async function prepareRecoveryEmails(nearAccountId: AccountId, recoveryEmails: string[]): Promise<{\n hashes: number[][];\n pairs: RecoveryEmailEntry[];\n}> {\n const accountId = toAccountId(nearAccountId);\n\n const trimmedEmails = (recoveryEmails || []).map(e => e.trim()).filter(e => e.length > 0);\n const canonicalEmails = trimmedEmails.map(canonicalizeEmail);\n const recoveryEmailHashes = await hashRecoveryEmails(recoveryEmails, accountId);\n\n const pairs: RecoveryEmailEntry[] = recoveryEmailHashes.map((hashBytes, idx) => ({\n hashHex: bytesToHex(hashBytes),\n email: canonicalEmails[idx],\n }));\n\n void (async () => {\n try {\n await IndexedDBManager.upsertRecoveryEmails(accountId, pairs);\n } catch (error) {\n console.warn('[EmailRecovery] Failed to persist local recovery emails', error);\n }\n })();\n\n return { hashes: recoveryEmailHashes, pairs };\n}\n\nexport async function getLocalRecoveryEmails(nearAccountId: AccountId): Promise<RecoveryEmailRecord[]> {\n return IndexedDBManager.getRecoveryEmails(nearAccountId);\n}\n"],"mappings":";;;;;;;AAqBA,SAAS,wBAAwB,SAA+C;CAC9E,MAAM,SAAS,QAAQ;AACvB,KAAI,CAAC,UAAU,OAAO,WAAW,SAAU,QAAO;AAClD,KAAI,EAAE,kBAAkB,QAAS,QAAO;CACxC,MAAM,QAAQ,OAAO;AACrB,QAAO,OAAO,UAAU,YAAY,MAAM,SAAS,IAAI,QAAQ;;AAGjE,SAAgB,oCACd,SACuC;AACvC,KAAI;EACF,MAAM,kBAAkB,wBAAwB;AAChD,MAAI,CAAC,gBAAiB,QAAO;EAE7B,MAAM,QAAQ,aAAa;EAC3B,MAAM,OAAO,IAAI,cAAc,OAAO;AACtC,MAAI,CAAC,KAAK,OAAQ,QAAO;EAEzB,MAAM,SAAS,KAAK,MAAM;AAC1B,MAAI,CAAC,UAAU,OAAO,WAAW,SAAU,QAAO;EAClD,MAAM,YAAY;AAClB,SAAO,OAAO,UAAU,aAAa,YAAY,YAAY;SACvD;AACN,SAAO;;;AAwCX,eAAe,mBAAmB,QAAkB,WAA2C;CAC7F,MAAM,UAAU,IAAI;CACpB,MAAM,QAAQ,aAAa,IAAI,OAAO;CACtC,MAAM,cAAc,UAAU,IAC3B,KAAI,MAAK,EAAE,QACX,QAAO,MAAK,EAAE,SAAS;CAE1B,MAAMA,SAAqB;AAE3B,MAAK,MAAM,SAAS,WAClB,KAAI;EACF,MAAM,iBAAiB,kBAAkB;EACzC,MAAM,QAAQ,GAAG,eAAe,GAAG;EACnC,MAAM,OAAO,QAAQ,OAAO;EAC5B,MAAM,SAAS,MAAM,OAAO,OAAO,OAAO,WAAW;EACrD,MAAM,QAAQ,IAAI,WAAW;AAC7B,SAAO,KAAK,MAAM,KAAK;SACjB;EACN,MAAM,QAAQ,QAAQ,OAAO,MAAM;AACnC,SAAO,KAAK,MAAM,KAAK;;AAI3B,QAAO;;;;;;AAOT,eAAsB,sBAAsB,eAA0B,gBAGnE;CACD,MAAM,YAAY,YAAY;CAE9B,MAAM,iBAAiB,kBAAkB,IAAI,KAAI,MAAK,EAAE,QAAQ,QAAO,MAAK,EAAE,SAAS;CACvF,MAAM,kBAAkB,cAAc,IAAI;CAC1C,MAAM,sBAAsB,MAAM,mBAAmB,gBAAgB;CAErE,MAAMC,QAA8B,oBAAoB,KAAK,WAAW,SAAS;EAC/E,SAAS,WAAW;EACpB,OAAO,gBAAgB;;AAGzB,EAAM,YAAY;AAChB,MAAI;AACF,SAAM,iBAAiB,qBAAqB,WAAW;WAChD,OAAO;AACd,WAAQ,KAAK,2DAA2D;;;AAI5E,QAAO;EAAE,QAAQ;EAAqB;;;AAGxC,eAAsB,uBAAuB,eAA0D;AACrG,QAAO,iBAAiB,kBAAkB;;;;;;;;CA7F/B,qBAAqB,UAA0B;EAC1D,MAAM,MAAM,OAAO,SAAS,IAAI;AAChC,MAAI,CAAC,IAAK,QAAO;EAGjB,MAAM,oBAAoB,IAAI,QAAQ,uBAAuB,IAAI;EAEjE,MAAM,aACJ;EAIF,MAAM,aAAa,kBAAkB,MAAM;EAC3C,MAAM,aAAa,CACjB,aAAa,IACb,mBACA,QAAQ,MAAmB,OAAO,MAAM,YAAY,EAAE,SAAS;AAEjE,OAAK,MAAM,aAAa,YAAY;GAClC,MAAM,UAAU,UAAU,QAAQ,gBAAgB;GAClD,MAAM,QAAQ,QAAQ,MAAM;AAC5B,OAAI,QAAQ,GACV,QAAO,MAAM,GAAG,OAAO;;AAI3B,SAAO,kBAAkB;;CAGd,cAAc,UAAyC;EAClE,MAAM,MAAM,iBAAiB,aAAa,QAAQ,WAAW,KAAK;AAClE,SAAO,KAAK,MAAM,KAAK,KACpB,KAAI,MAAK,EAAE,SAAS,IAAI,SAAS,GAAG,MACpC,KAAK"}
|
|
@@ -3,12 +3,16 @@ import { init_validation, validateNearAccountId } from "../../utils/validation.j
|
|
|
3
3
|
import { init_accountIds, toAccountId } from "../types/accountIds.js";
|
|
4
4
|
import { IndexedDBManager, init_IndexedDBManager } from "../IndexedDBManager/index.js";
|
|
5
5
|
import { createRandomVRFChallenge, init_vrf_worker } from "../types/vrf-worker.js";
|
|
6
|
+
import { errorMessage, init_errors } from "../../utils/errors.js";
|
|
6
7
|
import { EmailRecoveryPhase, EmailRecoveryStatus, init_sdkSentEvents } from "../types/sdkSentEvents.js";
|
|
7
8
|
import { DEFAULT_WAIT_STATUS, init_rpc } from "../types/rpc.js";
|
|
9
|
+
import { getEmailRecoveryAttempt, init_rpcCalls } from "../rpcCalls.js";
|
|
8
10
|
import { init_getDeviceNumber, parseDeviceNumber } from "../WebAuthnManager/SignerWorkerManager/getDeviceNumber.js";
|
|
11
|
+
import { ensureEd25519Prefix, init_nearCrypto } from "../nearCrypto.js";
|
|
9
12
|
import { getLoginSession, init_login } from "./login.js";
|
|
10
13
|
import { EmailRecoveryPendingStore } from "../EmailRecovery/emailRecoveryPendingStore.js";
|
|
11
|
-
import { init_EmailRecovery } from "../EmailRecovery/index.js";
|
|
14
|
+
import { init_EmailRecovery, parseLinkDeviceRegisterUserResponse } from "../EmailRecovery/index.js";
|
|
15
|
+
import { EmailRecoveryError, EmailRecoveryErrorCode, init_emailRecovery as init_emailRecovery$1 } from "../types/emailRecovery.js";
|
|
12
16
|
|
|
13
17
|
//#region src/core/TatchiPasskey/emailRecovery.ts
|
|
14
18
|
var emailRecovery_exports = {};
|
|
@@ -23,16 +27,12 @@ function getEmailRecoveryConfig(configs) {
|
|
|
23
27
|
const maxPollingDurationMs = Number(relayerEmailCfg.maxPollingDurationMs);
|
|
24
28
|
const pendingTtlMs = Number(relayerEmailCfg.pendingTtlMs);
|
|
25
29
|
const mailtoAddress = String(relayerEmailCfg.mailtoAddress);
|
|
26
|
-
const dkimVerifierAccountId = String(relayerEmailCfg.dkimVerifierAccountId);
|
|
27
|
-
const verificationViewMethod = String(relayerEmailCfg.verificationViewMethod);
|
|
28
30
|
return {
|
|
29
31
|
minBalanceYocto,
|
|
30
32
|
pollingIntervalMs,
|
|
31
33
|
maxPollingDurationMs,
|
|
32
34
|
pendingTtlMs,
|
|
33
|
-
mailtoAddress
|
|
34
|
-
dkimVerifierAccountId,
|
|
35
|
-
verificationViewMethod
|
|
35
|
+
mailtoAddress
|
|
36
36
|
};
|
|
37
37
|
}
|
|
38
38
|
function generateEmailRecoveryRequestId() {
|
|
@@ -48,6 +48,7 @@ var EmailRecoveryFlow;
|
|
|
48
48
|
var init_emailRecovery = __esm({ "src/core/TatchiPasskey/emailRecovery.ts": (() => {
|
|
49
49
|
init_IndexedDBManager();
|
|
50
50
|
init_validation();
|
|
51
|
+
init_errors();
|
|
51
52
|
init_accountIds();
|
|
52
53
|
init_sdkSentEvents();
|
|
53
54
|
init_vrf_worker();
|
|
@@ -55,6 +56,9 @@ var init_emailRecovery = __esm({ "src/core/TatchiPasskey/emailRecovery.ts": (()
|
|
|
55
56
|
init_getDeviceNumber();
|
|
56
57
|
init_login();
|
|
57
58
|
init_EmailRecovery();
|
|
59
|
+
init_emailRecovery$1();
|
|
60
|
+
init_rpcCalls();
|
|
61
|
+
init_nearCrypto();
|
|
58
62
|
EmailRecoveryFlow = class {
|
|
59
63
|
context;
|
|
60
64
|
options;
|
|
@@ -82,8 +86,9 @@ var init_emailRecovery = __esm({ "src/core/TatchiPasskey/emailRecovery.ts": (()
|
|
|
82
86
|
emit(event) {
|
|
83
87
|
this.options?.onEvent?.(event);
|
|
84
88
|
}
|
|
85
|
-
emitError(step,
|
|
86
|
-
const err = new Error(
|
|
89
|
+
emitError(step, messageOrError) {
|
|
90
|
+
const err = typeof messageOrError === "string" ? new Error(messageOrError) : messageOrError;
|
|
91
|
+
const message = err.message || (typeof messageOrError === "string" ? messageOrError : "Unknown error");
|
|
87
92
|
this.phase = EmailRecoveryPhase.ERROR;
|
|
88
93
|
this.error = err;
|
|
89
94
|
this.emit({
|
|
@@ -142,20 +147,19 @@ var init_emailRecovery = __esm({ "src/core/TatchiPasskey/emailRecovery.ts": (()
|
|
|
142
147
|
const accountView = await this.context.nearClient.viewAccount(nearAccountId);
|
|
143
148
|
const available = this.computeAvailableBalance(accountView);
|
|
144
149
|
if (available < BigInt(minBalanceYocto)) await this.fail(1, `This account does not have enough NEAR to finalize recovery. Available: ${available.toString()} yocto; required: ${String(minBalanceYocto)}. Please top up and try again.`);
|
|
145
|
-
} catch (
|
|
146
|
-
await this.fail(1,
|
|
150
|
+
} catch (err) {
|
|
151
|
+
await this.fail(1, errorMessage(err) || "Failed to fetch account balance for recovery");
|
|
147
152
|
}
|
|
148
153
|
}
|
|
149
|
-
|
|
154
|
+
getCanonicalRecoveryEmail(recoveryEmail) {
|
|
150
155
|
const canonicalEmail = String(recoveryEmail || "").trim().toLowerCase();
|
|
151
|
-
|
|
152
|
-
return canonicalEmail;
|
|
156
|
+
return canonicalEmail || void 0;
|
|
153
157
|
}
|
|
154
158
|
async getNextDeviceNumberFromContract(nearAccountId) {
|
|
155
159
|
try {
|
|
156
160
|
const { syncAuthenticatorsContractCall } = await import("../rpcCalls.js");
|
|
157
161
|
const authenticators = await syncAuthenticatorsContractCall(this.context.nearClient, this.context.configs.contractId, nearAccountId);
|
|
158
|
-
const numbers = authenticators.map((
|
|
162
|
+
const numbers = authenticators.map(({ authenticator }) => authenticator.deviceNumber).filter((n) => typeof n === "number" && Number.isFinite(n));
|
|
159
163
|
const max = numbers.length > 0 ? Math.max(...numbers) : 0;
|
|
160
164
|
return max + 1;
|
|
161
165
|
} catch {
|
|
@@ -207,7 +211,7 @@ var init_emailRecovery = __esm({ "src/core/TatchiPasskey/emailRecovery.ts": (()
|
|
|
207
211
|
message: "New device key created; please send the recovery email from your registered address.",
|
|
208
212
|
data: {
|
|
209
213
|
accountId: rec.accountId,
|
|
210
|
-
recoveryEmail: rec.recoveryEmail,
|
|
214
|
+
...rec.recoveryEmail ? { recoveryEmail: rec.recoveryEmail } : {},
|
|
211
215
|
nearPublicKey: rec.nearPublicKey,
|
|
212
216
|
requestId: rec.requestId,
|
|
213
217
|
mailtoUrl
|
|
@@ -223,44 +227,57 @@ var init_emailRecovery = __esm({ "src/core/TatchiPasskey/emailRecovery.ts": (()
|
|
|
223
227
|
data
|
|
224
228
|
});
|
|
225
229
|
}
|
|
226
|
-
async
|
|
227
|
-
const { dkimVerifierAccountId, verificationViewMethod } = this.getConfig();
|
|
228
|
-
if (!dkimVerifierAccountId) return null;
|
|
230
|
+
async checkViaEmailRecovererAttempt(rec) {
|
|
229
231
|
try {
|
|
230
|
-
const
|
|
231
|
-
|
|
232
|
-
if (!result) return {
|
|
232
|
+
const attempt = await getEmailRecoveryAttempt(this.context.nearClient, rec.accountId, rec.requestId);
|
|
233
|
+
if (!attempt) return {
|
|
233
234
|
completed: false,
|
|
234
|
-
success: false
|
|
235
|
+
success: false,
|
|
236
|
+
missing: true
|
|
235
237
|
};
|
|
236
|
-
if (
|
|
237
|
-
|
|
238
|
-
|
|
238
|
+
if (attempt.request_id && attempt.request_id !== rec.requestId) return {
|
|
239
|
+
completed: true,
|
|
240
|
+
success: false,
|
|
241
|
+
errorMessage: "Email recovery attempt request_id does not match requested requestId."
|
|
242
|
+
};
|
|
243
|
+
if (attempt.new_public_key && attempt.new_public_key !== rec.nearPublicKey) {
|
|
244
|
+
const expected = ensureEd25519Prefix(rec.nearPublicKey);
|
|
245
|
+
const actual = ensureEd25519Prefix(attempt.new_public_key);
|
|
246
|
+
if (actual === expected) {} else return {
|
|
239
247
|
completed: true,
|
|
240
248
|
success: false,
|
|
241
|
-
errorMessage
|
|
242
|
-
transactionHash: result.transaction_hash
|
|
249
|
+
errorMessage: `Email recovery new_public_key mismatch for request ${rec.requestId}. Expected ${expected}; got ${actual}. This usually means the recovery email you sent was generated for a different device/attempt.`
|
|
243
250
|
};
|
|
244
251
|
}
|
|
245
|
-
|
|
252
|
+
const normalized = attempt.status.toLowerCase();
|
|
253
|
+
if (normalized === "complete" || normalized === "completed") return {
|
|
246
254
|
completed: true,
|
|
247
|
-
success:
|
|
248
|
-
errorMessage: "Email verification account_id does not match requested account.",
|
|
249
|
-
transactionHash: result.transaction_hash
|
|
255
|
+
success: true
|
|
250
256
|
};
|
|
251
|
-
if (
|
|
257
|
+
if (normalized.includes("failed")) return {
|
|
252
258
|
completed: true,
|
|
253
259
|
success: false,
|
|
254
|
-
errorMessage:
|
|
255
|
-
transactionHash: result.transaction_hash
|
|
260
|
+
errorMessage: attempt.error || `Email recovery failed (${attempt.status || "unknown status"})`
|
|
256
261
|
};
|
|
257
262
|
return {
|
|
258
|
-
completed:
|
|
259
|
-
success:
|
|
260
|
-
transactionHash: result.transaction_hash
|
|
263
|
+
completed: false,
|
|
264
|
+
success: false
|
|
261
265
|
};
|
|
262
266
|
} catch (err) {
|
|
263
|
-
console.warn("[EmailRecoveryFlow]
|
|
267
|
+
console.warn("[EmailRecoveryFlow] get_recovery_attempt view failed; will retry", err);
|
|
268
|
+
return null;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
async isRecoveryAccessKeyPresent(rec) {
|
|
272
|
+
try {
|
|
273
|
+
await this.context.nearClient.viewAccessKey(rec.accountId, rec.nearPublicKey);
|
|
274
|
+
return true;
|
|
275
|
+
} catch (err) {
|
|
276
|
+
const kind = typeof err?.kind === "string" ? String(err.kind) : "";
|
|
277
|
+
const short = typeof err?.short === "string" ? String(err.short) : "";
|
|
278
|
+
const msg = typeof err?.message === "string" ? String(err.message) : "";
|
|
279
|
+
if (/AccessKeyDoesNotExist/i.test(kind) || /AccessKeyDoesNotExist/i.test(short) || /access key does not exist/i.test(msg) || /access key .*does not exist/i.test(msg)) return false;
|
|
280
|
+
console.warn("[EmailRecoveryFlow] view_access_key failed while checking recovery key; will retry", err);
|
|
264
281
|
return null;
|
|
265
282
|
}
|
|
266
283
|
}
|
|
@@ -369,7 +386,7 @@ var init_emailRecovery = __esm({ "src/core/TatchiPasskey/emailRecovery.ts": (()
|
|
|
369
386
|
});
|
|
370
387
|
const nearAccountId = await this.assertValidAccountIdOrFail(1, accountId);
|
|
371
388
|
await this.assertSufficientBalance(nearAccountId);
|
|
372
|
-
const canonicalEmail =
|
|
389
|
+
const canonicalEmail = this.getCanonicalRecoveryEmail(recoveryEmail);
|
|
373
390
|
const deviceNumber = await this.getNextDeviceNumberFromContract(nearAccountId);
|
|
374
391
|
this.phase = EmailRecoveryPhase.STEP_2_TOUCH_ID_REGISTRATION;
|
|
375
392
|
this.emit({
|
|
@@ -403,7 +420,7 @@ var init_emailRecovery = __esm({ "src/core/TatchiPasskey/emailRecovery.ts": (()
|
|
|
403
420
|
nearPublicKey: rec.nearPublicKey
|
|
404
421
|
};
|
|
405
422
|
} catch (e) {
|
|
406
|
-
const err = this.emitError(2, e
|
|
423
|
+
const err = this.emitError(2, errorMessage(e) || "Email recovery TouchID/derivation failed");
|
|
407
424
|
await this.options?.afterCall?.(false);
|
|
408
425
|
throw err;
|
|
409
426
|
}
|
|
@@ -500,29 +517,39 @@ var init_emailRecovery = __esm({ "src/core/TatchiPasskey/emailRecovery.ts": (()
|
|
|
500
517
|
await this.options?.afterCall?.(true, void 0);
|
|
501
518
|
}
|
|
502
519
|
async pollUntilAddKey(rec) {
|
|
503
|
-
const { pollingIntervalMs, maxPollingDurationMs
|
|
504
|
-
if (!dkimVerifierAccountId) {
|
|
505
|
-
const err$1 = this.emitError(4, "Email recovery verification contract (dkimVerifierAccountId) is not configured");
|
|
506
|
-
await this.options?.afterCall?.(false);
|
|
507
|
-
throw err$1;
|
|
508
|
-
}
|
|
520
|
+
const { pollingIntervalMs, maxPollingDurationMs } = this.getConfig();
|
|
509
521
|
this.phase = EmailRecoveryPhase.STEP_4_POLLING_VERIFICATION_RESULT;
|
|
510
522
|
this.pollingStartedAt = Date.now();
|
|
523
|
+
let sawAttempt = false;
|
|
511
524
|
const pollResult = await this.pollUntil({
|
|
512
525
|
intervalMs: pollingIntervalMs,
|
|
513
526
|
timeoutMs: maxPollingDurationMs,
|
|
514
527
|
isCancelled: () => this.cancelled,
|
|
515
528
|
tick: async ({ elapsedMs, pollCount }) => {
|
|
516
|
-
const verification = await this.
|
|
517
|
-
|
|
518
|
-
|
|
529
|
+
const verification = await this.checkViaEmailRecovererAttempt(rec);
|
|
530
|
+
if (verification && !verification.missing) sawAttempt = true;
|
|
531
|
+
let completed = verification?.completed === true;
|
|
532
|
+
let success = verification?.success === true;
|
|
533
|
+
let errorMessage$1 = verification?.errorMessage;
|
|
534
|
+
let transactionHash;
|
|
535
|
+
if (verification?.missing) {
|
|
536
|
+
const hasKey = await this.isRecoveryAccessKeyPresent(rec);
|
|
537
|
+
if (hasKey === true) {
|
|
538
|
+
completed = true;
|
|
539
|
+
success = true;
|
|
540
|
+
} else if (hasKey === false && sawAttempt) {
|
|
541
|
+
completed = true;
|
|
542
|
+
success = false;
|
|
543
|
+
errorMessage$1 = "Email recovery attempt was cleared on-chain before completion. Please resend the recovery email or restart the flow.";
|
|
544
|
+
} else if (hasKey === null) return { done: false };
|
|
545
|
+
}
|
|
519
546
|
this.emit({
|
|
520
547
|
step: 4,
|
|
521
548
|
phase: EmailRecoveryPhase.STEP_4_POLLING_VERIFICATION_RESULT,
|
|
522
549
|
status: EmailRecoveryStatus.PROGRESS,
|
|
523
|
-
message: completed && success ? `Email
|
|
550
|
+
message: completed && success ? `Email recovery completed for request ${rec.requestId}; finalizing registration` : `Waiting for email recovery for request ${rec.requestId}`,
|
|
524
551
|
data: this.buildPollingEventData(rec, {
|
|
525
|
-
transactionHash
|
|
552
|
+
transactionHash,
|
|
526
553
|
elapsedMs,
|
|
527
554
|
pollCount
|
|
528
555
|
})
|
|
@@ -532,7 +559,7 @@ var init_emailRecovery = __esm({ "src/core/TatchiPasskey/emailRecovery.ts": (()
|
|
|
532
559
|
done: true,
|
|
533
560
|
value: {
|
|
534
561
|
outcome: "failed",
|
|
535
|
-
errorMessage:
|
|
562
|
+
errorMessage: errorMessage$1 || "Email recovery failed"
|
|
536
563
|
}
|
|
537
564
|
};
|
|
538
565
|
return {
|
|
@@ -573,7 +600,7 @@ var init_emailRecovery = __esm({ "src/core/TatchiPasskey/emailRecovery.ts": (()
|
|
|
573
600
|
accountId
|
|
574
601
|
};
|
|
575
602
|
}
|
|
576
|
-
async
|
|
603
|
+
async signNewDevice2RegistrationTx(rec, accountId) {
|
|
577
604
|
const vrfChallenge = rec.vrfChallenge;
|
|
578
605
|
if (!vrfChallenge) return this.fail(5, "Missing VRF challenge for email recovery registration");
|
|
579
606
|
const registrationResult = await this.context.webAuthnManager.signDevice2RegistrationWithStoredKey({
|
|
@@ -587,28 +614,56 @@ var init_emailRecovery = __esm({ "src/core/TatchiPasskey/emailRecovery.ts": (()
|
|
|
587
614
|
return registrationResult.signedTransaction;
|
|
588
615
|
}
|
|
589
616
|
async broadcastRegistrationTxAndWaitFinal(rec, signedTx) {
|
|
617
|
+
let txResult;
|
|
590
618
|
try {
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
619
|
+
txResult = await this.context.nearClient.sendTransaction(signedTx, DEFAULT_WAIT_STATUS.linkDeviceRegistration);
|
|
620
|
+
} catch (err) {
|
|
621
|
+
const msg = errorMessage(err) || "Failed to broadcast email recovery registration transaction (insufficient funds or RPC error)";
|
|
622
|
+
throw new Error(msg);
|
|
623
|
+
}
|
|
624
|
+
const txHash = this.getTxHash(txResult);
|
|
625
|
+
const linkDeviceResult = parseLinkDeviceRegisterUserResponse(txResult);
|
|
626
|
+
if (linkDeviceResult?.verified === false) {
|
|
627
|
+
const logs = this.extractNearExecutionLogs(txResult);
|
|
628
|
+
const isStaleChallenge = logs.some((log) => /StaleChallenge|freshness validation failed/i.test(log));
|
|
629
|
+
const txHint = txHash ? ` (tx: ${txHash})` : "";
|
|
630
|
+
const code = isStaleChallenge ? EmailRecoveryErrorCode.VRF_CHALLENGE_EXPIRED : EmailRecoveryErrorCode.REGISTRATION_NOT_VERIFIED;
|
|
631
|
+
const message = isStaleChallenge ? `Timed out finalizing registration (VRF challenge expired). Please restart email recovery and try again${txHint}.` : `Registration did not verify on-chain. Please try again${txHint}.`;
|
|
632
|
+
throw new EmailRecoveryError(message, code, {
|
|
633
|
+
accountId: rec.accountId,
|
|
634
|
+
nearPublicKey: rec.nearPublicKey,
|
|
635
|
+
transactionHash: txHash,
|
|
636
|
+
logs,
|
|
637
|
+
result: linkDeviceResult
|
|
638
|
+
});
|
|
639
|
+
}
|
|
640
|
+
if (txHash) this.emit({
|
|
641
|
+
step: 5,
|
|
642
|
+
phase: EmailRecoveryPhase.STEP_5_FINALIZING_REGISTRATION,
|
|
643
|
+
status: EmailRecoveryStatus.PROGRESS,
|
|
644
|
+
message: "Registration transaction confirmed",
|
|
645
|
+
data: {
|
|
646
|
+
accountId: rec.accountId,
|
|
647
|
+
nearPublicKey: rec.nearPublicKey,
|
|
648
|
+
transactionHash: txHash
|
|
649
|
+
}
|
|
650
|
+
});
|
|
651
|
+
return txHash;
|
|
652
|
+
}
|
|
653
|
+
getTxHash(outcome) {
|
|
654
|
+
const txUnknown = outcome.transaction;
|
|
655
|
+
if (txUnknown && typeof txUnknown === "object") {
|
|
656
|
+
const hash = txUnknown.hash;
|
|
657
|
+
if (typeof hash === "string" && hash.length > 0) return hash;
|
|
610
658
|
}
|
|
611
|
-
|
|
659
|
+
const fallback = outcome.transaction_hash;
|
|
660
|
+
return typeof fallback === "string" && fallback.length > 0 ? fallback : void 0;
|
|
661
|
+
}
|
|
662
|
+
extractNearExecutionLogs(outcome) {
|
|
663
|
+
const logs = [];
|
|
664
|
+
for (const entry of outcome.transaction_outcome.outcome.logs) logs.push(String(entry));
|
|
665
|
+
for (const receipt of outcome.receipts_outcome) for (const entry of receipt.outcome.logs) logs.push(String(entry));
|
|
666
|
+
return logs;
|
|
612
667
|
}
|
|
613
668
|
mapAuthenticatorsFromContract(authenticators) {
|
|
614
669
|
return authenticators.map(({ authenticator }) => ({
|
|
@@ -644,7 +699,7 @@ var init_emailRecovery = __esm({ "src/core/TatchiPasskey/emailRecovery.ts": (()
|
|
|
644
699
|
}
|
|
645
700
|
async updateNonceBestEffort(nonceManager, signedTx) {
|
|
646
701
|
try {
|
|
647
|
-
const txNonce = signedTx.transaction
|
|
702
|
+
const txNonce = signedTx.transaction.nonce;
|
|
648
703
|
if (txNonce != null) await nonceManager.updateNonceFromBlockchain(this.context.nearClient, String(txNonce));
|
|
649
704
|
} catch {}
|
|
650
705
|
}
|
|
@@ -760,7 +815,7 @@ var init_emailRecovery = __esm({ "src/core/TatchiPasskey/emailRecovery.ts": (()
|
|
|
760
815
|
} catch (err) {
|
|
761
816
|
return {
|
|
762
817
|
success: false,
|
|
763
|
-
reason: err
|
|
818
|
+
reason: errorMessage(err) || String(err)
|
|
764
819
|
};
|
|
765
820
|
}
|
|
766
821
|
}
|
|
@@ -788,7 +843,7 @@ var init_emailRecovery = __esm({ "src/core/TatchiPasskey/emailRecovery.ts": (()
|
|
|
788
843
|
});
|
|
789
844
|
try {
|
|
790
845
|
const { nonceManager, accountId } = this.initializeNonceManager(rec);
|
|
791
|
-
const signedTx = await this.
|
|
846
|
+
const signedTx = await this.signNewDevice2RegistrationTx(rec, accountId);
|
|
792
847
|
const txHash = await this.broadcastRegistrationTxAndWaitFinal(rec, signedTx);
|
|
793
848
|
if (!txHash) console.warn("[EmailRecoveryFlow] Registration transaction confirmed without hash; continuing local persistence");
|
|
794
849
|
await this.persistRecoveredUserData(rec, accountId);
|
|
@@ -816,7 +871,10 @@ var init_emailRecovery = __esm({ "src/core/TatchiPasskey/emailRecovery.ts": (()
|
|
|
816
871
|
}
|
|
817
872
|
});
|
|
818
873
|
} catch (e) {
|
|
819
|
-
|
|
874
|
+
rec.status = "error";
|
|
875
|
+
await this.savePending(rec).catch(() => {});
|
|
876
|
+
const original = e instanceof Error ? e : new Error(errorMessage(e) || "Email recovery finalization failed");
|
|
877
|
+
const err = this.emitError(5, original);
|
|
820
878
|
await this.options?.afterCall?.(false);
|
|
821
879
|
throw err;
|
|
822
880
|
}
|
|
@@ -838,7 +896,7 @@ var init_emailRecovery = __esm({ "src/core/TatchiPasskey/emailRecovery.ts": (()
|
|
|
838
896
|
};
|
|
839
897
|
return this.handleAutoLoginFailure(touchIdResult.reason || "Auto-login failed");
|
|
840
898
|
} catch (err) {
|
|
841
|
-
return this.handleAutoLoginFailure(err
|
|
899
|
+
return this.handleAutoLoginFailure(errorMessage(err) || String(err), err);
|
|
842
900
|
}
|
|
843
901
|
}
|
|
844
902
|
};
|