@tatchi-xyz/sdk 0.16.0 → 0.18.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.
Files changed (501) hide show
  1. package/dist/cjs/core/EmailRecovery/emailRecoveryPendingStore.js +69 -0
  2. package/dist/cjs/core/EmailRecovery/emailRecoveryPendingStore.js.map +1 -0
  3. package/dist/cjs/core/EmailRecovery/index.js +32 -13
  4. package/dist/cjs/core/EmailRecovery/index.js.map +1 -1
  5. package/dist/cjs/core/IndexedDBManager/passkeyClientDB.js +35 -36
  6. package/dist/cjs/core/IndexedDBManager/passkeyClientDB.js.map +1 -1
  7. package/dist/cjs/core/NearClient.js +2 -1
  8. package/dist/cjs/core/NearClient.js.map +1 -1
  9. package/dist/cjs/core/TatchiPasskey/emailRecovery.js +557 -377
  10. package/dist/cjs/core/TatchiPasskey/emailRecovery.js.map +1 -1
  11. package/dist/cjs/core/TatchiPasskey/faucets/createAccountRelayServer.js +1 -0
  12. package/dist/cjs/core/TatchiPasskey/faucets/createAccountRelayServer.js.map +1 -1
  13. package/dist/cjs/core/TatchiPasskey/index.js +26 -0
  14. package/dist/cjs/core/TatchiPasskey/index.js.map +1 -1
  15. package/dist/cjs/core/TatchiPasskey/linkDevice.js +2 -0
  16. package/dist/cjs/core/TatchiPasskey/linkDevice.js.map +1 -1
  17. package/dist/cjs/core/TatchiPasskey/login.js +15 -4
  18. package/dist/cjs/core/TatchiPasskey/login.js.map +1 -1
  19. package/dist/cjs/core/TatchiPasskey/recoverAccount.js +1 -0
  20. package/dist/cjs/core/TatchiPasskey/recoverAccount.js.map +1 -1
  21. package/dist/cjs/core/TatchiPasskey/relay.js +23 -1
  22. package/dist/cjs/core/TatchiPasskey/relay.js.map +1 -1
  23. package/dist/cjs/core/TatchiPasskey/scanDevice.js +1 -0
  24. package/dist/cjs/core/TatchiPasskey/scanDevice.js.map +1 -1
  25. package/dist/cjs/core/WalletIframe/client/IframeTransport.js +3 -0
  26. package/dist/cjs/core/WalletIframe/client/IframeTransport.js.map +1 -1
  27. package/dist/cjs/core/WalletIframe/client/router.js +15 -2
  28. package/dist/cjs/core/WalletIframe/client/router.js.map +1 -1
  29. package/dist/cjs/core/WebAuthnManager/LitComponents/IframeTxConfirmer/viewer-drawer.js +1 -1
  30. package/dist/cjs/core/WebAuthnManager/LitComponents/IframeTxConfirmer/viewer-drawer.js.map +1 -1
  31. package/dist/cjs/core/WebAuthnManager/LitComponents/IframeTxConfirmer/viewer-modal.js +52 -52
  32. package/dist/cjs/core/WebAuthnManager/LitComponents/IframeTxConfirmer/viewer-modal.js.map +1 -1
  33. package/dist/cjs/core/WebAuthnManager/SignerWorkerManager/getDeviceNumber.js +10 -1
  34. package/dist/cjs/core/WebAuthnManager/SignerWorkerManager/getDeviceNumber.js.map +1 -1
  35. package/dist/cjs/core/WebAuthnManager/SignerWorkerManager/handlers/checkCanRegisterUser.js +1 -0
  36. package/dist/cjs/core/WebAuthnManager/SignerWorkerManager/handlers/checkCanRegisterUser.js.map +1 -1
  37. package/dist/cjs/core/WebAuthnManager/SignerWorkerManager/handlers/decryptPrivateKeyWithPrf.js +1 -0
  38. package/dist/cjs/core/WebAuthnManager/SignerWorkerManager/handlers/decryptPrivateKeyWithPrf.js.map +1 -1
  39. package/dist/cjs/core/WebAuthnManager/SignerWorkerManager/handlers/deriveNearKeypairAndEncryptFromSerialized.js +1 -0
  40. package/dist/cjs/core/WebAuthnManager/SignerWorkerManager/handlers/deriveNearKeypairAndEncryptFromSerialized.js.map +1 -1
  41. package/dist/cjs/core/WebAuthnManager/SignerWorkerManager/handlers/exportNearKeypairUi.js +1 -0
  42. package/dist/cjs/core/WebAuthnManager/SignerWorkerManager/handlers/exportNearKeypairUi.js.map +1 -1
  43. package/dist/cjs/core/WebAuthnManager/SignerWorkerManager/handlers/registerDevice2WithDerivedKey.js +2 -1
  44. package/dist/cjs/core/WebAuthnManager/SignerWorkerManager/handlers/registerDevice2WithDerivedKey.js.map +1 -1
  45. package/dist/cjs/core/WebAuthnManager/SignerWorkerManager/handlers/signDelegateAction.js +1 -0
  46. package/dist/cjs/core/WebAuthnManager/SignerWorkerManager/handlers/signDelegateAction.js.map +1 -1
  47. package/dist/cjs/core/WebAuthnManager/SignerWorkerManager/handlers/signNep413Message.js +1 -0
  48. package/dist/cjs/core/WebAuthnManager/SignerWorkerManager/handlers/signNep413Message.js.map +1 -1
  49. package/dist/cjs/core/WebAuthnManager/SignerWorkerManager/handlers/signTransactionsWithActions.js +1 -0
  50. package/dist/cjs/core/WebAuthnManager/SignerWorkerManager/handlers/signTransactionsWithActions.js.map +1 -1
  51. package/dist/cjs/core/WebAuthnManager/SignerWorkerManager/handlers/validation.js +2 -0
  52. package/dist/cjs/core/WebAuthnManager/SignerWorkerManager/handlers/validation.js.map +1 -1
  53. package/dist/cjs/core/WebAuthnManager/SignerWorkerManager/index.js +1 -0
  54. package/dist/cjs/core/WebAuthnManager/SignerWorkerManager/index.js.map +1 -1
  55. package/dist/cjs/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/adapters/vrf.js +1 -0
  56. package/dist/cjs/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/adapters/vrf.js.map +1 -1
  57. package/dist/cjs/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/adapters/webauthn.js +6 -0
  58. package/dist/cjs/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/adapters/webauthn.js.map +1 -1
  59. package/dist/cjs/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/determineConfirmationConfig.js +2 -1
  60. package/dist/cjs/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/determineConfirmationConfig.js.map +1 -1
  61. package/dist/cjs/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/localOnly.js +1 -0
  62. package/dist/cjs/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/localOnly.js.map +1 -1
  63. package/dist/cjs/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/registration.js +1 -0
  64. package/dist/cjs/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/registration.js.map +1 -1
  65. package/dist/cjs/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/transactions.js +4 -15
  66. package/dist/cjs/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/transactions.js.map +1 -1
  67. package/dist/cjs/core/WebAuthnManager/VrfWorkerManager/handlers/deriveVrfKeypairFromPrf.js +1 -0
  68. package/dist/cjs/core/WebAuthnManager/VrfWorkerManager/handlers/deriveVrfKeypairFromPrf.js.map +1 -1
  69. package/dist/cjs/core/WebAuthnManager/VrfWorkerManager/handlers/generateVrfChallenge.js +1 -0
  70. package/dist/cjs/core/WebAuthnManager/VrfWorkerManager/handlers/generateVrfChallenge.js.map +1 -1
  71. package/dist/cjs/core/WebAuthnManager/VrfWorkerManager/handlers/generateVrfKeypairBootstrap.js +1 -0
  72. package/dist/cjs/core/WebAuthnManager/VrfWorkerManager/handlers/generateVrfKeypairBootstrap.js.map +1 -1
  73. package/dist/cjs/core/WebAuthnManager/WebAuthnFallbacks/index.js +17 -0
  74. package/dist/cjs/core/WebAuthnManager/WebAuthnFallbacks/index.js.map +1 -0
  75. package/dist/cjs/core/WebAuthnManager/WebAuthnFallbacks/safari-fallbacks.js +64 -54
  76. package/dist/cjs/core/WebAuthnManager/WebAuthnFallbacks/safari-fallbacks.js.map +1 -1
  77. package/dist/cjs/core/WebAuthnManager/credentialsHelpers.js +12 -2
  78. package/dist/cjs/core/WebAuthnManager/credentialsHelpers.js.map +1 -1
  79. package/dist/cjs/core/WebAuthnManager/index.js +6 -1
  80. package/dist/cjs/core/WebAuthnManager/index.js.map +1 -1
  81. package/dist/cjs/core/WebAuthnManager/touchIdPrompt.js +209 -201
  82. package/dist/cjs/core/WebAuthnManager/touchIdPrompt.js.map +1 -1
  83. package/dist/cjs/core/WebAuthnManager/userHandle.js +2 -1
  84. package/dist/cjs/core/WebAuthnManager/userHandle.js.map +1 -1
  85. package/dist/cjs/core/defaultConfigs.js +1 -1
  86. package/dist/cjs/core/defaultConfigs.js.map +1 -1
  87. package/dist/cjs/core/rpcCalls.js +8 -0
  88. package/dist/cjs/core/rpcCalls.js.map +1 -1
  89. package/dist/cjs/core/types/vrf-worker.js +10 -1
  90. package/dist/cjs/core/types/vrf-worker.js.map +1 -1
  91. package/dist/cjs/index.js +6 -2
  92. package/dist/cjs/index.js.map +1 -1
  93. package/dist/cjs/react/components/AccountMenuButton/{LinkedDevicesModal-STvIsylA.css → LinkedDevicesModal-CSSowiHP.css} +1 -1
  94. package/dist/{esm/react/components/AccountMenuButton/LinkedDevicesModal-STvIsylA.css.map → cjs/react/components/AccountMenuButton/LinkedDevicesModal-CSSowiHP.css.map} +1 -1
  95. package/dist/cjs/react/components/AccountMenuButton/{ProfileDropdown-iARgUwK1.css → ProfileDropdown-CEPMZ1gY.css} +1 -1
  96. package/dist/{esm/react/components/AccountMenuButton/ProfileDropdown-iARgUwK1.css.map → cjs/react/components/AccountMenuButton/ProfileDropdown-CEPMZ1gY.css.map} +1 -1
  97. package/dist/cjs/react/components/AccountMenuButton/{Web3AuthProfileButton-Db3NeoAC.css → Web3AuthProfileButton-DopOg7Xc.css} +1 -1
  98. package/dist/cjs/react/components/AccountMenuButton/{Web3AuthProfileButton-Db3NeoAC.css.map → Web3AuthProfileButton-DopOg7Xc.css.map} +1 -1
  99. package/dist/cjs/react/components/AccountMenuButton/icons/{TouchIcon-BXM5NR4A.css → TouchIcon-BQWentvJ.css} +1 -1
  100. package/dist/cjs/react/components/AccountMenuButton/icons/{TouchIcon-BXM5NR4A.css.map → TouchIcon-BQWentvJ.css.map} +1 -1
  101. package/dist/cjs/react/components/PasskeyAuthMenu/{PasskeyAuthMenu-De1qTSmU.css → PasskeyAuthMenu-DwrzWMYx.css} +14 -1
  102. package/dist/cjs/react/components/PasskeyAuthMenu/{PasskeyAuthMenu-De1qTSmU.css.map → PasskeyAuthMenu-DwrzWMYx.css.map} +1 -1
  103. package/dist/cjs/react/components/PasskeyAuthMenu/ui/EmailRecoverySlide.js +122 -53
  104. package/dist/cjs/react/components/PasskeyAuthMenu/ui/EmailRecoverySlide.js.map +1 -1
  105. package/dist/cjs/react/components/{ShowQRCode-DCnR__fx.css → ShowQRCode-CCN4h6Uv.css} +1 -1
  106. package/dist/cjs/react/components/{ShowQRCode-DCnR__fx.css.map → ShowQRCode-CCN4h6Uv.css.map} +1 -1
  107. package/dist/cjs/react/deviceDetection.js +75 -92
  108. package/dist/cjs/react/deviceDetection.js.map +1 -1
  109. package/dist/cjs/react/hooks/useQRCamera.js +1 -0
  110. package/dist/cjs/react/hooks/useQRCamera.js.map +1 -1
  111. package/dist/cjs/react/sdk/src/core/EmailRecovery/emailRecoveryPendingStore.js +69 -0
  112. package/dist/cjs/react/sdk/src/core/EmailRecovery/emailRecoveryPendingStore.js.map +1 -0
  113. package/dist/cjs/react/sdk/src/core/EmailRecovery/index.js +32 -13
  114. package/dist/cjs/react/sdk/src/core/EmailRecovery/index.js.map +1 -1
  115. package/dist/cjs/react/sdk/src/core/IndexedDBManager/passkeyClientDB.js +35 -36
  116. package/dist/cjs/react/sdk/src/core/IndexedDBManager/passkeyClientDB.js.map +1 -1
  117. package/dist/cjs/react/sdk/src/core/NearClient.js +2 -1
  118. package/dist/cjs/react/sdk/src/core/NearClient.js.map +1 -1
  119. package/dist/cjs/react/sdk/src/core/TatchiPasskey/emailRecovery.js +557 -377
  120. package/dist/cjs/react/sdk/src/core/TatchiPasskey/emailRecovery.js.map +1 -1
  121. package/dist/cjs/react/sdk/src/core/TatchiPasskey/faucets/createAccountRelayServer.js +1 -0
  122. package/dist/cjs/react/sdk/src/core/TatchiPasskey/faucets/createAccountRelayServer.js.map +1 -1
  123. package/dist/cjs/react/sdk/src/core/TatchiPasskey/index.js +26 -0
  124. package/dist/cjs/react/sdk/src/core/TatchiPasskey/index.js.map +1 -1
  125. package/dist/cjs/react/sdk/src/core/TatchiPasskey/linkDevice.js +2 -0
  126. package/dist/cjs/react/sdk/src/core/TatchiPasskey/linkDevice.js.map +1 -1
  127. package/dist/cjs/react/sdk/src/core/TatchiPasskey/login.js +15 -4
  128. package/dist/cjs/react/sdk/src/core/TatchiPasskey/login.js.map +1 -1
  129. package/dist/cjs/react/sdk/src/core/TatchiPasskey/recoverAccount.js +1 -0
  130. package/dist/cjs/react/sdk/src/core/TatchiPasskey/recoverAccount.js.map +1 -1
  131. package/dist/cjs/react/sdk/src/core/TatchiPasskey/relay.js +23 -1
  132. package/dist/cjs/react/sdk/src/core/TatchiPasskey/relay.js.map +1 -1
  133. package/dist/cjs/react/sdk/src/core/TatchiPasskey/scanDevice.js +1 -0
  134. package/dist/cjs/react/sdk/src/core/TatchiPasskey/scanDevice.js.map +1 -1
  135. package/dist/cjs/react/sdk/src/core/WalletIframe/client/IframeTransport.js +3 -0
  136. package/dist/cjs/react/sdk/src/core/WalletIframe/client/IframeTransport.js.map +1 -1
  137. package/dist/cjs/react/sdk/src/core/WalletIframe/client/router.js +15 -2
  138. package/dist/cjs/react/sdk/src/core/WalletIframe/client/router.js.map +1 -1
  139. package/dist/cjs/react/sdk/src/core/WebAuthnManager/LitComponents/IframeTxConfirmer/viewer-drawer.js +1 -1
  140. package/dist/cjs/react/sdk/src/core/WebAuthnManager/LitComponents/IframeTxConfirmer/viewer-drawer.js.map +1 -1
  141. package/dist/cjs/react/sdk/src/core/WebAuthnManager/LitComponents/IframeTxConfirmer/viewer-modal.js +52 -52
  142. package/dist/cjs/react/sdk/src/core/WebAuthnManager/LitComponents/IframeTxConfirmer/viewer-modal.js.map +1 -1
  143. package/dist/cjs/react/sdk/src/core/WebAuthnManager/SignerWorkerManager/getDeviceNumber.js +10 -1
  144. package/dist/cjs/react/sdk/src/core/WebAuthnManager/SignerWorkerManager/getDeviceNumber.js.map +1 -1
  145. package/dist/cjs/react/sdk/src/core/WebAuthnManager/SignerWorkerManager/handlers/checkCanRegisterUser.js +1 -0
  146. package/dist/cjs/react/sdk/src/core/WebAuthnManager/SignerWorkerManager/handlers/checkCanRegisterUser.js.map +1 -1
  147. package/dist/cjs/react/sdk/src/core/WebAuthnManager/SignerWorkerManager/handlers/decryptPrivateKeyWithPrf.js +1 -0
  148. package/dist/cjs/react/sdk/src/core/WebAuthnManager/SignerWorkerManager/handlers/decryptPrivateKeyWithPrf.js.map +1 -1
  149. package/dist/cjs/react/sdk/src/core/WebAuthnManager/SignerWorkerManager/handlers/deriveNearKeypairAndEncryptFromSerialized.js +1 -0
  150. package/dist/cjs/react/sdk/src/core/WebAuthnManager/SignerWorkerManager/handlers/deriveNearKeypairAndEncryptFromSerialized.js.map +1 -1
  151. package/dist/cjs/react/sdk/src/core/WebAuthnManager/SignerWorkerManager/handlers/exportNearKeypairUi.js +1 -0
  152. package/dist/cjs/react/sdk/src/core/WebAuthnManager/SignerWorkerManager/handlers/exportNearKeypairUi.js.map +1 -1
  153. package/dist/cjs/react/sdk/src/core/WebAuthnManager/SignerWorkerManager/handlers/registerDevice2WithDerivedKey.js +2 -1
  154. package/dist/cjs/react/sdk/src/core/WebAuthnManager/SignerWorkerManager/handlers/registerDevice2WithDerivedKey.js.map +1 -1
  155. package/dist/cjs/react/sdk/src/core/WebAuthnManager/SignerWorkerManager/handlers/signDelegateAction.js +1 -0
  156. package/dist/cjs/react/sdk/src/core/WebAuthnManager/SignerWorkerManager/handlers/signDelegateAction.js.map +1 -1
  157. package/dist/cjs/react/sdk/src/core/WebAuthnManager/SignerWorkerManager/handlers/signNep413Message.js +1 -0
  158. package/dist/cjs/react/sdk/src/core/WebAuthnManager/SignerWorkerManager/handlers/signNep413Message.js.map +1 -1
  159. package/dist/cjs/react/sdk/src/core/WebAuthnManager/SignerWorkerManager/handlers/signTransactionsWithActions.js +1 -0
  160. package/dist/cjs/react/sdk/src/core/WebAuthnManager/SignerWorkerManager/handlers/signTransactionsWithActions.js.map +1 -1
  161. package/dist/cjs/react/sdk/src/core/WebAuthnManager/SignerWorkerManager/handlers/validation.js +2 -0
  162. package/dist/cjs/react/sdk/src/core/WebAuthnManager/SignerWorkerManager/handlers/validation.js.map +1 -1
  163. package/dist/cjs/react/sdk/src/core/WebAuthnManager/SignerWorkerManager/index.js +1 -0
  164. package/dist/cjs/react/sdk/src/core/WebAuthnManager/SignerWorkerManager/index.js.map +1 -1
  165. package/dist/cjs/react/sdk/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/adapters/vrf.js +1 -0
  166. package/dist/cjs/react/sdk/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/adapters/vrf.js.map +1 -1
  167. package/dist/cjs/react/sdk/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/adapters/webauthn.js +6 -0
  168. package/dist/cjs/react/sdk/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/adapters/webauthn.js.map +1 -1
  169. package/dist/cjs/react/sdk/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/determineConfirmationConfig.js +2 -1
  170. package/dist/cjs/react/sdk/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/determineConfirmationConfig.js.map +1 -1
  171. package/dist/cjs/react/sdk/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/localOnly.js +1 -0
  172. package/dist/cjs/react/sdk/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/localOnly.js.map +1 -1
  173. package/dist/cjs/react/sdk/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/registration.js +1 -0
  174. package/dist/cjs/react/sdk/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/registration.js.map +1 -1
  175. package/dist/cjs/react/sdk/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/transactions.js +4 -15
  176. package/dist/cjs/react/sdk/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/transactions.js.map +1 -1
  177. package/dist/cjs/react/sdk/src/core/WebAuthnManager/VrfWorkerManager/handlers/deriveVrfKeypairFromPrf.js +1 -0
  178. package/dist/cjs/react/sdk/src/core/WebAuthnManager/VrfWorkerManager/handlers/deriveVrfKeypairFromPrf.js.map +1 -1
  179. package/dist/cjs/react/sdk/src/core/WebAuthnManager/VrfWorkerManager/handlers/generateVrfChallenge.js +1 -0
  180. package/dist/cjs/react/sdk/src/core/WebAuthnManager/VrfWorkerManager/handlers/generateVrfChallenge.js.map +1 -1
  181. package/dist/cjs/react/sdk/src/core/WebAuthnManager/VrfWorkerManager/handlers/generateVrfKeypairBootstrap.js +1 -0
  182. package/dist/cjs/react/sdk/src/core/WebAuthnManager/VrfWorkerManager/handlers/generateVrfKeypairBootstrap.js.map +1 -1
  183. package/dist/cjs/react/sdk/src/core/WebAuthnManager/WebAuthnFallbacks/index.js +17 -0
  184. package/dist/cjs/react/sdk/src/core/WebAuthnManager/WebAuthnFallbacks/index.js.map +1 -0
  185. package/dist/cjs/react/sdk/src/core/WebAuthnManager/WebAuthnFallbacks/safari-fallbacks.js +64 -54
  186. package/dist/cjs/react/sdk/src/core/WebAuthnManager/WebAuthnFallbacks/safari-fallbacks.js.map +1 -1
  187. package/dist/cjs/react/sdk/src/core/WebAuthnManager/credentialsHelpers.js +12 -2
  188. package/dist/cjs/react/sdk/src/core/WebAuthnManager/credentialsHelpers.js.map +1 -1
  189. package/dist/cjs/react/sdk/src/core/WebAuthnManager/index.js +6 -1
  190. package/dist/cjs/react/sdk/src/core/WebAuthnManager/index.js.map +1 -1
  191. package/dist/cjs/react/sdk/src/core/WebAuthnManager/touchIdPrompt.js +209 -201
  192. package/dist/cjs/react/sdk/src/core/WebAuthnManager/touchIdPrompt.js.map +1 -1
  193. package/dist/cjs/react/sdk/src/core/WebAuthnManager/userHandle.js +2 -1
  194. package/dist/cjs/react/sdk/src/core/WebAuthnManager/userHandle.js.map +1 -1
  195. package/dist/cjs/react/sdk/src/core/defaultConfigs.js +1 -1
  196. package/dist/cjs/react/sdk/src/core/defaultConfigs.js.map +1 -1
  197. package/dist/cjs/react/sdk/src/core/rpcCalls.js +8 -0
  198. package/dist/cjs/react/sdk/src/core/rpcCalls.js.map +1 -1
  199. package/dist/cjs/react/sdk/src/core/types/vrf-worker.js +10 -1
  200. package/dist/cjs/react/sdk/src/core/types/vrf-worker.js.map +1 -1
  201. package/dist/cjs/react/sdk/src/utils/index.js +13 -3
  202. package/dist/cjs/server/email-recovery/emailEncryptor.js +11 -0
  203. package/dist/cjs/server/email-recovery/emailEncryptor.js.map +1 -1
  204. package/dist/cjs/server/email-recovery/emailParsers.js +57 -0
  205. package/dist/cjs/server/email-recovery/emailParsers.js.map +1 -1
  206. package/dist/cjs/server/email-recovery/index.js +1 -1
  207. package/dist/cjs/server/email-recovery/index.js.map +1 -1
  208. package/dist/cjs/server/email-recovery/rpcCalls.js +14 -1
  209. package/dist/cjs/server/email-recovery/rpcCalls.js.map +1 -1
  210. package/dist/cjs/server/index.js +1 -0
  211. package/dist/cjs/server/router/cloudflare.js.map +1 -1
  212. package/dist/cjs/server/router/express.js.map +1 -1
  213. package/dist/cjs/server/sdk/src/core/defaultConfigs.js +1 -1
  214. package/dist/cjs/server/sdk/src/core/defaultConfigs.js.map +1 -1
  215. package/dist/cjs/utils/index.js +13 -3
  216. package/dist/esm/core/EmailRecovery/emailRecoveryPendingStore.js +63 -0
  217. package/dist/esm/core/EmailRecovery/emailRecoveryPendingStore.js.map +1 -0
  218. package/dist/esm/core/EmailRecovery/index.js +28 -14
  219. package/dist/esm/core/EmailRecovery/index.js.map +1 -1
  220. package/dist/esm/core/IndexedDBManager/passkeyClientDB.js +35 -36
  221. package/dist/esm/core/IndexedDBManager/passkeyClientDB.js.map +1 -1
  222. package/dist/esm/core/NearClient.js +2 -1
  223. package/dist/esm/core/NearClient.js.map +1 -1
  224. package/dist/esm/core/TatchiPasskey/emailRecovery.js +557 -377
  225. package/dist/esm/core/TatchiPasskey/emailRecovery.js.map +1 -1
  226. package/dist/esm/core/TatchiPasskey/faucets/createAccountRelayServer.js +2 -1
  227. package/dist/esm/core/TatchiPasskey/faucets/createAccountRelayServer.js.map +1 -1
  228. package/dist/esm/core/TatchiPasskey/index.js +28 -2
  229. package/dist/esm/core/TatchiPasskey/index.js.map +1 -1
  230. package/dist/esm/core/TatchiPasskey/linkDevice.js +4 -2
  231. package/dist/esm/core/TatchiPasskey/linkDevice.js.map +1 -1
  232. package/dist/esm/core/TatchiPasskey/login.js +13 -7
  233. package/dist/esm/core/TatchiPasskey/login.js.map +1 -1
  234. package/dist/esm/core/TatchiPasskey/recoverAccount.js +2 -1
  235. package/dist/esm/core/TatchiPasskey/recoverAccount.js.map +1 -1
  236. package/dist/esm/core/TatchiPasskey/relay.js +23 -1
  237. package/dist/esm/core/TatchiPasskey/relay.js.map +1 -1
  238. package/dist/esm/core/TatchiPasskey/scanDevice.js +2 -1
  239. package/dist/esm/core/TatchiPasskey/scanDevice.js.map +1 -1
  240. package/dist/esm/core/WalletIframe/client/IframeTransport.js +4 -1
  241. package/dist/esm/core/WalletIframe/client/IframeTransport.js.map +1 -1
  242. package/dist/esm/core/WalletIframe/client/router.js +16 -3
  243. package/dist/esm/core/WalletIframe/client/router.js.map +1 -1
  244. package/dist/esm/core/WebAuthnManager/LitComponents/IframeTxConfirmer/viewer-drawer.js +1 -1
  245. package/dist/esm/core/WebAuthnManager/LitComponents/IframeTxConfirmer/viewer-drawer.js.map +1 -1
  246. package/dist/esm/core/WebAuthnManager/LitComponents/IframeTxConfirmer/viewer-modal.js +52 -52
  247. package/dist/esm/core/WebAuthnManager/LitComponents/IframeTxConfirmer/viewer-modal.js.map +1 -1
  248. package/dist/esm/core/WebAuthnManager/SignerWorkerManager/getDeviceNumber.js +6 -2
  249. package/dist/esm/core/WebAuthnManager/SignerWorkerManager/handlers/checkCanRegisterUser.js +2 -1
  250. package/dist/esm/core/WebAuthnManager/SignerWorkerManager/handlers/checkCanRegisterUser.js.map +1 -1
  251. package/dist/esm/core/WebAuthnManager/SignerWorkerManager/handlers/decryptPrivateKeyWithPrf.js +2 -1
  252. package/dist/esm/core/WebAuthnManager/SignerWorkerManager/handlers/decryptPrivateKeyWithPrf.js.map +1 -1
  253. package/dist/esm/core/WebAuthnManager/SignerWorkerManager/handlers/deriveNearKeypairAndEncryptFromSerialized.js +2 -1
  254. package/dist/esm/core/WebAuthnManager/SignerWorkerManager/handlers/deriveNearKeypairAndEncryptFromSerialized.js.map +1 -1
  255. package/dist/esm/core/WebAuthnManager/SignerWorkerManager/handlers/exportNearKeypairUi.js +2 -1
  256. package/dist/esm/core/WebAuthnManager/SignerWorkerManager/handlers/exportNearKeypairUi.js.map +1 -1
  257. package/dist/esm/core/WebAuthnManager/SignerWorkerManager/handlers/registerDevice2WithDerivedKey.js +2 -1
  258. package/dist/esm/core/WebAuthnManager/SignerWorkerManager/handlers/registerDevice2WithDerivedKey.js.map +1 -1
  259. package/dist/esm/core/WebAuthnManager/SignerWorkerManager/handlers/signDelegateAction.js +2 -1
  260. package/dist/esm/core/WebAuthnManager/SignerWorkerManager/handlers/signDelegateAction.js.map +1 -1
  261. package/dist/esm/core/WebAuthnManager/SignerWorkerManager/handlers/signNep413Message.js +2 -1
  262. package/dist/esm/core/WebAuthnManager/SignerWorkerManager/handlers/signNep413Message.js.map +1 -1
  263. package/dist/esm/core/WebAuthnManager/SignerWorkerManager/handlers/signTransactionsWithActions.js +2 -1
  264. package/dist/esm/core/WebAuthnManager/SignerWorkerManager/handlers/signTransactionsWithActions.js.map +1 -1
  265. package/dist/esm/core/WebAuthnManager/SignerWorkerManager/handlers/validation.js +4 -2
  266. package/dist/esm/core/WebAuthnManager/SignerWorkerManager/handlers/validation.js.map +1 -1
  267. package/dist/esm/core/WebAuthnManager/SignerWorkerManager/index.js +2 -1
  268. package/dist/esm/core/WebAuthnManager/SignerWorkerManager/index.js.map +1 -1
  269. package/dist/esm/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/adapters/vrf.js +1 -0
  270. package/dist/esm/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/adapters/vrf.js.map +1 -1
  271. package/dist/esm/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/adapters/webauthn.js +8 -2
  272. package/dist/esm/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/adapters/webauthn.js.map +1 -1
  273. package/dist/esm/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/determineConfirmationConfig.js +2 -1
  274. package/dist/esm/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/determineConfirmationConfig.js.map +1 -1
  275. package/dist/esm/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/localOnly.js +2 -1
  276. package/dist/esm/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/localOnly.js.map +1 -1
  277. package/dist/esm/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/registration.js +2 -1
  278. package/dist/esm/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/registration.js.map +1 -1
  279. package/dist/esm/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/transactions.js +5 -16
  280. package/dist/esm/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/transactions.js.map +1 -1
  281. package/dist/esm/core/WebAuthnManager/VrfWorkerManager/handlers/deriveVrfKeypairFromPrf.js +2 -1
  282. package/dist/esm/core/WebAuthnManager/VrfWorkerManager/handlers/deriveVrfKeypairFromPrf.js.map +1 -1
  283. package/dist/esm/core/WebAuthnManager/VrfWorkerManager/handlers/generateVrfChallenge.js +2 -1
  284. package/dist/esm/core/WebAuthnManager/VrfWorkerManager/handlers/generateVrfChallenge.js.map +1 -1
  285. package/dist/esm/core/WebAuthnManager/VrfWorkerManager/handlers/generateVrfKeypairBootstrap.js +2 -1
  286. package/dist/esm/core/WebAuthnManager/VrfWorkerManager/handlers/generateVrfKeypairBootstrap.js.map +1 -1
  287. package/dist/esm/core/WebAuthnManager/WebAuthnFallbacks/index.js +12 -0
  288. package/dist/esm/core/WebAuthnManager/WebAuthnFallbacks/index.js.map +1 -0
  289. package/dist/esm/core/WebAuthnManager/WebAuthnFallbacks/safari-fallbacks.js +61 -55
  290. package/dist/esm/core/WebAuthnManager/WebAuthnFallbacks/safari-fallbacks.js.map +1 -1
  291. package/dist/esm/core/WebAuthnManager/credentialsHelpers.js +8 -3
  292. package/dist/esm/core/WebAuthnManager/index.js +8 -3
  293. package/dist/esm/core/WebAuthnManager/index.js.map +1 -1
  294. package/dist/esm/core/WebAuthnManager/touchIdPrompt.js +207 -204
  295. package/dist/esm/core/WebAuthnManager/touchIdPrompt.js.map +1 -1
  296. package/dist/esm/core/WebAuthnManager/userHandle.js +2 -1
  297. package/dist/esm/core/WebAuthnManager/userHandle.js.map +1 -1
  298. package/dist/esm/core/defaultConfigs.js +1 -1
  299. package/dist/esm/core/defaultConfigs.js.map +1 -1
  300. package/dist/esm/core/rpcCalls.js +8 -1
  301. package/dist/esm/core/rpcCalls.js.map +1 -1
  302. package/dist/esm/core/types/vrf-worker.js +6 -2
  303. package/dist/esm/index.js +4 -1
  304. package/dist/esm/index.js.map +1 -1
  305. package/dist/esm/react/components/AccountMenuButton/{LinkedDevicesModal-STvIsylA.css → LinkedDevicesModal-CSSowiHP.css} +1 -1
  306. package/dist/{cjs/react/components/AccountMenuButton/LinkedDevicesModal-STvIsylA.css.map → esm/react/components/AccountMenuButton/LinkedDevicesModal-CSSowiHP.css.map} +1 -1
  307. package/dist/esm/react/components/AccountMenuButton/{ProfileDropdown-iARgUwK1.css → ProfileDropdown-CEPMZ1gY.css} +1 -1
  308. package/dist/{cjs/react/components/AccountMenuButton/ProfileDropdown-iARgUwK1.css.map → esm/react/components/AccountMenuButton/ProfileDropdown-CEPMZ1gY.css.map} +1 -1
  309. package/dist/esm/react/components/AccountMenuButton/{Web3AuthProfileButton-Db3NeoAC.css → Web3AuthProfileButton-DopOg7Xc.css} +1 -1
  310. package/dist/esm/react/components/AccountMenuButton/{Web3AuthProfileButton-Db3NeoAC.css.map → Web3AuthProfileButton-DopOg7Xc.css.map} +1 -1
  311. package/dist/esm/react/components/AccountMenuButton/icons/{TouchIcon-BXM5NR4A.css → TouchIcon-BQWentvJ.css} +1 -1
  312. package/dist/esm/react/components/AccountMenuButton/icons/{TouchIcon-BXM5NR4A.css.map → TouchIcon-BQWentvJ.css.map} +1 -1
  313. package/dist/esm/react/components/PasskeyAuthMenu/{PasskeyAuthMenu-De1qTSmU.css → PasskeyAuthMenu-DwrzWMYx.css} +14 -1
  314. package/dist/esm/react/components/PasskeyAuthMenu/{PasskeyAuthMenu-De1qTSmU.css.map → PasskeyAuthMenu-DwrzWMYx.css.map} +1 -1
  315. package/dist/esm/react/components/PasskeyAuthMenu/ui/EmailRecoverySlide.js +123 -54
  316. package/dist/esm/react/components/PasskeyAuthMenu/ui/EmailRecoverySlide.js.map +1 -1
  317. package/dist/esm/react/components/{ShowQRCode-DCnR__fx.css → ShowQRCode-CCN4h6Uv.css} +1 -1
  318. package/dist/esm/react/components/{ShowQRCode-DCnR__fx.css.map → ShowQRCode-CCN4h6Uv.css.map} +1 -1
  319. package/dist/esm/react/deviceDetection.js +72 -93
  320. package/dist/esm/react/deviceDetection.js.map +1 -1
  321. package/dist/esm/react/hooks/useQRCamera.js +2 -1
  322. package/dist/esm/react/hooks/useQRCamera.js.map +1 -1
  323. package/dist/esm/react/sdk/src/core/EmailRecovery/emailRecoveryPendingStore.js +63 -0
  324. package/dist/esm/react/sdk/src/core/EmailRecovery/emailRecoveryPendingStore.js.map +1 -0
  325. package/dist/esm/react/sdk/src/core/EmailRecovery/index.js +28 -14
  326. package/dist/esm/react/sdk/src/core/EmailRecovery/index.js.map +1 -1
  327. package/dist/esm/react/sdk/src/core/IndexedDBManager/passkeyClientDB.js +35 -36
  328. package/dist/esm/react/sdk/src/core/IndexedDBManager/passkeyClientDB.js.map +1 -1
  329. package/dist/esm/react/sdk/src/core/NearClient.js +2 -1
  330. package/dist/esm/react/sdk/src/core/NearClient.js.map +1 -1
  331. package/dist/esm/react/sdk/src/core/TatchiPasskey/emailRecovery.js +557 -377
  332. package/dist/esm/react/sdk/src/core/TatchiPasskey/emailRecovery.js.map +1 -1
  333. package/dist/esm/react/sdk/src/core/TatchiPasskey/faucets/createAccountRelayServer.js +2 -1
  334. package/dist/esm/react/sdk/src/core/TatchiPasskey/faucets/createAccountRelayServer.js.map +1 -1
  335. package/dist/esm/react/sdk/src/core/TatchiPasskey/index.js +28 -2
  336. package/dist/esm/react/sdk/src/core/TatchiPasskey/index.js.map +1 -1
  337. package/dist/esm/react/sdk/src/core/TatchiPasskey/linkDevice.js +4 -2
  338. package/dist/esm/react/sdk/src/core/TatchiPasskey/linkDevice.js.map +1 -1
  339. package/dist/esm/react/sdk/src/core/TatchiPasskey/login.js +13 -7
  340. package/dist/esm/react/sdk/src/core/TatchiPasskey/login.js.map +1 -1
  341. package/dist/esm/react/sdk/src/core/TatchiPasskey/recoverAccount.js +2 -1
  342. package/dist/esm/react/sdk/src/core/TatchiPasskey/recoverAccount.js.map +1 -1
  343. package/dist/esm/react/sdk/src/core/TatchiPasskey/relay.js +23 -1
  344. package/dist/esm/react/sdk/src/core/TatchiPasskey/relay.js.map +1 -1
  345. package/dist/esm/react/sdk/src/core/TatchiPasskey/scanDevice.js +2 -1
  346. package/dist/esm/react/sdk/src/core/TatchiPasskey/scanDevice.js.map +1 -1
  347. package/dist/esm/react/sdk/src/core/WalletIframe/client/IframeTransport.js +4 -1
  348. package/dist/esm/react/sdk/src/core/WalletIframe/client/IframeTransport.js.map +1 -1
  349. package/dist/esm/react/sdk/src/core/WalletIframe/client/router.js +16 -3
  350. package/dist/esm/react/sdk/src/core/WalletIframe/client/router.js.map +1 -1
  351. package/dist/esm/react/sdk/src/core/WebAuthnManager/LitComponents/IframeTxConfirmer/viewer-drawer.js +1 -1
  352. package/dist/esm/react/sdk/src/core/WebAuthnManager/LitComponents/IframeTxConfirmer/viewer-drawer.js.map +1 -1
  353. package/dist/esm/react/sdk/src/core/WebAuthnManager/LitComponents/IframeTxConfirmer/viewer-modal.js +52 -52
  354. package/dist/esm/react/sdk/src/core/WebAuthnManager/LitComponents/IframeTxConfirmer/viewer-modal.js.map +1 -1
  355. package/dist/esm/react/sdk/src/core/WebAuthnManager/SignerWorkerManager/getDeviceNumber.js +6 -2
  356. package/dist/esm/react/sdk/src/core/WebAuthnManager/SignerWorkerManager/handlers/checkCanRegisterUser.js +2 -1
  357. package/dist/esm/react/sdk/src/core/WebAuthnManager/SignerWorkerManager/handlers/checkCanRegisterUser.js.map +1 -1
  358. package/dist/esm/react/sdk/src/core/WebAuthnManager/SignerWorkerManager/handlers/decryptPrivateKeyWithPrf.js +2 -1
  359. package/dist/esm/react/sdk/src/core/WebAuthnManager/SignerWorkerManager/handlers/decryptPrivateKeyWithPrf.js.map +1 -1
  360. package/dist/esm/react/sdk/src/core/WebAuthnManager/SignerWorkerManager/handlers/deriveNearKeypairAndEncryptFromSerialized.js +2 -1
  361. package/dist/esm/react/sdk/src/core/WebAuthnManager/SignerWorkerManager/handlers/deriveNearKeypairAndEncryptFromSerialized.js.map +1 -1
  362. package/dist/esm/react/sdk/src/core/WebAuthnManager/SignerWorkerManager/handlers/exportNearKeypairUi.js +2 -1
  363. package/dist/esm/react/sdk/src/core/WebAuthnManager/SignerWorkerManager/handlers/exportNearKeypairUi.js.map +1 -1
  364. package/dist/esm/react/sdk/src/core/WebAuthnManager/SignerWorkerManager/handlers/registerDevice2WithDerivedKey.js +2 -1
  365. package/dist/esm/react/sdk/src/core/WebAuthnManager/SignerWorkerManager/handlers/registerDevice2WithDerivedKey.js.map +1 -1
  366. package/dist/esm/react/sdk/src/core/WebAuthnManager/SignerWorkerManager/handlers/signDelegateAction.js +2 -1
  367. package/dist/esm/react/sdk/src/core/WebAuthnManager/SignerWorkerManager/handlers/signDelegateAction.js.map +1 -1
  368. package/dist/esm/react/sdk/src/core/WebAuthnManager/SignerWorkerManager/handlers/signNep413Message.js +2 -1
  369. package/dist/esm/react/sdk/src/core/WebAuthnManager/SignerWorkerManager/handlers/signNep413Message.js.map +1 -1
  370. package/dist/esm/react/sdk/src/core/WebAuthnManager/SignerWorkerManager/handlers/signTransactionsWithActions.js +2 -1
  371. package/dist/esm/react/sdk/src/core/WebAuthnManager/SignerWorkerManager/handlers/signTransactionsWithActions.js.map +1 -1
  372. package/dist/esm/react/sdk/src/core/WebAuthnManager/SignerWorkerManager/handlers/validation.js +4 -2
  373. package/dist/esm/react/sdk/src/core/WebAuthnManager/SignerWorkerManager/handlers/validation.js.map +1 -1
  374. package/dist/esm/react/sdk/src/core/WebAuthnManager/SignerWorkerManager/index.js +2 -1
  375. package/dist/esm/react/sdk/src/core/WebAuthnManager/SignerWorkerManager/index.js.map +1 -1
  376. package/dist/esm/react/sdk/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/adapters/vrf.js +1 -0
  377. package/dist/esm/react/sdk/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/adapters/vrf.js.map +1 -1
  378. package/dist/esm/react/sdk/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/adapters/webauthn.js +8 -2
  379. package/dist/esm/react/sdk/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/adapters/webauthn.js.map +1 -1
  380. package/dist/esm/react/sdk/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/determineConfirmationConfig.js +2 -1
  381. package/dist/esm/react/sdk/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/determineConfirmationConfig.js.map +1 -1
  382. package/dist/esm/react/sdk/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/localOnly.js +2 -1
  383. package/dist/esm/react/sdk/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/localOnly.js.map +1 -1
  384. package/dist/esm/react/sdk/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/registration.js +2 -1
  385. package/dist/esm/react/sdk/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/registration.js.map +1 -1
  386. package/dist/esm/react/sdk/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/transactions.js +5 -16
  387. package/dist/esm/react/sdk/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/transactions.js.map +1 -1
  388. package/dist/esm/react/sdk/src/core/WebAuthnManager/VrfWorkerManager/handlers/deriveVrfKeypairFromPrf.js +2 -1
  389. package/dist/esm/react/sdk/src/core/WebAuthnManager/VrfWorkerManager/handlers/deriveVrfKeypairFromPrf.js.map +1 -1
  390. package/dist/esm/react/sdk/src/core/WebAuthnManager/VrfWorkerManager/handlers/generateVrfChallenge.js +2 -1
  391. package/dist/esm/react/sdk/src/core/WebAuthnManager/VrfWorkerManager/handlers/generateVrfChallenge.js.map +1 -1
  392. package/dist/esm/react/sdk/src/core/WebAuthnManager/VrfWorkerManager/handlers/generateVrfKeypairBootstrap.js +2 -1
  393. package/dist/esm/react/sdk/src/core/WebAuthnManager/VrfWorkerManager/handlers/generateVrfKeypairBootstrap.js.map +1 -1
  394. package/dist/esm/react/sdk/src/core/WebAuthnManager/WebAuthnFallbacks/index.js +12 -0
  395. package/dist/esm/react/sdk/src/core/WebAuthnManager/WebAuthnFallbacks/index.js.map +1 -0
  396. package/dist/esm/react/sdk/src/core/WebAuthnManager/WebAuthnFallbacks/safari-fallbacks.js +61 -55
  397. package/dist/esm/react/sdk/src/core/WebAuthnManager/WebAuthnFallbacks/safari-fallbacks.js.map +1 -1
  398. package/dist/esm/react/sdk/src/core/WebAuthnManager/credentialsHelpers.js +8 -3
  399. package/dist/esm/react/sdk/src/core/WebAuthnManager/index.js +8 -3
  400. package/dist/esm/react/sdk/src/core/WebAuthnManager/index.js.map +1 -1
  401. package/dist/esm/react/sdk/src/core/WebAuthnManager/touchIdPrompt.js +207 -204
  402. package/dist/esm/react/sdk/src/core/WebAuthnManager/touchIdPrompt.js.map +1 -1
  403. package/dist/esm/react/sdk/src/core/WebAuthnManager/userHandle.js +2 -1
  404. package/dist/esm/react/sdk/src/core/WebAuthnManager/userHandle.js.map +1 -1
  405. package/dist/esm/react/sdk/src/core/defaultConfigs.js +1 -1
  406. package/dist/esm/react/sdk/src/core/defaultConfigs.js.map +1 -1
  407. package/dist/esm/react/sdk/src/core/rpcCalls.js +8 -1
  408. package/dist/esm/react/sdk/src/core/rpcCalls.js.map +1 -1
  409. package/dist/esm/react/sdk/src/core/types/vrf-worker.js +6 -2
  410. package/dist/esm/react/sdk/src/utils/index.js +10 -4
  411. package/dist/esm/react/styles/styles.css +13 -0
  412. package/dist/esm/sdk/{safari-fallbacks-oQKu9xUs.js → WebAuthnFallbacks-Bl4BTsNt.js} +131 -135
  413. package/dist/esm/sdk/{createAdapters-pNiL2KNq.js → createAdapters-BumKM2ft.js} +59 -54
  414. package/dist/esm/sdk/createAdapters-BumKM2ft.js.map +1 -0
  415. package/dist/esm/sdk/{createAdapters-BWLe9Ddo.js → createAdapters-qVGD6i0g.js} +10 -3
  416. package/dist/esm/sdk/{defaultConfigs-VzvDejmy.js → defaultConfigs-DpslkAQd.js} +1 -1
  417. package/dist/esm/sdk/{getDeviceNumber-CkWRT17I.js → getDeviceNumber-fXizNGQl.js} +2 -2
  418. package/dist/esm/sdk/getDeviceNumber-fXizNGQl.js.map +1 -0
  419. package/dist/esm/sdk/{getDeviceNumber-CfmlgfMX.js → getDeviceNumber-zsOHT_Um.js} +6 -3
  420. package/dist/esm/sdk/{localOnly-DnpSyDaF.js → localOnly-Byi3AK7A.js} +2 -2
  421. package/dist/esm/sdk/{localOnly-DnpSyDaF.js.map → localOnly-Byi3AK7A.js.map} +1 -1
  422. package/dist/esm/sdk/{localOnly-BdumO2st.js → localOnly-pXMTqh1m.js} +5 -4
  423. package/dist/esm/sdk/offline-export-app.js +46 -44
  424. package/dist/esm/sdk/offline-export-app.js.map +1 -1
  425. package/dist/esm/sdk/{overlay-BTqPGG-o.js → overlay-ZGbucXIa.js} +2 -0
  426. package/dist/esm/sdk/{registration-C633u6x8.js → registration-CBiS4Ua_.js} +2 -2
  427. package/dist/esm/sdk/{registration-C633u6x8.js.map → registration-CBiS4Ua_.js.map} +1 -1
  428. package/dist/esm/sdk/{registration-xyYUFRqk.js → registration-DLPLsGCz.js} +5 -4
  429. package/dist/esm/sdk/{requestHelpers-DLBGBHMw.js → requestHelpers-Dh1hEYL9.js} +206 -204
  430. package/dist/esm/sdk/{router-BG6KC_p7.js → router-DuGYOd3G.js} +19 -4
  431. package/dist/esm/sdk/{rpcCalls-fLObBbbz.js → rpcCalls-BQrJMTdg.js} +3 -3
  432. package/dist/esm/sdk/{rpcCalls-CAU5XYEF.js → rpcCalls-YVeUVMk2.js} +9 -2
  433. package/dist/esm/sdk/{transactions-jH38BZ-Q.js → transactions-BIqKZeR0.js} +6 -18
  434. package/dist/esm/sdk/transactions-BIqKZeR0.js.map +1 -0
  435. package/dist/esm/sdk/{transactions-CzZAt1Yn.js → transactions-Bk-VavcV.js} +10 -21
  436. package/dist/esm/sdk/tx-confirm-ui.js +53 -53
  437. package/dist/esm/sdk/{tx-confirmer-wrapper-CqfVBUaA.js → tx-confirmer-wrapper-lHNgz9i4.js} +53 -53
  438. package/dist/esm/sdk/tx-confirmer.css +6 -4
  439. package/dist/esm/sdk/w3a-tx-confirmer.js +1 -1
  440. package/dist/esm/sdk/wallet-iframe-host.js +782 -447
  441. package/dist/esm/server/email-recovery/emailEncryptor.js +11 -1
  442. package/dist/esm/server/email-recovery/emailEncryptor.js.map +1 -1
  443. package/dist/esm/server/email-recovery/emailParsers.js +55 -1
  444. package/dist/esm/server/email-recovery/emailParsers.js.map +1 -1
  445. package/dist/esm/server/email-recovery/index.js +2 -2
  446. package/dist/esm/server/email-recovery/index.js.map +1 -1
  447. package/dist/esm/server/email-recovery/rpcCalls.js +14 -1
  448. package/dist/esm/server/email-recovery/rpcCalls.js.map +1 -1
  449. package/dist/esm/server/index.js +2 -2
  450. package/dist/esm/server/router/cloudflare.js.map +1 -1
  451. package/dist/esm/server/router/express.js.map +1 -1
  452. package/dist/esm/server/sdk/src/core/defaultConfigs.js +1 -1
  453. package/dist/esm/server/sdk/src/core/defaultConfigs.js.map +1 -1
  454. package/dist/esm/utils/index.js +10 -4
  455. package/dist/esm/wasm_vrf_worker/pkg/wasm_vrf_worker.js +3 -0
  456. package/dist/esm/wasm_vrf_worker/pkg/wasm_vrf_worker_bg.wasm +0 -0
  457. package/dist/types/src/core/EmailRecovery/emailRecoveryPendingStore.d.ts +25 -0
  458. package/dist/types/src/core/EmailRecovery/emailRecoveryPendingStore.d.ts.map +1 -0
  459. package/dist/types/src/core/EmailRecovery/index.d.ts +1 -0
  460. package/dist/types/src/core/EmailRecovery/index.d.ts.map +1 -1
  461. package/dist/types/src/core/IndexedDBManager/passkeyClientDB.d.ts +11 -21
  462. package/dist/types/src/core/IndexedDBManager/passkeyClientDB.d.ts.map +1 -1
  463. package/dist/types/src/core/TatchiPasskey/emailRecovery.d.ts +45 -5
  464. package/dist/types/src/core/TatchiPasskey/emailRecovery.d.ts.map +1 -1
  465. package/dist/types/src/core/TatchiPasskey/index.d.ts +10 -2
  466. package/dist/types/src/core/TatchiPasskey/index.d.ts.map +1 -1
  467. package/dist/types/src/core/TatchiPasskey/relay.d.ts +2 -1
  468. package/dist/types/src/core/TatchiPasskey/relay.d.ts.map +1 -1
  469. package/dist/types/src/core/WalletIframe/TatchiPasskeyIframe.d.ts +4 -0
  470. package/dist/types/src/core/WalletIframe/TatchiPasskeyIframe.d.ts.map +1 -1
  471. package/dist/types/src/core/WalletIframe/client/router.d.ts +7 -3
  472. package/dist/types/src/core/WalletIframe/client/router.d.ts.map +1 -1
  473. package/dist/types/src/core/WalletIframe/host/wallet-iframe-handlers.d.ts.map +1 -1
  474. package/dist/types/src/core/WalletIframe/shared/messages.d.ts +6 -2
  475. package/dist/types/src/core/WalletIframe/shared/messages.d.ts.map +1 -1
  476. package/dist/types/src/core/WebAuthnManager/LitComponents/IframeTxConfirmer/viewer-drawer.d.ts.map +1 -1
  477. package/dist/types/src/core/WebAuthnManager/LitComponents/IframeTxConfirmer/viewer-modal.d.ts.map +1 -1
  478. package/dist/types/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/adapters/vrf.d.ts.map +1 -1
  479. package/dist/types/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/adapters/webauthn.d.ts.map +1 -1
  480. package/dist/types/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/transactions.d.ts.map +1 -1
  481. package/dist/types/src/core/WebAuthnManager/index.d.ts.map +1 -1
  482. package/dist/types/src/core/defaultConfigs.d.ts.map +1 -1
  483. package/dist/types/src/core/rpcCalls.d.ts +9 -0
  484. package/dist/types/src/core/rpcCalls.d.ts.map +1 -1
  485. package/dist/types/src/index.d.ts +1 -0
  486. package/dist/types/src/index.d.ts.map +1 -1
  487. package/dist/types/src/react/components/PasskeyAuthMenu/ui/EmailRecoverySlide.d.ts.map +1 -1
  488. package/dist/types/src/server/email-recovery/emailEncryptor.d.ts +4 -0
  489. package/dist/types/src/server/email-recovery/emailEncryptor.d.ts.map +1 -1
  490. package/dist/types/src/server/email-recovery/emailParsers.d.ts +7 -0
  491. package/dist/types/src/server/email-recovery/emailParsers.d.ts.map +1 -1
  492. package/dist/types/src/server/email-recovery/index.d.ts +1 -1
  493. package/dist/types/src/server/email-recovery/rpcCalls.d.ts +1 -1
  494. package/dist/types/src/server/email-recovery/rpcCalls.d.ts.map +1 -1
  495. package/dist/types/src/wasm_vrf_worker/pkg/wasm_vrf_worker.d.ts.map +1 -1
  496. package/dist/workers/wasm_vrf_worker_bg.wasm +0 -0
  497. package/dist/workers/web3authn-vrf.worker.js +3 -0
  498. package/package.json +1 -1
  499. package/dist/esm/sdk/createAdapters-pNiL2KNq.js.map +0 -1
  500. package/dist/esm/sdk/getDeviceNumber-CkWRT17I.js.map +0 -1
  501. package/dist/esm/sdk/transactions-jH38BZ-Q.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"recoverAccount.js","names":["validateNearAccountId","toAccountId","createRandomVRFChallenge","inferredAccountId: string | null","assertion: any","parseAccountIdFromUserHandle","option: PasskeyOption","error: any","getCredentialIdsContractCall","AccountRecoveryPhase","AccountRecoveryStatus","vrfInputData: VRFInputData","errorResult: RecoveryResult","syncAuthenticatorsContractCall","IndexedDBManager"],"sources":["../../../../src/core/TatchiPasskey/recoverAccount.ts"],"sourcesContent":["import type { AfterCall, AccountRecoverySSEEvent, EventCallback } from '../types/sdkSentEvents';\nimport { AccountRecoveryPhase, AccountRecoveryStatus, AccountRecoveryHooksOptions } from '../types/sdkSentEvents';\nimport type { PasskeyManagerContext } from './index';\nimport type {\n AccountId,\n StoredAuthenticator,\n VRFChallenge,\n WebAuthnAuthenticationCredential\n} from '../types';\nimport type { EncryptedVRFKeypair, ServerEncryptedVrfKeypair } from '../types/vrf-worker';\nimport { validateNearAccountId } from '../../utils/validation';\nimport { parseAccountIdFromUserHandle } from '../WebAuthnManager/userHandle';\nimport { toAccountId } from '../types/accountIds';\nimport { createRandomVRFChallenge } from '../types/vrf-worker';\nimport { WebAuthnManager } from '../WebAuthnManager';\nimport { IndexedDBManager } from '../IndexedDBManager';\nimport type { VRFInputData } from '../types/vrf-worker';\nimport type { OriginPolicyInput, UserVerificationPolicy } from '../types/authenticatorOptions';\nimport {\n getCredentialIdsContractCall,\n syncAuthenticatorsContractCall\n} from '../rpcCalls';\n\n/**\n * Use case:\n * Suppose a user accidentally clears their browser's indexedDB, and deletes their:\n * - encrypted NEAR keypair\n * - encrypted VRF keypair\n * - webauthn authenticator\n * Provide a way for the user to recover their account from onchain authenticator information with their Passkey.\n */\n\nexport interface RecoveryResult {\n success: boolean;\n accountId: string;\n publicKey: string;\n message: string;\n error?: string;\n loginState?: {\n isLoggedIn: boolean;\n vrfActive: boolean;\n vrfSessionDuration?: number;\n };\n}\n\nexport interface AccountLookupResult {\n accountId: string;\n publicKey: string;\n hasAccess: boolean;\n}\n\nexport interface PasskeyOption {\n credentialId: string;\n accountId: AccountId | null;\n publicKey: string;\n displayName: string;\n credential: WebAuthnAuthenticationCredential | null;\n}\n\n// Public-facing passkey option without sensitive credential data\nexport interface PasskeyOptionWithoutCredential {\n credentialId: string;\n accountId: string | null;\n publicKey: string;\n displayName: string;\n}\n\n// Internal selection identifier for secure credential lookup\nexport interface PasskeySelection {\n credentialId: string;\n accountId: string | null;\n}\n\n/**\n * Account recovery flow with credential encapsulation\n *\n * Usage:\n * ```typescript\n * const flow = new AccountRecoveryFlow(context);\n * const options = await flow.discover(); // Get safe display options\n * // ... user selects account in UI ...\n * const result = await flow.recover({ credentialId, accountId }); // Execute recovery\n * ```\n */\nexport class AccountRecoveryFlow {\n private context: PasskeyManagerContext;\n private options?: AccountRecoveryHooksOptions;\n private availableAccounts?: PasskeyOption[]; // Full options with credentials (private)\n private phase: 'idle' | 'discovering' | 'ready' | 'recovering' | 'complete' | 'error' = 'idle';\n private error?: Error;\n\n constructor(context: PasskeyManagerContext, options?: AccountRecoveryHooksOptions) {\n this.context = context;\n this.options = options;\n }\n\n /**\n * Phase 1: Discover available accounts\n * Returns safe display data without exposing credentials to UI\n */\n async discover(accountId: string): Promise<PasskeyOptionWithoutCredential[]> {\n try {\n this.phase = 'discovering';\n const hasValidAccount = !!accountId && validateNearAccountId(accountId).valid;\n\n if (hasValidAccount) {\n const nearAccountId = toAccountId(accountId);\n // Contract-based lookup; no WebAuthn prompt during discovery\n this.availableAccounts = await getRecoverableAccounts(this.context, nearAccountId);\n } else {\n // Fallback discovery without a typed account: prompt once to select a passkey\n // Then infer the accountId from userHandle (set at registration time)\n const challenge = createRandomVRFChallenge();\n const credential = await this.context.webAuthnManager.getAuthenticationCredentialsSerializedDualPrf({\n // Account is unknown here – salts aren't used downstream for discovery\n nearAccountId: '' as any,\n challenge: challenge as VRFChallenge,\n credentialIds: [],\n });\n\n // Try to infer accountId from userHandle\n let inferredAccountId: string | null = null;\n try {\n const assertion: any = credential.response as any;\n inferredAccountId = parseAccountIdFromUserHandle(assertion?.userHandle);\n } catch {}\n\n const option: PasskeyOption = {\n credentialId: credential.id,\n accountId: inferredAccountId ? (inferredAccountId as any as AccountId) : null,\n publicKey: '',\n displayName: inferredAccountId ? `${inferredAccountId}` : 'Recovered passkey',\n credential,\n };\n this.availableAccounts = [option];\n }\n\n if (!this.availableAccounts || this.availableAccounts.length === 0) {\n console.warn('No recoverable accounts found for this passkey');\n } else {\n console.debug(`AccountRecoveryFlow: Found ${this.availableAccounts.length} recoverable accounts`);\n }\n\n this.phase = 'ready';\n\n // Return safe options without credentials for UI display\n return this.availableAccounts.map(option => ({\n credentialId: option.credentialId,\n accountId: option.accountId,\n publicKey: option.publicKey,\n displayName: option.displayName\n }));\n\n } catch (error: any) {\n this.phase = 'error';\n this.error = error;\n console.error('AccountRecoveryFlow: Discovery failed:', error);\n throw error;\n }\n }\n\n /**\n * Phase 2: Execute recovery with user selection\n * Securely looks up credential based on selection\n */\n async recover(selection: PasskeySelection): Promise<RecoveryResult> {\n if (this.phase !== 'ready') {\n throw new Error(`Cannot recover - flow is in ${this.phase} phase. Call discover() first.`);\n }\n if (!this.availableAccounts) {\n throw new Error('No available accounts found. Call discover() first.');\n }\n\n try {\n this.phase = 'recovering';\n console.debug(`AccountRecoveryFlow: Recovering account: ${selection.accountId}`);\n\n // Securely lookup the full option with credential\n const selectedOption = this.availableAccounts.find(\n option => option.credentialId === selection.credentialId &&\n option.accountId === selection.accountId\n );\n\n if (!selectedOption) {\n throw new Error('Invalid selection - account not found in available options');\n }\n if (!selectedOption.accountId) {\n // Attempt a one-time re-prompt to infer accountId from userHandle for this credential\n try {\n const challenge = createRandomVRFChallenge();\n const cred = await this.context.webAuthnManager.getAuthenticationCredentialsSerializedDualPrf({\n nearAccountId: '' as any,\n challenge: challenge as VRFChallenge,\n credentialIds: selectedOption.credentialId && selectedOption.credentialId !== 'manual-input'\n ? [selectedOption.credentialId]\n : [],\n });\n const assertion: any = cred.response as any;\n const maybeAccount = parseAccountIdFromUserHandle(assertion?.userHandle);\n if (maybeAccount) {\n selectedOption.accountId = maybeAccount as any as AccountId;\n }\n } catch {}\n if (!selectedOption.accountId) {\n throw new Error('Invalid account selection - no account ID provided');\n }\n }\n\n // If multiple credentials exist for this account, allow the platform chooser UI\n // by passing all known credential IDs for this account as allowCredentials.\n const allowList = (() => {\n try {\n if (!this.availableAccounts) return undefined;\n if (!selectedOption.credentialId || selectedOption.credentialId === 'manual-input') return undefined;\n const ids = this.availableAccounts\n .filter(opt => opt.accountId === selectedOption.accountId && opt.credentialId && opt.credentialId !== 'manual-input')\n .map(opt => opt.credentialId);\n const uniq = Array.from(new Set(ids));\n return uniq.length > 0 ? uniq : undefined;\n } catch { return undefined; }\n })();\n\n const recoveryResult = await recoverAccount(\n this.context,\n selectedOption.accountId,\n this.options,\n undefined,\n allowList\n );\n\n this.phase = 'complete';\n return recoveryResult;\n\n } catch (error: any) {\n this.phase = 'error';\n this.error = error;\n console.error('AccountRecoveryFlow: Recovery failed:', error);\n throw error;\n }\n }\n\n /**\n * Get current flow state (safe display data only)\n */\n getState() {\n // Convert internal accounts to safe display format\n const safeAccounts = this.availableAccounts?.map(option => ({\n credentialId: option.credentialId,\n accountId: option.accountId,\n publicKey: option.publicKey,\n displayName: option.displayName\n }));\n\n return {\n phase: this.phase,\n availableAccounts: safeAccounts,\n error: this.error,\n isReady: this.phase === 'ready',\n isComplete: this.phase === 'complete',\n hasError: this.phase === 'error'\n };\n }\n\n /**\n * Reset flow to initial state\n */\n reset() {\n this.phase = 'idle';\n this.availableAccounts = undefined;\n this.error = undefined;\n }\n}\n\n/**\n * Get available passkeys for account recovery\n */\nasync function getRecoverableAccounts(\n context: PasskeyManagerContext,\n accountId: AccountId\n): Promise<PasskeyOption[]> {\n const availablePasskeys = await getAvailablePasskeysForDomain(context, accountId);\n return availablePasskeys.filter(passkey => passkey.accountId !== null);\n}\n\n/**\n * Discover passkeys for domain using contract-based lookup\n */\nasync function getAvailablePasskeysForDomain(\n context: PasskeyManagerContext,\n accountId: AccountId\n): Promise<PasskeyOption[]> {\n const { nearClient, configs } = context;\n\n const credentialIds = await getCredentialIdsContractCall(nearClient, configs.contractId, accountId);\n\n // Do not invoke WebAuthn here; just return display options bound to credential IDs\n if (credentialIds.length > 0) {\n return credentialIds.map((credentialId, idx) => ({\n credentialId,\n accountId,\n publicKey: '',\n displayName: credentialIds.length > 1 ? `${accountId} (passkey ${idx + 1})` : `${accountId}`,\n credential: null,\n }));\n }\n // If no contract credentials found for this specific account, do not fall back\n // to an unrestricted OS credential prompt here. Returning an empty set lets\n // the caller surface a precise message (no recoverable accounts for this ID),\n // avoiding accidental selection of a passkey from a different account.\n return [];\n}\n\n/**\n * Main account recovery function\n */\nexport async function recoverAccount(\n context: PasskeyManagerContext,\n accountId: AccountId,\n options?: AccountRecoveryHooksOptions,\n reuseCredential?: WebAuthnAuthenticationCredential,\n allowedCredentialIds?: string[]\n): Promise<RecoveryResult> {\n const { onEvent, onError, afterCall } = options || {};\n const { webAuthnManager, nearClient, configs } = context;\n\n onEvent?.({\n step: 1,\n phase: AccountRecoveryPhase.STEP_1_PREPARATION,\n status: AccountRecoveryStatus.PROGRESS,\n message: 'Preparing account recovery...',\n });\n\n try {\n const validation = validateNearAccountId(accountId);\n if (!validation.valid) {\n return handleRecoveryError(accountId, `Invalid NEAR account ID: ${validation.error}`, onError, afterCall);\n }\n\n onEvent?.({\n step: 2,\n phase: AccountRecoveryPhase.STEP_2_WEBAUTHN_AUTHENTICATION,\n status: AccountRecoveryStatus.PROGRESS,\n message: 'Authenticating with contract...',\n });\n\n const credential = await getOrCreateCredential(\n webAuthnManager,\n accountId,\n reuseCredential,\n allowedCredentialIds\n );\n // Cross-check: ensure the authenticator's userHandle maps to the same account,\n // when available. This avoids deriving a key for the wrong account if the user\n // picks an unrelated passkey from the platform chooser.\n const assertion: any = (credential as any)?.response;\n const passkeyAccount = parseAccountIdFromUserHandle(assertion?.userHandle);\n if (passkeyAccount && passkeyAccount !== accountId) {\n return handleRecoveryError(\n accountId,\n `Selected passkey belongs to ${passkeyAccount}, not ${accountId}`,\n onError,\n afterCall\n );\n }\n\n const blockInfo = await nearClient.viewBlock({ finality: 'final' });\n const blockHeight = String(blockInfo.header.height);\n const blockHash = blockInfo.header.hash;\n\n const vrfInputData: VRFInputData = {\n userId: accountId,\n rpId: webAuthnManager.getRpId(),\n blockHeight,\n blockHash,\n };\n\n // Generate VRF keypair first before recovering keypair\n // keypair recovery needs the VRF keypair in memory to derive the WrapKeySeed\n const deterministicVrfResult = await webAuthnManager.deriveVrfKeypair({\n credential,\n nearAccountId: accountId,\n vrfInputData,\n });\n\n if (!deterministicVrfResult.success) {\n throw new Error('Failed to derive deterministic VRF keypair and generate challenge from PRF');\n }\n\n // Now recover the NEAR keypair (uses VRF keypair to derive WrapKeySeed)\n const recoveredKeypair = await webAuthnManager.recoverKeypairFromPasskey(\n credential,\n accountId\n );\n if (!recoveredKeypair.wrapKeySalt) {\n throw new Error('Missing wrapKeySalt in recovered key material; re-register to upgrade vault format.');\n }\n\n // Check if the recovered public key has access to the account\n const hasAccess = await nearClient.viewAccessKey(accountId, recoveredKeypair.publicKey);\n\n if (!hasAccess) {\n return handleRecoveryError(accountId, `Account ${accountId} was not created with this passkey`, onError, afterCall);\n }\n\n const recoveryResult = await performAccountRecovery({\n context,\n accountId,\n publicKey: recoveredKeypair.publicKey,\n encryptedKeypair: {\n encryptedPrivateKey: recoveredKeypair.encryptedPrivateKey,\n chacha20NonceB64u: recoveredKeypair.chacha20NonceB64u,\n wrapKeySalt: recoveredKeypair.wrapKeySalt,\n },\n credential: credential,\n encryptedVrfResult: {\n vrfPublicKey: deterministicVrfResult.vrfPublicKey,\n encryptedVrfKeypair: deterministicVrfResult.encryptedVrfKeypair,\n serverEncryptedVrfKeypair: deterministicVrfResult.serverEncryptedVrfKeypair || undefined,\n },\n onEvent,\n });\n\n onEvent?.({\n step: 5,\n phase: AccountRecoveryPhase.STEP_5_ACCOUNT_RECOVERY_COMPLETE,\n status: AccountRecoveryStatus.SUCCESS,\n message: 'Account recovery completed successfully',\n data: { recoveryResult },\n });\n\n afterCall?.(true, recoveryResult);\n return recoveryResult;\n } catch (error: any) {\n // Clear any VRF session that might have been established during recovery\n try {\n await webAuthnManager.clearVrfSession();\n } catch (clearError) {\n console.warn('Failed to clear VRF session after recovery error:', clearError);\n }\n\n onError?.(error);\n return handleRecoveryError(accountId, error.message, onError, afterCall);\n }\n}\n\n/**\n * Get credential (reuse existing or create new)\n */\nasync function getOrCreateCredential(\n webAuthnManager: WebAuthnManager,\n accountId: AccountId,\n reuseCredential?: WebAuthnAuthenticationCredential,\n allowedCredentialIds?: string[]\n): Promise<WebAuthnAuthenticationCredential> {\n\n if (reuseCredential) {\n const prfResults = reuseCredential.clientExtensionResults?.prf?.results;\n if (!prfResults?.first || !prfResults?.second) {\n throw new Error('Reused credential missing PRF outputs - cannot proceed with recovery');\n }\n return reuseCredential;\n }\n\n const challenge = createRandomVRFChallenge();\n\n return await webAuthnManager.getAuthenticationCredentialsSerializedDualPrf({\n nearAccountId: accountId,\n challenge: challenge as VRFChallenge,\n credentialIds: allowedCredentialIds ?? []\n });\n}\n\n/**\n * Handle recovery error\n */\nfunction handleRecoveryError(\n accountId: AccountId,\n errorMessage: string,\n onError?: (error: Error) => void,\n afterCall?: AfterCall<any>\n): RecoveryResult {\n console.error('[recoverAccount] Error:', errorMessage);\n onError?.(new Error(errorMessage));\n\n const errorResult: RecoveryResult = {\n success: false,\n accountId,\n publicKey: '',\n message: `Recovery failed: ${errorMessage}`,\n error: errorMessage\n };\n\n const result = { success: false, accountId, error: errorMessage } as any;\n afterCall?.(false);\n return errorResult;\n}\n\n/**\n * Perform the actual recovery process\n * Syncs on-chain data and restores local IndexedDB data\n */\nasync function performAccountRecovery({\n context,\n accountId,\n publicKey,\n encryptedKeypair,\n credential,\n encryptedVrfResult,\n onEvent,\n}: {\n context: PasskeyManagerContext,\n accountId: AccountId,\n publicKey: string,\n encryptedKeypair: {\n encryptedPrivateKey: string,\n /**\n * Base64url-encoded AEAD nonce (ChaCha20-Poly1305) for the encrypted private key.\n */\n chacha20NonceB64u: string,\n wrapKeySalt?: string,\n },\n credential: WebAuthnAuthenticationCredential,\n encryptedVrfResult: {\n encryptedVrfKeypair: EncryptedVRFKeypair;\n vrfPublicKey: string\n serverEncryptedVrfKeypair?: ServerEncryptedVrfKeypair\n },\n onEvent?: EventCallback<AccountRecoverySSEEvent>,\n}): Promise<RecoveryResult> {\n\n const { webAuthnManager, nearClient, configs } = context;\n\n try {\n console.debug(`Performing recovery for account: ${accountId}`);\n onEvent?.({\n step: 3,\n phase: AccountRecoveryPhase.STEP_3_SYNC_AUTHENTICATORS_ONCHAIN,\n status: AccountRecoveryStatus.PROGRESS,\n message: 'Syncing authenticators from onchain...',\n });\n\n // 1. Sync on-chain authenticator data\n const contractAuthenticators = await syncAuthenticatorsContractCall(nearClient, configs.contractId, accountId);\n\n // 2. Find the matching authenticator to get the correct device number\n // Serialized auth credential.rawId is already base64url-encoded\n const credentialIdUsed = credential.rawId;\n const matchingAuthenticator = contractAuthenticators.find(auth => auth.credentialId === credentialIdUsed);\n\n if (!matchingAuthenticator) {\n throw new Error(`Could not find authenticator for credential ${credentialIdUsed}`);\n }\n\n const deviceNumber = matchingAuthenticator.authenticator.deviceNumber;\n if (deviceNumber === undefined) {\n throw new Error(`Device number not found for authenticator ${credentialIdUsed}`);\n }\n\n // 3. Restore user data to IndexedDB with correct device number\n // Use the server-encrypted VRF keypair directly from the VRF worker result\n const serverEncryptedVrfKeypairObj = encryptedVrfResult.serverEncryptedVrfKeypair;\n\n await restoreUserData({\n webAuthnManager,\n accountId,\n deviceNumber,\n publicKey,\n encryptedVrfKeypair: encryptedVrfResult.encryptedVrfKeypair,\n serverEncryptedVrfKeypair: serverEncryptedVrfKeypairObj,\n encryptedNearKeypair: encryptedKeypair,\n credential\n });\n\n // 4. Restore only the authenticator used for recovery\n await restoreAuthenticators({\n webAuthnManager,\n accountId,\n contractAuthenticators: [matchingAuthenticator],\n vrfPublicKey: encryptedVrfResult.vrfPublicKey\n // deterministically derived VRF keypair\n });\n\n onEvent?.({\n step: 4,\n phase: AccountRecoveryPhase.STEP_4_AUTHENTICATOR_SAVED,\n status: AccountRecoveryStatus.SUCCESS,\n message: 'Restored Passkey authenticator...',\n });\n\n // 5. Unlock VRF keypair in memory for immediate use\n console.debug('Unlocking VRF keypair in memory after account recovery');\n const unlockResult = await webAuthnManager.unlockVRFKeypair({\n nearAccountId: accountId,\n encryptedVrfKeypair: encryptedVrfResult.encryptedVrfKeypair,\n credential: credential,\n });\n\n if (!unlockResult.success) {\n console.warn('Failed to unlock VRF keypair after recovery:', unlockResult.error);\n // Don't throw error here - recovery was successful, but VRF unlock failed\n } else {\n console.debug('VRF keypair unlocked successfully after account recovery');\n }\n\n // 6. Initialize current user only after a successful unlock\n try {\n await webAuthnManager.initializeCurrentUser(accountId, context.nearClient);\n } catch (initErr) {\n console.warn('Failed to initialize current user after recovery:', initErr);\n }\n\n return {\n success: true,\n accountId,\n publicKey,\n message: 'Account successfully recovered',\n };\n\n } catch (error: any) {\n console.error('[performAccountRecovery] Error:', error);\n throw new Error(`Recovery process failed: ${error.message}`);\n }\n}\n\n/** Stored authenticator onchain uses snake case */\nexport interface ContractStoredAuthenticator {\n // V4 contract fields (snake_case JSON)\n credential_public_key: number[] | Uint8Array;\n transports?: AuthenticatorTransport[] | null;\n registered: string; // ISO timestamp (legacy contracts may return numeric timestamp string)\n expected_rp_id?: string;\n origin_policy?: OriginPolicyInput;\n user_verification?: UserVerificationPolicy;\n vrf_public_keys?: Array<number[] | Uint8Array> | string[];\n device_number: number; // 1-indexed for UX\n near_public_key?: string;\n}\n\nasync function restoreUserData({\n webAuthnManager,\n accountId,\n deviceNumber,\n publicKey,\n encryptedVrfKeypair,\n serverEncryptedVrfKeypair,\n encryptedNearKeypair,\n credential\n}: {\n webAuthnManager: WebAuthnManager,\n accountId: AccountId,\n deviceNumber: number,\n publicKey: string,\n encryptedVrfKeypair: EncryptedVRFKeypair,\n serverEncryptedVrfKeypair?: ServerEncryptedVrfKeypair,\n encryptedNearKeypair: {\n encryptedPrivateKey: string;\n /** Base64url-encoded AEAD nonce (ChaCha20-Poly1305) for the encrypted private key */\n chacha20NonceB64u: string;\n wrapKeySalt?: string;\n },\n credential: WebAuthnAuthenticationCredential\n}) {\n const existingUser = await webAuthnManager.getUserByDevice(accountId, deviceNumber);\n if (!encryptedNearKeypair.wrapKeySalt) {\n throw new Error('Missing wrapKeySalt in recovered key material; re-register to upgrade vault format.');\n }\n const wrapKeySalt = encryptedNearKeypair.wrapKeySalt;\n\n const chacha20NonceB64u = encryptedNearKeypair.chacha20NonceB64u;\n if (!chacha20NonceB64u) {\n throw new Error('Missing chacha20NonceB64u in recovered key material; cannot store encrypted NEAR key.');\n }\n\n // Store the encrypted NEAR keypair in the encrypted keys database\n await IndexedDBManager.nearKeysDB.storeEncryptedKey({\n nearAccountId: accountId,\n deviceNumber,\n encryptedData: encryptedNearKeypair.encryptedPrivateKey,\n chacha20NonceB64u,\n wrapKeySalt,\n version: 2,\n timestamp: Date.now()\n });\n\n if (!existingUser) {\n await webAuthnManager.registerUser({\n nearAccountId: accountId,\n deviceNumber,\n version: 2,\n clientNearPublicKey: publicKey,\n lastUpdated: Date.now(),\n passkeyCredential: {\n id: credential.id,\n rawId: credential.rawId\n },\n encryptedVrfKeypair: {\n encryptedVrfDataB64u: encryptedVrfKeypair.encryptedVrfDataB64u,\n chacha20NonceB64u: encryptedVrfKeypair.chacha20NonceB64u,\n },\n serverEncryptedVrfKeypair,\n });\n } else {\n await webAuthnManager.storeUserData({\n nearAccountId: accountId,\n clientNearPublicKey: publicKey,\n lastUpdated: Date.now(),\n passkeyCredential: existingUser.passkeyCredential,\n encryptedVrfKeypair: {\n encryptedVrfDataB64u: encryptedVrfKeypair.encryptedVrfDataB64u,\n chacha20NonceB64u: encryptedVrfKeypair.chacha20NonceB64u,\n },\n serverEncryptedVrfKeypair,\n deviceNumber\n });\n }\n}\n\nasync function restoreAuthenticators({\n webAuthnManager,\n accountId,\n contractAuthenticators,\n vrfPublicKey\n}: {\n webAuthnManager: WebAuthnManager,\n accountId: AccountId,\n contractAuthenticators: {\n credentialId: string,\n authenticator: StoredAuthenticator\n }[],\n vrfPublicKey: string\n}) {\n for (const { credentialId, authenticator } of contractAuthenticators) {\n const credentialPublicKey = authenticator.credentialPublicKey;\n\n // Fix transport processing: filter out undefined values and provide fallback\n const validTransports = authenticator.transports.filter((transport) =>\n transport !== undefined && transport !== null && typeof transport === 'string'\n );\n\n // If no valid transports, default to 'internal' for platform authenticators\n const transports = validTransports?.length > 0 ? validTransports : ['internal'];\n\n // Extract device number from contract authenticator data (now camelCase)\n const deviceNumber = authenticator.deviceNumber;\n console.debug(\"Restoring authenticator with device number:\", deviceNumber, authenticator);\n\n await webAuthnManager.storeAuthenticator({\n nearAccountId: accountId,\n credentialId: credentialId,\n credentialPublicKey,\n transports,\n name: `Recovered Device ${deviceNumber} Passkey`,\n registered: authenticator.registered.toISOString(),\n syncedAt: new Date().toISOString(),\n vrfPublicKey,\n deviceNumber // Pass the device number from contract data\n });\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"recoverAccount.js","names":["validateNearAccountId","toAccountId","createRandomVRFChallenge","inferredAccountId: string | null","assertion: any","parseAccountIdFromUserHandle","option: PasskeyOption","error: any","getCredentialIdsContractCall","AccountRecoveryPhase","AccountRecoveryStatus","vrfInputData: VRFInputData","errorResult: RecoveryResult","syncAuthenticatorsContractCall","IndexedDBManager"],"sources":["../../../../src/core/TatchiPasskey/recoverAccount.ts"],"sourcesContent":["import type { AfterCall, AccountRecoverySSEEvent, EventCallback } from '../types/sdkSentEvents';\nimport { AccountRecoveryPhase, AccountRecoveryStatus, AccountRecoveryHooksOptions } from '../types/sdkSentEvents';\nimport type { PasskeyManagerContext } from './index';\nimport type {\n AccountId,\n StoredAuthenticator,\n VRFChallenge,\n WebAuthnAuthenticationCredential\n} from '../types';\nimport type { EncryptedVRFKeypair, ServerEncryptedVrfKeypair } from '../types/vrf-worker';\nimport { validateNearAccountId } from '../../utils/validation';\nimport { parseAccountIdFromUserHandle } from '../WebAuthnManager/userHandle';\nimport { toAccountId } from '../types/accountIds';\nimport { createRandomVRFChallenge } from '../types/vrf-worker';\nimport { WebAuthnManager } from '../WebAuthnManager';\nimport { IndexedDBManager } from '../IndexedDBManager';\nimport type { VRFInputData } from '../types/vrf-worker';\nimport type { OriginPolicyInput, UserVerificationPolicy } from '../types/authenticatorOptions';\nimport {\n getCredentialIdsContractCall,\n syncAuthenticatorsContractCall\n} from '../rpcCalls';\n\n/**\n * Use case:\n * Suppose a user accidentally clears their browser's indexedDB, and deletes their:\n * - encrypted NEAR keypair\n * - encrypted VRF keypair\n * - webauthn authenticator\n * Provide a way for the user to recover their account from onchain authenticator information with their Passkey.\n */\n\nexport interface RecoveryResult {\n success: boolean;\n accountId: string;\n publicKey: string;\n message: string;\n error?: string;\n loginState?: {\n isLoggedIn: boolean;\n vrfActive: boolean;\n vrfSessionDuration?: number;\n };\n}\n\nexport interface AccountLookupResult {\n accountId: string;\n publicKey: string;\n hasAccess: boolean;\n}\n\nexport interface PasskeyOption {\n credentialId: string;\n accountId: AccountId | null;\n publicKey: string;\n displayName: string;\n credential: WebAuthnAuthenticationCredential | null;\n}\n\n// Public-facing passkey option without sensitive credential data\nexport interface PasskeyOptionWithoutCredential {\n credentialId: string;\n accountId: string | null;\n publicKey: string;\n displayName: string;\n}\n\n// Internal selection identifier for secure credential lookup\nexport interface PasskeySelection {\n credentialId: string;\n accountId: string | null;\n}\n\n/**\n * Account recovery flow with credential encapsulation\n *\n * Usage:\n * ```typescript\n * const flow = new AccountRecoveryFlow(context);\n * const options = await flow.discover(); // Get safe display options\n * // ... user selects account in UI ...\n * const result = await flow.recover({ credentialId, accountId }); // Execute recovery\n * ```\n */\nexport class AccountRecoveryFlow {\n private context: PasskeyManagerContext;\n private options?: AccountRecoveryHooksOptions;\n private availableAccounts?: PasskeyOption[]; // Full options with credentials (private)\n private phase: 'idle' | 'discovering' | 'ready' | 'recovering' | 'complete' | 'error' = 'idle';\n private error?: Error;\n\n constructor(context: PasskeyManagerContext, options?: AccountRecoveryHooksOptions) {\n this.context = context;\n this.options = options;\n }\n\n /**\n * Phase 1: Discover available accounts\n * Returns safe display data without exposing credentials to UI\n */\n async discover(accountId: string): Promise<PasskeyOptionWithoutCredential[]> {\n try {\n this.phase = 'discovering';\n const hasValidAccount = !!accountId && validateNearAccountId(accountId).valid;\n\n if (hasValidAccount) {\n const nearAccountId = toAccountId(accountId);\n // Contract-based lookup; no WebAuthn prompt during discovery\n this.availableAccounts = await getRecoverableAccounts(this.context, nearAccountId);\n } else {\n // Fallback discovery without a typed account: prompt once to select a passkey\n // Then infer the accountId from userHandle (set at registration time)\n const challenge = createRandomVRFChallenge();\n const credential = await this.context.webAuthnManager.getAuthenticationCredentialsSerializedDualPrf({\n // Account is unknown here – salts aren't used downstream for discovery\n nearAccountId: '' as any,\n challenge: challenge as VRFChallenge,\n credentialIds: [],\n });\n\n // Try to infer accountId from userHandle\n let inferredAccountId: string | null = null;\n try {\n const assertion: any = credential.response as any;\n inferredAccountId = parseAccountIdFromUserHandle(assertion?.userHandle);\n } catch {}\n\n const option: PasskeyOption = {\n credentialId: credential.id,\n accountId: inferredAccountId ? (inferredAccountId as any as AccountId) : null,\n publicKey: '',\n displayName: inferredAccountId ? `${inferredAccountId}` : 'Recovered passkey',\n credential,\n };\n this.availableAccounts = [option];\n }\n\n if (!this.availableAccounts || this.availableAccounts.length === 0) {\n console.warn('No recoverable accounts found for this passkey');\n } else {\n console.debug(`AccountRecoveryFlow: Found ${this.availableAccounts.length} recoverable accounts`);\n }\n\n this.phase = 'ready';\n\n // Return safe options without credentials for UI display\n return this.availableAccounts.map(option => ({\n credentialId: option.credentialId,\n accountId: option.accountId,\n publicKey: option.publicKey,\n displayName: option.displayName\n }));\n\n } catch (error: any) {\n this.phase = 'error';\n this.error = error;\n console.error('AccountRecoveryFlow: Discovery failed:', error);\n throw error;\n }\n }\n\n /**\n * Phase 2: Execute recovery with user selection\n * Securely looks up credential based on selection\n */\n async recover(selection: PasskeySelection): Promise<RecoveryResult> {\n if (this.phase !== 'ready') {\n throw new Error(`Cannot recover - flow is in ${this.phase} phase. Call discover() first.`);\n }\n if (!this.availableAccounts) {\n throw new Error('No available accounts found. Call discover() first.');\n }\n\n try {\n this.phase = 'recovering';\n console.debug(`AccountRecoveryFlow: Recovering account: ${selection.accountId}`);\n\n // Securely lookup the full option with credential\n const selectedOption = this.availableAccounts.find(\n option => option.credentialId === selection.credentialId &&\n option.accountId === selection.accountId\n );\n\n if (!selectedOption) {\n throw new Error('Invalid selection - account not found in available options');\n }\n if (!selectedOption.accountId) {\n // Attempt a one-time re-prompt to infer accountId from userHandle for this credential\n try {\n const challenge = createRandomVRFChallenge();\n const cred = await this.context.webAuthnManager.getAuthenticationCredentialsSerializedDualPrf({\n nearAccountId: '' as any,\n challenge: challenge as VRFChallenge,\n credentialIds: selectedOption.credentialId && selectedOption.credentialId !== 'manual-input'\n ? [selectedOption.credentialId]\n : [],\n });\n const assertion: any = cred.response as any;\n const maybeAccount = parseAccountIdFromUserHandle(assertion?.userHandle);\n if (maybeAccount) {\n selectedOption.accountId = maybeAccount as any as AccountId;\n }\n } catch {}\n if (!selectedOption.accountId) {\n throw new Error('Invalid account selection - no account ID provided');\n }\n }\n\n // If multiple credentials exist for this account, allow the platform chooser UI\n // by passing all known credential IDs for this account as allowCredentials.\n const allowList = (() => {\n try {\n if (!this.availableAccounts) return undefined;\n if (!selectedOption.credentialId || selectedOption.credentialId === 'manual-input') return undefined;\n const ids = this.availableAccounts\n .filter(opt => opt.accountId === selectedOption.accountId && opt.credentialId && opt.credentialId !== 'manual-input')\n .map(opt => opt.credentialId);\n const uniq = Array.from(new Set(ids));\n return uniq.length > 0 ? uniq : undefined;\n } catch { return undefined; }\n })();\n\n const recoveryResult = await recoverAccount(\n this.context,\n selectedOption.accountId,\n this.options,\n undefined,\n allowList\n );\n\n this.phase = 'complete';\n return recoveryResult;\n\n } catch (error: any) {\n this.phase = 'error';\n this.error = error;\n console.error('AccountRecoveryFlow: Recovery failed:', error);\n throw error;\n }\n }\n\n /**\n * Get current flow state (safe display data only)\n */\n getState() {\n // Convert internal accounts to safe display format\n const safeAccounts = this.availableAccounts?.map(option => ({\n credentialId: option.credentialId,\n accountId: option.accountId,\n publicKey: option.publicKey,\n displayName: option.displayName\n }));\n\n return {\n phase: this.phase,\n availableAccounts: safeAccounts,\n error: this.error,\n isReady: this.phase === 'ready',\n isComplete: this.phase === 'complete',\n hasError: this.phase === 'error'\n };\n }\n\n /**\n * Reset flow to initial state\n */\n reset() {\n this.phase = 'idle';\n this.availableAccounts = undefined;\n this.error = undefined;\n }\n}\n\n/**\n * Get available passkeys for account recovery\n */\nasync function getRecoverableAccounts(\n context: PasskeyManagerContext,\n accountId: AccountId\n): Promise<PasskeyOption[]> {\n const availablePasskeys = await getAvailablePasskeysForDomain(context, accountId);\n return availablePasskeys.filter(passkey => passkey.accountId !== null);\n}\n\n/**\n * Discover passkeys for domain using contract-based lookup\n */\nasync function getAvailablePasskeysForDomain(\n context: PasskeyManagerContext,\n accountId: AccountId\n): Promise<PasskeyOption[]> {\n const { nearClient, configs } = context;\n\n const credentialIds = await getCredentialIdsContractCall(nearClient, configs.contractId, accountId);\n\n // Do not invoke WebAuthn here; just return display options bound to credential IDs\n if (credentialIds.length > 0) {\n return credentialIds.map((credentialId, idx) => ({\n credentialId,\n accountId,\n publicKey: '',\n displayName: credentialIds.length > 1 ? `${accountId} (passkey ${idx + 1})` : `${accountId}`,\n credential: null,\n }));\n }\n // If no contract credentials found for this specific account, do not fall back\n // to an unrestricted OS credential prompt here. Returning an empty set lets\n // the caller surface a precise message (no recoverable accounts for this ID),\n // avoiding accidental selection of a passkey from a different account.\n return [];\n}\n\n/**\n * Main account recovery function\n */\nexport async function recoverAccount(\n context: PasskeyManagerContext,\n accountId: AccountId,\n options?: AccountRecoveryHooksOptions,\n reuseCredential?: WebAuthnAuthenticationCredential,\n allowedCredentialIds?: string[]\n): Promise<RecoveryResult> {\n const { onEvent, onError, afterCall } = options || {};\n const { webAuthnManager, nearClient, configs } = context;\n\n onEvent?.({\n step: 1,\n phase: AccountRecoveryPhase.STEP_1_PREPARATION,\n status: AccountRecoveryStatus.PROGRESS,\n message: 'Preparing account recovery...',\n });\n\n try {\n const validation = validateNearAccountId(accountId);\n if (!validation.valid) {\n return handleRecoveryError(accountId, `Invalid NEAR account ID: ${validation.error}`, onError, afterCall);\n }\n\n onEvent?.({\n step: 2,\n phase: AccountRecoveryPhase.STEP_2_WEBAUTHN_AUTHENTICATION,\n status: AccountRecoveryStatus.PROGRESS,\n message: 'Authenticating with contract...',\n });\n\n const credential = await getOrCreateCredential(\n webAuthnManager,\n accountId,\n reuseCredential,\n allowedCredentialIds\n );\n // Cross-check: ensure the authenticator's userHandle maps to the same account,\n // when available. This avoids deriving a key for the wrong account if the user\n // picks an unrelated passkey from the platform chooser.\n const assertion: any = (credential as any)?.response;\n const passkeyAccount = parseAccountIdFromUserHandle(assertion?.userHandle);\n if (passkeyAccount && passkeyAccount !== accountId) {\n return handleRecoveryError(\n accountId,\n `Selected passkey belongs to ${passkeyAccount}, not ${accountId}`,\n onError,\n afterCall\n );\n }\n\n const blockInfo = await nearClient.viewBlock({ finality: 'final' });\n const blockHeight = String(blockInfo.header.height);\n const blockHash = blockInfo.header.hash;\n\n const vrfInputData: VRFInputData = {\n userId: accountId,\n rpId: webAuthnManager.getRpId(),\n blockHeight,\n blockHash,\n };\n\n // Generate VRF keypair first before recovering keypair\n // keypair recovery needs the VRF keypair in memory to derive the WrapKeySeed\n const deterministicVrfResult = await webAuthnManager.deriveVrfKeypair({\n credential,\n nearAccountId: accountId,\n vrfInputData,\n });\n\n if (!deterministicVrfResult.success) {\n throw new Error('Failed to derive deterministic VRF keypair and generate challenge from PRF');\n }\n\n // Now recover the NEAR keypair (uses VRF keypair to derive WrapKeySeed)\n const recoveredKeypair = await webAuthnManager.recoverKeypairFromPasskey(\n credential,\n accountId\n );\n if (!recoveredKeypair.wrapKeySalt) {\n throw new Error('Missing wrapKeySalt in recovered key material; re-register to upgrade vault format.');\n }\n\n // Check if the recovered public key has access to the account\n const hasAccess = await nearClient.viewAccessKey(accountId, recoveredKeypair.publicKey);\n\n if (!hasAccess) {\n return handleRecoveryError(accountId, `Account ${accountId} was not created with this passkey`, onError, afterCall);\n }\n\n const recoveryResult = await performAccountRecovery({\n context,\n accountId,\n publicKey: recoveredKeypair.publicKey,\n encryptedKeypair: {\n encryptedPrivateKey: recoveredKeypair.encryptedPrivateKey,\n chacha20NonceB64u: recoveredKeypair.chacha20NonceB64u,\n wrapKeySalt: recoveredKeypair.wrapKeySalt,\n },\n credential: credential,\n encryptedVrfResult: {\n vrfPublicKey: deterministicVrfResult.vrfPublicKey,\n encryptedVrfKeypair: deterministicVrfResult.encryptedVrfKeypair,\n serverEncryptedVrfKeypair: deterministicVrfResult.serverEncryptedVrfKeypair || undefined,\n },\n onEvent,\n });\n\n onEvent?.({\n step: 5,\n phase: AccountRecoveryPhase.STEP_5_ACCOUNT_RECOVERY_COMPLETE,\n status: AccountRecoveryStatus.SUCCESS,\n message: 'Account recovery completed successfully',\n data: { recoveryResult },\n });\n\n afterCall?.(true, recoveryResult);\n return recoveryResult;\n } catch (error: any) {\n // Clear any VRF session that might have been established during recovery\n try {\n await webAuthnManager.clearVrfSession();\n } catch (clearError) {\n console.warn('Failed to clear VRF session after recovery error:', clearError);\n }\n\n onError?.(error);\n return handleRecoveryError(accountId, error.message, onError, afterCall);\n }\n}\n\n/**\n * Get credential (reuse existing or create new)\n */\nasync function getOrCreateCredential(\n webAuthnManager: WebAuthnManager,\n accountId: AccountId,\n reuseCredential?: WebAuthnAuthenticationCredential,\n allowedCredentialIds?: string[]\n): Promise<WebAuthnAuthenticationCredential> {\n\n if (reuseCredential) {\n const prfResults = reuseCredential.clientExtensionResults?.prf?.results;\n if (!prfResults?.first || !prfResults?.second) {\n throw new Error('Reused credential missing PRF outputs - cannot proceed with recovery');\n }\n return reuseCredential;\n }\n\n const challenge = createRandomVRFChallenge();\n\n return await webAuthnManager.getAuthenticationCredentialsSerializedDualPrf({\n nearAccountId: accountId,\n challenge: challenge as VRFChallenge,\n credentialIds: allowedCredentialIds ?? []\n });\n}\n\n/**\n * Handle recovery error\n */\nfunction handleRecoveryError(\n accountId: AccountId,\n errorMessage: string,\n onError?: (error: Error) => void,\n afterCall?: AfterCall<any>\n): RecoveryResult {\n console.error('[recoverAccount] Error:', errorMessage);\n onError?.(new Error(errorMessage));\n\n const errorResult: RecoveryResult = {\n success: false,\n accountId,\n publicKey: '',\n message: `Recovery failed: ${errorMessage}`,\n error: errorMessage\n };\n\n const result = { success: false, accountId, error: errorMessage } as any;\n afterCall?.(false);\n return errorResult;\n}\n\n/**\n * Perform the actual recovery process\n * Syncs on-chain data and restores local IndexedDB data\n */\nasync function performAccountRecovery({\n context,\n accountId,\n publicKey,\n encryptedKeypair,\n credential,\n encryptedVrfResult,\n onEvent,\n}: {\n context: PasskeyManagerContext,\n accountId: AccountId,\n publicKey: string,\n encryptedKeypair: {\n encryptedPrivateKey: string,\n /**\n * Base64url-encoded AEAD nonce (ChaCha20-Poly1305) for the encrypted private key.\n */\n chacha20NonceB64u: string,\n wrapKeySalt?: string,\n },\n credential: WebAuthnAuthenticationCredential,\n encryptedVrfResult: {\n encryptedVrfKeypair: EncryptedVRFKeypair;\n vrfPublicKey: string\n serverEncryptedVrfKeypair?: ServerEncryptedVrfKeypair\n },\n onEvent?: EventCallback<AccountRecoverySSEEvent>,\n}): Promise<RecoveryResult> {\n\n const { webAuthnManager, nearClient, configs } = context;\n\n try {\n console.debug(`Performing recovery for account: ${accountId}`);\n onEvent?.({\n step: 3,\n phase: AccountRecoveryPhase.STEP_3_SYNC_AUTHENTICATORS_ONCHAIN,\n status: AccountRecoveryStatus.PROGRESS,\n message: 'Syncing authenticators from onchain...',\n });\n\n // 1. Sync on-chain authenticator data\n const contractAuthenticators = await syncAuthenticatorsContractCall(nearClient, configs.contractId, accountId);\n\n // 2. Find the matching authenticator to get the correct device number\n // Serialized auth credential.rawId is already base64url-encoded\n const credentialIdUsed = credential.rawId;\n const matchingAuthenticator = contractAuthenticators.find(auth => auth.credentialId === credentialIdUsed);\n\n if (!matchingAuthenticator) {\n throw new Error(`Could not find authenticator for credential ${credentialIdUsed}`);\n }\n\n const deviceNumber = matchingAuthenticator.authenticator.deviceNumber;\n if (deviceNumber === undefined) {\n throw new Error(`Device number not found for authenticator ${credentialIdUsed}`);\n }\n\n // 3. Restore user data to IndexedDB with correct device number\n // Use the server-encrypted VRF keypair directly from the VRF worker result\n const serverEncryptedVrfKeypairObj = encryptedVrfResult.serverEncryptedVrfKeypair;\n\n await restoreUserData({\n webAuthnManager,\n accountId,\n deviceNumber,\n publicKey,\n encryptedVrfKeypair: encryptedVrfResult.encryptedVrfKeypair,\n serverEncryptedVrfKeypair: serverEncryptedVrfKeypairObj,\n encryptedNearKeypair: encryptedKeypair,\n credential\n });\n\n // 4. Restore only the authenticator used for recovery\n await restoreAuthenticators({\n webAuthnManager,\n accountId,\n contractAuthenticators: [matchingAuthenticator],\n vrfPublicKey: encryptedVrfResult.vrfPublicKey\n // deterministically derived VRF keypair\n });\n\n onEvent?.({\n step: 4,\n phase: AccountRecoveryPhase.STEP_4_AUTHENTICATOR_SAVED,\n status: AccountRecoveryStatus.SUCCESS,\n message: 'Restored Passkey authenticator...',\n });\n\n // 5. Unlock VRF keypair in memory for immediate use\n console.debug('Unlocking VRF keypair in memory after account recovery');\n const unlockResult = await webAuthnManager.unlockVRFKeypair({\n nearAccountId: accountId,\n encryptedVrfKeypair: encryptedVrfResult.encryptedVrfKeypair,\n credential: credential,\n });\n\n if (!unlockResult.success) {\n console.warn('Failed to unlock VRF keypair after recovery:', unlockResult.error);\n // Don't throw error here - recovery was successful, but VRF unlock failed\n } else {\n console.debug('VRF keypair unlocked successfully after account recovery');\n }\n\n // 6. Initialize current user only after a successful unlock\n try {\n await webAuthnManager.initializeCurrentUser(accountId, context.nearClient);\n } catch (initErr) {\n console.warn('Failed to initialize current user after recovery:', initErr);\n }\n\n return {\n success: true,\n accountId,\n publicKey,\n message: 'Account successfully recovered',\n };\n\n } catch (error: any) {\n console.error('[performAccountRecovery] Error:', error);\n throw new Error(`Recovery process failed: ${error.message}`);\n }\n}\n\n/** Stored authenticator onchain uses snake case */\nexport interface ContractStoredAuthenticator {\n // V4 contract fields (snake_case JSON)\n credential_public_key: number[] | Uint8Array;\n transports?: AuthenticatorTransport[] | null;\n registered: string; // ISO timestamp (legacy contracts may return numeric timestamp string)\n expected_rp_id?: string;\n origin_policy?: OriginPolicyInput;\n user_verification?: UserVerificationPolicy;\n vrf_public_keys?: Array<number[] | Uint8Array> | string[];\n device_number: number; // 1-indexed for UX\n near_public_key?: string;\n}\n\nasync function restoreUserData({\n webAuthnManager,\n accountId,\n deviceNumber,\n publicKey,\n encryptedVrfKeypair,\n serverEncryptedVrfKeypair,\n encryptedNearKeypair,\n credential\n}: {\n webAuthnManager: WebAuthnManager,\n accountId: AccountId,\n deviceNumber: number,\n publicKey: string,\n encryptedVrfKeypair: EncryptedVRFKeypair,\n serverEncryptedVrfKeypair?: ServerEncryptedVrfKeypair,\n encryptedNearKeypair: {\n encryptedPrivateKey: string;\n /** Base64url-encoded AEAD nonce (ChaCha20-Poly1305) for the encrypted private key */\n chacha20NonceB64u: string;\n wrapKeySalt?: string;\n },\n credential: WebAuthnAuthenticationCredential\n}) {\n const existingUser = await webAuthnManager.getUserByDevice(accountId, deviceNumber);\n if (!encryptedNearKeypair.wrapKeySalt) {\n throw new Error('Missing wrapKeySalt in recovered key material; re-register to upgrade vault format.');\n }\n const wrapKeySalt = encryptedNearKeypair.wrapKeySalt;\n\n const chacha20NonceB64u = encryptedNearKeypair.chacha20NonceB64u;\n if (!chacha20NonceB64u) {\n throw new Error('Missing chacha20NonceB64u in recovered key material; cannot store encrypted NEAR key.');\n }\n\n // Store the encrypted NEAR keypair in the encrypted keys database\n await IndexedDBManager.nearKeysDB.storeEncryptedKey({\n nearAccountId: accountId,\n deviceNumber,\n encryptedData: encryptedNearKeypair.encryptedPrivateKey,\n chacha20NonceB64u,\n wrapKeySalt,\n version: 2,\n timestamp: Date.now()\n });\n\n if (!existingUser) {\n await webAuthnManager.registerUser({\n nearAccountId: accountId,\n deviceNumber,\n version: 2,\n clientNearPublicKey: publicKey,\n lastUpdated: Date.now(),\n passkeyCredential: {\n id: credential.id,\n rawId: credential.rawId\n },\n encryptedVrfKeypair: {\n encryptedVrfDataB64u: encryptedVrfKeypair.encryptedVrfDataB64u,\n chacha20NonceB64u: encryptedVrfKeypair.chacha20NonceB64u,\n },\n serverEncryptedVrfKeypair,\n });\n } else {\n await webAuthnManager.storeUserData({\n nearAccountId: accountId,\n clientNearPublicKey: publicKey,\n lastUpdated: Date.now(),\n passkeyCredential: existingUser.passkeyCredential,\n encryptedVrfKeypair: {\n encryptedVrfDataB64u: encryptedVrfKeypair.encryptedVrfDataB64u,\n chacha20NonceB64u: encryptedVrfKeypair.chacha20NonceB64u,\n },\n serverEncryptedVrfKeypair,\n deviceNumber\n });\n }\n}\n\nasync function restoreAuthenticators({\n webAuthnManager,\n accountId,\n contractAuthenticators,\n vrfPublicKey\n}: {\n webAuthnManager: WebAuthnManager,\n accountId: AccountId,\n contractAuthenticators: {\n credentialId: string,\n authenticator: StoredAuthenticator\n }[],\n vrfPublicKey: string\n}) {\n for (const { credentialId, authenticator } of contractAuthenticators) {\n const credentialPublicKey = authenticator.credentialPublicKey;\n\n // Fix transport processing: filter out undefined values and provide fallback\n const validTransports = authenticator.transports.filter((transport) =>\n transport !== undefined && transport !== null && typeof transport === 'string'\n );\n\n // If no valid transports, default to 'internal' for platform authenticators\n const transports = validTransports?.length > 0 ? validTransports : ['internal'];\n\n // Extract device number from contract authenticator data (now camelCase)\n const deviceNumber = authenticator.deviceNumber;\n console.debug(\"Restoring authenticator with device number:\", deviceNumber, authenticator);\n\n await webAuthnManager.storeAuthenticator({\n nearAccountId: accountId,\n credentialId: credentialId,\n credentialPublicKey,\n transports,\n name: `Recovered Device ${deviceNumber} Passkey`,\n registered: authenticator.registered.toISOString(),\n syncedAt: new Date().toISOString(),\n vrfPublicKey,\n deviceNumber // Pass the device number from contract data\n });\n }\n}\n"],"mappings":"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"}
@@ -3,8 +3,30 @@ const require_sdkSentEvents = require('../types/sdkSentEvents.js');
3
3
 
4
4
  //#region src/core/TatchiPasskey/relay.ts
5
5
  require_sdkSentEvents.init_sdkSentEvents();
6
+ const toNumberArray = (value) => Array.isArray(value) ? value : Array.from(value);
7
+ const normalizeSignedDelegateForRelay = (signedDelegate) => {
8
+ const delegateAction = signedDelegate.delegateAction;
9
+ const signature = signedDelegate.signature;
10
+ return {
11
+ delegateAction: {
12
+ ...delegateAction,
13
+ publicKey: {
14
+ ...delegateAction.publicKey,
15
+ keyData: toNumberArray(delegateAction.publicKey.keyData)
16
+ }
17
+ },
18
+ signature: {
19
+ ...signature,
20
+ signatureData: toNumberArray(signature.signatureData)
21
+ }
22
+ };
23
+ };
6
24
  async function sendDelegateActionViaRelayer(args) {
7
25
  const { url, payload, signal, options } = args;
26
+ const normalizedPayload = {
27
+ ...payload,
28
+ signedDelegate: normalizeSignedDelegateForRelay(payload.signedDelegate)
29
+ };
8
30
  const emit = (event) => options?.onEvent?.(event);
9
31
  const emitError = (message) => {
10
32
  emit({
@@ -26,7 +48,7 @@ async function sendDelegateActionViaRelayer(args) {
26
48
  res = await fetch(url, {
27
49
  method: "POST",
28
50
  headers: { "content-type": "application/json" },
29
- body: JSON.stringify(payload),
51
+ body: JSON.stringify(normalizedPayload),
30
52
  signal
31
53
  });
32
54
  } catch (err) {
@@ -1 +1 @@
1
- {"version":3,"file":"relay.js","names":["ActionPhase","ActionStatus","res: Response","err: unknown","response: DelegateRelayResult","response","json: any"],"sources":["../../../../src/core/TatchiPasskey/relay.ts"],"sourcesContent":["import { ActionPhase, ActionStatus, type ActionSSEEvent, type DelegateRelayHooksOptions } from '../types/sdkSentEvents';\nimport type { DelegateRelayResult } from '../types/tatchi';\nimport type { SignedDelegate } from '../types/delegate';\n\nexport interface RelayDelegateRequest {\n hash: string;\n signedDelegate: SignedDelegate;\n}\n\nexport async function sendDelegateActionViaRelayer(args: {\n url: string;\n payload: RelayDelegateRequest;\n signal?: AbortSignal;\n options?: DelegateRelayHooksOptions;\n}): Promise<DelegateRelayResult> {\n\n const { url, payload, signal, options } = args;\n\n const emit = (event: ActionSSEEvent) => options?.onEvent?.(event);\n const emitError = (message: string) => {\n emit({\n step: 0,\n phase: ActionPhase.ACTION_ERROR,\n status: ActionStatus.ERROR,\n message,\n error: message,\n });\n };\n\n emit({\n step: 7,\n phase: ActionPhase.STEP_7_BROADCASTING,\n status: ActionStatus.PROGRESS,\n message: 'Submitting delegate to relayer...',\n });\n\n let res: Response;\n try {\n res = await fetch(url, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(payload),\n signal,\n });\n } catch (err: unknown) {\n const error = err instanceof Error ? err : new Error(String(err));\n options?.onError?.(error);\n emitError(error.message);\n await options?.afterCall?.(false);\n throw error;\n }\n\n if (!res.ok) {\n const response: DelegateRelayResult = {\n ok: false,\n error: `Relayer HTTP ${res.status}`,\n };\n options?.onError?.(new Error(response.error));\n emitError(response.error!);\n await options?.afterCall?.(false);\n return response;\n }\n\n let json: any;\n try {\n json = await res.json();\n } catch (err: unknown) {\n const response: DelegateRelayResult = {\n ok: false,\n error: 'Relayer returned non-JSON response',\n };\n const error = err instanceof Error ? err : new Error(String(err));\n options?.onError?.(error);\n emitError(response.error!);\n await options?.afterCall?.(false);\n return response;\n }\n\n const response: DelegateRelayResult = {\n ok: Boolean(json?.ok ?? true),\n relayerTxHash: json?.relayerTxHash ?? json?.transactionId ?? json?.txHash,\n status: json?.status,\n outcome: json?.outcome,\n error: json?.error,\n };\n\n const success = response.ok !== false;\n if (success) {\n emit({\n step: 8,\n phase: ActionPhase.STEP_8_ACTION_COMPLETE,\n status: ActionStatus.SUCCESS,\n message: 'Delegate relayed successfully',\n });\n await options?.afterCall?.(true, response);\n } else {\n const message = response.error || 'Relayer execution failed';\n options?.onError?.(new Error(message));\n emitError(message);\n await options?.afterCall?.(false);\n }\n\n return response;\n}\n"],"mappings":";;;;;AASA,eAAsB,6BAA6B,MAKlB;CAE/B,MAAM,EAAE,KAAK,SAAS,QAAQ,YAAY;CAE1C,MAAM,QAAQ,UAA0B,SAAS,UAAU;CAC3D,MAAM,aAAa,YAAoB;AACrC,OAAK;GACH,MAAM;GACN,OAAOA,kCAAY;GACnB,QAAQC,mCAAa;GACrB;GACA,OAAO;;;AAIX,MAAK;EACH,MAAM;EACN,OAAOD,kCAAY;EACnB,QAAQC,mCAAa;EACrB,SAAS;;CAGX,IAAIC;AACJ,KAAI;AACF,QAAM,MAAM,MAAM,KAAK;GACrB,QAAQ;GACR,SAAS,EAAE,gBAAgB;GAC3B,MAAM,KAAK,UAAU;GACrB;;UAEKC,KAAc;EACrB,MAAM,QAAQ,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO;AAC5D,WAAS,UAAU;AACnB,YAAU,MAAM;AAChB,QAAM,SAAS,YAAY;AAC3B,QAAM;;AAGR,KAAI,CAAC,IAAI,IAAI;EACX,MAAMC,aAAgC;GACpC,IAAI;GACJ,OAAO,gBAAgB,IAAI;;AAE7B,WAAS,UAAU,IAAI,MAAMC,WAAS;AACtC,YAAUA,WAAS;AACnB,QAAM,SAAS,YAAY;AAC3B,SAAOA;;CAGT,IAAIC;AACJ,KAAI;AACF,SAAO,MAAM,IAAI;UACVH,KAAc;EACrB,MAAMC,aAAgC;GACpC,IAAI;GACJ,OAAO;;EAET,MAAM,QAAQ,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO;AAC5D,WAAS,UAAU;AACnB,YAAUC,WAAS;AACnB,QAAM,SAAS,YAAY;AAC3B,SAAOA;;CAGT,MAAMD,WAAgC;EACpC,IAAI,QAAQ,MAAM,MAAM;EACxB,eAAe,MAAM,iBAAiB,MAAM,iBAAiB,MAAM;EACnE,QAAQ,MAAM;EACd,SAAS,MAAM;EACf,OAAO,MAAM;;CAGf,MAAM,UAAU,SAAS,OAAO;AAChC,KAAI,SAAS;AACX,OAAK;GACH,MAAM;GACN,OAAOJ,kCAAY;GACnB,QAAQC,mCAAa;GACrB,SAAS;;AAEX,QAAM,SAAS,YAAY,MAAM;QAC5B;EACL,MAAM,UAAU,SAAS,SAAS;AAClC,WAAS,UAAU,IAAI,MAAM;AAC7B,YAAU;AACV,QAAM,SAAS,YAAY;;AAG7B,QAAO"}
1
+ {"version":3,"file":"relay.js","names":["normalizedPayload: RelayDelegateRequest","ActionPhase","ActionStatus","res: Response","err: unknown","response: DelegateRelayResult","response","json: any"],"sources":["../../../../src/core/TatchiPasskey/relay.ts"],"sourcesContent":["import { ActionPhase, ActionStatus, type ActionSSEEvent, type DelegateRelayHooksOptions } from '../types/sdkSentEvents';\nimport type { DelegateRelayResult } from '../types/tatchi';\nimport type { SignedDelegate } from '../types/delegate';\nimport type { WasmSignedDelegate } from '../types/signer-worker';\n\nexport interface RelayDelegateRequest {\n hash: string;\n signedDelegate: SignedDelegate | WasmSignedDelegate;\n}\n\nconst toNumberArray = (value: number[] | Uint8Array): number[] =>\n Array.isArray(value) ? value : Array.from(value);\n\nconst normalizeSignedDelegateForRelay = (\n signedDelegate: SignedDelegate | WasmSignedDelegate\n): SignedDelegate => {\n const delegateAction = signedDelegate.delegateAction as SignedDelegate['delegateAction'] & {\n publicKey: { keyType: number; keyData: number[] | Uint8Array };\n };\n const signature = signedDelegate.signature as SignedDelegate['signature'] & {\n keyType: number;\n signatureData: number[] | Uint8Array;\n };\n\n return {\n delegateAction: {\n ...delegateAction,\n publicKey: {\n ...delegateAction.publicKey,\n keyData: toNumberArray(delegateAction.publicKey.keyData),\n },\n },\n signature: {\n ...signature,\n signatureData: toNumberArray(signature.signatureData),\n },\n };\n};\n\nexport async function sendDelegateActionViaRelayer(args: {\n url: string;\n payload: RelayDelegateRequest;\n signal?: AbortSignal;\n options?: DelegateRelayHooksOptions;\n}): Promise<DelegateRelayResult> {\n\n const { url, payload, signal, options } = args;\n const normalizedPayload: RelayDelegateRequest = {\n ...payload,\n signedDelegate: normalizeSignedDelegateForRelay(payload.signedDelegate),\n };\n\n const emit = (event: ActionSSEEvent) => options?.onEvent?.(event);\n const emitError = (message: string) => {\n emit({\n step: 0,\n phase: ActionPhase.ACTION_ERROR,\n status: ActionStatus.ERROR,\n message,\n error: message,\n });\n };\n\n emit({\n step: 7,\n phase: ActionPhase.STEP_7_BROADCASTING,\n status: ActionStatus.PROGRESS,\n message: 'Submitting delegate to relayer...',\n });\n\n let res: Response;\n try {\n res = await fetch(url, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(normalizedPayload),\n signal,\n });\n } catch (err: unknown) {\n const error = err instanceof Error ? err : new Error(String(err));\n options?.onError?.(error);\n emitError(error.message);\n await options?.afterCall?.(false);\n throw error;\n }\n\n if (!res.ok) {\n const response: DelegateRelayResult = {\n ok: false,\n error: `Relayer HTTP ${res.status}`,\n };\n options?.onError?.(new Error(response.error));\n emitError(response.error!);\n await options?.afterCall?.(false);\n return response;\n }\n\n let json: any;\n try {\n json = await res.json();\n } catch (err: unknown) {\n const response: DelegateRelayResult = {\n ok: false,\n error: 'Relayer returned non-JSON response',\n };\n const error = err instanceof Error ? err : new Error(String(err));\n options?.onError?.(error);\n emitError(response.error!);\n await options?.afterCall?.(false);\n return response;\n }\n\n const response: DelegateRelayResult = {\n ok: Boolean(json?.ok ?? true),\n relayerTxHash: json?.relayerTxHash ?? json?.transactionId ?? json?.txHash,\n status: json?.status,\n outcome: json?.outcome,\n error: json?.error,\n };\n\n const success = response.ok !== false;\n if (success) {\n emit({\n step: 8,\n phase: ActionPhase.STEP_8_ACTION_COMPLETE,\n status: ActionStatus.SUCCESS,\n message: 'Delegate relayed successfully',\n });\n await options?.afterCall?.(true, response);\n } else {\n const message = response.error || 'Relayer execution failed';\n options?.onError?.(new Error(message));\n emitError(message);\n await options?.afterCall?.(false);\n }\n\n return response;\n}\n"],"mappings":";;;;;AAUA,MAAM,iBAAiB,UACrB,MAAM,QAAQ,SAAS,QAAQ,MAAM,KAAK;AAE5C,MAAM,mCACJ,mBACmB;CACnB,MAAM,iBAAiB,eAAe;CAGtC,MAAM,YAAY,eAAe;AAKjC,QAAO;EACL,gBAAgB;GACd,GAAG;GACH,WAAW;IACT,GAAG,eAAe;IAClB,SAAS,cAAc,eAAe,UAAU;;;EAGpD,WAAW;GACT,GAAG;GACH,eAAe,cAAc,UAAU;;;;AAK7C,eAAsB,6BAA6B,MAKlB;CAE/B,MAAM,EAAE,KAAK,SAAS,QAAQ,YAAY;CAC1C,MAAMA,oBAA0C;EAC9C,GAAG;EACH,gBAAgB,gCAAgC,QAAQ;;CAG1D,MAAM,QAAQ,UAA0B,SAAS,UAAU;CAC3D,MAAM,aAAa,YAAoB;AACrC,OAAK;GACH,MAAM;GACN,OAAOC,kCAAY;GACnB,QAAQC,mCAAa;GACrB;GACA,OAAO;;;AAIX,MAAK;EACH,MAAM;EACN,OAAOD,kCAAY;EACnB,QAAQC,mCAAa;EACrB,SAAS;;CAGX,IAAIC;AACJ,KAAI;AACF,QAAM,MAAM,MAAM,KAAK;GACrB,QAAQ;GACR,SAAS,EAAE,gBAAgB;GAC3B,MAAM,KAAK,UAAU;GACrB;;UAEKC,KAAc;EACrB,MAAM,QAAQ,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO;AAC5D,WAAS,UAAU;AACnB,YAAU,MAAM;AAChB,QAAM,SAAS,YAAY;AAC3B,QAAM;;AAGR,KAAI,CAAC,IAAI,IAAI;EACX,MAAMC,aAAgC;GACpC,IAAI;GACJ,OAAO,gBAAgB,IAAI;;AAE7B,WAAS,UAAU,IAAI,MAAMC,WAAS;AACtC,YAAUA,WAAS;AACnB,QAAM,SAAS,YAAY;AAC3B,SAAOA;;CAGT,IAAIC;AACJ,KAAI;AACF,SAAO,MAAM,IAAI;UACVH,KAAc;EACrB,MAAMC,aAAgC;GACpC,IAAI;GACJ,OAAO;;EAET,MAAM,QAAQ,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO;AAC5D,WAAS,UAAU;AACnB,YAAUC,WAAS;AACnB,QAAM,SAAS,YAAY;AAC3B,SAAOA;;CAGT,MAAMD,WAAgC;EACpC,IAAI,QAAQ,MAAM,MAAM;EACxB,eAAe,MAAM,iBAAiB,MAAM,iBAAiB,MAAM;EACnE,QAAQ,MAAM;EACd,SAAS,MAAM;EACf,OAAO,MAAM;;CAGf,MAAM,UAAU,SAAS,OAAO;AAChC,KAAI,SAAS;AACX,OAAK;GACH,MAAM;GACN,OAAOJ,kCAAY;GACnB,QAAQC,mCAAa;GACrB,SAAS;;AAEX,QAAM,SAAS,YAAY,MAAM;QAC5B;EACL,MAAM,UAAU,SAAS,SAAS;AAClC,WAAS,UAAU,IAAI,MAAM;AAC7B,YAAU;AACV,QAAM,SAAS,YAAY;;AAG7B,QAAO"}
@@ -8,6 +8,7 @@ const require_linkDevice = require('../types/linkDevice.js');
8
8
 
9
9
  //#region src/core/TatchiPasskey/scanDevice.ts
10
10
  require_validation.init_validation();
11
+ require_login.init_login();
11
12
  require_sdkSentEvents.init_sdkSentEvents();
12
13
  require_rpcCalls.init_rpcCalls();
13
14
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"scanDevice.js","names":["DeviceLinkingPhase","DeviceLinkingStatus","getLoginSession","vrfInputData: VRFInputData","executeDeviceLinkingContractCalls","error: any","DeviceLinkingError","DeviceLinkingErrorCode","DEVICE_LINKING_CONFIG"],"sources":["../../../../src/core/TatchiPasskey/scanDevice.ts"],"sourcesContent":["import type { PasskeyManagerContext } from './index';\nimport { validateNearAccountId } from '../../utils/validation';\nimport { getLoginSession } from './login';\nimport type { VRFInputData } from '../types/vrf-worker';\nimport type {\n DeviceLinkingQRData,\n LinkDeviceResult,\n ScanAndLinkDeviceOptionsDevice1,\n} from '../types/linkDevice';\nimport { DeviceLinkingPhase, DeviceLinkingStatus } from '../types/sdkSentEvents';\nimport { DeviceLinkingError, DeviceLinkingErrorCode } from '../types/linkDevice';\nimport { DEVICE_LINKING_CONFIG } from '../../config.js';\nimport { executeDeviceLinkingContractCalls } from '../rpcCalls';\n\n/**\n * Device1 (original device): Link device using pre-scanned QR data\n */\nexport async function linkDeviceWithScannedQRData(\n context: PasskeyManagerContext,\n qrData: DeviceLinkingQRData,\n options: ScanAndLinkDeviceOptionsDevice1\n): Promise<LinkDeviceResult> {\n const { onEvent, onError } = options || {};\n\n try {\n onEvent?.({\n step: 2,\n phase: DeviceLinkingPhase.STEP_2_SCANNING,\n status: DeviceLinkingStatus.PROGRESS,\n message: 'Validating QR data...'\n });\n\n // Validate QR data\n validateDeviceLinkingQRData(qrData);\n\n // 3. Get Device1's current account (the account that will receive the new key)\n const { login: device1LoginState } = await getLoginSession(context);\n\n if (!device1LoginState.isLoggedIn || !device1LoginState.nearAccountId) {\n throw new Error('Device1 must be logged in to authorize device linking');\n }\n\n const device1AccountId = device1LoginState.nearAccountId;\n\n // 4. Execute batched transaction: AddKey + Contract notification\n const fundingAmount = options.fundingAmount;\n\n // Parse the device public key for AddKey action\n const device2PublicKey = qrData.device2PublicKey;\n if (!device2PublicKey.startsWith('ed25519:')) {\n throw new Error('Invalid device public key format');\n }\n\n onEvent?.({\n step: 3,\n phase: DeviceLinkingPhase.STEP_3_AUTHORIZATION,\n status: DeviceLinkingStatus.PROGRESS,\n message: `Performing TouchID authentication for device linking...`\n });\n\n const userData = await context.webAuthnManager.getLastUser();\n const nearPublicKeyStr = userData?.clientNearPublicKey;\n if (!nearPublicKeyStr) {\n throw new Error('Client NEAR public key not found in user data');\n }\n // Generate VRF challenge once for both transactions\n const {\n accessKeyInfo,\n nextNonce,\n txBlockHeight,\n txBlockHash\n } = await context.webAuthnManager.getNonceManager().getNonceBlockHashAndHeight(context.nearClient);\n const nextNextNonce = (BigInt(nextNonce) + BigInt(1)).toString();\n const nextNextNextNonce = (BigInt(nextNonce) + BigInt(2)).toString();\n\n const vrfInputData: VRFInputData = {\n userId: device1AccountId,\n rpId: context.webAuthnManager.getRpId(),\n blockHeight: txBlockHeight,\n blockHash: txBlockHash,\n };\n\n const vrfChallenge = await context.webAuthnManager.generateVrfChallengeOnce(vrfInputData);\n\n onEvent?.({\n step: 6,\n phase: DeviceLinkingPhase.STEP_6_REGISTRATION,\n status: DeviceLinkingStatus.PROGRESS,\n message: 'TouchID successful! Signing AddKey transaction...'\n });\n\n // Execute device linking transactions using the centralized RPC function\n const {\n addKeyTxResult,\n storeDeviceLinkingTxResult,\n signedDeleteKeyTransaction\n } = await executeDeviceLinkingContractCalls({\n context,\n device1AccountId,\n device2PublicKey,\n nextNonce,\n nextNextNonce,\n nextNextNextNonce,\n txBlockHash,\n vrfChallenge,\n onEvent,\n confirmationConfigOverride: options?.confirmationConfig,\n confirmerText: options?.confirmerText,\n });\n\n const result = {\n success: true,\n device2PublicKey: qrData.device2PublicKey,\n transactionId: addKeyTxResult?.transaction?.hash\n || storeDeviceLinkingTxResult?.transaction?.hash\n || 'unknown',\n fundingAmount,\n linkedToAccount: device1AccountId, // Include which account the key was added to\n signedDeleteKeyTransaction\n };\n\n onEvent?.({\n step: 6,\n phase: DeviceLinkingPhase.STEP_6_REGISTRATION,\n status: DeviceLinkingStatus.SUCCESS,\n message: `Device2's key added to ${device1AccountId} successfully!`\n });\n\n return result;\n\n } catch (error: any) {\n console.error('LinkDeviceFlow: linkDeviceWithQRData caught error:', error);\n\n const errorMessage = `Failed to scan and link device: ${error.message}`;\n onError?.(new Error(errorMessage));\n\n throw new DeviceLinkingError(\n errorMessage,\n DeviceLinkingErrorCode.AUTHORIZATION_TIMEOUT,\n 'authorization'\n );\n }\n}\n\nexport function validateDeviceLinkingQRData(qrData: DeviceLinkingQRData): void {\n if (!qrData.device2PublicKey) {\n throw new DeviceLinkingError(\n 'Missing device public key',\n DeviceLinkingErrorCode.INVALID_QR_DATA,\n 'authorization'\n );\n }\n\n if (!qrData.timestamp) {\n throw new DeviceLinkingError(\n 'Missing timestamp',\n DeviceLinkingErrorCode.INVALID_QR_DATA,\n 'authorization'\n );\n }\n\n // Check timestamp is not too old (max 15 minutes)\n const maxAge = DEVICE_LINKING_CONFIG.TIMEOUTS.QR_CODE_MAX_AGE_MS;\n if (Date.now() - qrData.timestamp > maxAge) {\n throw new DeviceLinkingError(\n 'QR code expired',\n DeviceLinkingErrorCode.SESSION_EXPIRED,\n 'authorization'\n );\n }\n\n // Account ID is optional - Device2 discovers it from contract logs\n if (qrData.accountId) {\n validateNearAccountId(qrData.accountId);\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;AAiBA,eAAsB,4BACpB,SACA,QACA,SAC2B;CAC3B,MAAM,EAAE,SAAS,YAAY,WAAW;AAExC,KAAI;AACF,YAAU;GACR,MAAM;GACN,OAAOA,yCAAmB;GAC1B,QAAQC,0CAAoB;GAC5B,SAAS;;AAIX,8BAA4B;EAG5B,MAAM,EAAE,OAAO,sBAAsB,MAAMC,8BAAgB;AAE3D,MAAI,CAAC,kBAAkB,cAAc,CAAC,kBAAkB,cACtD,OAAM,IAAI,MAAM;EAGlB,MAAM,mBAAmB,kBAAkB;EAG3C,MAAM,gBAAgB,QAAQ;EAG9B,MAAM,mBAAmB,OAAO;AAChC,MAAI,CAAC,iBAAiB,WAAW,YAC/B,OAAM,IAAI,MAAM;AAGlB,YAAU;GACR,MAAM;GACN,OAAOF,yCAAmB;GAC1B,QAAQC,0CAAoB;GAC5B,SAAS;;EAGX,MAAM,WAAW,MAAM,QAAQ,gBAAgB;EAC/C,MAAM,mBAAmB,UAAU;AACnC,MAAI,CAAC,iBACH,OAAM,IAAI,MAAM;EAGlB,MAAM,EACJ,eACA,WACA,eACA,gBACE,MAAM,QAAQ,gBAAgB,kBAAkB,2BAA2B,QAAQ;EACvF,MAAM,iBAAiB,OAAO,aAAa,OAAO,IAAI;EACtD,MAAM,qBAAqB,OAAO,aAAa,OAAO,IAAI;EAE1D,MAAME,eAA6B;GACjC,QAAQ;GACR,MAAM,QAAQ,gBAAgB;GAC9B,aAAa;GACb,WAAW;;EAGb,MAAM,eAAe,MAAM,QAAQ,gBAAgB,yBAAyB;AAE5E,YAAU;GACR,MAAM;GACN,OAAOH,yCAAmB;GAC1B,QAAQC,0CAAoB;GAC5B,SAAS;;EAIX,MAAM,EACJ,gBACA,4BACA,+BACE,MAAMG,mDAAkC;GAC1C;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA,4BAA4B,SAAS;GACrC,eAAe,SAAS;;EAG1B,MAAM,SAAS;GACb,SAAS;GACT,kBAAkB,OAAO;GACzB,eAAe,gBAAgB,aAAa,QACvC,4BAA4B,aAAa,QACzC;GACL;GACA,iBAAiB;GACjB;;AAGF,YAAU;GACR,MAAM;GACN,OAAOJ,yCAAmB;GAC1B,QAAQC,0CAAoB;GAC5B,SAAS,0BAA0B,iBAAiB;;AAGtD,SAAO;UAEAI,OAAY;AACnB,UAAQ,MAAM,sDAAsD;EAEpE,MAAM,eAAe,mCAAmC,MAAM;AAC9D,YAAU,IAAI,MAAM;AAEpB,QAAM,IAAIC,sCACR,cACAC,0CAAuB,uBACvB;;;AAKN,SAAgB,4BAA4B,QAAmC;AAC7E,KAAI,CAAC,OAAO,iBACV,OAAM,IAAID,sCACR,6BACAC,0CAAuB,iBACvB;AAIJ,KAAI,CAAC,OAAO,UACV,OAAM,IAAID,sCACR,qBACAC,0CAAuB,iBACvB;CAKJ,MAAM,SAASC,qCAAsB,SAAS;AAC9C,KAAI,KAAK,QAAQ,OAAO,YAAY,OAClC,OAAM,IAAIF,sCACR,mBACAC,0CAAuB,iBACvB;AAKJ,KAAI,OAAO,UACT,0CAAsB,OAAO"}
1
+ {"version":3,"file":"scanDevice.js","names":["DeviceLinkingPhase","DeviceLinkingStatus","getLoginSession","vrfInputData: VRFInputData","executeDeviceLinkingContractCalls","error: any","DeviceLinkingError","DeviceLinkingErrorCode","DEVICE_LINKING_CONFIG"],"sources":["../../../../src/core/TatchiPasskey/scanDevice.ts"],"sourcesContent":["import type { PasskeyManagerContext } from './index';\nimport { validateNearAccountId } from '../../utils/validation';\nimport { getLoginSession } from './login';\nimport type { VRFInputData } from '../types/vrf-worker';\nimport type {\n DeviceLinkingQRData,\n LinkDeviceResult,\n ScanAndLinkDeviceOptionsDevice1,\n} from '../types/linkDevice';\nimport { DeviceLinkingPhase, DeviceLinkingStatus } from '../types/sdkSentEvents';\nimport { DeviceLinkingError, DeviceLinkingErrorCode } from '../types/linkDevice';\nimport { DEVICE_LINKING_CONFIG } from '../../config.js';\nimport { executeDeviceLinkingContractCalls } from '../rpcCalls';\n\n/**\n * Device1 (original device): Link device using pre-scanned QR data\n */\nexport async function linkDeviceWithScannedQRData(\n context: PasskeyManagerContext,\n qrData: DeviceLinkingQRData,\n options: ScanAndLinkDeviceOptionsDevice1\n): Promise<LinkDeviceResult> {\n const { onEvent, onError } = options || {};\n\n try {\n onEvent?.({\n step: 2,\n phase: DeviceLinkingPhase.STEP_2_SCANNING,\n status: DeviceLinkingStatus.PROGRESS,\n message: 'Validating QR data...'\n });\n\n // Validate QR data\n validateDeviceLinkingQRData(qrData);\n\n // 3. Get Device1's current account (the account that will receive the new key)\n const { login: device1LoginState } = await getLoginSession(context);\n\n if (!device1LoginState.isLoggedIn || !device1LoginState.nearAccountId) {\n throw new Error('Device1 must be logged in to authorize device linking');\n }\n\n const device1AccountId = device1LoginState.nearAccountId;\n\n // 4. Execute batched transaction: AddKey + Contract notification\n const fundingAmount = options.fundingAmount;\n\n // Parse the device public key for AddKey action\n const device2PublicKey = qrData.device2PublicKey;\n if (!device2PublicKey.startsWith('ed25519:')) {\n throw new Error('Invalid device public key format');\n }\n\n onEvent?.({\n step: 3,\n phase: DeviceLinkingPhase.STEP_3_AUTHORIZATION,\n status: DeviceLinkingStatus.PROGRESS,\n message: `Performing TouchID authentication for device linking...`\n });\n\n const userData = await context.webAuthnManager.getLastUser();\n const nearPublicKeyStr = userData?.clientNearPublicKey;\n if (!nearPublicKeyStr) {\n throw new Error('Client NEAR public key not found in user data');\n }\n // Generate VRF challenge once for both transactions\n const {\n accessKeyInfo,\n nextNonce,\n txBlockHeight,\n txBlockHash\n } = await context.webAuthnManager.getNonceManager().getNonceBlockHashAndHeight(context.nearClient);\n const nextNextNonce = (BigInt(nextNonce) + BigInt(1)).toString();\n const nextNextNextNonce = (BigInt(nextNonce) + BigInt(2)).toString();\n\n const vrfInputData: VRFInputData = {\n userId: device1AccountId,\n rpId: context.webAuthnManager.getRpId(),\n blockHeight: txBlockHeight,\n blockHash: txBlockHash,\n };\n\n const vrfChallenge = await context.webAuthnManager.generateVrfChallengeOnce(vrfInputData);\n\n onEvent?.({\n step: 6,\n phase: DeviceLinkingPhase.STEP_6_REGISTRATION,\n status: DeviceLinkingStatus.PROGRESS,\n message: 'TouchID successful! Signing AddKey transaction...'\n });\n\n // Execute device linking transactions using the centralized RPC function\n const {\n addKeyTxResult,\n storeDeviceLinkingTxResult,\n signedDeleteKeyTransaction\n } = await executeDeviceLinkingContractCalls({\n context,\n device1AccountId,\n device2PublicKey,\n nextNonce,\n nextNextNonce,\n nextNextNextNonce,\n txBlockHash,\n vrfChallenge,\n onEvent,\n confirmationConfigOverride: options?.confirmationConfig,\n confirmerText: options?.confirmerText,\n });\n\n const result = {\n success: true,\n device2PublicKey: qrData.device2PublicKey,\n transactionId: addKeyTxResult?.transaction?.hash\n || storeDeviceLinkingTxResult?.transaction?.hash\n || 'unknown',\n fundingAmount,\n linkedToAccount: device1AccountId, // Include which account the key was added to\n signedDeleteKeyTransaction\n };\n\n onEvent?.({\n step: 6,\n phase: DeviceLinkingPhase.STEP_6_REGISTRATION,\n status: DeviceLinkingStatus.SUCCESS,\n message: `Device2's key added to ${device1AccountId} successfully!`\n });\n\n return result;\n\n } catch (error: any) {\n console.error('LinkDeviceFlow: linkDeviceWithQRData caught error:', error);\n\n const errorMessage = `Failed to scan and link device: ${error.message}`;\n onError?.(new Error(errorMessage));\n\n throw new DeviceLinkingError(\n errorMessage,\n DeviceLinkingErrorCode.AUTHORIZATION_TIMEOUT,\n 'authorization'\n );\n }\n}\n\nexport function validateDeviceLinkingQRData(qrData: DeviceLinkingQRData): void {\n if (!qrData.device2PublicKey) {\n throw new DeviceLinkingError(\n 'Missing device public key',\n DeviceLinkingErrorCode.INVALID_QR_DATA,\n 'authorization'\n );\n }\n\n if (!qrData.timestamp) {\n throw new DeviceLinkingError(\n 'Missing timestamp',\n DeviceLinkingErrorCode.INVALID_QR_DATA,\n 'authorization'\n );\n }\n\n // Check timestamp is not too old (max 15 minutes)\n const maxAge = DEVICE_LINKING_CONFIG.TIMEOUTS.QR_CODE_MAX_AGE_MS;\n if (Date.now() - qrData.timestamp > maxAge) {\n throw new DeviceLinkingError(\n 'QR code expired',\n DeviceLinkingErrorCode.SESSION_EXPIRED,\n 'authorization'\n );\n }\n\n // Account ID is optional - Device2 discovers it from contract logs\n if (qrData.accountId) {\n validateNearAccountId(qrData.accountId);\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAiBA,eAAsB,4BACpB,SACA,QACA,SAC2B;CAC3B,MAAM,EAAE,SAAS,YAAY,WAAW;AAExC,KAAI;AACF,YAAU;GACR,MAAM;GACN,OAAOA,yCAAmB;GAC1B,QAAQC,0CAAoB;GAC5B,SAAS;;AAIX,8BAA4B;EAG5B,MAAM,EAAE,OAAO,sBAAsB,MAAMC,8BAAgB;AAE3D,MAAI,CAAC,kBAAkB,cAAc,CAAC,kBAAkB,cACtD,OAAM,IAAI,MAAM;EAGlB,MAAM,mBAAmB,kBAAkB;EAG3C,MAAM,gBAAgB,QAAQ;EAG9B,MAAM,mBAAmB,OAAO;AAChC,MAAI,CAAC,iBAAiB,WAAW,YAC/B,OAAM,IAAI,MAAM;AAGlB,YAAU;GACR,MAAM;GACN,OAAOF,yCAAmB;GAC1B,QAAQC,0CAAoB;GAC5B,SAAS;;EAGX,MAAM,WAAW,MAAM,QAAQ,gBAAgB;EAC/C,MAAM,mBAAmB,UAAU;AACnC,MAAI,CAAC,iBACH,OAAM,IAAI,MAAM;EAGlB,MAAM,EACJ,eACA,WACA,eACA,gBACE,MAAM,QAAQ,gBAAgB,kBAAkB,2BAA2B,QAAQ;EACvF,MAAM,iBAAiB,OAAO,aAAa,OAAO,IAAI;EACtD,MAAM,qBAAqB,OAAO,aAAa,OAAO,IAAI;EAE1D,MAAME,eAA6B;GACjC,QAAQ;GACR,MAAM,QAAQ,gBAAgB;GAC9B,aAAa;GACb,WAAW;;EAGb,MAAM,eAAe,MAAM,QAAQ,gBAAgB,yBAAyB;AAE5E,YAAU;GACR,MAAM;GACN,OAAOH,yCAAmB;GAC1B,QAAQC,0CAAoB;GAC5B,SAAS;;EAIX,MAAM,EACJ,gBACA,4BACA,+BACE,MAAMG,mDAAkC;GAC1C;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA,4BAA4B,SAAS;GACrC,eAAe,SAAS;;EAG1B,MAAM,SAAS;GACb,SAAS;GACT,kBAAkB,OAAO;GACzB,eAAe,gBAAgB,aAAa,QACvC,4BAA4B,aAAa,QACzC;GACL;GACA,iBAAiB;GACjB;;AAGF,YAAU;GACR,MAAM;GACN,OAAOJ,yCAAmB;GAC1B,QAAQC,0CAAoB;GAC5B,SAAS,0BAA0B,iBAAiB;;AAGtD,SAAO;UAEAI,OAAY;AACnB,UAAQ,MAAM,sDAAsD;EAEpE,MAAM,eAAe,mCAAmC,MAAM;AAC9D,YAAU,IAAI,MAAM;AAEpB,QAAM,IAAIC,sCACR,cACAC,0CAAuB,uBACvB;;;AAKN,SAAgB,4BAA4B,QAAmC;AAC7E,KAAI,CAAC,OAAO,iBACV,OAAM,IAAID,sCACR,6BACAC,0CAAuB,iBACvB;AAIJ,KAAI,CAAC,OAAO,UACV,OAAM,IAAID,sCACR,qBACAC,0CAAuB,iBACvB;CAKJ,MAAM,SAASC,qCAAsB,SAAS;AAC9C,KAAI,KAAK,QAAQ,OAAO,YAAY,OAClC,OAAM,IAAIF,sCACR,mBACAC,0CAAuB,iBACvB;AAKJ,KAAI,OAAO,UACT,0CAAsB,OAAO"}
@@ -2,10 +2,13 @@ const require_rolldown_runtime = require('../../../_virtual/rolldown_runtime.js'
2
2
  const require_validation = require('../validation.js');
3
3
  const require_credentialsHelpers = require('../../WebAuthnManager/credentialsHelpers.js');
4
4
  const require_safari_fallbacks = require('../../WebAuthnManager/WebAuthnFallbacks/safari-fallbacks.js');
5
+ const require_index = require('../../WebAuthnManager/WebAuthnFallbacks/index.js');
5
6
  const require_overlay_styles = require('./overlay-styles.js');
6
7
 
7
8
  //#region src/core/WalletIframe/client/IframeTransport.ts
8
9
  require_validation.init_validation();
10
+ require_credentialsHelpers.init_credentialsHelpers();
11
+ require_index.init_WebAuthnFallbacks();
9
12
  const IframeMessage = {
10
13
  Connect: "CONNECT",
11
14
  Ready: "READY",
@@ -1 +1 @@
1
- {"version":3,"file":"IframeTransport.js","names":["isObject","WebAuthnBridgeMessage","timeout: number | undefined","pub: PublicKeyCredentialCreationOptions","serializeRegistrationCredentialWithPRF","pub: PublicKeyCredentialRequestOptions","serializeAuthenticationCredentialWithPRF"],"sources":["../../../../../src/core/WalletIframe/client/IframeTransport.ts"],"sourcesContent":["/**\n * IframeTransport - Client-Side Communication Layer\n *\n * This module handles the low-level iframe management and connection establishment.\n * It encapsulates all the complex browser-specific logic for safely creating and\n * connecting to the wallet service iframe.\n *\n * Key Responsibilities:\n * - Iframe Creation: Creates and mounts the iframe element with proper security attributes\n * - Security Hardening: Sets appropriate allow/sandbox attributes for WebAuthn and clipboard access\n * - Load Event Handling: Waits for iframe load to avoid postMessage races\n * - Connection Handshake: Performs robust CONNECT → READY handshake using MessageChannel\n * - Boot Latency Handling: Manages cross-origin boot delays with SERVICE_HOST_BOOTED hints\n * - Connection Deduplication: Prevents multiple concurrent connection attempts\n * - Error Handling: Provides clear error messages for connection failures\n *\n * Security Model:\n * - Uses explicit allow attributes for WebAuthn and clipboard permissions\n * - Avoids sandboxing for cross-origin deployments (prevents MessagePort transfer issues)\n * - Validates wallet origin URLs to prevent security issues\n * - Uses MessageChannel for secure, bidirectional communication\n *\n * Browser Compatibility:\n * - Handles various browser quirks around iframe loading and MessagePort transfer\n * - Provides fallback behavior for different browser implementations\n * - Manages timing issues with cross-origin iframe boot sequences\n */\n\nimport type { ChildToParentEnvelope } from '../shared/messages';\nimport { isObject } from '../validation';\nimport { serializeRegistrationCredentialWithPRF, serializeAuthenticationCredentialWithPRF } from '../../WebAuthnManager/credentialsHelpers';\nimport { ensureOverlayBase } from './overlay-styles';\nimport { WebAuthnBridgeMessage } from '../../WebAuthnManager/WebAuthnFallbacks';\n\n// Message constants (typed string literals, tree‑shake friendly)\nexport const IframeMessage = {\n Connect: 'CONNECT',\n Ready: 'READY',\n HostBooted: 'SERVICE_HOST_BOOTED',\n HostDebugOrigin: 'SERVICE_HOST_DEBUG_ORIGIN',\n HostLog: 'SERVICE_HOST_LOG',\n} as const;\n\n// Bridge request payloads\ntype CreateReq = { requestId?: string; publicKey?: PublicKeyCredentialCreationOptions };\ntype GetReq = { requestId?: string; publicKey?: PublicKeyCredentialRequestOptions };\n\nexport interface IframeTransportOptions {\n walletOrigin: string; // e.g., https://wallet.example.com\n servicePath?: string; // default '/wallet-service'\n connectTimeoutMs?: number; // total budget for handshake retries\n testOptions?: {\n routerId?: string; // identity tag for the iframe element\n ownerTag?: string; // e.g., 'app' | 'tests'\n };\n}\n\nexport class IframeTransport {\n private readonly opts: Required<IframeTransportOptions>;\n private iframeEl: HTMLIFrameElement | null = null;\n private serviceBooted = false; // set when wallet host sends SERVICE_HOST_BOOTED (best-effort only)\n private connectInFlight: Promise<MessagePort> | null = null;\n private readonly walletServiceUrl: URL;\n private readonly walletOrigin: string;\n private readonly testOptions: { routerId?: string; ownerTag?: string };\n\n constructor(options: IframeTransportOptions) {\n this.opts = {\n servicePath: '/wallet-service',\n connectTimeoutMs: 8000,\n ...options,\n } as Required<IframeTransportOptions>;\n\n try {\n this.walletServiceUrl = new URL(this.opts.servicePath, this.opts.walletOrigin);\n } catch (err) {\n throw new Error(`[IframeTransport] Invalid wallet origin (${options.walletOrigin}) or servicePath (${options.servicePath || '/wallet-service'})`);\n }\n this.walletOrigin = this.walletServiceUrl.origin;\n this.testOptions = {\n routerId: options.testOptions?.routerId,\n ownerTag: options.testOptions?.ownerTag,\n };\n\n // Listen for a best-effort boot hint from the wallet host. Not required for correctness,\n // but helps reduce redundant CONNECT posts while the host script is still booting.\n try {\n window.addEventListener('message', (e) => {\n const data = e.data as unknown;\n if (!isObject(data)) return;\n const type = (data as { type?: unknown }).type;\n if (type === IframeMessage.HostBooted && e.origin === this.walletOrigin) {\n this.serviceBooted = true;\n return;\n }\n if (type === IframeMessage.HostDebugOrigin) {\n }\n if (type === IframeMessage.HostLog) {\n // Only surface wallet logs when debug is enabled\n console.debug('[IframeTransport][wallet-log]', (data as { payload?: unknown }).payload);\n }\n // Parent‑performed WebAuthn bridge for Safari cross‑origin scenarios.\n // Only accept requests originating from the wallet iframe origin.\n if (e.origin === this.walletOrigin) {\n if (type === WebAuthnBridgeMessage.Create || type === WebAuthnBridgeMessage.Get) {\n // Delegate to common bridge handler\n this.performWebAuthnBridge(type as typeof WebAuthnBridgeMessage.Create | typeof WebAuthnBridgeMessage.Get, data, e);\n return;\n }\n }\n });\n } catch {}\n }\n\n /** Returns the underlying iframe element if it exists. */\n getIframeEl(): HTMLIFrameElement | null { return this.iframeEl; }\n\n /**\n * Guardrail: prevent multiple overlay iframes accumulating when apps accidentally\n * create more than one WalletIframeRouter/TatchiPasskey instance.\n */\n private removeExistingOverlaysForOrigin(): void {\n try {\n const isDev = (() => {\n const env = (globalThis as any)?.process?.env?.NODE_ENV;\n if (env && env !== 'production') return true;\n const h = window.location.hostname || '';\n if (/localhost|127\\.(?:0|[1-9]\\d?)\\.(?:0|[1-9]\\d?)\\.(?:0|[1-9]\\d?)|\\.local(?:host)?$/i.test(h)) return true;\n return false;\n })();\n\n const existing = Array.from(document.querySelectorAll('iframe.w3a-wallet-overlay')) as HTMLIFrameElement[];\n const matches = existing.filter((el) => {\n const dsOrigin = (el as any)?.dataset?.w3aOrigin as string | undefined;\n if (dsOrigin) return dsOrigin === this.walletOrigin;\n try { return new URL(el.src).origin === this.walletOrigin; } catch { return false; }\n });\n\n if (!matches.length) return;\n\n if (isDev) {\n const routerIds = matches\n .map((el) => (el as any)?.dataset?.w3aRouterId)\n .filter((v): v is string => typeof v === 'string' && v.length > 0);\n console.warn(\n `[IframeTransport] Found existing wallet overlay iframe(s) for ${this.walletOrigin}. This usually indicates multiple SDK instances. Removing old iframe(s) to avoid duplicates.`,\n { count: matches.length, routerIds }\n );\n }\n\n for (const el of matches) {\n try { el.remove(); } catch {}\n }\n } catch {}\n }\n\n /** Ensure the iframe element exists and is appended to the DOM. Idempotent. */\n ensureIframeMounted(): HTMLIFrameElement {\n if (this.iframeEl) {\n return this.iframeEl;\n }\n\n this.removeExistingOverlaysForOrigin();\n\n const iframe = document.createElement('iframe');\n // Hidden by default via CSS classes; higher layers toggle state using overlay-styles.\n iframe.classList.add('w3a-wallet-overlay', 'is-hidden');\n // Ensure the base overlay stylesheet is installed early so computed styles\n // (opacity/pointer-events) reflect the hidden state immediately after mount.\n try { ensureOverlayBase(iframe); } catch {}\n // Ensure no initial footprint even before stylesheet attaches\n iframe.setAttribute('width', '0');\n iframe.setAttribute('height', '0');\n iframe.setAttribute('aria-hidden', 'true');\n iframe.setAttribute('tabindex', '-1');\n // Hint higher priority fetch for the iframe document on supporting browsers\n iframe.setAttribute('loading', 'eager');\n iframe.setAttribute('fetchpriority', 'high');\n\n iframe.dataset.w3aRouterId = this.testOptions?.routerId || '';\n if (this.testOptions?.ownerTag) iframe.dataset.w3aOwner = this.testOptions.ownerTag;\n iframe.dataset.w3aOrigin = this.walletOrigin;\n\n // Delegate WebAuthn + clipboard capabilities to the wallet origin frame\n try {\n iframe.setAttribute('allow', this.buildAllowAttr(this.walletOrigin));\n } catch {\n iframe.setAttribute('allow', \"publickey-credentials-get 'self'; publickey-credentials-create 'self'; clipboard-read; clipboard-write\");\n }\n\n // Track load state to guard against races where we post before content is listening\n iframe._svc_loaded = false;\n iframe.addEventListener('load', () => { iframe._svc_loaded = true; }, { once: true });\n\n const src = this.walletServiceUrl.toString();\n console.debug('[IframeTransport] mount: external origin', src);\n iframe.src = src;\n\n document.body.appendChild(iframe);\n console.debug('[IframeTransport] mount: iframe appended');\n this.iframeEl = iframe;\n return iframe;\n }\n\n /**\n * Connect to the wallet iframe using a MessageChannel handshake.\n * - Repeatedly posts {type:'CONNECT'} with a fresh port until a 'READY' message arrives\n * - Times out after connectTimeoutMs\n * - Deduplicates concurrent calls and returns the same MessagePort promise\n */\n async connect(): Promise<MessagePort> {\n if (this.connectInFlight) return this.connectInFlight;\n this.connectInFlight = (async () => {\n const iframe = this.ensureIframeMounted();\n\n // Ensure load fired at least once so the host script can attach listeners\n await this.waitForLoad(iframe);\n\n // For cross-origin pages, give the host only a very brief moment to boot its script\n // Keep this low to avoid adding noticeable latency to the first CONNECT attempt.\n // The handshake will continue retrying regardless, so a shorter wait improves TTFB.\n const bootWaitMs = Math.min(this.opts.connectTimeoutMs / 12, 300);\n const startBoot = Date.now();\n while (!this.serviceBooted && (Date.now() - startBoot) < bootWaitMs) {\n await new Promise(r => setTimeout(r, 50));\n }\n\n let resolved = false;\n let attempt = 0;\n let warnedNullOrigin = false;\n const start = Date.now();\n const overallTimeout = this.opts.connectTimeoutMs;\n\n const port = await new Promise<MessagePort>((resolve, reject) => {\n const tick = () => {\n if (resolved) return;\n const elapsed = Date.now() - start;\n if (elapsed >= overallTimeout) {\n console.debug('[IframeTransport] handshake timeout after %d ms', elapsed);\n return reject(new Error('Wallet iframe READY timeout'));\n }\n\n attempt += 1;\n const channel = new MessageChannel();\n const port1 = channel.port1;\n const port2 = channel.port2;\n const cleanup = () => { try { port1.onmessage = null; } catch {} };\n\n port1.onmessage = (e: MessageEvent<ChildToParentEnvelope>) => {\n const data = e.data;\n if (data.type === IframeMessage.Ready) {\n resolved = true;\n cleanup();\n port1.start?.();\n return resolve(port1);\n }\n };\n\n // Ensure the receiving side is actively listening before we post the CONNECT\n try { port1.start?.(); } catch {}\n\n const cw = iframe.contentWindow;\n if (!cw) {\n cleanup();\n return reject(new Error('Wallet iframe window missing'));\n }\n // Explicitly target the wallet origin so Chromium delivers the MessagePort\n // transfer across origins. Using '*' can silently drop the transferable\n // port in stricter environments, preventing the host from ever adopting it.\n //\n // However, some dev setups (e.g., mDNS/.local + reverse proxy ports) can\n // result in the iframe document resolving to a slightly different serialized\n // origin (e.g., host without the expected port). In those cases, the strict\n // target will never deliver. As a pragmatic fallback for development, we\n // periodically attempt with '*' so the wallet host can adopt the port and\n // reply with READY. Subsequent communication uses MessagePort, not window.postMessage.\n // Try strict origin first, but fall back to '*' more frequently in dev to\n // avoid stalls when local origins serialize differently (e.g., iOS + mDNS).\n // Using '*' here only affects this CONNECT; subsequent traffic uses MessagePort.\n //\n // Prefer wildcard target until we have observed SERVICE_HOST_BOOTED,\n // which indicates the iframe has a stable, non-opaque origin and is\n // ready to adopt a MessagePort. This avoids noisy 'null' origin\n // warnings while still allowing strict-origin delivery as soon as\n // the host is booted.\n const targetOrigin = this.serviceBooted ? this.walletOrigin : '*';\n ({ warnedNullOrigin } = this.postConnectMessage(\n cw,\n { type: IframeMessage.Connect },\n port2,\n targetOrigin,\n warnedNullOrigin,\n elapsed,\n attempt,\n ));\n\n // Schedule next tick if not resolved yet (light backoff to reduce spam)\n const interval = attempt < 10 ? 200 : attempt < 20 ? 400 : 800;\n setTimeout(() => { if (!resolved) tick(); }, interval);\n };\n\n tick();\n });\n\n return port;\n })();\n\n try {\n return await this.connectInFlight;\n } finally {\n this.connectInFlight = null;\n }\n }\n\n private postConnectMessage(\n cw: Window,\n data: unknown,\n port2: MessagePort,\n targetOrigin: string,\n warnedNullOrigin: boolean,\n elapsed: number,\n attempt: number,\n ): { warnedNullOrigin: boolean } {\n try {\n cw.postMessage(data, targetOrigin, [port2]);\n return { warnedNullOrigin };\n } catch (e) {\n const message = e instanceof Error ? e.message ?? String(e) : String(e);\n if (!warnedNullOrigin && message.includes(\"'null'\")) {\n warnedNullOrigin = true;\n console.warn('[IframeTransport] CONNECT blocked; iframe origin appears to be null. Check that %s is reachable and responds with Cross-Origin-Resource-Policy: cross-origin.', this.walletServiceUrl.toString());\n }\n // Attempt wildcard fallback and continue retries\n try { cw.postMessage(data, '*', [port2]); } catch {}\n console.debug('[IframeTransport] CONNECT attempt %d threw after %d ms; retrying.', attempt, elapsed);\n return { warnedNullOrigin };\n }\n }\n\n /** Guard against posting to the iframe before it has fired load. */\n private async waitForLoad(iframe: HTMLIFrameElement): Promise<void> {\n if (iframe._svc_loaded) return;\n await new Promise<void>((resolve) => {\n try {\n let timeout: number | undefined;\n iframe.addEventListener?.('load', () => {\n if (typeof timeout === 'number') {\n clearTimeout(timeout);\n }\n resolve();\n }, { once: true });\n // Safety net: resolve shortly even if load listener fails to attach\n timeout = window.setTimeout(() => {\n console.debug('[IframeTransport] waitForLoad did not observe load event within 150ms; continuing');\n resolve();\n }, 150);\n } catch {\n resolve();\n }\n });\n }\n\n private buildAllowAttr(walletOrigin: string): string {\n return `publickey-credentials-get 'self' ${walletOrigin}; publickey-credentials-create 'self' ${walletOrigin}; clipboard-read; clipboard-write`;\n }\n\n private postBridgeResult(\n source: WindowProxy | null,\n type: 'WALLET_WEBAUTHN_CREATE_RESULT' | 'WALLET_WEBAUTHN_GET_RESULT',\n requestId: string,\n ok: boolean,\n payload: { credential?: unknown; error?: string },\n ): void {\n // Reply directly to the requesting window; wildcard target avoids transient\n // 'null' origin warnings during early navigation while remaining safe since\n // we already validated the sender's origin before bridging.\n source?.postMessage({ type, requestId, ok, ...payload }, '*');\n }\n\n private performWebAuthnBridge(\n kind: typeof WebAuthnBridgeMessage.Create | typeof WebAuthnBridgeMessage.Get,\n raw: unknown,\n e: MessageEvent,\n ): void {\n if (kind === WebAuthnBridgeMessage.Create) {\n void this.handleWebAuthnCreate(raw as CreateReq, e);\n return;\n }\n // kind === 'WALLET_WEBAUTHN_GET'\n void this.handleWebAuthnGet(raw as GetReq, e);\n }\n\n private async handleWebAuthnCreate(req: CreateReq, e: MessageEvent): Promise<void> {\n const requestId = req?.requestId || '';\n try {\n const src = req?.publicKey || ({} as PublicKeyCredentialCreationOptions);\n const rpName = src.rp?.name || 'WebAuthn';\n const rpId = src.rp?.id || window.location.hostname;\n const pub: PublicKeyCredentialCreationOptions = { ...src, rp: { name: rpName, id: rpId } };\n console.debug('[IframeTransport][bridge] CREATE received', { requestId, from: e.origin, rpId: pub.rp?.id });\n const cred = await navigator.credentials.create({ publicKey: pub }) as PublicKeyCredential;\n const serialized = serializeRegistrationCredentialWithPRF({ credential: cred, firstPrfOutput: true, secondPrfOutput: true });\n this.postBridgeResult(e.source as WindowProxy | null, WebAuthnBridgeMessage.CreateResult, requestId, true, { credential: serialized });\n console.debug('[IframeTransport][bridge] CREATE ok', { requestId });\n } catch (err) {\n console.warn('[IframeTransport][bridge] CREATE failed', { requestId, err: String((err as Error)?.message || err) });\n this.postBridgeResult(e.source as WindowProxy | null, WebAuthnBridgeMessage.CreateResult, requestId, false, { error: String((err as Error)?.message || err) });\n }\n }\n\n private async handleWebAuthnGet(req: GetReq, e: MessageEvent): Promise<void> {\n const requestId = req?.requestId || '';\n try {\n const src = req?.publicKey || ({} as PublicKeyCredentialRequestOptions);\n const rpId = src.rpId || window.location.hostname;\n const pub: PublicKeyCredentialRequestOptions = { ...src, rpId };\n console.debug('[IframeTransport][bridge] GET received', { requestId, from: e.origin, rpId: pub.rpId });\n const cred = await navigator.credentials.get({ publicKey: pub }) as PublicKeyCredential;\n const serialized = serializeAuthenticationCredentialWithPRF({ credential: cred, firstPrfOutput: true, secondPrfOutput: true });\n this.postBridgeResult(e.source as WindowProxy | null, WebAuthnBridgeMessage.GetResult, requestId, true, { credential: serialized });\n console.debug('[IframeTransport][bridge] GET ok', { requestId });\n } catch (err) {\n console.warn('[IframeTransport][bridge] GET failed', { requestId, err: String((err as Error)?.message || err) });\n this.postBridgeResult(e.source as WindowProxy | null, WebAuthnBridgeMessage.GetResult, requestId, false, { error: String((err as Error)?.message || err) });\n }\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"IframeTransport.js","names":["isObject","WebAuthnBridgeMessage","timeout: number | undefined","pub: PublicKeyCredentialCreationOptions","serializeRegistrationCredentialWithPRF","pub: PublicKeyCredentialRequestOptions","serializeAuthenticationCredentialWithPRF"],"sources":["../../../../../src/core/WalletIframe/client/IframeTransport.ts"],"sourcesContent":["/**\n * IframeTransport - Client-Side Communication Layer\n *\n * This module handles the low-level iframe management and connection establishment.\n * It encapsulates all the complex browser-specific logic for safely creating and\n * connecting to the wallet service iframe.\n *\n * Key Responsibilities:\n * - Iframe Creation: Creates and mounts the iframe element with proper security attributes\n * - Security Hardening: Sets appropriate allow/sandbox attributes for WebAuthn and clipboard access\n * - Load Event Handling: Waits for iframe load to avoid postMessage races\n * - Connection Handshake: Performs robust CONNECT → READY handshake using MessageChannel\n * - Boot Latency Handling: Manages cross-origin boot delays with SERVICE_HOST_BOOTED hints\n * - Connection Deduplication: Prevents multiple concurrent connection attempts\n * - Error Handling: Provides clear error messages for connection failures\n *\n * Security Model:\n * - Uses explicit allow attributes for WebAuthn and clipboard permissions\n * - Avoids sandboxing for cross-origin deployments (prevents MessagePort transfer issues)\n * - Validates wallet origin URLs to prevent security issues\n * - Uses MessageChannel for secure, bidirectional communication\n *\n * Browser Compatibility:\n * - Handles various browser quirks around iframe loading and MessagePort transfer\n * - Provides fallback behavior for different browser implementations\n * - Manages timing issues with cross-origin iframe boot sequences\n */\n\nimport type { ChildToParentEnvelope } from '../shared/messages';\nimport { isObject } from '../validation';\nimport { serializeRegistrationCredentialWithPRF, serializeAuthenticationCredentialWithPRF } from '../../WebAuthnManager/credentialsHelpers';\nimport { ensureOverlayBase } from './overlay-styles';\nimport { WebAuthnBridgeMessage } from '../../WebAuthnManager/WebAuthnFallbacks';\n\n// Message constants (typed string literals, tree‑shake friendly)\nexport const IframeMessage = {\n Connect: 'CONNECT',\n Ready: 'READY',\n HostBooted: 'SERVICE_HOST_BOOTED',\n HostDebugOrigin: 'SERVICE_HOST_DEBUG_ORIGIN',\n HostLog: 'SERVICE_HOST_LOG',\n} as const;\n\n// Bridge request payloads\ntype CreateReq = { requestId?: string; publicKey?: PublicKeyCredentialCreationOptions };\ntype GetReq = { requestId?: string; publicKey?: PublicKeyCredentialRequestOptions };\n\nexport interface IframeTransportOptions {\n walletOrigin: string; // e.g., https://wallet.example.com\n servicePath?: string; // default '/wallet-service'\n connectTimeoutMs?: number; // total budget for handshake retries\n testOptions?: {\n routerId?: string; // identity tag for the iframe element\n ownerTag?: string; // e.g., 'app' | 'tests'\n };\n}\n\nexport class IframeTransport {\n private readonly opts: Required<IframeTransportOptions>;\n private iframeEl: HTMLIFrameElement | null = null;\n private serviceBooted = false; // set when wallet host sends SERVICE_HOST_BOOTED (best-effort only)\n private connectInFlight: Promise<MessagePort> | null = null;\n private readonly walletServiceUrl: URL;\n private readonly walletOrigin: string;\n private readonly testOptions: { routerId?: string; ownerTag?: string };\n\n constructor(options: IframeTransportOptions) {\n this.opts = {\n servicePath: '/wallet-service',\n connectTimeoutMs: 8000,\n ...options,\n } as Required<IframeTransportOptions>;\n\n try {\n this.walletServiceUrl = new URL(this.opts.servicePath, this.opts.walletOrigin);\n } catch (err) {\n throw new Error(`[IframeTransport] Invalid wallet origin (${options.walletOrigin}) or servicePath (${options.servicePath || '/wallet-service'})`);\n }\n this.walletOrigin = this.walletServiceUrl.origin;\n this.testOptions = {\n routerId: options.testOptions?.routerId,\n ownerTag: options.testOptions?.ownerTag,\n };\n\n // Listen for a best-effort boot hint from the wallet host. Not required for correctness,\n // but helps reduce redundant CONNECT posts while the host script is still booting.\n try {\n window.addEventListener('message', (e) => {\n const data = e.data as unknown;\n if (!isObject(data)) return;\n const type = (data as { type?: unknown }).type;\n if (type === IframeMessage.HostBooted && e.origin === this.walletOrigin) {\n this.serviceBooted = true;\n return;\n }\n if (type === IframeMessage.HostDebugOrigin) {\n }\n if (type === IframeMessage.HostLog) {\n // Only surface wallet logs when debug is enabled\n console.debug('[IframeTransport][wallet-log]', (data as { payload?: unknown }).payload);\n }\n // Parent‑performed WebAuthn bridge for Safari cross‑origin scenarios.\n // Only accept requests originating from the wallet iframe origin.\n if (e.origin === this.walletOrigin) {\n if (type === WebAuthnBridgeMessage.Create || type === WebAuthnBridgeMessage.Get) {\n // Delegate to common bridge handler\n this.performWebAuthnBridge(type as typeof WebAuthnBridgeMessage.Create | typeof WebAuthnBridgeMessage.Get, data, e);\n return;\n }\n }\n });\n } catch {}\n }\n\n /** Returns the underlying iframe element if it exists. */\n getIframeEl(): HTMLIFrameElement | null { return this.iframeEl; }\n\n /**\n * Guardrail: prevent multiple overlay iframes accumulating when apps accidentally\n * create more than one WalletIframeRouter/TatchiPasskey instance.\n */\n private removeExistingOverlaysForOrigin(): void {\n try {\n const isDev = (() => {\n const env = (globalThis as any)?.process?.env?.NODE_ENV;\n if (env && env !== 'production') return true;\n const h = window.location.hostname || '';\n if (/localhost|127\\.(?:0|[1-9]\\d?)\\.(?:0|[1-9]\\d?)\\.(?:0|[1-9]\\d?)|\\.local(?:host)?$/i.test(h)) return true;\n return false;\n })();\n\n const existing = Array.from(document.querySelectorAll('iframe.w3a-wallet-overlay')) as HTMLIFrameElement[];\n const matches = existing.filter((el) => {\n const dsOrigin = (el as any)?.dataset?.w3aOrigin as string | undefined;\n if (dsOrigin) return dsOrigin === this.walletOrigin;\n try { return new URL(el.src).origin === this.walletOrigin; } catch { return false; }\n });\n\n if (!matches.length) return;\n\n if (isDev) {\n const routerIds = matches\n .map((el) => (el as any)?.dataset?.w3aRouterId)\n .filter((v): v is string => typeof v === 'string' && v.length > 0);\n console.warn(\n `[IframeTransport] Found existing wallet overlay iframe(s) for ${this.walletOrigin}. This usually indicates multiple SDK instances. Removing old iframe(s) to avoid duplicates.`,\n { count: matches.length, routerIds }\n );\n }\n\n for (const el of matches) {\n try { el.remove(); } catch {}\n }\n } catch {}\n }\n\n /** Ensure the iframe element exists and is appended to the DOM. Idempotent. */\n ensureIframeMounted(): HTMLIFrameElement {\n if (this.iframeEl) {\n return this.iframeEl;\n }\n\n this.removeExistingOverlaysForOrigin();\n\n const iframe = document.createElement('iframe');\n // Hidden by default via CSS classes; higher layers toggle state using overlay-styles.\n iframe.classList.add('w3a-wallet-overlay', 'is-hidden');\n // Ensure the base overlay stylesheet is installed early so computed styles\n // (opacity/pointer-events) reflect the hidden state immediately after mount.\n try { ensureOverlayBase(iframe); } catch {}\n // Ensure no initial footprint even before stylesheet attaches\n iframe.setAttribute('width', '0');\n iframe.setAttribute('height', '0');\n iframe.setAttribute('aria-hidden', 'true');\n iframe.setAttribute('tabindex', '-1');\n // Hint higher priority fetch for the iframe document on supporting browsers\n iframe.setAttribute('loading', 'eager');\n iframe.setAttribute('fetchpriority', 'high');\n\n iframe.dataset.w3aRouterId = this.testOptions?.routerId || '';\n if (this.testOptions?.ownerTag) iframe.dataset.w3aOwner = this.testOptions.ownerTag;\n iframe.dataset.w3aOrigin = this.walletOrigin;\n\n // Delegate WebAuthn + clipboard capabilities to the wallet origin frame\n try {\n iframe.setAttribute('allow', this.buildAllowAttr(this.walletOrigin));\n } catch {\n iframe.setAttribute('allow', \"publickey-credentials-get 'self'; publickey-credentials-create 'self'; clipboard-read; clipboard-write\");\n }\n\n // Track load state to guard against races where we post before content is listening\n iframe._svc_loaded = false;\n iframe.addEventListener('load', () => { iframe._svc_loaded = true; }, { once: true });\n\n const src = this.walletServiceUrl.toString();\n console.debug('[IframeTransport] mount: external origin', src);\n iframe.src = src;\n\n document.body.appendChild(iframe);\n console.debug('[IframeTransport] mount: iframe appended');\n this.iframeEl = iframe;\n return iframe;\n }\n\n /**\n * Connect to the wallet iframe using a MessageChannel handshake.\n * - Repeatedly posts {type:'CONNECT'} with a fresh port until a 'READY' message arrives\n * - Times out after connectTimeoutMs\n * - Deduplicates concurrent calls and returns the same MessagePort promise\n */\n async connect(): Promise<MessagePort> {\n if (this.connectInFlight) return this.connectInFlight;\n this.connectInFlight = (async () => {\n const iframe = this.ensureIframeMounted();\n\n // Ensure load fired at least once so the host script can attach listeners\n await this.waitForLoad(iframe);\n\n // For cross-origin pages, give the host only a very brief moment to boot its script\n // Keep this low to avoid adding noticeable latency to the first CONNECT attempt.\n // The handshake will continue retrying regardless, so a shorter wait improves TTFB.\n const bootWaitMs = Math.min(this.opts.connectTimeoutMs / 12, 300);\n const startBoot = Date.now();\n while (!this.serviceBooted && (Date.now() - startBoot) < bootWaitMs) {\n await new Promise(r => setTimeout(r, 50));\n }\n\n let resolved = false;\n let attempt = 0;\n let warnedNullOrigin = false;\n const start = Date.now();\n const overallTimeout = this.opts.connectTimeoutMs;\n\n const port = await new Promise<MessagePort>((resolve, reject) => {\n const tick = () => {\n if (resolved) return;\n const elapsed = Date.now() - start;\n if (elapsed >= overallTimeout) {\n console.debug('[IframeTransport] handshake timeout after %d ms', elapsed);\n return reject(new Error('Wallet iframe READY timeout'));\n }\n\n attempt += 1;\n const channel = new MessageChannel();\n const port1 = channel.port1;\n const port2 = channel.port2;\n const cleanup = () => { try { port1.onmessage = null; } catch {} };\n\n port1.onmessage = (e: MessageEvent<ChildToParentEnvelope>) => {\n const data = e.data;\n if (data.type === IframeMessage.Ready) {\n resolved = true;\n cleanup();\n port1.start?.();\n return resolve(port1);\n }\n };\n\n // Ensure the receiving side is actively listening before we post the CONNECT\n try { port1.start?.(); } catch {}\n\n const cw = iframe.contentWindow;\n if (!cw) {\n cleanup();\n return reject(new Error('Wallet iframe window missing'));\n }\n // Explicitly target the wallet origin so Chromium delivers the MessagePort\n // transfer across origins. Using '*' can silently drop the transferable\n // port in stricter environments, preventing the host from ever adopting it.\n //\n // However, some dev setups (e.g., mDNS/.local + reverse proxy ports) can\n // result in the iframe document resolving to a slightly different serialized\n // origin (e.g., host without the expected port). In those cases, the strict\n // target will never deliver. As a pragmatic fallback for development, we\n // periodically attempt with '*' so the wallet host can adopt the port and\n // reply with READY. Subsequent communication uses MessagePort, not window.postMessage.\n // Try strict origin first, but fall back to '*' more frequently in dev to\n // avoid stalls when local origins serialize differently (e.g., iOS + mDNS).\n // Using '*' here only affects this CONNECT; subsequent traffic uses MessagePort.\n //\n // Prefer wildcard target until we have observed SERVICE_HOST_BOOTED,\n // which indicates the iframe has a stable, non-opaque origin and is\n // ready to adopt a MessagePort. This avoids noisy 'null' origin\n // warnings while still allowing strict-origin delivery as soon as\n // the host is booted.\n const targetOrigin = this.serviceBooted ? this.walletOrigin : '*';\n ({ warnedNullOrigin } = this.postConnectMessage(\n cw,\n { type: IframeMessage.Connect },\n port2,\n targetOrigin,\n warnedNullOrigin,\n elapsed,\n attempt,\n ));\n\n // Schedule next tick if not resolved yet (light backoff to reduce spam)\n const interval = attempt < 10 ? 200 : attempt < 20 ? 400 : 800;\n setTimeout(() => { if (!resolved) tick(); }, interval);\n };\n\n tick();\n });\n\n return port;\n })();\n\n try {\n return await this.connectInFlight;\n } finally {\n this.connectInFlight = null;\n }\n }\n\n private postConnectMessage(\n cw: Window,\n data: unknown,\n port2: MessagePort,\n targetOrigin: string,\n warnedNullOrigin: boolean,\n elapsed: number,\n attempt: number,\n ): { warnedNullOrigin: boolean } {\n try {\n cw.postMessage(data, targetOrigin, [port2]);\n return { warnedNullOrigin };\n } catch (e) {\n const message = e instanceof Error ? e.message ?? String(e) : String(e);\n if (!warnedNullOrigin && message.includes(\"'null'\")) {\n warnedNullOrigin = true;\n console.warn('[IframeTransport] CONNECT blocked; iframe origin appears to be null. Check that %s is reachable and responds with Cross-Origin-Resource-Policy: cross-origin.', this.walletServiceUrl.toString());\n }\n // Attempt wildcard fallback and continue retries\n try { cw.postMessage(data, '*', [port2]); } catch {}\n console.debug('[IframeTransport] CONNECT attempt %d threw after %d ms; retrying.', attempt, elapsed);\n return { warnedNullOrigin };\n }\n }\n\n /** Guard against posting to the iframe before it has fired load. */\n private async waitForLoad(iframe: HTMLIFrameElement): Promise<void> {\n if (iframe._svc_loaded) return;\n await new Promise<void>((resolve) => {\n try {\n let timeout: number | undefined;\n iframe.addEventListener?.('load', () => {\n if (typeof timeout === 'number') {\n clearTimeout(timeout);\n }\n resolve();\n }, { once: true });\n // Safety net: resolve shortly even if load listener fails to attach\n timeout = window.setTimeout(() => {\n console.debug('[IframeTransport] waitForLoad did not observe load event within 150ms; continuing');\n resolve();\n }, 150);\n } catch {\n resolve();\n }\n });\n }\n\n private buildAllowAttr(walletOrigin: string): string {\n return `publickey-credentials-get 'self' ${walletOrigin}; publickey-credentials-create 'self' ${walletOrigin}; clipboard-read; clipboard-write`;\n }\n\n private postBridgeResult(\n source: WindowProxy | null,\n type: 'WALLET_WEBAUTHN_CREATE_RESULT' | 'WALLET_WEBAUTHN_GET_RESULT',\n requestId: string,\n ok: boolean,\n payload: { credential?: unknown; error?: string },\n ): void {\n // Reply directly to the requesting window; wildcard target avoids transient\n // 'null' origin warnings during early navigation while remaining safe since\n // we already validated the sender's origin before bridging.\n source?.postMessage({ type, requestId, ok, ...payload }, '*');\n }\n\n private performWebAuthnBridge(\n kind: typeof WebAuthnBridgeMessage.Create | typeof WebAuthnBridgeMessage.Get,\n raw: unknown,\n e: MessageEvent,\n ): void {\n if (kind === WebAuthnBridgeMessage.Create) {\n void this.handleWebAuthnCreate(raw as CreateReq, e);\n return;\n }\n // kind === 'WALLET_WEBAUTHN_GET'\n void this.handleWebAuthnGet(raw as GetReq, e);\n }\n\n private async handleWebAuthnCreate(req: CreateReq, e: MessageEvent): Promise<void> {\n const requestId = req?.requestId || '';\n try {\n const src = req?.publicKey || ({} as PublicKeyCredentialCreationOptions);\n const rpName = src.rp?.name || 'WebAuthn';\n const rpId = src.rp?.id || window.location.hostname;\n const pub: PublicKeyCredentialCreationOptions = { ...src, rp: { name: rpName, id: rpId } };\n console.debug('[IframeTransport][bridge] CREATE received', { requestId, from: e.origin, rpId: pub.rp?.id });\n const cred = await navigator.credentials.create({ publicKey: pub }) as PublicKeyCredential;\n const serialized = serializeRegistrationCredentialWithPRF({ credential: cred, firstPrfOutput: true, secondPrfOutput: true });\n this.postBridgeResult(e.source as WindowProxy | null, WebAuthnBridgeMessage.CreateResult, requestId, true, { credential: serialized });\n console.debug('[IframeTransport][bridge] CREATE ok', { requestId });\n } catch (err) {\n console.warn('[IframeTransport][bridge] CREATE failed', { requestId, err: String((err as Error)?.message || err) });\n this.postBridgeResult(e.source as WindowProxy | null, WebAuthnBridgeMessage.CreateResult, requestId, false, { error: String((err as Error)?.message || err) });\n }\n }\n\n private async handleWebAuthnGet(req: GetReq, e: MessageEvent): Promise<void> {\n const requestId = req?.requestId || '';\n try {\n const src = req?.publicKey || ({} as PublicKeyCredentialRequestOptions);\n const rpId = src.rpId || window.location.hostname;\n const pub: PublicKeyCredentialRequestOptions = { ...src, rpId };\n console.debug('[IframeTransport][bridge] GET received', { requestId, from: e.origin, rpId: pub.rpId });\n const cred = await navigator.credentials.get({ publicKey: pub }) as PublicKeyCredential;\n const serialized = serializeAuthenticationCredentialWithPRF({ credential: cred, firstPrfOutput: true, secondPrfOutput: true });\n this.postBridgeResult(e.source as WindowProxy | null, WebAuthnBridgeMessage.GetResult, requestId, true, { credential: serialized });\n console.debug('[IframeTransport][bridge] GET ok', { requestId });\n } catch (err) {\n console.warn('[IframeTransport][bridge] GET failed', { requestId, err: String((err as Error)?.message || err) });\n this.postBridgeResult(e.source as WindowProxy | null, WebAuthnBridgeMessage.GetResult, requestId, false, { error: String((err as Error)?.message || err) });\n }\n }\n}\n"],"mappings":"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"}
@@ -662,7 +662,7 @@ var WalletIframeRouter = class {
662
662
  recoveryEmail: payload.recoveryEmail,
663
663
  options: safeOptions && Object.keys(safeOptions).length > 0 ? safeOptions : void 0
664
664
  },
665
- options: { onProgress: payload.onEvent }
665
+ options: { onProgress: this.wrapOnEvent(payload.onEvent, isEmailRecoverySSEEvent) }
666
666
  });
667
667
  return res.result;
668
668
  }
@@ -673,7 +673,16 @@ var WalletIframeRouter = class {
673
673
  accountId: payload.accountId,
674
674
  nearPublicKey: payload.nearPublicKey
675
675
  },
676
- options: { onProgress: payload.onEvent }
676
+ options: { onProgress: this.wrapOnEvent(payload.onEvent, isEmailRecoverySSEEvent) }
677
+ });
678
+ }
679
+ async stopEmailRecovery(payload) {
680
+ await this.post({
681
+ type: "PM_STOP_EMAIL_RECOVERY",
682
+ payload: {
683
+ accountId: payload?.accountId,
684
+ nearPublicKey: payload?.nearPublicKey
685
+ }
677
686
  });
678
687
  }
679
688
  async linkDeviceWithScannedQRData(payload) {
@@ -1004,6 +1013,7 @@ const LOGIN_PHASES = new Set(Object.values(require_sdkSentEvents.LoginPhase));
1004
1013
  const ACTION_PHASES = new Set(Object.values(require_sdkSentEvents.ActionPhase));
1005
1014
  const DEVICE_LINKING_PHASES = new Set(Object.values(require_sdkSentEvents.DeviceLinkingPhase));
1006
1015
  const ACCOUNT_RECOVERY_PHASES = new Set(Object.values(require_sdkSentEvents.AccountRecoveryPhase));
1016
+ const EMAIL_RECOVERY_PHASES = new Set(Object.values(require_sdkSentEvents.EmailRecoveryPhase));
1007
1017
  function phaseOf(progress) {
1008
1018
  return String(progress?.phase ?? "");
1009
1019
  }
@@ -1022,6 +1032,9 @@ function isDeviceLinkingSSEEvent(p) {
1022
1032
  function isAccountRecoverySSEEvent(p) {
1023
1033
  return ACCOUNT_RECOVERY_PHASES.has(phaseOf(p));
1024
1034
  }
1035
+ function isEmailRecoverySSEEvent(p) {
1036
+ return EMAIL_RECOVERY_PHASES.has(phaseOf(p));
1037
+ }
1025
1038
  /**
1026
1039
  * Strips out class functions as they cannot be sent over postMessage to iframe
1027
1040
  */